--- /dev/null
+Author: Reiner Herrmann <reiner@reiner-h.de>
+Description: Port game to python3
+Bug-Debian: https://bugs.debian.org/912500
+
+--- a/run_game.py
++++ b/run_game.py
+@@ -1,4 +1,4 @@
+-#! /usr/bin/env python
++#! /usr/bin/env python3
+
+ import sys
+ import os
+--- a/lib/util.py
++++ b/lib/util.py
+@@ -113,12 +113,12 @@
+ try:\r
+ conffile = codecs.open(file_path, "w", "utf_8")\r
+ for world in WORLDS:\r
+- print >> conffile, "unlocked\t%(world)s\t%(unlocked)s" % {"world": world, "unlocked": Variables.vdict["unlocked" + world]}\r
+- print >> conffile, "hiscore\t%(world)s\t%(hiscore)s" % {"world": world, "hiscore": Variables.vdict["hiscore" + world]}\r
+- print >> conffile, "besttime\t%(world)s\t%(besttime)s" % {"world": world, "besttime": Variables.vdict["besttime" + world]}\r
+- print >> conffile, "sound\t%s" % bool_to_str(Variables.vdict["sound"])\r
+- print >> conffile, "dialogue\t%s" % bool_to_str(Variables.vdict["dialogue"])\r
+- print >> conffile, "fullscreen\t%s" % bool_to_str(Variables.vdict["fullscreen"])\r
++ print("unlocked\t%(world)s\t%(unlocked)s" % {"world": world, "unlocked": Variables.vdict["unlocked" + world]}, file=conffile)\r
++ print("hiscore\t%(world)s\t%(hiscore)s" % {"world": world, "hiscore": Variables.vdict["hiscore" + world]}, file=conffile)\r
++ print("besttime\t%(world)s\t%(besttime)s" % {"world": world, "besttime": Variables.vdict["besttime" + world]}, file=conffile)\r
++ print("sound\t%s" % bool_to_str(Variables.vdict["sound"]), file=conffile)\r
++ print("dialogue\t%s" % bool_to_str(Variables.vdict["dialogue"]), file=conffile)\r
++ print("fullscreen\t%s" % bool_to_str(Variables.vdict["fullscreen"]), file=conffile)\r
+ except:\r
+ error_message("Could not write configuration file to " + file_path)\r
+ return False\r
+@@ -136,13 +136,13 @@
+ count += 1\r
+ if count > MAX_OLD_LOG_LINES:\r
+ break\r
+- if Variables.vdict.has_key("log"):\r
++ if "log" in Variables.vdict:\r
+ try:\r
+ conffile = codecs.open(file_path, "w", "utf_8")\r
+- print >> conffile, "Log updated " + str(datetime.date.today())\r
+- print >> conffile, Variables.vdict["log"]\r
+- print >> conffile, ""\r
+- print >> conffile, old_log\r
++ print("Log updated " + str(datetime.date.today()), file=conffile)\r
++ print(Variables.vdict["log"], file=conffile)\r
++ print("", file=conffile)\r
++ print(old_log, file=conffile)\r
+ except:\r
+ error_message("Could not write log file to " + file_path)\r
+ return False\r
+@@ -166,7 +166,7 @@
+ The constant colors can be found from locals.py.\r
+ '''\r
+ def render_text(string, color = COLOR_GUI, bgcolor = COLOR_GUI_BG):\r
+- if Util.cached_text_images.has_key(string + str(color) + str(bgcolor)):\r
++ if (string + str(color) + str(bgcolor)) in Util.cached_text_images:\r
+ final_image = Util.cached_text_images[string + str(color) + str(bgcolor)]\r
+ else:\r
+ text_image_bg = Util.smallfont.render(string, True, bgcolor)\r
+@@ -200,8 +200,8 @@
+ rendered_string = string[0:phase]\r
+ string_image = render_text(rendered_string)\r
+ string_rect = string_image.get_rect()\r
+- string_rect.centerx = SCREEN_WIDTH / 2\r
+- string_rect.centery = SCREEN_HEIGHT / 2\r
++ string_rect.centerx = SCREEN_WIDTH // 2\r
++ string_rect.centery = SCREEN_HEIGHT // 2\r
+ \r
+ if key == "p":\r
+ skip_image = Util.cached_images["key_p"]\r
+@@ -209,7 +209,7 @@
+ skip_image = Util.cached_images["key_z"]\r
+ \r
+ skip_rect = skip_image.get_rect()\r
+- skip_rect.centerx = SCREEN_WIDTH / 2\r
++ skip_rect.centerx = SCREEN_WIDTH // 2\r
+ skip_rect.top = string_rect.bottom + 5\r
+ \r
+ bg_rect = pygame.Rect(string_rect.left - 10, string_rect.top - 5, string_rect.width + 20, string_rect.height + skip_rect.height + 15)\r
+--- a/lib/animation.py
++++ b/lib/animation.py
+@@ -58,9 +58,9 @@
+ self.finished = True\r
+ else:\r
+ self.i = 0\r
+- if Animation.cached_frames.has_key(self.cache_name + str(self.i)):\r
++ if (self.cache_name + str(self.i)) in Animation.cached_frames:\r
+ self.image = Animation.cached_frames[self.cache_name + str(self.i)]\r
+ else:\r
+ self.image = (self.frames[self.i]).get_image()\r
+ Animation.cached_frames[self.cache_name + str(self.i)] = self.image\r
+- return self.image
+\ No newline at end of file
++ return self.image\r
+--- a/lib/edit_utils.py
++++ b/lib/edit_utils.py
+@@ -16,23 +16,23 @@
+ return\r
+ \r
+ def update(self, inputs):\r
+- if inputs.has_key("REMOVE_TILE"):\r
++ if "REMOVE_TILE" in inputs:\r
+ return Change("remove", self.cursor)\r
+- if inputs.has_key("ADD_TILE_WALL"):\r
++ if "ADD_TILE_WALL" in inputs:\r
+ return Change("W", self.cursor)\r
+- if inputs.has_key("ADD_TILE_SPIKES"):\r
++ if "ADD_TILE_SPIKES" in inputs:\r
+ return Change("S", self.cursor)\r
+- if inputs.has_key("ADD_TILE_BARS"):\r
++ if "ADD_TILE_BARS" in inputs:\r
+ return Change("B", self.cursor)\r
+- if inputs.has_key("SAVE_TILES"):\r
++ if "SAVE_TILES" in inputs:\r
+ return Change("save", (0, 0))\r
+- if inputs.has_key("EDIT_RIGHT") and self.cursor[0] < (TILES_HOR - 1):\r
++ if "EDIT_RIGHT" in inputs and self.cursor[0] < (TILES_HOR - 1):\r
+ self.cursor[0] += 1\r
+- if inputs.has_key("EDIT_LEFT") and self.cursor[0] > 0:\r
++ if "EDIT_LEFT" in inputs and self.cursor[0] > 0:\r
+ self.cursor[0] -= 1\r
+- if inputs.has_key("EDIT_DOWN") and self.cursor[1] < (TILES_VER - 1):\r
++ if "EDIT_DOWN" in inputs and self.cursor[1] < (TILES_VER - 1):\r
+ self.cursor[1] += 1\r
+- if inputs.has_key("EDIT_UP") and self.cursor[1] > 0:\r
++ if "EDIT_UP" in inputs and self.cursor[1] > 0:\r
+ self.cursor[1] -= 1\r
+ return None\r
+ \r
+--- a/lib/game.py
++++ b/lib/game.py
+@@ -265,7 +265,7 @@
+ trigger = None\r
+ \r
+ if scripted_event_on:\r
+- if inputs.has_key("JUMP") or inputs.has_key("DOWN"):\r
++ if "JUMP" in inputs or "DOWN" in inputs:\r
+ cleared = True\r
+ \r
+ moved = False\r
+@@ -277,20 +277,20 @@
+ #There isn't anything special going on: player can control the character\r
+ #Translates input to commands to the player object\r
+ add_time = True\r
+- if inputs.has_key("LEFT"):\r
++ if "LEFT" in inputs:\r
+ player.move((-PLAYER_MAX_ACC, 0))\r
+ moved = True\r
+ \r
+- if inputs.has_key("RIGHT"):\r
++ if "RIGHT" in inputs:\r
+ player.move((PLAYER_MAX_ACC, 0))\r
+ moved = True\r
+ \r
+- if inputs.has_key("JUMP"):\r
++ if "JUMP" in inputs:\r
+ if (player.on_ground):\r
+ count = 0\r
+ while (count < 5):\r
+ count += 1\r
+- particles.append(Particle(screen, 10, player.rect.centerx - player.dx / 4 + random.uniform(-3, 3), player.rect.bottom, -player.dx * 0.1, -0.5, 0.3, level.dust_color, 4))\r
++ particles.append(Particle(screen, 10, player.rect.centerx - player.dx // 4 + random.uniform(-3, 3), player.rect.bottom, -player.dx * 0.1, -0.5, 0.3, level.dust_color, 4))\r
+ player.jump()\r
+ \r
+ #The blobs always try to jump when the player jumps\r
+@@ -299,10 +299,10 @@
+ if o.itemclass == "blob":\r
+ o.jump()\r
+ \r
+- if inputs.has_key("UP") and not player.on_ground:\r
++ if "UP" in inputs and not player.on_ground:\r
+ player.jump()\r
+ \r
+- if inputs.has_key("DOWN"):\r
++ if "DOWN" in inputs:\r
+ pick_up_item = level.pick_up(player.x, player.y)\r
+ if pick_up_item != None:\r
+ play_sound("coins")\r
+@@ -314,10 +314,10 @@
+ trigger = level.trigger(player.x, player.y)\r
+ \r
+ #Debug command for flipping:\r
+- if inputs.has_key("SPECIAL"):\r
++ if "SPECIAL" in inputs:\r
+ trigger = Trigger(TRIGGER_FLIP, player.x, player.y)\r
+ \r
+- if inputs.has_key("PAUSE") and player.current_animation != "dying":\r
++ if "PAUSE" in inputs and player.current_animation != "dying":\r
+ paused = not paused\r
+ \r
+ #Decelerates the player, if he doesn't press any movement keys or when he is dead and on the ground\r
+@@ -344,7 +344,7 @@
+ #Dust effect rising from the character's feet:\r
+ \r
+ if (player.current_animation == "walking"):\r
+- particles.append(Particle(screen, 10, player.rect.centerx - player.dx / 2 + random.uniform(-2, 2), player.rect.bottom, -player.dx * 0.1, 0.1, 0.3, level.dust_color))\r
++ particles.append(Particle(screen, 10, player.rect.centerx - player.dx // 2 + random.uniform(-2, 2), player.rect.bottom, -player.dx * 0.1, 0.1, 0.3, level.dust_color))\r
+ \r
+ #Updating level and objects:\r
+ \r
+@@ -455,7 +455,7 @@
+ player.orientation = current_scripted_event_element.orientation\r
+ current_scripted_event_element.finished = True\r
+ elif current_scripted_event_element.event_type == "change_level":\r
+- score.score += (5 + score_mod) * ((player.life + 4) / 5 + 12)\r
++ score.score += (5 + score_mod) * ((player.life + 4) // 5 + 12)\r
+ score.levels += 1\r
+ current_scripted_event_element.finished = True\r
+ if player.current_animation != "gone":\r
+--- a/lib/level.py
++++ b/lib/level.py
+@@ -129,8 +129,8 @@
+ self.bg_animations["default"] = Animation(self.set + "_background", "static")\r
+ self.current_animation = "default"\r
+ self.rect = (self.bg_animations[self.current_animation].update_and_get_image()).get_rect()\r
+- self.rect.centerx = SCREEN_WIDTH / 2\r
+- self.rect.centery = SCREEN_HEIGHT / 2\r
++ self.rect.centerx = SCREEN_WIDTH // 2\r
++ self.rect.centery = SCREEN_HEIGHT // 2\r
+ \r
+ self.reset_active_tiles()\r
+ return\r
+@@ -217,7 +217,7 @@
+ \r
+ #Checks the point for solid ground\r
+ def ground_check(self, x, y):\r
+- if self.cached_ground_check.has_key(str(x) + "_" + str(y)):\r
++ if (str(x) + "_" + str(y)) in self.cached_ground_check:\r
+ return self.cached_ground_check[str(x) + "_" + str(y)]\r
+ else:\r
+ if x > SCREEN_WIDTH or y > SCREEN_HEIGHT or x < 0 or y < 0:\r
+@@ -333,7 +333,7 @@
+ def remove_tile(self, coords):\r
+ """Remove a tile from the level with coordinates relative to the corner of the area currently visible."""\r
+ for t in self.active_tiles:\r
+- if t.rect.collidepoint(coords[0]*TILE_DIM + TILE_DIM / 2, coords[1]*TILE_DIM + TILE_DIM / 2):\r
++ if t.rect.collidepoint(coords[0]*TILE_DIM + TILE_DIM // 2, coords[1]*TILE_DIM + TILE_DIM // 2):\r
+ self.active_tiles.remove(t)\r
+ self.tiles.remove(t)\r
+ self.edited = True\r
+--- a/lib/log.py
++++ b/lib/log.py
+@@ -15,7 +15,7 @@
+ """Add a message to the message log, which can be written on disk later."""\r
+ \r
+ #Multiple messages of the same type aren't added to the log:\r
+- if Variables.vdict.has_key("last_log_message"):\r
++ if "last_log_message" in Variables.vdict:\r
+ if string == Variables.vdict["last_log_message"]:\r
+ return\r
+ \r
+@@ -24,9 +24,9 @@
+ \r
+ Variables.vdict["last_log_message"] = string\r
+ \r
+- if Variables.vdict.has_key("log"):\r
++ if "log" in Variables.vdict:\r
+ Variables.vdict["log"] = string + "\n" + Variables.vdict["log"]\r
+ else:\r
+ Variables.vdict["log"] = string\r
+ \r
+- return
+\ No newline at end of file
++ return\r
+--- a/lib/sound.py
++++ b/lib/sound.py
+@@ -25,7 +25,7 @@
+ if not Variables.vdict["sound"]:\r
+ return\r
+ snd = None\r
+- if (not sounds.has_key(sound_id)):\r
++ if sound_id not in sounds:\r
+ try:\r
+ sound_path = data.filepath(os.path.join("sounds", sound_id + ".ogg"))\r
+ snd = sounds[sound_id] = pygame.mixer.Sound(sound_path)\r
+--- a/lib/visibleobject.py
++++ b/lib/visibleobject.py
+@@ -27,9 +27,9 @@
+ self.x = x\r
+ self.y = y\r
+ if (self.x == None):\r
+- self.x = SCREEN_WIDTH / 2\r
++ self.x = SCREEN_WIDTH // 2\r
+ if (self.y == None):\r
+- self.y = SCREEN_HEIGHT / 2\r
++ self.y = SCREEN_HEIGHT // 2\r
+ \r
+ self.flipping = False\r
+ self.flipcounter = 0\r
+@@ -122,7 +122,7 @@
+ \r
+ def die(self):\r
+ """Make the object die - if the object has a death animation, it will be played first."""\r
+- if self.animations.has_key("dying"):\r
++ if "dying" in self.animations:\r
+ self.current_animation = "dying"\r
+ else:\r
+ self.dead = True\r
+--- a/lib/player.py
++++ b/lib/player.py
+@@ -79,7 +79,7 @@
+ \r
+ blood = []\r
+ \r
+- if collision_type > 0:\r
++ if collision_type and collision_type > 0:\r
+ blood = self.take_damage(collision_type)\r
+ if self.current_animation != "dying":\r
+ self.dy -= collision_type*PLAYER_JUMP_ACC / 4.5\r
+--- a/lib/object.py
++++ b/lib/object.py
+@@ -34,7 +34,7 @@
+ self.initial_y = y\r
+ self.gravity = gravity\r
+ self.colliding = colliding\r
+- self.active = (self.x + self.rect.width / 2 > 0) and (self.y + self.rect.height / 2 > 0)\r
++ self.active = (self.x + self.rect.width // 2 > 0) and (self.y + self.rect.height // 2 > 0)\r
+ \r
+ self.on_ground = False\r
+ \r
+@@ -76,7 +76,7 @@
+ VisibleObject.update(self)\r
+ \r
+ if self.flip_finished and self.itemclass != "player":\r
+- self.active = (self.x + self.rect.width / 2 > 0) and (self.y + self.rect.height / 2 > 0)\r
++ self.active = (self.x + self.rect.width // 2 > 0) and (self.y + self.rect.height // 2 > 0)\r
+ \r
+ if self.flipping:\r
+ return\r
+@@ -101,9 +101,9 @@
+ """Make the object flip with the level to either direction"""\r
+ if VisibleObject.flip(self, flip_direction):\r
+ if flip_direction == CLOCKWISE:\r
+- self.initial_x, self.initial_y = -self.initial_y + PLAY_AREA_WIDTH / TILES_HOR * (TILES_HOR*2 - FULL_TILES_HOR), self.initial_x\r
++ self.initial_x, self.initial_y = -self.initial_y + PLAY_AREA_WIDTH // TILES_HOR * (TILES_HOR*2 - FULL_TILES_HOR), self.initial_x\r
+ else:\r
+- self.initial_x, self.initial_y = self.initial_y, -self.initial_x + PLAY_AREA_WIDTH / TILES_HOR * (TILES_HOR*2 - FULL_TILES_HOR)\r
++ self.initial_x, self.initial_y = self.initial_y, -self.initial_x + PLAY_AREA_WIDTH // TILES_HOR * (TILES_HOR*2 - FULL_TILES_HOR)\r
+ return\r
+ \r
+ def check_collisions(self, level):\r
+@@ -116,25 +116,25 @@
+ \r
+ self.on_ground = False\r
+ \r
+- if self.x < 0 + self.rect.width / 2:\r
+- self.x = 0 + self.rect.width / 2\r
++ if self.x < 0 + self.rect.width // 2:\r
++ self.x = 0 + self.rect.width // 2\r
+ self.dx = 0\r
+ collision_type = 0\r
+ \r
+- if self.x > PLAY_AREA_WIDTH - self.rect.width / 2:\r
+- self.x = PLAY_AREA_WIDTH - self.rect.width / 2\r
++ if self.x > PLAY_AREA_WIDTH - self.rect.width // 2:\r
++ self.x = PLAY_AREA_WIDTH - self.rect.width // 2\r
+ self.dx = 0\r
+ collision_type = 0\r
+ \r
+ # The commented block is the collision code for the upper edge of the screen.\r
+ # The spiders and projectiles might need this, but they use simplified\r
+ # collision detection for better performance anyway.\r
+- '''if self.y < 0 + self.rect.height / 2:\r
+- self.y = 0 + self.rect.height / 2\r
++ '''if self.y < 0 + self.rect.height // 2:\r
++ self.y = 0 + self.rect.height // 2\r
+ self.dy = 0'''\r
+ \r
+- if self.y > PLAY_AREA_HEIGHT - self.rect.height / 2:\r
+- self.y = PLAY_AREA_HEIGHT - self.rect.height / 2\r
++ if self.y > PLAY_AREA_HEIGHT - self.rect.height // 2:\r
++ self.y = PLAY_AREA_HEIGHT - self.rect.height // 2\r
+ self.dy = 0\r
+ self.on_ground = True\r
+ collision_type = 0\r
+--- a/lib/locals.py
++++ b/lib/locals.py
+@@ -16,8 +16,8 @@
+ \r
+ TILE_DIM = 40\r
+ \r
+-PLAY_AREA_CENTER_X = (-FULL_TILES_HOR / 2 + TILES_HOR) * TILE_DIM\r
+-PLAY_AREA_CENTER_Y = (-FULL_TILES_VER / 2 + TILES_VER) * TILE_DIM\r
++PLAY_AREA_CENTER_X = (-FULL_TILES_HOR // 2 + TILES_HOR) * TILE_DIM\r
++PLAY_AREA_CENTER_Y = (-FULL_TILES_VER // 2 + TILES_VER) * TILE_DIM\r
+ \r
+ GRAVITY = 1.0\r
+ GRAVITY_PARTICLE = 0.5\r
+--- a/lib/mainmenu.py
++++ b/lib/mainmenu.py
+@@ -73,19 +73,19 @@
+ \r
+ menu_image = render_text("World " + str(self.world.number) + ": " + self.world.name, COLOR_GUI)\r
+ rect = menu_image.get_rect()\r
+- rect.centerx = SCREEN_WIDTH / 2\r
++ rect.centerx = SCREEN_WIDTH // 2\r
+ rect.top = GUI_MENU_TOP - 75\r
+ self.bgscreen.blit(menu_image, rect)\r
+ \r
+ menu_image = render_text(score_text, COLOR_GUI)\r
+ rect = menu_image.get_rect()\r
+- rect.centerx = SCREEN_WIDTH / 2\r
++ rect.centerx = SCREEN_WIDTH // 2\r
+ rect.top = GUI_MENU_TOP - 50\r
+ self.bgscreen.blit(menu_image, rect)\r
+ \r
+ menu_image = render_text(time_text, COLOR_GUI)\r
+ rect = menu_image.get_rect()\r
+- rect.centerx = SCREEN_WIDTH / 2\r
++ rect.centerx = SCREEN_WIDTH // 2\r
+ rect.top = GUI_MENU_TOP - 30\r
+ self.bgscreen.blit(menu_image, rect)\r
+ \r
+--- a/lib/menu.py
++++ b/lib/menu.py
+@@ -91,14 +91,14 @@
+ \r
+ menu_bg = pygame.image.load(data.picpath("menu", "bg")).convert_alpha()\r
+ rect = menu_bg.get_rect()\r
+- rect.centerx = SCREEN_WIDTH / 2\r
++ rect.centerx = SCREEN_WIDTH // 2\r
+ rect.top = GUI_MENU_TOP\r
+ self.screen.blit(menu_bg, rect)\r
+ \r
+ if self.heading_text != None:\r
+ menu_head = render_text(self.heading_text)\r
+ rect = menu_head.get_rect()\r
+- rect.centerx = SCREEN_WIDTH / 2\r
++ rect.centerx = SCREEN_WIDTH // 2\r
+ rect.top = GUI_MENU_TOP + 50 + menu_offset\r
+ self.screen.blit(menu_head, rect)\r
+ \r
+@@ -120,7 +120,7 @@
+ else:\r
+ menu_image = render_text(m, COLOR_GUI)\r
+ rect = menu_image.get_rect()\r
+- rect.centerx = SCREEN_WIDTH / 2\r
++ rect.centerx = SCREEN_WIDTH // 2\r
+ rect.top = GUI_MENU_TOP + 60 + (menu_visible + 1) * 20 + menu_offset\r
+ self.screen.blit(menu_image, rect)\r
+ current_menu_index += 1\r
+--- a/lib/particle.py
++++ b/lib/particle.py
+@@ -28,9 +28,9 @@
+ self.radius = radius\r
+ self.gravity = gravity\r
+ if (self.x == None):\r
+- self.x = SCREEN_WIDTH / 2\r
++ self.x = SCREEN_WIDTH // 2\r
+ if (self.y == None):\r
+- self.y = SCREEN_HEIGHT / 2\r
++ self.y = SCREEN_HEIGHT // 2\r
+ if (self.dx == None):\r
+ self.dx = 0.0\r
+ if (self.dy == None):\r
+--- a/lib/tile.py
++++ b/lib/tile.py
+@@ -47,8 +47,8 @@
+ def realign(self):\r
+ self.rect.centerx = self.x\r
+ self.rect.centery = self.y\r
+- self.x = round((float(self.rect.right)/float(TILE_DIM)), 0)*TILE_DIM - self.rect.width / 2\r
+- self.y = round((float(self.rect.bottom)/float(TILE_DIM)), 0)*TILE_DIM - self.rect.height / 2\r
++ self.x = round((float(self.rect.right)/float(TILE_DIM)), 0)*TILE_DIM - self.rect.width // 2\r
++ self.y = round((float(self.rect.bottom)/float(TILE_DIM)), 0)*TILE_DIM - self.rect.height // 2\r
+ if self.rect.height % 2 == 1:\r
+ self.y -= 1\r
+ if self.rect.width % 2 == 1:\r