diff -ruN a/main.py b/main.py --- a/main.py 1969-12-31 21:00:00.000000000 -0300 +++ b/main.py 2026-11-04 23:55:36.421964589 -0300 @@ -0,0 +1,6 @@ +import pygame +import asyncio + +from src import main + +asyncio.run(main.main()) diff -ruN a/run_game.py b/run_game.py --- a/run_game.py 2026-11-04 23:53:33.245748931 -0300 +++ b/run_game.py 1969-12-31 21:00:00.000000000 -0300 @@ -1,3 +0,0 @@ -import math -math.tau = 2 * math.pi -from src import main diff -ruN a/src/bosses.py b/src/bosses.py --- a/src/bosses.py 2026-11-04 23:53:33.318132983 -0300 +++ b/src/bosses.py 2026-11-04 23:55:36.036016209 -0300 @@ -1,6 +1,6 @@ -from __future__ import division + import math, pygame, random -import ships, state, vista, sound, effects +from . import ships, state, vista, sound, effects class Boss(ships.Ship): diff -ruN a/src/buildscene.py b/src/buildscene.py --- a/src/buildscene.py 2026-11-04 23:53:33.331318121 -0300 +++ b/src/buildscene.py 2026-11-04 23:55:36.067190280 -0300 @@ -1,7 +1,7 @@ -from __future__ import division + import pygame, random -import vista, state, scene, settings, img, parts, dialog, button, sound, gamescene -from settings import F +from . import vista, state, scene, settings, img, parts, dialog, button, sound, gamescene +from .settings import F controls = [] cps = [] @@ -52,7 +52,8 @@ state.save() # can be: a tuple on the board, a button name, or a module or conduit -def pointat((mx, my)): +def pointat(xxx_todo_changeme): + (mx, my) = xxx_todo_changeme if brect.collidepoint((mx, my)): return (mx - bx0) / bscale, (my - by0) / bscale for button in buttons: @@ -77,11 +78,11 @@ else: sound.play("cantbuild") else: - part = state.state.partat(map(int, point)) + part = state.state.partat(list(map(int, point))) if part: state.state.removepart(part) sound.play("unbuild") - elif isinstance(point, basestring): + elif isinstance(point, str): if point == "Remove All": state.state.removeall() sound.play("unbuild") @@ -148,7 +149,7 @@ for x in range(settings.shipw): for y in range(settings.shiph): vista.screen.fill((100, 50, 0), (bx0 + bscale*x, by0 + bscale*y, bscale-3, bscale-3)) - for x0, y0, x1, y1 in state.state.supplies.values(): + for x0, y0, x1, y1 in list(state.state.supplies.values()): dx, dy = x1 - x0, y1 - y0 rot = { (1, 0): -90, diff -ruN a/src/button.py b/src/button.py --- a/src/button.py 2026-11-04 23:53:33.327041324 -0300 +++ b/src/button.py 2026-11-04 23:55:36.074711801 -0300 @@ -1,5 +1,5 @@ import pygame -import vista, state, img +from . import vista, state, img class Button(object): def __init__(self, name, rect, fontsize = 22, fontname = "viga", bcolor = (0, 0, 255, 100), color = (255, 255, 255), text = None): @@ -43,5 +43,5 @@ def click(buttonname): if state.state.handlebutton(buttonname): return - print("unhandled button: %s" % buttonname) + print(("unhandled button: %s" % buttonname)) diff -ruN a/src/cutscene.py b/src/cutscene.py --- a/src/cutscene.py 2026-11-04 23:53:33.321252707 -0300 +++ b/src/cutscene.py 2026-11-04 23:55:36.078405686 -0300 @@ -1,6 +1,6 @@ import pygame -import vista, state, button, scene, gamescene, buildscene, dialog, img, settings, sound, quest -from settings import F +from . import vista, state, button, scene, gamescene, buildscene, dialog, img, settings, sound, quest +from .settings import F buttons = [] diff -ruN a/src/dialog.py b/src/dialog.py --- a/src/dialog.py 2026-11-04 23:53:33.316324165 -0300 +++ b/src/dialog.py 2026-11-04 23:55:36.090181379 -0300 @@ -1,7 +1,7 @@ -from __future__ import division + import pygame -import settings, img, vista, state -from settings import F +from . import settings, img, vista, state +from .settings import F avatarscale = F(80) / 256 textsize = F(24) @@ -69,7 +69,7 @@ if dname in lines: state.state.playing.extend(lines[dname]) else: - print "playing %s" % dname + print("playing %s" % dname) def clear(dname): pass diff -ruN a/src/effects.py b/src/effects.py --- a/src/effects.py 2026-11-04 23:53:33.313941894 -0300 +++ b/src/effects.py 2026-11-04 23:55:36.105448125 -0300 @@ -1,5 +1,5 @@ import pygame, math, random -import state, img, vista +from . import state, img, vista class Explosion(object): imgname = "boom" @@ -8,7 +8,7 @@ self.x, self.y = parent.x, parent.y self.vx, self.vy = v self.t = 0 - self.imgname = "smoke%s" % random.choice(range(4)) + self.imgname = "smoke%s" % random.choice(list(range(4))) self.angle = random.uniform(0, math.tau) def think(self, dt): diff -ruN a/src/F.py b/src/F.py --- a/src/F.py 2026-11-04 23:53:33.332305446 -0300 +++ b/src/F.py 2026-11-04 23:55:36.018997894 -0300 @@ -3,12 +3,12 @@ # NB: DOESNT WORK -from __future__ import division + import sys class F(object): def __call__(self, *args): - import settings + from . import settings if len(args) == 1: return int(round(args[0] * settings.sy / 480)) else: diff -ruN a/src/gamescene.py b/src/gamescene.py --- a/src/gamescene.py 2026-11-04 23:53:33.336580535 -0300 +++ b/src/gamescene.py 2026-11-04 23:55:36.122392566 -0300 @@ -1,6 +1,6 @@ import pygame -import vista, state, button, scene, buildscene, dialog, img, settings, sound, quest -from settings import F +from . import vista, state, button, scene, buildscene, dialog, img, settings, sound, quest +from .settings import F apart = { "mother": False, @@ -83,7 +83,7 @@ else: shroud.set_alpha(alpha) if not state.state.you.corpse.alive: - import menuscene + from . import menuscene scene.pop() scene.push(menuscene) diff -ruN a/src/img.py b/src/img.py --- a/src/img.py 2026-11-04 23:53:33.323669353 -0300 +++ b/src/img.py 2026-11-04 23:55:36.421346302 -0300 @@ -1,6 +1,6 @@ -from __future__ import division + import pygame, random, math, os.path -import vista, settings +from . import vista, settings cache = {} @@ -111,12 +111,10 @@ cache[key] = pygame.transform.rotozoom(img0, angle, scale) elif not bad: cache[key] = img0 = getimg(imgname, angle, scale).copy() - alphas = pygame.surfarray.pixels_alpha(img0) - alphas *= alpha + img0.fill((255, 255, 255, int(255 * alpha)), special_flags=pygame.BLEND_RGBA_MULT) else: cache[key] = img0 = getimg(imgname, angle, scale, alpha).copy() - pixels = pygame.surfarray.pixels3d(img0) - pixels[:,:,1:3] *= 0.1 + img0.fill((255, 26, 26, 255), special_flags=pygame.BLEND_RGBA_MULT) return cache[key] def draw(imgname, screenpos, angle = None, scale = 1.0, alpha = 1.0, bad = False): diff -ruN a/src/main.py b/src/main.py --- a/src/main.py 2026-11-04 23:53:33.335628126 -0300 +++ b/src/main.py 2026-11-04 23:55:36.421150804 -0300 @@ -1,31 +1,29 @@ -from __future__ import division +import asyncio +import time import pygame -import settings, scene, vista, gamescene, buildscene, menuscene +from . import settings, scene, vista, gamescene, buildscene, menuscene -pygame.init() -vista.init() -pygame.display.set_caption(settings.gamename) -scene.push(menuscene) -#scene.push(gamescene) -#scene.push(buildscene) - -clock = pygame.time.Clock() -while True: - scene0 = scene.top() - if not scene0: - break - dt = min(clock.tick(settings.maxfps) * 0.001, 1 / settings.minfps) - events = pygame.event.get() - if settings.DEBUG: - pygame.display.set_caption("%s | %.1ffps" % (settings.gamename, clock.get_fps())) - if any(event.type == pygame.QUIT for event in events): - break - - scene0.think(dt, events, pygame.mouse.get_pos()) - vista.clear() - scene0.draw() - vista.flip() - if any(event.type == pygame.KEYDOWN and event.key == pygame.K_F12 for event in events): - vista.screenshot() +async def main(): + pygame.init() + vista.init() + pygame.display.set_caption(settings.gamename) + scene.push(menuscene) + last = time.time() + while True: + scene0 = scene.top() + if not scene0: + # the page has nowhere to quit to: back to the title screen + scene.push(menuscene) + scene0 = scene.top() + # frame limiter for the browser: the page needs control back every frame + await asyncio.sleep(max(0.0, 1.0 / settings.maxfps - (time.time() - last))) + now = time.time() + dt = min(now - last, 1 / settings.minfps) + last = now + events = pygame.event.get() + scene0.think(dt, events, pygame.mouse.get_pos()) + vista.clear() + scene0.draw() + vista.flip() diff -ruN a/src/menuscene.py b/src/menuscene.py --- a/src/menuscene.py 2026-11-04 23:53:33.322974275 -0300 +++ b/src/menuscene.py 2026-11-04 23:55:36.463861486 -0300 @@ -1,14 +1,13 @@ import pygame -import vista, state, button, scene, gamescene, cutscene, buildscene, dialog, img, settings, sound, quest -from settings import F +from . import vista, state, button, scene, gamescene, cutscene, buildscene, dialog, img, settings, sound, quest +from .settings import F buttons = [] def init(): global buttons buttons = [ - button.Button("Continue" if state.canload() else "New Game", F(854/2 - 90, 300, 180, 30), fontsize = F(22)), - button.Button("Quit", F(854/2 - 90, 350, 180, 30), fontsize = F(22)), + button.Button("Continue" if state.canload() else "New Game", F(854//2 - 90, 300, 180, 30), fontsize = F(22)), ] def think(dt, events, mpos): @@ -16,15 +15,12 @@ if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: if buttons[0].within(mpos): play() - if buttons[1].within(mpos): - quit() sound.playmusic("title") def draw(): vista.screen.fill((255, 255, 255)) - img.drawtext(settings.gamename, F(60), center = F(854/2, 120), fontname = "prosto") - img.drawtext("by Christoper Night\n ", F(20), center = F(854/2, 220), fontname = "prosto") - img.drawtext("music by Mary Bichner\n ", F(20), center = F(854/2, 270), fontname = "prosto") + img.drawtext(settings.gamename, F(60), center = F(854//2, 120), fontname = "prosto") + img.drawtext("by Christoper Night\n ", F(20), center = F(854//2, 220), fontname = "prosto") for b in buttons: b.draw() diff -ruN a/src/parts.py b/src/parts.py --- a/src/parts.py 2026-11-04 23:53:33.333780642 -0300 +++ b/src/parts.py 2026-11-04 23:55:36.178681510 -0300 @@ -1,7 +1,7 @@ -from __future__ import division + import pygame, random -import settings, vista, img -from settings import F +from . import settings, vista, img +from .settings import F def offset(edge, p0 = (0, 0)): x0, y0 = p0 @@ -16,22 +16,24 @@ def __init__(self, name, blocks, inputs, outputs): self.name = name self.blocks = list(blocks) - xs, ys = zip(*blocks) + xs, ys = list(zip(*blocks)) self.center = (max(xs) + min(xs) + 1) / 2, (max(ys) + min(ys) + 1) / 2 self.inputs = list(inputs) self.outputs = list(outputs) self.color = random.randint(100, 200), random.randint(100, 200), random.randint(100, 200) self.dull = tuple(c // 2 for c in self.color) - def shift(self, (dx, dy)): + def shift(self, xxx_todo_changeme): + (dx, dy) = xxx_todo_changeme blocks = [(x + dx, y + dy) for x, y in self.blocks] inputs = [(x0 + dx, y0 + dy, x1 + dx, y1 + dy) for x0, y0, x1, y1 in self.inputs] outputs = [(x0 + dx, y0 + dy, x1 + dx, y1 + dy) for x0, y0, x1, y1 in self.outputs] p = Part(self.name, blocks, inputs, outputs) return p - def nearest(self, (x, y)): - xs, ys = zip(*self.blocks) + def nearest(self, xxx_todo_changeme1): + (x, y) = xxx_todo_changeme1 + xs, ys = list(zip(*self.blocks)) xmin, xmax = -min(xs), settings.shipw - max(xs) - 1 ymin, ymax = -min(ys), settings.shiph - max(ys) - 1 dx = clamp(int(round(x - self.center[0])), xmin, xmax) @@ -68,7 +70,8 @@ py = int(y0 + blocksize * self.center[1]) pygame.draw.circle(vista.screen, (255, 0, 255), (px, py), int(1.3 * blocksize), F(2)) - def contains(self, (x, y)): + def contains(self, xxx_todo_changeme2): + (x, y) = xxx_todo_changeme2 return (int(x), int(y)) in self.blocks class Conduit(Part): @@ -89,7 +92,8 @@ y0 += (self.p0[1] + 0.5) * blocksize img.draw(self.name, (x0, y0), scale = blocksize / 64, angle = -90 * self.rot, bad = bad) - def shift(self, (dx, dy)): + def shift(self, xxx_todo_changeme3): + (dx, dy) = xxx_todo_changeme3 dx0, dy0 = self.p0 return Conduit(self.oedges, self.rot, (dx + dx0, dy + dy0)) diff -ruN a/src/quest.py b/src/quest.py --- a/src/quest.py 2026-11-04 23:53:33.326229414 -0300 +++ b/src/quest.py 2026-11-04 23:55:36.210945655 -0300 @@ -1,5 +1,5 @@ import random, math -import dialog, state, gamescene, buildscene, scene, starmap, bosses, vista, settings, sound +from . import dialog, state, gamescene, buildscene, scene, starmap, bosses, vista, settings, sound class Quest(object): def think(self, dt): @@ -125,7 +125,7 @@ def __init__(self): self.addquest(EndQuest()) def think(self, dt): - import things + from . import things if sum(t.surveyed and isinstance(t, things.Sun) for t in state.state.things) > 2: self.addquest(Baron3Quest()) self.complete() @@ -151,7 +151,7 @@ class EndQuest(Quest): def think(self, dt): if state.state.angel5.surveyed: - print "You beat the game. See the README for the cutscene that didn't make it in. Thanks for playing!" + print("You beat the game. See the README for the cutscene that didn't make it in. Thanks for playing!") exit() diff -ruN a/src/settings.py b/src/settings.py --- a/src/settings.py 2026-11-04 23:53:33.330759668 -0300 +++ b/src/settings.py 2026-11-04 23:55:36.225007537 -0300 @@ -1,6 +1,6 @@ # THESE ARE NOT USER-EDITABLE SETTINGS, PEOPLE. DON'T MESS WITH THESE. -from __future__ import division + import pygame DEBUG = False diff -ruN a/src/ships.py b/src/ships.py --- a/src/ships.py 2026-11-04 23:53:33.322016200 -0300 +++ b/src/ships.py 2026-11-04 23:55:36.271598686 -0300 @@ -1,5 +1,5 @@ import math, random, pygame -import vista, img, settings, weapon, state, effects +from . import vista, img, settings, weapon, state, effects class Ship(object): vmax = 1 @@ -133,7 +133,7 @@ def takedamage(self, damage): Ship.takedamage(self, damage) - import gamescene + from . import gamescene gamescene.setshroud((255, 0, 0), 70) def die(self): @@ -155,7 +155,8 @@ state.state.effects.append(effects.Explosion(self, v)) Ship.think(self, dt) - def within(self, (x, y)): + def within(self, xxx_todo_changeme): + (x, y) = xxx_todo_changeme return (x - self.x) ** 2 + (y - self.y) ** 2 <= self.radius ** 2 diff -ruN a/src/sound.py b/src/sound.py --- a/src/sound.py 2026-11-04 23:53:33.313431106 -0300 +++ b/src/sound.py 2026-11-04 23:55:36.275780360 -0300 @@ -1,5 +1,5 @@ import pygame, os.path -import settings +from . import settings music = None @@ -9,7 +9,7 @@ if sname not in sounds: filename = "data/%s.ogg" % sname if not os.path.exists(filename): - print "Missing sound:", sname + print("Missing sound:", sname) sounds[sname] = None return sounds[sname] = pygame.mixer.Sound(filename) diff -ruN a/src/starmap.py b/src/starmap.py --- a/src/starmap.py 2026-11-04 23:53:33.314859386 -0300 +++ b/src/starmap.py 2026-11-04 23:55:36.421819756 -0300 @@ -1,11 +1,12 @@ -import cPickle, pygame +import pickle, pygame def init(): global ps, oortdata, scale, rx, ry, oortmap - obj = cPickle.load(open("data/starmap.pkl", "rb")) + obj = pickle.load(open("data/starmap.pkl", "rb"), encoding="latin1") ps, oortdata, scale, rx, ry = obj -def getoort((x, y)): +def getoort(xxx_todo_changeme): + (x, y) = xxx_todo_changeme key = int(round((x + rx) * scale)), int(round((y + ry) * scale)) if key in oortdata: return oortdata[key] diff -ruN a/src/state.py b/src/state.py --- a/src/state.py 2026-11-04 23:53:33.337465819 -0300 +++ b/src/state.py 2026-11-04 23:55:36.422125337 -0300 @@ -1,6 +1,6 @@ -import cPickle, pygame -import settings, ships, things, quest, parts, starmap, sound, bosses, scene, buildscene, dialog -from settings import F +import os, pickle, pygame +from . import settings, ships, things, quest, parts, starmap, sound, bosses, scene, buildscene, dialog +from .settings import F starmap.init() @@ -25,7 +25,7 @@ self.mother, ] self.things = [] - for tname, p in starmap.ps.items(): + for tname, p in list(starmap.ps.items()): if tname.startswith("angel"): t = things.Sun(p) setattr(self, tname, t) @@ -119,7 +119,8 @@ return "engine" in self.hookup or "turbo" in self.hookup or "hyperdrive" in self.hookup def think(self, dt): - import random, math, vista + import random, math + from . import vista self.bossmode = self.boss is not None and self.boss.distfromyou() < settings.fadedistance if self.bossmode and not self.boss.corpse.alive: self.startnextact() @@ -241,7 +242,7 @@ self.bank += t.value def drawviewport(self): - import vista, img + from . import vista, img a = starmap.getoort((self.you.x, self.you.y)) vista.screen.fill((0, int(80 * a), int(60 * a))) vista.drawstars() @@ -259,7 +260,7 @@ img.drawtext(settings.inames[iname], F(20), (0, 255, 255), fontname = "teko", center = vista.worldtoscreen(pos)) def drawmainmap(self): - import vista, img + from . import vista, img vista.drawmainoort() scale = settings.grect.width / (2 * starmap.rx) for t in self.things: @@ -280,7 +281,7 @@ def drawnavmap(self): - import vista, img + from . import vista, img vista.drawnavoort() for t in self.things: if not t.showsup: @@ -325,7 +326,7 @@ def sethookup(self): powered = { - edge: [supply] for supply, edge in self.supplies.items() + edge: [supply] for supply, edge in list(self.supplies.items()) } self.hookup = {} self.modules = [part.name for part in self.parts if part.ismodule] @@ -358,16 +359,17 @@ self.removepart(part) +SAVEFILE = "/saves/savegame.pkl" if os.path.isdir("/saves") else "savegame.pkl" # on the web the page keeps /saves in the browser + def save(): - cPickle.dump(state, open("savegame.pkl", "wb")) + pickle.dump(state, open(SAVEFILE, "wb")) def load(): global state - state = cPickle.load(open("savegame.pkl", "rb")) + state = pickle.load(open(SAVEFILE, "rb")) def canload(): - import os.path - return os.path.exists("savegame.pkl") + return os.path.exists(SAVEFILE) def reset(): global state diff -ruN a/src/things.py b/src/things.py --- a/src/things.py 2026-11-04 23:53:33.314403348 -0300 +++ b/src/things.py 2026-11-04 23:55:36.342527882 -0300 @@ -1,6 +1,6 @@ -from __future__ import division + import math, random, pygame -import vista, img, settings, weapon, state, effects +from . import vista, img, settings, weapon, state, effects class Planet(object): radius = 2 diff -ruN a/src/vista.py b/src/vista.py --- a/src/vista.py 2026-11-04 23:53:33.318569313 -0300 +++ b/src/vista.py 2026-11-04 23:55:36.361364432 -0300 @@ -1,6 +1,6 @@ -from __future__ import division + import pygame, datetime, math, random -import settings, state, starmap +from . import settings, state, starmap scale = settings.grect.width / 2 / settings.vsize x0, y0 = 0, 0 # center of viewport in world coordinates @@ -40,39 +40,43 @@ navx0 = clamp(state.state.you.x, -starmap.rx + xmargin, starmap.rx - xmargin) navy0 = clamp(state.state.you.y, -starmap.ry + ymargin, starmap.ry - ymargin) -def worldtoscreen((x, y)): +def worldtoscreen(xxx_todo_changeme): + (x, y) = xxx_todo_changeme return ( int(round(X0 + (x - x0) * scale)), int(round(Y0 + (y - y0) * scale)), ) -def worldtonav((x, y)): +def worldtonav(xxx_todo_changeme1): + (x, y) = xxx_todo_changeme1 return ( int(round(settings.nrect.width / 2 + (x - navx0) * settings.nscale)), int(round(settings.nrect.height / 2 + (y - navy0) * settings.nscale)), ) -def worldtomainmap((x, y)): +def worldtomainmap(xxx_todo_changeme2): + (x, y) = xxx_todo_changeme2 scale = settings.grect.width / (2 * starmap.rx) return ( int(round(settings.grect.centerx + x * scale)), int(round(settings.grect.centery + y * scale)), ) -def screentoworld((X, Y)): +def screentoworld(xxx_todo_changeme3): + (X, Y) = xxx_todo_changeme3 return ( (X - X0) / scale + x0, (Y - Y0) / scale + y0, ) def drawnavoort(): - import img + from . import img X0, Y0 = worldtonav((0, 0)) i = img.getimg("oort", scale = settings.nscale / starmap.scale) navmap.blit(i, i.get_rect(center = (X0, Y0))) def drawmainoort(): - import img + from . import img scale = settings.grect.width / (2 * starmap.rx) img.draw("oort", settings.grect.center, scale = scale) @@ -83,7 +87,9 @@ c = int(255 * z) screen.set_at((px, py), (c, c, c)) -def drawbolt((x0, y0), (x1, y1), color): +def drawbolt(xxx_todo_changeme4, xxx_todo_changeme5, color): + (x0, y0) = xxx_todo_changeme4 + (x1, y1) = xxx_todo_changeme5 dx, dy = x1 - x0, y1 - y0 if dx ** 2 + dy ** 2 < 1: pygame.draw.aaline(screen, color, worldtoscreen((x0, y0)), worldtoscreen((x1, y1))) @@ -94,11 +100,13 @@ drawbolt((x0, y0), (xc, yc), color) drawbolt((xc, yc), (x1, y1), color) -def isvisible((x, y), r = 0): +def isvisible(xxx_todo_changeme6, r = 0): + (x, y) = xxx_todo_changeme6 return xmin - r < x < xmax + r and ymin - r < y < ymax + r # Where to put an indicator -def indpos((x, y)): +def indpos(xxx_todo_changeme7): + (x, y) = xxx_todo_changeme7 gw, gh = settings.grect.size rx, ry = 0.45 * gw / scale, 0.45 * gh / scale dx, dy = x - x0, y - y0 diff -ruN a/src/weapon.py b/src/weapon.py --- a/src/weapon.py 2026-11-04 23:53:33.326650619 -0300 +++ b/src/weapon.py 2026-11-04 23:55:36.371602804 -0300 @@ -1,5 +1,5 @@ import math -import state, effects, sound +from . import state, effects, sound class Weapon(object): cooldown = 1