Skip to content

Commit 2bcd7f1

Browse files
Dragon Drop: minimally playable mechanics; still needs score, health, title
1 parent 9c3afac commit 2bcd7f1

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

Macropad_Dragon_Drop/code.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
"""
2+
Dragon Drop: a simple game for Adafruit MACROPAD. Uses OLED display in
3+
portrait (vertical) orientation. Tap one of four keys across a row to
4+
catch falling eggs before they hit the ground. Avoid fireballs.
5+
"""
6+
7+
# pylint: disable=import-error, unused-import
8+
import gc
9+
import random
10+
import time
11+
import displayio
12+
import adafruit_imageload
13+
from adafruit_macropad import MacroPad
14+
#from adafruit_display_shapes.rect import Rect
15+
#from adafruit_display_text import label
16+
17+
18+
# CONFIGURABLES ------------------------
19+
20+
MAX_EGGS = 6
21+
22+
23+
# UTILITY FUNCTIONS AND CLASSES --------
24+
25+
# pylint: disable=too-few-public-methods
26+
class Sprite:
27+
""" Class for sprite (eggs, fireballs) state information """
28+
def __init__(self, column, start_time):
29+
self.column = column # 0-3
30+
self.is_fire = (random.random() < 0.25) # 1/4 chance of fireballs
31+
self.start_time = start_time # For drop physics
32+
self.paused = False
33+
self.prev_pos = 0
34+
35+
# List of Sprite objects, appended and popped as needed. Same is done with
36+
# the GROUP list (below), but they're not 1:1 -- latter has extra elements
37+
# for background, score, etc.
38+
SPRITES = []
39+
40+
# ONE-TIME INITIALIZATION --------------
41+
42+
MACROPAD = MacroPad(rotation=90)
43+
MACROPAD.display.auto_refresh = False
44+
MACROPAD.pixels.auto_write = False
45+
46+
GROUP = displayio.Group(max_size=MAX_EGGS + 10)
47+
48+
# Bitmap containing five shadow tiles (no shadow through max shadow)
49+
BITMAP, PALETTE = adafruit_imageload.load(
50+
'shadow.bmp', bitmap=displayio.Bitmap, palette=displayio.Palette)
51+
# Tilegrid containing four shadows; one per column
52+
SHADOW = displayio.TileGrid(BITMAP, pixel_shader=PALETTE, width=4, height=1,
53+
tile_width=16, tile_height=BITMAP.height,
54+
x=0, y=MACROPAD.display.height - BITMAP.height)
55+
GROUP.append(SHADOW)
56+
57+
# Bitmap containing eggs, hatchling and fireballs
58+
SPRITE_BITMAP, SPRITE_PALETTE = adafruit_imageload.load(
59+
'sprites.bmp', bitmap=displayio.Bitmap, palette=displayio.Palette)
60+
SPRITE_PALETTE.make_transparent(0)
61+
62+
MACROPAD.display.show(GROUP)
63+
MACROPAD.display.refresh()
64+
65+
# Sprite states include falling, paused for catch and paused for breakage.
66+
# Following pause, sprite is removed.
67+
68+
69+
# MAIN LOOP ----------------------------
70+
71+
START_TIME = time.monotonic()
72+
73+
while True:
74+
NOW = time.monotonic()
75+
SPEED = 10 + (NOW - START_TIME) / 30
76+
77+
# Coalese any/all queued-up keypress events per column
78+
COLUMN_PRESSED = [False] * 4
79+
while True:
80+
EVENT = MACROPAD.keys.events.get()
81+
if not EVENT:
82+
break
83+
if EVENT.pressed:
84+
COLUMN_PRESSED[EVENT.key_number % 4] = True
85+
86+
# For determining upper/lower extents of active egg sprites per column
87+
COLUMN_MIN = [MACROPAD.display.height] * 4
88+
COLUMN_MAX = [0] * 4
89+
90+
# for i, sprite in enumerate(SPRITES):
91+
# Traverse sprite list backwards so we can pop() without index problems
92+
for i in range(len(SPRITES) - 1, -1, -1):
93+
sprite = SPRITES[i]
94+
COLUMN = sprite.column
95+
TILE = GROUP[i + 1] # Corresponding TileGroup for sprite
96+
ELAPSED = NOW - sprite.start_time # Time since add or pause event
97+
98+
if sprite.paused:
99+
if ELAPSED > 0.75: # Hold position for 3/4 second
100+
SPRITES.pop(i)
101+
GROUP.pop(i + 1)
102+
continue
103+
if not sprite.is_fire:
104+
COLUMN_MAX[COLUMN] = max(COLUMN_MAX[COLUMN], MACROPAD.display.height - 22)
105+
else:
106+
y = SPEED * ELAPSED * ELAPSED - 16
107+
COLUMN_MIN[COLUMN] = min(COLUMN_MIN[COLUMN], y)
108+
if not sprite.is_fire:
109+
COLUMN_MAX[COLUMN] = max(COLUMN_MAX[COLUMN], y)
110+
TILE.y = int(y) # Sprite's vertical position in GROUP list
111+
112+
if sprite.is_fire:
113+
if y >= MACROPAD.display.height:
114+
SPRITES.pop(i)
115+
GROUP.pop(i + 1)
116+
continue
117+
else:
118+
# Animate fire sprites
119+
TILE[0] = 3 + int((NOW * 6) % 2.0)
120+
# Fire catch logic
121+
if y >= MACROPAD.display.height - 40 and COLUMN_PRESSED[COLUMN]:
122+
sprite.paused = True
123+
sprite.start_time = NOW
124+
TILE.y = MACROPAD.display.height - 20
125+
else:
126+
if y >= MACROPAD.display.height - 22:
127+
# Egg hit ground
128+
TILE.y = MACROPAD.display.height - 22
129+
TILE[0] = 1 # Broken egg
130+
sprite.paused = True
131+
sprite.start_time = NOW
132+
elif y >= MACROPAD.display.height - 40:
133+
if COLUMN_PRESSED[COLUMN]:
134+
# Egg caught at right time
135+
TILE.y = MACROPAD.display.height - 22
136+
TILE[0] = 2 # Dragon hatchling
137+
sprite.paused = True
138+
sprite.start_time = NOW
139+
elif y >= MACROPAD.display.height - 58:
140+
if COLUMN_PRESSED[COLUMN]:
141+
# Egg caught too soon
142+
TILE.y = MACROPAD.display.height - 40
143+
TILE[0] = 1 # Broken egg
144+
sprite.paused = True
145+
sprite.start_time = NOW
146+
147+
sprite.prev_pos = y
148+
149+
# Select shadow bitmaps based on each column's lowest sprite
150+
for i in range(4):
151+
SHADOW[i] = min(4, int(5 * COLUMN_MAX[i] / MACROPAD.display.height))
152+
153+
# Time to introduce a new sprite?
154+
if len(SPRITES) < MAX_EGGS and random.random() < 0.05:
155+
if max(COLUMN_MIN) > 16: # At least one column has space
156+
while True:
157+
COLUMN = random.randint(0, 3)
158+
if COLUMN_MIN[COLUMN] <= 16:
159+
continue
160+
# Found a spot. Add sprite and break loop
161+
SPRITES.append(Sprite(COLUMN, NOW))
162+
GROUP.append(displayio.TileGrid(SPRITE_BITMAP,
163+
pixel_shader=SPRITE_PALETTE,
164+
width=1, height=1,
165+
tile_width=16,
166+
tile_height=SPRITE_BITMAP.height,
167+
x=COLUMN * 16,
168+
y=-16))
169+
break
170+
171+
MACROPAD.display.refresh()
172+
gc.collect()

0 commit comments

Comments
 (0)