diff --git a/math_hurdler.py b/math_hurdler.py index c9548f1..0dd3e34 100755 --- a/math_hurdler.py +++ b/math_hurdler.py @@ -5,6 +5,7 @@ from fractions import Fraction from question import Question from objects.button import Button +from option_button import Option_Button from sprites.horse import Horse from sprites.sun import Sun @@ -28,7 +29,6 @@ class Color: class MathHurdler: def __init__(self): - # Set up a clock for managing the frame rate. self.clock = pygame.time.Clock() self.x = -100 @@ -162,7 +162,6 @@ def load_highscore(self): return 0 return 0 - # The main game loop. def run(self): @@ -181,25 +180,11 @@ def run(self): screen = pygame.display.get_surface() screen_size = screen.get_size() - button_panel = pygame.Surface((screen_size[0] / 3, screen_size[1] / 7)) ground = pygame.image.load('./assets/images/ground.png') - ground = pygame.transform.scale(ground, (int(screen_size[0]), int(screen_size[1] / 3))) - - self.buttons = [ - Button( - str(self.question.choices[i]), - self.lg_font, - Color.BLACK, - button_panel.get_width() / 2, - button_panel.get_height() / 2, - Color.WHITE, - Color.BLACK, - -2 - ) for i in range(4) - ] + ground = pygame.transform.scale(ground, (int(screen_size[0]), int(screen_size[1] // 3))) grass = pygame.image.load('./assets/images/grass.png') - grass = pygame.transform.scale(grass, (int(screen_size[0]), int(screen_size[1] / 12))) + grass = pygame.transform.scale(grass, (int(screen_size[0]), int(screen_size[1] // 12))) ground.blit(grass, (0,0)) points_label = self.lg_font.render('POINTS', 1, Color.BLACK) @@ -208,20 +193,37 @@ def run(self): sun.rect.topleft = (screen_size[0] - sun.image.get_width(), 0) horse = Horse() - horse.rect.x = display_info.current_h / 3 + horse.rect.x = display_info.current_h // 3 horse.rect.y = display_info.current_h - \ horse.image.get_height() - ground.get_height() hurdle = pygame.image.load('./assets/images/hurdle.png') hurdle = pygame.transform.scale( hurdle, - (int(hurdle.get_height() / 3), int(hurdle.get_width() / 3))) + (int(hurdle.get_height() // 3), int(hurdle.get_width() // 3))) hurdle_y = display_info.current_h - \ - hurdle.get_height() - (2 * ground.get_height() / 3) + hurdle.get_height() - (2 * ground.get_height() // 3) + 70 + + self.buttons = [] + button_width = 200 + button_height = 80 + buttons_x_padding = 50 + buttons_y_padding = (ground.get_rect().height - grass.get_rect().height - button_height) // 4 + buttons_gap = (screen_size[0] - (buttons_x_padding * 2 + button_width * 4)) // 3 + + for i, choice in enumerate(self.question.choices): + choice_text = self.lg_font.render(("A", "B", "C", "D")[i] + " " + str(choice), 1, Color.BLACK) + self.buttons.append( + Option_Button( + buttons_x_padding + button_width * i + buttons_gap * i, + screen_size[1] - button_height - buttons_y_padding, + button_width, button_height, choice_text, str(choice) + ) + ) question_board = pygame.Surface( - (screen_size[0] / 3, screen_size[1] / 5)) + (screen_size[0] // 3, screen_size[1] // 5)) question_board = question_board.convert() question_board.fill(Color.WHITE) @@ -259,21 +261,18 @@ def reset(): def generate_question(): next(self.question) - self.buttons[0].set_text(str(self.question.choices[0])) - self.buttons[1].set_text(str(self.question.choices[1])) - self.buttons[2].set_text(str(self.question.choices[2])) - self.buttons[3].set_text(str(self.question.choices[3])) - - self.buttons[0].set_color(self.buttons[0].color, False) - self.buttons[1].set_color(self.buttons[0].color, False) - self.buttons[2].set_color(self.buttons[0].color, False) - self.buttons[3].set_color(self.buttons[0].color, False) + for i in range(4): + choice_text = self.lg_font.render(("A", "B", "C", "D")[i] + " " + str(self.question.choices[i]), + 1, Color.BLACK) + self.buttons[i].set_text(choice_text) + self.buttons[i].value = str(self.question.choices[i]) + self.buttons[i].set_color((255, 255, 255)) self.question_text_label = self.lg_font.render( str(self.question), 1, Color.BLACK) self.hurdle_number += 1 # start at vx=5 and accelerate towards vx=10 - self.vx = 4 + (self.hurdle_number * 6) / (self.hurdle_number + 5) + self.vx = 4 + (self.hurdle_number * 6) // (self.hurdle_number + 5) self.score_label = self.lg_font.render( str(self.points), 1, Color.BLACK) self.question_label = self.font.render( @@ -287,12 +286,11 @@ def generate_question(): def set_answer(answer_index): self.vx *= 2 - # unselect the previous answer button if self.last_answer_index >= 0: self.buttons[self.last_answer_index].set_selected(False) if answer_index >= 0: - self.last_answer = Fraction(self.buttons[answer_index].text) + self.last_answer = Fraction(self.buttons[answer_index].value) self.buttons[answer_index].set_selected(True) self.last_answer_index = answer_index else: @@ -313,14 +311,12 @@ def evaluate_answer(answer): self.set_gameover(True) if self.last_answer_index >= 0: self.buttons[self.last_answer_index].set_color( - Color.RED, False) + Color.RED) self.buttons[self.question.answer_index].set_color( - Color.GREEN, False) - + Color.GREEN) while self.running: - # Processing Gtk Events while Gtk.events_pending(): Gtk.main_iteration() if self.playing: @@ -333,8 +329,14 @@ def evaluate_answer(answer): elif event.type == pygame.KEYDOWN: if event.key == pygame.K_p: self.paused = not self.paused - elif event.key == pygame.K_c: - set_answer(self.question.answer_index) + elif event.key == pygame.K_a or event.key == pygame.K_1: + set_answer(0) + elif event.key == pygame.K_b or event.key == pygame.K_2: + set_answer(1) + elif event.key == pygame.K_c or event.key == pygame.K_3: + set_answer(2) + elif event.key == pygame.K_d or event.key == pygame.K_4: + set_answer(3) elif event.type == pygame.MOUSEBUTTONDOWN: if not self.gameover: for i in range(0, 4): @@ -365,9 +367,9 @@ def evaluate_answer(answer): self.horse_change += 1 - horse.rect.x = display_info.current_w / 3 + horse.rect.x = display_info.current_w // 3 horse.rect.y = display_info.current_h - \ - hurdle.get_height() - (3 * ground.get_height() / 4) + hurdle.get_height() - (3 * ground.get_height() // 4) + 70 # Check if hurdle and horse in same spot. if horse.rect.colliderect(hurdle_rect): @@ -379,7 +381,7 @@ def evaluate_answer(answer): # if not gameover, jump the hurdle if not self.gameover: horse.set_horse(Horse.JUMP) - horse.rect.x = display_info.current_w / 3 + horse.rect.x = display_info.current_w // 3 horse.rect.y = display_info.current_h \ - horse.image.get_height() \ - ground.get_height() \ @@ -392,68 +394,45 @@ def evaluate_answer(answer): question_dirty = False if self.gameover: - # spin the horse self.save_highscore() horse.set_horse(Horse.DEAD) - # Set the "sky" color to blue screen.fill(background_color) sun.rect.topleft = (screen_size[0] - sun.image.get_width(), 0) screen.blit( question_board, - (screen_size[0] / 4, - screen_size[1] / 5)) + (screen_size[0] // 4, + screen_size[1] // 5)) question_board.blit(self.question_label, (10, 10)) question_board.blit( self.question_text_label, (10, self.question_label.get_height() + 10)) screen.blit(ground, (0, screen_size[1] - ground.get_height())) - button_panel_x = ground.get_width() / 4 - button_panel_y = screen_size[1] - \ - ground.get_height() + ground.get_height() / 3 + 10 - screen.blit(button_panel, (button_panel_x, button_panel_y)) - - self.buttons[0].rect.x = button_panel_x - self.buttons[0].rect.y = button_panel_y - self.buttons[0].draw(screen) - - self.buttons[1].rect.x = button_panel_x + \ - self.buttons[0].image.get_width() - self.buttons[1].rect.y = button_panel_y - self.buttons[1].draw(screen) - - self.buttons[2].rect.x = button_panel_x - self.buttons[2].rect.y = button_panel_y + \ - self.buttons[0].image.get_height() - self.buttons[2].draw(screen) - - self.buttons[3].rect.x = button_panel_x + \ - self.buttons[2].image.get_width() - self.buttons[3].rect.y = button_panel_y + \ - self.buttons[2].image.get_height() - self.buttons[3].draw(screen) screen.blit(hurdle, (self.x, hurdle_y)) allsprites = pygame.sprite.RenderPlain(sun, horse) allsprites.draw(screen) + for btn in self.buttons: + btn.draw() + screen.blit( self.score_label, ( - sun.rect.x + sun.image.get_width() / 4, - sun.rect.y + sun.image.get_height() / 3 + sun.rect.x + sun.image.get_width() // 4, + sun.rect.y + sun.image.get_height() // 3 ) ) screen.blit( points_label, ( - sun.rect.x + sun.image.get_width() / 4, - sun.rect.y + sun.image.get_height() / 3 + sun.rect.x + sun.image.get_width() // 4, + sun.rect.y + sun.image.get_height() // 3 + self.score_label.get_height() ) ) @@ -464,12 +443,11 @@ def evaluate_answer(answer): screen.blit( gameover_label, ( - (screen_size[0] - gameover_label.get_width()) / 2, - (screen_size[1] - gameover_label.get_height()) / 2, + (screen_size[0] - gameover_label.get_width()) // 2, + (screen_size[1] - gameover_label.get_height()) // 2, ) ) - # Draw the frame pygame.display.flip() if self.gameover: @@ -477,7 +455,6 @@ def evaluate_answer(answer): self.set_playing(False) reset() - # Try to stay at 30 FPS self.clock.tick(18) else: @@ -485,7 +462,6 @@ def start_game(): reset() self.set_playing(True) - # in the menu for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() @@ -506,7 +482,6 @@ def start_game(): self.horse_change += 1 - # draw rainbow background fill screen.fill( ( math.floor( @@ -521,33 +496,29 @@ def start_game(): ) ) - # draw menu horse - horse.rect.x = (screen_size[0] - horse.image.get_width()) / 2 + horse.rect.x = (screen_size[0] - horse.image.get_width()) // 2 horse.rect.y = ( - screen_size[1] - horse.image.get_height()) / 2 + 200 + screen_size[1] - horse.image.get_height()) // 2 + 200 menu_sprites = pygame.sprite.RenderPlain(horse) menu_sprites.draw(screen) - # draw play button play_button.rect.x = ( - screen_size[0] - play_button.rect.width) / 2 + screen_size[0] - play_button.rect.width) // 2 play_button.rect.y = ( - screen_size[1] - play_button.rect.height) / 2 + screen_size[1] - play_button.rect.height) // 2 play_button.draw(screen) - # draw menu label screen.blit( menu_label, ( - (screen_size[0] - menu_label.get_width()) / 2, - (screen_size[1] - menu_label.get_height()) / 2 - 200, + (screen_size[0] - menu_label.get_width()) // 2, + (screen_size[1] - menu_label.get_height()) // 2 - 200, ) ) pygame.display.flip() - # Try to stay at 30 FPS self.clock.tick(30) diff --git a/math_hurdlerActivity.py b/math_hurdlerActivity.py index d07e7c6..45a109e 100644 --- a/math_hurdlerActivity.py +++ b/math_hurdlerActivity.py @@ -33,6 +33,7 @@ def __init__(self, handle): # Note that set_canvas implicitly calls read_file when # resuming from the Journal. self.set_canvas(self._pygamecanvas) + self._pygamecanvas.grab_focus() # Start the game running (self.game.run is called when the # activity constructor returns). diff --git a/option_button.py b/option_button.py new file mode 100644 index 0000000..2356128 --- /dev/null +++ b/option_button.py @@ -0,0 +1,66 @@ +# Copyright (C) 2023 Riya Jain +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +import pygame + + +class Option_Button: + + def __init__(self, x, y, width, height, text, value): + self.text = text + self.text_rect = text.get_rect() + self.value = value + + self.rect = pygame.Rect(x, y, width, height) + + self.gameDisplay = pygame.display.get_surface() + + self.color = (255, 255, 255) + self.selected_color = (180, 180, 180) + + self.press = False + self.draw() + + def set_text(self, text): + self.text = text + self.text_rect = text.get_rect() + + def draw(self): + c_radius = self.rect.height // 2 + c_center = [self.rect.x, self.rect.y + c_radius] + pygame.draw.circle(self.gameDisplay, self.color, c_center, c_radius) + c_center[0] += self.rect.width + pygame.draw.circle(self.gameDisplay, self.color, c_center, c_radius) + pygame.draw.rect(self.gameDisplay, self.color, self.rect) + m_x = self.rect.x + self.rect.width // 2 - self.text_rect.width // 2 + m_y = self.rect.y + self.rect.height // 2 - self.text_rect.height // 2 + self.gameDisplay.blit(self.text, (m_x, m_y)) + + def set_color(self, color): + self.color = color + + def hovered(self): + return self.rect.collidepoint(pygame.mouse.get_pos()) + + def mouse_click(self, mouse, action, *args): + if self.rect.collidepoint(mouse): + action(*args) + + def set_selected(self, selected): + if selected: + self.set_color(self.selected_color) + else: + self.set_color((255, 255, 255)) diff --git a/sugargame/__init__.py b/sugargame/__init__.py index 6f4fa58..9fb438e 100644 --- a/sugargame/__init__.py +++ b/sugargame/__init__.py @@ -1 +1,23 @@ +# +# Copyright (c) 2020 Wade Brainerd +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + __version__ = '1.3' diff --git a/sugargame/canvas.py b/sugargame/canvas.py index 90856ca..beb6279 100644 --- a/sugargame/canvas.py +++ b/sugargame/canvas.py @@ -1,3 +1,25 @@ +# +# Copyright (c) 2020 Wade Brainerd +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + import os from gi.repository import Gtk from gi.repository import GLib diff --git a/sugargame/event.py b/sugargame/event.py index 611ed20..e719081 100644 --- a/sugargame/event.py +++ b/sugargame/event.py @@ -1,3 +1,25 @@ +# +# Copyright (c) 2020 Wade Brainerd +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + import logging from gi.repository import GLib from gi.repository import Gdk @@ -42,6 +64,143 @@ class Translator(object): pygame.K_RSHIFT: pygame.KMOD_RSHIFT, } + keys = [ + pygame.K_UNKNOWN, + pygame.K_BACKSPACE, + pygame.K_TAB, + pygame.K_RETURN, + pygame.K_ESCAPE, + pygame.K_SPACE, + pygame.K_EXCLAIM, + pygame.K_QUOTEDBL, + pygame.K_HASH, + pygame.K_DOLLAR, + pygame.K_PERCENT, + pygame.K_AMPERSAND, + pygame.K_QUOTE, + pygame.K_LEFTPAREN, + pygame.K_RIGHTPAREN, + pygame.K_ASTERISK, + pygame.K_PLUS, + pygame.K_COMMA, + pygame.K_MINUS, + pygame.K_PERIOD, + pygame.K_SLASH, + pygame.K_0, + pygame.K_1, + pygame.K_2, + pygame.K_3, + pygame.K_4, + pygame.K_5, + pygame.K_6, + pygame.K_7, + pygame.K_8, + pygame.K_9, + pygame.K_COLON, + pygame.K_SEMICOLON, + pygame.K_LESS, + pygame.K_EQUALS, + pygame.K_GREATER, + pygame.K_QUESTION, + pygame.K_AT, + pygame.K_LEFTBRACKET, + pygame.K_BACKSLASH, + pygame.K_RIGHTBRACKET, + pygame.K_CARET, + pygame.K_UNDERSCORE, + pygame.K_BACKQUOTE, + pygame.K_a, + pygame.K_b, + pygame.K_c, + pygame.K_d, + pygame.K_e, + pygame.K_f, + pygame.K_g, + pygame.K_h, + pygame.K_i, + pygame.K_j, + pygame.K_k, + pygame.K_l, + pygame.K_m, + pygame.K_n, + pygame.K_o, + pygame.K_p, + pygame.K_q, + pygame.K_r, + pygame.K_s, + pygame.K_t, + pygame.K_u, + pygame.K_v, + pygame.K_w, + pygame.K_x, + pygame.K_y, + pygame.K_z, + pygame.K_DELETE, + pygame.K_CAPSLOCK, + pygame.K_F1, + pygame.K_F2, + pygame.K_F3, + pygame.K_F4, + pygame.K_F5, + pygame.K_F6, + pygame.K_F7, + pygame.K_F8, + pygame.K_F9, + pygame.K_F10, + pygame.K_F11, + pygame.K_F12, + pygame.K_PRINT, + pygame.K_SCROLLOCK, + pygame.K_BREAK, + pygame.K_INSERT, + pygame.K_HOME, + pygame.K_PAGEUP, + pygame.K_END, + pygame.K_PAGEDOWN, + pygame.K_RIGHT, + pygame.K_LEFT, + pygame.K_DOWN, + pygame.K_UP, + pygame.K_NUMLOCK, + pygame.K_KP_DIVIDE, + pygame.K_KP_MULTIPLY, + pygame.K_KP_MINUS, + pygame.K_KP_PLUS, + pygame.K_KP_ENTER, + pygame.K_KP1, + pygame.K_KP2, + pygame.K_KP3, + pygame.K_KP4, + pygame.K_KP5, + pygame.K_KP6, + pygame.K_KP7, + pygame.K_KP8, + pygame.K_KP9, + pygame.K_KP0, + pygame.K_KP_PERIOD, + pygame.K_POWER, + pygame.K_KP_EQUALS, + pygame.K_F13, + pygame.K_F14, + pygame.K_F15, + pygame.K_HELP, + pygame.K_MENU, + pygame.K_SYSREQ, + pygame.K_CLEAR, + pygame.K_CURRENCYUNIT, + pygame.K_CURRENCYSUBUNIT, + pygame.K_LCTRL, + pygame.K_LSHIFT, + pygame.K_LALT, + pygame.K_LMETA, + pygame.K_RCTRL, + pygame.K_RSHIFT, + pygame.K_RALT, + pygame.K_RMETA, + pygame.K_MODE, + pygame.K_AC_BACK + ] + def __init__(self, activity, inner_evb): """Initialise the Translator with the windows to which to listen""" self._activity = activity @@ -78,7 +237,6 @@ def __init__(self, activity, inner_evb): self._inner_evb.connect('screen-changed', self._screen_changed_cb) # Internal data - self.__keystate = [0] * 323 self.__button_state = [0, 0, 0] self.__mouse_pos = (0, 0) self.__repeat = (None, None) @@ -86,6 +244,7 @@ def __init__(self, activity, inner_evb): self.__held_time_left = {} self.__held_last_time = {} self.__tick_id = None + self.__keystate = dict((i, False) for i in self.keys) def hook_pygame(self): pygame.key.get_pressed = self._get_pressed @@ -162,7 +321,7 @@ def _keyevent(self, widget, event, type): # view source request, specially handled... self._activity.view_source() else: - logging.error('Key %s unrecognized' % key) + logging.warning('Key %s unrecognized' % key) if keycode is not None: if type == pygame.KEYDOWN: @@ -179,7 +338,7 @@ def _keyevent(self, widget, event, type): return True def _get_pressed(self): - return self.__keystate + return list(self.__keystate.values()) def _get_mouse_pressed(self): return self.__button_state