Skip to content

Commit

Permalink
Merge pull request #261 from lauren32bit/master
Browse files Browse the repository at this point in the history
Add MULE
  • Loading branch information
TinyCircuits authored Oct 15, 2024
2 parents 7f9362a + 3fc0618 commit ef9d063
Show file tree
Hide file tree
Showing 3 changed files with 395 additions and 0 deletions.
379 changes: 379 additions & 0 deletions MULE/MULE.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,379 @@
# MULE.PY
# (c) Lauren32 2024
# Once upon a time, a guy made a game like this for personal computers.
# Now, he is one of the 10 richest people in the world.
# Could this work for me? Only time will tell

import thumby
import time
import random
import gc

LANE1_Y = const(4) # Lane 2 X pos
LANE2_Y = const(19) # Lane 2 Y pos
LINE_Y = const(15) # Center line Y pos

# Draw a centered text box
def textbox(text,y,large=False):
if large == True:
font_w = 5
font_h = 7
thumby.display.setFont("/lib/font5x7.bin",font_w,font_h,1)
else:
font_w = 3
font_h = 5
thumby.display.setFont("/lib/font3x5.bin",font_w,font_h,1)
w = len(text)*(font_w+1)+(font_w+1)
x = round(36-(len(text)/2)*(font_w+1))-2
thumby.display.drawFilledRectangle(x,y,w,font_h+4,0)
thumby.display.drawRectangle(x,y,w,font_h+4,1)
thumby.display.drawText(text,x+3,y+2,1)

class Car():
WIDTH = 12
def __init__(self):
self.x = 5
self.lane = 1 # lane 1 = high, 0 = low

def reset(self):
self.__init__()

class Mule():
def __init__(self,x=72):
self.x = x
self.lane = random.randint(0,1) # lane 1 = high, 0 = low
self.active = False

def move(self):
if self.active:
self.x -= 1

def reset(self):
self.x = 72
self.lane = random.randint(0,1)

class Game:
# BITMAP: width: 12, height: 8
bmp_car = bytearray([0,24,189,255,189,60,60,189,231,189,24,0])

# BITMAP: width: 12, height: 8
bmp_mule = bytearray([32,16,248,24,248,56,56,249,30,255,12,0])
# BITMAP: width: 6, height: 4
bmp_mule_ul = bytearray([0,0,8,8,8,8])
bmp_mule_ul_mask = bytearray([15,15,7,7,7,7])
bmp_mule_ur = bytearray([8,9,14,15,12,0])
bmp_mule_ur_mask = bytearray([7,6,1,0,3,15])
bmp_mule_ll = bytearray([2,1,15,1,15,3])
bmp_mule_ll_mask = bytearray([13,14,0,14,0,12])
bmp_mule_lr = bytearray([3,3,15,1,15,0])
bmp_mule_lr_mask = bytearray([12,12,0,14,0,15])

def __init__(self):
self.score = 0
self.hiscore = 0
self.mules = [Mule()]
self.mules[0].active = True
self.car = Car()
self.g_line_x = 5

def reset(self):
self.score = 0
self.car.reset()
self.mules = [Mule()] # Delete and recreate mules
self.mules[0].active = True

def game_over(self):
thumby.display.fill(0)
textbox("Game Over",5,True)
thumby.display.drawText(f"Score: {self.score}",5,22,1)
thumby.display.drawText(f"High: {self.hiscore}",5,31,1)
thumby.display.update()
self.score = 0
while not thumby.inputJustPressed():
pass

# Detect a collision from the front.
# Returns -1 if no collision or mule number
def collision_detect_front(self):
for i in range(len(self.mules)):
if self.mules[i].lane == 1:
y = LANE1_Y + 4
else:
y = LANE2_Y + 4

if thumby.display.getPixel(self.mules[i].x,y):
return i

return -1

# Detect a collision when switching lanes.
# Returns -1 if no collision or mule number
def collision_detect_side(self):
for i in range(len(self.mules)):
if self.mules[i].lane == 1:
y = LANE2_Y + 4
else:
y = LANE1_Y + 4

if thumby.display.getPixel(self.mules[i].x,y) or thumby.display.getPixel(self.mules[i].x+10,y):
return i

return -1

