diff --git a/game_core/src/control/ai.rs b/game_core/src/control/ai.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ba6e5cb2cc6683b070117c2f7487eb7540803122
--- /dev/null
+++ b/game_core/src/control/ai.rs
@@ -0,0 +1,13 @@
+use bevy::prelude::*;
+
+#[derive(Copy, Clone, Debug, Component)]
+pub struct VisionRange(usize);
+#[derive(Copy, Clone, Debug, Component)]
+pub struct AggroTarget(Entity);
+#[derive(Copy, Clone, Debug, Component)]
+pub struct MoveTarget(UVec2);
+#[derive(Copy, Clone, Debug, Component)]
+pub struct Meander;
+
+#[derive(Copy, Clone, Debug, Component)]
+pub struct ShouldAct;
diff --git a/game_core/src/control/mod.rs b/game_core/src/control/mod.rs
index 632784b683760f1c0d88f21d0a25a4b2dd03ccfc..09bbf084840d0489b2c1dc08e21cb0e1effdc8f7 100644
--- a/game_core/src/control/mod.rs
+++ b/game_core/src/control/mod.rs
@@ -1,4 +1,5 @@
 pub mod actions;
+pub mod ai;
 pub mod player;
 
 mod __plugin {
diff --git a/game_core/src/control/player.rs b/game_core/src/control/player.rs
index 44f52965a1ae898105d399cd39a807cde946fc09..9d554930f6a50867ec29236088a26ce05b7d6639 100644
--- a/game_core/src/control/player.rs
+++ b/game_core/src/control/player.rs
@@ -1,31 +1,40 @@
+use std::time::Duration;
+
 use bevy::math::ivec2;
 use bevy::prelude::*;
 
 use crate::entities::lifecycle::Player;
+use crate::entities::timing::ActionCooldown;
 use crate::world::level_map::GridPosition;
 
 pub fn handle_player_input(
+	mut commands: Commands,
 	input: Res<Input<KeyCode>>,
-	mut query: Query<&mut GridPosition, With<Player>>,
+	mut query: Query<(Entity, &mut GridPosition), (With<Player>, Without<ActionCooldown>)>,
 ) {
 	let mut dx = 0;
 	let mut dy = 0;
 
-	if input.just_released(KeyCode::D) || input.just_released(KeyCode::Right) {
+	if input.pressed(KeyCode::D) || input.pressed(KeyCode::Right) {
 		dx += 1;
 	}
-	if input.just_released(KeyCode::W) || input.just_released(KeyCode::Up) {
+	if input.pressed(KeyCode::W) || input.pressed(KeyCode::Up) {
 		dy += 1;
 	}
-	if input.just_released(KeyCode::A) || input.just_released(KeyCode::Left) {
+	if input.pressed(KeyCode::A) || input.pressed(KeyCode::Left) {
 		dx -= 1;
 	}
-	if input.just_released(KeyCode::S) || input.just_released(KeyCode::Down) {
+	if input.pressed(KeyCode::S) || input.pressed(KeyCode::Down) {
 		dy -= 1;
 	}
 
-	for mut position in &mut query {
-		let next_position = (position.0.as_ivec2()) + ivec2(dx, dy);
-		**position = next_position.as_uvec2();
+	for (entity, mut position) in &mut query {
+		if dx != 0 || dy != 0 {
+			let next_position = (position.0.as_ivec2()) + ivec2(dx, dy);
+			**position = next_position.as_uvec2();
+			commands
+				.entity(entity)
+				.insert(ActionCooldown::from(Duration::from_millis(250)));
+		}
 	}
 }
diff --git a/game_core/src/entities/timing.rs b/game_core/src/entities/timing.rs
index e6c35869ce39ec564a6877eb11b658c58da1982f..e210d3c72e7416bf8a0598030d3aaae4f140bd56 100644
--- a/game_core/src/entities/timing.rs
+++ b/game_core/src/entities/timing.rs
@@ -1,3 +1,4 @@
+use std::ops::{Deref, DerefMut};
 use std::time::Duration;
 
 use bevy::prelude::*;
@@ -78,6 +79,41 @@ pub fn remove_global_timer_ui(mut commands: Commands, query: Query<Entity, With<
 	}
 }
 
+#[derive(Copy, Clone, Debug, Component)]
+#[repr(transparent)]
+#[component(storage = "SparseSet")]
+pub struct ActionCooldown(Duration);
+impl From<Duration> for ActionCooldown {
+	fn from(other: Duration) -> Self {
+		ActionCooldown(other)
+	}
+}
+impl Deref for ActionCooldown {
+	type Target = Duration;
+	fn deref(&self) -> &Self::Target {
+		&self.0
+	}
+}
+impl DerefMut for ActionCooldown {
+	fn deref_mut(&mut self) -> &mut Self::Target {
+		&mut self.0
+	}
+}
+
+pub fn tick_cooldown(
+	time: Res<Time>,
+	mut commands: Commands,
+	mut query: Query<(Entity, &mut ActionCooldown)>,
+) {
+	let delta = time.delta();
+	for (entity, mut cooldown) in &mut query {
+		**cooldown = cooldown.saturating_sub(delta);
+		if cooldown.is_zero() {
+			commands.entity(entity).remove::<ActionCooldown>();
+		}
+	}
+}
+
 pub struct TimingPlugin;
 impl Plugin for TimingPlugin {
 	fn build(&self, app: &mut App) {
@@ -88,6 +124,7 @@ impl Plugin for TimingPlugin {
 				ConditionSet::new()
 					.run_in_state(AppState::InGame)
 					.with_system(update_global_timer_ui)
+					.with_system(tick_cooldown)
 					.into(),
 			)
 			.add_exit_system(AppState::InGame, remove_global_timer_ui);