diff --git a/client/autoloads/gamestate.gd b/client/autoloads/gamestate.gd
index 2d65ccd098bcf5ec3c2217be83cb59772039e8e5..86ce2750b072132682c2fd52751073970f193e7c 100644
--- a/client/autoloads/gamestate.gd
+++ b/client/autoloads/gamestate.gd
@@ -11,11 +11,11 @@ signal server_disconnected
 
 func joinServer(ip, port):
 	# Create the network client
-	var peer_join = NetworkedMultiplayerENet.new()
+	var peer_join:NetworkedMultiplayerENet = NetworkedMultiplayerENet.new()
 	peer_join.create_client(ip, int(port))
 	get_tree().set_network_peer(peer_join)
 	
-	print("Trying to join the server ", ip, ":", port) 
+	print("Trying to join the server ", ip, ":", port, " with id: ", get_tree().get_network_unique_id()) 
 
 
 puppet func registerPlayer(id): 
@@ -37,8 +37,8 @@ puppet func startGame():
 
 	get_node("/root/lobby").hide()
 	
-	var game = preload("res://levels/test/game.tscn").instance()
-	var player_scene = preload("res://entities/characters/player.tscn")
+	var game:Node = preload("res://levels/test/game.tscn").instance()
+	var player_scene:PackedScene = preload("res://entities/characters/player.tscn")
 	get_tree().get_root().add_child(game)
 	
 	# TODO: check (and perhaps) the scene changing functionality
@@ -47,16 +47,16 @@ puppet func startGame():
 	#Next evey player will spa every other player including the server's own client! Try to move this to server only 
 	var i = 1
 	for peer_id in players:
-		var player = player_scene.instance()
+		var player:Node = player_scene.instance()
 		
 		player.set_name(str(peer_id))
 		get_node(GAMEPATH).add_child(player)
 			
 		
-		var spawnPosition = get_node("/root/game/spawnCollection/spawn"+str(i)).get_translation()
+		var spawnPosition:Vector3 = get_node("/root/game/spawnCollection/spawn"+str(i)).get_translation()
 		player.translate(spawnPosition)
 		
 		i+=1
 		
-	var playerArrow = preload("res://entities/characters/playerArrow.tscn").instance()
+	var playerArrow:Node = preload("res://entities/characters/playerArrow.tscn").instance()
 	get_node(GAMEPATH+str(get_tree().get_network_unique_id())).add_child(playerArrow)
\ No newline at end of file
diff --git a/client/entities/characters/player.gd b/client/entities/characters/player.gd
index b1de2aa2cdf1c54b31805c473a44b2da1b719249..ddd4dad04953d30674add880868bef123094f7e9 100644
--- a/client/entities/characters/player.gd
+++ b/client/entities/characters/player.gd
@@ -1,5 +1,9 @@
 extends KinematicBody
 
 # Update the player's position based on the position computed by the remote scene on the server
-puppet func getRemoteMovement(position):
-	self.set_translation(position)
\ No newline at end of file
+puppet func getRemoteMovement(position:Vector3):
+	self.set_translation(position)
+
+# Called by the remote player
+puppet func hurt(damages:int):
+	print("You suffered a hit and lost " + str(damages) + " HPs !")
\ No newline at end of file
diff --git a/client/entities/characters/player.tscn b/client/entities/characters/player.tscn
index 27fb14fa6b16c22833b18417730590721df0bdf4..1d62a17316f3c1b7e404cdd206abeb868248e4bb 100644
--- a/client/entities/characters/player.tscn
+++ b/client/entities/characters/player.tscn
@@ -6,6 +6,7 @@
 script = ExtResource( 1 )
 
 [node name="Model" type="Spatial" parent="."]
+editor/display_folded = true
 
 [node name="CSGBox" type="CSGBox" parent="Model"]
 transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.513621, 0 )
