diff -ruN -x '*.ttf' -x '*.ogg' a/gamelib/audio.py b/gamelib/audio.py --- a/gamelib/audio.py 2026-11-04 23:49:54.914660532 -0300 +++ b/gamelib/audio.py 2026-11-05 00:05:47.036209231 -0300 @@ -7,7 +7,10 @@ if name in sounds: sounds[name].play() return - sounds[name] = pygame.mixer.Sound(data.filepath(name + ".wav")) + try: + sounds[name] = pygame.mixer.Sound(data.filepath(name + ".wav")) + except (FileNotFoundError, pygame.error): + return # the sound effects are not part of the published game sounds[name].play() diff -ruN -x '*.ttf' -x '*.ogg' a/gamelib/cursor.py b/gamelib/cursor.py --- a/gamelib/cursor.py 2026-11-04 23:49:54.917369759 -0300 +++ b/gamelib/cursor.py 2026-11-05 00:05:47.084242827 -0300 @@ -72,7 +72,7 @@ color1 = 100, 50, 0 color2 = 60, 30, 0 def __init__(self, name, text, key=None, icon=None): - pygame.sprite.Sprite.__init__(self) + thing._spriteinit(self) self.image0 = pygame.Surface((self.width, self.height)) self.image0.fill(self.color1) for d in (0,1): @@ -94,7 +94,7 @@ self.icon = icon if icon: r = icon.get_rect() - r.center = self.image0.get_rect().centerx, y/2 + r.center = self.image0.get_rect().centerx, y//2 self.image0.blit(icon, r) self.image = self.image0 self.group.add(self) @@ -109,8 +109,8 @@ if n < 0: return -self.diminish(-n) if n == 0: return 0 if n < k: return 1 - if n/k > m: return m - return n/k + if n//k > m: return m + return n//k def setpos(self): self.rect.right = gamestate.screen.get_rect().right + self.x self.rect.top = gamestate.screen.get_rect().top + self.y @@ -418,7 +418,7 @@ MenuBlock.group.draw(gamestate.screen) def somebodydosomething(self): if not thing.Thing.sgroup: return - if random.randint(0, int(100/len(thing.Thing.sgroup))): return + if random.randint(0, int(100//len(thing.Thing.sgroup))): return if random.randint(0, 3) == 0 and thing.Dewdrop.group: damaged = [] for s in thing.Structure.group: diff -ruN -x '*.ttf' -x '*.ogg' a/gamelib/cutscene.py b/gamelib/cutscene.py --- a/gamelib/cutscene.py 2026-11-04 23:49:54.913535916 -0300 +++ b/gamelib/cutscene.py 2026-11-05 00:05:46.729144730 -0300 @@ -79,7 +79,7 @@ del infosigns[activeinfo] activeinfo = False return False - for iname, i in infosigns.items(): + for iname, i in list(infosigns.items()): if infocriterion(iname): activeinfo = iname infostep, infoticker = 0, 0 diff -ruN -x '*.ttf' -x '*.ogg' a/gamelib/data.py b/gamelib/data.py --- a/gamelib/data.py 2026-11-04 23:49:54.916780889 -0300 +++ b/gamelib/data.py 2026-11-05 00:05:47.036330605 -0300 @@ -11,7 +11,7 @@ def filepath(filename): return os.path.join(data_dir, filename) -def load(filename, mode='rb'): +def load(filename, mode='r'): return open(filepath(filename), mode) def loadimg(filename): diff -ruN -x '*.ttf' -x '*.ogg' a/gamelib/imagebank.py b/gamelib/imagebank.py --- a/gamelib/imagebank.py 2026-11-04 23:49:54.916427017 -0300 +++ b/gamelib/imagebank.py 2026-11-05 00:05:47.081069979 -0300 @@ -22,7 +22,7 @@ def image0(self): i = data.loadimg(self.f0 + ".png").convert_alpha() if self.width == None: self.width = i.get_width() - height = self.width * i.get_height() / i.get_width() + height = self.width * i.get_height() // i.get_width() self.image0 = pygame.transform.smoothscale(i, (self.width, height)) self.dict[""] = self.image0 return self.image0 diff -ruN -x '*.ttf' -x '*.ogg' a/gamelib/main.py b/gamelib/main.py --- a/gamelib/main.py 2026-11-04 23:49:54.915160153 -0300 +++ b/gamelib/main.py 1969-12-31 21:00:00.000000000 -0300 @@ -1,254 +0,0 @@ -import data, gamestate, lawn, thing, rain, cursor, cutscene, signage, audio -import math, random, pygame - -def startup(): - pygame.init() - pygame.mixer.init() - gamestate.screen = pygame.display.set_mode(gamestate.sdim) - cursor.startup() - cutscene.makeinfo() - pygame.display.set_caption("Panspermia") - -def coverage(): - a = 100 - im = pygame.Surface((2*a, 2*a)) - im.fill((0, 255, 0)) - for s in thing.StarPlant.group.sprites(): - x, y, r = int(s.x * a) + a, int(s.y * a) + a, int(s.rstar * a) - pygame.draw.ellipse(im, (255, 255, 0), pygame.Rect(x-r, y-r, 2*r, 2*r)) - lawn.cookiecut(im) -# r = im.get_rect() -# r.topright = gamestate.screen.get_rect().topright -# gamestate.screen.blit(im, r) - s = 1 - im = pygame.transform.smoothscale(im, (s,s)) - r, g = 0., 0 - for x in range(s): - for y in range(s): - r += im.get_at((x,y))[0] - g += im.get_at((x,y))[1] -# print r,g - return r,g,r/g - -def titlescreen(): - if not pygame.font.get_init(): pygame.font.init() - f = pygame.font.Font(data.filepath("janken.ttf"), 200) - f2 = pygame.font.Font(None, 16) - f3 = pygame.font.Font(None, 22) - - s0 = pygame.Surface(gamestate.sdim) - s0.fill((0,0,0)) - s0.set_colorkey(0) - s = f.render("Panspermia", True, (128, 255, 128)) - r = s.get_rect() - x, y = gamestate.screen.get_rect().center - r.center = x, y - 80 - s0.blit(s, r) - - s = f2.render("by Christopher Night, Pyweek 8, May 2009", True, (180, 180, 180)) - r = s.get_rect() - x, y = s0.get_rect().midright - r.midright = x - 20, y + 30 - s0.blit(s, r) - s = f2.render("music by Kevin MacLeod [incompetech.com]", True, (180, 180, 180)) - r = s.get_rect() - x, y = s0.get_rect().midright - r.midright = x - 20, y + 45 - s0.blit(s, r) - - s = f3.render("Press Esc to quit or any other key to begin", True, (180, 180, 180)) - r = s.get_rect() - x, y = s0.get_rect().midbottom - r.midbottom = x, y - 30 - s0.blit(s, r) - - stars = [(random.uniform(-20, 20), random.uniform(-20, 20), random.randint(20,100)) for j in range(1000)] - clock = pygame.time.Clock() - while 1: - clock.tick(30.) - gamestate.screen.fill((0,0,0)) - for j in range(len(stars)): - x, y, t = stars[j] - px, py = int(400 + x * t), int(250 + y * t) - gamestate.screen.set_at((px, py), (192, 192, 192)) - if (x**2 + y**2) * t**2 > 400**2 + 250**2: - stars[j] = (random.uniform(-20, 20), random.uniform(-20, 20), random.randint(20,100)) - else: - stars[j] = x, y, t+1 - gamestate.screen.blit(s0, (0,0)) - pygame.display.flip() - events = pygame.event.get(pygame.KEYDOWN) - for e in events: - return e.key - -def glow(): - s = pygame.Surface(gamestate.sdim) - s.fill((255, 255, 255)) - s.set_alpha(16) - r = s.get_rect() - for j in range(24): - gamestate.screen.blit(s, r) - pygame.display.flip() - -def main(): - startup() - pygame.mixer.music.load(data.filepath("ominous-intro.mp3")) - pygame.mixer.music.play() - key = titlescreen() - while key != pygame.K_ESCAPE: - cheat = {pygame.K_1:1, pygame.K_2:2, pygame.K_3:3, pygame.K_4:4, pygame.K_5:5, pygame.K_6:6} - if key in cheat: - gamestate.level = cheat[key] - print "Cheat code for level %d entered!" % cheat[key] - alive = True - while alive: - gamestate.loadlevel() - transition() - if gamestate.level < 6: - pygame.mixer.music.stop() - alive = playlevel() - if alive: glow() - if alive: gamestate.level += 1 - pygame.mixer.music.load(data.filepath("ominous-intro.mp3")) - pygame.mixer.music.play() - else: - s = data.loadimg("the-end.png") - gamestate.screen.blit(s, (0,0)) - pygame.display.flip() - pygame.event.get() - while not pygame.event.get(pygame.KEYDOWN): pass - alive = False - gamestate.level = 1 - key = titlescreen() - -def transition(): - pygame.event.get() - text = [] - file = data.load("dialogue") - for line in file: - if "@" in line: - n, t = line.split("@") - if n == str(gamestate.level): text.append(t) - for t in text: - s = signage.Sign(240, fsize=20) - s.centered = True - s.addtext(t) - s.render() - s.rect.center = gamestate.screen.get_rect().center - gamestate.screen.fill((0,0,0)) - s.draw(gamestate.screen) - pygame.display.flip() - - done = False - while not done: - event = [] - event = pygame.event.get() -# pygame.event.get() - lawn.freetime() - for e in event: - if e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE: return False - if e.type == pygame.KEYDOWN: done = True - if e.type == pygame.MOUSEBUTTONDOWN: done = True - return True - -def helpscreen(): - darkness = pygame.Surface(gamestate.sdim) - darkness.fill((0,0,0)) - darkness.set_alpha(140) - gamestate.screen.blit(darkness, (0,0)) - - s = signage.Sign(240, fsize=20) - s.centered = True - s.addtext("Game paused: press Esc to quit, or any other key to continue") - s.render() - s.rect.center = gamestate.screen.get_rect().center - s.draw(gamestate.screen) - - pygame.display.flip() - event = [] - while not event: - event = pygame.event.get(pygame.KEYDOWN) - for e in event: - if e.key == pygame.K_ESCAPE: return False - return True - - - -def playlevel(): - clock = pygame.time.Clock() - gamestate.done = False - ctheta = 0 - gamestate.theta = 0 - gamestate.setCS() - fskip, jframe = 1, 0 - - for s in thing.Thing.group: s.kill() - s = thing.Seeder((0,0)) - curs = cursor.Cursor() - curs.makemenu() - curs.updatemenu() - r = rain.Rain() - if gamestate.level == 1: - pygame.mixer.music.load(data.filepath("rain.mp3")) - else: - pygame.mixer.music.load(data.filepath("arcadia.mp3")) - pygame.mixer.music.play(-1) - - while not gamestate.done: - if gamestate.helpon and not cutscene.checkinfo(): - clock.tick(30) - pygame.display.flip() - continue - - # stuff done every frame, no matter what - curs.update() - if random.randint(0, gamestate.twinklefrequency) == 0: - t = thing.Twinkle(gamestate.randompos()) - r.dohazard() - if gamestate.coverage >= 0.9: gamestate.cticker -= 1 - else: gamestate.cticker = gamestate.cticker0 - if gamestate.cticker == 0: - pygame.mixer.music.stop() - audio.play("glissando") - return True # win - if not thing.Seeder.group: - pygame.mixer.music.stop() - return False # lose -# thing.update() # should be here, but causes blitting problems - jframe += 1 - if jframe % fskip: continue - - # stuff done only on non-skipped frames - clock.tick(gamestate.framerate / fskip) - events = pygame.event.get() - curs.keypress(events) - if gamestate.done: - gamestate.done = not helpscreen() - - m = pygame.key.get_pressed() - if m[pygame.K_RIGHT]: - gamestate.addtheta(gamestate.lawnspeed) - if m[pygame.K_LEFT]: - gamestate.addtheta(-gamestate.lawnspeed) - - if jframe % gamestate.setcoveragefrequency == 0: gamestate.setcoverage() - - gamestate.screen.fill(r.skycolor()) - gamestate.screen.blit(lawn.frame(int(gamestate.theta/1.)%360), (0,100)) - if jframe % 30 == 0: curs.makecoverage() - curs.drawcoverage() - curs.drawselector() - thing.update() - thing.draw(gamestate.screen) - curs.drawcursor() - r.draw(gamestate.screen) - r.drawfx() - curs.drawinfo() - curs.drawmenu() -# print coverage(), clock.get_fps() -# if jframe % 30 == 0: print clock.get_fps() -# for a in thing.Thing.tgroup.sprites(): a.drawpoint() - pygame.display.flip() - pygame.mixer.music.stop() - - diff -ruN -x '*.ttf' -x '*.ogg' a/gamelib/panspermia_main.py b/gamelib/panspermia_main.py --- a/gamelib/panspermia_main.py 1969-12-31 21:00:00.000000000 -0300 +++ b/gamelib/panspermia_main.py 2026-11-05 00:05:47.036899934 -0300 @@ -0,0 +1,265 @@ +import data, gamestate, lawn, thing, rain, cursor, cutscene, signage, audio +import math, random, pygame +import asyncio +import time as pytime + +_last = [0.0] +async def tick(fps): + # frame limiter for the browser: the page needs control back every frame + await asyncio.sleep(max(0.0, 1.0 / fps - (pytime.time() - _last[0]))) + _last[0] = pytime.time() + +def startup(): + pygame.init() + pygame.mixer.init() + gamestate.screen = pygame.display.set_mode(gamestate.sdim) + cursor.startup() + cutscene.makeinfo() + pygame.display.set_caption("Panspermia") + +def coverage(): + a = 100 + im = pygame.Surface((2*a, 2*a)) + im.fill((0, 255, 0)) + for s in thing.StarPlant.group.sprites(): + x, y, r = int(s.x * a) + a, int(s.y * a) + a, int(s.rstar * a) + pygame.draw.ellipse(im, (255, 255, 0), pygame.Rect(x-r, y-r, 2*r, 2*r)) + lawn.cookiecut(im) +# r = im.get_rect() +# r.topright = gamestate.screen.get_rect().topright +# gamestate.screen.blit(im, r) + s = 1 + im = pygame.transform.smoothscale(im, (s,s)) + r, g = 0., 0 + for x in range(s): + for y in range(s): + r += im.get_at((x,y))[0] + g += im.get_at((x,y))[1] +# print r,g + return r,g,r/g + +async def titlescreen(): + if not pygame.font.get_init(): pygame.font.init() + f = pygame.font.Font(data.filepath("Bangers-Regular.ttf"), 160) + f2 = pygame.font.Font(None, 16) + f3 = pygame.font.Font(None, 22) + + s0 = pygame.Surface(gamestate.sdim) + s0.fill((0,0,0)) + s0.set_colorkey(0) + s = f.render("Panspermia", True, (128, 255, 128)) + r = s.get_rect() + x, y = gamestate.screen.get_rect().center + r.center = x, y - 80 + s0.blit(s, r) + + s = f2.render("by Christopher Night, Pyweek 8, May 2009", True, (180, 180, 180)) + r = s.get_rect() + x, y = s0.get_rect().midright + r.midright = x - 20, y + 30 + s0.blit(s, r) + s = f2.render("music by Kevin MacLeod [incompetech.com]", True, (180, 180, 180)) + r = s.get_rect() + x, y = s0.get_rect().midright + r.midright = x - 20, y + 45 + s0.blit(s, r) + + s = f3.render("Press any key to begin", True, (180, 180, 180)) + r = s.get_rect() + x, y = s0.get_rect().midbottom + r.midbottom = x, y - 30 + s0.blit(s, r) + + stars = [(random.uniform(-20, 20), random.uniform(-20, 20), random.randint(20,100)) for j in range(1000)] + clock = pygame.time.Clock() + while 1: + await tick(30.) + gamestate.screen.fill((0,0,0)) + for j in range(len(stars)): + x, y, t = stars[j] + px, py = int(400 + x * t), int(250 + y * t) + gamestate.screen.set_at((px, py), (192, 192, 192)) + if (x**2 + y**2) * t**2 > 400**2 + 250**2: + stars[j] = (random.uniform(-20, 20), random.uniform(-20, 20), random.randint(20,100)) + else: + stars[j] = x, y, t+1 + gamestate.screen.blit(s0, (0,0)) + pygame.display.flip() + events = pygame.event.get(pygame.KEYDOWN) + for e in events: + if e.key != pygame.K_ESCAPE: # the page has nowhere to quit to + return e.key + +def glow(): + s = pygame.Surface(gamestate.sdim) + s.fill((255, 255, 255)) + s.set_alpha(16) + r = s.get_rect() + for j in range(24): + gamestate.screen.blit(s, r) + pygame.display.flip() + +async def main(): + startup() + pygame.mixer.music.load(data.filepath("ominous-intro.ogg")) + pygame.mixer.music.play() + key = await titlescreen() + while key != pygame.K_ESCAPE: + cheat = {pygame.K_1:1, pygame.K_2:2, pygame.K_3:3, pygame.K_4:4, pygame.K_5:5, pygame.K_6:6} + if key in cheat: + gamestate.level = cheat[key] + print("Cheat code for level %d entered!" % cheat[key]) + alive = True + while alive: + gamestate.loadlevel() + await transition() + if gamestate.level < 6: + pygame.mixer.music.stop() + alive = await playlevel() + if alive: glow() + if alive: gamestate.level += 1 + pygame.mixer.music.load(data.filepath("ominous-intro.ogg")) + pygame.mixer.music.play() + else: + s = data.loadimg("the-end.png") + gamestate.screen.blit(s, (0,0)) + pygame.display.flip() + pygame.event.get() + while not pygame.event.get(pygame.KEYDOWN): await tick(30.) + alive = False + gamestate.level = 1 + key = await titlescreen() + +async def transition(): + pygame.event.get() + text = [] + file = data.load("dialogue") + for line in file: + if "@" in line: + n, t = line.split("@") + if n == str(gamestate.level): text.append(t) + for t in text: + s = signage.Sign(240, fsize=20) + s.centered = True + s.addtext(t) + s.render() + s.rect.center = gamestate.screen.get_rect().center + gamestate.screen.fill((0,0,0)) + s.draw(gamestate.screen) + pygame.display.flip() + + done = False + while not done: + event = [] + await tick(30.) + event = pygame.event.get() +# pygame.event.get() + lawn.freetime() + for e in event: + if e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE: return False + if e.type == pygame.KEYDOWN: done = True + if e.type == pygame.MOUSEBUTTONDOWN: done = True + return True + +async def helpscreen(): + darkness = pygame.Surface(gamestate.sdim) + darkness.fill((0,0,0)) + darkness.set_alpha(140) + gamestate.screen.blit(darkness, (0,0)) + + s = signage.Sign(240, fsize=20) + s.centered = True + s.addtext("Game paused: press Esc to quit, or any other key to continue") + s.render() + s.rect.center = gamestate.screen.get_rect().center + s.draw(gamestate.screen) + + pygame.display.flip() + event = [] + while not event: + await tick(30.) + event = pygame.event.get(pygame.KEYDOWN) + for e in event: + if e.key == pygame.K_ESCAPE: return False + return True + + + +async def playlevel(): + clock = pygame.time.Clock() + gamestate.done = False + ctheta = 0 + gamestate.theta = 0 + gamestate.setCS() + fskip, jframe = 1, 0 + + for s in thing.Thing.group: s.kill() + s = thing.Seeder((0,0)) + curs = cursor.Cursor() + curs.makemenu() + curs.updatemenu() + r = rain.Rain() + if gamestate.level == 1: + pygame.mixer.music.load(data.filepath("arcadia.ogg")) + else: + pygame.mixer.music.load(data.filepath("arcadia.ogg")) + pygame.mixer.music.play(-1) + + while not gamestate.done: + if gamestate.helpon and not cutscene.checkinfo(): + await tick(30) + pygame.display.flip() + continue + + # stuff done every frame, no matter what + curs.update() + if random.randint(0, gamestate.twinklefrequency) == 0: + t = thing.Twinkle(gamestate.randompos()) + r.dohazard() + if gamestate.coverage >= 0.9: gamestate.cticker -= 1 + else: gamestate.cticker = gamestate.cticker0 + if gamestate.cticker == 0: + pygame.mixer.music.stop() + audio.play("glissando") + return True # win + if not thing.Seeder.group: + pygame.mixer.music.stop() + return False # lose +# thing.update() # should be here, but causes blitting problems + jframe += 1 + if jframe % fskip: continue + + # stuff done only on non-skipped frames + await tick(gamestate.framerate / fskip) + events = pygame.event.get() + curs.keypress(events) + if gamestate.done: + gamestate.done = not await helpscreen() + + m = pygame.key.get_pressed() + if m[pygame.K_RIGHT]: + gamestate.addtheta(gamestate.lawnspeed) + if m[pygame.K_LEFT]: + gamestate.addtheta(-gamestate.lawnspeed) + + if jframe % gamestate.setcoveragefrequency == 0: gamestate.setcoverage() + + gamestate.screen.fill(r.skycolor()) + gamestate.screen.blit(lawn.frame(int(gamestate.theta/1.)%360), (0,100)) + if jframe % 30 == 0: curs.makecoverage() + curs.drawcoverage() + curs.drawselector() + thing.update() + thing.draw(gamestate.screen) + curs.drawcursor() + r.draw(gamestate.screen) + r.drawfx() + curs.drawinfo() + curs.drawmenu() +# print coverage(), clock.get_fps() +# if jframe % 30 == 0: print clock.get_fps() +# for a in thing.Thing.tgroup.sprites(): a.drawpoint() + pygame.display.flip() + pygame.mixer.music.stop() + + diff -ruN -x '*.ttf' -x '*.ogg' a/gamelib/rain.py b/gamelib/rain.py --- a/gamelib/rain.py 2026-11-04 23:49:54.915947480 -0300 +++ b/gamelib/rain.py 2026-11-05 00:05:47.079861280 -0300 @@ -3,12 +3,12 @@ class Drop: c = (64, 64, 128) - def __init__(self, xrange, yrange, smin, smax): - self.x = random.uniform(0, xrange) + def __init__(self, range, yrange, smin, smax): + self.x = random.uniform(0, range) self.y = random.uniform(0, yrange) self.s = random.uniform(smin, smax) - def addtop(self, xrange, y): - self.x = random.uniform(0, xrange) + def addtop(self, range, y): + self.x = random.uniform(0, range) self.y = y - self.s def draw(self, surf, dx, dy): pygame.draw.aaline(surf, self.c, (self.x, self.y), (self.x + dx, self.y + dy)) diff -ruN -x '*.ttf' -x '*.ogg' a/gamelib/thing.py b/gamelib/thing.py --- a/gamelib/thing.py 2026-11-04 23:49:54.915604066 -0300 +++ b/gamelib/thing.py 2026-11-05 00:05:47.078409918 -0300 @@ -11,6 +11,13 @@ # class Shadow: not a Thing. Why is it in this module? +def _spriteinit(self): + # pygame-ce's Sprite.__init__ resets image and rect to None; pygame 1.9 left them alone + image, rect = getattr(self, "image", None), getattr(self, "rect", None) + pygame.sprite.Sprite.__init__(self) + if image is not None: self.image = image + if rect is not None: self.rect = rect + class Thing (pygame.sprite.Sprite): group = pygame.sprite.LayeredUpdates() tgroup = pygame.sprite.Group() @@ -18,7 +25,7 @@ igroup = pygame.sprite.Group() dpx = dpy = None def __init__(self, p): - pygame.sprite.Sprite.__init__(self) + _spriteinit(self) self.movable = False self.shadow = None self.indicator = None @@ -29,7 +36,7 @@ self.target = None self.isstony = False if self.dpx == None: - self.dpx, self.dpy = self.rect.width/2, self.rect.height + self.dpx, self.dpy = self.rect.width//2, self.rect.height self.doq = [] # The "do" queue Thing.group.add(self) Thing.tgroup.add(self) @@ -47,7 +54,7 @@ Thing.igroup.remove(self) def update(self): if len(self.doq): - exec self.doq.pop(0) + exec(self.doq.pop(0)) if not self in Thing.group: return layer = gamestate.Rxy(self.x,self.y)[1] Thing.group.change_layer(self, layer) @@ -327,7 +334,7 @@ def update(self): if not self.isdisabled() and len(Acorn.group) < gamestate.maxacorns: self.ticker += 1. - g = int(gamestate.seederfrequency / (gamestate.maxacorns - len(Acorn.group))) + g = int(gamestate.seederfrequency // (gamestate.maxacorns - len(Acorn.group))) if self.ticker > g: self.image = self.imagebank.image() self.ticker = 0 @@ -698,13 +705,13 @@ group = pygame.sprite.Group() def getimage(self, size): if size in Shadow.images: return Shadow.images[size] - s = pygame.Surface((size,size/2)).convert_alpha() + s = pygame.Surface((size,size//2)).convert_alpha() s.fill((0,0,0,0)) pygame.draw.ellipse(s, (0,0,0,Shadow.alpha), s.get_rect()) Shadow.images[size] = s return s def __init__(self, caster): - pygame.sprite.Sprite.__init__(self) + _spriteinit(self) self.caster = caster self.size = caster.image.get_width() self.image = self.getimage(self.size) @@ -717,7 +724,7 @@ class Indicator(pygame.sprite.Sprite): image = None def __init__(self, target): - pygame.sprite.Sprite.__init__(self) + _spriteinit(self) if not Indicator.image: Indicator.image = data.loadimg("arrow.png") self.rect = Indicator.image.get_rect() @@ -739,7 +746,7 @@ Ibar.images[(w, wmax)] = s return s def __init__(self, wmax): - pygame.sprite.Sprite.__init__(self) + _spriteinit(self) self.image = self.getimage(0, wmax) self.rect = self.image.get_rect() Thing.group.add(self) diff -ruN -x '*.ttf' -x '*.ogg' a/main.py b/main.py --- a/main.py 1969-12-31 21:00:00.000000000 -0300 +++ b/main.py 2026-11-05 00:05:47.036445854 -0300 @@ -0,0 +1,9 @@ +import pygame +import asyncio +import os +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "gamelib")) +import panspermia_main + +asyncio.run(panspermia_main.main()) diff -ruN -x '*.ttf' -x '*.ogg' a/run_game.py b/run_game.py --- a/run_game.py 2026-11-04 23:49:54.912521592 -0300 +++ b/run_game.py 1969-12-31 21:00:00.000000000 -0300 @@ -1,14 +0,0 @@ -#! /usr/bin/env python -import sys, os - -try: - __file__ -except NameError: - pass -else: - libdir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'gamelib')) - sys.path.insert(0, libdir) - -sys.path.append(os.path.join("gamelib")) -import main -main.main()