Skip to content

Commit

Permalink
Merge pull request #1073 from tabreturn/megaball
Browse files Browse the repository at this point in the history
Megaball
  • Loading branch information
Cebion authored Dec 17, 2024
2 parents 3a649d5 + 974f3e0 commit 3a46f8b
Show file tree
Hide file tree
Showing 30 changed files with 2,199 additions and 0 deletions.
69 changes: 69 additions & 0 deletions ports/megaball/Megaball.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/bin/bash

XDG_DATA_HOME=${XDG_DATA_HOME:-$HOME/.local/share}

if [ -d "/opt/system/Tools/PortMaster/" ]; then
controlfolder="/opt/system/Tools/PortMaster"
elif [ -d "/opt/tools/PortMaster/" ]; then
controlfolder="/opt/tools/PortMaster"
elif [ -d "$XDG_DATA_HOME/PortMaster/" ]; then
controlfolder="$XDG_DATA_HOME/PortMaster"
else
controlfolder="/roms/ports/PortMaster"
fi

source $controlfolder/control.txt
[ -f "${controlfolder}/mod_${CFW_NAME}.txt" ] && source "${controlfolder}/mod_${CFW_NAME}.txt"
get_controls

GAMEDIR="/$directory/ports/megaball"
CONFDIR="$GAMEDIR/conf"
PYXEL_PKG="main.py"

cd "${GAMEDIR}"

> "${GAMEDIR}/log.txt" && exec > >(tee "${GAMEDIR}/log.txt") 2>&1

mkdir -p "$GAMEDIR/conf"
bind_directories "$HOME/.config/.pyxel/megaball" "$CONFDIR"

# Load Pyxel runtime
runtime="pyxel_2.2.8_python_3.11"
export pyxel_dir="$HOME/pyxel"
mkdir -p "${pyxel_dir}"

if [ ! -f "$controlfolder/libs/${runtime}.squashfs" ]; then
# Check for runtime if not downloaded via PM
if [ ! -f "$controlfolder/harbourmaster" ]; then
pm_message "This port requires the latest PortMaster to run, please go to https://portmaster.games/ for more info."
sleep 5
exit 1
fi

$ESUDO $controlfolder/harbourmaster --quiet --no-check runtime_check "${runtime}.squashfs"
fi

if [[ "$PM_CAN_MOUNT" != "N" ]]; then
$ESUDO umount "${pyxel_dir}"
fi

$ESUDO mount "$controlfolder/libs/${runtime}.squashfs" "${pyxel_dir}"

export SDL_GAMECONTROLLERCONFIG="$sdl_controllerconfig"

$GPTOKEYB "pyxel" &

pm_platform_helper "${pyxel_dir}/bin/pyxel"

# Enable Pyxel virtual env
source "${pyxel_dir}/bin/activate"
export PYTHONHOME="${pyxel_dir}"
export PYTHONPYCACHEPREFIX="${GAMEDIR}/${runtime}.cache"

"${pyxel_dir}/bin/pyxel" run "${GAMEDIR}/gamedata/${PYXEL_PKG}"

if [[ "$PM_CAN_MOUNT" != "N" ]]; then
$ESUDO umount "${pyxel_dir}"
fi

pm_finish
30 changes: 30 additions & 0 deletions ports/megaball/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## Notes

Thanks [helpcomputer](https://helpcomputer.itch.io) (Adam) for creating this fantastic game and releasing it under an MIT license.


## Controls

| Button | Action |
| -------| ------------- |
| D-PAD | Movement |
| START | Menu |
| A | Self-destruct |


## Compile

```shell
apt update
apt install wget git python3-venv # python >=3.8 is required

# Setup pyxel virtual env
python3 -m venv pyxel-venv
source pyxel-venv/bin/activate
pip install pyxel

# Test the game
git clone https://github.com/PortsMaster/PortMaster-New.git
cd ports/megaball
pyxel run megaball/gamedata/main.py
```
13 changes: 13 additions & 0 deletions ports/megaball/gameinfo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<gameList>
<game>
<path>./Megaball.sh</path>
<name>Megaball</name>
<desc>The goal is to roll the ball over every flashing panel to complete the stage. For each stage you complete, you gain one extra life. If you need to, you can self-destruct (and lose a life) and shatter into ten pieces, killing any enemy which collides with these pieces; you will immediately re-spawn, and the enemy will reappear in 5 seconds.</desc>
<releasedate>20200906T000000</releasedate>
<developer>helpcomputer (Adam)</developer>
<publisher>helpcomputer</publisher>
<genre>Arcade</genre>
<image>./megaball/screenshot.png</image>
</game>
</gameList>
1 change: 1 addition & 0 deletions ports/megaball/megaball/gamedata/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__/
Binary file added ports/megaball/megaball/gamedata/assets/img0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
57 changes: 57 additions & 0 deletions ports/megaball/megaball/gamedata/audio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

import pyxel

import globals

MUS_IN_GAME = 0
MUS_TITLE = 1
MUS_START = 2
MUS_STAGE_COMPLETE = 3
MUS_DEATH = 4
MUS_GAME_OVER = 5

MUSIC = [
MUS_IN_GAME,
MUS_TITLE,
MUS_START,
MUS_STAGE_COMPLETE,
MUS_DEATH,
#MUS_GAME_OVER
]

SND_MENU_MOVE = 16
SND_MENU_SELECT = 17
SND_HIT_WALL = 18
SND_HIT_TARGET = 19
SND_USED_WEAPON = 20

SOUNDS = [
SND_MENU_MOVE,
SND_MENU_SELECT,
SND_HIT_WALL,
SND_HIT_TARGET,
SND_USED_WEAPON,
]

def play_sound(snd, looping=False):
if globals.g_sound_on == False:
return

if snd not in SOUNDS:
return

if pyxel.play_pos(3) != -1:
return

pyxel.play(3, snd, loop=looping)

def play_music(msc, looping=False):
if globals.g_music_on == False:
return

if msc not in MUSIC:
return

pyxel.stop()

pyxel.playm(msc, loop=looping)
29 changes: 29 additions & 0 deletions ports/megaball/megaball/gamedata/circle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

def overlap(x1, y1, r1, x2, y2, r2):
dx = x1 - x2
dy = y1 - y2
dist = dx * dx + dy * dy
radiusSum = r1 + r2
return dist < radiusSum * radiusSum

def contains_other(x1, y1, r1, x2, y2, r2):
radiusDiff = r1 - r2
if radiusDiff < 0:
return False

dx = x1 - x2
dy = y1 - y2
dist = dx * dx + dy * dy
radiusSum = r1 + r2
return (not(radiusDiff * radiusDiff < dist) and (dist < radiusSum * radiusSum))

def contains_point(x, y, radius, px, py):
dx = x - px
dy = y - py
return dx * dx + dy * dy <= radius * radius

class Circle:
def __init__(self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
73 changes: 73 additions & 0 deletions ports/megaball/megaball/gamedata/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

GAME_TITLE = "MEGABALL"
GAME_WIDTH = 160
GAME_HEIGHT = 144
GAME_FPS = 60
GAME_SCALE = 4

IMAGE_BANK_0_FILE = "assets/img0.png"
RESOURCE_FILE = "assets/my_resource.pyxres"

COLLIDE_TOP_LEFT = [
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 0, 0],
[1, 1, 1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0],
]

COLLIDE_TOP_RIGHT = [
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 1],
]

