From 683e22c419757744ce853c35d732f607ddb9af16 Mon Sep 17 00:00:00 2001
From: Kelly Rauchenberger <fefferburbia@gmail.com>
Date: Wed, 3 Feb 2021 12:33:03 -0500
Subject: Added input system

---
 CMakeLists.txt           |   1 +
 src/character_system.cpp | 271 ++++++++++++++++++++++++-----------------------
 src/character_system.h   |  14 ++-
 src/game.h               |  17 +--
 src/input_system.cpp     |  89 ++++++++++++++++
 src/input_system.h       |  22 ++++
 src/main.cpp             |  43 +++-----
 src/sprite.h             |   9 +-
 src/system.h             |   5 +-
 9 files changed, 295 insertions(+), 176 deletions(-)
 create mode 100644 src/input_system.cpp
 create mode 100644 src/input_system.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 65abfe1..a87b017 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -41,6 +41,7 @@ add_executable(tanetane
   src/camera_system.cpp
   src/animation_system.cpp
   src/character_system.cpp
+  src/input_system.cpp
 )
 
 set_property(TARGET tanetane PROPERTY CXX_STANDARD 17)
diff --git a/src/character_system.cpp b/src/character_system.cpp
index 64e2f3b..ac0f01a 100644
--- a/src/character_system.cpp
+++ b/src/character_system.cpp
@@ -6,6 +6,11 @@
 #include "transform_system.h"
 #include "animation_system.h"
 
