diff --git a/assets/icons/dice.svg b/assets/icons/dice.svg new file mode 100644 index 0000000..a9433a9 --- /dev/null +++ b/assets/icons/dice.svg @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/assets/icons/dice.svg.import b/assets/icons/dice.svg.import new file mode 100644 index 0000000..49ad6e5 --- /dev/null +++ b/assets/icons/dice.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dl2kjtyrsj4xx" +path="res://.godot/imported/dice.svg-a20a09c674b3455f8a3f0c7b27f5d197.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/icons/dice.svg" +dest_files=["res://.godot/imported/dice.svg-a20a09c674b3455f8a3f0c7b27f5d197.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/project.godot b/project.godot index 1c4db6e..869b533 100644 --- a/project.godot +++ b/project.godot @@ -18,6 +18,7 @@ config/icon="uid://eyvap3pllv" [autoload] Globals="*uid://bjvmd2grgcjl1" +Dice="*uid://idushb7pc7k4" [display] diff --git a/scenes/ball.tscn b/scenes/ball.tscn index 106340f..1990cc3 100644 --- a/scenes/ball.tscn +++ b/scenes/ball.tscn @@ -6,8 +6,7 @@ [sub_resource type="CircleShape2D" id="CircleShape2D_cxlvu"] radius = 13.0 -[node name="Ball" type="RigidBody2D" unique_id=1681382008] -mass = 0.65 +[node name="Ball" type="Area2D" unique_id=689624972] script = ExtResource("1_x8fbi") [node name="BallSprite" type="Sprite2D" parent="." unique_id=982207895] diff --git a/scenes/game.tscn b/scenes/game.tscn index f3af23c..282c9c2 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -9,9 +9,9 @@ [ext_resource type="PackedScene" uid="uid://jbmh0ufhsbng" path="res://scenes/debug/debug_container.tscn" id="7_u5sy4"] [ext_resource type="PackedScene" uid="uid://b4qlhcnyklahj" path="res://scenes/debug/debug_object.tscn" id="8_gee14"] -[node name="Game" type="Node2D" unique_id=980666470 node_paths=PackedStringArray("ball", "court", "home_team", "away_team")] +[node name="Game" type="Node2D" unique_id=980666470 node_paths=PackedStringArray("game_ball", "court", "home_team", "away_team")] script = ExtResource("1_7jktm") -ball = NodePath("Ball") +game_ball = NodePath("Ball") court = NodePath("Court") home_team = NodePath("OrangeTeam") away_team = NodePath("BlueTeam") @@ -56,14 +56,17 @@ rotation = 2.8848953 position = Vector2(760, 873) zoom = Vector2(1.5, 1.5) -[node name="GameDebug" type="CanvasLayer" parent="." unique_id=1401074573] +[node name="DebugLayer" type="CanvasLayer" parent="." unique_id=1401074573] -[node name="DebugContainer" parent="GameDebug" unique_id=379013793 instance=ExtResource("7_u5sy4")] +[node name="DebugContainer" parent="DebugLayer" unique_id=379013793 instance=ExtResource("7_u5sy4")] -[node name="BallDebug" parent="GameDebug/DebugContainer" unique_id=1345287721 node_paths=PackedStringArray("target_object") instance=ExtResource("8_gee14")] +[node name="BallDebug" parent="DebugLayer/DebugContainer" unique_id=1345287721 node_paths=PackedStringArray("target_object") instance=ExtResource("8_gee14")] layout_mode = 2 target_object = NodePath("../../../Ball") target_variables = Array[StringName]([&"position", &"global_position"]) +[node name="GameDebug" parent="DebugLayer/DebugContainer" unique_id=360848029 instance=ExtResource("8_gee14")] +layout_mode = 2 + [editable path="OrangeTeam"] [editable path="BlueTeam"] diff --git a/scenes/player.tscn b/scenes/player.tscn index 671491f..7bf391d 100644 --- a/scenes/player.tscn +++ b/scenes/player.tscn @@ -9,7 +9,7 @@ radius = 11.0 height = 46.0 [sub_resource type="CircleShape2D" id="CircleShape2D_qhqgy"] -radius = 75.0 +radius = 50.0 [sub_resource type="Gradient" id="Gradient_i3pqv"] offsets = PackedFloat32Array(0, 0.74882627) @@ -33,9 +33,9 @@ position = Vector2(1, 0) shape = SubResource("CapsuleShape2D_i3pqv") debug_color = Color(0.23987567, 0.6988715, 0.40862384, 0.41960785) -[node name="PlayerReachArea" type="Area2D" parent="." unique_id=1000158753] +[node name="PlayerReach" type="Area2D" parent="." unique_id=1000158753] -[node name="CollisionShape2D" type="CollisionShape2D" parent="PlayerReachArea" unique_id=1228042585] +[node name="CollisionShape2D" type="CollisionShape2D" parent="PlayerReach" unique_id=1228042585] shape = SubResource("CircleShape2D_qhqgy") [node name="PlayerCamera" type="Camera2D" parent="." unique_id=1839930911] diff --git a/scripts/autoloads/dice.gd b/scripts/autoloads/dice.gd new file mode 100644 index 0000000..1a47799 --- /dev/null +++ b/scripts/autoloads/dice.gd @@ -0,0 +1,41 @@ +@icon("res://assets/icons/dice.svg") +extends Node + +enum DiceType { + d4, + d6, + d8, + d10, + d12, + d20, + d100 +} + +var dice: Dictionary[DiceType, Array] = { + DiceType.d4: [1, 2, 3, 4], + DiceType.d6: [1, 2, 3, 4, 5, 6], + DiceType.d8: [1, 2, 3, 4, 5, 6, 7, 8], + DiceType.d10: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + DiceType.d12: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + DiceType.d20: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + DiceType.d100: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90] +} + +func roll(dice_type: DiceType, number: int) -> int: + var total: int = 0 + for die in number: + var this_roll = _roll_this_die(dice_type, number) + total += this_roll + return total + +func _roll_this_die(d_t: DiceType, roll_index: int) -> int: + print_debug("Roll #%s commencing..." % roll_index) + var roll: int = dice[d_t].pick_random() + var crit_hit: bool = roll == dice[d_t].back() + if crit_hit: + print_debug("That's a critical hit!") + var crit_miss: bool = roll == dice[d_t].front() + if crit_miss: + print_debug("That's a critical miss!") + print_debug("Roll #%s total: %s" % [roll_index, roll]) + return roll diff --git a/scripts/autoloads/dice.gd.uid b/scripts/autoloads/dice.gd.uid new file mode 100644 index 0000000..4633b65 --- /dev/null +++ b/scripts/autoloads/dice.gd.uid @@ -0,0 +1 @@ +uid://idushb7pc7k4 diff --git a/scripts/ball.gd b/scripts/ball.gd index 6283cc1..6b92624 100644 --- a/scripts/ball.gd +++ b/scripts/ball.gd @@ -1,3 +1,22 @@ @icon("res://assets/icons/ball.svg") class_name Ball -extends RigidBody2D +extends Area2D + +var handler: Player: set = set_handler +var is_loose: bool = true: set = set_is_loose + +## -- SETTERS -- +func set_handler(h: Player) -> void: + handler = h + if handler: + print_debug("The ball is in %s's hands" % handler.attributes.player_name) + else: + print_debug("The ball is in noone's hands") + +func set_is_loose(i_l: bool) -> void: + is_loose = i_l + if is_loose: + handler = null + print_debug("The ball is loose!") + else: + print_debug("The ball is no longer loose.") diff --git a/scripts/custom resources/player_attributes.gd b/scripts/custom resources/player_attributes.gd index 1e78e4a..8f9615e 100644 --- a/scripts/custom resources/player_attributes.gd +++ b/scripts/custom resources/player_attributes.gd @@ -3,12 +3,20 @@ extends Resource @export_group("Texture") @export var player_texture: Texture2D + @export_group("Basics") @export var player_name: String -@export_range(0.0, 99.0, 1.0, "prefer_slider") var player_number: int +@export_range(0, 99, 1, "prefer_slider") var player_number: int @export var player_position: Globals.PlayerPosition -@export_range(150.0, 250.0, 1.0, "prefer_slider") var player_height: float ## Player height in centimetres; determines reach. +@export_range(150.0, 250.0, 1.0, "prefer_slider", "suffix:cm") var player_height: float ## Player height in centimetres; determines reach. +@export_range(50.0, 150.0, 1.0, "prefer_slider", "suffix:kg") var player_weight: float @export var player_handedness: Globals.PlayerHandedness +@export_range(5.0, 10.0, 0.1, "prefer_slider", "or_greater", "suffix:m/s") var movement_speed: float = 6.0 + +@export_group("Modifiers") @export_group("Abilities") -@export var speed: float -@export var reflexes: float +@export_range(-5, 5, 1, "prefer_slider") var dexterity: int = 0 ## Quick hands, quick feet +@export_range(-5, 5, 1, "prefer_slider") var strength: int = 0 ## Fight for rebounds, pound the post +@export_range(-5, 5, 1, "prefer_slider") var constitution: int = 0 ## Stay on the floor, pull through in the clutch +@export_group("Skills") +@export_range(-5, 5, 1, "prefer_slider") var ball_handling: int = 0 ## Hang on to the rock diff --git a/scripts/game_manager.gd b/scripts/game_manager.gd index 0e913a7..1a00ae6 100644 --- a/scripts/game_manager.gd +++ b/scripts/game_manager.gd @@ -2,24 +2,45 @@ class_name GameManager extends Node2D -@export var ball: Ball +## -- EXPORTS -- +@export var game_ball: Ball @export var court: Court @export_group("Teams") @export var home_team: Team @export var away_team: Team +## -- VARIABLES -- var players: Array var active_player: Player: set = set_active_player +var ball_handler: Player: set = set_ball_handler +## -- OVERRIDES -- func _ready() -> void: players = get_tree().get_nodes_in_group("players") for player in players: - player.connect("turn_finished", on_player_turn_finished) + player = player as Player + _connect_player_signals(player) player.is_active = false + player.current_ball = game_ball active_player = players[0] as Player +## -- RECEIVERS -- +func on_player_ball_dropped() -> void: + print_debug("%s has dropped the ball" % ball_handler.attributes.player_name) + game_ball.is_loose = true + ball_handler = null + +func on_player_grab_attempted() -> void: + print_debug("%s has attempted to grab the ball" % active_player.attributes.player_name) + if game_ball.is_loose: + print_debug("The ball is loose. This should be easy.") + active_player.has_ball = true + ball_handler = active_player + game_ball.is_loose = false + else: + print_debug("%s has the ball. This could be tough." % ball_handler.attributes.player_name) + _determine_steal_outcome(active_player, ball_handler) -## Signal-handlers func on_player_turn_finished() -> void: print_debug("Moving to the next player in the turn order") if players.find(active_player) < players.size() -1: @@ -27,8 +48,39 @@ func on_player_turn_finished() -> void: else: active_player = players.get(0) -## Setters -func set_active_player(new_active_player) -> void: - active_player = new_active_player +## -- SETTERS -- +func set_active_player(a_p) -> void: + active_player = a_p print_debug("The game is setting the active player to %s" % active_player.attributes.player_name) active_player.is_active = true + +func set_ball_handler(b_h: Player) -> void: + ball_handler = b_h + if ball_handler: + print_debug("%s has the ball" % ball_handler.attributes.player_name) + +## -- HELPERS + +func _connect_player_signals(p: Player) -> void: + p.connect("ball_dropped", on_player_ball_dropped) + p.connect("turn_finished", on_player_turn_finished) + p.connect("grab_attempted", on_player_grab_attempted) + +func _determine_steal_outcome(grabber: Player, handler: Player) -> void: + print_debug("%s attempting a steal from %s" % [grabber.attributes.player_name, handler.attributes.player_name]) + var grabber_dexterity_mod: int = grabber.attributes.dexterity + var handler_handling_mod: int = handler.attributes.ball_handling + var grabber_roll: int = Dice.roll(Dice.DiceType.d20, 1) + var grabber_total: int = grabber_roll + grabber_dexterity_mod + print_debug("%s rolled a %s and has a %s dexterity modifier, for a total of %s" % [grabber.attributes.player_name, grabber_roll, grabber_dexterity_mod, grabber_total]) + var handler_roll: int = Dice.roll(Dice.DiceType.d20, 1) + var handler_total: int = handler_roll + handler_handling_mod + print_debug("%s rolled a %s and has a %s ball-handling modifier, for a total of %s" % [handler.attributes.player_name, handler_roll, handler_handling_mod, handler_total]) + var steal_successful: bool = grabber_total > handler_total + if steal_successful: + print_debug("Steal successful! Changing possession from %s to %s" % [handler.attributes.player_name, grabber.attributes.player_name]) + ball_handler.has_ball = false + ball_handler = active_player + active_player.has_ball = true + else: + print_debug("Steal unsuccessful! %s keeps possession" % [handler.attributes.player_name]) diff --git a/scripts/player.gd b/scripts/player.gd index 9d3a169..77d09f1 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -3,17 +3,19 @@ class_name Player extends CharacterBody2D ## Signals +signal ball_dropped +signal grab_attempted signal turn_finished ## Export variables @export var attributes: PlayerAttributes ## The attributes resource that defines the player's abilities, vitals etc. ## Regular variables -var current_ball: Ball +var current_ball: Ball: set = set_current_ball var ball_within_reach: bool = false var has_ball: bool = false: set = set_has_ball var is_active: bool = false: set = set_active -var desired_velocity: Vector2: set = set_desired_velocity +var desired_velocity: Vector2 var player_label_offset: Vector2 ## Onready variables @@ -24,11 +26,10 @@ var player_label_offset: Vector2 @onready var player_right_hand: RemoteTransform2D = %PlayerRightHand @onready var player_label: PlayerLabel = $PlayerLabel @onready var player_nav: NavigationAgent2D = $PlayerNav -@onready var player_reach_area: Area2D = $PlayerReachArea +@onready var player_reach: Area2D = $PlayerReach @onready var nav_target_sprite: Sprite2D = $NavTargetSprite @onready var player_sprite: Sprite2D = $PlayerSprite @onready var player_ui: CanvasLayer = $PlayerUI -#@onready var end_turn_button: Button = %EndTurnButton ## -- Overrides -- ## func _ready() -> void: @@ -42,17 +43,10 @@ func _process(_delta: float) -> void: if is_active: nav_target_sprite.global_position = get_global_mouse_position() -func _physics_process(delta: float) -> void: - desired_velocity = global_position.direction_to(player_nav.get_next_path_position()) * attributes.speed - if get_last_slide_collision(): - var collision = get_last_slide_collision() - var collider = collision.get_collider() - if collider is Ball: - collider.apply_central_impulse(collision.get_normal() * -1.0) - if has_ball: - current_ball.apply_central_force( - current_ball.global_position.direction_to(global_position) * current_ball.global_position.distance_to(global_position) - ) +func _physics_process(_delta: float) -> void: + desired_velocity = global_position.direction_to(player_nav.get_next_path_position()) * attributes.movement_speed * 100.0 + player_nav.velocity = desired_velocity + func _input(event: InputEvent) -> void: if event.is_action_pressed("end_turn"): @@ -63,7 +57,7 @@ func _input(event: InputEvent) -> void: else: print_debug("%s can't drop the ball" % attributes.player_name) if event.is_action_pressed("grab_ball"): - if ball_within_reach: grab_ball(current_ball) + if ball_within_reach: attempt_grab(current_ball) else: print_debug("%s can't grab the ball" % attributes.player_name) if event is InputEventMouseButton and event.is_pressed() and event.button_index == 1: @@ -75,26 +69,22 @@ func _input(event: InputEvent) -> void: ## -- ACTIONS -- ## -func drop_ball(b: Ball) -> void: +func attempt_grab(_b: Ball) -> void: + print_debug("%s will attempt to grab the ball" % attributes.player_name) + grab_attempted.emit() + +func drop_ball(_b: Ball) -> void: print_debug("%s will attempt to drop the ball" % attributes.player_name) - #for hand in player_hands.get_children(): - #hand = hand as RemoteTransform2D - #hand.remote_path = NodePath("") - has_ball = false + for hand in player_hands.get_children(): + hand = hand as RemoteTransform2D + hand.remote_path = NodePath("") + ball_dropped.emit() func end_turn() -> void: print_debug("%s says: I'd like my turn to end." % attributes.player_name) is_active = false turn_finished.emit() -func grab_ball(b: Ball) -> void: - print_debug("%s will attempt to grab the ball" % attributes.player_name) - #if attributes.player_handedness == Globals.PlayerHandedness.LEFT: - #player_left_hand.remote_path = player_left_hand.get_path_to(b) - #else: - #player_right_hand.remote_path = player_right_hand.get_path_to(b) - has_ball = true - ## -- SETTERS -- ## func set_active(active: bool) -> void: is_active = active @@ -112,15 +102,48 @@ func set_active(active: bool) -> void: player_nav.avoidance_priority = 0.5 set_process_input(false) -func set_desired_velocity(d_v: Vector2) -> void: - desired_velocity = d_v - player_nav.velocity = desired_velocity +func set_current_ball(b: Ball) -> void: + current_ball = b + print_debug("%s sees the game ball" % attributes.player_name) func set_has_ball(h_b: bool) -> void: has_ball = h_b - if has_ball: print_debug("%s has the ball" % attributes.player_name) - else: print_debug("%s no longer as the ball" % attributes.player_name) + if has_ball: + print_debug("%s says: I have the ball" % attributes.player_name) + if attributes.player_handedness == Globals.PlayerHandedness.LEFT: + player_left_hand.remote_path = player_left_hand.get_path_to(current_ball) + else: + player_right_hand.remote_path = player_right_hand.get_path_to(current_ball) + else: + print_debug("%s says: I no longer have the ball" % attributes.player_name) + if attributes.player_handedness == Globals.PlayerHandedness.LEFT: + player_left_hand.remote_path = NodePath("") + else: + player_right_hand.remote_path = NodePath("") +## -- RECEIVERS -- ## +func on_player_reach_body_entered(body: PhysicsBody2D) -> void: + if body is Player: + print_debug((body.attributes.player_name) + " has entered %s's reach area" % attributes.player_name) + +func on_player_reach_area_entered(area: CollisionObject2D) -> void: + if area is Ball: + print_debug("The ball has entered %s's reach area" % attributes.player_name) + ball_within_reach = true + current_ball = area + +func on_player_reach_body_exited(body: PhysicsBody2D) -> void: + if body is Player: + print_debug((body.attributes.player_name) + " has exited %s's reach area" % attributes.player_name) + +func on_player_reach_area_exited(area: CollisionObject2D) -> void: + if area is Ball: + print_debug("The ball has exited %s's reach area" % attributes.player_name) + ball_within_reach = false + +func on_velocity_computed(safe_velocity: Vector2) -> void: + velocity = safe_velocity + move_and_slide() ## -- HELPERS -- ## func _apply_attributes() -> void: @@ -131,27 +154,7 @@ func _apply_attributes() -> void: func _connect_signals() -> void: player_nav.connect("velocity_computed", on_velocity_computed) - player_reach_area.connect("body_entered", on_player_reach_area_body_entered) - player_reach_area.connect("body_exited", on_player_reach_area_body_exited) - - -## -- RECEIVERS -- ## -func on_player_reach_area_body_entered(body: PhysicsBody2D) -> void: - if body is Player: - print_debug((body.attributes.player_name) + " has entered %s's reach area" % attributes.player_name) - elif body is Ball: - print_debug("The ball has entered %s's reach area" % attributes.player_name) - ball_within_reach = true - current_ball = body - -func on_player_reach_area_body_exited(body: PhysicsBody2D) -> void: - if body is Player: - print_debug((body.attributes.player_name) + " has exited %s's reach area" % attributes.player_name) - elif body is Ball: - print_debug("The ball has exited %s's reach area" % attributes.player_name) - ball_within_reach = false - -func on_velocity_computed(safe_velocity: Vector2) -> void: - look_at(player_nav.get_next_path_position()) - velocity = safe_velocity - move_and_slide() + player_reach.connect("body_entered", on_player_reach_body_entered) + player_reach.connect("body_exited", on_player_reach_body_exited) + player_reach.connect("area_entered", on_player_reach_area_entered) + player_reach.connect("area_exited", on_player_reach_area_exited) diff --git a/teams/blue_team.tscn b/teams/blue_team.tscn index bb5294a..c6daad3 100644 --- a/teams/blue_team.tscn +++ b/teams/blue_team.tscn @@ -20,8 +20,10 @@ player_texture = ExtResource("4_21h5f") player_name = "Stevie" player_number = 14 player_handedness = 1 -speed = 250.0 -reflexes = 5.0 +dexterity = 5 +strength = -2 +constitution = -1 +ball_handling = 5 metadata/_custom_type_script = "uid://bbey5ttwv5utd" [sub_resource type="Resource" id="Resource_srnfe"] @@ -31,18 +33,14 @@ player_name = "Brendan" player_number = 9 player_position = 1 player_handedness = 1 -speed = 170.0 -reflexes = 3.0 metadata/_custom_type_script = "uid://bbey5ttwv5utd" [sub_resource type="Resource" id="Resource_6cfox"] script = ExtResource("5_bwj7y") player_texture = ExtResource("7_bwj7y") player_name = "Big Country" -player_number = 50 +player_number = 15 player_position = 2 -speed = 100.0 -reflexes = 1.0 metadata/_custom_type_script = "uid://bbey5ttwv5utd" [node name="BlueTeam" unique_id=1449767365 instance=ExtResource("1_7xvty")] diff --git a/teams/orange_team.tscn b/teams/orange_team.tscn index 222354a..739c136 100644 --- a/teams/orange_team.tscn +++ b/teams/orange_team.tscn @@ -20,8 +20,11 @@ player_texture = ExtResource("4_dbbjc") player_name = "Dave" player_number = 11 player_handedness = 1 -speed = 400.0 -reflexes = 5.0 +movement_speed = 10.0 +dexterity = 2 +strength = -1 +constitution = 2 +ball_handling = 4 metadata/_custom_type_script = "uid://bbey5ttwv5utd" [sub_resource type="Resource" id="Resource_ufsyq"] @@ -31,8 +34,6 @@ player_name = "Matt" player_number = 13 player_position = 1 player_handedness = 1 -speed = 150.0 -reflexes = 3.0 metadata/_custom_type_script = "uid://bbey5ttwv5utd" [sub_resource type="Resource" id="Resource_nbn31"] @@ -42,8 +43,6 @@ player_name = "Orville" player_number = 33 player_position = 2 player_handedness = 1 -speed = 175.0 -reflexes = 4.0 metadata/_custom_type_script = "uid://bbey5ttwv5utd" [node name="OrangeTeam" unique_id=1449767365 instance=ExtResource("1_m0gtt")]