Mercurial > touhou
view pytouhou/game/laser.pyx @ 772:7492d384d122 default tip
Rust: Add a Glide renderer (2D only for now)
This is an experiment for a Rust renderer, iterating over the Python data using
pyo3. It requires --feature=glide to be passed to cargo build, doesn’t support
NPOT textures, text rendering, the background, or even msg faces, some of that
may come in a future changeset.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Mon, 05 Sep 2022 17:53:36 +0200 (2022-09-05) |
parents | a6af3ff86612 |
children |
line wrap: on
line source
# -*- encoding: utf-8 -*- ## ## Copyright (C) 2012 Thibaut Girka <thib@sitedethib.com> ## ## 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 libc.math cimport cos, sin, M_PI as pi from pytouhou.game.game cimport Game from pytouhou.vm import ANMRunner cdef class LaserLaunchAnim(Element): def __init__(self, Laser laser, anm, unsigned long index): Element.__init__(self, (0, 0)) self._laser = laser self.sprite = Sprite() self.sprite.anm = anm self.sprite.texcoords = anm.sprites[index] self.sprite.blendfunc = 1 cpdef update(self): laser = self._laser length = <double>min(laser.end_offset - laser.start_offset, laser.max_length) offset = laser.end_offset - length dx, dy = cos(laser.angle), sin(laser.angle) self.x = laser.base_pos[0] + offset * dx self.y = laser.base_pos[1] + offset * dy scale = laser.width / 10. - (offset - laser.start_offset) #TODO: check self.sprite._rescale[:] = [scale, scale] self.sprite.changed = True if laser.removed or scale <= 0.: self.removed = True cdef class Laser(Element): def __init__(self, tuple base_pos, LaserType laser_type, unsigned long sprite_idx_offset, double angle, double speed, double start_offset, double end_offset, double max_length, double width, unsigned long start_duration, unsigned long duration, unsigned long stop_duration, unsigned long grazing_delay, unsigned long grazing_extra_duration, Game game): Element.__init__(self, (0, 0)) launch_anim = LaserLaunchAnim(self, laser_type.anm, laser_type.launch_anim_offsets[sprite_idx_offset] + laser_type.launch_sprite_idx) game.effects.append(launch_anim) self._laser_type = laser_type self.state = STARTING #TODO: hitbox self.frame = 0 self.start_duration = start_duration self.duration = duration self.stop_duration = stop_duration self.grazing_delay = grazing_delay self.grazing_extra_duration = grazing_extra_duration self.sprite_idx_offset = sprite_idx_offset self.set_base_pos(base_pos[0], base_pos[1]) self.angle = angle self.speed = speed self.start_offset = start_offset self.end_offset = end_offset self.max_length = max_length self.width = width self.set_anim() cdef bint set_anim(self, long sprite_idx_offset=-1) except True: if sprite_idx_offset >= 0: self.sprite_idx_offset = sprite_idx_offset lt = self._laser_type self.sprite = Sprite() self.sprite.angle = self.angle self.anmrunner = ANMRunner(lt.anm, lt.anim_index, self.sprite, self.sprite_idx_offset) cpdef set_base_pos(self, double x, double y): self.base_pos[:] = [x, y] cdef bint _check_collision(self, double point[2], double border_size): cdef double c1[2] cdef double c2[2] cdef double c3[2] x, y = point[0] - self.base_pos[0], point[1] - self.base_pos[1] dx, dy = cos(self.angle), sin(self.angle) dx2, dy2 = -dy, dx length = <double>min(self.end_offset - self.start_offset, self.max_length) offset = self.end_offset - length - border_size / 2. end_offset = self.end_offset + border_size / 2. half_width = self.width / 4. + border_size / 2. c1[:] = [dx * offset - dx2 * half_width, dy * offset - dy2 * half_width] c2[:] = [dx * offset + dx2 * half_width, dy * offset + dy2 * half_width] c3[:] = [dx * end_offset + dx2 * half_width, dy * end_offset + dy2 * half_width] vx, vy = x - c2[0], y - c2[1] v1x, v1y = c1[0] - c2[0], c1[1] - c2[1] v2x, v2y = c3[0] - c2[0], c3[1] - c2[1] return (0 <= vx * v1x + vy * v1y <= v1x * v1x + v1y * v1y and 0 <= vx * v2x + vy * v2y <= v2x * v2x + v2y * v2y) cdef bint check_collision(self, double point[2]): if self.state != STARTED: return False return self._check_collision(point, 2.5) cdef bint check_grazing(self, double point[2]): #TODO: quadruple check! if self.state == STOPPING and self.frame >= self.grazing_extra_duration: return False if self.state == STARTING and self.frame <= self.grazing_delay: return False if self.frame % 12 != 0: return False return self._check_collision(point, 96 + 2.5) def get_bullets_pos(self): #TODO: check length = <double>min(self.end_offset - self.start_offset, self.max_length) offset = self.end_offset - length dx, dy = cos(self.angle), sin(self.angle) while self.start_offset <= offset < self.end_offset: yield (self.base_pos[0] + offset * dx, self.base_pos[1] + offset * dy) offset += 48. cpdef cancel(self): self.grazing_extra_duration = 0 if self.state != STOPPING: self.frame = 0 self.state = STOPPING cpdef update(self): if self.anmrunner is not None and not self.anmrunner.run_frame(): self.anmrunner = None self.end_offset += self.speed length = <double>min(self.end_offset - self.start_offset, self.max_length) # TODO width = 0. if self.state == STARTING: if self.frame == self.start_duration: self.frame = 0 self.state = STARTED else: width = self.width * float(self.frame) / self.start_duration #TODO elif self.state == STARTED: width = self.width #TODO if self.frame == self.duration: self.frame = 0 self.state = STOPPING elif self.state == STOPPING: if self.frame == self.stop_duration: self.removed = True else: width = self.width * (1. - float(self.frame) / self.stop_duration) #TODO offset = self.end_offset - length / 2. self.x = self.base_pos[0] + offset * cos(self.angle) self.y = self.base_pos[1] + offset * sin(self.angle) self.sprite.visible = (width > 0 and length > 0) self.sprite.width_override = width self.sprite.height_override = length self.sprite.update_orientation(pi/2. - self.angle, True) self.sprite.changed = True #TODO self.frame += 1 cdef class PlayerLaser(Element): def __init__(self, LaserType laser_type, unsigned long sprite_idx_offset, tuple hitbox, unsigned long damage, double angle, double offset, unsigned long duration, Element origin): Element.__init__(self) self._laser_type = laser_type self.origin = origin self.hitbox[:] = [hitbox[0], hitbox[1]] self.frame = 0 self.duration = duration self.sprite_idx_offset = sprite_idx_offset self.angle = angle self.offset = offset self.damage = damage self.set_anim() cdef bint set_anim(self, long sprite_idx_offset=-1) except True: if sprite_idx_offset >= 0: self.sprite_idx_offset = sprite_idx_offset lt = self._laser_type self.sprite = Sprite() self.anmrunner = ANMRunner(lt.anm, lt.anim_index, self.sprite, self.sprite_idx_offset) #self.sprite.blendfunc = 1 #XXX cdef bint cancel(self) except True: self.anmrunner.interrupt(1) cdef bint update(self) except True: if self.anmrunner is not None and not self.anmrunner.run_frame(): self.anmrunner = None self.removed = True length = self.origin.y if self.frame == self.duration: self.cancel() self.sprite.visible = (length > 0) self.sprite.height_override = length self.sprite.changed = True #TODO self.x = self.origin.x + self.offset * cos(self.angle) self.y = self.origin.y / 2. + self.offset * sin(self.angle) self.frame += 1