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"