diff --git a/.gitignore b/.gitignore
index caf783c..cc0eb7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
-.cache/clangd/index
+.vscode
+.cache
+compile_commands/
 bin/
 build/
 intermediate/
diff --git a/Build.lua b/Build.lua
index a2777cf..339cff8 100644
--- a/Build.lua
+++ b/Build.lua
@@ -5,7 +5,7 @@ workspace "Fencer-Template"
   startproject "Game"
 
   filter "system:windows"
-    defines { "SDL_MAIN_HANDLED" }
+    defines { "SDL_MAIN_HANDLED", "_CRT_SECURE_NO_WARNINGS" }
     buildoptions { "/EHsc", "/Zc:preprocessor" }
 
   OutputDir = "%{cfg.system}-%{cfg.architecture}/%{cfg.buildcfg}"
diff --git a/assets/sprites/player/player_walk.png b/assets/sprites/player/player_walk.png
deleted file mode 100644
index b90fd8e..0000000
Binary files a/assets/sprites/player/player_walk.png and /dev/null differ
diff --git a/core/src/animation_sprite.c b/core/src/animation_sprite.c
new file mode 100644
index 0000000..692721b
--- /dev/null
+++ b/core/src/animation_sprite.c
@@ -0,0 +1,58 @@
+#include "animation_sprite.h"
+#include "debug.h"
+#include "program.h"
+
+struct AnimationSprite {
+	Spritesheet* sheet;
+	Sprite* sprite;
+
+	AnimationSpriteLoopMode loop_mode;
+
+	float frame_interval;
+	float start_time;
+};
+
+AnimationSprite* animation_sprite_new(Spritesheet* sheet, float framerate) {
+	AnimationSprite* self = malloc(sizeof(AnimationSprite));
+	ASSERT_RETURN(self != NULL, NULL, "Failed to allocate memory for AnimationSprite");
+	*self = (AnimationSprite){
+		.sheet = sheet,
+		.frame_interval = 1.0f / framerate,
+		.loop_mode = LoopMode_Loop,
+		.start_time = game_time(),
+		.sprite = sprite_from_spritesheet(sheet, 0)
+	};
+
+	return self;
+}
+
+void animation_sprite_destroy(AnimationSprite* self) {
+	sprite_destroy(self->sprite);
+	spritesheet_destroy(self->sheet);
+	free(self);
+}
+
+void animation_sprite_play_from(AnimationSprite* self, float normalized_time) {
+	self->start_time = game_time() - normalized_time * animation_sprite_get_length(self);
+}
+
+void animation_sprite_draw(AnimationSprite* self, Transform* transform) {
+	const float time = game_time() - self->start_time;
+	const size_t frame = time / self->frame_interval;
+
+	sprite_set_spritesheet(self->sprite, self->sheet);
+	sprite_set_tile(self->sprite, frame);
+	sprite_draw(self->sprite, *transform);
+}
+
+float animation_sprite_get_length(AnimationSprite* self) {
+	return (float)spritesheet_get_tile_count(self->sheet) * self->frame_interval;
+}
+
+void animation_sprite_set_framerate(AnimationSprite* self, float framerate) {
+	self->frame_interval = 1.0f / framerate;
+}
+
+float animation_sprite_get_framerate(const AnimationSprite* self) {
+	return 1.0f / self->frame_interval;
+}
diff --git a/core/src/animation_sprite.h b/core/src/animation_sprite.h
new file mode 100644
index 0000000..135e440
--- /dev/null
+++ b/core/src/animation_sprite.h
@@ -0,0 +1,26 @@
+#ifndef _fencer_animation_sprite_h
+#define _fencer_animation_sprite_h
+
+#include "sprite.h"
+#include "spritesheet.h"
+
+typedef enum AnimationSpriteLoopMode {
+	LoopMode_Stop,
+	LoopMode_Hide,
+	LoopMode_Loop,
+	LoopMode_PingPong,
+} AnimationSpriteLoopMode;
+
+typedef struct AnimationSprite AnimationSprite;
+
+extern AnimationSprite* animation_sprite_new(Spritesheet* sheet, float framerate);
+extern void animation_sprite_destroy(AnimationSprite* self);
+
+extern void animation_sprite_play_from(AnimationSprite* self, float normalized_time);
+extern void animation_sprite_draw(AnimationSprite* self, Transform* transform);
+
+extern float animation_sprite_get_length(AnimationSprite* self);
+extern void animation_sprite_set_framerate(AnimationSprite* self, float framerate);
+extern float animation_sprite_get_framerate(const AnimationSprite* self);
+
+#endif // !_fencer_animation_sprite_h
\ No newline at end of file
diff --git a/core/src/assets.c b/core/src/assets.c
index 6473d2c..0f90a1d 100644
--- a/core/src/assets.c
+++ b/core/src/assets.c
@@ -10,16 +10,16 @@ static asset_id _next_id = 0;
 
 static
 size_t file_length(FILE* fp) {
-    size_t start = ftell(fp);
+    long start = ftell(fp);
     fseek(fp, 0, SEEK_END);
-    size_t r = ftell(fp);
+    long r = ftell(fp);
     fseek(fp, start, SEEK_SET);
-    return r;
+    return (size_t)r;
 }
 
 static
 void read_file(FILE* fp, char* out_buffer, size_t out_size) {
-    size_t start = ftell(fp);
+    long start = ftell(fp);
     fread(out_buffer, 1, out_size, fp);
     fseek(fp, start, SEEK_SET);
 }
