From 5bb304742332627220adc58d962180abbe11e84b Mon Sep 17 00:00:00 2001 From: Pavel Kulyov Date: Mon, 4 May 2020 10:38:56 +0300 Subject: [PATCH] Pass and use dt, update EAF to 0.2 Signed-off-by: Pavel Kulyov --- README.rst | 4 ++-- pyproject.toml | 11 ++++++----- xoinvader/animation.py | 12 ++++++------ xoinvader/background.py | 25 ++++++++++--------------- xoinvader/charge.py | 4 ++-- xoinvader/config/xoinvader.toml | 12 ++++++------ xoinvader/gui.py | 14 +++++++------- xoinvader/ingame.py | 6 +++--- xoinvader/ship.py | 26 ++++++++++++++------------ xoinvader/utils.py | 23 +++++++++++------------ xoinvader/weapon.py | 4 ++-- 11 files changed, 69 insertions(+), 72 deletions(-) diff --git a/README.rst b/README.rst index 71f224e..7576006 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,8 @@ General ------- * >= python-3.7 -* >= eaf-0.1 +* >= eaf-0.2 +* >= xo1-0.1 * >= toml-0.10 * ncurses >=5.9 @@ -35,7 +36,6 @@ Development .. code-block:: console $ poetry install - $ poetry run xoigame Testing diff --git a/pyproject.toml b/pyproject.toml index f512475..09cc1bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "xoinvader" -version = "0.1.3" +version = "0.2.0" description = "(not so) small python curses space game" authors = ["Pavel Kulyov "] license = "MIT" @@ -20,10 +20,10 @@ classifiers = [ "Intended Audience :: Education", "Intended Audience :: End Users/Desktop", "License :: OSI Approved :: MIT License", - "Natural Language :: English", "Operating System :: POSIX :: Linux", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Games/Entertainment :: Arcade", "Topic :: Software Development :: Libraries :: pygame", @@ -34,8 +34,9 @@ xoigame = "xoinvader.game:main" [tool.poetry.dependencies] python = "^3.7" -eaf = "*" -toml = ">=0.10" +eaf = "^0.2" +xo1 = "^0.1" +toml = "^0.10" [tool.poetry.dev-dependencies] codecov = "*" diff --git a/xoinvader/animation.py b/xoinvader/animation.py index c80ceab..16b5ced 100644 --- a/xoinvader/animation.py +++ b/xoinvader/animation.py @@ -77,14 +77,14 @@ def add(self, name, *args, **kwargs): if not self._animation: self._animation = animation - def update(self): + def update(self, dt): """Update manager's state.""" if not self._animation: return try: - self._animation.update() + self._animation.update(dt) except StopIteration: return # TODO: think about method to change animation @@ -152,7 +152,7 @@ def _apply_value(self, value): setattr(self._obj, self._attr, value) - def _update_interpolated(self): + def _update_interpolated(self, dt): """Advance animation and interpolate value. NOTE: animation frame switching depends on interp mode @@ -161,7 +161,7 @@ def _update_interpolated(self): """ self._check_animation_state() - self._timer.update() + self._timer.update(dt) current_time = self._timer.get_elapsed() keyframe = self._keyframes[self._current] @@ -183,7 +183,7 @@ def _update_interpolated(self): value = interpolate(keyframe, next_keyframe, current_time) self._apply_value(value) - def _update_discrete(self): + def _update_discrete(self, dt): """Advance animation without interpolating value. NOTE: animation frame switching depends on interp mode @@ -194,7 +194,7 @@ def _update_discrete(self): """ self._check_animation_state() - self._timer.update() + self._timer.update(dt) keyframe = self._keyframes[self._current] diff --git a/xoinvader/background.py b/xoinvader/background.py index 39e94e0..e7cd717 100644 --- a/xoinvader/background.py +++ b/xoinvader/background.py @@ -1,5 +1,6 @@ """Level background.""" +from xoinvader import app from xoinvader.common import Settings from xoinvader.render import Renderable from xoinvader.utils import Point, Surface @@ -142,7 +143,7 @@ def __init__(self, filename=None, speed=0, loop=False, loop_all=False): self._current_chunk = None self._current_chunk_num = 0 # position of chunk in chunk list self._chunk_line = 0 # position in current chunk - self._ticks_since_last_update = 0 + self._elapsed_ms = 0 if filename: self.load_file(filename) @@ -230,14 +231,8 @@ def clear(self): self._background = [" " * self._w] * self._h - def load_file(self, filename): - """Load background from file. - - Calls `load_chunks` function int trim mode, and stores return value in - the `chunks` field. - - :param str filename: - """ + def load_file(self, filename: str): + """Load background from file.""" self._chunks = load_chunks(filename, self._w) @@ -262,7 +257,7 @@ def start(self, filled=False): :param bool filled: if true, perform initial background fill """ - self._ticks_since_last_update = 0 + self._elapsed_ms = 0 self._current_chunk_num = 0 self._current_chunk = self._chunks[0] self._chunk_line = 0 @@ -349,22 +344,22 @@ def update_surface(self): self._background_surface = Surface(self._background) - def update(self): + def update(self, dt): """Update background. Checks if time to update has come, and calls `_advance_chunk` method with appropriate parameter. Calls `update_surface` at the end. - """ if self._speed == 0: return - self._ticks_since_last_update += abs(self._speed) - if self._ticks_since_last_update < 60: # TODO FIXME: hardcode wololo + # FIXME: now this doesn't work properly + self._elapsed_ms += dt * abs(self._speed) + if self._elapsed_ms / 1000 > (1.0 / app.current().fps * 1000 * abs(self._speed)): return - self._ticks_since_last_update = 0 + self._elapsed_ms = 0 advance = 1 if self._speed > 0 else -1 new_line = self._advance_chunk(advance) diff --git a/xoinvader/charge.py b/xoinvader/charge.py index 927d0fd..34701c6 100644 --- a/xoinvader/charge.py +++ b/xoinvader/charge.py @@ -74,10 +74,10 @@ def remove_obsolete(self, pos): app.current().state.remove(self) self._destroy = True - def update(self): + def update(self, dt): """Update coords.""" - self._pos += Point(self._dx, self._dy) + self._pos += Point(self._dx, self._dy) * dt / 1000 def destroy(self): """Self-destroying routine.""" diff --git a/xoinvader/config/xoinvader.toml b/xoinvader/config/xoinvader.toml index d7d5eae..e787654 100644 --- a/xoinvader/config/xoinvader.toml +++ b/xoinvader/config/xoinvader.toml @@ -2,7 +2,7 @@ level1bg = "/res/level1.bg" scoreboard = "/data/scoreboard" [ship.PlayerShip] -dx = 1 +dx = 40 hull = 100 shield = 100 max_hull = 100 @@ -41,25 +41,25 @@ cooldown = 2 [weapon.EBlaster] ammo = "infinite" max_ammo = "infinite" -cooldown = 0.7 +cooldown = 1.2 [charge.BasicPlasmaCannon] damage = 3 radius = 0 -dy = -1 +dy = -20 [charge.EBasicPlasmaCannon] damage = 60 radius = 1 -dy = 1 +dy = 20 [charge.BasicLaserCharge] damage = 10 radius = 0 -dy = -2 +dy = -25 [charge.BasicUnguidedMissile] damage = 50 radius = 3 -dy = -1 +dy = -15 diff --git a/xoinvader/gui.py b/xoinvader/gui.py index 3d2b13a..c41d2ec 100644 --- a/xoinvader/gui.py +++ b/xoinvader/gui.py @@ -43,7 +43,7 @@ def _make_image(self) -> Surface: [[_style for _ in range(len(self._text))]], ) - def update(self, text: Optional[str] = None, style: Optional[int] = None): + def update(self, dt: int, text: Optional[str] = None, style: Optional[int] = None): """Obtain (or not) new data and refresh image. :param text: new text @@ -82,7 +82,7 @@ def __init__( super(TextCallbackWidget, self).__init__(pos, callback(), style) - def update(self): + def update(self, dt): self._text = self._callback() self._image = self._make_image() @@ -225,7 +225,7 @@ def current(self): def get_render_data(self): return [], [] - def update(self): + def update(self, dt): pass def get_renderable_objects(self): @@ -268,9 +268,9 @@ def _finalize_cb(self): if self._callback: self._callback(self) - def update(self, text=None, style=None): + def update(self, dt: int, text: Optional[str] = None, style: Optional[int] = None): self._update_text(text, style) - self._timer.update() + self._timer.update(dt) class WeaponWidget(Renderable): @@ -302,7 +302,7 @@ def _make_image(self): [[Style().gui["yellow"] for _ in range(len(self._data))]], ) - def update(self): + def update(self, dt): """Obtain new data and refresh image.""" self._data = self._get_data() @@ -418,7 +418,7 @@ def _update_image(self): [[ch[0] for ch in image]], [[st[1] for st in image]] ) - def update(self, val=None): + def update(self, dt: int, val=None): """Update bar if there's need for it.""" if self._callback: diff --git a/xoinvader/ingame.py b/xoinvader/ingame.py index 276c5d1..6b38e23 100644 --- a/xoinvader/ingame.py +++ b/xoinvader/ingame.py @@ -226,10 +226,10 @@ def _create_gui(self): def events(self): self._events.handle() - def update(self): + def update(self, dt): self.collision.update() self.level.update() if not self.level.running: self.level.start() - - super().update() + LOG.info(self._objects) + super().update(dt) diff --git a/xoinvader/ship.py b/xoinvader/ship.py index b0f732d..041382d 100644 --- a/xoinvader/ship.py +++ b/xoinvader/ship.py @@ -132,22 +132,22 @@ def add_weapon(self, weapon): self._weapons.append(weapon) self._weapon = weapon - def update_position(self): + def update_position(self, dt): """Update ship position. Allows to go behind field borders. """ - self._pos.x += self._direction * self._dx + self._pos.x += self._direction * self._dx * dt self._direction = 0 - def update(self): + def update(self, dt): """Update ship object's state.""" - self.update_position() + self.update_position(dt) for weapon in self._weapons: - weapon.update() + weapon.update(dt) if self._fire: try: @@ -234,7 +234,7 @@ def __init__(self, pos): def add_animation(self, *args, **kwargs): self._animgr.add(*args, **kwargs) - def update(self): + def update(self, dt): if self._hull <= 0: # TODO: [scoring] # * Parametrize scores, move them to ships.conf @@ -243,9 +243,9 @@ def update(self): self.destroy() return - self._animgr.update() + self._animgr.update(dt) - super(GenericXEnemy, self).update() + super(GenericXEnemy, self).update(dt) @collision.register("GenericXEnemy", "BasicPlasmaCannon") @collision.register("GenericXEnemy", "BasicLaserCharge") @@ -283,7 +283,7 @@ def __init__(self, pos): self._weapon = self._weapons.current() self._wbay = Point(x=self._image.width // 2, y=-1) - def update_position(self): + def update_position(self, dt): """Update player ship position. Prohibits moving out behind the border. @@ -299,17 +299,19 @@ def update_position(self): self._pos.x = 1 else: - self._pos.x += self._direction * self._dx + # NOTE: Converting float to int reduces ship teleportation because + # we have no pixels. + self._pos.x += int(self._direction * self._dx * dt / 1000) self._direction = 0 - def update(self): + def update(self, dt): if self._hull <= 0: app.current().trigger_state( "GameOverState", score=app.current().state.score ) - super(PlayerShip, self).update() + super(PlayerShip, self).update(dt) def get_weapon_info(self): """Return information about current weapon.""" diff --git a/xoinvader/utils.py b/xoinvader/utils.py index 87a26d7..16e8449 100644 --- a/xoinvader/utils.py +++ b/xoinvader/utils.py @@ -3,7 +3,6 @@ import copy import datetime import logging -import time # FIXME: temporary backward compatibility from eaf.core import Vec3 as Point @@ -219,14 +218,14 @@ class Timer(object): def __init__(self, end_time, func): self._end = float(end_time) self._func = func - self._start = time.perf_counter() + self._start = 0.0 self._current = self._start self._running = False - def _tick(self): + def _tick(self, dt): """Refresh counter.""" if self.running: - self._current = time.perf_counter() + self._current += dt / 1000 def _time_is_up(self): """return is it time to fire fuction or not. @@ -239,8 +238,8 @@ def _time_is_up(self): def start(self): """Start timer.""" self._running = True - self._start = time.perf_counter() - self._current = time.perf_counter() + self._start = 0.0 + self._current = 0.0 def stop(self): """Stop timer.""" @@ -248,23 +247,23 @@ def stop(self): def restart(self): """Restart timer.""" - self._start = time.perf_counter() - self._current = self._start + self._start = 0.0 + self._current = 0.0 self.start() def reset(self): """Reset timer.""" self._running = False - self._start = 0 - self._current = 0 + self._start = 0.0 + self._current = 0.0 - def update(self): + def update(self, dt): """Public method for using in loops.""" if not self.running: return # Timer's accuracy depends on owner's loop - self._tick() + self._tick(dt) if self._time_is_up() and self.running: self._func() self.stop() diff --git a/xoinvader/weapon.py b/xoinvader/weapon.py index 220d2d8..d1decf7 100644 --- a/xoinvader/weapon.py +++ b/xoinvader/weapon.py @@ -99,13 +99,13 @@ def load_percentage(self): return 100.0 return self._timer.get_elapsed() * 100.0 / self._cooldown - def update(self): + def update(self, dt): """Update weapon timer.""" if self.ready: return - self._timer.update() + self._timer.update(dt) class Blaster(Weapon):