This commit is contained in:
Nguyễn Gia Phong 2017-07-13 16:26:31 +07:00
commit a83e8d581a
13 changed files with 358 additions and 0 deletions

51
README.txt Normal file
View File

@ -0,0 +1,51 @@
Slacker
===============
Entry in PyWeek #4 <http://www.pyweek.org/4/>
Team: Last Minute Frivolities
Members: Clint "HanClinto" Herron (coding)
Jennifer Herron (graphics)
DEPENDENCIES:
You might need to install some of these before running the game:
Python: http://www.python.org/
PyGame: http://www.pygame.org/
RUNNING THE GAME:
Open a terminal / console and "cd" to the game directory and run:
python run_game.py
HOW TO PLAY THE GAME:
Keys:
Spacebar - advance menus, or place your stacking boxes.
Description:
Boxes will move back and forth at varying speeds. Press the spacebar to stop the boxes and move on to the next row. Only boxes that have something underneath them will be stacked. As the tower rises, the game will make your tower thinner. You win a minor prize at the 10th level (the blocks change color), and if you reach the 15th level, you will win the major prize. Good luck!
GAME BACKGROUND:
"Slacker" is a clone/parody of the popular arcade game "Stacker", in which you must stack blocks to the top of the screen in order to win the game. In the arcade version, credits are expensive (often $1 per play), but the prizes are excellent (the one that I played let you choose between an iPod, a PSP, a DS, or a camcorder). As you might guess, it is an extremely difficult game to win. I have made my own version of the game correspondingly hard, so that the next time I go back to the restaurant, I will have a better chance to win a good prize. Despite the hard nature of the game, I hope will enjoy it, and if you wind up practicing with the game and getting so good that you win an extra iPod, I wouldn't mind if you sent it my way. :)
Please forgive the small scope of the project - I started it on the last day of the competition, and didn't have much time to put into it. Still, I hope you enjoyed it!
VERSION HISTORY:
1.0 - First version, submitted to PyWeek #4
1.2 - Released shortly after PyWeek, adding a number of updates:
- Game no longer quits after losing, but restarts automatically
- ESC exits
- Added animations for showing falling blocks that are missed
- Updated graphics for consistency
- Added a more fun and rewarding win screen to congratulate the player
- Tower is shown after win/lose so that you can see how you did
- Packaging is now supported for Mac/Windows
LICENSE:
This game, its source code, and graphics, are freely available for modification as well as use in private, commercial, or open-source projects. Credit is appreciated, but not necessary. We hope you have fun and learn something.

BIN
data/game.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
data/intro.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
data/lose.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
data/win.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
lib/Slacker.icns Normal file

Binary file not shown.

3
lib/Slacker.py Executable file
View File

@ -0,0 +1,3 @@
#This is an automatically generated file that can be deleted
import main
main.main()

27
lib/data.py Normal file
View File

@ -0,0 +1,27 @@
'''Simple data loader module.
Loads data files from the "data" directory shipped with a game.
Enhancing this to handle caching etc. is left as an exercise for the reader.
'''
import os
import pygame
data_py = os.path.abspath(os.path.dirname(__file__))
data_dir = os.path.normpath(os.path.join(data_py, '..', 'data'))
# The following is only used when packaging for .app
#data_dir = os.path.normpath(os.path.join(data_py, '..', '..', '..', 'data'))
def filepath(filename):
'''Determine the path to a file in the data directory.
'''
return os.path.join(data_dir, filename)
def load_image(filename):
# Open a file in the data directory.
return pygame.image.load(os.path.join(data_dir, filename))
#return open(os.path.join(data_dir, filename), mode)

BIN
lib/data.pyc Normal file

Binary file not shown.

237
lib/main.py Normal file
View File