diff --git a/client/levels/lobby/lobby.gd b/client/levels/lobby/lobby.gd
index 0603a645ee166e46207be127816417a0dc1141c0..3c453e05ff8fe45fde29c903f5ea0d6bcf39c2f3 100644
--- a/client/levels/lobby/lobby.gd
+++ b/client/levels/lobby/lobby.gd
@@ -10,13 +10,13 @@ func _ready():
 func _on_joinButton_pressed():
 	
 	# Get and check the Ip
-	var ip = get_node("menu/ipLineEdit").text
+	var ip:String = get_node("menu/ipLineEdit").text
 	if not ip.is_valid_ip_address():
 		get_node("menu/errorLabel").text = "Invalid IPv4 address!"
 		return
 
 	# Get and check the port
-	var port = get_node("menu/portLineEdit").text
+	var port:String = get_node("menu/portLineEdit").text
 	if port == "":
 		get_node("menu/errorLabel").text = "Invalid port!"
 		return
diff --git a/client/levels/test/game.gd b/client/levels/test/game.gd
index 75a680441385c0d2bb8f011cc89c704e1e1fd9f6..27c3788e0bbe6b93dec45e57fa49de0ba11a7e9d 100644
--- a/client/levels/test/game.gd
+++ b/client/levels/test/game.gd
@@ -1,28 +1,39 @@
 extends Node
 
+# Movements
 var movementInput:int = 0
 
 var jumpId:int = 0
 var sprintInput:bool = false
 
+# Attacks
+enum {NONE=0, PRIMARY=1, SECONDARY=2}
+
 func _physics_process(delta):
 	getPlayerInput()
-	
+
 func getPlayerInput():
 	movementInput = 0
-		
+	var attackStateInput:int = NONE
+
 	if Input.is_action_pressed("movementLeft"):
 		movementInput -= 1
 	if Input.is_action_pressed("movementRight"):
 		movementInput += 1
-		
+
 	if Input.is_action_just_pressed("movementJump"):
 		jumpId += 1
-		
+
 	if Input.is_action_pressed("movementSprint"):
 		sprintInput = true
 	else:
 		sprintInput = false
-	
+
+	if Input.is_action_pressed("primaryAttack"):
+		attackStateInput = PRIMARY
+
+	elif Input.is_action_pressed("secondaryAttack"):
+		attackStateInput = SECONDARY
+
 	# Sent without safety resend
-	rpc_unreliable_id(1, "sendPlayerInputs", movementInput, sprintInput, jumpId)
\ No newline at end of file
+	rpc_unreliable_id(1, "sendPlayerInputs", movementInput, sprintInput, jumpInput, attackStateInput)
diff --git a/client/project.godot b/client/project.godot
index 8048c79bb543536fa0d9dab757afacfc13e00440..936fcb642772d9109535f83e7acdc6d2878940f4 100644
--- a/client/project.godot
+++ b/client/project.godot
@@ -48,6 +48,16 @@ movementSprint={
 "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777237,"unicode":0,"echo":false,"script":null)
  ]
 }
+primaryAttack={
+"deadzone": 0.5,
+"events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"pressed":false,"doubleclick":false,"script":null)
+ ]
+}
+secondaryAttack={
+"deadzone": 0.5,
+"events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":2,"pressed":false,"doubleclick":false,"script":null)
+ ]
+}
 
 [rendering]
 
diff --git a/server/entities/characters/player.gd b/server/entities/characters/player.gd
index 9f833da1348ab2c0d6d4e39f02e5c227c39f1bb1..92eb7a1a88985da38e28caf229f6c0bdadde4e55 100644
--- a/server/entities/characters/player.gd
+++ b/server/entities/characters/player.gd
@@ -1,5 +1,10 @@
 extends KinematicBody
 
+onready var ownId = self.name
+
+
+######## MOVEMENT AND POSITION VARS ########
+
 # Speed values
 const MAX_SPEED:float = 8.0
 const MAX_SPRINT_SPEED:float = 12.0