@@ -88,15 +88,13 @@ asset_id get_asset_id(void* asset) {
 }
 
 void free_asset(asset_id id) {
-    Asset* found;
-    size_t found_index;
+    Asset* found = NULL;
+    size_t found_index = _assets.len;
     for(size_t i = 0; i < _assets.len; ++i) {
         found = list_at_as(Asset, &_assets, i);
         if(found->tc->get_id(found->data) == id) {
             found_index = i;
             break;
-        } else {
-            found = NULL;
         }
     }
     ASSERT_RETURN(found != NULL,, "Attempt to free nonexistent asset.");
diff --git a/core/src/assets.h b/core/src/assets.h
index 8f6a11b..579254a 100644
--- a/core/src/assets.h
+++ b/core/src/assets.h
@@ -31,8 +31,8 @@ size_t json_array_len(cJSON* array);
 static inline
 Vector json_array_to_vector(cJSON* array) {
     return (Vector) {
-        cJSON_GetArrayItem(array, 0)->valuedouble,
-        cJSON_GetArrayItem(array, 1)->valuedouble,
+        (float)cJSON_GetArrayItem(array, 0)->valuedouble,
+        (float)cJSON_GetArrayItem(array, 1)->valuedouble,
     };
 }
 static inline
diff --git a/core/src/camera.c b/core/src/camera.c
index 80b7946..c9c896b 100644
--- a/core/src/camera.c
+++ b/core/src/camera.c
@@ -21,7 +21,7 @@ SDL_FRect camera_world_to_pixel_rect(Camera* self, SDL_FRect* world_space) {
     t.scale = OneVector;
     t = transform_invert(t);
 
-    Vector tl = {world_space->x + (self->fov / 2.0), world_space->y + (_camera_height(self) / 2.0)};
+    Vector tl = {world_space->x + (self->fov / 2.0f), world_space->y + (_camera_height(self) / 2.0f)};
     Vector size = {world_space->w, world_space->h};
 
     tl = vmulff(transform_point(&t, tl), g_render_resolution.x / self->fov);
@@ -40,7 +40,7 @@ Vector camera_world_to_pixel_point(Camera* self, Vector point) {
     t.scale = OneVector;
     t = transform_invert(t);
 
-    point = (Vector){point.x + (self->fov / 2.0), point.y + (_camera_height(self) / 2.0)};
+    point = (Vector){point.x + (self->fov / 2.0f), point.y + (_camera_height(self) / 2.0f)};
 
     return vmulff(transform_point(&t, point), g_render_resolution.x / self->fov);
 }
diff --git a/core/src/drop.c b/core/src/drop.c
new file mode 100644
index 0000000..7bd372a
--- /dev/null
+++ b/core/src/drop.c
@@ -0,0 +1,6 @@
+#include "drop.h"
+#include "stdlib.h"
+
+void default_drop(void* data) {
+    free(data);
+}
diff --git a/core/src/drop.h b/core/src/drop.h
index e5aaef4..c5a9936 100644
--- a/core/src/drop.h
+++ b/core/src/drop.h
@@ -19,8 +19,13 @@ static inline Drop T##_as_Drop(T* x) {\
     return (Drop){.tc = &tc, .data = x};\
 }
 
-#define impl_default_Drop_for(T)\
-    static void default_drop_##T(T* v) { free(v); }\
-    impl_Drop_for(T, default_drop_##T)
+extern void default_drop(void*);
 
+#define impl_default_Drop_for(T)\
+static inline Drop T##_as_Drop(T* x) {\
+    static IDrop const tc = {\
+        .drop = default_drop,\
+    };\
+    return (Drop){.tc = &tc, .data = x};\
+}
 #endif // !_fencer_drop_h
diff --git a/core/src/game_world.c b/core/src/game_world.c
index 8dfa86f..b93a029 100644
--- a/core/src/game_world.c
+++ b/core/src/game_world.c
@@ -73,7 +73,7 @@ void game_world_update() {
     _internal_clear_removed();
 }
 
-void game_word_draw() {
+void game_world_draw() {
     list_foreach(BehaviourEntity*, entity, &_game_entities) {
         entity->tc->draw(entity->data);
     }
diff --git a/core/src/input_axis.c b/core/src/input_axis.c
index 36ee80f..162a897 100644
--- a/core/src/input_axis.c
+++ b/core/src/input_axis.c
@@ -4,6 +4,7 @@
 
 KeyBind* keybind_new(SDL_Scancode key) {
     KeyBind* self = malloc(sizeof(KeyBind));
+    ASSERT_RETURN(self != NULL, NULL, "Failed to allocate space for KeyBind instance");
     *self = (KeyBind) {
         .device = NULL,
         .scancode = key,
@@ -31,6 +32,7 @@ void keybind_set_device(KeyBind* self, InputDevice* device) {
 
 ControllerAxis* controlleraxis_new(int axis) {
     ControllerAxis* self = malloc(sizeof(ControllerAxis));
+    ASSERT_RETURN(self != NULL, NULL, "Failed to allocate space for ControllerAxis instance");
     *self = (ControllerAxis){
         .axis = axis,
         .device = NULL
@@ -48,7 +50,6 @@ int controlleraxis_is_changed_by(ControllerAxis* self, SDL_Event event) {
 
 InputEvent controlleraxis_evaluate(ControllerAxis* self, SDL_Event event) {
     float result = (float)event.caxis.value / 32767.0;
-    LOG_INFO("axis %f", result);
     return (InputEvent) {
         .type = InputEvent_Float,
         .as_float = result
@@ -61,6 +62,7 @@ void controlleraxis_set_device(ControllerAxis* self, InputDevice* device) {
 
 ControllerButton* controllerbutton_new(int button) {
     ControllerButton* self = malloc(sizeof(ControllerButton));
+    ASSERT_RETURN(self != NULL, NULL, "Failed to allocate space for ControllerButton instance");
     *self = (ControllerButton) {
         .button = button,
         .device = NULL
@@ -88,6 +90,7 @@ void controllerbutton_set_device(ControllerButton* self, InputDevice* device) {
 
 CompositeAxis1D* compositeaxis1d_new(InputAxis left, InputAxis right, InputEventType type) {
     CompositeAxis1D* self = malloc(sizeof(CompositeAxis1D));
+    ASSERT_RETURN(self != NULL, NULL, "Failed to allocate space for CompositeAxis1D instance");
     *self = (CompositeAxis1D) {
         .left = left,
         .right = right,
diff --git a/core/src/physics_entity.c b/core/src/physics_entity.c
index 5b7b45d..4d2f5bb 100644
--- a/core/src/physics_entity.c
+++ b/core/src/physics_entity.c
@@ -18,11 +18,11 @@ void physics_entity_debug_draw(PhysicsEntity self) {
     lhs = camera_world_to_pixel_point(&g_camera, lhs);
     rhs = camera_world_to_pixel_point(&g_camera, rhs);
     SDL_SetRenderDrawColor(g_renderer, 0, 255, 0, 255);
-    SDL_RenderDrawLine(g_renderer, lhs.x, lhs.y, rhs.x, rhs.y);
+    SDL_RenderDrawLineF(g_renderer, lhs.x, lhs.y, rhs.x, rhs.y);
 
     rhs = camera_world_to_pixel_point(&g_camera, vaddf(transform->position, rigidbody_get_force(body)));
     SDL_SetRenderDrawColor(g_renderer, 0, 255, 255, 255);
-    SDL_RenderDrawLine(g_renderer, lhs.x, lhs.y, rhs.x, rhs.y);
+    SDL_RenderDrawLineF(g_renderer, lhs.x, lhs.y, rhs.x, rhs.y);
 }
 static  inline
 Vector _internal_calculate_contact_force(RigidBody* self, Contact* contact) {
diff --git a/core/src/player_input.c b/core/src/player_input.c
index d8220f8..55d1efc 100644
--- a/core/src/player_input.c
+++ b/core/src/player_input.c
@@ -1,7 +1,9 @@
 #include "player_input.h"
+#include "debug.h"
 
 PlayerInput* playerinput_new(void* target, int device) {
     PlayerInput* self = malloc(sizeof(PlayerInput));
+    ASSERT_RETURN(self != NULL, NULL, "Could not allocate memory for PlayerInput instance");
     self->listeners = list_from_type(InputListener);
     self->device = input_get_device_by_id(device);
     self->target = target;
diff --git a/core/src/program.c b/core/src/program.c
index 62a7220..538c960 100644
--- a/core/src/program.c
+++ b/core/src/program.c
@@ -25,7 +25,7 @@ double tstos(struct timespec ts) {
 
 struct timespec get_time() {
     struct timespec ts;
-    timespec_get(&ts, TIME_UTC);
+    (void)timespec_get(&ts, TIME_UTC);
     return ts;
 }
 
@@ -80,6 +80,7 @@ void program_run(const struct ProgramSettings* settings) {
             physics_world_tick();
         }
         settings->on_draw();
+        game_world_draw();
         SDL_Delay(1);
     }
 
@@ -132,10 +133,10 @@ void program_handle_windowevent(SDL_WindowEvent* event) {
 
 inline
 float delta_time() {
-    return _target_delta_time == 0 ? _delta_time : _target_delta_time;
+    return (float)(_target_delta_time == 0 ? _delta_time : _target_delta_time);
 }
 
 inline
 float game_time() {
-    return get_time_s() - _game_start_time;
+    return (float)(get_time_s() - _game_start_time);
 }
diff --git a/core/src/sprite.c b/core/src/sprite.c
index ac86bd8..3524502 100644
--- a/core/src/sprite.c
+++ b/core/src/sprite.c
@@ -42,10 +42,10 @@ void sprite_draw(Sprite* self, Transform transform) {
 
     Vector origin = self->origin;
     if(self->flip_state && SDL_FLIP_HORIZONTAL) {
-        origin.x = 1.0-origin.x;
+        origin.x = 1.0f-origin.x;
     }
     if((self->flip_state & SDL_FLIP_VERTICAL) != 0) {
-        origin.y = 1.0-origin.y;
+        origin.y = 1.0f-origin.y;
     }
 
     Vector left_top = transform_point(&transform, vinvf(origin));
@@ -75,8 +75,7 @@ size_t sprite_get_tile(const Sprite* self) {
 }
 
 void sprite_set_tile(Sprite* self, size_t frame) {
-    frame = frame % spritesheet_get_tile_count(self->spritesheet);
-    self->tile_index = frame;
+    self->tile_index = frame % spritesheet_get_tile_count(self->spritesheet);
 }
 
 Spritesheet* sprite_get_spritesheet(const Sprite* self) {
diff --git a/core/src/spritesheet.c b/core/src/spritesheet.c
index 777492a..554fa6f 100644
--- a/core/src/spritesheet.c
+++ b/core/src/spritesheet.c
@@ -43,8 +43,8 @@ Spritesheet* spritesheet_from_texture(SDL_Texture* texture, IVector tile_size) {
     SDL_QueryTexture(self->texture, NULL, NULL, &self->resolution.x, &self->resolution.y);
 
     self->tile_size = tile_size;
-    self->tile_shear = self->resolution.x / self->tile_size.x;
-    self->tile_count = self->resolution.x / self->tile_size.x * self->resolution.y / self->tile_size.y;
+    self->tile_shear = (size_t)self->resolution.x / self->tile_size.x;
+    self->tile_count = (size_t)self->resolution.x / self->tile_size.x * self->resolution.y / self->tile_size.y;
     return self;
 }
 
@@ -57,7 +57,7 @@ SDL_Texture* spritesheet_get_texture(const Spritesheet* self) {
 }
 
 SDL_Rect spritesheet_get_tile_rect(const Spritesheet* self, size_t index) {
-    IVector tile_coord = {index % self->tile_shear, index / self->tile_shear};
+    IVector tile_coord = {(int)(index % self->tile_shear), (int)(index / self->tile_shear)};
     tile_coord = vmuli(tile_coord, self->tile_size);
 
     return (SDL_Rect) {
diff --git a/core/src/state.h b/core/src/state.h
new file mode 100644
index 0000000..ebaea05
--- /dev/null
+++ b/core/src/state.h
@@ -0,0 +1,27 @@
+#ifndef _fencer_state_h
+#define _fencer_state_h
+
+#include "typeclass_helpers.h"
+
+typedef struct State State;
+
+struct State {
+    void (*const enter)(void* data);
+    void (*const exit)(void* data);
+    const State* (*const update)(void* data, float dt);
+};
+
+#define DefineState(_StateName, _DataType, enter_fn, exit_fn, update_fn)\
+static inline const State* _StateName() {\
+    TC_FN_TYPECHECK(void, enter_fn, _DataType*);\
+    TC_FN_TYPECHECK(void, exit_fn, _DataType*);\
+    TC_FN_TYPECHECK(const State*, update_fn, _DataType*, float);\
+    static const State instance = {\
+        .enter = (void(*const)(void*)) enter_fn,\
+        .exit = (void(*const)(void*)) exit_fn,\
+        .update = (const State*(*const)(void*, float)) update_fn,\
+    };\
+    return &instance;\
+}
+
+#endif // !_fencer_state_h
diff --git a/core/src/state_machine.c b/core/src/state_machine.c
new file mode 100644
index 0000000..4e18ad8
--- /dev/null
+++ b/core/src/state_machine.c
@@ -0,0 +1,42 @@
+#include "state_machine.h"
+#include "stdlib.h"
+#include "debug.h"
+
+struct StateMachine {
+    const State* current_state;
+    void* data;
+};
+
+static inline
+void internal_state_machine_set_state(StateMachine* self, const State* state) {
+    self->current_state->exit(self->data);
+    self->current_state = state;
+    self->current_state->enter(self->data);
+}
+
+StateMachine* state_machine_init(void* data, const State* start_state) {
+    StateMachine* self = malloc(sizeof(StateMachine));
+    ASSERT_RETURN(self != NULL, NULL, "Failed to allocate space for StateMachine instance");
+    *self = (StateMachine){
+        .current_state = start_state,
+        .data = data
+    };
+
+    self->current_state->enter(self->data);
+    return self;
+}
+
+void state_machine_destroy(StateMachine* self) {
+    self->current_state->exit(self->data);
+    free(self);
+}
+
+void state_machine_update(StateMachine* self, float dt) {
+    const State* next = self->current_state->update(self->data, dt);
+    if(next != self->current_state)
+        internal_state_machine_set_state(self, next);
+}
+
+const State* state_machine_get_current_state(StateMachine* self) {
+    return self->current_state;
+}
diff --git a/core/src/state_machine.h b/core/src/state_machine.h
new file mode 100644
index 0000000..0d5c2fb
--- /dev/null
+++ b/core/src/state_machine.h
@@ -0,0 +1,15 @@
+#ifndef _fencer_state_machine_h
+#define _fencer_state_machine_h
+
+#include "state.h"
+
+typedef struct StateMachine StateMachine;
+
+extern StateMachine* state_machine_init(void* data, const State* start_state);
+extern void state_machine_destroy(StateMachine* self);
+
+extern void state_machine_update(StateMachine* self, float dt);
+
+extern const State* state_machine_get_current_state(StateMachine* self);
+
+#endif // !_fencer_state_machine_h
diff --git a/core/src/vmath.h b/core/src/vmath.h
index 4c92d6b..029463f 100644
--- a/core/src/vmath.h
+++ b/core/src/vmath.h
@@ -106,7 +106,7 @@ float vsqrmagnitudef(Vector a) {
 static inline
 Vector vnormalizedf(Vector a) {
     if(veqf(a, ZeroVector)) return ZeroVector;
-    return vmulff(a, 1.0/vmagnitudef(a));
+    return vmulff(a, 1.0f/vmagnitudef(a));
 }
 static inline
 float vdotf(Vector a, Vector b) {
@@ -122,7 +122,7 @@ float vsqrdistf(Vector a, Vector b) {
 }
 static inline
 Vector vreciprocalf(Vector a) {
-    return (Vector){1.0/a.x, 1.0/a.y};
+    return (Vector){1.0f/a.x, 1.0f/a.y};
 }
 static inline
 Vector vrotatef(Vector a, float t) {
@@ -157,7 +157,7 @@ Vector vaveragef(Vector* array, size_t count) {
     for(size_t i = 0; i < count; ++i) {
         acc = vaddf(acc, array[i]);
     }
-    return vmulff(acc, 1.0/(float)count);
+    return vmulff(acc, 1.0f/(float)count);
 }
 
 static inline
diff --git a/game/Build-Game.lua b/game/Build-Game.lua
index fd6bf22..8fb6e6a 100644
--- a/game/Build-Game.lua
+++ b/game/Build-Game.lua
@@ -3,17 +3,22 @@ project "Game"
   language "C"
   staticruntime "Off"
   targetdir "bin/%{cfg.buildcfg}"
+  debugdir "."
 
   defines { "VMATH_SDL" }
 
   files { "src/**.c", "src/**.h" }
   includedirs {
     "src/",
-    "../core/src"
+    "../core/src/"
   }
   links {
     "Engine-Core"
   }
+  
+  postbuildcommands {
+    "{COPYDIR} assets/ %{cfg.targetdir}/assets"
+  }
 
   targetdir ("../bin/" .. OutputDir .. "/%{prj.name}" )
   objdir ("../intermediate/" .. OutputDir .. "/%{prj.name}" )
diff --git a/game/src/fencer.c b/game/src/fencer.c
index 47ab192..050c644 100644
--- a/game/src/fencer.c
+++ b/game/src/fencer.c
@@ -1,10 +1,7 @@
-#include "camera.h"
 #include "program.h"
 
 static
-void play() {
-    g_camera.fov = 40;
-}
+void play() {}
 
 static
 void tick() {}