diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index dba7ff9..b5472fd 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -20,7 +20,7 @@ jobs:
python-version: '3.10'
- run: pip install -r requirements.txt
- run: pyinstaller --noconsole creeper_adventure/creeper.py
- - run: cp -r assets dist/creeper
+ - run: cp -r creeper_adventure/assets dist/creeper
- name: Step 3 - Use the Upload Artifact GitHub Action
uses: actions/upload-artifact@v2
with:
@@ -82,9 +82,9 @@ jobs:
git add .
git commit -m "Bump version: `hatch version`"
git tag v$NEW_VERSION
- git push
- git push --tags
+ git push --force
+ git push --tags --force
- name: publish
run: |
- runner hatch build
- runner hatch publish
+ hatch build
+ hatch publish
diff --git a/.gitignore b/.gitignore
index 1930ca0..26750c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -956,3 +956,4 @@ FodyWeavers.xsd
# Additional files built by Visual Studio
# End of https://www.toptal.com/developers/gitignore/api/vim,node,data,emacs,python,pycharm,executable,sublimetext,visualstudio,visualstudiocode
+.null-ls_574349_tetris.py
diff --git a/README.md b/README.md
index 0319349..6a25d87 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,10 @@
+
+
+
# creeper-adventure
+Creeper adventure is a top down 2d adventure game that I am building with my son using pygame.
+
[](https://pypi.org/project/creeper-adventure)
[](https://pypi.org/project/creeper-adventure)
@@ -16,6 +21,31 @@
pip install creeper-adventure
```
+## Running
+
+```console
+# run the game
+creeper-adventure
+
+# run in debug mode
+creeper-adventure --debug
+
+# see all options available
+creeper-adventure --help
+```
+
+## running with pipx
+
+`creeper-adventure` can also run using pipx if you have pipx installed.
+
+```console
+pipx run creeper-adventure
+```
+
+## Download an exe
+
+pre-built binaries are available from the website [creeper-adventure](https://creeper-adventure.waylonwalker.com).
+
## License
`creeper-adventure` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
diff --git a/aforest.py b/aforest.py
new file mode 100644
index 0000000..47db5fe
--- /dev/null
+++ b/aforest.py
@@ -0,0 +1,108 @@
+import math
+
+import noise
+import pygame
+
+# set up pygame
+pygame.init()
+
+# set up the window
+screen_width, screen_height = 1280, 800
+screen = pygame.display.set_mode((screen_width, screen_height))
+# set up the noise surface
+noise_width, noise_height = 1280, 800
+noise_scale = 10
+noise_width = int(screen_width / noise_scale)
+noise_height = int(screen_height / noise_scale)
+# noise_width, noise_height = round(1280 / 10), round(800 / 10)
+noise_surface = pygame.Surface((noise_width, noise_height))
+# set up the colors
+BLACK = (0, 0, 0)
+GREEN = (0, 255, 0)
+BLUE = (0, 0, 255)
+colors = [(0, 128, 0), (0, 64, 0), (0, 32, 0)]
+# set up the noise
+octaves = 1
+freq = 256 * octaves
+# generate the noise
+noise_map = [
+ [
+ noise.pnoise2(x / freq, y / freq, octaves, repeatx=3024, repeaty=3024)
+ for x in range(screen_width)
+ ]
+ for y in range(screen_height)
+]
+# set up the clock
+clock = pygame.time.Clock()
+# set up the animation
+frame = 0
+# set up the noise surface cache
+noise_surface_cache = []
+# set up the noise surface cache index
+noise_surface_cache_index = 0
+# set up the noise surface cache size
+noise_surface_cache_size = 120
+# set up the noise surface cache
+for i in range(noise_surface_cache_size):
+
+ print(f"caching {i} of {noise_surface_cache_size}")
+ # draw the background
+ for y in range(noise_height):
+ for x in range(noise_width):
+ color = colors[2]
+ if (
+ noise_map[y * noise_scale][x * noise_scale]
+ + math.sin(i * (3.14 / noise_surface_cache_size))
+ > 0.2
+ ):
+ color = colors[1]
+ elif (
+ noise_map[y * noise_scale][x * noise_scale]
+ + math.sin(i * (3.14 / noise_surface_cache_size))
+ < 0.15
+ ):
+ color = colors[0]
+ noise_surface.set_at((x, y), color)
+ # cache the noise surface
+
+ noise_surface_scaled = pygame.transform.scale(
+ noise_surface, (screen_width, screen_height)
+ )
+ # noise_surface_scaled_pil = Image.frombytes(
+ # "RGB",
+ # (screen_width, screen_height),
+ # pygame.image.tostring(noise_surface_scaled, "RGB", False),
+ # )
+
+ # noise_surface_scaled_pil = noise_surface_scaled_pil.filter(
+ # ImageFilter.GaussianBlur(radius=20)
+ # )
+ # noise_surface_scaled = pygame.image.fromstring(
+ # noise_surface_scaled_pil.tobytes(), (screen_width, screen_height), "RGB"
+ # )
+ noise_surface_cache.append(noise_surface_scaled)
+
+noise_surface_cache.extend([n for n in noise_surface_cache[::-1]])
+# main loop
+running = True
+print("running")
+while running:
+ # keep loop running at the right speed
+ clock.tick(60)
+ # process events
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ running = False
+ # increment the frame
+ frame += 0.2
+ if frame > noise_surface_cache_size - 1:
+ frame = 0
+ # draw the noise surface onto the screen
+ screen.blit(noise_surface_cache[int(frame)], (0, 0))
+
+ # display the fps on the screen
+ pygame.display.set_caption(str(clock.get_fps()))
+ # update the display
+ pygame.display.flip()
+
+pygame.quit()
diff --git a/camera.py b/camera.py
new file mode 100644
index 0000000..807f8e7
--- /dev/null
+++ b/camera.py
@@ -0,0 +1,180 @@
+"""Basic showcase on how the transform property on SpaceDebugDrawOptions can
+be used as a camera to allow panning. Use arrows to move the camera.
+"""
+
+__docformat__ = "reStructuredText"
+
+import random
+import sys
+
+import pygame
+import pymunk.pygame_util
+from pymunk.vec2d import Vec2d
+
+random.seed(0)
+
+
+def main():
+ pygame.init()
+ screen = pygame.display.set_mode((600, 600))
+ clock = pygame.time.Clock()
+ running = True
+ font = pygame.font.Font(None, 16)
+ text = font.render(
+ "Use Arrows (up, down, left, right) to move the camera, "
+ "a and z to zoom in / out and s and x to rotate.",
+ True,
+ pygame.Color("black"),
+ )
+
+ ### Physics stuff
+ space = pymunk.Space()
+ space.gravity = Vec2d(0.0, 900.0)
+ draw_options = pymunk.pygame_util.DrawOptions(screen)
+
+ ## Balls
+ balls = []
+
+ body = pymunk.Body()
+ body.position = pymunk.Vec2d(407, 354)
+ s1 = pymunk.Segment(body, Vec2d(-300, -30), Vec2d(0, 0), 1.0)
+ s2 = pymunk.Segment(body, Vec2d(0, 0), Vec2d(0, -100), 1.0)
+ s1.density = 0.1
+ s2.density = 0.1
+ s1.friction = 1
+ s2.friction = 1
+ space.add(body, s1, s2)
+
+ c1 = pymunk.constraints.DampedSpring(
+ space.static_body,
+ body,
+ (427, 200),
+ (0, -100),
+ Vec2d(407, 254).get_distance((427, 200)),
+ 2000,
+ 100,
+ )
+
+ c2 = pymunk.constraints.DampedSpring(
+ space.static_body,
+ body,
+ (87, 200),
+ (-300, -30),
+ Vec2d(107, 324).get_distance((87, 200)),
+ 2000,
+ 100,
+ )
+ space.add(c1, c2)
+
+ # extra to show how constraints are drawn when very small / large
+ body = pymunk.Body(1, 100)
+ body.position = 450, 305
+ c3 = pymunk.constraints.DampedSpring(
+ space.static_body, body, (450, 300), (0, 0), 5, 1000, 100
+ )
+ space.add(body, c3)
+ body = pymunk.Body(1, 100)
+ body.position = 500, 2025
+ c3 = pymunk.constraints.DampedSpring(
+ space.static_body, body, (500, 25), (0, 0), 2000, 1000, 100
+ )
+ space.add(body, c3)
+
+ ticks_to_next_ball = 10
+
+ translation = pymunk.Transform()
+ scaling = 1
+ rotation = 0
+
+ while running:
+ for event in pygame.event.get():
+ if (
+ event.type == pygame.QUIT
+ or event.type == pygame.KEYDOWN
+ and event.key == pygame.K_ESCAPE
+ ):
+ running = False
+ elif event.type == pygame.KEYDOWN and event.key == pygame.K_p:
+ pygame.image.save(screen, "camera.png")
+
+ keys = pygame.key.get_pressed()
+ left = int(keys[pygame.K_LEFT])
+ up = int(keys[pygame.K_UP])
+ down = int(keys[pygame.K_DOWN])
+ right = int(keys[pygame.K_RIGHT])
+
+ zoom_in = int(keys[pygame.K_a])
+ zoom_out = int(keys[pygame.K_z])
+ rotate_left = int(keys[pygame.K_s])
+ rotate_right = int(keys[pygame.K_x])
+
+ translate_speed = 10
+ translation = translation.translated(
+ translate_speed * left - translate_speed * right,
+ translate_speed * up - translate_speed * down,
+ )
+
+ zoom_speed = 0.1
+ scaling *= 1 + (zoom_speed * zoom_in - zoom_speed * zoom_out)
+
+ rotation_speed = 0.1
+ rotation += rotation_speed * rotate_left - rotation_speed * rotate_right
+
+ # to zoom with center of screen as origin we need to offset with
+ # center of screen, scale, and then offset back
+ draw_options.transform = (
+ pymunk.Transform.translation(300, 300)
+ @ pymunk.Transform.scaling(scaling)
+ @ translation
+ @ pymunk.Transform.rotation(rotation)
+ @ pymunk.Transform.translation(-300, -300)
+ )
+
+ ticks_to_next_ball -= 1
+ if ticks_to_next_ball <= 0:
+ ticks_to_next_ball = 100
+ mass = 10
+ radius = 25
+ inertia = pymunk.moment_for_circle(mass, 0, radius, (0, 0))
+ body = pymunk.Body(mass, inertia)
+ x = random.randint(115, 350)
+ body.position = x, 100
+ if random.random() > 0.5:
+ shape = pymunk.Circle(body, radius)
+ else:
+ shape = pymunk.Poly.create_box(
+ body, size=(radius * 2, radius * 2), radius=2
+ )
+ shape.friction = 1
+ space.add(body, shape)
+ balls.append(shape)
+
+ ### Clear screen
+ screen.fill(pygame.Color("white"))
+
+ ### Draw stuff
+ space.debug_draw(draw_options)
+
+ balls_to_remove = []
+ for ball in balls:
+ if ball.body.position.y > 500:
+ balls_to_remove.append(ball)
+
+ for ball in balls_to_remove:
+ space.remove(ball, ball.body)
+ balls.remove(ball)
+
+ screen.blit(text, (5, 5))
+
+ ### Update physics
+ dt = 1.0 / 60.0
+ space.step(dt)
+
+ ### Flip screen
+ pygame.display.flip()
+ clock.tick(50)
+ pygame.display.set_caption("fps: " + str(clock.get_fps()))
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/constraints.py b/constraints.py
new file mode 100644
index 0000000..a9d518d
--- /dev/null
+++ b/constraints.py
@@ -0,0 +1,250 @@
+"""
+Pymunk constraints demo. Showcase of all the constraints included in Pymunk.
+
+Adapted from the Chipmunk Joints demo:
+https://github.com/slembcke/Chipmunk2D/blob/master/demo/Joints.c
+"""
+
+import inspect
+import math
+
+import pygame
+import pymunk.pygame_util
+from pymunk.vec2d import Vec2d
+
+pygame.init()
+screen = pygame.display.set_mode((1200, 600))
+clock = pygame.time.Clock()
+font = pygame.font.Font(None, 24)
+
+
+help_txt = font.render(
+ "Pymunk constraints demo. Use mouse to drag/drop. Hover to see descr.",
+ True,
+ pygame.Color("darkgray"),
+)
+
+space = pymunk.Space()
+space.gravity = (0.0, 900.0)
+draw_options = pymunk.pygame_util.DrawOptions(screen)
+
+# containers
+box_size = 200
+w = screen.get_width()
+h = screen.get_height()
+for i in range(6):
+ sw = pymunk.Segment(space.static_body, (0, i * box_size), (w, i * box_size), 1)
+ sw.friction = 1
+ sw.elasticity = 1
+ sh = pymunk.Segment(
+ space.static_body, (i * box_size, 0), (i * box_size, h - box_size), 1
+ )
+ sh.friction = 1
+ sh.elasticity = 1
+ space.add(sw, sh)
+
+
+def add_ball(space, pos, box_offset):
+ body = pymunk.Body()
+ body.position = Vec2d(*pos) + box_offset
+ shape = pymunk.Circle(body, 20)
+ shape.mass = 1
+ shape.friction = 0.7
+ space.add(body, shape)
+ return body
+
+
+def add_bar(space, pos, box_offset):
+ body = pymunk.Body()
+ body.position = Vec2d(*pos) + box_offset
+ shape = pymunk.Segment(body, (0, 40), (0, -40), 6)
+ shape.mass = 2
+ shape.friction = 0.7
+ space.add(body, shape)
+ return body
+
+
+def add_lever(space, pos, box_offset):
+ body = pymunk.Body()
+ body.position = pos + Vec2d(*box_offset) + (0, -20)
+ shape = pymunk.Segment(body, (0, 20), (0, -20), 5)
+ shape.mass = 1
+ shape.friction = 0.7
+ space.add(body, shape)
+ return body
+
+
+def main():
+ txts = {}
+
+ box_offset = 0, 0
+ b1 = add_ball(space, (50, 60), box_offset)
+ b2 = add_ball(space, (150, 60), box_offset)
+ c: pymunk.Constraint = pymunk.PinJoint(b1, b2, (20, 0), (-20, 0))
+ txts[box_offset] = inspect.getdoc(c)
+ space.add(c)
+
+ box_offset = box_size, 0
+ b1 = add_ball(space, (50, 60), box_offset)
+ b2 = add_ball(space, (150, 60), box_offset)
+ c = pymunk.SlideJoint(b1, b2, (20, 0), (-20, 0), 40, 80)
+ txts[box_offset] = inspect.getdoc(c)
+ space.add(c)
+
+ box_offset = box_size * 2, 0
+ b1 = add_ball(space, (50, 60), box_offset)
+ b2 = add_ball(space, (150, 60), box_offset)
+ c = pymunk.PivotJoint(b1, b2, Vec2d(*box_offset) + (100, 60))
+ txts[box_offset] = inspect.getdoc(c)
+ space.add(c)
+
+ box_offset = box_size * 3, 0
+ b1 = add_ball(space, (50, 60), box_offset)
+ b2 = add_ball(space, (150, 60), box_offset)
+ c = pymunk.GrooveJoint(b1, b2, (50, 50), (50, -50), (-50, 0))
+ txts[box_offset] = inspect.getdoc(c)
+ space.add(c)
+
+ box_offset = box_size * 4, 0
+ b1 = add_ball(space, (50, 60), box_offset)
+ b2 = add_ball(space, (150, 60), box_offset)
+ c = pymunk.DampedSpring(b1, b2, (30, 0), (-30, 0), 20, 5, 0.3)
+ txts[box_offset] = inspect.getdoc(c)
+ space.add(c)
+
+ box_offset = box_size * 5, 0
+ b1 = add_bar(space, (50, 80), box_offset)
+ b2 = add_bar(space, (150, 80), box_offset)
+ # Add some joints to hold the circles in place.
+ space.add(pymunk.PivotJoint(b1, space.static_body, (50, 80) + Vec2d(*box_offset)))
+ space.add(pymunk.PivotJoint(b2, space.static_body, (150, 80) + Vec2d(*box_offset)))
+ c = pymunk.DampedRotarySpring(b1, b2, 0, 3000, 60)
+ txts[box_offset] = inspect.getdoc(c)
+ space.add(c)
+
+ box_offset = 0, box_size
+ b1 = add_lever(space, (50, 100), box_offset)
+ b2 = add_lever(space, (150, 100), box_offset)
+ # Add some joints to hold the circles in place.
+ space.add(pymunk.PivotJoint(b1, space.static_body, (50, 100) + Vec2d(*box_offset)))
+ space.add(pymunk.PivotJoint(b2, space.static_body, (150, 100) + Vec2d(*box_offset)))
+ # Hold their rotation within 90 degrees of each other.
+ c = pymunk.RotaryLimitJoint(b1, b2, math.pi / 2, math.pi / 2)
+ txts[box_offset] = inspect.getdoc(c)
+ space.add(c)
+
+ box_offset = box_size, box_size
+ b1 = add_lever(space, (50, 100), box_offset)
+ b2 = add_lever(space, (150, 100), box_offset)
+ # Add some pin joints to hold the circles in place.
+ space.add(pymunk.PivotJoint(b1, space.static_body, (50, 100) + Vec2d(*box_offset)))
+ space.add(pymunk.PivotJoint(b2, space.static_body, (150, 100) + Vec2d(*box_offset)))
+ # Ratchet every 90 degrees
+ c = pymunk.RatchetJoint(b1, b2, 0, math.pi / 2)
+ txts[box_offset] = inspect.getdoc(c)
+ space.add(c)
+
+ box_offset = box_size * 2, box_size
+ b1 = add_bar(space, (50, 100), box_offset)
+ b2 = add_bar(space, (150, 100), box_offset)
+ # Add some pin joints to hold the circles in place.
+ space.add(pymunk.PivotJoint(b1, space.static_body, (50, 100) + Vec2d(*box_offset)))
+ space.add(pymunk.PivotJoint(b2, space.static_body, (150, 100) + Vec2d(*box_offset)))
+ # Force one to sping 2x as fast as the other
+ c = pymunk.GearJoint(b1, b2, 0, 2)
+ txts[box_offset] = inspect.getdoc(c)
+ space.add(c)
+
+ box_offset = box_size * 3, box_size
+ b1 = add_bar(space, (50, 100), box_offset)
+ b2 = add_bar(space, (150, 100), box_offset)
+ # Add some pin joints to hold the circles in place.
+ space.add(pymunk.PivotJoint(b1, space.static_body, (50, 100) + Vec2d(*box_offset)))
+ space.add(pymunk.PivotJoint(b2, space.static_body, (150, 100) + Vec2d(*box_offset)))
+ # Make them spin at 1/2 revolution per second in relation to each other.
+ c = pymunk.SimpleMotor(b1, b2, math.pi)
+ txts[box_offset] = inspect.getdoc(c)
+ space.add(c)
+
+ # TODO add one or two advanced constraints examples, such as a car or rope
+
+ mouse_joint = None
+ mouse_body = pymunk.Body(body_type=pymunk.Body.KINEMATIC)
+
+ # Build rendered help texts
+ box_texts = {}
+ for k in txts:
+ l = 0
+ box_texts[k] = []
+ # Only take the first 5 lines.
+ for line in txts[k].splitlines()[:5]:
+ txt = font.render(line, True, pygame.Color("black"))
+ box_texts[k].append(txt)
+
+ while True:
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ exit()
+ elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
+ exit()
+ elif event.type == pygame.MOUSEBUTTONDOWN:
+ if mouse_joint is not None:
+ space.remove(mouse_joint)
+ mouse_joint = None
+
+ p = Vec2d(*event.pos)
+ hit = space.point_query_nearest(p, 5, pymunk.ShapeFilter())
+ if hit is not None and hit.shape.body.body_type == pymunk.Body.DYNAMIC:
+ shape = hit.shape
+ # Use the closest point on the surface if the click is outside
+ # of the shape.
+ if hit.distance > 0:
+ nearest = hit.point
+ else:
+ nearest = p
+ mouse_joint = pymunk.PivotJoint(
+ mouse_body,
+ shape.body,
+ (0, 0),
+ shape.body.world_to_local(nearest),
+ )
+ mouse_joint.max_force = 50000
+ mouse_joint.error_bias = (1 - 0.15) ** 60
+ space.add(mouse_joint)
+
+ elif event.type == pygame.MOUSEBUTTONUP:
+ if mouse_joint is not None:
+ space.remove(mouse_joint)
+ mouse_joint = None
+
+ screen.fill(pygame.Color("white"))
+
+ screen.blit(help_txt, (5, screen.get_height() - 20))
+
+ mouse_pos = pygame.mouse.get_pos()
+
+ # Display help message
+ x = mouse_pos[0] // box_size * box_size
+ y = mouse_pos[1] // box_size * box_size
+
+ if (x, y) in box_texts:
+ txts = box_texts[(x, y)]
+ i = 0
+ for txt in txts:
+ pos = (5, box_size * 2 + 10 + i * 20)
+ screen.blit(txt, pos)
+ i += 1
+
+ mouse_body.position = mouse_pos
+
+ space.step(1.0 / 60)
+
+ space.debug_draw(draw_options)
+ pygame.display.flip()
+
+ clock.tick(60)
+ pygame.display.set_caption(f"fps: {clock.get_fps()}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/creeper_adventure/__about__.py b/creeper_adventure/__about__.py
index b5a47bc..79f884d 100644
--- a/creeper_adventure/__about__.py
+++ b/creeper_adventure/__about__.py
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023-present Waylon S. Walker
#
# SPDX-License-Identifier: MIT
-__version__ = '1.0.0'
+__version__ = '11.0.0'
diff --git a/creeper_adventure/assets/bee/idle/1.png b/creeper_adventure/assets/bee/idle/1.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/assets/creeper/idle/1.png b/creeper_adventure/assets/creeper/idle/1.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/assets/creeper/idle/2.png b/creeper_adventure/assets/creeper/idle/2.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/assets/creeper/idle/3.png b/creeper_adventure/assets/creeper/idle/3.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/assets/creeper/idle/4.png b/creeper_adventure/assets/creeper/idle/4.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/assets/creeper/idle/5.png b/creeper_adventure/assets/creeper/idle/5.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/assets/leaf.png b/creeper_adventure/assets/leaf.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/assets/oak_trees/1.png b/creeper_adventure/assets/oak_trees/1.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/assets/oak_trees/2.png b/creeper_adventure/assets/oak_trees/2.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/assets/oak_trees/4.png b/creeper_adventure/assets/oak_trees/4.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/assets/oak_trees/5.png b/creeper_adventure/assets/oak_trees/5.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/assets/oak_trees/6.png b/creeper_adventure/assets/oak_trees/6.png
new file mode 100644
index 0000000..908228e
Binary files /dev/null and b/creeper_adventure/assets/oak_trees/6.png differ
diff --git a/creeper_adventure/assets/plank-bottom.png b/creeper_adventure/assets/plank-bottom.png
new file mode 100644
index 0000000..bf2efab
Binary files /dev/null and b/creeper_adventure/assets/plank-bottom.png differ
diff --git a/creeper_adventure/assets/plank-top.png b/creeper_adventure/assets/plank-top.png
new file mode 100644
index 0000000..18e5437
Binary files /dev/null and b/creeper_adventure/assets/plank-top.png differ
diff --git a/creeper_adventure/assets/spotlight.png b/creeper_adventure/assets/spotlight.png
old mode 100755
new mode 100644
diff --git a/creeper_adventure/cli/__init__.py b/creeper_adventure/cli/__init__.py
index 76b12d9..b7e0d8e 100644
--- a/creeper_adventure/cli/__init__.py
+++ b/creeper_adventure/cli/__init__.py
@@ -15,5 +15,4 @@ from ..creeper import main
@click.pass_context
@click.option("--debug", is_flag=True, help="start with the debug menu open")
def creeper_adventure(ctx: click.Context, debug):
- print(debug)
main(debug)
diff --git a/creeper_adventure/creeper.py b/creeper_adventure/creeper.py
old mode 100755
new mode 100644
index e90309d..f9eccd1
--- a/creeper_adventure/creeper.py
+++ b/creeper_adventure/creeper.py
@@ -1,45 +1,91 @@
-import random
from copy import copy
from itertools import cycle, repeat
from pathlib import Path
+import random
+from typing import List, Optional
-import pygame
from more_itertools import flatten
+from pydantic import BaseModel
+import pygame
from creeper_adventure.game import Game
ASSETS = Path(__file__).parent / "assets"
+class Button:
+ def __init__(self, game, surf, text, x, y, w, h, on_click=lambda: ...):
+ self.game = game
+ self.surf = surf
+ self.text = text
+ self.x = x
+ self.y = y
+ self.w = w
+ self.h = h
+ self.on_click = on_click
+ self.font = pygame.font.SysFont(None, 32)
+
+ def draw(self):
+ pygame.draw.rect(self.surf, (255, 255, 255), (self.x, self.y, self.w, self.h))
+ label = self.font.render(self.text, True, (0, 0, 0))
+ label_rect = label.get_rect()
+ label_rect.center = (self.x + self.w / 2, self.y + self.h / 2)
+ self.surf.blit(label, label_rect)
+ for event in self.game.events:
+ if event.type == pygame.MOUSEBUTTONDOWN:
+ if self.is_clicked(event.pos):
+ self.on_click()
+ # elif quit_button.is_clicked(event.pos):
+ # running = False
+ # if self.is_clicked:
+ # self.on_click()
+
+ def is_clicked(self, pos):
+ if pos[0] > self.x and pos[0] < self.x + self.w:
+ if pos[1] > self.y and pos[1] < self.y + self.h:
+ return True
+ return False
+
+
class MouseSprite:
- def __init__(self, surf, hotbar):
+ def __init__(self, game, surf, hotbar):
+ self.game = game
self.surf = surf
self.hotbar = hotbar
@property
def mouse_pos(self):
- return [i - 2 for i in pygame.mouse.get_pos()]
+ return (
+ pygame.mouse.get_pos()[0] - 2 - self.game.camera[0],
+ pygame.mouse.get_pos()[1] - 2 - self.game.camera[1],
+ )
@property
def rect(self):
return pygame.Rect(self.mouse_pos, (4, 4))
def get_nearest_block_pos(self):
- return ([i - (i % 16) for i in pygame.mouse.get_pos()],)
+ return (
+ [
+ self.mouse_pos[0] - (self.mouse_pos[0] % 16),
+ self.mouse_pos[1] - (self.mouse_pos[1] % 16),
+ ],
+ )
def draw(self):
if self.hotbar.selected.type is None:
pygame.draw.rect(self.surf, (255, 0, 0), self.rect)
else:
- self.img = pygame.image.load(ASSETS / f"{self.hotbar.selected.type}.png")
+ self.img = self.game.get_img(self.hotbar.selected.type).convert()
self.surf.blit(
- pygame.transform.scale(self.img, (16, 16)),
+ pygame.transform.scale(self.img, (64, 64)),
self.get_nearest_block_pos(),
)
class TreeSprite:
- def __init__(self, tree, x, y, scale, flip, surf):
+ def __init__(self, game, tree, x, y, scale, flip, surf):
+ self.game = game
self.image = tree
self.health = 100
self.x = x
@@ -47,7 +93,7 @@ class TreeSprite:
self.scale = scale
self.flip = flip
self.surf = surf
- self.leafs = [Leaf(self, self.surf, (x + 25, y + 25)) for i in range(2)]
+ self.leafs = [Leaf(self.game, self.surf, (x + 25, y + 25)) for i in range(2)]
self.shaking = 0
def shake(self):
@@ -57,7 +103,7 @@ class TreeSprite:
self.leafs.extend(
[
Leaf(
- self,
+ self.game,
self.surf,
(
self.x + 25 + random.randint(-10, 10),
@@ -115,7 +161,11 @@ class HotBar:
self.game = game
self.items = [
HotBarItem(
- game=self, surf=self.ui, pos=pos, scale=self.scale, margin=self.margin
+ game=self.game,
+ surf=self.ui,
+ pos=pos,
+ scale=self.scale,
+ margin=self.margin,
)
for pos in range(num)
]
@@ -164,6 +214,7 @@ class HotBarItem:
self.selected = False
self.surf.fill((0, 0, 0))
self.type = None
+ self.font = pygame.font.SysFont(None, self.scale)
def draw(self):
self.surf.fill((0, 0, 0, 60))
@@ -171,7 +222,7 @@ class HotBarItem:
self.surf.fill((185, 185, 205, 60))
if self.type:
- self.img = pygame.image.load(ASSETS / f"{self.type}.png")
+ self.img = self.game.get_img(self.type)
self.ui.blit(
pygame.transform.scale(
self.img,
@@ -179,9 +230,8 @@ class HotBarItem:
),
(self.pos * self.scale + self.margin, self.margin),
)
- font = pygame.font.SysFont(None, self.scale)
- qty = str(self.game.game.inventory[self.type])
- img = font.render(qty, True, (255, 255, 255))
+ qty = str(self.game.inventory[self.type])
+ img = self.font.render(qty, True, (255, 255, 255))
self.ui.blit(
pygame.transform.scale(img, (self.scale * 0.6, self.scale * 0.6)),
(self.pos * self.scale + self.margin, self.margin),
@@ -208,10 +258,12 @@ class Menu:
self.scale = scale
self.margin = margin
self.alpha = alpha
+ self.font = pygame.font.SysFont(None, 20)
+ self.surf = pygame.Surface(self.game.screen.get_size()).convert_alpha()
def draw(self):
if self.is_open:
- self.surf = pygame.Surface(self.game.screen.get_size()).convert_alpha()
+ # self.surf = pygame.Surface(self.game.screen.get_size()).convert_alpha()
self.surf.fill((0, 0, 0, self.alpha))
font = pygame.font.SysFont(None, self.scale)
img = font.render(self.title, True, (255, 255, 255))
@@ -231,7 +283,6 @@ class Menu:
class DebugMenu(Menu):
def __init__(self, game):
super().__init__(title="Debug Menu", game=game, alpha=0)
- self.font = pygame.font.SysFont(None, 20)
def _draw(self):
self.surf.blit(
@@ -262,6 +313,54 @@ class DebugMenu(Menu):
),
(10, 80),
)
+ self.surf.blit(
+ self.font.render(
+ f"fps: {round(self.game.clock.get_fps())}",
+ True,
+ (255, 255, 255),
+ ),
+ (10, 95),
+ )
+ self.surf.blit(
+ self.font.render(
+ f"fullscreen: {pygame.FULLSCREEN}",
+ True,
+ (255, 255, 255),
+ ),
+ (10, 110),
+ )
+ self.surf.blit(
+ self.font.render(
+ f"elapsed: {self.game.elapsed}",
+ True,
+ (255, 255, 255),
+ ),
+ (10, 125),
+ )
+
+
+class MainMenu(Menu):
+ def __init__(self, game, title):
+ super().__init__(game=game, title=title)
+ self.set_button_text()
+
+ @property
+ def start_button(self):
+ w = 200
+ h = 50
+ x = self.game.screen.get_size()[0] / 2 - w / 2
+ y = 300
+ return Button(self.game, self.surf, self.button_text, x, y, w, h, self.toggle)
+
+ def set_button_text(self):
+ self.button_text = "Start Game" if self.game.frames < 100 else "Continue"
+
+ def toggle(self):
+ self.set_button_text()
+ self.is_open = not self.is_open
+
+ def _draw(self):
+ self.start_button.draw()
class LightSource:
@@ -271,7 +370,7 @@ class LightSource:
self.img = img
self.center = center
self.sx, self.sy = center
- self.spot = pygame.image.load(ASSETS / "spotlight.png")
+ self.spot = self.game.get_img("spotlight")
class Leaf:
@@ -281,11 +380,12 @@ class Leaf:
self.center = center
self.sx, self.sy = center
self.img = pygame.transform.scale(
- pygame.image.load(ASSETS / "leaf.png"), (4, 4)
- ).convert_alpha()
+ self.game.get_img("leaf"), (4, 4)
+ ) # .convert_alpha()
self.lifespan = lifespan
self.r = random.randint(0, 360)
self.x, self.y = [int(i) + random.randint(-8, 8) for i in self.center]
+ self.speed = 3
self.restart()
def restart(self):
@@ -301,13 +401,13 @@ class Leaf:
)
if self.y < self.sy + 40:
- self.y += random.randint(0, 5) / 4
- self.x += random.randint(-15, 5) / 10
- self.r += random.randint(-10, 10)
+ self.y += random.randint(0, 5) / 4 * self.speed * self.game.elapsed
+ self.x += random.randint(-15, 5) / 10 * self.speed * self.game.elapsed
+ self.r += random.randint(-10, 10) * self.speed / 3 * self.game.elapsed
elif self.y < self.sy + 45:
- self.y += random.randint(-2, 5) / 10
- self.x += random.randint(-18, 2) / 10
- self.r += random.randint(-10, 25)
+ self.y += random.randint(-2, 5) / 10 * self.speed * self.game.elapsed
+ self.x += random.randint(-18, 2) / 10 * self.speed * self.game.elapsed
+ self.r += random.randint(-10, 25) * self.speed * self.game.elapsed
else:
self.restart()
if self.x > self.sx + 100:
@@ -335,15 +435,19 @@ class Bee:
class Creeper(Game):
def __init__(self, debug=False):
super().__init__()
- self.inventory = {}
+ self.inventory = {"plank-bottom": 1, "plank-top": 1}
+ self._imgs = {}
+ self.blocks = {}
+ self.camera = (0, 0)
self.day_len = 1000 * 60
self.background = pygame.Surface(self.screen.get_size())
self.foreground = pygame.Surface(self.screen.get_size())
self.build = pygame.Surface(self.screen.get_size())
- self.darkness = pygame.Surface(self.screen.get_size()).convert_alpha()
+ # self.darkness = pygame.Surface(self.screen.get_size())
self.axe_sound = pygame.mixer.Sound(ASSETS / "sounds/axe.mp3")
self.walking_sound = pygame.mixer.Sound(ASSETS / "sounds/walking.mp3")
self.walking_sound_is_playing = False
+ self.speed = 5
self.background.fill((0, 255, 247))
self.x, self.y = [i / 2 for i in self.screen.get_size()]
@@ -372,20 +476,21 @@ class Creeper(Game):
self.trees = []
x_range = [
- self.screen.get_size()[0] * 0.1,
- self.screen.get_size()[0] * 0.9,
+ 0,
+ self.screen.get_size()[0],
]
y_range = [
self.screen.get_size()[1] * 0.35,
self.screen.get_size()[1] * 0.5,
]
- for i in range(10):
+ for i in range(30):
x = random.randint(*x_range)
y = random.randint(*y_range)
scale = random.randint(42, 86)
self.trees.append(
TreeSprite(
+ game=self,
tree=random.choice(self.tree_imgs),
x=x,
y=y,
@@ -395,17 +500,35 @@ class Creeper(Game):
)
)
- self.mouse_box = MouseSprite(self.screen, hotbar=self.hotbar)
+ self.mouse_box = MouseSprite(self, self.background, hotbar=self.hotbar)
self.joysticks = {}
+
self.hotbar_back_debounce = 1
self.hotbar_forward_debounce = 1
self.inventory_open_debounce = 1
self.debug_open_debounce = 1
self.main_open_debounce = 1
+
+ self.controller_hotbar_back_debounce = 1
+ self.controller_hotbar_forward_debounce = 1
+ self.controller_inventory_open_debounce = 1
+ self.controller_debug_open_debounce = 1
+ self.controller_main_open_debounce = 1
+
+ self.debounce_walk_img = 0
+
self.inventory_menu = Menu(self, title="inventory")
self.debug_menu = DebugMenu(self)
self.debug_menu.is_open = debug
- self.main_menu = Menu(self, title="main menu")
+ self.main_menu = MainMenu(self, title="main menu")
+ self.main_menu.is_open = True
+
+ def get_img(self, img):
+ try:
+ return self._imgs[img]
+ except KeyError:
+ self._imgs[img] = pygame.image.load(ASSETS / f"{img}.png")
+ return self._imgs[img]
def attack(self):
@@ -423,8 +546,12 @@ class Creeper(Game):
return True
def place_block(self):
- if self.hotbar.selected.type is None:
- return False
+ # if self.hotbar.selected.type is None:
+ # return False
+ self.blocks[str(self.mouse_box.get_nearest_block_pos())] = Block(
+ self.hotbar.selected.type, self.mouse_box.get_nearest_block_pos()
+ )
+ print(self.blocks)
def process_deaths(self):
for i, tree in enumerate(copy(self.trees)):
@@ -439,14 +566,14 @@ class Creeper(Game):
def normal_keys(self):
keys = self.keys
- if keys[pygame.K_a]:
- self.x -= 10
- if keys[pygame.K_d]:
- self.x += 10
- if keys[pygame.K_w]:
- self.y -= 10
- if keys[pygame.K_d]:
- self.y += 10
+ if keys[pygame.K_a] or keys[pygame.K_LEFT]:
+ self.x -= 10 * self.speed * self.elapsed
+ if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
+ self.x += 10 * self.speed * self.elapsed
+ if keys[pygame.K_w] or keys[pygame.K_UP]:
+ self.y -= 10 * self.speed * self.elapsed
+ if keys[pygame.K_s] or keys[pygame.K_DOWN]:
+ self.y += 10 * self.speed * self.elapsed
if keys[pygame.K_k]:
self.hotbar.next(1)
if keys[pygame.K_j]:
@@ -469,80 +596,136 @@ class Creeper(Game):
if event.type == pygame.JOYDEVICEREMOVED:
del self.joysticks[event.instance_id]
+ if not self.joysticks:
+ if (keys[pygame.K_ESCAPE]) and self.main_open_debounce:
+ self.main_menu.is_open = not self.main_menu.is_open
+ self.main_open_debounce = 0
+ elif not keys[pygame.K_ESCAPE]:
+ self.main_open_debounce = 1
+
+ if keys[pygame.K_e] and self.inventory_open_debounce:
+ self.inventory_menu.is_open = not self.inventory_menu.is_open
+ self.inventory_open_debounce = 0
+ elif not keys[pygame.K_e]:
+ self.inventory_open_debounce = 1
+
for joystick in self.joysticks.values():
+ if joystick.get_axis(5) > 0:
+ if self.attack():
+ joystick.rumble(0.1, 0.0, 100)
- if joystick.get_button(4) and self.hotbar_back_debounce:
+ if joystick.get_button(4) and self.controller_hotbar_back_debounce:
+ print(self.controller_hotbar_back_debounce)
self.hotbar.next(-1)
- self.hotbar_back_debounce = 0
- elif not joystick.get_button(4):
- self.hotbar_back_debounce = 1
+ self.controller_hotbar_back_debounce = 0
+ print(self.hotbar_back_debounce)
+ if not joystick.get_button(4):
+ self.controller_hotbar_back_debounce = 1
- if joystick.get_button(5) and self.hotbar_forward_debounce:
+ if joystick.get_button(5) and self.controller_hotbar_forward_debounce:
self.hotbar.next(1)
- self.hotbar_forward_debounce = 0
+ self.controller_hotbar_forward_debounce = 0
elif not joystick.get_button(5):
- self.hotbar_forward_debounce = 1
+ self.controller_hotbar_forward_debounce = 1
if (
keys[pygame.K_e] or joystick.get_button(2)
- ) and self.inventory_open_debounce:
+ ) and self.controller_inventory_open_debounce:
self.inventory_menu.is_open = not self.inventory_menu.is_open
- self.inventory_open_debounce = 0
- elif not (keys[pygame.K_e] or joystick.get_button(2)):
- self.inventory_open_debounce = 1
+ self.controller_inventory_open_debounce = 0
+ elif not keys[pygame.K_e] and not joystick.get_button(2):
+ self.controller_inventory_open_debounce = 1
if (
keys[pygame.K_ESCAPE] or joystick.get_button(9)
- ) and self.main_open_debounce:
+ ) and self.controller_main_open_debounce:
self.main_menu.is_open = not self.main_menu.is_open
- self.main_open_debounce = 0
+ self.controller_main_open_debounce = 0
elif not (keys[pygame.K_ESCAPE] or joystick.get_button(9)):
- self.main_open_debounce = 1
+ self.controller_main_open_debounce = 1
- if keys[pygame.K_F3] and self.debug_open_debounce:
+ if keys[pygame.K_F3] and self.controller_debug_open_debounce:
self.debug_menu.is_open = not self.debug_menu.is_open
- self.debug_open_debounce = 0
+ self.controller_debug_open_debounce = 0
elif not keys[pygame.K_F3]:
- self.debug_open_debounce = 1
+ self.controller_debug_open_debounce = 1
hats = joystick.get_numhats()
for i in range(hats):
hat = joystick.get_hat(i)
if hat[0] == 1:
- self.x += 10
+ self.x += 10 * self.speed * self.elapsed
if hat[0] == -1:
- self.x -= 10
+ self.x -= 10 * self.speed * self.elapsed
if hat[1] == -1:
- self.y += 10
+ self.y += 10 * self.speed * self.elapsed
if hat[1] == 1:
- self.y -= 10
+ self.y -= 10 * self.speed * self.elapsed
if abs(joystick.get_axis(0)) > 0.2:
- self.x += joystick.get_axis(0) * 10
+ self.x += joystick.get_axis(0) * 10 * self.speed * self.elapsed
if abs(joystick.get_axis(1)) > 0.2:
- self.y += joystick.get_axis(1) * 10
+ self.y += joystick.get_axis(1) * 10 * self.speed * self.elapsed
+
+ if abs(joystick.get_axis(3)) > 0.2 and abs(joystick.get_axis(4)) > 0.2:
+ pygame.mouse.set_pos(
+ (
+ pygame.mouse.get_pos()[0] + joystick.get_axis(3) * 32,
+ pygame.mouse.get_pos()[1] + joystick.get_axis(4) * 32,
+ )
+ )
+ elif abs(joystick.get_axis(3)) > 0.2:
+ pygame.mouse.set_pos(
+ (
+ pygame.mouse.get_pos()[0] + joystick.get_axis(3) * 32,
+ pygame.mouse.get_pos()[1],
+ )
+ )
+ elif abs(joystick.get_axis(4)) > 0.2:
+ pygame.mouse.set_pos(
+ (
+ pygame.mouse.get_pos()[0],
+ pygame.mouse.get_pos()[1] + joystick.get_axis(4) * 32,
+ )
+ )
def inventory_keys(self):
keys = self.keys
+ if not self.joysticks:
+ if keys[pygame.K_e] and self.inventory_open_debounce:
+ self.inventory_menu.is_open = not self.inventory_menu.is_open
+ self.inventory_open_debounce = 0
+ elif not keys[pygame.K_e]:
+ self.inventory_open_debounce = 1
+
for joystick in self.joysticks.values():
if (
keys[pygame.K_e] or joystick.get_button(2)
- ) and self.inventory_open_debounce:
+ ) and self.controller_inventory_open_debounce:
self.inventory_menu.is_open = not self.inventory_menu.is_open
- self.inventory_open_debounce = 0
- elif not (keys[pygame.K_e] or joystick.get_button(2)):
- self.inventory_open_debounce = 1
+ self.controller_inventory_open_debounce = 0
+ elif not keys[pygame.K_e] and not joystick.get_button(2):
+ self.controller_inventory_open_debounce = 1
def main_keys(self):
keys = self.keys
+
+ if not self.joysticks:
+ if (keys[pygame.K_ESCAPE]) and self.main_open_debounce:
+ print("opens")
+ self.main_menu.is_open = not self.main_menu.is_open
+ self.main_open_debounce = 0
+ elif not keys[pygame.K_ESCAPE]:
+ self.main_open_debounce = 1
+
for joystick in self.joysticks.values():
if (
keys[pygame.K_ESCAPE] or joystick.get_button(9)
- ) and self.main_open_debounce:
+ ) and self.controller_main_open_debounce:
self.main_menu.is_open = not self.main_menu.is_open
- self.main_open_debounce = 0
+ self.controller_main_open_debounce = 0
elif not (keys[pygame.K_ESCAPE] or joystick.get_button(9)):
- self.main_open_debounce = 1
+ self.controller_main_open_debounce = 1
def make_sound(self):
if not hasattr(self, "last_x"):
@@ -560,14 +743,22 @@ class Creeper(Game):
) and not self.walking_sound_is_playing:
self.walking_sound.play()
self.walking_sound_is_playing = True
- if self.last_x != self.x or self.last_y != self.y:
-
+ if (
+ self.last_x != self.x or self.last_y != self.y
+ ) and self.debounce_walk_img < 0:
self.creeper = next(self.creepers)
+ self.debounce_walk_img = 0.15
self.last_x = self.x
self.last_y = self.y
def game(self):
- self.screen.blit(self.background, (0, 0))
+ # self.camera = (self.camera[0] + 1, self.camera[1])
+
+ self.debounce_walk_img -= self.elapsed
+ self.screen.blit(self.background, self.camera)
+ for block in self.blocks.values():
+ self.screen.blit(block.img, block.pos)
+
self.background.fill((0, 255, 247))
self.process_deaths()
for tree in self.trees:
@@ -587,29 +778,27 @@ class Creeper(Game):
light_level = pygame.time.get_ticks() % self.day_len
light_level = abs(light_level * (255 * 2 / self.day_len) - 255)
- self.darkness.fill((light_level, light_level, light_level))
- if self.light_power < 500:
- self.light_power = min(self.light_power**1.1, 500)
- self.darkness.blit(
- pygame.transform.smoothscale(
- self.spot, [self.light_power, self.light_power]
- ),
- (self.x - self.light_power / 2, self.y - self.light_power / 2),
- )
- self.screen.blit(
- self.darkness,
- (0, 0),
- special_flags=pygame.BLEND_RGBA_MULT,
- )
+ # self.darkness.fill((light_level, light_level, light_level))
+ # if self.light_power < 500:
+ # self.light_power = min(self.light_power**1.1, 500)
+ # self.darkness.blit(
+ # pygame.transform.smoothscale(
+ # self.spot, [self.light_power, self.light_power]
+ # ),
+ # (self.x - self.light_power / 2, self.y - self.light_power / 2),
+ # )
+ # self.screen.blit(
+ # pygame.transform.scale(self.darkness, self.screen.get_size()).convert(),
+ # (0, 0),
+ # special_flags=pygame.BLEND_MULT,
+ # )
self.hotbar.draw()
self.debug_menu.draw()
self.inventory_menu.draw()
self.main_menu.draw()
- self.mouse_box = MouseSprite(self.screen, hotbar=self.hotbar)
-
- self.mouse_box.draw()
+ self.mouse_box = MouseSprite(self, self.background, hotbar=self.hotbar)
self.make_sound()
@@ -619,6 +808,7 @@ class Creeper(Game):
self.main_keys()
else:
self.normal_keys()
+ self.mouse_box.draw()
def main(debug=False):
diff --git a/creeper_adventure/game.py b/creeper_adventure/game.py
old mode 100755
new mode 100644
index 852a343..7cfeeb9
--- a/creeper_adventure/game.py
+++ b/creeper_adventure/game.py
@@ -4,17 +4,22 @@ import pygame
class Game:
def __init__(self):
pygame.init()
+ # pygame.mouse.set_visible(False)
pygame.mixer.init()
pygame.display.set_caption(__file__)
self.screen_size = (854, 480)
self.screen_size = (1280, 800)
self.screen_size = (1920, 1080)
- self.screen = pygame.display.set_mode(self.screen_size)
+ # pygame.display.set_mode(resolution, flags, bpp)
+ flags = pygame.DOUBLEBUF
+ self.screen = pygame.display.set_mode(self.screen_size, flags)
self.clock = pygame.time.Clock()
self.running = True
self.surfs = []
+ self.elapsed = 0
+ self.frames = 0
def should_quit(self):
for event in self.events:
@@ -25,7 +30,8 @@ class Game:
...
def reset_screen(self):
- self.screen.fill((0, 0, 0))
+ ...
+ # self.screen.fill((0, 0, 0))
def run(self):
while self.running:
@@ -37,7 +43,8 @@ class Game:
for surf in self.surfs:
pygame.blit(surf)
pygame.display.update()
- self.clock.tick(30)
+ self.elapsed = self.clock.tick(60) / 100
+ self.frames += 1
pygame.quit()
diff --git a/forest.py b/forest.py
new file mode 100644
index 0000000..3a67d9c
--- /dev/null
+++ b/forest.py
@@ -0,0 +1,63 @@
+import math
+
+from noise import pnoise2
+import pygame
+
+# Initialize pygame
+pygame.init()
+
+base = 0
+persistence = 0.4
+lacunarity = 2.0
+more_x = 0
+# Set up the drawing window
+screen = pygame.display.set_mode([800, 600])
+
+
+def S():
+ i = 0
+ while True:
+ i += 0.1
+ yield math.sin(i)
+
+
+s = S()
+
+# Run until the user asks to quit
+running = True
+while running:
+
+ # Did the user click the window close button?
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ running = False
+
+ # Fill the background with white
+ screen.fill((255, 255, 255))
+
+ m = next(s)
+ # Generate Perlin noise
+ for x in range(800):
+ for y in range(600):
+ noise = pnoise2(
+ x / 10,
+ y / 10,
+ octaves=4,
+ persistence=persistence + m / 10,
+ lacunarity=lacunarity,
+ repeatx=1024,
+ repeaty=1024,
+ base=base,
+ )
+ if noise > 0.2:
+ pygame.draw.rect(screen, (0, 255, 0), (x, y, 1, 1))
+ elif noise > 0:
+ pygame.draw.rect(screen, (0, 128, 0), (x, y, 1, 1))
+ else:
+ pygame.draw.rect(screen, (0, 64, 0), (x, y, 1, 1))
+
+ # Flip the display
+ pygame.display.flip()
+
+# Done! Time to quit.
+pygame.quit()
diff --git a/gpt-menu.py b/gpt-menu.py
new file mode 100644
index 0000000..12c26fe
--- /dev/null
+++ b/gpt-menu.py
@@ -0,0 +1,85 @@
+import pygame
+
+# Initialize Pygame
+pygame.init()
+
+EVENTS = []
+# Set screen size
+screen = pygame.display.set_mode((800, 600))
+
+# Set title
+pygame.display.set_caption("My RPG Game")
+
+# Load font
+font = pygame.font.Font(None, 30)
+
+# Define button class
+class Button:
+ def __init__(self, text, x, y, w, h, on_click=lambda: ...):
+ self.text = text
+ self.x = x
+ self.y = y
+ self.w = w
+ self.h = h
+ self.on_click = on_click
+
+ def draw(self, surface):
+ pygame.draw.rect(surface, (255, 255, 255), (self.x, self.y, self.w, self.h))
+ label = font.render(self.text, True, (0, 0, 0))
+ label_rect = label.get_rect()
+ label_rect.center = (self.x + self.w / 2, self.y + self.h / 2)
+ surface.blit(label, label_rect)
+ for event in EVENTS:
+ if event.type == pygame.MOUSEBUTTONDOWN:
+ if self.is_clicked(event.pos):
+ self.on_click()
+ # elif quit_button.is_clicked(event.pos):
+ # running = False
+ # if self.is_clicked:
+ # self.on_click()
+
+ def is_clicked(self, pos):
+ if pos[0] > self.x and pos[0] < self.x + self.w:
+ if pos[1] > self.y and pos[1] < self.y + self.h:
+ return True
+ return False
+
+
+# Create buttons
+start_button = Button(
+ "Start Game", 300, 300, 200, 50, lambda: print("start this thing")
+)
+running = True
+
+
+def stop():
+ global running
+ running = False
+
+
+quit_button = Button("Quit Game", 300, 400, 200, 50, stop)
+
+# Main loop
+while running:
+ EVENTS = pygame.event.get()
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ running = False
+
+ # Check for button clicks
+
+ # Draw background
+ screen.fill((0, 0, 0))
+
+ # Draw title
+ title = font.render("Creeper Adventure", True, (255, 255, 255))
+ screen.blit(title, (250, 200))
+
+ # Draw buttons
+ start_button.draw(screen)
+ quit_button.draw(screen)
+
+ pygame.display.update()
+
+# Quit Pygame
+pygame.quit()
diff --git a/gradients.py b/gradients.py
new file mode 100644
index 0000000..4834b3b
--- /dev/null
+++ b/gradients.py
@@ -0,0 +1,42 @@
+from noise import pnoise2
+import pygame
+import random
+
+# Initialize pygame
+pygame.init()
+
+# Set the size of the window
+width, height = 800, 600
+screen = pygame.display.set_mode((width, height))
+
+# Create a list of 3 random blues
+blues = []
+for i in range(3):
+ blues.append(
+ (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
+ )
+
+# Create a perlin noise surface
+noise_surface = pygame.Surface((width, height))
+for x in range(width):
+ for y in range(height):
+ # Calculate the perlin noise value
+ noise_value = pnoise2(x / 100, y / 100)
+ # Map the noise value to a color
+ color_index = int(noise_value * (len(blues) - 1))
+ color = blues[color_index]
+ # Set the color of the pixel
+ noise_surface.set_at((x, y), color)
+
+# Blit the noise surface to the screen
+screen.blit(noise_surface, (0, 0))
+
+# Update the display
+pygame.display.flip()
+
+# Keep the window open until it is closed
+while True:
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ pygame.quit()
+ exit()
diff --git a/grass.png b/grass.png
new file mode 100644
index 0000000..894be7d
Binary files /dev/null and b/grass.png differ
diff --git a/iso.py b/iso.py
new file mode 100644
index 0000000..6923180
--- /dev/null
+++ b/iso.py
@@ -0,0 +1,56 @@
+import sys
+
+import pygame
+from pygame.locals import DOUBLEBUF, K_ESCAPE, KEYUP, QUIT
+
+pygame.init()
+
+DISPLAYSURF = pygame.display.set_mode(
+ (640, 480), DOUBLEBUF
+) # set the display mode, window title and FPS clock
+pygame.display.set_caption("Map Rendering Demo")
+FPSCLOCK = pygame.time.Clock()
+
+map_data = [
+ [1, 1, 1, 1, 1],
+ [1, 0, 0, 0, 1],
+ [1, 0, 0, 0, 1],
+ [1, 0, 0, 0, 1],
+ [1, 0, 0, 0, 1],
+ [1, 1, 1, 1, 1],
+] # the data for the map expressed as [row[tile]].
+
+wall = pygame.image.load("wall.png").convert_alpha() # load images
+grass = pygame.image.load("grass.png").convert_alpha()
+
+TILEWIDTH = 64 # holds the tile width and height
+TILEHEIGHT = 64
+TILEHEIGHT_HALF = TILEHEIGHT / 2
+TILEWIDTH_HALF = TILEWIDTH / 2
+
+for row_nb, row in enumerate(map_data): # for every row of the map...
+ for col_nb, tile in enumerate(row):
+ if tile == 1:
+ tileImage = wall
+ else:
+ tileImage = grass
+ cart_x = row_nb * TILEWIDTH_HALF
+ cart_y = col_nb * TILEHEIGHT_HALF
+ iso_x = cart_x - cart_y
+ iso_y = (cart_x + cart_y) / 2
+ centered_x = DISPLAYSURF.get_rect().centerx + iso_x
+ centered_y = DISPLAYSURF.get_rect().centery / 2 + iso_y
+ DISPLAYSURF.blit(tileImage, (centered_x, centered_y)) # display the actual tile
+
+while True:
+ for event in pygame.event.get():
+ if event.type == QUIT:
+ pygame.quit()
+ sys.exit()
+ if event.type == KEYUP:
+ if event.key == K_ESCAPE:
+ pygame.quit()
+ sys.exit()
+
+ pygame.display.flip()
+ FPSCLOCK.tick(30)
diff --git a/pyproject.toml b/pyproject.toml
index ec8968e..58be8c4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -26,6 +26,7 @@ classifiers = [
dependencies = [
"click",
"pygame",
+ "noise",
"more_itertools",
]
dynamic = ["version"]
diff --git a/requirements.txt b/requirements.txt
old mode 100755
new mode 100644
diff --git a/site/static/creeper-1.png b/site/static/creeper-1.png
old mode 100755
new mode 100644
diff --git a/spacegame.py b/spacegame.py
new file mode 100644
index 0000000..f2aecf6
--- /dev/null
+++ b/spacegame.py
@@ -0,0 +1,99 @@
+import pygame
+
+# Define some colors
+BLACK = (0, 0, 0)
+WHITE = (255, 255, 255)
+GREEN = (0, 255, 0)
+RED = (255, 0, 0)
+BLUE = (0, 0, 255)
+
+# Define some constants
+WIDTH = 800
+HEIGHT = 600
+FPS = 60
+GRAVITY = 0.5
+
+# Define some variables
+x = WIDTH / 2
+y = HEIGHT / 2
+vx = 0
+vy = 0
+
+# Define some functions
+
+
+def draw_dot(x, y):
+ pygame.draw.circle(window, BLACK, (int(x), int(y)), 10)
+
+
+def move_dot(x, y, vx, vy):
+ x += vx
+ y += vy
+ return x, y
+
+
+def apply_gravity(vy):
+ vy += GRAVITY
+ return vy
+
+
+def jump(vy):
+ vy = -10
+ return vy
+
+
+# Initialize pygame
+pygame.init()
+
+# Create a window
+# Create a clock
+clock = pygame.time.Clock()
+
+# Create a window
+window = pygame.display.set_mode((WIDTH, HEIGHT))
+
+# Set window title
+pygame.display.set_caption("My Game")
+
+# Game loop
+running = True
+# Set the frame rate
+clock.tick(FPS)
+
+while running:
+ # Process events
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ running = False
+ if event.type == pygame.KEYDOWN:
+ if event.key == pygame.K_a:
+ vx = -5
+ if event.key == pygame.K_d:
+ vx = 5
+ if event.key == pygame.K_SPACE:
+ vy = jump(vy)
+ if event.type == pygame.KEYUP:
+ if event.key == pygame.K_a:
+ vx = 0
+ if event.key == pygame.K_d:
+ vx = 0
+
+ running = False
+ x, y = move_dot(x, y, vx, vy)
+ vy = apply_gravity(vy)
+
+ # Render
+ # Clear the screen
+
+ # Draw the dot
+ draw_dot(x, y)
+
+ # Update the display
+ # Update
+
+ # Render
+ window.fill((255, 255, 255))
+ pygame.display.update()
+
+# Close window on quit
+pygame.quit()
diff --git a/tetris.py b/tetris.py
new file mode 100644
index 0000000..33626bf
--- /dev/null
+++ b/tetris.py
@@ -0,0 +1,521 @@
+# Tetromino (a Tetris clone)
+# By Al Sweigart al@inventwithpython.com
+# http://inventwithpython.com/pygame
+# Released under a "Simplified BSD" license
+
+import random
+import time
+import pygame
+import sys
+from pygame.locals import *
+
+FPS = 25
+WINDOWWIDTH = 640
+WINDOWZE = 20
+BOARHEIGHT = 480
+BOXSIDWIDTH = 10
+BOARDHEIGHT = 20
+BLANK = '.'
+
+MOVESIDEWAYSFREQ = 0.15
+MOVEDOWNFREQ = 0.1
+
+XMARGIN = int((WINDOWWIDTH - BOARDWIDTH * BOXSIZE) / 2)
+TOPMARGIN = WINDOWHEIGHT - (BOARDHEIGHT * BOXSIZE) - 5
+
+# R G B
+WHITE = (255, 255, 255)
+GRAY = (185, 185, 185)
+BLACK = (0, 0, 0)
+RED = (155, 0, 0)
+LIGHTRED = (175, 20, 20)
+GREEN = (0, 155, 0)
+LIGHTGREEN = (20, 175, 20)
+BLUE = (0, 0, 155)
+LIGHTBLUE = (20, 20, 175)
+YELLOW = (155, 155, 0)
+LIGHTYELLOW = (175, 175, 20)
+
+BORDERCOLOR = BLUE
+BGCOLOR = BLACK
+TEXTCOLOR = WHITE
+TEXTSHADOWCOLOR = GRAY
+COLORS = (BLUE, GREEN, RED, YELLOW)
+LIGHTCOLORS = (LIGHTBLUE, LIGHTGREEN, LIGHTRED, LIGHTYELLOW)
+assert len(COLORS) == len(LIGHTCOLORS) # each color must have light color
+
+TEMPLATEWIDTH = 5
+TEMPLATEHEIGHT = 5
+
+S_SHAPE_TEMPLATE = [['.....',
+ '.....',
+ '..OO.',
+ '.OO..',
+ '.....'],
+ ['.....',
+ '..O..',
+ '..OO.',
+ '...O.',
+ '.....']]
+
+Z_SHAPE_TEMPLATE = [['.....',
+ '.....',
+ '.OO..',
+ '..OO.',
+ '.....'],
+ ['.....',
+ '..O..',
+ '.OO..',
+ '.O...',
+ '.....']]
+
+I_SHAPE_TEMPLATE = [['..O..',
+ '..O..',
+ '..O..',
+ '..O..',
+ '.....'],
+ ['.....',
+ '.....',
+ 'OOOO.',
+ '.....',
+ '.....']]
+
+O_SHAPE_TEMPLATE = [['.....',
+ '.....',
+ '.OO..',
+ '.OO..',
+ '.....']]
+
+J_SHAPE_TEMPLATE = [['.....',
+ '.O...',
+ '.OOO.',
+ '.....',
+ '.....'],
+ ['.....',
+ '..OO.',
+ '..O..',
+ '..O..',
+ '.....'],
+ ['.....',
+ '.....',
+ '.OOO.',
+ '...O.',
+ '.....'],
+ ['.....',
+ '..O..',
+ '..O..',
+ '.OO..',
+ '.....']]
+
+L_SHAPE_TEMPLATE = [['.....',
+ '...O.',
+ '.OOO.',
+ '.....',
+ '.....'],
+ ['.....',
+ '..O..',
+ '..O..',
+ '..OO.',
+ '.....'],
+ ['.....',
+ '.....',
+ '.OOO.',
+ '.O...',
+ '.....'],
+ ['.....',
+ '.OO..',
+ '..O..',
+ '..O..',
+ '.....']]
+
+T_SHAPE_TEMPLATE = [['.....',
+ '..O..',
+ '.OOO.',
+ '.....',
+ '.....'],
+ ['.....',
+ '..O..',
+ '..OO.',
+ '..O..',
+ '.....'],
+ ['.....',
+ '.....',
+ '.OOO.',
+ '..O..',
+ '.....'],
+ ['.....',
+ '..O..',
+ '.OO..',
+ '..O..',
+ '.....']]
+
+PIECES = {'S': S_SHAPE_TEMPLATE,
+ 'Z': Z_SHAPE_TEMPLATE,
+ 'J': J_SHAPE_TEMPLATE,
+ 'L': L_SHAPE_TEMPLATE,
+ 'I': I_SHAPE_TEMPLATE,
+ 'O': O_SHAPE_TEMPLATE,
+ 'T': T_SHAPE_TEMPLATE}
+
+
+def main():
+ global FPSCLOCK, DISPLAYSURF, BASICFONT, BIGFONT
+ pygame.init()
+ FPSCLOCK = pygame.time.Clock()
+ DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
+ BASICFONT = pygame.font.Font('freesansbold.ttf', 18)
+ BIGFONT = pygame.font.Font('freesansbold.ttf', 100)
+ pygame.display.set_caption('Tetromino')
+
+ showTextScreen('Tetromino')
+ while True: # game loop
+ # if random.randint(0, 1) == 0:
+ # pygame.mixer.music.load('tetrisb.mid')
+ # else:
+ # pygame.mixer.music.load('tetrisc.mid')
+ #pygame.mixer.music.play(-1, 0.0)
+ runGame()
+ # pygame.mixer.music.stop()
+ showTextScreen('Game Over')
+
+
+def runGame():
+ # setup variables for the start of the game
+ board = getBlankBoard()
+ lastMoveDownTime = time.time()
+ lastMoveSidewaysTime = time.time()
+ lastFallTime = time.time()
+ movingDown = False # note: there is no movingUp variable
+ movingLeft = False
+ movingRight = False
+ score = 0
+ level, fallFreq = calculateLevelAndFallFreq(score)
+
+ fallingPiece = getNewPiece()
+ nextPiece = getNewPiece()
+
+ while True: # game loop
+ if fallingPiece == None:
+ # No falling piece in play, so start a new piece at the top
+ fallingPiece = nextPiece
+ nextPiece = getNewPiece()
+ lastFallTime = time.time() # reset lastFallTime
+
+ if not isValidPosition(board, fallingPiece):
+ return # can't fit a new piece on the board, so game over
+
+ checkForQuit()
+ for event in pygame.event.get(): # event handling loop
+ if event.type == KEYUP:
+ if (event.key == K_p):
+ # Pausing the game
+ DISPLAYSURF.fill(BGCOLOR)
+ pygame.mixer.music.stop()
+ showTextScreen('Paused') # pause until a key press
+ pygame.mixer.music.play(-1, 0.0)
+ lastFallTime = time.time()
+ lastMoveDownTime = time.time()
+ lastMoveSidewaysTime = time.time()
+ elif (event.key == K_LEFT or event.key == K_a):
+ movingLeft = False
+ elif (event.key == K_RIGHT or event.key == K_d):
+ movingRight = False
+ elif (event.key == K_DOWN or event.key == K_s):
+ movingDown = False
+
+ elif event.type == KEYDOWN:
+ # moving the piece sideways
+ if (event.key == K_LEFT or event.key == K_a) and isValidPosition(board, fallingPiece, adjX=-1):
+ fallingPiece['x'] -= 1
+ movingLeft = True
+ movingRight = False
+ lastMoveSidewaysTime = time.time()
+
+ elif (event.key == K_RIGHT or event.key == K_d) and isValidPosition(board, fallingPiece, adjX=1):
+ fallingPiece['x'] += 1
+ movingRight = True
+ movingLeft = False
+ lastMoveSidewaysTime = time.time()
+
+ # rotating the piece (if there is room to rotate)
+ elif (event.key == K_UP or event.key == K_w):
+ fallingPiece['rotation'] = (
+ fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']])
+ if not isValidPosition(board, fallingPiece):
+ fallingPiece['rotation'] = (
+ fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']])
+ elif (event.key == K_q): # rotate the other direction
+ fallingPiece['rotation'] = (
+ fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']])
+ if not isValidPosition(board, fallingPiece):
+ fallingPiece['rotation'] = (
+ fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']])
+
+ # making the piece fall faster with the down key
+ elif (event.key == K_DOWN or event.key == K_s):
+ movingDown = True
+ if isValidPosition(board, fallingPiece, adjY=1):
+ fallingPiece['y'] += 1
+ lastMoveDownTime = time.time()
+
+ # move the current piece all the way down
+ elif event.key == K_SPACE:
+ movingDown = False
+ movingLeft = False
+ movingRight = False
+ for i in range(1, BOARDHEIGHT):
+ if not isValidPosition(board, fallingPiece, adjY=i):
+ break
+ fallingPiece['y'] += i - 1
+
+ # handle moving the piece because of user input
+ if (movingLeft or movingRight) and time.time() - lastMoveSidewaysTime > MOVESIDEWAYSFREQ:
+ if movingLeft and isValidPosition(board, fallingPiece, adjX=-1):
+ fallingPiece['x'] -= 1
+ elif movingRight and isValidPosition(board, fallingPiece, adjX=1):
+ fallingPiece['x'] += 1
+ lastMoveSidewaysTime = time.time()
+
+ if movingDown and time.time() - lastMoveDownTime > MOVEDOWNFREQ and isValidPosition(board, fallingPiece, adjY=1):
+ fallingPiece['y'] += 1
+ lastMoveDownTime = time.time()
+
+ # let the piece fall if it is time to fall
+ if time.time() - lastFallTime > fallFreq:
+ # see if the piece has landed
+ if not isValidPosition(board, fallingPiece, adjY=1):
+ # falling piece has landed, set it on the board
+ addToBoard(board, fallingPiece)
+ score += removeCompleteLines(board)
+ level, fallFreq = calculateLevelAndFallFreq(score)
+ fallingPiece = None
+ else:
+ # piece did not land, just move the piece down
+ fallingPiece['y'] += 1
+ lastFallTime = time.time()
+
+ # drawing everything on the screen
+ DISPLAYSURF.fill(BGCOLOR)
+ drawBoard(board)
+ drawStatus(score, level)
+ drawNextPiece(nextPiece) # Here
+ if fallingPiece != None:
+ drawPiece(fallingPiece)
+
+ pygame.display.update()
+ FPSCLOCK.tick(FPS)
+
+
+def makeTextObjs(text, font, color):
+ surf = font.render(text, True, color)
+ return surf, surf.get_rect()
+
+
+def terminate():
+ pygame.quit()
+ sys.exit()
+
+
+def checkForKeyPress():
+ # Go through event queue looking for a KEYUP event.
+ # Grab KEYDOWN events to remove them from the event queue.
+ checkForQuit()
+
+ for event in pygame.event.get([KEYDOWN, KEYUP]):
+ if event.type == KEYDOWN:
+ continue
+ return event.key
+ return None
+
+
+def showTextScreen(text):
+ # This function displays large text in the
+ # center of the screen until a key is pressed.
+ # Draw the text drop shadow
+ titleSurf, titleRect = makeTextObjs(text, BIGFONT, TEXTSHADOWCOLOR)
+ titleRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2))
+ DISPLAYSURF.blit(titleSurf, titleRect)
+
+ # Draw the text
+ titleSurf, titleRect = makeTextObjs(text, BIGFONT, TEXTCOLOR)
+ titleRect.center = (int(WINDOWWIDTH / 2) - 3, int(WINDOWHEIGHT / 2) - 3)
+ DISPLAYSURF.blit(titleSurf, titleRect)
+
+ # Draw the additional "Press a key to play." text.
+ pressKeySurf, pressKeyRect = makeTextObjs(
+ 'Press a key to play.', BASICFONT, TEXTCOLOR)
+ pressKeyRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2) + 100)
+ DISPLAYSURF.blit(pressKeySurf, pressKeyRect)
+
+ while checkForKeyPress() == None:
+ pygame.display.update()
+ FPSCLOCK.tick()
+
+
+def checkForQuit():
+ for event in pygame.event.get(QUIT): # get all the QUIT events
+ terminate() # terminate if any QUIT events are present
+ for event in pygame.event.get(KEYUP): # get all the KEYUP events
+ if event.key == K_ESCAPE:
+ terminate() # terminate if the KEYUP event was for the Esc key
+ pygame.event.post(event) # put the other KEYUP event objects back
+
+
+def calculateLevelAndFallFreq(score):
+ # Based on the score, return the level the player is on and
+ # how many seconds pass until a falling piece falls one space.
+ level = int(score / 10) + 1
+ fallFreq = 0.27 - (level * 0.02)
+ return level, fallFreq
+
+
+def getNewPiece():
+ # return a random new piece in a random rotation and color
+ shape = random.choice(list(PIECES.keys()))
+ newPiece = {'shape': shape,
+ 'rotation': random.randint(0, len(PIECES[shape]) - 1),
+ 'x': int(BOARDWIDTH / 2) - int(TEMPLATEWIDTH / 2),
+ 'y': -2, # start it above the board (i.e. less than 0)
+ 'color': random.randint(0, len(COLORS) - 1)}
+ return newPiece
+
+
+def addToBoard(board, piece):
+ # fill in the board based on piece's location, shape, and rotation
+ for x in range(TEMPLATEWIDTH):
+ for y in range(TEMPLATEHEIGHT):
+ if PIECES[piece['shape']][piece['rotation']][y][x] != BLANK:
+ board[x + piece['x']][y + piece['y']] = piece['color']
+
+
+def getBlankBoard():
+ # create and return a new blank board data structure
+ board = []
+ for i in range(BOARDWIDTH):
+ board.append([BLANK] * BOARDHEIGHT)
+ return board
+
+
+def isOnBoard(x, y):
+ return x >= 0 and x < BOARDWIDTH and y < BOARDHEIGHT
+
+
+def isValidPosition(board, piece, adjX=0, adjY=0):
+ # Return True if the piece is within the board and not colliding
+ for x in range(TEMPLATEWIDTH):
+ for y in range(TEMPLATEHEIGHT):
+ isAboveBoard = y + piece['y'] + adjY < 0
+ if isAboveBoard or PIECES[piece['shape']][piece['rotation']][y][x] == BLANK:
+ continue
+ if not isOnBoard(x + piece['x'] + adjX, y + piece['y'] + adjY):
+ return False
+ if board[x + piece['x'] + adjX][y + piece['y'] + adjY] != BLANK:
+ return False
+ return True
+
+
+def isCompleteLine(board, y):
+ # Return True if the line filled with boxes with no gaps.
+ for x in range(BOARDWIDTH):
+ if board[x][y] == BLANK:
+ return False
+ return True
+
+
+def removeCompleteLines(board):
+ # Remove any completed lines on the board, move everything above them down, and return the number of complete lines.
+ numLinesRemoved = 0
+ y = BOARDHEIGHT - 1 # start y at the bottom of the board
+ while y >= 0:
+ if isCompleteLine(board, y):
+ # Remove the line and pull boxes down by one line.
+ for pullDownY in range(y, 0, -1):
+ for x in range(BOARDWIDTH):
+ board[x][pullDownY] = board[x][pullDownY - 1]
+ # Set very top line to blank.
+ for x in range(BOARDWIDTH):
+ board[x][0] = BLANK
+ numLinesRemoved += 1
+ # Note on the next iteration of the loop, y is the same.
+ # This is so that if the line that was pulled down is also
+ # complete, it will be removed.
+ else:
+ y -= 1 # move on to check next row up
+ return numLinesRemoved
+
+
+def convertToPixelCoords(boxx, boxy):
+ # Convert the given xy coordinates of the board to xy
+ # coordinates of the location on the screen.
+ return (XMARGIN + (boxx * BOXSIZE)), (TOPMARGIN + (boxy * BOXSIZE))
+
+
+def drawBox(boxx, boxy, color, pixelx=None, pixely=None):
+ # draw a single box (each tetromino piece has four boxes)
+ # at xy coordinates on the board. Or, if pixelx & pixely
+ # are specified, draw to the pixel coordinates stored in
+ # pixelx & pixely (this is used for the "Next" piece).
+ if color == BLANK:
+ return
+ if pixelx == None and pixely == None:
+ pixelx, pixely = convertToPixelCoords(boxx, boxy)
+ pygame.draw.rect(DISPLAYSURF, COLORS[color],
+ (pixelx + 1, pixely + 1, BOXSIZE - 1, BOXSIZE - 1))
+ pygame.draw.rect(
+ DISPLAYSURF, LIGHTCOLORS[color], (pixelx + 1, pixely + 1, BOXSIZE - 4, BOXSIZE - 4))
+
+
+def drawBoard(board):
+ # draw the border around the board
+ pygame.draw.rect(DISPLAYSURF, BORDERCOLOR, (XMARGIN - 3, TOPMARGIN - 7,
+ (BOARDWIDTH * BOXSIZE) + 8, (BOARDHEIGHT * BOXSIZE) + 8), 5)
+
+ # fill the background of the board
+ pygame.draw.rect(DISPLAYSURF, BGCOLOR, (XMARGIN, TOPMARGIN,
+ BOXSIZE * BOARDWIDTH, BOXSIZE * BOARDHEIGHT))
+ # draw the individual boxes on the board
+ for x in range(BOARDWIDTH):
+ for y in range(BOARDHEIGHT):
+ drawBox(x, y, board[x][y])
+
+
+def drawStatus(score, level):
+ # draw the score text
+ scoreSurf = BASICFONT.render('Score: %s' % score, True, TEXTCOLOR)
+ scoreRect = scoreSurf.get_rect()
+ scoreRect.topleft = (WINDOWWIDTH - 150, 20)
+ DISPLAYSURF.blit(scoreSurf, scoreRect)
+
+ # draw the level text
+ levelSurf = BASICFONT.render('Level: %s' % level, True, TEXTCOLOR)
+ levelRect = levelSurf.get_rect()
+ levelRect.topleft = (WINDOWWIDTH - 150, 50)
+ DISPLAYSURF.blit(levelSurf, levelRect)
+
+
+def drawPiece(piece, pixelx=None, pixely=None):
+ shapeToDraw = PIECES[piece['shape']][piece['rotation']]
+ if pixelx == None and pixely == None:
+ # if pixelx & pixely hasn't been specified, use the location stored in the piece data structure
+ pixelx, pixely = convertToPixelCoords(piece['x'], piece['y'])
+
+ # draw each of the boxes that make up the piece
+ for x in range(TEMPLATEWIDTH):
+ for y in range(TEMPLATEHEIGHT):
+ if shapeToDraw[y][x] != BLANK:
+ drawBox(None, None, piece['color'], pixelx
+ + (x * BOXSIZE), pixely + (y * BOXSIZE))
+
+
+def drawNextPiece(piece):
+ # draw the "next" text
+ nextSurf = BASICFONT.render('Next:', True, TEXTCOLOR)
+ nextRect = nextSurf.get_rect()
+ nextRect.topleft = (WINDOWWIDTH - 120, 80)
+ DISPLAYSURF.blit(nextSurf, nextRect)
+ # draw the "next" piece
+ drawPiece(piece, pixelx=WINDOWWIDTH - 120, pixely=100)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tmp.py b/tmp.py
new file mode 100644
index 0000000..a82b9ca
--- /dev/null
+++ b/tmp.py
@@ -0,0 +1,41 @@
+import noise
+import pygame
+
+# Initialize pygame
+pygame.init()
+
+# Set up the drawing window
+screen = pygame.display.set_mode([800, 600])
+
+# Run until the user asks to quit
+running = True
+while running:
+
+ # Did the user click the window close button?
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ running = False
+
+ # Fill the background with black
+ screen.fill((0, 0, 0))
+
+ # Draw stars
+ for i in range(2000):
+ x = int(noise.pnoise1(i / 10.0, octaves=4) * 800)
+ y = int(noise.pnoise1(i / 10.0 + 1000, octaves=4) * 600)
+ pygame.draw.circle(screen, (255, 255, 255), (x, y), 2)
+
+ # Draw the moon
+ pygame.draw.circle(screen, (180, 180, 180), (400, 5600 - 200), 5000, 0) # moon
+
+ # Draw craters on the surface of the moon (masked by the shape of the moon)
+ for i in range(20):
+ x = int(noise.pnoise1(i / 10.0, octaves=4) * 800)
+ y = int(noise.pnoise1(i / 10.0 + 1000, octaves=4) * 600)
+ pygame.draw.circle(screen, (100, 100, 100), (x, y), 20, 0)
+
+ # Flip the display
+ pygame.display.flip()
+
+# Done! Time to quit.
+pygame.quit()
diff --git a/wall.png b/wall.png
new file mode 100644
index 0000000..a81bb68
Binary files /dev/null and b/wall.png differ