+void CharacterSystem::initSprite(int spriteId) {
+  Sprite& sprite = game_.getSprite(spriteId);
+  sprite.orientable = true;
+}
+
 void CharacterSystem::addSpriteToParty(int leaderId, int followerId) {
   Sprite& leader = game_.getSprite(leaderId);
   Sprite& follower = game_.getSprite(followerId);
@@ -18,146 +23,124 @@ void CharacterSystem::addSpriteToParty(int leaderId, int followerId) {
   game_.getSystem<AnimationSystem>().setSpriteAnimation(followerId, "still");
 }
 
-void CharacterSystem::moveSprite(int spriteId, Mixer& mixer, const Input& keystate) {
+void CharacterSystem::moveInDirection(int spriteId, Direction dir) {
   Sprite& sprite = game_.getSprite(spriteId);
-  Direction dir = sprite.dir;
-
-  if (!keystate.up && !keystate.down && !keystate.left && !keystate.right) {
-    if (sprite.characterState != CharacterState::Running) {
-      if (sprite.characterState == CharacterState::Normal) {
-        game_.getSystem<AnimationSystem>().setSpriteAnimation(spriteId, "still");
-        for (int followerId : sprite.followers) {
-          game_.getSystem<AnimationSystem>().setSpriteAnimation(followerId, "still");
-        }
-      }
-
-      return;
-    }
-  } else {
-    if (keystate.up)
-    {
-      dir = Direction::up;
-    } else if (keystate.down)
-    {
-      dir = Direction::down;
-    }
 
-    if (keystate.left)
-    {
-      if (dir == Direction::up) {
-        dir = Direction::up_left;
-      } else if (dir == Direction::down) {
-        dir = Direction::down_left;
-      } else {
-        dir = Direction::left;
-      }
-    } else if (keystate.right)
-    {
-      if (dir == Direction::up) {
-        dir = Direction::up_right;
-      } else if (dir == Direction::down) {
-        dir = Direction::down_right;
-      } else {
-        dir = Direction::right;
-      }
-    }
-  }
-
-  vec2i pLoc = sprite.loc;
   game_.getSystem<AnimationSystem>().setSpriteDirection(spriteId, dir);
 
-  if (sprite.characterState == CharacterState::Crouching) {
+  if (sprite.characterState == CharacterState::Still) {
+    setPartyState(spriteId, CharacterState::Walking);
+  } else if (sprite.characterState == CharacterState::Crouching) {
     for (int followerId : sprite.followers) {
       game_.getSystem<AnimationSystem>().setSpriteDirection(followerId, dir);
     }
-
-    return;
   }
+}
 
-  int speed = MOVEMENT_SPEED;
-  if (sprite.characterState == CharacterState::Running) {
-    speed *= 2;
-  } else {
-    game_.getSystem<AnimationSystem>().setSpriteAnimation(spriteId, "walk");
-    for (int followerId : sprite.followers) {
-      game_.getSystem<AnimationSystem>().setSpriteAnimation(followerId, "walk");
-    }
-  }
+void CharacterSystem::stopDirecting(int spriteId) {
+  Sprite& sprite = game_.getSprite(spriteId);
 
-  pLoc += (unitVecInDirection(dir) * speed);
-
-  // Check collision.
-  const Map& map = game_.getMap();
-  bool blocked = false;
-
-  const vec2i UL_COL_BOX = { 8, 8 };
-  const vec2i DR_COL_BOX = { 4, 0 };
-  vec2i oldColPosUL = (sprite.loc - UL_COL_BOX) / map.getTileSize();
-  vec2i newColPosUL = (pLoc - UL_COL_BOX) / map.getTileSize();
-  vec2i oldColPosDR = (sprite.loc + DR_COL_BOX) / map.getTileSize();
-  vec2i newColPosDR = (pLoc + DR_COL_BOX) / map.getTileSize();
-
-  if (dirHasDir(dir, Direction::right) &&
-      newColPosDR.x() > oldColPosDR.x()) {
-    for (int y = newColPosUL.y(); y <= newColPosDR.y(); y++) {
-      if (map.isBlocked(newColPosDR.x(), y)) {
-        blocked = true;
-        pLoc.x() = sprite.loc.x();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1;
-        break;
-      }
-    }
+  if (sprite.characterState == CharacterState::Walking) {
+    setPartyState(spriteId, CharacterState::Still);
   }
+}
 
-  if (dirHasDir(dir, Direction::left) &&
-      newColPosUL.x() < oldColPosUL.x()) {
-    for (int y = newColPosUL.y(); y <= newColPosDR.y(); y++) {
-      if (map.isBlocked(newColPosUL.x(), y)) {
-        blocked = true;
-        pLoc.x() = sprite.loc.x();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1;
-        break;
-      }
-    }
-  }
+void CharacterSystem::tick(double dt) {
+  inputTimer_.accumulate(dt);
+  while (inputTimer_.step()) {
+    for (int spriteId : game_.getSprites()) {
+      Sprite& sprite = game_.getSprite(spriteId);
 
-  if (dirHasDir(dir, Direction::down) &&
-      newColPosDR.y() > oldColPosDR.y()) {
-    for (int x = newColPosUL.x(); x <= newColPosDR.x(); x++) {
-      if (map.isBlocked(x, newColPosDR.y())) {
-        blocked = true;
-        pLoc.y() = sprite.loc.y();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1;
-        break;
-      }
-    }
-  }
+      if (sprite.orientable) {
+        vec2i pLoc = sprite.loc;
 
-  if (dirHasDir(dir, Direction::up) &&
-      newColPosUL.y() < oldColPosUL.y()) {
-    for (int x = newColPosUL.x(); x <= newColPosDR.x(); x++) {
-      if (map.isBlocked(x, newColPosUL.y())) {
-        blocked = true;
-        pLoc.y() = sprite.loc.y();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1;
-        break;
-      }
-    }
-  }
+        if (sprite.characterState == CharacterState::Still ||
+            sprite.characterState == CharacterState::Crouching) {
+          continue;
+        }
 
-  if (blocked && sprite.characterState == CharacterState::Running) {
-    stopRunning(spriteId);
-    mixer.playSound("../res/bump.wav");
-  }
+        int speed = MOVEMENT_SPEED;
+        if (sprite.characterState == CharacterState::Running) {
+          speed *= 2;
+        }
 
-  // Move everything
-  if (pLoc != sprite.loc) {
-    game_.getSystem<TransformSystem>().moveSprite(spriteId, pLoc);
+        pLoc += (unitVecInDirection(sprite.dir) * speed);
+
+        // Check collision.
+        const Map& map = game_.getMap();
+        bool blocked = false;
+
+        const vec2i UL_COL_BOX = { 8, 8 };
+        const vec2i DR_COL_BOX = { 4, 0 };
+        vec2i oldColPosUL = (sprite.loc - UL_COL_BOX) / map.getTileSize();
+        vec2i newColPosUL = (pLoc - UL_COL_BOX) / map.getTileSize();
+        vec2i oldColPosDR = (sprite.loc + DR_COL_BOX) / map.getTileSize();
+        vec2i newColPosDR = (pLoc + DR_COL_BOX) / map.getTileSize();
+
+        if (dirHasDir(sprite.dir, Direction::right) &&
+            newColPosDR.x() > oldColPosDR.x()) {
+          for (int y = newColPosUL.y(); y <= newColPosDR.y(); y++) {
+            if (map.isBlocked(newColPosDR.x(), y)) {
+              blocked = true;
+              pLoc.x() = sprite.loc.x();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1;
+              break;
+            }
+          }
+        }
 
-    for (int followerId : sprite.followers) {
-      Sprite& pNext = game_.getSprite(followerId);
-      const Movement& posdir = pNext.trail.front();
-      game_.getSystem<TransformSystem>().moveSprite(followerId, posdir.pos);
-      game_.getSystem<AnimationSystem>().setSpriteDirection(followerId, posdir.dir);
+        if (dirHasDir(sprite.dir, Direction::left) &&
+            newColPosUL.x() < oldColPosUL.x()) {
+          for (int y = newColPosUL.y(); y <= newColPosDR.y(); y++) {
+            if (map.isBlocked(newColPosUL.x(), y)) {
+              blocked = true;
+              pLoc.x() = sprite.loc.x();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1;
+              break;
+            }
+          }
+        }
 
-      pNext.trail.pop_front();
-      pNext.trail.push_back({.pos = pLoc, .dir = dir});
+        if (dirHasDir(sprite.dir, Direction::down) &&
+            newColPosDR.y() > oldColPosDR.y()) {
+          for (int x = newColPosUL.x(); x <= newColPosDR.x(); x++) {
+            if (map.isBlocked(x, newColPosDR.y())) {
+              blocked = true;
+              pLoc.y() = sprite.loc.y();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1;
+              break;
+            }
+          }
+        }
+
+        if (dirHasDir(sprite.dir, Direction::up) &&
+            newColPosUL.y() < oldColPosUL.y()) {
+          for (int x = newColPosUL.x(); x <= newColPosDR.x(); x++) {
+            if (map.isBlocked(x, newColPosUL.y())) {
+              blocked = true;
+              pLoc.y() = sprite.loc.y();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1;
+              break;
+            }
+          }
+        }
+
+        if (blocked && sprite.characterState == CharacterState::Running) {
+          stopRunning(spriteId);
+          game_.getMixer().playSound("../res/bump.wav");
+        }
+
+        // Move everything
+        if (pLoc != sprite.loc) {
+          game_.getSystem<TransformSystem>().moveSprite(spriteId, pLoc);
+
+          for (int followerId : sprite.followers) {
+            Sprite& pNext = game_.getSprite(followerId);
+            const Movement& posdir = pNext.trail.front();
+            game_.getSystem<TransformSystem>().moveSprite(followerId, posdir.pos);
+            game_.getSystem<AnimationSystem>().setSpriteDirection(followerId, posdir.dir);
+
+            pNext.trail.pop_front();
+            pNext.trail.push_back({.pos = pLoc, .dir = sprite.dir});
+          }
+        }
+      }
     }
   }
 }
@@ -168,12 +151,7 @@ void CharacterSystem::beginCrouch(int spriteId) {
   if (sprite.characterState == CharacterState::Running) {
     stopRunning(spriteId);
   } else {
-    sprite.characterState = CharacterState::Crouching;
-
-    game_.getSystem<AnimationSystem>().setSpriteAnimation(spriteId, "crouch");
-    for (int followerId : sprite.followers) {
-      game_.getSystem<AnimationSystem>().setSpriteAnimation(followerId, "crouch");
-    }
+    setPartyState(spriteId, CharacterState::Crouching);
   }
 }
 
@@ -181,12 +159,9 @@ void CharacterSystem::endCrouch(int spriteId) {
   Sprite& sprite = game_.getSprite(spriteId);
 
   if (sprite.characterState == CharacterState::Crouching) {
-    sprite.characterState = CharacterState::Running;
+    setPartyState(spriteId, CharacterState::Running);
 
-    game_.getSystem<AnimationSystem>().setSpriteAnimation(spriteId, "run");
     for (int followerId : sprite.followers) {
-      game_.getSystem<AnimationSystem>().setSpriteAnimation(followerId, "run");
-
       // Halve the movement buffer for the followers.
       Sprite& follower = game_.getSprite(followerId);
       std::deque<Movement> newMove;
@@ -204,7 +179,7 @@ void CharacterSystem::endCrouch(int spriteId) {
 
 void CharacterSystem::stopRunning(int spriteId) {
   Sprite& sprite = game_.getSprite(spriteId);
-  sprite.characterState = CharacterState::Normal;
+  setPartyState(spriteId, CharacterState::Still);
 
   // Double the movement buffer for the followers.
   for (int followerId : sprite.followers) {
@@ -227,3 +202,33 @@ void CharacterSystem::stopRunning(int spriteId) {
     follower.trail = std::move(newMove);
   }
 }
+
+void CharacterSystem::setPartyState(int spriteId, CharacterState state) {
+  std::string animName;
+  switch (state) {
+    case CharacterState::Still: {
+      animName = "still";
+      break;
+    }
+    case CharacterState::Walking: {
+      animName = "walk";
+      break;
+    }
+    case CharacterState::Crouching: {
+      animName = "crouch";
+      break;
+    }
+    case CharacterState::Running: {
+      animName = "run";
+      break;
+    }
+  }
+
+  Sprite& sprite = game_.getSprite(spriteId);
+  sprite.characterState = state;
+
+  game_.getSystem<AnimationSystem>().setSpriteAnimation(spriteId, animName);
+  for (int followerId : sprite.followers) {
+    game_.getSystem<AnimationSystem>().setSpriteAnimation(followerId, animName);
+  }
+}
diff --git a/src/character_system.h b/src/character_system.h
index ff5db02..2eea233 100644
--- a/src/character_system.h
+++ b/src/character_system.h
@@ -2,6 +2,9 @@
 #define PARTY_H_826F91BA
 
 #include "system.h"
+#include "direction.h"
+#include "timer.h"
+#include "sprite.h"
 
 class Mixer;
 class Game;
@@ -14,9 +17,15 @@ public:
 
   CharacterSystem(Game& game) : game_(game) {}
 
+  void initSprite(int spriteId);
+
+  void tick(double dt) override;
+
   void addSpriteToParty(int leaderId, int followerId);
 
-  void moveSprite(int spriteId, Mixer& mixer, const Input& keystate);
+  void moveInDirection(int spriteId, Direction dir);
+
+  void stopDirecting(int spriteId);
 
   void beginCrouch(int spriteId);
 
@@ -26,7 +35,10 @@ private:
 
   void stopRunning(int spriteId);
 
+  void setPartyState(int spriteId, CharacterState state);
+
   Game& game_;
+  Timer inputTimer_ {33};
 };
 
 #endif /* end of include guard: PARTY_H_826F91BA */
diff --git a/src/game.h b/src/game.h
index f8e4b0d..36398bc 100644
--- a/src/game.h
+++ b/src/game.h
@@ -10,17 +10,17 @@
 #include "map.h"
 #include "consts.h"
 #include "system.h"
-
-struct Input {
-  bool left = false;
-  bool right = false;
-  bool up = false;
-  bool down = false;
-};
+#include "mixer.h"
 
 class Game {
 public:
 
+  Mixer& getMixer() { return mixer_; }
+
+  bool shouldQuit() const { return shouldQuit_; }
+
+  void quit() { shouldQuit_ = true; }
+
   template <typename T>
   void emplaceSystem() {
     systems_.push_back(std::make_unique<T>(*this));
@@ -70,6 +70,9 @@ public:
 
 private:
 
+  Mixer mixer_;
+  bool shouldQuit_ = false;
+
   std::list<std::unique_ptr<System>> systems_;
   std::map<SystemKey, System*> systemByKey_;
 
diff --git a/src/input_system.cpp b/src/input_system.cpp
new file mode 100644
index 0000000..54a291c
--- /dev/null
+++ b/src/input_system.cpp
@@ -0,0 +1,89 @@
+#include "input_system.h"
+#include "game.h"
+#include "character_system.h"
+
+struct Input {
+  bool left = false;
+  bool right = false;
+  bool up = false;
+  bool down = false;
+};
+
+void InputSystem::tick(double dt) {
+  SDL_Event e;
+  while (SDL_PollEvent(&e)) {
+    if (e.type == SDL_QUIT || (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) {
+      game_.quit();
+
+      return;
+    } else if (e.type == SDL_KEYDOWN && (e.key.keysym.sym == SDLK_LSHIFT || e.key.keysym.sym == SDLK_RSHIFT)) {
+      for (int spriteId : game_.getSprites()) {
+        Sprite& sprite = game_.getSprite(spriteId);
+        if (sprite.controllable) {
+          game_.getSystem<CharacterSystem>().beginCrouch(spriteId);
+        }
+      }
+    } else if (e.type == SDL_KEYUP && (e.key.keysym.sym == SDLK_LSHIFT || e.key.keysym.sym == SDLK_RSHIFT)) {
+      for (int spriteId : game_.getSprites()) {
+        Sprite& sprite = game_.getSprite(spriteId);
+        if (sprite.controllable) {
+          game_.getSystem<CharacterSystem>().endCrouch(spriteId);
+        }
+      }
+    }
+  }
+
+  Input keystate;
+  const Uint8* state = SDL_GetKeyboardState(NULL);
+  keystate.left = state[SDL_SCANCODE_LEFT];
+  keystate.right = state[SDL_SCANCODE_RIGHT];
+  keystate.up = state[SDL_SCANCODE_UP];
+  keystate.down = state[SDL_SCANCODE_DOWN];
+
+  for (int spriteId : game_.getSprites()) {
+    Sprite& sprite = game_.getSprite(spriteId);
+
+    if (sprite.controllable) {
+      bool directed = false;
+      Direction dir = Direction::left;
+
+      if (keystate.up)
+      {
+        directed = true;
+        dir = Direction::up;
+      } else if (keystate.down)
+      {
+        directed = true;
+        dir = Direction::down;
+      }
+
+      if (keystate.left)
+      {
+        directed = true;
+        if (dir == Direction::up) {
+          dir = Direction::up_left;
+        } else if (dir == Direction::down) {
+          dir = Direction::down_left;
+        } else {
+          dir = Direction::left;
+        }
+      } else if (keystate.right)
+      {
+        directed = true;
+        if (dir == Direction::up) {
+          dir = Direction::up_right;
+        } else if (dir == Direction::down) {
+          dir = Direction::down_right;
+        } else {
+          dir = Direction::right;
+        }
+      }
+
+      if (directed) {
+        game_.getSystem<CharacterSystem>().moveInDirection(spriteId, dir);
+      } else {
+        game_.getSystem<CharacterSystem>().stopDirecting(spriteId);
+      }
+    }
+  }
+}
diff --git a/src/input_system.h b/src/input_system.h
new file mode 100644
index 0000000..4e5bb22
--- /dev/null
+++ b/src/input_system.h
@@ -0,0 +1,22 @@
+#ifndef INPUT_SYSTEM_H_47764575
+#define INPUT_SYSTEM_H_47764575
+
+#include "system.h"
+
+class Game;
+
+class InputSystem : public System {
+public:
+
+  static constexpr SystemKey Key = SystemKey::Input;
+
+  InputSystem(Game& game) : game_(game) {}
+
+  void tick(double dt) override;
+
+private:
+
+  Game& game_;
+};
+
+#endif /* end of include guard: INPUT_SYSTEM_H_47764575 */
diff --git a/src/main.cpp b/src/main.cpp
index 3ac4d09..771aa1c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -9,15 +9,15 @@
 #include "camera_system.h"
 #include "animation_system.h"
 #include "character_system.h"
+#include "input_system.h"
 
-void loop(Renderer& renderer, Mixer& mixer) {
+void loop(Renderer& renderer) {
   Game game;
   game.emplaceSystem<TransformSystem>();
-  game.emplaceSystem<CameraSystem>();
-  game.emplaceSystem<AnimationSystem>();
+  game.emplaceSystem<InputSystem>();
   game.emplaceSystem<CharacterSystem>();
-
-  Input keystate;
+  game.emplaceSystem<AnimationSystem>();
+  game.emplaceSystem<CameraSystem>();
 
   auto map = std::make_unique<Map>("../res/map1.tmx", renderer);
   game.setMap(std::move(map));
@@ -25,6 +25,8 @@ void loop(Renderer& renderer, Mixer& mixer) {
   int lucasSprite = game.emplaceSprite();
   game.getSystem<TransformSystem>().initSprite(lucasSprite, {32, 32});
   game.getSystem<AnimationSystem>().initSprite(lucasSprite, "../res/lucas_anim.txt", renderer);
+  game.getSprite(lucasSprite).controllable = true;
+  game.getSystem<CharacterSystem>().initSprite(lucasSprite);
 
   int kumaSprite = game.emplaceSprite();
   game.getSystem<TransformSystem>().initSprite(kumaSprite, {32, 32});
@@ -46,8 +48,6 @@ void loop(Renderer& renderer, Mixer& mixer) {
 
   renderer.render(game);
 
-  Timer inputTimer(33);
-
   size_t lastTime = SDL_GetTicks();
 
   for (;;) {
@@ -55,33 +55,15 @@ void loop(Renderer& renderer, Mixer& mixer) {
     size_t frameTime = currentTime - lastTime;
     lastTime = currentTime;
 
-    SDL_Event e;
-    while (SDL_PollEvent(&e)) {
-      if (e.type == SDL_QUIT || (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) {
-        return;
-      } else if (e.type == SDL_KEYDOWN && (e.key.keysym.sym == SDLK_LSHIFT || e.key.keysym.sym == SDLK_RSHIFT)) {
-        game.getSystem<CharacterSystem>().beginCrouch(lucasSprite);
-      } else if (e.type == SDL_KEYUP && (e.key.keysym.sym == SDLK_LSHIFT || e.key.keysym.sym == SDLK_RSHIFT)) {
-        game.getSystem<CharacterSystem>().endCrouch(lucasSprite);
-      }
-    }
-
-    const Uint8* state = SDL_GetKeyboardState(NULL);
-    keystate.left = state[SDL_SCANCODE_LEFT];
-    keystate.right = state[SDL_SCANCODE_RIGHT];
-    keystate.up = state[SDL_SCANCODE_UP];
-    keystate.down = state[SDL_SCANCODE_DOWN];
-
-    inputTimer.accumulate(frameTime);
-    while (inputTimer.step()) {
-      game.getSystem<CharacterSystem>().moveSprite(lucasSprite, mixer, keystate);
-    }
-
     for (System& system : game.systems()) {
       system.tick(frameTime);
     }
 
     renderer.render(game);
+
+    if (game.shouldQuit()) {
+      return;
+    }
   }
 }
 
@@ -89,9 +71,8 @@ int main(int, char**) {
   try
   {
     Renderer renderer;
-    Mixer mixer;
 
-    loop(renderer, mixer);
+    loop(renderer);
   } catch (const sdl_error& ex)
   {
     std::cout << "SDL error (" << ex.what() << ")" << std::endl;
diff --git a/src/sprite.h b/src/sprite.h
index 65a7a66..2dc0ee1 100644
--- a/src/sprite.h
+++ b/src/sprite.h
@@ -16,7 +16,8 @@ struct SpriteFrame {
 };
 
 enum class CharacterState {
-  Normal,
+  Still,
+  Walking,
   Crouching,
   Running
 };
@@ -44,9 +45,13 @@ public:
   std::map<std::string, std::map<Direction, int>> nameDirToAnim;
 
   // Character
+  bool orientable = false;
   std::vector<int> followers;
   std::deque<Movement> trail;
-  CharacterState characterState = CharacterState::Normal;
+  CharacterState characterState = CharacterState::Still;
+
+  // Input
+  bool controllable = false;
 };
 
 #endif /* end of include guard: SPRITE_H_70503825 */
diff --git a/src/system.h b/src/system.h
index 6f09d61..fc89503 100644
--- a/src/system.h
+++ b/src/system.h
@@ -3,9 +3,10 @@
 
 enum class SystemKey {
   Transform,
-  Camera,
+  Input,
+  Character,
   Animation,
-  Character
+  Camera
 };
 
 class System {
-- 
cgit 1.4.1