COLLIDE_BOTTOM_RIGHT = [
[0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1],
[0, 0, 0, 1, 1, 1, 1, 1],
[0, 0, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
]

COLLIDE_BOTTOM_LEFT = [
[1, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1],
]

COLLIDE_MATRIX_ALL = [
COLLIDE_TOP_LEFT,
COLLIDE_TOP_RIGHT,
COLLIDE_BOTTOM_RIGHT,
COLLIDE_BOTTOM_LEFT
]

def is_colliding_matrix(x, y, matrix):
if matrix not in COLLIDE_MATRIX_ALL:
return False

if x < 0 or x > 7 or y < 0 or y > 7:
return False

return matrix[y][x]




103 changes: 103 additions & 0 deletions ports/megaball/megaball/gamedata/game.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@

import pyxel

import constants
import palette
import hud
import input
import stage
import screenshake
import mainmenu
import globals
import audio

class Game:
def __init__(self):
self.pal_control = palette.PaletteControl()

self.screen_shake = screenshake.ScreenShake(self)

self.main_menu = mainmenu.MainMenu(self)
self.stage = stage.Stage(self, 0)
self.hud = hud.Hud(self)

self.pal_index = 0

audio.play_music(audio.MUS_TITLE, True)

def restart_music(self):
if self.main_menu.is_visible:
audio.play_music(audio.MUS_TITLE)
else:
self.stage.restart_music()

def quit_to_main_menu(self):
del self.stage
self.stage = stage.Stage(self, 0)
globals.set_high_score()
globals.reset()
self.main_menu.reset()
self.add_fade(palette.FADE_STEP_TICKS_DEFAULT, palette.FADE_LEVEL_3)

def go_to_next_stage(self):
globals.g_stage_num += 1
del self.stage
self.stage = stage.Stage(self, globals.g_stage_num)
self.add_fade(palette.FADE_STEP_TICKS_DEFAULT, palette.FADE_LEVEL_3)

def go_to_game_complete_stage(self):
del self.stage
self.stage = stage.Stage(self, stage.MAX_STAGE_NUM + 1)
self.add_fade(palette.FADE_STEP_TICKS_DEFAULT, palette.FADE_LEVEL_3)

def restart_stage(self):
del self.stage
self.stage = stage.Stage(self, globals.g_stage_num)
self.add_fade(palette.FADE_STEP_TICKS_DEFAULT, palette.FADE_LEVEL_3)

def start_game(self):
self.main_menu.hide()
del self.stage
self.stage = stage.Stage(self, globals.g_stage_num)
self.add_fade(palette.FADE_STEP_TICKS_DEFAULT, palette.FADE_LEVEL_3)

def add_screen_shake(self, ticks, magnitude, queue=False):
self.screen_shake.add_event(ticks, magnitude, queue)

def cycle_palette(self):
self.pal_index += 1
if self.pal_index == len(palette.ALL):
self.pal_index = 0
self.pal_control.add_palette_event(1, palette.ALL[self.pal_index])
self.add_fade(palette.FADE_STEP_TICKS_DEFAULT, palette.FADE_LEVEL_3)

def add_fade(self, ticks_per_level, target_level, callback=None):
self.pal_control.add_fade_event(ticks_per_level, target_level, callback)

def update(self, last_inputs):
if pyxel.btnp(pyxel.KEY_F1):
globals.toggle_sound()

if pyxel.btnp(pyxel.KEY_F2):
globals.toggle_music(self)

self.main_menu.update(last_inputs)

self.stage.update(last_inputs)

self.pal_control.update()
self.screen_shake.update()

def draw(self):
for c in range(palette.NUM_COLOURS):
pyxel.pal(palette.DEFAULT[c], self.pal_control.get_col(c))

pyxel.cls(self.pal_control.get_col(0))

self.stage.draw(self.screen_shake.x, self.screen_shake.y)
self.hud.draw(self.screen_shake.x, self.screen_shake.y)

self.main_menu.draw(self.screen_shake.x, self.screen_shake.y)

pyxel.pal()

Loading

0 comments on commit 3a46f8b

Please sign in to comment.