@@ -21,8 +26,28 @@ const GRAVITY:float = -28.0
 var vel:Vector3 = Vector3(0,0,0)
 
 # move_and_slide need that
-const FLOOR_NORMAL:Vector3 = Vector3(0,1,0)
+const FLOOR_NORMAL:Vector3 = Vector3(0, 1, 0)
+
+
+######### ATTACKS VARS ########
 
+var isAttacking:bool = false
+onready var attackTimer:Tween = $attackTween
+
+# attack type
+enum {NONE=0, PRIMARY=1, SECONDARY=2}
+
+# Primary attack parameters
+onready var primaryHitArea:Area = $primaryHitArea
+var primaryAttackDmg:float = 30.0
+var primaryAttackDist:float = 1.2
+var primaryAttackDuration:float = 0.6
+
+# Secondary attack parameters
+onready var secondaryHitArea:Area = $secondaryHitArea
+var secondaryAttackDmg:float = 15.0
+var secondaryAttackDist:float = 1.2
+var secondaryAttackDuration:float = 0.3
 
 # Character informations
 var damages:int = 0
@@ -34,49 +59,57 @@ var lastJumpId:int = 0      # Detect a new jump
 var lastPunchId:int = 0     # Detect a new punch (of any type)
 var idleTime:float = 0      # Stun when hitted + prevent spam
 
+######## FUNCTIONS ########
 
 # called by the engine
 func _physics_process(delta):
 	processMovement(delta)
-	broadcastMovement(delta)
-
+	broadcastMovement()
 
 # Called from the game script to update the vars
-func getPlayerInputs(movementInput, sprintInput, jumpId):
-	if lastJumpId != jumpId:
-		lastJumpId = jumpId
-		isJumping = true
-	else:
-		isJumping = false
-	
+func getPlayerInputs(movementInput, sprintInput, jumpInput, attackTypeInput):
+	motion = 0
+
+	# Check if the character isn't already attacking
+	if isAttacking:
+		return
+
 	motion = movementInput
 	isSprinting = sprintInput
+	isJumping = jumpInput
 
+	if attackTypeInput != NONE:
+		processAttack(attackTypeInput)
 
 func processMovement(delta):
-	# reset max_jump
+	# Set the side faced by the character
+	if motion > 0:
+		set_rotation_degrees(Vector3(0, 0, 0))
+	elif motion < 0:
+		set_rotation_degrees(Vector3(0, 180, 0))
+
 	if self.is_on_floor():
 		currentJump = 0
-	
+
 	if isJumping:
 		if currentJump < maxJump:
 			currentJump += 1
 			vel.y = JUMP_SPEED - delta*GRAVITY
-	
+
 	# Consider the gravity
 	vel.y += delta * GRAVITY
-	
+
 	# Speed of the player
-	var target = Vector3(motion, 0, 0)
-	
+	var target:Vector3 = Vector3(motion, 0, 0)
+
 	if isSprinting:
 		target *= MAX_SPRINT_SPEED
 	else:
 		target *= MAX_SPEED
-	
+
 	# Acceleration of the player if he is moving horizontally
-	var accel
-	var hvel:Vector3 = Vector3(vel.x,0,0)
+	var accel:float
+	var hvel:Vector3 = Vector3(vel.x, 0, 0)
 	if target.dot(hvel)>0:
 		if isSprinting:
 			accel = SPRINT_ACCEL
@@ -84,13 +117,80 @@ func processMovement(delta):
 			accel = ACCEL
 	else:
 		accel = DEACCEL
-		
+
 	# Interpolating speed and acceleration through time and getting it
 	vel.x = hvel.linear_interpolate(target, accel * delta).x
-	
+
 	# Update the velocity after the collisions (physical engine ftw)
 	vel = self.move_and_slide(vel, FLOOR_NORMAL)
