sunder/scenes/entities/flag/flag.gd
2026-03-16 03:44:38 -04:00

94 lines
3.1 KiB
GDScript

# This file is part of sunder.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
class_name Flag extends RigidBody3D
signal grabbed(by:Node)
signal dropped(by:Node)
const scene:PackedScene = preload("res://scenes/entities/flag/flag.tscn")
static func instantiate() -> Flag:
return scene.instantiate()
enum FlagState {
## The flag is fresh and at its initial position
ON_STAND,
## The flag is dropped by a [FlagHolder]
DROPPED,
## The flag is taken and being carried by a [FlagHolder]
TAKEN
}
@export var state:FlagState = FlagState.ON_STAND
@export var last_carrier:Node
@onready var area:Area3D = $GripArea
@onready var mesh:Node3D = $Mesh
@onready var waypoint:Waypoint3D = $Waypoint
# @TODO: move this variable to a server.cfg
var dropped_max_duration := 30.
## This timer is responsible for returning the flag when it is dropped for more than [member dropped_max_duration].
var respawn_timer := Timer.new()
func _ready() -> void:
area.body_entered.connect(_on_body_entered)
grabbed.connect(_on_grabbed)
dropped.connect(_on_dropped)
if multiplayer.is_server():
respawn_timer.wait_time = dropped_max_duration
respawn_timer.one_shot = true
add_child(respawn_timer)
func _process(_delta:float) -> void:
if state == FlagState.DROPPED and not respawn_timer.is_stopped():
waypoint.text = "%.2f s" % respawn_timer.time_left
else:
waypoint.text = ""
# @NOTE: simple way to deal with dynamic collision for distant objects
#func _physics_process(delta:float) -> void:
#var root := get_tree().current_scene
#var distance_to_camera := global_position.distance_to(get_viewport().get_camera_3d().global_position)
## Determine if terrain collision is active (or too far from camera when using dynamic terrain colliton mode)
#var using_fake_ground:bool = distance_to_camera > root.map.collision_radius
#if using_fake_ground:
#global_position.y = root.map.data.get_height(global_position) # ground_height
#linear_velocity.y = 0
func _on_body_entered(body: Node) -> void:
if body is Player:
assert(body.flag_holder)
body.flag_holder.grab(self)
## This method is called immediatly after the flag is grabbed
func _on_grabbed(by:Node) -> void:
assert(state < FlagState.TAKEN)
state = FlagState.TAKEN
freeze = true
area.set_deferred("monitoring", false)
last_carrier = by
hide()
func _on_dropped(by:Node) -> void:
assert(state == FlagState.TAKEN)
state = FlagState.DROPPED
freeze = false
# enable monitoring back after some delay
var _timer := get_tree().create_timer(1.)
_timer.timeout.connect(func() -> void: area.monitoring = true)
last_carrier = by
show()