def mule_explode(self, mule_num):
if self.mules[mule_num].lane == 1:
y1 = LANE1_Y
else:
y1 = LANE2_Y
y2 = y1 + 4
x1 = self.mules[mule_num].x
x2 = self.mules[mule_num].x + 6

for i in range(9):
if i>0:
thumby.display.blit(Game.bmp_mule_ul,x1-i,y1-i,6,4,0,0,0)
thumby.display.blit(Game.bmp_mule_ur,x2+i,y1-i,6,4,0,0,0)
thumby.display.blit(Game.bmp_mule_ll,x1-i,y2+i,6,4,0,0,0)
thumby.display.blit(Game.bmp_mule_lr,x2+i,y2+i,6,4,0,0,0)
thumby.display.update()

thumby.audio.playBlocking(random.randint(500,1000),200)

thumby.display.blit(Game.bmp_mule_ul_mask,x1-i,y1-i,6,4,1,0,0)
thumby.display.blit(Game.bmp_mule_ur_mask,x2+i,y1-i,6,4,1,0,0)
thumby.display.blit(Game.bmp_mule_ll_mask,x1-i,y2+i,6,4,1,0,0)
thumby.display.blit(Game.bmp_mule_lr_mask,x2+i,y2+i,6,4,1,0,0)

thumby.display.drawLine(0,LANE1_Y-2,72,LANE1_Y-2,1)
thumby.display.drawLine(0,LANE2_Y+9,72,LANE2_Y+9,1)
self.draw_score()
thumby.display.update()
textbox("BANG!",13)
thumby.display.update()

def draw_road(self):
thumby.display.drawLine(0,LANE1_Y-2,72,LANE1_Y-2,1)
thumby.display.drawLine(0,LANE2_Y+9,72,LANE2_Y+9,1)

for x in range(self.g_line_x,70,10):
thumby.display.drawLine(x,LINE_Y,x+4,LINE_Y,1)
self.g_line_x -= 1
if self.g_line_x < -5:
self.g_line_x = 5

def draw_actors(self):
if self.car.lane == 1:
car_y = LANE1_Y
else:
car_y = LANE2_Y
thumby.display.blit(Game.bmp_car,self.car.x,car_y,12,8,0,0,0)

for mule in self.mules:
if mule.lane == 1:
mule_y = LANE1_Y
else:
mule_y = LANE2_Y

thumby.display.blit(Game.bmp_mule,mule.x,mule_y,12,8,0,0,0)

# This micropython doesn't have abc, but this is intended to be overriden
def draw_score(self):
pass

def draw(self):
thumby.display.fill(0)
self.draw_road()
self.draw_actors()
self.draw_score()

# Classic game mode
class GameA(Game):
def __init__(self):
super().__init__()
self.mule_score = 0

def draw_score(self):
thumby.display.drawText(f"Driver:{self.score} Mule:{self.mule_score}",3,32,1)

# Main game loop
def start(self):
thumby.display.setFont("/lib/font3x5.bin",3,5,1)
while True:
self.draw()
thumby.display.update()
if thumby.buttonL.justPressed():
break
elif thumby.actionJustPressed():
collision = self.collision_detect_side()
self.car.lane = int(not self.car.lane)
self.draw()
thumby.display.update()
thumby.audio.play(1000,200)
else:
self.mules[0].move()
if self.mules[0].x == 5:
self.car.x += 3
self.mules[0].reset()
self.mules[0].lane = random.randint(0,1)
continue
elif self.car.x > 40:
self.score += 1
self.car.x = 5
textbox("Mule loses!",13)
thumby.display.update()
time.sleep(1)
continue
else:
collision = self.collision_detect_front()

if collision != -1:
self.mule_explode(0)
self.mule_score += 1
self.mules[0].reset()
self.car.reset()
time.sleep(1)

try:
s = round(self.score/self.mule_score) # score is a ratio
except ZeroDivisionError:
s = self.score
self.score = s
self.mule_score = 0
self.game_over()
self.reset()
return

# New game mode
class GameB(Game):
def __init__(self):
super().__init__()
self.delay = 0.020 # game speed for difficulty

def draw_score(self):
thumby.display.drawText(f"Score: {self.score}",5,34,1)

