mirror of
https://codeberg.org/sunder/sunder.git
synced 2026-07-15 05:54:36 +00:00
fix tests and move scripts
This commit is contained in:
parent
3ca9d4ec59
commit
a99473a35d
24 changed files with 128 additions and 128 deletions
87
scripts/entities/components/flag_holder.gd
Normal file
87
scripts/entities/components/flag_holder.gd
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# 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/>.
|
||||
@icon("res://assets/icons/flag_holder.svg")
|
||||
## This component is a unified system to manage [Flag] ownership, interaction
|
||||
## logic, and visual displays for the owning Entity.
|
||||
class_name FlagHolder extends Node3D
|
||||
|
||||
@export var throw_force:int = 16
|
||||
|
||||
## The radial progress bar to display the throwing force
|
||||
@onready var texture_progress:TextureProgressBar = $ThrowForce
|
||||
@onready var timer:Timer = $ThrowTimer
|
||||
|
||||
## The flag mesh to display when the owner carries the flag
|
||||
@onready var mesh: Node3D = $Mesh
|
||||
|
||||
## The [Flag] instance to hold
|
||||
var flag:Flag
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if is_visible_in_tree() and not timer.is_stopped():
|
||||
var time_elapsed: float = timer.wait_time - timer.time_left
|
||||
texture_progress.value = time_elapsed / timer.wait_time * 100
|
||||
|
||||
## This method grabs a [Flag] which set the mesh visible, holds a reference to it
|
||||
## and emits the [Flag.grabbed] signal to handle further state changes
|
||||
func grab(p_flag:Flag) -> void:
|
||||
if p_flag.state < Flag.FlagState.TAKEN:
|
||||
flag = p_flag
|
||||
flag.grabbed.emit(self)
|
||||
show()
|
||||
|
||||
func drop() -> void:
|
||||
if multiplayer.is_server() and flag and flag.state == flag.FlagState.TAKEN:
|
||||
flag.global_position = global_position
|
||||
flag.global_rotation.y = global_rotation.y
|
||||
# inheritance
|
||||
if owner is RigidBody3D:
|
||||
flag.global_rotation.y = -flag.global_basis.z.dot(owner.linear_velocity)
|
||||
flag.apply_impulse.call_deferred(flag.mass * owner.linear_velocity)
|
||||
flag.dropped.emit(self)
|
||||
flag = null
|
||||
|
||||
## This method displays throw force progress on the [HUD] of a client and upon release
|
||||
## it throws the [Flag]. It has no effect when holding no flag or flag is [member Flag.FlagState.TAKEN]
|
||||
func throw(pressed:bool) -> void:
|
||||
if flag and flag.state == flag.FlagState.TAKEN:
|
||||
if pressed:
|
||||
timer.start()
|
||||
texture_progress.visible = true
|
||||
else:
|
||||
if timer.is_stopped():
|
||||
texture_progress.visible = false
|
||||
return
|
||||
var time_elapsed:float = timer.wait_time - timer.time_left
|
||||
timer.stop()
|
||||
texture_progress.visible = false
|
||||
|
||||
var pumped_force:float = clampf(time_elapsed / timer.wait_time * throw_force, 6., throw_force)
|
||||
var throw_velocity:Vector3 = -global_basis.z * pumped_force
|
||||
|
||||
if owner:
|
||||
flag.global_position = owner.global_position
|
||||
flag.global_rotation.y = owner.camera.global_rotation.y
|
||||
throw_velocity = -owner.camera.global_basis.z * pumped_force + owner.linear_velocity
|
||||
else:
|
||||
flag.global_position = global_position
|
||||
flag.global_position.y = global_rotation.y
|
||||
|
||||
var impulse:Vector3 = flag.mass * throw_velocity
|
||||
flag.apply_impulse.call_deferred(impulse)
|
||||
|
||||
hide()
|
||||
flag.dropped.emit(self)
|
||||
flag = null
|
||||
1
scripts/entities/components/flag_holder.gd.uid
Normal file
1
scripts/entities/components/flag_holder.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cefq7u2fatx2e
|
||||
78
scripts/entities/components/health.gd
Normal file
78
scripts/entities/components/health.gd
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# 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/>.
|
||||
## This class defines a Health component for entities.
|
||||
class_name Health extends Area3D
|
||||
|
||||
## An entity with this component can be either dead or alive.
|
||||
enum HealthState {
|
||||
## The entity is dead
|
||||
DEAD,
|
||||
## The entity is alive ([member value] > 0)
|
||||
ALIVE,
|
||||
## The entity is wounded ([member value] < 128)
|
||||
DEGRADED,
|
||||
## Stop it, get help ([member value] < 32)
|
||||
CRITICAL
|
||||
}
|
||||
|
||||
@export var state : HealthState = HealthState.ALIVE
|
||||
@export var max_value:int = 255
|
||||
@export var value:int = 255:
|
||||
set(new_value):
|
||||
value = clampi(new_value, 0, max_value)
|
||||
changed.emit(value)
|
||||
|
||||
@export var shape:Shape3D:
|
||||
set(new_shape):
|
||||
shape = new_shape
|
||||
if is_node_ready():
|
||||
collider.shape = new_shape
|
||||
|
||||
@onready var collider: CollisionShape3D = $CollisionShape3D
|
||||
|
||||
## Emitted when a peer damaged this component too much.
|
||||
signal killed(by_peer_id:int)
|
||||
## Emitted when value is exhausted.
|
||||
signal exhausted()
|
||||
## Emitted when the value is changed.
|
||||
signal changed(new_value:int)
|
||||
|
||||
## Emitted when the health component is damaged.
|
||||
signal damaged(source: Node, target: Node, amount: int)
|
||||
|
||||
func _ready() -> void:
|
||||
# only collide with the layer 3 named "Damage", disable monitoring completely
|
||||
collision_layer = 0b00000000_00000000_00000000_00000100
|
||||
collision_mask = 0
|
||||
monitoring = false
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
if not collider.shape:
|
||||
return ["A shape must be provided for Health to be damaged. Please create or load a shape resource."]
|
||||
return []
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func damage(amount:int, by_peer_id:int) -> void:
|
||||
value -= amount
|
||||
if value == 0 and state != HealthState.DEAD:
|
||||
state = HealthState.DEAD
|
||||
killed.emit(by_peer_id)
|
||||
exhausted.emit(owner)
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func heal(amount:int = max_value) -> void:
|
||||
# add strictly positive amount to value
|
||||
value += clampi(amount, 1, max_value)
|
||||
state = HealthState.ALIVE
|
||||
1
scripts/entities/components/health.gd.uid
Normal file
1
scripts/entities/components/health.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://c2df3oj23o0wa
|
||||
23
scripts/entities/components/inventory.gd
Normal file
23
scripts/entities/components/inventory.gd
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
class_name Inventory extends Node3D
|
||||
|
||||
# weapons, tuple of 3 unique weapons
|
||||
# flag, object
|
||||
# grenades, typed array of max 2 grenade resources
|
||||
|
||||
@export_range(-1, 2) var cursor: int = -1
|
||||
|
||||
@export var loadout:Loadout
|
||||
|
||||
@onready var weapons:Node3D = $Weapons
|
||||
|
||||
func _ready() -> void:
|
||||
if loadout and multiplayer.is_server():
|
||||
for weapon in loadout.weapons:
|
||||
weapons.add_child(weapon.instantiate())
|
||||
|
||||
#func _on_primary() -> void:
|
||||
#equipped.trigger()
|
||||
|
||||
func clear() -> void:
|
||||
for child in weapons.get_children():
|
||||
child.queue_free()
|
||||
1
scripts/entities/components/inventory.gd.uid
Normal file
1
scripts/entities/components/inventory.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bih0550c0aj65
|
||||
5
scripts/entities/components/item.gd
Normal file
5
scripts/entities/components/item.gd
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
## This class defines an Item
|
||||
class_name Item extends Resource
|
||||
|
||||
@export var name:String
|
||||
@export var texture:Texture
|
||||
1
scripts/entities/components/item.gd.uid
Normal file
1
scripts/entities/components/item.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://borwqw2eea3uf
|
||||
37
scripts/entities/components/item_storage.gd
Normal file
37
scripts/entities/components/item_storage.gd
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
@tool
|
||||
class_name ItemStorage extends Resource
|
||||
|
||||
## An array of [Item]
|
||||
var items: Array[Item] = []
|
||||
|
||||
@export var size:int = 3:
|
||||
set = set_size
|
||||
|
||||
func set_size(new_size:int) -> void:
|
||||
size = new_size
|
||||
items.resize(size)
|
||||
notify_property_list_changed()
|
||||
|
||||
func _get_property_list() -> Array:
|
||||
var properties := []
|
||||
for i in range(items.size()):
|
||||
properties.append({
|
||||
"name": "slot_%d" % i,
|
||||
"type": TYPE_DICTIONARY,
|
||||
#"hint": PROPERTY_HINT_DICTIONARY_TYPE,
|
||||
"hint_string": "Item"
|
||||
})
|
||||
return properties
|
||||
|
||||
func _get(name:StringName) -> Item:
|
||||
if name.begins_with("slot_"):
|
||||
var index := name.get_slice("_", 1).to_int()
|
||||
return items[index]
|
||||
return null
|
||||
|
||||
func _set(name:StringName, value:Variant) -> bool:
|
||||
if name.begins_with("slot_"):
|
||||
var index := name.get_slice("_", 1).to_int()
|
||||
items[index] = value
|
||||
return true
|
||||
return false
|
||||
1
scripts/entities/components/item_storage.gd.uid
Normal file
1
scripts/entities/components/item_storage.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://c782w1epbdnps
|
||||
38
scripts/entities/components/loadout.gd
Normal file
38
scripts/entities/components/loadout.gd
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
@tool
|
||||
class_name Loadout extends Resource
|
||||
|
||||
@export_enum("Light", "Medium", "Heavy") var armor_type: int = 0
|
||||
|
||||
var weapons:Array[PackedScene] = []:
|
||||
set(value):
|
||||
weapons = value
|
||||
weapons.resize(3)
|
||||
notify_property_list_changed()
|
||||
|
||||
func _init() -> void:
|
||||
weapons.resize(3)
|
||||
notify_property_list_changed()
|
||||
|
||||
func _get_property_list() -> Array:
|
||||
var properties := []
|
||||
for i in range(weapons.size()):
|
||||
properties.append({
|
||||
"name": "weapon_%d" % i,
|
||||
"type": TYPE_OBJECT,
|
||||
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
||||
"hint_string": "PackedScene"
|
||||
})
|
||||
return properties
|
||||
|
||||
func _get(prop:StringName) -> Variant:
|
||||
if prop.begins_with("weapon_"):
|
||||
var index := prop.get_slice("_", 1).to_int()
|
||||
return weapons[index]
|
||||
return null
|
||||
|
||||
func _set(prop:StringName, value:Variant) -> bool:
|
||||
if prop.begins_with("weapon_"):
|
||||
var index := prop.get_slice("_", 1).to_int()
|
||||
weapons[index] = value
|
||||
return true
|
||||
return false
|
||||
1
scripts/entities/components/loadout.gd.uid
Normal file
1
scripts/entities/components/loadout.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bh5mmolmfwj7h
|
||||
84
scripts/entities/components/old_inventory.gd
Normal file
84
scripts/entities/components/old_inventory.gd
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# 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/>.
|
||||
@tool
|
||||
## This component allows its entity to interact with items.
|
||||
class_name OldInventory extends Node3D
|
||||
|
||||
signal selection_changed(index : int)
|
||||
signal selected(node: Node)
|
||||
signal unselected(node: Node)
|
||||
|
||||
const _max_items = 3
|
||||
|
||||
## This is the total capacity of the inventory.
|
||||
@export_range(0, _max_items) var capacity : int = _max_items:
|
||||
set(value):
|
||||
capacity = clamp(value, 1, _max_items)
|
||||
|
||||
@export_range(0, _max_items - 1) var cursor : int = 0:
|
||||
set = set_cursor
|
||||
|
||||
# Function to set the cursor position in the inventory
|
||||
func set_cursor(new_cursor: int) -> void:
|
||||
# Ensure the cursor is within the valid range
|
||||
if new_cursor >= 0 and new_cursor < _max_items:
|
||||
# emit unselected signal
|
||||
_emit_child(unselected, cursor)
|
||||
# update cursor position
|
||||
cursor = new_cursor
|
||||
# emit selected signal
|
||||
_emit_child(selected, cursor)
|
||||
|
||||
# helper function to emit the selected signal for the current cursor position
|
||||
func _emit_child(sig: Signal, idx: int) -> void:
|
||||
var child : Node = get_child(idx)
|
||||
if child:
|
||||
sig.emit(child)
|
||||
|
||||
func _ready() -> void:
|
||||
unselected.connect(_on_unselected)
|
||||
selected.connect(_on_selected)
|
||||
|
||||
func _on_unselected(node: Node) -> void:
|
||||
node.hide()
|
||||
|
||||
func _on_selected(node: Node) -> void:
|
||||
node.show()
|
||||
|
||||
## This method overrides [method Node.add_child] to make sure not to exceed
|
||||
## inventory capacity.
|
||||
func _add_child(node: Node, force_readable_name: bool = false,
|
||||
internal: InternalMode = InternalMode.INTERNAL_MODE_DISABLED) -> void:
|
||||
if get_child_count() <= capacity:
|
||||
super.add_child(node, force_readable_name, internal)
|
||||
else:
|
||||
push_warning("inventory is full")
|
||||
|
||||
## This method moves the [member selection] cursor to specified item index.
|
||||
func select(index : int) -> void:
|
||||
cursor = wrapi(index, 0, get_child_count())
|
||||
|
||||
## This method cycles through items in the inventory.
|
||||
func cycle(shift : int = 1) -> void:
|
||||
select(cursor + shift)
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func req_loadout(_index:int = 0) -> void:
|
||||
if is_multiplayer_authority():
|
||||
rcv_loadout.rpc_id(multiplayer.get_remote_sender_id(), "")
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func rcv_loadout(loadout:String) -> void:
|
||||
print(loadout)
|
||||
1
scripts/entities/components/old_inventory.gd.uid
Normal file
1
scripts/entities/components/old_inventory.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dagc5l6b73fr4
|
||||
Loading…
Add table
Add a link
Reference in a new issue