diff --git a/Throot.py b/Throot.py index 2f07a60..3f51c61 100644 --- a/Throot.py +++ b/Throot.py @@ -30,29 +30,29 @@ class GameManager: self.room_index = min(self.room_index + 1, len(self.rooms)) else: self.room_index = index - + self.rooms[self.room_index].start(event) def restart(self): self.room_index = 0 - + def update(self, tpf): event = self.rooms[self.room_index].update(tpf) - + thumby.display.update() - + if event: self.room_goto(event) - + class Room: def __init__(self): self.ind_button = 0 - + def start(self, event): t0 = time.ticks_ms() # Get time (ms) thumby.display.fill(0) # Fill canvas to black - + def update(self, tpf): pass @@ -60,46 +60,47 @@ class Room: class RoomTitle(Room): def update(self, tpf): super().update(tpf) - + if thumby.inputPressed(): return { 'room': ROOM_MAIN } - + # draw title sprite #thumby.display.drawSprite(sprite_title.width, sprite_title.height, sprite_title.image) - - thumby.display.blit(sprite_title.image, 0, 0, sprite_title.width, sprite_title.height, + + thumby.display.blit(sprite_title.image, 0, 0, sprite_title.width, sprite_title.height, -1, 0, 0) - + # title text chrx = 5 chry = 7 buff = 2 - + x = 14#int(round(thumby.display.width / 2 - (chr_len * len(title_text)) / 2)) y = 40 - chry - buff#int(round(thumby.display.height / 2 - chr_len / 2)) - + 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, + + 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, + + # 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): + def __init__(self): self.diving_velocity = 10 self.diving_accel = 0 @@ -108,16 +109,16 @@ class GameLoop(Room): self.player = None self.camera = None - + 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( @@ -125,16 +126,20 @@ class GameLoop(Room): ) self.current_obstacle_slice = OBSTACLE_SLICES[0] self.player = Player() - self.player.yspeed = 10 + self.player.speed_y = 16 + self.player.isdecend = 1 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.finish_height = -self.finish_depth self.camera = Camera(self.player) + self.stage = DESCEND_STAGE + def get_obstacle_slice(self): return OBSTACLE_SLICES[randrange(0, len(OBSTACLE_SLICES))] - + def draw_sky(self): sky_x = 0 sky_y = -thumby.display.height @@ -150,46 +155,46 @@ class GameLoop(Room): 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), + int(x), int(y), sprite_water.width, sprite_water.height, - 0, + 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) @@ -201,8 +206,8 @@ class GameLoop(Room): """ super().update(tpf) - self.player.update_phys(tpf, self.camera) - + self.player.update_phys(tpf, self.stage in (DESCEND_STAGE, ASCEND_STAGE) and self.camera or None) + self.camera.update_phys() # Generate new entities as needed @@ -227,19 +232,27 @@ class GameLoop(Room): self.score += entity.score to_remove.add(entity) self.entities -= to_remove - - self.score += int(abs(self.player.ysub - self.player.prev_y) * 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) + self.score += int(abs(self.player.ysub - self.player.prev_y) * 10) + + # finish level + if self.stage == DESCEND_STAGE: + if self.player.y > self.finish_depth + int(thumby.display.height * 1.5): + self.stage = ASCEND_STAGE + self.player.ysub = 0 + self.player.prev_y = 0 + self.player.speed_y *= -1 + self.player.isdecend = 0 + elif self.stage == ASCEND_STAGE: + if self.player.y < self.finish_height - int(thumby.display.height * 1.5): + self.score += 20 + self.level += 1 + self.start({ + 'score': self.score, + 'level': self.level + }) + + thumby.display.fill(int(self.player.y < 0)) self.draw_sky() # Draw the entities for entity in self.entities: @@ -247,19 +260,19 @@ class GameLoop(Room): self.player.update_draw(self.camera) self.draw_water() - + thumby.display.drawText(str(self.score), thumby.display.width - (len(str(self.score)) + 2) * 5, 1, 1) - - + + 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 @@ -271,7 +284,7 @@ class EndRoom(Room): 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.start() diff --git a/constants.py b/constants.py index 2c386f5..5511e74 100644 --- a/constants.py +++ b/constants.py @@ -5,3 +5,13 @@ ROOM_MAIN = 1 ROOM_END = 2 ROOM_NEXT = -1 + +# The first part of the level, where we are reaching the water +DESCEND_STAGE = 0 +# The transition to the second part of the level, where the camera travels +# back to the surface +WATER_STAGE = 1 +# The second part of the level, where we are growing into the sky +ASCEND_STAGE = 2 +# The "level complete" animation +BLOOM_STAGE = 3 diff --git a/player.py b/player.py index d6a4452..6861a7b 100644 --- a/player.py +++ b/player.py @@ -15,10 +15,10 @@ class Input: thumby.buttonA.pressed() or thumby.buttonR.pressed() ) - + def move_left(self): return thumby.buttonL.pressed() - + inp = Input() ### classes @@ -30,77 +30,77 @@ class Player: self.ysub = float(0) self.x = int(self.xsub) self.y = int(self.ysub) - + self.isdecend = 1 - + self.input_x = float(0) - + # constants self.speed_x_start = 32 self.speed_x_max = 200 self.acc_x_start = 64 self.jerk_x = 128 - + self.speed_x = self.speed_x_start self.acc_x = self.acc_x_start - + # y speed is constant self.speed_y = 16 - + 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 input if inp.move_left() and (not inp.move_right()): self.input_x = -1 - + 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 self.speed_x += self.acc_x * tpf self.acc_x += self.jerk_x * tpf - + # limit speed self.speed_x = min(self.speed_x_max, self.speed_x) - + # 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 - + # 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 self.x = int(self.xsub) self.y = int(self.ysub) - + ### animation physics self.anim.update_phys(camera) - + def update_draw(self, camera): ### animation render self.anim.update_draw(camera, self.isdecend) - + class Camera: def __init__(self, target): @@ -110,106 +110,106 @@ class Camera: self.height = thumby.display.height self.offset = -self.height / 2 self.target = target - + def entity_in_camera(self, ent_x, ent_y, ent_width = 0, ent_height = 0) -> bool: 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 self.y = self.target.y + self.offset class PlayerAnimation: MAX_ROOT_OFFSET = int(4) - + MAX_GROW_DISTANCE = int(6)#int(3) MIN_GROW_DISTANCE = int(2) - + Point = namedtuple("Point", ["x", "y"]) def __init__(self, player): self.player = player - + self.root_offset = self.MAX_ROOT_OFFSET self.grow_distance = self.MAX_GROW_DISTANCE - + self.points = [] self.add_point() - + self.debug_visible = False self.debug_exact = True - + def add_point(self): 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 thumby.buttonU.justPressed(): self.debug_exact = not self.debug_exact - + # exit if distance from player to last root point is less than gorw distance dist_sqr = ( (self.player.x - self.points[-1].x) ** 2 + (self.player.y - self.points[-1].y) ** 2 ) - + if dist_sqr < self.grow_distance ** 2: return - + # set new grow distance self.grow_distance = random.randint(self.MIN_GROW_DISTANCE, self.MAX_GROW_DISTANCE) - + # offset last root point to look random self.points[-1] = self.Point( - self.points[-1].x + random.randrange(-self.root_offset, self.root_offset), + self.points[-1].x + random.randrange(-self.root_offset, self.root_offset), self.points[-1].y + random.randrange(-self.root_offset, self.root_offset) ) - + # new root point created exactly on player self.add_point() - + # remove points in list that would make lines off the camera for point in self.points[1:]: 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 if self.debug_exact: x1, y1 = camera.relative_to_camera(self.player.x, self.player.y) x2, y2 = camera.relative_to_camera(self.points[-1].x, self.points[-1].y) thumby.display.drawLine( int(x1), - int(y1), + int(y1), int(x2), int(y2), 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(y1), int(x2), int(y2), isdecend