# Return true if there are no mules on the road
def road_empty(self):
empty = True
for mule in self.mules:
if mule.x > 0 and mule.x < 70:
empty = False
return empty

# Line mules back up at regular intervals
def reset_mules(self):
x = 72
for i in range(len(self.mules)):
self.mules[i].lane = random.randint(0,1)
self.mules[i].active = True
self.mules[i].x = x
x += 36

# Main game loop
def start(self):
thumby.display.setFont("/lib/font3x5.bin", 3, 5, 1)
add_mules = True
while True:
thumby.display.update()
if thumby.actionJustPressed():
collide = self.collision_detect_side()
self.car.lane = int(not self.car.lane)
self.draw()
thumby.display.update()
thumby.audio.play(1000,200)
elif thumby.buttonL.pressed():
break
else:
for mule in self.mules:
mule.move()
if mule.x == -11:
print(f"mule count: {len(self.mules)}")
self.score += 1
# Update difficulty on certain levels
if (self.score % 10 == 0) and self.delay > 0.001:
self.delay -= 0.005
if self.car.x < 30 and (self.score % 3 == 0):
self.car.x += 1
if self.score % 5 == 0:
if len(self.mules) < 4:
self.mules.append(Mule())
# At 4 mules, stop adding more and start looping
if add_mules == False:
mule.x = self.mules[-1].x + 36
mule.lane = random.randint(0,1)
self.mules.append(self.mules[0])
self.mules = self.mules[1:]
elif self.road_empty():
self.reset_mules()
if len(self.mules) == 4:
add_mules = False
collide = self.collision_detect_front()

if collide != -1:
self.mule_explode(collide)
time.sleep(1)
break

self.draw()
time.sleep(self.delay)

if self.score > self.hiscore:
self.hiscore = self.score
self.game_over()
self.reset()
return

# Display title screen
def title_screen(index):
thumby.display.setFont("/lib/font5x7.bin",5,7,1)
thumby.display.fill(0)
thumby.display.drawText("MULE.PY",18,2,1)
thumby.display.drawText("Game A",18,13,1)
thumby.display.drawText("Game B",18,22,1)
thumby.display.drawText("Quit",18,31,1)
bullet_y = 0
if index == 0:
bullet_y = 13
elif index == 1:
bullet_y = 22
else:
bullet_y = 31
thumby.display.drawText("*",3,bullet_y,1)
thumby.display.update()

# Dsiplay about screen
def about_screen():
thumby.display.setFont("/lib/font5x7.bin",5,7,1)
thumby.display.fill(0)
thumby.display.drawText("MULE.PY 1.0",2,2,1)
thumby.display.drawText("Lauren32",2,13,1)
thumby.display.drawText("2024",2,22,1)
thumby.display.drawText("CC BY-SA3.0",2,31,1)
thumby.display.update()

game_a = GameA()
game_b = GameB()
menu_index = 0
thumby.display.setFPS(60)
gc.enable() # enable garbage collection

while True:
title_screen(menu_index)
if thumby.buttonU.justPressed() and menu_index > 0:
menu_index -= 1
elif thumby.buttonD.justPressed() and menu_index < 2:
menu_index += 1
elif thumby.buttonL.justPressed():
about_screen()
while not thumby.inputJustPressed():
pass
elif thumby.actionJustPressed():
if menu_index == 0:
game_a.start()
elif menu_index == 1:
game_b.start()
elif menu_index == 2:
thumby.reset()



16 changes: 16 additions & 0 deletions MULE/arcade_description.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
MULE.PY 1.0
2024 lauren32bit
https://github.com/lauren32bit/MULE.PY

Once upon a time, a guy made a game like this for personal computers.
Now, he is one of the 10 richest people in the world.
Could this work for me? Only time will tell ...

From the main menu, select a game mode or press left for info.
Press either action button to change lanes and avoid the mules in the road.

In game mode A, the game is played in rounds; making it to the right of the screen will award a point, and getting hit will award a point to the mule. This continues until you quit by pressing the left button.

In Game B, you will endlessly dodge mules until you hit one. You can also press left here to quit.

That's pretty much it!
Binary file added MULE/mule_video.webm
Binary file not shown.

0 comments on commit ef9d063

Please sign in to comment.