├── Actor.py ├── Enum.py ├── HelpMenu.py ├── HexScatter.py ├── HexTile.py ├── LICENSE ├── MainMenu.py ├── MapCanvas.py ├── MapGenerator.py ├── Player.py ├── README.md ├── SettingsJson.py ├── UpDownBubble.py ├── assets ├── __init__.py ├── audio │ ├── __init__.py │ ├── enemy_death.ogg │ ├── game_over.ogg │ ├── music.ogg │ ├── pick_key.ogg │ ├── pick_star.ogg │ ├── player_jump.ogg │ └── player_walk.ogg └── graphics │ ├── __init__.py │ ├── enemies │ ├── blobLeft.zip │ ├── blobRight.zip │ ├── flyLeft.zip │ ├── flyRight.zip │ ├── spiderLeft.zip │ └── spiderRight.zip │ ├── player │ ├── playerDeath.png │ ├── playerJumpLeft.png │ ├── playerJumpRight.png │ ├── playerStandLeft.png │ ├── playerStandLeftSelected.png │ ├── playerStandRight.png │ ├── playerStandRightSelected.png │ ├── playerWalkLeft.zip │ └── playerWalkRight.zip │ ├── tiles │ ├── keyBlue.png │ ├── keyGreen.png │ ├── keyOrange.png │ ├── keyYellow.png │ ├── lockBlue.png │ ├── lockGreen.png │ ├── lockOrange.png │ ├── lockYellow.png │ ├── noKey.png │ ├── overlay01.png │ ├── overlay02.png │ ├── overlay03.png │ ├── overlay04.png │ ├── overlay05.png │ ├── overlay06.png │ ├── portal.png │ ├── powerup.png │ ├── starGold.png │ ├── tileAutumn.png │ ├── tileDirt.png │ ├── tileDirtOver.png │ ├── tileGrass.png │ ├── tileLava.png │ ├── tileMagic.png │ ├── tileRock.png │ ├── tileSand.png │ ├── tileSnow.png │ ├── tileStone.png │ └── tileWater.png │ └── ui │ ├── arrow.png │ ├── background.png │ ├── button_down.png │ ├── button_normal.png │ ├── downdis.png │ ├── enemyTurn.png │ ├── goal.png │ ├── killenemies.png │ ├── loading.png │ ├── movement.png │ ├── playerselect.png │ ├── popupImg.png │ ├── portal.png │ ├── stars.png │ ├── tileTooFar.png │ ├── tileselect.png │ ├── turnSkip.png │ ├── updis.png │ └── updown.png ├── data ├── __init__.py └── gameData.py ├── gameScreen.kv ├── icon.png ├── main.py ├── mainMenu.kv └── presplash.png /Actor.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | 3 | from kivy.uix.image import Image 4 | from data.gameData import * 5 | from random import choice 6 | 7 | 8 | class Actor(Image): 9 | def __init__(self, col, row, actor_type=None, ended_turn=False, **kwargs): 10 | super(Actor, self).__init__(**kwargs) 11 | self.col = col 12 | self.row = row 13 | self.ended_turn = ended_turn 14 | if actor_type: 15 | self.actor_type = actor_type 16 | else: 17 | self.actor_type = choice(enemies) 18 | if self.actor_type in flyables: 19 | self.can_fly = True 20 | else: 21 | self.can_fly = False 22 | self.source = 'assets/graphics/enemies/' + self.actor_type + choice(['Left', 'Right']) + '.zip' 23 | 24 | def move_left(self): 25 | self.source = 'assets/graphics/enemies/' + self.actor_type + 'Left' + '.zip' 26 | 27 | def move_right(self): 28 | self.source = 'assets/graphics/enemies/' + self.actor_type + 'Right' + '.zip' -------------------------------------------------------------------------------- /Enum.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | 3 | 4 | class Enum(set): 5 | def __getattr__(self, name): 6 | if name in self: 7 | return name 8 | raise AttributeError -------------------------------------------------------------------------------- /HelpMenu.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | 3 | from kivy.uix.carousel import Carousel 4 | from kivy.uix.boxlayout import BoxLayout 5 | from kivy.lang import Builder 6 | 7 | from kivy.properties import StringProperty 8 | from data.gameData import * 9 | 10 | 11 | Builder.load_string(''' 12 | #:kivy 1.8.0 13 | 14 | orientation: 'vertical' 15 | id: helpPage 16 | BoxLayout: 17 | Image: 18 | source: root.current_image 19 | allow_stretch: True 20 | mipmap: False 21 | keep_ratio: False 22 | anim_delay: 0.5 23 | 24 | 25 | orientation: 'vertical' 26 | BoxLayout: 27 | size_hint: 1, 0.95 28 | HelpMenuCarousel: 29 | id: carousel 30 | BoxLayout: 31 | size_hint: 1.0, 0.05 32 | Button: 33 | id: backToMainMenu 34 | text: 'Back' 35 | on_release: root.back_to_main_menu() 36 | background_normal: 'assets/graphics/ui/button_normal.png' 37 | background_down: 'assets/graphics/ui/button_down.png' 38 | ''') 39 | 40 | 41 | class Page(BoxLayout): 42 | current_image = StringProperty() 43 | 44 | def __init__(self, **kwargs): 45 | super(Page, self).__init__(**kwargs) 46 | 47 | def set_current_image(self, image): 48 | self.current_image = image 49 | 50 | 51 | class HelpMenuCarousel(Carousel): 52 | def __init__(self, **kwargs): 53 | super(HelpMenuCarousel, self).__init__(**kwargs) 54 | for item in help_screens: 55 | page = Page() 56 | page.set_current_image('assets/graphics/ui/' + item + '.png') 57 | self.add_widget(page) 58 | 59 | 60 | class HelpMenu(BoxLayout): 61 | def __init__(self, **kwargs): 62 | super(HelpMenu, self).__init__(**kwargs) 63 | self.carousel = self.children[1].children[0] 64 | self.back_to_main = False 65 | 66 | def back_to_main_menu(self): 67 | self.back_to_main = True -------------------------------------------------------------------------------- /HexScatter.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | 3 | from kivy.uix.scatterlayout import ScatterLayout 4 | from kivy.animation import Animation 5 | from kivy.clock import Clock 6 | from data.gameData import * 7 | from HexTile import HexTile 8 | from UpDownBubble import UpDownBubble 9 | from random import choice, shuffle, random 10 | from kivy.properties import NumericProperty, BooleanProperty 11 | from kivy.core.audio import SoundLoader 12 | from kivy.uix.bubble import Bubble 13 | from kivy.uix.boxlayout import BoxLayout 14 | from kivy.uix.popup import Popup 15 | from Actor import Actor 16 | 17 | 18 | def level_diff(from_tile, to_tile): 19 | if from_tile.row > to_tile.row: 20 | from_level = from_tile.level + 3 21 | to_level = to_tile.level 22 | elif from_tile.row < to_tile.row: 23 | from_level = from_tile.level 24 | to_level = to_tile.level + 3 25 | else: 26 | from_level = from_tile.level 27 | to_level = to_tile.level 28 | return from_level, to_level 29 | 30 | 31 | class HexScatter(ScatterLayout): 32 | def __init__(self, hex_tiles, difficulty, soundsOption, musicOption, **kwargs): 33 | super(HexScatter, self).__init__(**kwargs) 34 | self.soundsOption = soundsOption 35 | self.musicOption = musicOption 36 | self.difficulty = difficulty 37 | self.x_offset = 1080 38 | self.y_offset = 1920 39 | self.hex_tiles = hex_tiles 40 | self.has_bubble = False 41 | self.bubble = None 42 | self.bubble_id = None 43 | self.selected_hex = None 44 | self.tiles = [] 45 | self.is_move_mode = False 46 | self.is_moving = False 47 | self.from_hex = None 48 | self.to_hex = None 49 | self.enemy_turn = False 50 | self.tile_with_player = None 51 | self.tile_with_portal = None 52 | self.is_just_selected = False 53 | self.move_anim = None 54 | self.death_anim = None 55 | self.player_death = False 56 | self.enemy_death = False 57 | self.actor_move_data = [] 58 | self.player = None 59 | self.player_is_jumping = False 60 | self.player_has_keys = [] 61 | self.player_has_exits = 0 62 | self.enemy_list = [] 63 | self.skip_turn_bubble = None 64 | self.score = 0 65 | self.popup = None 66 | self.player_won = False 67 | self.create_map_from_data() 68 | 69 | # for some reason the first time a bubble is loaded, it leads to a one or two second lag, preloading solves this 70 | self.shadow_bubble = UpDownBubble(pos=(1000, 1000)) 71 | self.add_widget(self.shadow_bubble) 72 | self.remove_widget(self.shadow_bubble) 73 | self.hex_tiles = None 74 | for enemy in self.enemy_list: 75 | self.add_widget(enemy) 76 | 77 | def create_map_from_data(self): 78 | tileradius = 120 79 | for tile in self.hex_tiles: 80 | if tile.has_land: 81 | tile_pos = ((tile.col * tileradius + (tileradius / 2) * (tile.row % 2)) + self.x_offset, 82 | (tile.row * 95) + tile.row * 102 + tile.level * 34 + self.y_offset) 83 | hex_tile = HexTile(tile.row, tile.col, tile.type, tile.level, tile.has_actor, tile.has_star, 84 | tile.has_player, tile.key_type, tile.exit_type, 85 | id='hexTile{0}'.format(str(tile.col) + str(tile.row)), 86 | source='assets/graphics/tiles/' + tile.type + '.png', 87 | pos=tile_pos) 88 | self.tiles.append(hex_tile) 89 | self.add_widget(hex_tile) 90 | if tile.has_actor: 91 | self.add_actor_to_map(tile, tile_pos) 92 | hex_tile.post_init() 93 | if tile.has_portal: 94 | self.tile_with_portal = hex_tile 95 | self.tile_with_portal.add_portal() 96 | if tile.has_player: 97 | self.tile_with_player = hex_tile 98 | self.tile_with_player.add_player() 99 | 100 | def add_actor_to_map(self, tile, tile_pos, add_to_enemy_list=False): 101 | delay = choice([x / 1000. for x in range(80, 200, 5)]) 102 | actor = Actor(tile.col, tile.row, pos=(tile_pos[0] + 10, tile_pos[1] + 253), anim_delay=delay) 103 | self.enemy_list.append(actor) 104 | if add_to_enemy_list: 105 | self.add_widget(actor) 106 | 107 | def add_tile(self, tile): 108 | self.tiles.append(tile) 109 | self.add_widget(tile) 110 | 111 | def move_mode(self, from_hex): 112 | self.from_hex = from_hex 113 | self.is_just_selected = True 114 | Clock.schedule_once(self.set_move_mode, 0.08) 115 | 116 | def set_move_mode(self, dt): 117 | if self.from_hex.player and not self.enemy_turn: 118 | self.enemy_turn = True 119 | self.is_move_mode = True 120 | self.is_just_selected = False 121 | self.from_hex.player.select_player() 122 | self.skip_turn_bubble = SkipTurnBubble(self, pos=(self.from_hex.x + 36, self.from_hex.y + 360)) 123 | self.add_widget(self.skip_turn_bubble) 124 | 125 | def check_proximity(self, to_hex): 126 | from_col = self.from_hex.col 127 | from_row = self.from_hex.row 128 | to_col = to_hex.col 129 | to_row = to_hex.row 130 | vector = [from_col - to_col, from_row - to_row] 131 | if from_row & 1: 132 | if vector in odd_directions: 133 | return False 134 | else: 135 | return True 136 | else: 137 | if vector in even_directions: 138 | return False 139 | else: 140 | return True 141 | 142 | def schedule_move_to_hex(self, to_hex): 143 | self.to_hex = to_hex 144 | Clock.schedule_once(self.move_to_hex_prep) 145 | 146 | def move_to_hex_prep(self, dt): 147 | self.remove_skip_turn() 148 | self.player_death = False 149 | self.enemy_death = False 150 | self.death_anim = Animation(y=self.to_hex.pos[1] + 500, duration=0.3) 151 | from_level, to_level = level_diff(self.from_hex, self.to_hex) 152 | if abs(to_level - from_level) > 1 or self.check_proximity(self.to_hex): 153 | self.end_without_movement(no_move=True, show_too_far=True) 154 | return 155 | elif to_level - from_level == 1: 156 | if self.to_hex.actor: 157 | self.player_death = True 158 | self.move_anim = Animation(x=self.to_hex.pos[0] + 10, duration=0.1) 159 | self.move_anim &= Animation(y=self.to_hex.pos[1] + 279, t='out_back', duration=0.3) 160 | jump = True 161 | elif to_level - from_level == -1: 162 | if self.to_hex.actor: 163 | self.enemy_death = True 164 | self.move_anim = Animation(x=self.to_hex.pos[0] + 10, duration=0.1) 165 | self.move_anim &= Animation(y=self.to_hex.pos[1] + 279, t='out_back', duration=0.3) 166 | jump = True 167 | else: 168 | if self.to_hex.actor: 169 | self.player_death = True 170 | jump = False 171 | self.move_anim = Animation(x=self.to_hex.pos[0] + 10, y=self.to_hex.pos[1] + 279, duration=0.5) 172 | self.is_moving = True 173 | 174 | if jump: 175 | self.player_is_jumping = True 176 | else: 177 | self.player_is_jumping = False 178 | if not self.is_move_mode: 179 | return 180 | self.player = self.from_hex.player 181 | self.from_hex.remove_player() 182 | self.add_widget(self.player) 183 | 184 | self.complete_player_movement(jump) 185 | 186 | def complete_player_movement(self, jump): 187 | if self.from_hex.col > self.to_hex.col or (self.from_hex.col >= self.to_hex.col and self.from_hex.row % 2): 188 | if jump: 189 | self.player.jump_left() 190 | else: 191 | self.player.move_left() 192 | else: 193 | if jump: 194 | self.player.jump_right() 195 | else: 196 | self.player.move_right() 197 | 198 | self.move_anim.start(self.player) 199 | if jump: 200 | jump_sound = SoundLoader.load('assets/audio/player_jump.ogg') 201 | jump_sound.play() 202 | else: 203 | if self.soundsOption: 204 | walk_sound = SoundLoader.load('assets/audio/player_walk.ogg') 205 | walk_sound.play() 206 | self.move_anim.bind(on_complete=self.end_with_player_transfer) 207 | if self.player_death: 208 | pass 209 | elif self.enemy_death: 210 | self.score += 5 * self.difficulty 211 | self.parent.counter.update_counter(0, 5 * self.difficulty) 212 | actor = self.get_enemy_by_tile(self.to_hex.row, self.to_hex.col) 213 | self.death_anim.start(actor) 214 | if self.soundsOption: 215 | enemy_death_sound = SoundLoader.load('assets/audio/enemy_death.ogg') 216 | enemy_death_sound.play() 217 | self.to_hex.remove_actor() 218 | for enemy in self.enemy_list: 219 | if enemy == actor: 220 | self.enemy_list.pop(self.enemy_list.index(enemy)) 221 | break 222 | self.death_anim.bind(on_complete=self.remove_enemy) 223 | 224 | def remove_enemy(self, a, b): 225 | self.remove_widget(b) 226 | 227 | def remove_skip_turn(self): 228 | if self.skip_turn_bubble: 229 | self.remove_widget(self.skip_turn_bubble) 230 | self.skip_turn_bubble = None 231 | 232 | def player_remove_on_death(self, a, b): 233 | self.tile_with_player.remove_player() 234 | 235 | def move_portal(self): 236 | self.selected_hex = self.tile_with_portal 237 | if self.tile_with_portal.level == MAX_LEVEL: 238 | self.move_hex_down(False) 239 | elif self.tile_with_portal.level == 0: 240 | self.move_hex_up(False) 241 | else: 242 | self.move_hex_up(False) if random() > 0.5 else self.move_hex_down(False) 243 | 244 | def spawn_enemy_on_portal(self): 245 | if not self.tile_with_portal.actor: 246 | self.tile_with_portal.add_actor() 247 | Clock.schedule_once(self.add_actor_to_portal, 0.7) 248 | 249 | def add_actor_to_portal(self, dt): 250 | self.add_actor_to_map(self.tile_with_portal, self.tile_with_portal.pos, True) 251 | if self.tile_with_portal.player: 252 | self.player_death = True 253 | self.complete_level() 254 | 255 | def handle_portal(self): 256 | if self.tile_with_portal: 257 | self.move_portal() 258 | if not self.tile_with_portal.actor and random() > 0.2: 259 | self.spawn_enemy_on_portal() 260 | 261 | def is_player_dead(self): 262 | if self.player_death: 263 | self.tile_with_player.player.player_dead() 264 | death_anim = Animation(x=self.tile_with_player.player.y + 500, duration=0.1) 265 | death_anim.start(self.tile_with_player.player) 266 | death_anim.bind(on_complete=self.player_remove_on_death) 267 | self.player_death = True 268 | self.complete_level() 269 | return True 270 | 271 | def handle_star(self): 272 | if self.tile_with_player.star: 273 | self.score += 1 * self.difficulty 274 | self.parent.counter.update_counter(1, 1 * self.difficulty) 275 | if self.soundsOption: 276 | pick_star_sound = SoundLoader.load('assets/audio/pick_star.ogg') 277 | pick_star_sound.play() 278 | star_anim = Animation(x=self.tile_with_player.star.x, 279 | y=self.tile_with_player.star.y + 80, 280 | duration=0.15) 281 | star_anim.start(self.tile_with_player.star) 282 | star_anim.bind(on_complete=self.tile_with_player.remove_star) 283 | self.tile_with_player.player.add_star() 284 | 285 | def handle_key(self): 286 | if self.tile_with_player.key: 287 | self.score += 5 * self.difficulty 288 | self.parent.counter.update_counter(0, 5 * self.difficulty) 289 | self.player_has_keys.append(self.tile_with_player.key_type) 290 | key_anim = Animation(x=self.tile_with_player.key.x, 291 | y=self.tile_with_player.key.y + 80, 292 | duration=0.15) 293 | key_anim.start(self.tile_with_player.key) 294 | key_id = self.tile_with_player.key_type 295 | if key_id == 'keyYellow': 296 | self.parent.star_counter.ids.keyYellow.source = key_sources[self.tile_with_player.key_type] 297 | self.parent.star_counter.ids.keyYellow.reload() 298 | elif key_id == 'keyGreen': 299 | self.parent.star_counter.ids.keyGreen.source = key_sources[self.tile_with_player.key_type] 300 | self.parent.star_counter.ids.keyGreen.reload() 301 | elif key_id == 'keyOrange': 302 | self.parent.star_counter.ids.keyOrange.source = key_sources[self.tile_with_player.key_type] 303 | self.parent.star_counter.ids.keyOrange.reload() 304 | elif key_id == 'keyBlue': 305 | self.parent.star_counter.ids.keyBlue.source = key_sources[self.tile_with_player.key_type] 306 | self.parent.star_counter.ids.keyBlue.reload() 307 | key_anim.bind(on_complete=self.tile_with_player.remove_key) 308 | if self.soundsOption: 309 | pick_key_sound = SoundLoader.load('assets/audio/pick_key.ogg') 310 | pick_key_sound.play() 311 | 312 | def player_has_all_exits(self): 313 | if self.tile_with_player.exit: 314 | self.score += 10 * self.difficulty 315 | self.parent.counter.update_counter(0, 10 * self.difficulty) 316 | for key in self.player_has_keys: 317 | if self.tile_with_player.exit_type == key_exit[key]: 318 | self.player_has_exits += 1 319 | exit_anim = Animation(x=self.tile_with_player.exit.x, 320 | y=self.tile_with_player.exit.y + 80, 321 | duration=0.15) 322 | exit_anim.start(self.tile_with_player.exit) 323 | exit_anim.bind(on_complete=self.tile_with_player.remove_exit) 324 | if self.tile_with_player.exit_type == 'lockYellow': 325 | self.parent.star_counter.ids.keyYellow.source = lock_sources[self.tile_with_player.exit_type] 326 | self.parent.star_counter.ids.keyYellow.reload() 327 | if self.tile_with_player.exit_type == 'lockGreen': 328 | self.parent.star_counter.ids.keyGreen.source = lock_sources[self.tile_with_player.exit_type] 329 | self.parent.star_counter.ids.keyGreen.reload() 330 | if self.tile_with_player.exit_type == 'lockBlue': 331 | self.parent.star_counter.ids.keyBlue.source = lock_sources[self.tile_with_player.exit_type] 332 | self.parent.star_counter.ids.keyBlue.reload() 333 | elif self.tile_with_player.exit_type == 'lockOrange': 334 | self.parent.star_counter.ids.keyOrange.source = lock_sources[self.tile_with_player.exit_type] 335 | self.parent.star_counter.ids.keyOrange.reload() 336 | if len(self.player_has_keys) == self.difficulty and self.player_has_exits == self.difficulty: 337 | self.player_won = True 338 | self.complete_level() 339 | return True 340 | 341 | def end_player_turn(self): 342 | self.end_without_movement() 343 | self.handle_portal() 344 | if self.is_player_dead(): 345 | return 346 | self.handle_star() 347 | self.handle_key() 348 | if self.player_has_all_exits(): 349 | return 350 | self.player_is_jumping = False 351 | self.parent.show_enemy_turn_splash() 352 | Clock.schedule_once(self.process_enemy_turn, 0.7) 353 | 354 | def complete_level(self): 355 | self.show_reload_popup(True) 356 | 357 | def get_tile_by_row_col(self, row, col): 358 | for tile in self.tiles: 359 | if tile.row == row and tile.col == col: 360 | return tile 361 | 362 | def get_enemy_by_tile(self, row, col): 363 | for enemy in self.enemy_list: 364 | if enemy.col == col and enemy.row == row: 365 | return enemy 366 | return 367 | 368 | def get_neighbouring_tiles(self, tile): 369 | n_tiles = [] 370 | if tile.row & 1: 371 | lookup = even_directions 372 | else: 373 | lookup = odd_directions 374 | for i in lookup: 375 | n_tile = self.get_tile_by_row_col(tile.row + i[1], tile.col + i[0]) 376 | if n_tile: 377 | from_level, to_level = level_diff(tile, n_tile) 378 | if not n_tile.actor and not n_tile.occupied and not abs(from_level - to_level) > 1 and self.get_enemy_by_tile(tile.row, tile.col): 379 | if abs(from_level - to_level) == 1 and not self.get_enemy_by_tile(tile.row, tile.col).can_fly: 380 | continue 381 | else: 382 | n_tiles.append(n_tile) 383 | 384 | return n_tiles 385 | 386 | def prepare_actor_move_data(self): 387 | for tile in self.tiles: 388 | if tile.actor: 389 | for e in self.enemy_list: 390 | if e.col == tile.col and e.row == tile.row: 391 | enemy = e 392 | break 393 | if not enemy.ended_turn: 394 | n_tiles = self.get_neighbouring_tiles(tile) 395 | if n_tiles: 396 | self.from_hex = tile 397 | for t in n_tiles: 398 | if t.player: 399 | tile_choice = t 400 | self.player_death = True 401 | break 402 | else: 403 | tile_choice = choice(n_tiles) 404 | tile_choice.occupied = True 405 | self.actor_move_data.append((tile, enemy, tile_choice)) 406 | tile.remove_actor() 407 | 408 | def process_actor_movement(self): 409 | for data in self.actor_move_data: 410 | tile = data[0] 411 | actor = data[1] 412 | to_hex = data[2] 413 | if tile.col > to_hex.col or (tile.col >= to_hex.col and tile.row % 2): 414 | actor.move_left() 415 | else: 416 | actor.move_right() 417 | from_level, to_level = level_diff(tile, to_hex) 418 | if abs(from_level - to_level) == 1: 419 | move_anim = Animation(x=to_hex.pos[0] + 10, duration=0.1) 420 | move_anim &= Animation(y=to_hex.pos[1] + 249, t='out_back', duration=0.2) 421 | else: 422 | move_anim = Animation(x=to_hex.pos[0] + 10, y=to_hex.pos[1] + 249, duration=0.2) 423 | move_anim.start(actor) 424 | 425 | def process_enemy_turn(self, dt): 426 | shuffle(self.enemy_list) 427 | self.is_moving = True 428 | self.is_move_mode = True 429 | self.prepare_actor_move_data() 430 | self.process_actor_movement() 431 | 432 | if self.actor_move_data: 433 | Clock.schedule_once(self.end_enemy_turn, 0.4) 434 | else: 435 | self.end_enemy_turn(0) 436 | 437 | def end_enemy_turn(self, dt): 438 | for data in self.actor_move_data: 439 | data[2].add_actor() 440 | data[1].col = data[2].col 441 | data[1].row = data[2].row 442 | for enemy in self.enemy_list: 443 | enemy.ended_turn = False 444 | for tile in self.tiles: 445 | if tile.actor: 446 | tile.occupied = True 447 | else: 448 | tile.occupied = False 449 | self.is_move_mode = False 450 | self.is_moving = False 451 | self.actor_move_data = [] 452 | if self.player_death: 453 | self.complete_level() 454 | self.enemy_turn = False 455 | self.parent.remove_enemy_turn_splash() 456 | 457 | def end_without_movement(self, no_move=False, show_too_far=False): 458 | self.is_move_mode = False 459 | self.is_moving = False 460 | if self.tile_with_player.player: 461 | self.tile_with_player.player.stop_player() 462 | self.remove_skip_turn() 463 | if no_move: 464 | self.enemy_turn = False 465 | if show_too_far: 466 | self.parent.show_too_far_splash() 467 | Clock.schedule_once(self.parent.remove_tile_too_far_splash, 0.6) 468 | 469 | def end_with_player_transfer(self, a, b): 470 | self.to_hex.add_player(self.player.stars, self.player.pos) 471 | self.is_move_mode = False 472 | self.is_moving = False 473 | self.to_hex.player.stop_player() 474 | self.tile_with_player = self.to_hex 475 | self.remove_widget(self.player) 476 | self.player = None 477 | self.end_player_turn() 478 | 479 | def add_bubble(self, pos, id, hex): 480 | if not self.bubble: 481 | bubble_pos = [pos[0], pos[1] + 344] 482 | self.selected_hex = hex 483 | if self.selected_hex.level == MAX_LEVEL: 484 | up_disabled = True 485 | else: 486 | up_disabled = False 487 | if self.selected_hex.level == 0: 488 | down_disabled = True 489 | else: 490 | down_disabled = False 491 | self.bubble = UpDownBubble(up_disabled, down_disabled, pos=bubble_pos) 492 | self.add_widget(self.bubble) 493 | self.has_bubble = True 494 | self.bubble_id = id 495 | 496 | def remove_bubble(self, id): 497 | if self.bubble and id == self.bubble_id: 498 | self.remove_widget(self.bubble) 499 | self.bubble = None 500 | self.selected_hex = None 501 | self.enemy_turn = False 502 | Clock.schedule_once(self.clear_bubble, 0.1) 503 | 504 | def move_hex_up(self, use_star=True): 505 | if self.tile_with_player.player.stars > 0: 506 | self.selected_hex.move_up(use_star) 507 | for enemy in self.enemy_list: 508 | if self.selected_hex.actor and enemy.col == self.selected_hex.col and enemy.row == self.selected_hex.row: 509 | actor_anim = Animation(y=enemy.pos[1] + 34, t='in_out_elastic', duration=.7) 510 | actor_anim.start(enemy) 511 | break 512 | if use_star: 513 | self.parent.counter.update_counter(-1) 514 | 515 | def move_hex_down(self, use_star=True): 516 | if self.tile_with_player.player.stars > 0: 517 | self.selected_hex.move_down(use_star) 518 | for enemy in self.enemy_list: 519 | if self.selected_hex.actor and enemy.col == self.selected_hex.col and enemy.row == self.selected_hex.row: 520 | actor_anim = Animation(y=enemy.pos[1] - 34, t='in_out_elastic', duration=.7) 521 | actor_anim.start(enemy) 522 | break 523 | if use_star: 524 | self.parent.counter.update_counter(-1) 525 | 526 | def clear_bubble(self, dt): 527 | self.has_bubble = False 528 | 529 | def show_reload_popup(self, death_or_victory=False): 530 | new_difficulty = choice([1,2,3,4]) 531 | self.popup = ReloadPopup(self, death_or_victory, new_difficulty, self.player_won) 532 | self.popup.open() 533 | 534 | 535 | class ReloadPopup(Popup): 536 | player_death = BooleanProperty(False) 537 | player_won = BooleanProperty(False) 538 | 539 | def __init__(self, context, player_death, new_difficulty, player_won, **kwargs): 540 | super(ReloadPopup, self).__init__(**kwargs) 541 | self.image = 'assets/graphics/ui/popupImg.png' 542 | self.context = context 543 | self.player_death = player_death 544 | self.player_won = player_won 545 | self.new_difficulty = new_difficulty 546 | 547 | 548 | class StarCounter(BoxLayout): 549 | stars = NumericProperty(0) 550 | score = NumericProperty(0) 551 | 552 | def __init__(self, stars, score=0, **kwargs): 553 | super(StarCounter, self).__init__(**kwargs) 554 | self.stars = stars 555 | self.score = score 556 | 557 | def update_counter(self, stars, score=0): 558 | self.stars += stars 559 | self.score += score 560 | 561 | 562 | class SkipTurnBubble(Bubble): 563 | def __init__(self, context, **kwargs): 564 | super(SkipTurnBubble, self).__init__(**kwargs) 565 | self.context = context -------------------------------------------------------------------------------- /HexTile.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | 3 | from kivy.uix.image import Image 4 | from kivy.animation import Animation 5 | from Player import Player 6 | from data.gameData import * 7 | from random import choice, random 8 | 9 | 10 | def in_circle(center_x, center_y, radius, x, y): 11 | square_dist = (center_x - x) ** 2 + (center_y - y) ** 2 12 | return square_dist <= radius ** 2 13 | 14 | 15 | class HexTile(Image): 16 | def __init__(self, row, col, type, level, has_actor, has_star, has_player, key_type, exit_type, **kwargs): 17 | super(HexTile, self).__init__(**kwargs) 18 | self.row = row 19 | self.col = col 20 | self.type = type 21 | self.level = level 22 | self.bubble = False 23 | self.actor = None 24 | self.player = None 25 | self.mod_layer = None 26 | self.game_map = None 27 | self.has_player = has_player 28 | self.star = None 29 | self.key = None 30 | self.exit = None 31 | self.key_type = key_type 32 | self.exit_type = exit_type 33 | self.occupied = False 34 | self.portal = None 35 | if random() < 0.7: 36 | self.add_mod_layer() 37 | if has_star: 38 | self.add_star() 39 | if key_type: 40 | self.add_key() 41 | if exit_type: 42 | self.add_exit() 43 | if self.portal: 44 | self.add_portal() 45 | if has_actor: 46 | self.add_actor() 47 | 48 | def post_init(self): 49 | self.game_map = self.parent.parent 50 | 51 | def on_touch_up(self, touch): 52 | if abs(touch.time_end - touch.time_start) < 0.18: 53 | x, y = self.to_local(touch.x, touch.y) 54 | if in_circle(self.x + self.width / 2, self.y + 244 + self.width / 2, self.width / 2, x, y) \ 55 | and ((not self.bubble and not self.game_map.has_bubble and not self.game_map.is_moving) or 56 | (self.game_map.is_move_mode and self.player and not self.game_map.is_moving)): 57 | if not self.player: 58 | if not self.game_map.is_move_mode and not self.game_map.is_just_selected: 59 | self.game_map.add_bubble(self.pos, self.id, self) 60 | self.bubble = True 61 | elif not self.game_map.is_just_selected: 62 | self.game_map.schedule_move_to_hex(self) 63 | elif self.player and not self.game_map.is_move_mode: 64 | self.game_map.move_mode(self) 65 | elif self.player and self.game_map.is_move_mode: 66 | self.game_map.is_move_mode = False 67 | self.player.stop_player() 68 | self.game_map.add_bubble(self.pos, self.id, self) 69 | self.game_map.remove_skip_turn() 70 | self.bubble = True 71 | else: 72 | self.game_map.remove_bubble(self.id) 73 | self.bubble = False 74 | else: 75 | if self.bubble: 76 | self.game_map.remove_bubble(self.id) 77 | self.bubble = False 78 | 79 | def add_portal(self): 80 | self.portal = Image(source='assets/graphics/tiles/portal.png', pos=(self.x + 10, self.y + 264)) 81 | self.add_widget(self.portal) 82 | 83 | def remove_portal(self): 84 | self.remove_widget(self.portal) 85 | self.portal = None 86 | 87 | def move_portal(self): 88 | if self.level == MAX_LEVEL: 89 | self.move_down(False) 90 | elif self.level == 0: 91 | self.move_up(False) 92 | else: 93 | self.move_up(False) if random() > 0.5 else self.move_down(False) 94 | 95 | def add_actor(self, actor_to_transfer=None): 96 | self.occupied = True 97 | self.actor = True 98 | 99 | def remove_actor(self, a=0, b=0): 100 | self.actor = False 101 | self.occupied = False 102 | 103 | def add_key(self): 104 | self.key = Image(source='assets/graphics/tiles/' + self.key_type + '.png', pos=(self.x + 20, self.y + 274)) 105 | self.add_widget(self.key) 106 | 107 | def remove_key(self, a, b): 108 | if self.key: 109 | self.remove_widget(self.key) 110 | self.key = None 111 | self.key_type = None 112 | 113 | def add_exit(self): 114 | self.exit = Image(source='assets/graphics/tiles/' + self.exit_type + '.png', pos=(self.x + 10, self.y + 264)) 115 | self.add_widget(self.exit) 116 | 117 | def remove_exit(self, a, b): 118 | if self.exit: 119 | self.remove_widget(self.exit) 120 | self.exit = None 121 | self.exit_type = None 122 | 123 | def add_star(self): 124 | self.star = Image(source='assets/graphics/tiles/starGold.png', pos=(self.x + STAR_POS[0], 125 | self.y + STAR_POS[1] + 204)) 126 | self.add_widget(self.star) 127 | 128 | def remove_star(self, a, b): 129 | if self.star: 130 | self.remove_widget(self.star) 131 | self.star = None 132 | 133 | def add_mod_layer(self): 134 | mod_type = choice(mod_layers) 135 | self.mod_layer = Image(source='assets/graphics/tiles/' + mod_type + '.png', 136 | pos=(self.x, self.y + 204), size=(120, 210)) 137 | self.add_widget(self.mod_layer) 138 | 139 | def add_player(self, stars=23, position=None): 140 | if position: 141 | self.player = Player(stars, pos=(position[0], position[1])) 142 | else: 143 | self.player = Player(stars, pos=(self.pos[0] + 10, self.pos[1] + 279)) 144 | self.add_widget(self.player) 145 | 146 | def remove_player(self): 147 | if self.player: 148 | self.remove_widget(self.player) 149 | self.player = None 150 | 151 | def move_up(self, use_star): 152 | if self.level == MAX_LEVEL: 153 | return 154 | self.level += 1 155 | anim = Animation(y=self.pos[1] + 34, t='in_out_elastic', duration=.7) 156 | anim.start(self) 157 | if self.player: 158 | actor_anim = Animation(y=self.player.pos[1] + 34, t='in_out_elastic', duration=.7) 159 | actor_anim.start(self.player) 160 | if self.mod_layer: 161 | actor_anim = Animation(y=self.mod_layer.pos[1] + 34, t='in_out_elastic', duration=.7) 162 | actor_anim.start(self.mod_layer) 163 | if self.star: 164 | actor_anim = Animation(y=self.star.pos[1] + 34, t='in_out_elastic', duration=.7) 165 | actor_anim.start(self.star) 166 | if self.key: 167 | actor_anim = Animation(y=self.key.pos[1] + 34, t='in_out_elastic', duration=.7) 168 | actor_anim.start(self.key) 169 | if self.exit: 170 | actor_anim = Animation(y=self.exit.pos[1] + 34, t='in_out_elastic', duration=.7) 171 | actor_anim.start(self.exit) 172 | if self.portal: 173 | portal_anim = Animation(y=self.portal.pos[1] + 34, t='in_out_elastic', duration=.7) 174 | portal_anim.start(self.portal) 175 | if use_star: 176 | self.game_map.tile_with_player.player.use_star() 177 | 178 | def move_down(self, use_star): 179 | if self.level == 0: 180 | return 181 | self.level -= 1 182 | anim = Animation(y=self.pos[1] - 34, t='in_out_elastic', duration=.7) 183 | anim.start(self) 184 | if self.player: 185 | actor_anim = Animation(y=self.player.pos[1] - 34, t='in_out_elastic', duration=.7) 186 | actor_anim.start(self.player) 187 | if self.mod_layer: 188 | actor_anim = Animation(y=self.mod_layer.pos[1] - 34, t='in_out_elastic', duration=.7) 189 | actor_anim.start(self.mod_layer) 190 | if self.star: 191 | actor_anim = Animation(y=self.star.pos[1] - 34, t='in_out_elastic', duration=.7) 192 | actor_anim.start(self.star) 193 | if self.key: 194 | actor_anim = Animation(y=self.key.pos[1] - 34, t='in_out_elastic', duration=.7) 195 | actor_anim.start(self.key) 196 | if self.exit: 197 | actor_anim = Animation(y=self.exit.pos[1] - 34, t='in_out_elastic', duration=.7) 198 | actor_anim.start(self.exit) 199 | if self.portal: 200 | portal_anim = Animation(y=self.portal.pos[1] - 34, t='in_out_elastic', duration=.7) 201 | portal_anim.start(self.portal) 202 | if use_star: 203 | self.game_map.tile_with_player.player.use_star() -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, oddbitdev 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /MainMenu.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | 3 | from kivy.uix.boxlayout import BoxLayout 4 | from kivy.uix.popup import Popup 5 | 6 | 7 | class MainMenu(BoxLayout): 8 | def __init__(self, **kwargs): 9 | super(MainMenu, self).__init__(**kwargs) 10 | self.start_game = False 11 | self.show_help = False 12 | 13 | def show_info(self): 14 | info = InfoPopup() 15 | info.open() 16 | 17 | 18 | class InfoPopup(Popup): 19 | pass -------------------------------------------------------------------------------- /MapCanvas.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | __author__ = 'oddBit' 4 | 5 | from kivy.uix.floatlayout import FloatLayout 6 | 7 | from HexScatter import HexScatter, StarCounter 8 | from MapGenerator import MapGenerator 9 | from kivy.core.window import Window 10 | from kivy.uix.image import Image 11 | from data.gameData import * 12 | 13 | class MapCanvas(FloatLayout): 14 | def __init__(self, cols, rows, difficulty, hardcoreOption, soundsOption, musicOptions, **kwargs): 15 | super(MapCanvas, self).__init__(**kwargs) 16 | self.soundsOption = soundsOption 17 | self.musicOption = musicOptions 18 | self.hardcore_option = hardcoreOption 19 | self.tile_map = None 20 | self.counter = None 21 | self.map_generator = MapGenerator(cols, rows) 22 | self.hex_tiles = self.map_generator.generate_map(difficulty, hardcoreOption) 23 | self.difficulty = difficulty 24 | tile_map = self.build_map_tiles() 25 | self.add_tile_map(tile_map) 26 | self.star_counter = StarCounter(23) 27 | self.add_counter(self.star_counter) 28 | self.return_to_menu = False 29 | self.enemy_turn_splash = None 30 | self.tile_too_far_splash = None 31 | 32 | def add_tile_map(self, tile_map): 33 | for tile in tile_map.tiles: 34 | if tile.player: 35 | px = tile.player.x 36 | py = tile.player.y 37 | break 38 | self.tile_map = tile_map 39 | self.tile_map.pos = (-px + Window.size[0] / 2, -py + Window.size[1] / 2) 40 | self.add_widget(self.tile_map) 41 | 42 | def add_counter(self, counter): 43 | self.counter = counter 44 | self.add_widget(self.counter) 45 | 46 | def go_to_main_menu(self): 47 | self.tile_map.popup.dismiss() 48 | self.return_to_menu = True 49 | 50 | def reload_level(self, new_map=False, new_difficulty=0): 51 | if new_difficulty: 52 | self.difficulty = new_difficulty 53 | if self.tile_map.popup: 54 | self.tile_map.popup.dismiss() 55 | self.clear_widgets() 56 | self.tile_map = None 57 | if new_map: 58 | self.hex_tiles = None 59 | print (new_difficulty) 60 | rows, cols = map_sizes[new_difficulty-1] 61 | self.map_generator = MapGenerator(cols, rows) 62 | self.hex_tiles = self.map_generator.generate_map(self.difficulty, self.hardcore_option) 63 | tile_map = self.build_map_tiles() 64 | self.add_tile_map(tile_map) 65 | self.star_counter = StarCounter(23) 66 | self.add_counter(self.star_counter) 67 | 68 | def show_enemy_turn_splash(self): 69 | self.enemy_turn_splash = Image(source='assets/graphics/ui/enemyTurn.png', size_hint=(1, 1), pos_hint={'x': 0, 'y': 0}) 70 | self.add_widget(self.enemy_turn_splash) 71 | 72 | def remove_enemy_turn_splash(self): 73 | if self.enemy_turn_splash: 74 | self.remove_widget(self.enemy_turn_splash) 75 | 76 | def show_too_far_splash(self): 77 | self.tile_too_far_splash = Image(source='assets/graphics/ui/tileTooFar.png', size_hint=(1, 1), pos_hint={'x': 0, 'y': 0}) 78 | self.add_widget(self.tile_too_far_splash) 79 | 80 | def remove_tile_too_far_splash(self, dt): 81 | if self.tile_too_far_splash: 82 | self.remove_widget(self.tile_too_far_splash) 83 | 84 | def build_map_tiles(self): 85 | return HexScatter(self.hex_tiles, self.difficulty, self.soundsOption, self.musicOption, 86 | size=[18 * 120 + 1080, 22 * 120 + 1920], pos=[0, 0], 87 | auto_bring_to_front=False) 88 | 89 | -------------------------------------------------------------------------------- /MapGenerator.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | 3 | from random import choice, random 4 | 5 | from data.gameData import * 6 | 7 | 8 | def get_new_type(current_type): 9 | if random() > 0.8: 10 | return choice(tiles) 11 | else: 12 | return current_type 13 | 14 | 15 | class Tile: 16 | def __init__(self, row, col, has_player, has_land, type, has_star, has_actor, level, key_type, exit_type, portal): 17 | self.exit_type = exit_type 18 | self.key_type = key_type 19 | self.level = level 20 | self.has_actor = has_actor 21 | self.has_star = has_star 22 | self.type = type 23 | self.col = col 24 | self.row = row 25 | self.has_player = has_player 26 | self.has_land = has_land 27 | self.has_portal = portal 28 | 29 | 30 | class MapGenerator: 31 | def __init__(self, h_tiles, v_tiles): 32 | self.h_tiles = h_tiles 33 | self.v_tiles = v_tiles 34 | 35 | def generate_map(self, difficulty, hardcoreOption=False, clone=False): 36 | hex_tiles = [] 37 | landed_tiles = [] 38 | player_tiles = [] 39 | exit_tiles = [] 40 | key_tiles = [] 41 | for row in reversed(range(self.v_tiles)): 42 | for col in range(self.h_tiles): 43 | hex_tiles.append(Tile(row, col, False, True, None, False, False, 0, None, None, False)) 44 | keys_and_exits = [] 45 | i = 0 46 | for k, v in key_exit.items(): 47 | if i < difficulty: 48 | keys_and_exits.append((k, v)) 49 | i += 1 50 | type_choice = choice(tiles) 51 | for tile in hex_tiles: 52 | if (tile.col == 0 or tile.col == self.h_tiles - 1) and random() > 0.7: 53 | tile.has_land = False 54 | if (tile.row == 0 or tile.row == self.v_tiles - 1) and random() > 0.7: 55 | tile.has_land = False 56 | if random() > 0.99 and (3 < tile.col < self.h_tiles - 3) and (3 < tile.row < self.v_tiles - 3): 57 | tile.has_land = False 58 | if random() > 0.55: 59 | pass 60 | elif random() > 0.6: 61 | tile.level = 1 62 | elif random() > 0.8: 63 | tile.level = 2 64 | elif random() > 0.9: 65 | tile.level = 3 66 | if tile.has_land: 67 | if tile.row <= 1: 68 | player_tiles.append(tile) 69 | else: 70 | key_tiles.append(tile) 71 | if tile.row >= self.v_tiles - 3: 72 | exit_tiles.append(tile) 73 | 74 | landed_tiles.append(tile) 75 | tile.type = type_choice 76 | type_choice = get_new_type(type_choice) 77 | 78 | player_tile = choice(player_tiles) 79 | if clone: 80 | i = player_tiles.index(player_tile) 81 | player_tiles.pop(i) 82 | clone_tile = choice(player_tiles) 83 | clone_tile.has_player = True 84 | if clone_tile.has_actor: 85 | clone_tile.has_actor = False 86 | player_tile.has_player = True 87 | 88 | picked_keys = [] 89 | picked_exits = [] 90 | for k, v in keys_and_exits: 91 | picked_exit = False 92 | picked_key = False 93 | while not picked_exit and not picked_key: 94 | key = choice(key_tiles) 95 | exit = choice(exit_tiles) 96 | if key != exit and exit not in picked_exits and key not in picked_keys and exit not in picked_keys and key not in picked_exits: 97 | picked_key = True 98 | picked_exit = True 99 | picked_keys.append(key) 100 | picked_exits.append(exit) 101 | key.key_type = k 102 | exit.exit_type = v 103 | key.has_actor = True 104 | exit.has_actor = True 105 | if hardcoreOption: 106 | hardMod = 0.15 107 | else: 108 | hardMod = 0 109 | picked_portal = False 110 | while not picked_portal and hardcoreOption: 111 | portal_tile = choice(key_tiles) 112 | if hardcoreOption and not picked_portal and portal_tile.has_land and not portal_tile.has_player and not portal_tile.has_actor and not portal_tile.key_type and not portal_tile.exit_type and portal_tile.row < self.v_tiles - 1: 113 | portal_tile.has_portal = True 114 | picked_portal = True 115 | for tile in hex_tiles: 116 | if tile.has_land and not tile.has_player and not tile.has_portal: 117 | if random() > 0.8 - hardMod and tile.row != player_tile.row: 118 | tile.has_actor = True 119 | if random() > 0.5: 120 | tile.has_star = True 121 | 122 | return hex_tiles 123 | 124 | -------------------------------------------------------------------------------- /Player.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | 3 | from kivy.uix.image import Image 4 | from random import choice 5 | from data.gameData import * 6 | 7 | 8 | class Player(Image): 9 | def __init__(self, stars, **kwargs): 10 | super(Player, self).__init__(**kwargs) 11 | self.state = PlayerStates.SIT_LEFT 12 | self.source = player_sprites[self.state] 13 | self.stars = stars 14 | self.anim_delay = 0.2 15 | 16 | def select_player(self): 17 | if self.state == PlayerStates.SIT_LEFT: 18 | self.state = PlayerStates.SIT_LEFT_SELECTED 19 | else: 20 | self.state = PlayerStates.SIT_RIGHT_SELECTED 21 | self.source = player_sprites[self.state] 22 | 23 | def stop_player(self): 24 | self.state = choice([PlayerStates.SIT_LEFT, PlayerStates.SIT_RIGHT]) 25 | self.source = player_sprites[self.state] 26 | 27 | def move_left(self): 28 | self.state = PlayerStates.WALK_LEFT 29 | self.source = player_sprites[self.state] 30 | 31 | def move_right(self): 32 | self.state = PlayerStates.WALK_RIGHT 33 | self.source = player_sprites[self.state] 34 | 35 | def jump_left(self): 36 | self.state = PlayerStates.JUMP_LEFT 37 | self.source = player_sprites[self.state] 38 | 39 | def jump_right(self): 40 | self.state = PlayerStates.JUMP_RIGHT 41 | self.source = player_sprites[self.state] 42 | 43 | def player_dead(self): 44 | self.state = PlayerStates.DEAD 45 | self.source = player_sprites[self.state] 46 | 47 | def add_star(self): 48 | self.stars += 1 49 | 50 | def use_star(self): 51 | self.stars -= 1 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | hexTap 2 | ====== 3 | 4 | hexTap game source for https://play.google.com/store/apps/details?id=com.oddbitstudio.hextap 5 | The game was made as a learning exercise in game development in general and Kivy in particular over the course of three weeks with around 90 - 100 hours invested 6 | 7 | If you plan to run this on desktop, you will need to install Kivy, and if you encounter any issues with the options settings (though I have hacked away in main.py to make sure that the loader is ready for whatever format the platform tries to save the settings) set self.musicOption, self.soundsOption and self.hardcoreOption manually to whatever you like and delete their respective get statements in the build method together with the if statements from build and on_config_change. 8 | -------------------------------------------------------------------------------- /SettingsJson.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | 3 | import json 4 | 5 | settings_json = json.dumps([ 6 | {'type': 'title', 7 | 'title': 'game settings'}, 8 | {'type': 'bool', 9 | 'title': 'hardcore mode', 10 | 'desc': 'adds enemies and a portal to the map', 11 | 'section': 'game', 12 | 'key': 'hardcoreOption'}, 13 | {'type': 'title', 14 | 'title': 'sound settings'}, 15 | {'type': 'bool', 16 | 'title': 'play music', 17 | 'desc': '', 18 | 'section': 'sound', 19 | 'key': 'musicOption'}, 20 | {'type': 'bool', 21 | 'title': 'play game sounds', 22 | 'desc': '', 23 | 'section': 'sound', 24 | 'key': 'soundsOption'} 25 | ]) -------------------------------------------------------------------------------- /UpDownBubble.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | 3 | from kivy.uix.bubble import Bubble 4 | from kivy.properties import BooleanProperty 5 | 6 | 7 | class UpDownBubble(Bubble): 8 | up_disabled = BooleanProperty(False) 9 | down_disabled = BooleanProperty(False) 10 | 11 | def __init__(self, up_disabled=False, down_disabled=False, **kwargs): 12 | super(UpDownBubble, self).__init__(**kwargs) 13 | self.up_disabled = up_disabled 14 | self.down_disabled = down_disabled 15 | 16 | def up(self): 17 | self.parent.parent.move_hex_up() 18 | 19 | def down(self): 20 | self.parent.parent.move_hex_down() -------------------------------------------------------------------------------- /assets/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | -------------------------------------------------------------------------------- /assets/audio/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | -------------------------------------------------------------------------------- /assets/audio/enemy_death.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/audio/enemy_death.ogg -------------------------------------------------------------------------------- /assets/audio/game_over.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/audio/game_over.ogg -------------------------------------------------------------------------------- /assets/audio/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/audio/music.ogg -------------------------------------------------------------------------------- /assets/audio/pick_key.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/audio/pick_key.ogg -------------------------------------------------------------------------------- /assets/audio/pick_star.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/audio/pick_star.ogg -------------------------------------------------------------------------------- /assets/audio/player_jump.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/audio/player_jump.ogg -------------------------------------------------------------------------------- /assets/audio/player_walk.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/audio/player_walk.ogg -------------------------------------------------------------------------------- /assets/graphics/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | -------------------------------------------------------------------------------- /assets/graphics/enemies/blobLeft.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/enemies/blobLeft.zip -------------------------------------------------------------------------------- /assets/graphics/enemies/blobRight.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/enemies/blobRight.zip -------------------------------------------------------------------------------- /assets/graphics/enemies/flyLeft.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/enemies/flyLeft.zip -------------------------------------------------------------------------------- /assets/graphics/enemies/flyRight.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/enemies/flyRight.zip -------------------------------------------------------------------------------- /assets/graphics/enemies/spiderLeft.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/enemies/spiderLeft.zip -------------------------------------------------------------------------------- /assets/graphics/enemies/spiderRight.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/enemies/spiderRight.zip -------------------------------------------------------------------------------- /assets/graphics/player/playerDeath.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/player/playerDeath.png -------------------------------------------------------------------------------- /assets/graphics/player/playerJumpLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/player/playerJumpLeft.png -------------------------------------------------------------------------------- /assets/graphics/player/playerJumpRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/player/playerJumpRight.png -------------------------------------------------------------------------------- /assets/graphics/player/playerStandLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/player/playerStandLeft.png -------------------------------------------------------------------------------- /assets/graphics/player/playerStandLeftSelected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/player/playerStandLeftSelected.png -------------------------------------------------------------------------------- /assets/graphics/player/playerStandRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/player/playerStandRight.png -------------------------------------------------------------------------------- /assets/graphics/player/playerStandRightSelected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/player/playerStandRightSelected.png -------------------------------------------------------------------------------- /assets/graphics/player/playerWalkLeft.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/player/playerWalkLeft.zip -------------------------------------------------------------------------------- /assets/graphics/player/playerWalkRight.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/player/playerWalkRight.zip -------------------------------------------------------------------------------- /assets/graphics/tiles/keyBlue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/keyBlue.png -------------------------------------------------------------------------------- /assets/graphics/tiles/keyGreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/keyGreen.png -------------------------------------------------------------------------------- /assets/graphics/tiles/keyOrange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/keyOrange.png -------------------------------------------------------------------------------- /assets/graphics/tiles/keyYellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/keyYellow.png -------------------------------------------------------------------------------- /assets/graphics/tiles/lockBlue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/lockBlue.png -------------------------------------------------------------------------------- /assets/graphics/tiles/lockGreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/lockGreen.png -------------------------------------------------------------------------------- /assets/graphics/tiles/lockOrange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/lockOrange.png -------------------------------------------------------------------------------- /assets/graphics/tiles/lockYellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/lockYellow.png -------------------------------------------------------------------------------- /assets/graphics/tiles/noKey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/noKey.png -------------------------------------------------------------------------------- /assets/graphics/tiles/overlay01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/overlay01.png -------------------------------------------------------------------------------- /assets/graphics/tiles/overlay02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/overlay02.png -------------------------------------------------------------------------------- /assets/graphics/tiles/overlay03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/overlay03.png -------------------------------------------------------------------------------- /assets/graphics/tiles/overlay04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/overlay04.png -------------------------------------------------------------------------------- /assets/graphics/tiles/overlay05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/overlay05.png -------------------------------------------------------------------------------- /assets/graphics/tiles/overlay06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/overlay06.png -------------------------------------------------------------------------------- /assets/graphics/tiles/portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/portal.png -------------------------------------------------------------------------------- /assets/graphics/tiles/powerup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/powerup.png -------------------------------------------------------------------------------- /assets/graphics/tiles/starGold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/starGold.png -------------------------------------------------------------------------------- /assets/graphics/tiles/tileAutumn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/tileAutumn.png -------------------------------------------------------------------------------- /assets/graphics/tiles/tileDirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/tileDirt.png -------------------------------------------------------------------------------- /assets/graphics/tiles/tileDirtOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/tileDirtOver.png -------------------------------------------------------------------------------- /assets/graphics/tiles/tileGrass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/tileGrass.png -------------------------------------------------------------------------------- /assets/graphics/tiles/tileLava.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/tileLava.png -------------------------------------------------------------------------------- /assets/graphics/tiles/tileMagic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/tileMagic.png -------------------------------------------------------------------------------- /assets/graphics/tiles/tileRock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/tileRock.png -------------------------------------------------------------------------------- /assets/graphics/tiles/tileSand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/tileSand.png -------------------------------------------------------------------------------- /assets/graphics/tiles/tileSnow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/tileSnow.png -------------------------------------------------------------------------------- /assets/graphics/tiles/tileStone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/tileStone.png -------------------------------------------------------------------------------- /assets/graphics/tiles/tileWater.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/tiles/tileWater.png -------------------------------------------------------------------------------- /assets/graphics/ui/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/arrow.png -------------------------------------------------------------------------------- /assets/graphics/ui/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/background.png -------------------------------------------------------------------------------- /assets/graphics/ui/button_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/button_down.png -------------------------------------------------------------------------------- /assets/graphics/ui/button_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/button_normal.png -------------------------------------------------------------------------------- /assets/graphics/ui/downdis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/downdis.png -------------------------------------------------------------------------------- /assets/graphics/ui/enemyTurn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/enemyTurn.png -------------------------------------------------------------------------------- /assets/graphics/ui/goal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/goal.png -------------------------------------------------------------------------------- /assets/graphics/ui/killenemies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/killenemies.png -------------------------------------------------------------------------------- /assets/graphics/ui/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/loading.png -------------------------------------------------------------------------------- /assets/graphics/ui/movement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/movement.png -------------------------------------------------------------------------------- /assets/graphics/ui/playerselect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/playerselect.png -------------------------------------------------------------------------------- /assets/graphics/ui/popupImg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/popupImg.png -------------------------------------------------------------------------------- /assets/graphics/ui/portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/portal.png -------------------------------------------------------------------------------- /assets/graphics/ui/stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/stars.png -------------------------------------------------------------------------------- /assets/graphics/ui/tileTooFar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/tileTooFar.png -------------------------------------------------------------------------------- /assets/graphics/ui/tileselect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/tileselect.png -------------------------------------------------------------------------------- /assets/graphics/ui/turnSkip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/turnSkip.png -------------------------------------------------------------------------------- /assets/graphics/ui/updis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/updis.png -------------------------------------------------------------------------------- /assets/graphics/ui/updown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/assets/graphics/ui/updown.png -------------------------------------------------------------------------------- /data/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | -------------------------------------------------------------------------------- /data/gameData.py: -------------------------------------------------------------------------------- 1 | __author__ = 'oddBit' 2 | 3 | from Enum import Enum 4 | 5 | tiles = ('tileAutumn', 'tileDirt', 'tileGrass', 'tileLava', 'tileMagic', 'tileRock', 'tileSand', 'tileSnow', 'tileSnow', 6 | 'tileStone', 'tileWater') 7 | 8 | mod_layers = ('overlay01', 'overlay02', 'overlay03', 'overlay04', 'overlay05', 'overlay06') 9 | 10 | enemies = ('blob', 'spider', 'fly') 11 | 12 | even_directions = ([1, 1], [1, 0], [1, -1], [0, -1], [-1, 0], [0, 1]) 13 | odd_directions = ([0, 1], [1, 0], [0, -1], [-1, -1], [-1, 0], [-1, 1]) 14 | 15 | key_exit = {'keyBlue': 'lockBlue', 'keyGreen': 'lockGreen', 'keyOrange': 'lockOrange', 'keyYellow': 'lockYellow'} 16 | 17 | key_sources = {'keyBlue': 'assets/graphics/tiles/keyBlue.png', 'keyGreen': 'assets/graphics/tiles/keyGreen.png', 18 | 'keyOrange': 'assets/graphics/tiles/keyOrange.png', 'keyYellow': 'assets/graphics/tiles/keyYellow.png'} 19 | 20 | lock_sources = {'lockBlue': 'assets/graphics/tiles/lockBlue.png', 'lockGreen': 'assets/graphics/tiles/lockGreen.png', 21 | 'lockOrange': 'assets/graphics/tiles/lockOrange.png', 22 | 'lockYellow': 'assets/graphics/tiles/lockYellow.png'} 23 | 24 | flyables = 'fly' 25 | 26 | map_sizes = ((6, 7), (7, 8), (7, 8), (8, 9)) 27 | 28 | PlayerStates = Enum( 29 | ['SIT_LEFT', 'SIT_RIGHT', 'SIT_LEFT_SELECTED', 'SIT_RIGHT_SELECTED', 'JUMP_LEFT', 'JUMP_RIGHT', 'WALK_LEFT', 30 | 'WALK_RIGHT', 'DEAD']) 31 | 32 | ActorStated = Enum(['SIT_LEFT', 'SIT_RIGHT', 'JUMP_LEFT', 'JUMP_RIGHT', 'WALK_LEFT', 'WALK_RIGHT', 'DEAD']) 33 | 34 | player_sprites = {'SIT_LEFT': 'assets/graphics/player/playerStandLeft.png', 35 | 'SIT_RIGHT': 'assets/graphics/player/playerStandRight.png', 36 | 'SIT_LEFT_SELECTED': 'assets/graphics/player/playerStandLeftSelected.png', 37 | 'SIT_RIGHT_SELECTED': 'assets/graphics/player/playerStandRightSelected.png', 38 | 'JUMP_LEFT': 'assets/graphics/player/playerJumpLeft.png', 39 | 'JUMP_RIGHT': 'assets/graphics/player/playerJumpRight.png', 40 | 'WALK_LEFT': 'assets/graphics/player/playerWalkLeft.zip', 41 | 'WALK_RIGHT': 'assets/graphics/player/playerWalkRight.zip', 42 | 'DEAD': 'assets/graphics/player/playerDeath.png'} 43 | 44 | help_screens = ['tileselect', 'stars', 'playerselect', 'movement', 'killenemies', 'goal', 'portal'] 45 | 46 | MAX_LEVEL = 3 47 | TREE_POS = ((30, 115), (30, 75), (50, 65)) 48 | FLOWER_POS = (((-15, 80), (15, 45), (35, 65)), ((-25, 60), (15, 85), (-5, 75)), ((45, 60), (25, 55), (35, 85)), 49 | ((45, 60), (30, 65), (-25, 55))) 50 | STAR_POS = (-20, 90) -------------------------------------------------------------------------------- /gameScreen.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.8.0 2 | #:import Window kivy.core.window.Window 3 | 4 | size_hint: None, None 5 | Image: 6 | source: self.source 7 | size: self.texture_size 8 | 9 | 10 | size_hint: None, None 11 | Image: 12 | source: self.source 13 | size: self.texture_size 14 | 15 | 16 | size_hint: 0.9, 0.4 17 | auto_dismiss: False if root.player_death else True 18 | title: 'Confirm?' 19 | BoxLayout: 20 | orientation: 'vertical' 21 | Label: 22 | size_hint: 1, 0.4 23 | text: 'You Won! Reload current map or generate a new one?' if root.player_won else ('You Lost! Reload current map or generate a new one?' if root.player_death else 'Reload current map or generate a new one?') 24 | font_size: 25 25 | text_size: self.size 26 | valign: 'middle' 27 | halign: 'center' 28 | BoxLayout: 29 | size_hint: 1, 0.6 30 | orientation: 'vertical' 31 | BoxLayout: 32 | size_hint: 1, 0.5 33 | Button: 34 | size_hint: 0.5, 1 35 | text: 'Reload' 36 | on_release: root.context.parent.reload_level() 37 | background_normal: 'assets/graphics/ui/button_normal.png' 38 | background_down: 'assets/graphics/ui/button_down.png' 39 | Button: 40 | size_hint: 0.5, 1 41 | text: 'New' 42 | on_release: root.context.parent.reload_level(True, root.new_difficulty) 43 | background_normal: 'assets/graphics/ui/button_normal.png' 44 | background_down: 'assets/graphics/ui/button_down.png' 45 | Button: 46 | size_hint: 1, 0.5 47 | text: 'Main Menu' 48 | on_release: root.context.parent.go_to_main_menu() 49 | background_normal: 'assets/graphics/ui/button_normal.png' 50 | background_down: 'assets/graphics/ui/button_down.png' 51 | 52 | 53 | canvas: 54 | Color: 55 | rgba: 80*(1/255.), 106*(1/255.), 210*(1/255.), 0.8 56 | Rectangle: 57 | size: self.size 58 | pos: self.pos 59 | size_hint: 1, 0.05 60 | pos: (0, Window.size[1] - self.size[1]) if self.parent else (0, 0) 61 | BoxLayout: 62 | BoxLayout: 63 | size_hint: 0.2, 1 64 | Image: 65 | size_hint: 0.2, 1 66 | source: 'assets/graphics/tiles/starGold.png' 67 | size: self.texture_size 68 | valign: True 69 | Label: 70 | size_hint: 0.3, 1 71 | text: str(root.stars) if root.stars else "0" 72 | text_size: self.size 73 | font_size: 30 74 | bold: True 75 | color: (250/255., 220/255., 50/255., 1) 76 | valign: 'middle' 77 | BoxLayout: 78 | size_hint: 0.12, 1 79 | Image: 80 | id: keyYellow 81 | source: 'assets/graphics/tiles/noKey.png' 82 | size: self.texture_size 83 | BoxLayout: 84 | size_hint: 0.12, 1 85 | Image: 86 | id: keyGreen 87 | source: 'assets/graphics/tiles/noKey.png' 88 | size: self.texture_size 89 | BoxLayout: 90 | size_hint: 0.12, 1 91 | Image: 92 | id: keyOrange 93 | source: 'assets/graphics/tiles/noKey.png' 94 | size: self.texture_size 95 | BoxLayout: 96 | size_hint: 0.12, 1 97 | Image: 98 | id: keyBlue 99 | source: 'assets/graphics/tiles/noKey.png' 100 | size: self.texture_size 101 | BoxLayout: 102 | size_hint: 0.15, 1 103 | Label: 104 | text: str(root.score) 105 | text_size: self.size 106 | font_size: 30 107 | bold: True 108 | color: (250/255., 220/255., 50/255., 1) 109 | valign: 'middle' 110 | BoxLayout: 111 | size_hint: 0.17, 1 112 | Button: 113 | text: 'menu' 114 | on_release: root.parent.tile_map.show_reload_popup() 115 | background_normal: 'assets/graphics/ui/button_normal.png' 116 | background_down: 'assets/graphics/ui/button_down.png' 117 | 118 | 119 | size_hint: None, None 120 | size: (130, 70) 121 | pos: self.pos 122 | border: [0, 0, 0, 0] 123 | background_image: 'assets/graphics/ui/updown.png' if (upButton.disabled == False and downButton.disabled == False) else ('assets/graphics/ui/updis.png' if upButton.disabled == True else 'assets/graphics/ui/downdis.png') 124 | arrow_image: 'assets/graphics/ui/arrow.png' 125 | 126 | BubbleButton: 127 | id: upButton 128 | text: '' 129 | disabled: root.up_disabled if root.up_disabled else False 130 | on_press: root.up() 131 | BubbleButton: 132 | id: downButton 133 | text: '' 134 | disabled: root.down_disabled if root.down_disabled else False 135 | on_press: root.down() 136 | 137 | 138 | size_hint: None, None 139 | size: (48, 45) 140 | pos: self.pos 141 | border: [0, 0, 0, 0] 142 | background_image: 'assets/graphics/ui/turnSkip.png' 143 | arrow_image: 'assets/graphics/ui/arrow.png' 144 | BubbleButton: 145 | id: skipTurnButton 146 | text: '' 147 | on_release: root.context.end_player_turn() 148 | 149 | : 150 | id: hexTile 151 | size_hint: None, None 152 | size: self.texture_size 153 | 154 | 155 | : 156 | size_hint: None, None 157 | do_rotation: False 158 | scale_min: 0.5 159 | scale_max: 1.6 160 | 161 | 162 | size_hint: 1, 1 163 | canvas: 164 | Color: 165 | rgb: 90*(1/255.), 206*(1/255.), 210*(1/255.) 166 | Rectangle: 167 | size: self.size 168 | pos: self.pos 169 | orientation: 'vertical' 170 | 171 | 172 | size_hint: 1, 1 -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/icon.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | 4 | __author__ = 'oddBit' 5 | 6 | import kivy 7 | 8 | kivy.require('1.8.0') 9 | from kivy.app import App 10 | from kivy.lang import Builder 11 | from MapCanvas import MapCanvas 12 | from MainMenu import MainMenu 13 | from kivy.uix.boxlayout import BoxLayout 14 | from kivy.clock import Clock 15 | from kivy.core.window import Window 16 | from kivy.uix.image import Image 17 | from SettingsJson import settings_json 18 | from kivy.core.audio import SoundLoader 19 | from HelpMenu import HelpMenu 20 | from kivy.utils import platform 21 | from data.gameData import * 22 | 23 | try: 24 | from revmob import RevMob as revmob 25 | use_ads = True 26 | except ImportError: 27 | use_ads = False 28 | 29 | Builder.load_file('gameScreen.kv') 30 | Builder.load_file('mainMenu.kv') 31 | 32 | if use_ads: 33 | if platform() == 'android': 34 | REVMOB_APP_ID = 'android_id' 35 | elif platform() == 'ios': 36 | REVMOB_APP_ID = 'ios_id' 37 | else: 38 | REVMOB_APP_ID = 'unknown platform for RevMob' 39 | 40 | revmob.start_session(REVMOB_APP_ID) 41 | 42 | 43 | class HexTap(App): 44 | def __init__(self, **kwargs): 45 | super(HexTap, self).__init__(**kwargs) 46 | self.difficulty = 1 47 | self.first_run = True 48 | self.current_state = 'mainMenu' 49 | self.content = BoxLayout() 50 | self.main_menu = MainMenu() 51 | self.help_menu = HelpMenu() 52 | self.game_map = None 53 | self.content.add_widget(self.main_menu) 54 | self.music = SoundLoader.load('assets/audio/music.ogg') 55 | self.soundsOption = True 56 | self.musicOption = False 57 | self.hardcoreOption = False 58 | 59 | def safe_options_loading(self): 60 | if type(self.soundsOption) == bool or type(self.soundsOption) == int: 61 | pass 62 | elif self.soundsOption == 'True' or '1' in self.soundsOption: 63 | self.soundsOption = True 64 | else: 65 | self.soundsOption = False 66 | if type(self.musicOption) == bool or type(self.musicOption) == int: 67 | pass 68 | elif self.musicOption == 'True' or '1' in self.musicOption: 69 | self.musicOption = True 70 | else: 71 | self.musicOption = False 72 | if type(self.hardcoreOption) == bool or type(self.hardcoreOption) == int: 73 | pass 74 | elif self.hardcoreOption == 'True' or '1' in self.hardcoreOption: 75 | self.hardcoreOption = True 76 | else: 77 | self.hardcoreOption = False 78 | 79 | def build(self): 80 | self.use_kivy_settings = False 81 | self.soundsOption = self.config.get('sound', 'soundsOption') 82 | self.musicOption = self.config.get('sound', 'musicOption') 83 | self.hardcoreOption = self.config.get('game', 'hardcoreOption') 84 | self.safe_options_loading() 85 | Clock.schedule_interval(self.update, 1. / 1.5) 86 | return self.content 87 | 88 | def show_help(self): 89 | self.content.clear_widgets() 90 | self.content.add_widget(self.help_menu) 91 | 92 | def generate_main_menu(self): 93 | self.content.clear_widgets() 94 | self.content.add_widget(self.main_menu) 95 | 96 | def new_game(self): 97 | if use_ads: 98 | revmob.show_popup() 99 | self.content.clear_widgets() 100 | self.content.add_widget(Image(source='assets/graphics/ui/loading.png', size=Window.size, allow_stretch=True)) 101 | Clock.schedule_once(self.post_splash) 102 | 103 | def post_splash(self, dt): 104 | rows, cols = map_sizes[self.difficulty-1] 105 | self.game_map = MapCanvas(cols, rows, self.difficulty, self.hardcoreOption, self.soundsOption, 106 | self.musicOption) 107 | self.content.clear_widgets() 108 | self.content.add_widget(self.game_map) 109 | 110 | def update(self, dt): 111 | if self.main_menu.start_game: 112 | self.new_game() 113 | self.main_menu.start_game = False 114 | if self.main_menu.show_help: 115 | self.show_help() 116 | self.main_menu.show_help = False 117 | if self.help_menu.back_to_main: 118 | self.generate_main_menu() 119 | self.help_menu.back_to_main = False 120 | if self.game_map and self.game_map.return_to_menu: 121 | self.generate_main_menu() 122 | self.game_map.return_to_menu = False 123 | if self.musicOption and self.music.state == 'stop': 124 | self.music.play() 125 | elif not self.musicOption and self.music.state == 'play': 126 | self.music.stop() 127 | 128 | def build_config(self, config): 129 | try: 130 | config.setdefaults('sound', { 131 | 'musicOption': '0', 132 | 'soundsOption': '1' 133 | }) 134 | config.setdefaults('game', { 135 | 'hardcoreOption': '0' 136 | }) 137 | except TypeError: 138 | config.setdefaults('sound', { 139 | 'musicOption': False, 140 | 'soundsOption': True 141 | }) 142 | config.setdefaults('game', { 143 | 'hardcoreOption': False 144 | }) 145 | 146 | def build_settings(self, settings): 147 | settings.add_json_panel('hexTap options', self.config, data=settings_json) 148 | 149 | def on_config_change(self, config, section, key, value): 150 | if key == 'musicOption': 151 | if type(value) == bool or type(value) == int: 152 | self.musicOption = value 153 | elif value == 'True' or '1' in value: 154 | self.musicOption = True 155 | else: 156 | self.musicOption = False 157 | elif key == 'soundsOption': 158 | if type(value) == bool or type(value) == int: 159 | self.soundsOption = value 160 | elif value == 'True' or '1' in value: 161 | self.soundsOption = True 162 | else: 163 | self.soundsOption = False 164 | elif key == 'hardcoreOption': 165 | if type(value) == bool or type(value) == int: 166 | self.hardcoreOption = value 167 | elif value == 'True' or '1' in value: 168 | self.hardcoreOption = True 169 | else: 170 | self.hardcoreOption = False 171 | 172 | def on_pause(self): 173 | return True 174 | 175 | def on_resume(self): 176 | pass 177 | 178 | 179 | if __name__ == '__main__': 180 | HexTap().run() 181 | -------------------------------------------------------------------------------- /mainMenu.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.8.0 2 | #:import Window kivy.core.window.Window 3 | 4 | canvas.before: 5 | BorderImage: 6 | source: 'assets/graphics/ui/background.png' 7 | size: Window.size 8 | AnchorLayout: 9 | anchor_y: 'bottom' 10 | Label: 11 | text: '' 12 | size_hint: 1, 0.8 13 | AnchorLayout: 14 | size_hint: 0.7, 0.2 15 | BoxLayout: 16 | orientation: 'vertical' 17 | Button: 18 | size_hint: 1, 0.5 19 | text: 'New Game' 20 | halign: 'right' 21 | on_release: root.start_game = True 22 | background_normal: 'assets/graphics/ui/button_normal.png' 23 | background_down: 'assets/graphics/ui/button_down.png' 24 | BoxLayout: 25 | size_hint: 1, 0.5 26 | Button: 27 | size_hint: 0.2, 1 28 | text: '?' 29 | halign: 'center' 30 | on_release: root.show_help = True 31 | background_normal: 'assets/graphics/ui/button_normal.png' 32 | background_down: 'assets/graphics/ui/button_down.png' 33 | Button: 34 | size_hint: 0.6, 1 35 | text: 'Options' 36 | halign: 'center' 37 | on_release: app.open_settings() 38 | background_normal: 'assets/graphics/ui/button_normal.png' 39 | background_down: 'assets/graphics/ui/button_down.png' 40 | Button: 41 | size_hint: 0.2, 1 42 | text: u"\u00A9" 43 | on_release: root.show_info() 44 | background_normal: 'assets/graphics/ui/button_normal.png' 45 | background_down: 'assets/graphics/ui/button_down.png' 46 | 47 | 48 | 49 | size_hint: 0.9, 0.6 50 | title: 'hexTap' 51 | BoxLayout: 52 | orientation: 'vertical' 53 | Label: 54 | size_hint: 1, 0.4 55 | text: 'Game design and programming: oddBit \n Graphics and sounds: kenney.nl under CC0 license\n Music by Edward Shallow under CC Attribution-ShareAlike 4.0 International license' 56 | font_size: 25 57 | text_size: self.size 58 | line_height: 1.5 59 | valign: 'middle' 60 | halign: 'center' -------------------------------------------------------------------------------- /presplash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oddbitdev/hexTap/1cbe0d0a506b84dbf417614432e34c0db0c32e93/presplash.png --------------------------------------------------------------------------------