1 Commits

Author SHA1 Message Date
Neal Finger
bf910238b9 level complete
water animations and start new level
2023-02-05 14:05:23 -07:00
4 changed files with 119 additions and 68 deletions

View File

@@ -7,12 +7,14 @@ 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, Camera from Games.Throot.player import Player
from Games.Throot.map import ObstacleSlice, OBSTACLE_SLICES from Games.Throot.map import ObstacleSlice, OBSTACLE_SLICES
from Games.Throot.sprites import Obstacle, update_entities from Games.Throot.sprites import Obstacle, update_entities
from Games.Throot.constants import * from Games.Throot.constants import *
class GameManager: class GameManager:
def __init__(self, *rooms): def __init__(self, *rooms):
self.rooms = rooms self.rooms = rooms
@@ -82,59 +84,117 @@ class GameLoop(Room):
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)
def start(self, event): def start(self, event):
self.current_depth = 0 self.current_depth = 0
self.entities.clear() self.entities.clear()
self.current_obstacle_slice = OBSTACLE_SLICES[0] self.current_obstacle_slice = OBSTACLE_SLICES[0]
self.player = Player() self.player = Player()
self.player.yspeed = 10
self.score = 0 self.score = 0
self.camera = Camera(self.player)
self.finish_depth = 100 + 5 * self.level ** 2
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_water(self):
# bookwater
# exit if depth is less than 2 screens of the finish depth
if self.current_depth + thumby.display.height * 2 < self.finish_depth:
return
self.waterx += sprite_water.width / CONST_FPS
if self.waterx > sprite_water.width:
self.waterx = 0
to_screeny = int(thumby.display.height - self.current_depth)
# water surface.
for i in range(math.ceil(thumby.display.width / sprite_water.width) + 1):
thumby.display.blit(
sprite_water[0],
(int(self.waterx) - sprite_water.width) + i * sprite_water.width,
self.finish_depth - sprite_water.height + to_screeny, sprite_water.width, sprite_water.height,
0, 0, 0
)
# water below surface
thumby.display.drawFilledRectangle(
0, self.finish_depth + to_screeny,
thumby.display.width, self.finish_depth + thumby.display.height * 3,
1,
)
# win text
if self.current_depth > self.finish_depth + (thumby.display.height // 2):
thumby.display.drawText('water reach!', 0, thumby.display.height // 2, 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.diving_velocity += self.diving_accel * tpf
self.current_depth += self.diving_velocity * tpf
self.player.update_phys(tpf, self.camera) #print("%f (%f)" % (self.current_depth, self.current_depth % ObstacleSlice.height))
self.camera.update_phys() #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
self.entities |= update_entities(self.entities, 5, self.player.ysub, self.player.prev_y) self.entities |= update_entities(self.entities, 5, self.current_depth, prev_depth)
# 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 { return {
'room': ROOM_END, 'room': ROOM_END,
'score': self.score 'score': self.score,
'level': self.level + 1
} }
self.entities -= to_remove self.entities -= to_remove
self.score = self.player.x#int(abs(self.player.y) * 10) self.score = int(self.current_depth * 10)
# finish level
if self.current_depth > self.finish_depth + int(thumby.display.height * 1.5):
self.level += 1
self.start({})
thumby.display.fill(0) thumby.display.fill(0)
# 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.draw_water()
self.player.update_draw(self.player.y)
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)
class EndRoom(Room): class EndRoom(Room):
def __init__(self): def __init__(self):
self.score = 0 self.score = 0

View File