@ -0,0 +1,237 @@
'''Game main module.
Last Day Game Entry, by Clint Herron
'''
import data
import pygame
from pygame.locals import *
from data import *
from math import sin
BOARD_SIZE = BOARD_WIDTH, BOARD_HEIGHT = 12, 20
SCREEN_SIZE = SCREEN_WIDTH, SCREEN_HEIGHT = 240, 400
TILE_SIZE = TILE_WIDTH, TILE_HEIGHT = SCREEN_WIDTH / BOARD_WIDTH, SCREEN_HEIGHT / BOARD_HEIGHT
TILE_COLOR = (127, 127, 255)
TILE_COLOR_ALT = (255, 127, 127)
TILE_COLOR_LOSE = (64, 64, 128)
TILE_COLOR_ALT_LOSE = (127, 64, 64)
BLACK = (0,0,0)
LEVEL_SPEED = ( 80, 80, 75, 75, 70, 70, 65, 60, 55, 50,
45, 40, 35, 30, 32 )
MAX_WIDTH = (3, 3, 3, 3, 2, 2, 2, 2, 1, 1,
1, 1, 1, 1, 1)
COLOR_CHANGE_Y = 10 # The block below which are displayed in the alternate color
WIN_LEVEL = 15
current_speed = 50 # Current tile speed in milliseconds
board = []
lose_tiles = []
current_direction = 1
current_x, current_y, current_width = 0, BOARD_HEIGHT - 1, 3
current_level = 0
INTRO = 0
PLAYING = 1
LOSE = 2
WIN = 3
game_state = INTRO
bg_images = ( load_image("intro.png"), load_image("game.png"), load_image("lose.png"), load_image("win.png") )
bg_images[WIN].set_colorkey(BLACK)
bg_images[LOSE].set_colorkey(BLACK)
keep_running = True
def main():
global game_state, current_x, current_y, current_speed, keep_running, current_width, current_level
pygame.init()
screen = pygame.display.set_mode( SCREEN_SIZE )
reset_game()
while(keep_running):
update_movement()
update_board_info()
update_screen(screen)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keep_running = False
elif event.type == KEYDOWN: if event.key == K_SPACE: key_hit()
elif event.key == K_ESCAPE:
if game_state == INTRO:
keep_running = False
else:
reset_game()
elif event.key == K_F1: # Yes, this is a cheat.
current_x -= 1
if (current_x < 0): current_x = 0
current_width += 1
if (current_width >= BOARD_WIDTH): current_width = BOARD_WIDTH - 1
pygame.display.quit()
def reset_game():
global game_state, current_x, current_y, current_speed, keep_running, current_width, current_level, lose_tiles
clear_board()
lose_tiles = []
keep_running = True
game_state = INTRO
current_x = 0
current_y = BOARD_HEIGHT - 1
current_level = 0
current_speed = LEVEL_SPEED[current_level]
current_width = MAX_WIDTH[current_level]
def key_hit():
global keep_running, game_state, current_x, current_y, current_width, current_speed, current_level, lose_tiles
if game_state == PLAYING:
if current_y < BOARD_HEIGHT - 1:
for x in range(current_x, current_x + current_width):
if board[x][current_y + 1] == 0: # If they're standing on a block that did not work
current_width -= 1 # Then next time, give them one less block
board[x][current_y] = 0 # Also, get rid of this block that isn't standing on solid ground.
# Then, add a lose tile for that missed block
# Lose tile format is (x, y, color, start time)
lose_tiles.append( (x, current_y,
pygame.time.get_ticks()) )
current_level += 1
check_win_lose()
current_y -= 1
elif game_state == INTRO:
game_state = PLAYING
elif (game_state == LOSE) or (game_state == WIN):
reset_game()
game_state = INTRO
else:
keep_running = False
def check_win_lose():
global game_state, current_width, current_level, current_speed, keep_running, TILE_COLOR
if current_width == 0:
game_state = LOSE
elif current_level == WIN_LEVEL:
current_speed = 100
game_state = WIN
else:
current_speed = LEVEL_SPEED[current_level]
if current_width > MAX_WIDTH[current_level]:
current_width = MAX_WIDTH[current_level]
last_time = 0
def update_movement():
global game_state, last_time, current_x, current_y, current_width, current_speed, current_direction
current_time = pygame.time.get_ticks()
if (last_time + current_speed <= current_time):
if game_state == PLAYING:
new_x = current_x + current_direction
if (new_x < 0) or (new_x + current_width > BOARD_WIDTH):
current_direction = -current_direction
current_x += current_direction
last_time = current_time
def update_screen(screen):
global game_state
if game_state == PLAYING:
draw_background(screen)
draw_board(screen)
elif game_state == INTRO:
draw_background(screen)
pass
elif (game_state == LOSE) or (game_state == WIN):
screen.fill(BLACK)
draw_board(screen)
draw_background(screen)
pygame.display.flip()
def draw_background(screen):
global game_state
screen.blit(bg_images[game_state], (0,0,SCREEN_WIDTH,SCREEN_HEIGHT), (0,0,SCREEN_WIDTH,SCREEN_HEIGHT))
def update_board_info():
global game_state
if game_state == PLAYING:
clear_row(current_y)
fill_current_row()
def draw_board(screen):
for x in range(BOARD_WIDTH):
for y in range(BOARD_HEIGHT):
if board[x][y] == 1:
draw_tile(screen, x, y)
draw_lose_tiles(screen)
def draw_tile(screen, x, y):
xoffset = 0 # XOffset is used to draw some wiggle in the tower when you win
col = TILE_COLOR
if (y < COLOR_CHANGE_Y):
col = TILE_COLOR_ALT
if (game_state == LOSE):
col = TILE_COLOR_LOSE
if (y < COLOR_CHANGE_Y):
col = TILE_COLOR_ALT_LOSE
if game_state == WIN:
xoffset = sin(pygame.time.get_ticks() * 0.004 + y * 0.5) * (SCREEN_WIDTH / 4)
pygame.draw.rect(screen, col, (x * TILE_WIDTH + xoffset, y * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT))
pygame.draw.rect(screen, BLACK, (x * TILE_WIDTH + xoffset, y * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT), 2)
# Lose tiles are ones that fall off from the edge when you miss placing them on the proper stack
def draw_lose_tiles(screen):
for lt in lose_tiles:
deltaT = (pygame.time.get_ticks() - lt[2]) * 0.008 # How long it has been falling
x = lt[0] * TILE_WIDTH
y = lt[1] * TILE_HEIGHT + deltaT * deltaT
col = TILE_COLOR_LOSE
if (lt[1] < COLOR_CHANGE_Y):
col = TILE_COLOR_ALT_LOSE
if (y > SCREEN_HEIGHT):
lose_tiles.remove(lt)
else:
pygame.draw.rect(screen, col, (x+2, y+2, TILE_WIDTH-3, TILE_HEIGHT-3))
def clear_board():
global board
board = []
for x in range(BOARD_WIDTH):
board.append([])
for y in range (BOARD_HEIGHT):
board[x].append(0)
def clear_row(y):
for x in range(BOARD_WIDTH):
board[x][y] = 0
def fill_current_row():
global current_x, current_y, current_width
for x in range(current_x, current_x + current_width):
board[x][current_y] = 1

BIN
lib/main.pyc Normal file

Binary file not shown.

27
lib/setup_py2app.py Normal file
View File

@ -0,0 +1,27 @@
"""
Script for building the example.
Usage:
python setup.py py2app
"""
from setuptools import setup
NAME = 'Slacker'
VERSION = '1.2'
plist = dict(
CFBundleIconFile='Slacker.icns',
CFBundleName=NAME,
CFBundleShortVersionString=VERSION,
CFBundleGetInfoString=' '.join([NAME, VERSION]),
CFBundleExecutable=NAME,
CFBundleIdentifier='pyweek.4.slacker',
)
setup(
data_files=['../data'],
app=[
dict(script="Slacker.py", plist=plist),
],
setup_requires=["py2app"],
)

13
run_game.py Normal file
View File

@ -0,0 +1,13 @@
#! /usr/bin/env python
import sys
import os
try:
libdir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'lib'))
sys.path.insert(0, libdir)
except:
# probably running inside py2exe which doesn't set __file__
pass
import main
main.main()