-	
-func broadcastMovement(delta):
-	# Send the position of the player
-	rpc("getRemoteMovement", self.get_translation())
\ No newline at end of file
+
+# Send the position of the player
+func broadcastMovement():
+	rpc("getRemoteMovement", self.get_translation())
+
+
+# Check and trigger the type of attack requested
+func processAttack(attackType):
+
+	# Turn off the other attacks input for the time being
+	isAttacking = true
+
+	# Use the right function
+	match attackType:
+		PRIMARY:
+			primaryAttack()
+		SECONDARY:
+			secondaryAttack()
+
+
+func primaryAttack():
+	# Set active the hitbox of the attack, its visual feedback on server
+	primaryHitArea.set_monitoring(true)
+	primaryHitArea.set_visible(true)
+
+	# Prepare the translation as an interpolation over time (end position, duration and function used)
+	attackTimer.interpolate_property(primaryHitArea, "translation:x", 0, primaryAttackDist, primaryAttackDuration, Tween.TRANS_BACK, Tween.EASE_IN)
+	attackTimer.start()
+
+	# Once the movement is finished, turn off the hitbox and visual feedback
+	yield(attackTimer, "tween_completed")
+	isAttacking = false
+	primaryHitArea.set_monitoring(false)
+	primaryHitArea.set_visible(false)
+
+func secondaryAttack():
+	# Set active the hitbox of the attack, its visual feedback on server
+	secondaryHitArea.set_monitoring(true)
+	secondaryHitArea.set_visible(true)
+
+	# Prepare the translation as an interpolation over time (end position, duration and function used)
+	attackTimer.interpolate_property(secondaryHitArea, "translation:x", 0, secondaryAttackDist, secondaryAttackDuration, Tween.TRANS_BACK, Tween.EASE_IN)
+	attackTimer.start()
+
+	# Once the movement is finished, turn off the hitbox and visual feedback
+	yield(attackTimer, "tween_completed")
+	secondaryHitArea.set_monitoring(false)
+	secondaryHitArea.set_visible(false)
+	isAttacking = false
+
+# Called when detected by an attack's hitbox
+func hurt(damages):
+	rpc_id(int(ownId), "hurt", damages);
+
+######## SIGNALS ########
+
+# Primary hit has landed on something
+func _on_primaryHitArea_body_entered(body):
+	if body.name==ownId:
+		return
+
+	print("body named " + body.name + " hit: primary")
+	body.hurt(primaryAttackDmg)
+
+# Secondary hit has landed on something
+func _on_secondaryHitArea_body_entered(body):
+	if body.name==ownId:
+		return
+
+	print("body named " + body.name + " hit: secondary")
+	body.hurt(secondaryAttackDmg)
diff --git a/server/entities/characters/player.tscn b/server/entities/characters/player.tscn
index 83b08840edd3ca8a7c8209020665e93abf5eec88..2c3ed4a4b6efdc7d8a1dfeb5182bfc99d34f19ac 100644
--- a/server/entities/characters/player.tscn
+++ b/server/entities/characters/player.tscn
@@ -1,11 +1,16 @@
-[gd_scene load_steps=3 format=2]
+[gd_scene load_steps=4 format=2]
 
 [ext_resource path="res://entities/characters/Class1/class1.gd" type="Script" id=1]
 
 [sub_resource type="BoxShape" id=1]
 extents = Vector3( 0.175, 0.275, 0.7 )
 
+[sub_resource type="BoxShape" id=2]
+extents = Vector3( 0.544245, 0.192381, 0.312851 )
+
 [node name="Player" type="KinematicBody"]
+collision_layer = 2
+collision_mask = 3
 script = ExtResource( 1 )
 
 [node name="Body_CollisionShape" type="CollisionShape" parent="."]
@@ -24,3 +29,39 @@ depth = 0.684
 transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.17856, 0 )
 radius = 0.2
 radial_segments = 24
