Mercurial > touhou
view pytouhou/game/text.py @ 792:11bc22bad1bf
python: Replace the image crate with png
We weren’t using any of its features anyway, so the png crate is exactly what
we need, without the many heavy dependencies of image.
https://github.com/image-rs/image-png/pull/670 will eventually make it even
faster to build.
| author | Link Mauve <linkmauve@linkmauve.fr> |
|---|---|
| date | Sat, 17 Jan 2026 22:22:25 +0100 |
| parents | f2c3848dabff |
| children |
line wrap: on
line source
# -*- encoding: utf-8 -*- ## ## Copyright (C) 2011 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published ## by the Free Software Foundation; version 3 only. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## from pytouhou.vm import ANMRunner class Glyph(Element): def __init__(self, sprite, pos): Element.__init__(self, pos) self.sprite = sprite class Widget(Element): def __init__(self, pos, back_anm=None, back_script=22): Element.__init__(self, pos) self.changed = True self.frame = 0 # Set up the backround sprite self.back_anm = back_anm if back_anm: self.sprite = Sprite() self.anmrunner = ANMRunner(back_anm, back_script, self.sprite) def normal_update(self): if self.changed: if self.anmrunner is not None and not self.anmrunner.run_frame(): self.anmrunner = None self.changed = False self.frame += 1 class GlyphCollection(Widget): def __init__(self, pos, anm, back_anm=None, ref_script=0, xspacing=14, back_script=22): Widget.__init__(self, pos, back_anm, back_script) self.ref_sprite = Sprite() self.anm = anm self.glyphes = [] self.xspacing = xspacing # Set up ref sprite ANMRunner(anm, ref_script, self.ref_sprite) self.ref_sprite.corner_relative_placement = True #TODO: perhaps not right def set_length(self, length): current_length = len(self.glyphes) if length > current_length: self.glyphes.extend([Glyph(self.ref_sprite.copy(), (self.x + self.xspacing * i, self.y)) for i in range(current_length, length)]) self.objects = [self] + self.glyphes elif length < current_length: self.glyphes[:] = self.glyphes[:length] self.objects = [self] + self.glyphes def set_sprites(self, sprite_indexes): self.set_length(len(sprite_indexes)) for glyph, idx in zip(self.glyphes, sprite_indexes): glyph.sprite.anm = self.anm glyph.sprite.texcoords = self.anm.sprites[idx] glyph.sprite.changed = True def set_color(self, text=None, color=None): if text is not None: colors = {'white': (255, 255, 255), 'yellow': (255, 255, 0), 'blue': (192, 192, 255), 'darkblue': (160, 128, 255), 'purple': (224, 128, 255), 'red': (255, 64, 0)} color = colors[text] else: assert color is not None self.ref_sprite.color = color for glyph in self.glyphes: glyph.sprite.color = color def set_alpha(self, alpha): self.ref_sprite.alpha = alpha for glyph in self.glyphes: glyph.sprite.alpha = alpha class Text(GlyphCollection): def __init__(self, pos, ascii_anm, back_anm=None, text=b'', xspacing=14, shift=21, back_script=22, align='left'): GlyphCollection.__init__(self, pos, ascii_anm, back_anm, xspacing=xspacing, back_script=back_script) self.text = b'' self.shift = shift if align == 'center': self.x -= xspacing * len(text) // 2 elif align == 'right': self.x -= xspacing * len(text) else: assert align == 'left' self.set_text(text) def set_text(self, text): if isinstance(text, str): text = text.encode() if text == self.text: return self.set_sprites([c - self.shift for c in text]) self.text = text self.changed = True def timeout_update(self): GlyphCollection.normal_update(self) if self.frame == self.timeout: self.removed = True def move_timeout_update(self): if self.frame % 2: for glyph in self.glyphes: glyph.y -= 1 self.timeout_update() def fadeout_timeout_update(self): if self.frame >= self.start: if self.frame == self.start: self.fade(self.duration, 255) elif self.frame == self.timeout - self.duration: self.fade(self.duration, 0) self.fade_interpolator.update(self.frame) self.alpha = int(self.fade_interpolator.values[0]) for glyph in self.glyphes: glyph.sprite.alpha = self.alpha glyph.sprite.changed = True self.timeout_update() def fade(self, duration, alpha, formula=None): self.fade_interpolator = Interpolator((self.alpha,), self.frame, (alpha,), self.frame + duration, formula) def set_timeout(self, timeout, effect=None, duration=0, start=0): self.timeout = timeout + start if effect == 'move': self.update = self.move_timeout_update elif effect == 'fadeout': self.alpha = 0 for glyph in self.glyphes: glyph.sprite.alpha = 0 self.update = self.fadeout_timeout_update self.duration = duration self.start = start else: self.update = self.timeout_update class Counter(GlyphCollection): def __init__(self, pos, anm, back_anm=None, script=0, xspacing=16, value=0, back_script=22): GlyphCollection.__init__(self, pos, anm, back_anm=back_anm, ref_script=script, xspacing=xspacing, back_script=back_script) self.value = value self.set_value(value) def set_value(self, value): if value < 0: value = 0 if value == self.value: return self.set_length(value) self.value = value self.changed = True class Gauge(Element): def __init__(self, pos, anm, max_length=280, maximum=1, value=0): Element.__init__(self, pos) self.sprite = Sprite() self.anmrunner = ANMRunner(anm, 21, self.sprite) self.sprite.corner_relative_placement = True #TODO: perhaps not right self.max_length = max_length self.maximum = maximum self.set_value(value) def set_value(self, value): self.value = value self.sprite.width_override = self.max_length * value / self.maximum self.sprite.changed = True #TODO def update(self): #XXX if self.value == 0: self.sprite.visible = False else: self.sprite.visible = True if self.anmrunner is not None and not self.anmrunner.run_frame(): self.anmrunner = None class NativeText(Element): def __init__(self, pos, text, gradient=None, alpha=255, shadow=False, align='left'): self.removed = False self.x, self.y = pos self.text = text self.alpha = alpha self.shadow = shadow self.align = align self.frame = 0 self.gradient = gradient or [(255, 255, 255), (255, 255, 255), (128, 128, 255), (128, 128, 255)] self.update = self.normal_update def normal_update(self): self.frame += 1 def timeout_update(self): self.normal_update() if self.frame == self.timeout: self.removed = True def move_timeout_update(self): if self.frame % 2: self.y -= 1 self.timeout_update() def move_ex_timeout_update(self): if self.frame >= self.start: if self.frame == self.start: self.move_in(self.duration, self.to[0], self.to[1]) elif self.frame == self.timeout - self.duration: self.move_in(self.duration, self.end[0], self.end[1]) if self.offset_interpolator: self.offset_interpolator.update(self.frame) self.x, self.y = self.offset_interpolator.values self.timeout_update() def fadeout_timeout_update(self): if self.frame >= self.start: if self.frame == self.start: self.fade(self.duration, 255) elif self.frame == self.timeout - self.duration: self.fade(self.duration, 0) self.fade_interpolator.update(self.frame) self.alpha = int(self.fade_interpolator.values[0]) self.timeout_update() def fade(self, duration, alpha, formula=None): self.fade_interpolator = Interpolator((self.alpha,), self.frame, (alpha,), self.frame + duration, formula) def move_in(self, duration, x, y, formula=None): self.offset_interpolator = Interpolator((self.x, self.y), self.frame, (x, y), self.frame + duration, formula) def set_timeout(self, timeout, effect=None, duration=0, start=0, to=None, end=None): self.timeout = timeout + start if effect == 'move': self.update = self.move_timeout_update elif effect == 'move_ex': self.update = self.move_ex_timeout_update self.duration = duration self.start = start self.to[:] = [to[0], to[1]] self.end[:] = [end[0], end[1]] elif effect == 'fadeout': self.alpha = 0 self.update = self.fadeout_timeout_update self.duration = duration self.start = start else: self.update = self.timeout_update
