added Dice global, rolling mechanic, skill and ability modifiers

This commit is contained in:
Adam Burns 2026-02-17 22:17:25 -05:00
parent 90a179e6d3
commit ee2d25bc0d
14 changed files with 281 additions and 92 deletions

22
assets/icons/dice.svg Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="16"
height="16"
viewBox="0 0 16 16"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<path
style="fill:#b55088;fill-opacity:1;stroke:#2ce8f5;stroke-width:1.02926;stroke-dasharray:none;stroke-opacity:1"
id="path6"
d="M 4.9352519,7.8705033 -0.02975355,6.6466934 -0.40011221,1.5465146 4.335999,-0.38175933 7.6334353,3.5266806 Z"
transform="matrix(1.4247666,0.30649892,-0.30649892,1.4247666,4.1731733,1.2010391)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 705 B

View File

@ -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

View File

@ -18,6 +18,7 @@ config/icon="uid://eyvap3pllv"
[autoload]
Globals="*uid://bjvmd2grgcjl1"
Dice="*uid://idushb7pc7k4"
[display]

View File

@ -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]

View File

@ -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"]

View File

@ -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]

41
scripts/autoloads/dice.gd Normal file
View File

@ -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

View File

@ -0,0 +1 @@
uid://idushb7pc7k4

View File

@ -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.")

View File

@ -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

View File

@ -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])

View File

@ -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)

View File

@ -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")]

View File

@ -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")]