3 Commits

Author SHA1 Message Date
William Hubbard
5967359e70 Refines the difficulty curve. 2023-02-05 10:30:42 -07:00
William Hubbard
81822ee80a Merge branch 'master' into random-obstacles 2023-02-05 09:45:32 -07:00
William Hubbard
3c6e070716 Changes the obstacle generation system.
More random, less level design needed.
2023-02-05 09:40:18 -07:00
7 changed files with 179 additions and 426 deletions

223
Throot.py
View File

@@ -7,15 +7,15 @@ import Games.Throot.map
import Games.Throot.sprites import Games.Throot.sprites
from random import randrange from random import randrange
from Games.Throot.artrepository import OBSTACLES
from Games.Throot.artrepository import * from Games.Throot.player import Player
from Games.Throot.player import Player, Camera
from Games.Throot.map import ObstacleSlice, OBSTACLE_SLICES from Games.Throot.map import ObstacleSlice, OBSTACLE_SLICES
from Games.Throot.sprites import Entity, Obstacle, Collectible, update_entities from Games.Throot.sprites import Obstacle, update_entities
from Games.Throot.constants import * from Games.Throot.constants import CONST_FPS
class GameManager: class GameManager:
EVENT_ROOM_CHANGE = {'start': 0, 'main': 1, 'end': 2, 'next': -1}
def __init__(self, *rooms): def __init__(self, *rooms):
self.rooms = rooms self.rooms = rooms
self.room_index = 0 self.room_index = 0
@@ -24,14 +24,13 @@ class GameManager:
# Set the FPS (without this call, the default fps is 30) # Set the FPS (without this call, the default fps is 30)
thumby.display.setFPS(CONST_FPS) thumby.display.setFPS(CONST_FPS)
def room_goto(self, event): def room_goto(self, index):
index = event['room']
if index < 0: if index < 0:
self.room_index = min(self.room_index + 1, len(self.rooms)) self.room_index = min(self.room_index + 1, len(self.rooms))
else: else:
self.room_index = index self.room_index = index
self.rooms[self.room_index].start(event) self.rooms[self.room_index].start()
def restart(self): def restart(self):
self.room_index = 0 self.room_index = 0
@@ -42,14 +41,11 @@ class GameManager:
thumby.display.update() thumby.display.update()
if event: if event:
self.room_goto(event) self.room_goto(self.EVENT_ROOM_CHANGE[event])
class Room: class Room:
def __init__(self): def start(self):
self.ind_button = 0
def start(self, event):
t0 = time.ticks_ms() # Get time (ms) t0 = time.ticks_ms() # Get time (ms)
thumby.display.fill(0) # Fill canvas to black thumby.display.fill(0) # Fill canvas to black
@@ -62,217 +58,82 @@ class RoomTitle(Room):
super().update(tpf) super().update(tpf)
if thumby.inputPressed(): if thumby.inputPressed():
return { return 'main'
'room': ROOM_MAIN
}
# draw title sprite # draw title sprite
#thumby.display.drawSprite(sprite_title.width, sprite_title.height, sprite_title.image) title_text = 'press a to start'
thumby.display.blit(sprite_title.image, 0, 0, sprite_title.width, sprite_title.height, chr_len = 5
-1, 0, 0) x = int(round(thumby.display.width / 2 - (chr_len * len(title_text)) / 2))
y = int(round(thumby.display.height / 2 - chr_len / 2))
# title text
chrx = 5
chry = 7
buff = 2 buff = 2
w = chr_len * len(title_text) + buff
x = 14#int(round(thumby.display.width / 2 - (chr_len * len(title_text)) / 2)) h = chr_len + buff
y = 40 - chry - buff#int(round(thumby.display.height / 2 - chr_len / 2)) thumby.display.drawFilledRectangle(x - buff, y - buff, w, h, 1)
thumby.display.drawText(title_text, x, y, 0)
w = chrx * 6 + buff
h = chry + buff
thumby.display.drawFilledRectangle(x - buff, y - buff, w, h * 2 + buff, 0)
#thumby.display.drawText('press a', x, y, 0)
thumby.display.drawText('start', x, y, 1)
spr_b = thumby.Sprite(sprite_buttona1.width, sprite_buttona1.height, sprite_buttona1.image + sprite_buttona2.image, 2, 40 - 8,
-1, 0, 0)
# thumby.display.blit(sprite_buttona.image, 2, 40 - 8, sprite_buttona.width, sprite_buttona.height,
# -1, 0, 0)
self.ind_button = (self.ind_button + 1) % (2 * CONST_FPS)
spr_b.setFrame(self.ind_button // (2 * CONST_FPS))
#print(self.ind_button // CONST_FPS)
thumby.display.drawSprite(spr_b)
class GameLoop(Room): class GameLoop(Room):
def __init__(self): def __init__(self):
self.current_depth = 0
self.diving_velocity = 10 self.diving_velocity = 10
self.diving_accel = 0 self.diving_accel = 0
self.current_obstacle_slice = OBSTACLE_SLICES[0] self.current_obstacle_slice = OBSTACLE_SLICES[0]
self.entities = set() self.entities = set()
self.player = None self.player = Player()
self.camera = None
self.score = 0 self.score = 0
self.level = 0
self.finish_depth = 0
self.waterx = float(0)
seed_sprite = thumby.Sprite(seed.width, seed.height, seed.image, 0, 0, 0, False, False)
self.seed = Entity((thumby.display.width - seed.width) // 2, 0, seed.width, seed.height, None, seed_sprite)
def start(self, event):
self.entities.clear()
self.entities.add(
self.seed
)
self.current_obstacle_slice = OBSTACLE_SLICES[0]
self.player = Player()
self.player.yspeed = 10
self.score = 'score' in event and event['score'] or 0
self.level = 'level' in event and event['level'] or 0
self.finish_depth = 100 + 5 * self.level ** 2
self.camera = Camera(self.player)
def get_obstacle_slice(self): def get_obstacle_slice(self):
return OBSTACLE_SLICES[randrange(0, len(OBSTACLE_SLICES))] return OBSTACLE_SLICES[randrange(0, len(OBSTACLE_SLICES))]
def draw_sky(self):
sky_x = 0
sky_y = -thumby.display.height
sky_width = thumby.display.width
sky_height = thumby.display.height
if self.camera.entity_in_camera(sky_x, sky_y, sky_width, sky_height):
sky_x, sky_y = self.camera.relative_to_camera(sky_x, sky_y)
thumby.display.drawFilledRectangle(int(sky_x), int(sky_y), int(sky_width), int(sky_height), 1)
sky_x = 0
sky_y = 0
sky_width = thumby.display.width
sky_height = thumby.display.height
if self.camera.entity_in_camera(sky_x, sky_y, sky_width, sky_height):
sky_x, sky_y = self.camera.relative_to_camera(sky_x, sky_y)
thumby.display.drawFilledRectangle(int(sky_x), int(sky_y), int(sky_width), int(sky_height), 0)
def draw_water(self):
# bookwater
# exit if depth is less than 2 screens of the finish depth
if self.player.y + thumby.display.height < self.finish_depth:
return
self.waterx += sprite_water.width / CONST_FPS
if self.waterx > sprite_water.width:
self.waterx = 0
# water surface.
for i in range(math.ceil(thumby.display.width / sprite_water.width) + 1):
x, y = self.camera.relative_to_camera(
(int(self.waterx) - sprite_water.width) + i * sprite_water.width,
self.finish_depth
)
thumby.display.blit(
sprite_water[0],
int(x), int(y),
sprite_water.width, sprite_water.height,
0,
0, 0
)
# water below surface
x, y = self.camera.relative_to_camera(
0,
self.finish_depth
)
thumby.display.drawFilledRectangle(
int(x), int(y) + sprite_water.height,
thumby.display.width, self.finish_depth + thumby.display.height * 3,
1,
)
# win text
if self.player.y > self.finish_depth + (thumby.display.height // 2):
thumby.display.drawText('water reach!', 0, thumby.display.height // 4, 0)
thumby.display.drawText(f' stage {self.level + 1}', 0, thumby.display.height // 4 + 9, 0)
def update(self, tpf): def update(self, tpf):
""" """
Executes one tick of the game Executes one tick of the game
""" """
super().update(tpf) super().update(tpf)
prev_depth = self.current_depth
self.player.update_phys(tpf, self.camera) self.diving_velocity += self.diving_accel * tpf
self.current_depth += self.diving_velocity * tpf
self.camera.update_phys() #print("%f (%f)" % (self.current_depth, self.current_depth % ObstacleSlice.height))
#print("%f -> %f: %f -> %f" % (prev_depth, self.current_depth, prev_depth % ObstacleSlice.height, self.current_depth % ObstacleSlice.height))
if (
self.current_depth > prev_depth and self.current_depth % ObstacleSlice.height < prev_depth % ObstacleSlice.height
or self.current_depth < prev_depth and self.current_depth % ObstacleSlice.height > prev_depth % ObstacleSlice.height
):
self.current_obstacle_slice = self.get_obstacle_slice()
self.player.update_phys(self.current_depth, prev_depth)
# Generate new entities as needed # Generate new entities as needed
if abs(self.player.y) < abs(self.finish_depth): self.entities |= update_entities(self.entities, 5, self.current_depth, prev_depth)
self.entities |= update_entities(self.entities, self.level, self.player.ysub, self.player.prev_y)
else:
self.entities.clear()
# Updates the entities # Updates the entities
to_remove = set() to_remove = set()
for entity in self.entities: for entity in self.entities:
if not entity.update(tpf, self.player, self.camera) or abs(entity.y - self.player.y) > self.camera.height * 1.5: if not entity.update(tpf, self.current_depth, prev_depth):
to_remove.add(entity) to_remove.add(entity)
elif entity.intersects_pixel(self.player.xsub, self.player.ysub): elif entity.intersects_pixel(self.player.xsub, self.player.ysub):
if isinstance(entity, Obstacle): if isinstance(entity, Obstacle):
return { print("Hit!")
'room': ROOM_END,
'score': self.score,
'level': self.level + 1
}
elif isinstance(entity, Collectible):
self.score += entity.score
to_remove.add(entity)
self.entities -= to_remove self.entities -= to_remove
self.score += int(abs(self.player.ysub - self.player.prev_y) * 10) self.score = int(self.current_depth * 10)
# finish level
if self.player.y > self.finish_depth + int(thumby.display.height * 1.5):
self.score += 20
self.level += 1
self.start({
'score': self.score,
'level': self.level
})
thumby.display.fill(0) thumby.display.fill(0)
self.draw_sky()
# Draw the entities # Draw the entities
for entity in self.entities: for entity in self.entities:
entity.render(tpf, self.camera) entity.render(tpf, self.current_depth, prev_depth)
self.player.update_draw(self.camera) self.player.update_draw(self.player.y)
self.draw_water()
thumby.display.drawText(str(self.score), thumby.display.width - (len(str(self.score)) + 2) * 5, 1, 1) thumby.display.drawText(str(self.score), thumby.display.width - (len(str(self.score)) + 2) * 5, 1, 1)
thumby.display.update()
class EndRoom(Room):
def __init__(self):
self.score = 0
def start(self, event):
self.score = event['score']
# Used to make sure that we aren't triggering from input mean't for the game.
self.still_pressed = thumby.inputPressed()
def update(self, tpf):
if not thumby.inputPressed():
self.still_pressed = False
elif not self.still_pressed and thumby.inputPressed():
return {
'room': ROOM_START
}
thumby.display.fill(0)
thumby.display.drawText("Final Score:", (thumby.display.width - 60) // 2, 1, 1)
thumby.display.drawText(str(self.score), (thumby.display.width - (len(str(self.score)) * 5)) // 2, 9, 1)
def main(): def main():
game = GameManager(RoomTitle(), GameLoop(), EndRoom()) game = GameManager(RoomTitle(), GameLoop())
game.start() game.start()
prev_time = 0 prev_time = 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -2,27 +2,6 @@ from collections import namedtuple
SpriteImage = namedtuple("SpriteImage", ["image", "collision", "width", "height"]) SpriteImage = namedtuple("SpriteImage", ["image", "collision", "width", "height"])
seed = SpriteImage(
bytearray([8,20,46,46,62,62,46,62,46,12,0]),
None,
11,
7
)
collectibleover = SpriteImage(
bytearray([9,4,6,9]),
None,
4,
4
)
collectibleunder = SpriteImage(
bytearray([6,11,9,6]),
None,
4,
4
)
# BITMAP: width: 8, height: 8 # BITMAP: width: 8, height: 8
skull = SpriteImage( skull = SpriteImage(
bytearray([0,28,102,126,38,30,28,0]), bytearray([0,28,102,126,38,30,28,0]),
@@ -196,32 +175,4 @@ Doritoobstaclelvl5 = SpriteImage(
30 30
) )
sprite_water = SpriteImage(
bytearray([224,240,240,248,252,248,240,240]),
None,
8,
8
)
sprite_title = SpriteImage(
bytearray([32,128,40,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,0,0,0,0,0,0,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,160,128,32,0,72,0,192,96,24,8,4,6,2,2,2,2,2,4,12,24,112,192,0,0,0,3,3,3,3,3,255,255,255,3,251,251,251,3,3,251,251,251,3,251,251,155,155,155,155,99,99,0,248,248,24,24,24,251,251,3,251,251,27,27,27,251,251,3,255,255,255,3,3,3,0,1,0,31,48,96,64,64,64,98,50,30,0,0,192,96,48,16,24,15,12,24,48,96,192,0,0,0,255,255,255,0,255,255,255,7,7,255,255,255,0,255,255,1,1,1,3,255,255,0,255,255,128,128,128,255,255,0,255,255,128,128,128,255,255,0,255,255,255,0,0,0,0,192,64,96,32,32,69,192,10,0,4,0,0,127,129,0,0,0,0,0,0,0,96,48,29,7,0,0,63,255,255,192,149,85,85,84,84,85,85,85,84,85,85,84,84,84,84,85,85,84,85,85,85,85,85,85,85,84,85,85,85,85,85,85,21,192,255,255,127,0,0,0,3,7,4,132,128,192,96,29,70,0,0,0,0,0,1,6,12,8,24,16,248,140,4,4,108,72,120,48,0,128,193,255,255,127,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,252,254,255,183,99,65,64,64,96,48,24,30,99,65,64,194,128,133,0,2,0,128,96,24,12,4,2,2,1,1]),
None,
72,
40
)
sprite_buttona1 = SpriteImage(
bytearray([60,98,221,203,203,221,98,60]),
None,
8,
8
)
sprite_buttona2 = SpriteImage(
bytearray([56,68,186,150,150,186,68,56]),
None,
8,
8
)
OBSTACLES = [skull, pizza, boulder, ball, steve, mathroot, leaf, robot, dinoskull, dinojaw, dinospine, bone, bigdinohead, bigboneddiag, bigdoritoobstacle, spaceinv,annoydead,lvl4wall,ballobstaclelvl3,Doritoobstaclelvl5] OBSTACLES = [skull, pizza, boulder, ball, steve, mathroot, leaf, robot, dinoskull, dinojaw, dinospine, bone, bigdinohead, bigboneddiag, bigdoritoobstacle, spaceinv,annoydead,lvl4wall,ballobstaclelvl3,Doritoobstaclelvl5]

View File

@@ -1,7 +1 @@
CONST_FPS = 60 CONST_FPS = 60
ROOM_START = 0
ROOM_MAIN = 1
ROOM_END = 2
ROOM_NEXT = -1

267
player.py
View File

@@ -2,215 +2,178 @@ import time
import thumby import thumby
import math import math
import random import random
from collections import namedtuple
from Games.Throot.constants import CONST_FPS from Games.Throot.constants import CONST_FPS
# didplay width 72 # didplay width 72
# display height 40 # display height 40
class Input: # constants
def move_right(self): CONST_PL_SCREEN_Y = int((0.5) * thumby.display.height)
return (
thumby.buttonA.pressed() or
thumby.buttonR.pressed()
)
def move_left(self):
return thumby.buttonL.pressed()
inp = Input()
### classes CONST_INP_MOVE_LEFT = thumby.buttonL
CONST_INP_MOVE_RIGHT = thumby.buttonA
# classes
class Player: class Player:
def __init__(self): def __init__(self):
self.prev_x = 0
self.prev_y = 0
self.xsub = float(thumby.display.width / 2) self.xsub = float(thumby.display.width / 2)
self.ysub = float(0) self.ysub = float(0)
self.x = int(self.xsub) self.x = int(self.xsub)
self.y = int(self.ysub) self.y = int(self.ysub)
self.movespeed = 32 / CONST_FPS
self.isdecend = 1 self.isdecend = 1
self.input_x = float(0) self.xspeed = 32 / CONST_FPS
self.yspeed = 32 / CONST_FPS
self.accx = 1 / (CONST_FPS * 4)
self.accaccx = self.accx
# constants self.anim = PlayerAnimation(self.x, self.y)
self.speed_x_start = 32 self.debugvisible = True
self.speed_x_max = 200
self.acc_x_start = 64
self.jerk_x = 128
self.speed_x = self.speed_x_start def update_phys(self, current_depth, prev_depth):
self.acc_x = self.acc_x_start inp = int(0)
if CONST_INP_MOVE_LEFT.pressed():
inp -= 1
if CONST_INP_MOVE_RIGHT.pressed():
inp += 1
# y speed is constant # debug visible
self.speed_y = 16 if thumby.buttonU.pressed():
self.debugvisible = not self.debugvisible
self.anim = PlayerAnimation(self)
def update_phys(self, tpf, camera: Camera):
# prevoius sub pixel position
self.prev_x = self.xsub
self.prev_y = self.ysub
### x movement ### x movement
self.xspeed += self.accx
self.accx += self.accaccx
# x input if (inp == 0) and (self.xspeed > self.movespeed):
if inp.move_left() and (not inp.move_right()): self.xspeed = self.movespeed
self.input_x = -1 self.accx = self.accaccx
if inp.move_right() and (not inp.move_left()):
self.input_x = 1
if (not inp.move_left()) and (not inp.move_right()):
self.input_x = 0
# acceleration and jerk xx = self.xsub + inp * self.xspeed
self.speed_x += self.acc_x * tpf
self.acc_x += self.jerk_x * tpf
# limit speed # clamp to screen
self.speed_x = min(self.speed_x_max, self.speed_x) self.xsub = max(min(xx, thumby.display.width - 1), 0)
# reset speed and acceleration if no input
if (self.input_x == 0) and (self.speed_x != self.speed_x_start):
self.speed_x = self.speed_x_start
self.acc_x = self.acc_x_start
# set x sub pixel position
self.xsub += self.speed_x * tpf * self.input_x
### y movement ### y movement
self.ysub = current_depth - (thumby.display.height / 2)
# set y sub pixel position
self.ysub += self.speed_y * tpf
### stay in world bounds
self.xsub = max(min(self.xsub, thumby.display.width - 1), 0)
### pixel positions ### pixel positions
self.x = int(self.xsub) self.x = int(self.xsub)
self.y = int(self.ysub) self.y = int(self.ysub)
### animation physics # animation
self.anim.update_phys(camera) self.anim.update_phys(self.x, self.y, CONST_PL_SCREEN_Y, current_depth)
def update_draw(self, camera): def update_draw(self, plworldy):
### animation render # debug draw
self.anim.update_draw(camera, self.isdecend) if self.debugvisible:
pix_x = self.x
pix_y = int(self.y - plworldy + 20)
thumby.display.setPixel(pix_x, pix_y, self.isdecend)
# animation
self.anim.update_draw(plworldy - 20, self.isdecend)
class Camera: class Camera:
def __init__(self, target): def __init__(self, target):
self.x = int(0) self.x = int(0)
self.y = int(0) self.y = int(0)
self.width = thumby.display.width
self.height = thumby.display.height
self.offset = -self.height / 2
self.target = target self.target = target
def entity_in_camera(self, ent_x, ent_y, ent_width = 0, ent_height = 0) -> bool: def update_phys(self, plscreeny):
return ent_x + ent_width >= self.x and ent_y + ent_height >= self.y and ent_x < self.x + self.width and ent_y < self.y + self.height
def relative_to_camera(self, global_x, global_y):
return (global_x - self.x, global_y - self.y)
def update_phys(self):
# position player # position player
self.y = self.target.y + self.offset self.y = self.target.y - plscreeny
class PlayerAnimation: class PlayerAnimation:
MAX_ROOT_OFFSET = int(4) # width and height of range bounding box
RANDOM_RANGE = int(7)
POS_RANGE = int(3)
POS_X = int(0)
POS_Y = int(1)
MAX_GROW_DISTANCE = int(6)#int(3) def __init__(self, plworldx, plworldy):
MIN_GROW_DISTANCE = int(2) self.poslist = [(plworldx, plworldy)]
self.debugrandomrange = self.RANDOM_RANGE
self.debugposrange = self.POS_RANGE
Point = namedtuple("Point", ["x", "y"]) def update_phys(self, plworldx, plworldy, plscreeny, current_depth):
def __init__(self, player):
self.player = player
self.root_offset = self.MAX_ROOT_OFFSET #print(plworldx, plworldy, plscreeny, current_depth)
self.grow_distance = self.MAX_GROW_DISTANCE
self.points = [] ### exit if player y pos less than random range
self.add_point() # # debug change random range
# inp = int(0)
self.debug_visible = False # if thumby.buttonD.justPressed():
self.debug_exact = True # inp -= 1
# if thumby.buttonU.justPressed():
def add_point(self): # inp += 1
self.points.append(self.Point(self.player.x, self.player.y))
def update_phys(self, camera):
# debug visible
if thumby.buttonB.justPressed():
self.debug_visible = not self.debug_visible
# debug exact # if inp != 0:
if thumby.buttonU.justPressed(): # self.debugposrange = max(self.debugposrange + inp * 2, 1)
self.debug_exact = not self.debug_exact # print('debugposrange: ', self.debugposrange)
# exit if distance from player to last root point is less than gorw distance # # if plworldy < self.poslist[-1][self.POS_Y] + random.randint(1, self.debugposrange):
dist_sqr = ( # # return
(self.player.x - self.points[-1].x) ** 2 +
(self.player.y - self.points[-1].y) ** 2
)
if dist_sqr < self.grow_distance ** 2: # if plworldy < self.poslist[-1][self.POS_Y] + self.debugposrange:
# return
if plworldy < self.poslist[-1][self.POS_Y] + self.POS_RANGE:
return return
# set new grow distance ### get new position
self.grow_distance = random.randint(self.MIN_GROW_DISTANCE, self.MAX_GROW_DISTANCE) rad = self.RANDOM_RANGE // 2
# offset last root point to look random # # debug change random range
self.points[-1] = self.Point( # inp = int(0)
self.points[-1].x + random.randrange(-self.root_offset, self.root_offset), # if thumby.buttonB.justPressed():
self.points[-1].y + random.randrange(-self.root_offset, self.root_offset) # inp -= 1
# if thumby.buttonA.justPressed():
# inp += 1
# if inp != 0:
# self.debugrandomrange = max(self.debugrandomrange + inp * 2, 1)
# print('debugrandomrange:', self.debugrandomrange)
# rad = max(self.debugrandomrange // 2, 1)
# vector int
# self.poslist.append((plworldx + random.randrange(-rad, rad),
# plworldy + random.randrange(-rad, rad)))
# move last pos
self.poslist[-1] = (
plworldx + random.randrange(-rad, rad),
plworldy + random.randrange(-rad, rad)
) )
# new root point created exactly on player # new pos is perfectly on player
self.add_point() self.poslist.append((plworldx, plworldy))
# remove points in list that would make lines off the camera # # debug
for point in self.points[1:]: # self.poslist.append((plworldx, plworldy))
if camera.entity_in_camera(point.x, point.y):
break
self.points.pop(0)
def update_draw(self, camera, isdecend=1):
# debug render
if self.debug_visible:
pix_x, pix_y = camera.relative_to_camera(self.player.x, self.player.y)
thumby.display.setPixel(int(pix_x), int(pix_y), isdecend)
return
# root always reaches player position ### remove the fist item in the list if its full line is off screen
if self.debug_exact: if self.poslist[1][self.POS_Y] < plworldy - plscreeny:
x1, y1 = camera.relative_to_camera(self.player.x, self.player.y) self.poslist.pop(0)
x2, y2 = camera.relative_to_camera(self.points[-1].x, self.points[-1].y)
#print(len(self.poslist))
def update_draw(self, topleft, prev_depth, isdecend=1):
### draw line from first position to next
#print(topleft)
for i in range(len(self.poslist) - 1):
thumby.display.drawLine( thumby.display.drawLine(
int(x1), int(self.poslist[i][self.POS_X]),
int(y1), int(self.poslist[i][self.POS_Y] - topleft),
int(x2), int(self.poslist[i + 1][self.POS_X]),
int(y2), int(self.poslist[i + 1][self.POS_Y] - topleft),
isdecend
)
# draw line from first position to next
point_prev = self.points[-1]
for point in self.points[-2::-1]:
x1, y1 = camera.relative_to_camera(point_prev.x, point_prev.y)
x2, y2 = camera.relative_to_camera(point.x, point.y)
point_prev = point
thumby.display.drawLine(
int(x1),
int(y1),
int(x2),
int(y2),
isdecend isdecend
) )

View File

@@ -4,8 +4,7 @@ from random import randrange, random
import thumby import thumby
from Games.Throot.map import ObstacleSlice from Games.Throot.map import ObstacleSlice
from Games.Throot.player import Player, Camera from Games.Throot.artrepository import OBSTACLES
from Games.Throot.artrepository import *
class Entity: class Entity:
""" """
@@ -37,9 +36,9 @@ class Entity:
return False return False
rel_x = int(loc_x - self.x) rel_x = int(loc_x - self.x)
rel_y = int(loc_y - self.y) rel_y = int(loc_y - self.y)
return bool((self.collision[rel_x] >> (rel_y)) & 1) return bool((self.collision[rel_x] >> (self.height - rel_y)) & 1)
def can_render(self, camera: Camera) -> bool: def can_render(self, current_depth: int) -> bool:
""" """
Checks to see if this sprite is within the game screen. Checks to see if this sprite is within the game screen.
@@ -50,9 +49,15 @@ class Entity:
:return: :return:
True if the sprite shows up on screen in any capacity, false otherwise. True if the sprite shows up on screen in any capacity, false otherwise.
""" """
return camera.entity_in_camera(self.x, self.y, self.width, self.height) if self.x < -self.width or self.x >= thumby.display.width:
return False
max = current_depth
min = max - thumby.display.height
if self.y < (min - self.height) or self.y > max:
return False
return True
def update(self, tpf: float, player: Player, camera: Camera) -> bool: def update(self, tpf: float, current_depth: int, prev_depth: int) -> bool:
""" """
Updates the state of the entity based on the game state. Updates the state of the entity based on the game state.
@@ -67,9 +72,9 @@ class Entity:
True if the game should keep this entity, false if the game should drop True if the game should keep this entity, false if the game should drop
this entity from memory. this entity from memory.
""" """
return True return False
def render(self, tpf: float, camera: Camera) -> bool: def render(self, tpf: float, current_depth: int, prev_depth: int) -> bool:
""" """
Sets the position of the sprite and draws it. Sets the position of the sprite and draws it.
@@ -85,25 +90,16 @@ class Entity:
should be hidden. should be hidden.
""" """
if self.sprite: if self.sprite:
coordinates = camera.relative_to_camera(self.x, self.y) self.sprite.y = int(self.y - current_depth + thumby.display.height)
self.sprite.x = coordinates[0] self.sprite.x = int(self.x)
self.sprite.y = coordinates[1] if self.can_render(current_depth):
if self.can_render(camera):
thumby.display.drawSprite(self.sprite) thumby.display.drawSprite(self.sprite)
return True return True
return False return False
class Obstacle(Entity): class Obstacle(Entity):
def update(self, tpf: float, player: Player, camera: Camera) -> bool: def update(self, tpf: float, current_depth: int, prev_depth: int) -> bool:
return True return self.can_render(current_depth)
class Collectible(Entity):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.score = 'score' in kwargs and kwargs['score'] or 10
def update(self, tpf: float, player: Player, camera: Camera) -> bool:
return True
def update_entities(entities, difficulty: int, current_depth: int, prev_depth: int): def update_entities(entities, difficulty: int, current_depth: int, prev_depth: int):
@@ -131,26 +127,14 @@ def update_entities(entities, difficulty: int, current_depth: int, prev_depth: i
return set() return set()
spawned = set() spawned = set()
max_spawned = randrange(0, difficulty + 1) max_spawned = 1#randrange(0, difficulty + 1)
while len(spawned) < max_spawned and random() < (2 * math.pow(difficulty + 0.01, 2)) / (3 * math.pow(difficulty + 0.01, 2) + 20 * (difficulty + 0.01)) + 0.05: while len(spawned) < max_spawned and random() < (2 * math.pow(difficulty + 0.01, 2)) / (3 * math.pow(difficulty + 0.01, 2) + 20 * (difficulty + 0.01)) + 0.05:
if random() < 0.2: sprite_image = OBSTACLES[randrange(0, min((difficulty + 1) * 3, len(OBSTACLES)))]
Clazz = Collectible
if current_depth >= 0:
sprite_image = collectibleunder
else:
sprite_image = collectibleover
else:
Clazz = Obstacle
sprite_image = OBSTACLES[randrange(0, min((difficulty + 1) * 3, len(OBSTACLES)))]
if sprite_image.width >= thumby.display.width: if sprite_image.width >= thumby.display.width:
x = 0 x = 0
else: else:
x = randrange(0, thumby.display.width - sprite_image.width) x = randrange(0, thumby.display.width - sprite_image.width)
y = current_depth y = current_depth
if current_depth > prev_depth:
y += thumby.display.height // 2
elif current_depth < prev_depth:
y -= thumby.display.height // 2 + sprite_image.height
error = False error = False
for entity in entities | spawned: for entity in entities | spawned:
if x > (entity.x - OBSTACLE_BUFFER) and x <= (entity.x + entity.width + OBSTACLE_BUFFER) and y > (entity.y - OBSTACLE_BUFFER) and y <= (entity.y + entity.height + OBSTACLE_BUFFER): if x > (entity.x - OBSTACLE_BUFFER) and x <= (entity.x + entity.width + OBSTACLE_BUFFER) and y > (entity.y - OBSTACLE_BUFFER) and y <= (entity.y + entity.height + OBSTACLE_BUFFER):
@@ -158,5 +142,5 @@ def update_entities(entities, difficulty: int, current_depth: int, prev_depth: i
break break
if not error: if not error:
sprite = thumby.Sprite(sprite_image.width, sprite_image.height, sprite_image.image, 0, 0, 0, False, False) sprite = thumby.Sprite(sprite_image.width, sprite_image.height, sprite_image.image, 0, 0, 0, False, False)
spawned.add(Clazz(x, y, sprite_image.width, sprite_image.height, sprite_image.collision or sprite_image.image, sprite)) spawned.add(Obstacle(x, y, sprite_image.width, sprite_image.height, sprite_image.collision or sprite_image.image, sprite))
return spawned return spawned