@@ -175,4 +175,11 @@ Doritoobstaclelvl5 = SpriteImage(
30 30
) )
sprite_water = SpriteImage(
bytearray([224,240,240,248,252,248,240,240]),
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

@@ -17,8 +17,6 @@ CONST_INP_MOVE_RIGHT = thumby.buttonA
# classes # 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)
@@ -28,16 +26,14 @@ class Player:
self.isdecend = 1 self.isdecend = 1
self.xspeed = 32 / CONST_FPS self.xspeed = 32 / CONST_FPS
self.yspeed = 32 self.yspeed = 32 / CONST_FPS
self.accx = 1 / (CONST_FPS * 4) self.accx = 1 / (CONST_FPS * 4)
self.accaccx = self.accx self.accaccx = self.accx
self.anim = PlayerAnimation(self.x, self.y) self.anim = PlayerAnimation(self.x, self.y)
self.debugvisible = True self.debugvisible = True
def update_phys(self, tpf, camera: Camera): def update_phys(self, current_depth, prev_depth):
self.prev_x = self.xsub
self.prev_y = self.ysub
inp = int(0) inp = int(0)
if CONST_INP_MOVE_LEFT.pressed(): if CONST_INP_MOVE_LEFT.pressed():
inp -= 1 inp -= 1
@@ -62,44 +58,35 @@ class Player:
self.xsub = max(min(xx, thumby.display.width - 1), 0) self.xsub = max(min(xx, thumby.display.width - 1), 0)
### y movement ### y movement
self.ysub = current_depth - (thumby.display.height / 2)
self.ysub += self.yspeed * tpf
### 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 # animation
self.anim.update_phys(self.x, self.y, 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):
# debug draw # debug draw
if self.debugvisible: if self.debugvisible:
pix_x, pix_y = camera.relative_to_camera(self.x, self.y) pix_x = self.x
thumby.display.setPixel(int(pix_x), int(pix_y), self.isdecend) pix_y = int(self.y - plworldy + 20)
thumby.display.setPixel(pix_x, pix_y, self.isdecend)
# animation # animation
self.anim.update_draw(camera, self.isdecend) 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:
@@ -114,7 +101,7 @@ class PlayerAnimation:
self.debugrandomrange = self.RANDOM_RANGE self.debugrandomrange = self.RANDOM_RANGE
self.debugposrange = self.POS_RANGE self.debugposrange = self.POS_RANGE
def update_phys(self, plworldx, plworldy, camera): def update_phys(self, plworldx, plworldy, plscreeny, current_depth):
#print(plworldx, plworldy, plscreeny, current_depth) #print(plworldx, plworldy, plscreeny, current_depth)
@@ -136,7 +123,7 @@ class PlayerAnimation:
# if plworldy < self.poslist[-1][self.POS_Y] + self.debugposrange: # if plworldy < self.poslist[-1][self.POS_Y] + self.debugposrange:
# return # return
if plworldy < self.poslist[-1][self.POS_Y] + self.POS_RANGE: if plworldy < self.poslist[-1][self.POS_Y] + random.randint(1, self.POS_RANGE):
return return
### get new position ### get new position
@@ -172,23 +159,21 @@ class PlayerAnimation:
# self.poslist.append((plworldx, plworldy)) # self.poslist.append((plworldx, plworldy))
### remove the fist item in the list if its full line is off screen ### remove the fist item in the list if its full line is off screen
if not camera.entity_in_camera(self.poslist[1][self.POS_X], self.poslist[1][self.POS_Y]): if self.poslist[1][self.POS_Y] < plworldy - plscreeny:
self.poslist.pop(0) self.poslist.pop(0)
#print(len(self.poslist)) #print(len(self.poslist))
def update_draw(self, camera, isdecend=1): def update_draw(self, topleft, prev_depth, isdecend=1):
### draw line from first position to next ### draw line from first position to next
#print(topleft) #print(topleft)
for i in range(len(self.poslist) - 1): for i in range(len(self.poslist) - 1):
x1, y1 = camera.relative_to_camera(self.poslist[i][self.POS_X], self.poslist[i][self.POS_Y])
x2, y2 = camera.relative_to_camera(self.poslist[i + 1][self.POS_X], self.poslist[i + 1][self.POS_Y])
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 isdecend
) )

View File

@@ -4,7 +4,6 @@ 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 OBSTACLES
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.
@@ -69,7 +74,7 @@ class Entity:
""" """
return False 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,17 +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)
def update_entities(entities, difficulty: int, current_depth: int, prev_depth: int): def update_entities(entities, difficulty: int, current_depth: int, prev_depth: int):
@@ -123,7 +127,7 @@ 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:
sprite_image = OBSTACLES[randrange(0, min((difficulty + 1) * 3, len(OBSTACLES)))] 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:
@@ -131,10 +135,6 @@ def update_entities(entities, difficulty: int, current_depth: int, prev_depth: i
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):
@@ -142,6 +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)
print("Adding obstacle of to (%d, %d) of size (%d, %d)" % (x, y, sprite_image.width, sprite_image.height))
spawned.add(Obstacle(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