+
+[node name="primaryHitArea" type="Area" parent="."]
+visible = false
+monitoring = false
+collision_layer = 0
+collision_mask = 2
+
+[node name="primaryHitCol" type="CollisionShape" parent="primaryHitArea"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.420072, 0.815, 0 )
+shape = SubResource( 2 )
+
+[node name="primaryHitCSG" type="CSGBox" parent="primaryHitArea"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.419673, 0.815, 0 )
+width = 1.09437
+height = 0.400067
+depth = 0.618424
+
+[node name="secondaryHitArea" type="Area" parent="."]
+visible = false
+monitoring = false
+collision_layer = 0
+collision_mask = 2
+
+[node name="secondaryHitCol" type="CollisionShape" parent="secondaryHitArea"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.42, 0.2, 0 )
+shape = SubResource( 2 )
+
+[node name="secondaryHitCSG" type="CSGBox" parent="secondaryHitArea"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.42, 0.2, 0 )
+width = 1.09437
+height = 0.400067
+depth = 0.618424
+
+[node name="attackTween" type="Tween" parent="."]
+[connection signal="body_entered" from="primaryHitArea" to="." method="_on_primaryHitArea_body_entered"]
+[connection signal="body_entered" from="secondaryHitArea" to="." method="_on_secondaryHitArea_body_entered"]
diff --git a/server/levels/test/bloc/bloc.tscn b/server/levels/test/bloc/bloc.tscn
index 6d021e8d13c903885f228214692a6ae0c54fd89b..af80b4db19cbc72ac3605db63dbda9f830ef0323 100644
--- a/server/levels/test/bloc/bloc.tscn
+++ b/server/levels/test/bloc/bloc.tscn
@@ -12,6 +12,7 @@ mesh = SubResource( 1 )
 material/0 = null
 
 [node name="StaticBody" type="StaticBody" parent="MeshInstance"]
+collision_mask = 2
 
 [node name="CollisionShape" type="CollisionShape" parent="MeshInstance/StaticBody"]
 shape = SubResource( 2 )
diff --git a/server/levels/test/game.gd b/server/levels/test/game.gd
index 2605dfc2dbce99397aa89fc5b5d55b17c961138a..06d87b58428414ee92e0d56a52628ae2fbf6ba5f 100644
--- a/server/levels/test/game.gd
+++ b/server/levels/test/game.gd
@@ -1,7 +1,8 @@
 extends Node
 
-master func sendPlayerInputs(movementInput, sprintInput, jumpId):
+master func sendPlayerInputs(movementInput, jumpInput, sprintInput, attackStateInput):
 	
 	# Get the input from a client and send it to the matchig remote player
 	var senderId = get_tree().get_rpc_sender_id()
-	gamestate.get_node("/root/game/"+str(senderId)).getPlayerInputs(movementInput, sprintInput, jumpId)
\ No newline at end of file
+	gamestate.get_node("/root/game/"+str(senderId)).getPlayerInputs(movementInput, jumpInput, sprintInput, attackStateInput)
+
diff --git a/server/levels/test/game.tscn b/server/levels/test/game.tscn
index 6eff24791d697350566bb065965364165b72f725..59b44e0dec6bf18746a57cecc1b19794543f933a 100644
--- a/server/levels/test/game.tscn
+++ b/server/levels/test/game.tscn
@@ -3,8 +3,6 @@
 [ext_resource path="res://levels/test/game.gd" type="Script" id=1]
 [ext_resource path="res://levels/test/bloc/bloc.tscn" type="PackedScene" id=2]
 
-
-
 [node name="game" type="Spatial"]
 script = ExtResource( 1 )
 
diff --git a/server/project.godot b/server/project.godot
index 73c0570efa700235bd7b818b4665e7b168107d46..29dcf551aea96ea5f1b05f11c84a9f255b5bc516 100644
--- a/server/project.godot
+++ b/server/project.godot
@@ -23,6 +23,11 @@ config/icon="res://icon.png"
 
 gamestate="*res://autoloads/gamestate.gd"
 
+[layer_names]
+
+3d_physics/layer_1="environment"
+3d_physics/layer_2="players"
+
 [rendering]
 
 environment/default_environment="res://default_env.tres"