mirror of
https://gitlab.com/open-fpsz/open-fpsz.git
synced 2026-07-13 15:34:53 +00:00
👽 interstellar delivery
This commit is contained in:
parent
547c97bfba
commit
97c8292858
257 changed files with 7309 additions and 4637 deletions
|
|
@ -1,16 +0,0 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://qb5sf7awdeui"]
|
||||
|
||||
[ext_resource type="Script" path="res://entities/components/explosive_damage/explosive_damage_component.gd" id="1_alx3x"]
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_1htx7"]
|
||||
radius = 5.0
|
||||
|
||||
[node name="ExplosiveDamage" type="Area3D"]
|
||||
collision_mask = 13
|
||||
script = ExtResource("1_alx3x")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
shape = SubResource("SphereShape3D_1htx7")
|
||||
|
||||
[connection signal="area_shape_entered" from="." to="." method="_on_area_shape_entered"]
|
||||
[connection signal="body_entered" from="." to="." method="_on_body_entered"]
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name ExplosiveDamageComponent extends Area3D
|
||||
|
||||
## Emitted when the scale tween is finished
|
||||
signal finished
|
||||
|
||||
@export var damage : float = 89
|
||||
@export var impulse_force : int = 1000
|
||||
var damage_dealer : MatchParticipant
|
||||
|
||||
var _damaged_objects : Dictionary= {
|
||||
"bodies": [],
|
||||
"areas": []
|
||||
}
|
||||
|
||||
func _ready() -> void:
|
||||
# scale explosive damage aoe
|
||||
var tween : Tween = get_tree().create_tween()
|
||||
tween.set_trans(Tween.TRANS_SINE)
|
||||
tween.tween_property($CollisionShape3D,
|
||||
"scale", Vector3.ONE, .3).from(Vector3.ZERO)
|
||||
tween.tween_property($CollisionShape3D,
|
||||
"scale", Vector3.ZERO, .1).from(Vector3.ONE)
|
||||
tween.finished.connect(func() -> void: finished.emit(); queue_free())
|
||||
|
||||
func _on_body_entered(body: Node3D) -> void:
|
||||
if body is RigidBody3D and body not in _damaged_objects["bodies"]:
|
||||
var center_of_mass_global_position : Vector3 = body.center_of_mass + body.global_position
|
||||
var direction : Vector3 = ( center_of_mass_global_position - global_position).normalized()
|
||||
body.apply_central_impulse(direction * impulse_force)
|
||||
_damaged_objects["bodies"].append(body)
|
||||
|
||||
func _on_area_shape_entered(_area_rid: RID, area: Area3D, _area_shape_index: int, _local_shape_index: int) -> void:
|
||||
if area is HealthComponent and area not in _damaged_objects["areas"]:
|
||||
if not is_multiplayer_authority():
|
||||
return
|
||||
assert(damage_dealer)
|
||||
area.damage.rpc(damage, damage_dealer.player_id, damage_dealer.team_id)
|
||||
_damaged_objects["areas"].append(area)
|
||||
|
|
@ -15,40 +15,35 @@
|
|||
## This component allows its entity to interact with flags
|
||||
class_name FlagCarryComponent extends Node3D
|
||||
|
||||
@export var max_throw_speed : float = 10.0
|
||||
@export var throw_force : int = 12
|
||||
@export var mesh : Node3D
|
||||
|
||||
var _carried_flag : Flag
|
||||
|
||||
func _process(_delta : float) -> void:
|
||||
if _is_carrying():
|
||||
_carried_flag.global_position = global_position
|
||||
_carried_flag.global_rotation = Vector3(.0, global_rotation.y, .0)
|
||||
|
||||
func _is_carrying() -> bool:
|
||||
return _carried_flag != null
|
||||
var _flag : Flag
|
||||
|
||||
func grab(flag : Flag) -> void:
|
||||
if not _is_carrying():
|
||||
flag.grabbed.emit(owner)
|
||||
_carried_flag = flag
|
||||
if flag.state < Flag.FlagState.TAKEN:
|
||||
show()
|
||||
_flag = flag
|
||||
flag.grabbed.emit(self)
|
||||
|
||||
func drop(dropper : Player) -> void:
|
||||
_release(Vector3.ZERO, 0.0, dropper)
|
||||
func drop(inherited_velocity : Vector3 = Vector3.ZERO) -> void:
|
||||
_release(inherited_velocity, 0)
|
||||
|
||||
func throw(inherited_velocity : Vector3, dropper : Player) -> void:
|
||||
_release(inherited_velocity, max_throw_speed, dropper)
|
||||
func throw(inherited_velocity : Vector3 = Vector3.ZERO, _force: int = throw_force) -> void:
|
||||
_release(inherited_velocity, _force)
|
||||
|
||||
func _release(inherited_velocity : Vector3, throw_speed : float, dropper : Player) -> void:
|
||||
if not _is_carrying() or !is_inside_tree():
|
||||
return
|
||||
# update carried flag global rotation based on component global rotation
|
||||
_carried_flag.global_rotation = Vector3(.0, global_rotation.y, .0)
|
||||
_carried_flag.linear_velocity = inherited_velocity + (global_basis.z * throw_speed)
|
||||
# Throw the flag from some distance in front of the player to avoid regrabbing mid-air
|
||||
_carried_flag.global_position = owner.global_position + (global_basis.z * 1.7)
|
||||
_carried_flag.dropped.emit(dropper)
|
||||
_carried_flag = null
|
||||
func _release(inherited_velocity : Vector3 = Vector3.ZERO, _force: int = throw_force) -> void:
|
||||
if not _flag or !is_inside_tree(): return
|
||||
_flag.dropped.emit(self)
|
||||
var impulse : Vector3 = _flag.mass * (inherited_velocity + global_basis.z * _force)
|
||||
_flag.apply_central_impulse(impulse) # wake up the flag
|
||||
# @NOTE: throw the flag from some distance in front of the player to avoid regrabbing mid-air
|
||||
# @FIXME: there is a small chance to send the flag under the map
|
||||
_flag.global_position = global_position + (global_basis.z * 1.7)
|
||||
# rotate carried flag based on flag carry global rotation
|
||||
_flag.global_rotation = Vector3(.0, global_rotation.y, .0)
|
||||
# drop reference to flag
|
||||
_flag = null
|
||||
# hide carried flag mesh
|
||||
hide()
|
||||
|
||||
|
|
|
|||
59
entities/components/health.gd
Normal file
59
entities/components/health.gd
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU 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 { DEAD, ALIVE }
|
||||
|
||||
## 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 updated.
|
||||
signal updated(new_value:int)
|
||||
|
||||
## Emitted when the health component is damaged.
|
||||
signal damaged(source: Node, target: Node, amount: int)
|
||||
|
||||
@export var collider:CollisionShape3D
|
||||
@export var max_value:int = 255
|
||||
@export var value:int = 255:
|
||||
set = set_value
|
||||
@export var state : HealthState = HealthState.ALIVE
|
||||
|
||||
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 set_value(new_value:int) -> void:
|
||||
value = clampi(new_value, 0, max_value)
|
||||
updated.emit(value)
|
||||
|
||||
@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,52 +0,0 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name HealthComponent extends Area3D
|
||||
|
||||
@export var match_participant : MatchParticipant
|
||||
|
||||
@export var max_health : int = 255
|
||||
@export var health : int = 255:
|
||||
set(new_health):
|
||||
health = new_health
|
||||
health_changed.emit(new_health)
|
||||
|
||||
signal health_zeroed(killer_id : int)
|
||||
signal health_changed(new_health : int)
|
||||
|
||||
func _ready() -> void:
|
||||
# only collide with the "Damage" layer, disable monitoring completely
|
||||
collision_layer = 0b00000000_00000000_00000000_00000100
|
||||
monitoring = false
|
||||
collision_mask = 0
|
||||
call_deferred("heal_full")
|
||||
|
||||
@rpc("call_local", "reliable")
|
||||
func damage(amount : int, damage_dealer_player_id : int, damage_dealer_team_id : int) -> void:
|
||||
if (damage_dealer_team_id == match_participant.team_id) and (damage_dealer_player_id != match_participant.player_id):
|
||||
return
|
||||
|
||||
health = clampi(health - amount, 0, max_health)
|
||||
if health == 0:
|
||||
health_zeroed.emit(damage_dealer_player_id)
|
||||
|
||||
@rpc("call_local", "reliable")
|
||||
func _heal(amount : int) -> void:
|
||||
health = clampi(health + amount, 0, max_health)
|
||||
|
||||
func heal_full() -> void:
|
||||
if not is_multiplayer_authority():
|
||||
return
|
||||
|
||||
_heal.rpc(max_health)
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
# This file is part of open-fpsz.
|
||||
# copyright (c) 2024 - anyreso & open-fpsz contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -12,63 +13,65 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
## This component allows its entity to interact with pickable nodes
|
||||
@tool
|
||||
## This component allows its entity to hold [Weapon] nodes.
|
||||
class_name Inventory 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 inventory.
|
||||
@export_range(0, _max_items) var capacity : int = _max_items:
|
||||
set(value):
|
||||
capacity = clamp(value, 1, _max_items)
|
||||
|
||||
@export var selected : Node3D
|
||||
@export_range(0, _max_items - 1) var cursor : int = 0:
|
||||
set = set_cursor
|
||||
|
||||
signal selection_changed(selected : Node3D, index : int)
|
||||
# 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:
|
||||
# make sure a child is selected if any
|
||||
if not selected and get_child_count() > 0:
|
||||
selected = get_child(0)
|
||||
# make sure this inventory is linked to its owner
|
||||
if not owner.inventory:
|
||||
owner.inventory = self
|
||||
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] method to make sure not to exceed the inventory capacity
|
||||
func _add_child(node: Node, force_readable_name: bool = false, internal: InternalMode = InternalMode.INTERNAL_MODE_DISABLED) -> void:
|
||||
## 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 cycles through the items in the inventory. If an item is already
|
||||
## [member selected], it hides the current item, shifts by the specified number
|
||||
## of items, and shows the new selected item. If no item is selected, it selects
|
||||
## the first item. If the inventory is empty, it displays a warning message.
|
||||
func cycle(shift : int = 1) -> void:
|
||||
var child_count : int = get_child_count()
|
||||
if selected:
|
||||
selected.hide()
|
||||
var index : int = wrapi(selected.get_index() + shift, 0, child_count)
|
||||
selected = get_child(index)
|
||||
selection_changed.emit(selected, index)
|
||||
selected.show()
|
||||
elif child_count > 0:
|
||||
selected = get_child(0)
|
||||
selection_changed.emit(selected, 0)
|
||||
selected.show()
|
||||
else:
|
||||
push_warning("inventory is empty")
|
||||
|
||||
## This method moves the [member selected] cursor to a specific inventory item.
|
||||
## This method moves the [member selection] cursor to specified item index.
|
||||
func select(index : int) -> void:
|
||||
if selected:
|
||||
selected.hide()
|
||||
selected = get_child(index)
|
||||
if selected:
|
||||
selection_changed.emit(selected, index)
|
||||
selected.show()
|
||||
else:
|
||||
selected = get_child(index)
|
||||
if selected:
|
||||
selection_changed.emit(selected, index)
|
||||
selected.show()
|
||||
cursor = wrapi(index, 0, get_child_count())
|
||||
|
||||
## This method cycles through items in the inventory.
|
||||
func cycle(shift : int = 1) -> void:
|
||||
select(cursor + shift)
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
class_name MatchParticipant extends Node
|
||||
|
||||
signal player_id_changed(new_player_id : int)
|
||||
signal username_changed(new_username : String)
|
||||
signal team_id_changed(new_team_id : int)
|
||||
|
||||
@export var username : String = "Newblood":
|
||||
set(value):
|
||||
username = value
|
||||
username_changed.emit(username)
|
||||
|
||||
@export var player_id : int:
|
||||
set(value):
|
||||
player_id = value
|
||||
player_id_changed.emit(player_id)
|
||||
|
||||
@export var team_id : int = 1:
|
||||
set(value):
|
||||
team_id = value
|
||||
team_id_changed.emit(team_id)
|
||||
|
|
@ -14,37 +14,45 @@
|
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name Flag extends RigidBody3D
|
||||
|
||||
signal grabbed(grabber : Player)
|
||||
signal regrabbed(grabber : Player)
|
||||
signal dropped(dropper : Player)
|
||||
signal grabbed(carry: FlagCarryComponent)
|
||||
signal dropped(carry: FlagCarryComponent)
|
||||
|
||||
enum FlagState { ON_STAND, DROPPED, TAKEN }
|
||||
enum FlagState {
|
||||
ON_STAND,
|
||||
DROPPED,
|
||||
TAKEN
|
||||
}
|
||||
|
||||
@export var state : FlagState = FlagState.ON_STAND
|
||||
|
||||
@onready var area : Area3D = $GripArea3D
|
||||
@onready var mesh : Node3D = $Mesh
|
||||
|
||||
var last_carrier : Player = null
|
||||
var last_carrier : FlagCarryComponent
|
||||
|
||||
func _ready() -> void:
|
||||
area.body_entered.connect(_on_body_entered)
|
||||
|
||||
func _on_grabbed(grabber : Player) -> void:
|
||||
if state != FlagState.TAKEN:
|
||||
hide()
|
||||
state = FlagState.TAKEN
|
||||
if (last_carrier == null) or (grabber != last_carrier):
|
||||
last_carrier = grabber
|
||||
else:
|
||||
regrabbed.emit(grabber)
|
||||
|
||||
func _on_dropped(_dropper : Player) -> void:
|
||||
if state == FlagState.TAKEN:
|
||||
show()
|
||||
state = FlagState.DROPPED
|
||||
grabbed.connect(_on_grabbed)
|
||||
dropped.connect(_on_dropped)
|
||||
|
||||
func _on_body_entered(body: Node) -> void:
|
||||
if body is Player:
|
||||
assert(body.flag_carry_component)
|
||||
body.flag_carry_component.grab(self)
|
||||
if state < FlagState.TAKEN:
|
||||
body.flag_carry_component.grab(self)
|
||||
hide()
|
||||
|
||||
func _on_grabbed(_carry: FlagCarryComponent) -> void:
|
||||
assert(state < FlagState.TAKEN)
|
||||
hide()
|
||||
state = FlagState.TAKEN
|
||||
sleeping = true
|
||||
area.set_deferred("monitoring", false)
|
||||
|
||||
func _on_dropped(carry: FlagCarryComponent) -> void:
|
||||
assert(state == FlagState.TAKEN)
|
||||
sleeping = false
|
||||
area.set_deferred("monitoring", true)
|
||||
state = FlagState.DROPPED
|
||||
last_carrier = carry
|
||||
show()
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ properties/2/replication_mode = 2
|
|||
properties/3/path = NodePath(".:state")
|
||||
properties/3/spawn = true
|
||||
properties/3/replication_mode = 2
|
||||
properties/4/path = NodePath(".:sleeping")
|
||||
properties/4/spawn = true
|
||||
properties/4/replication_mode = 2
|
||||
|
||||
[node name="Flag" type="RigidBody3D"]
|
||||
collision_layer = 8
|
||||
|
|
@ -55,6 +58,3 @@ foreground_color = Color(0, 0.75, 0.75, 1)
|
|||
|
||||
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
|
||||
replication_config = SubResource("SceneReplicationConfig_lpijf")
|
||||
|
||||
[connection signal="dropped" from="." to="." method="_on_dropped"]
|
||||
[connection signal="grabbed" from="." to="." method="_on_grabbed"]
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@ importer="texture"
|
|||
type="CompressedTexture2D"
|
||||
uid="uid://dmf12llra7aq5"
|
||||
path.s3tc="res://.godot/imported/Particle01.png-789728e4e363d58f11747b3cf3c5d5a3.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/Particle01.png-789728e4e363d58f11747b3cf3c5d5a3.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://entities/player/assets/jetpackfx/Particle01.png"
|
||||
dest_files=["res://.godot/imported/Particle01.png-789728e4e363d58f11747b3cf3c5d5a3.s3tc.ctex"]
|
||||
dest_files=["res://.godot/imported/Particle01.png-789728e4e363d58f11747b3cf3c5d5a3.s3tc.ctex", "res://.godot/imported/Particle01.png-789728e4e363d58f11747b3cf3c5d5a3.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@ importer="texture"
|
|||
type="CompressedTexture2D"
|
||||
uid="uid://ct1v5iadtpadm"
|
||||
path.s3tc="res://.godot/imported/smoke_01.png-5c3d69bc74b2f317eac64d37cd67aed3.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/smoke_01.png-5c3d69bc74b2f317eac64d37cd67aed3.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://entities/player/assets/jetpackfx/smoke_01.png"
|
||||
dest_files=["res://.godot/imported/smoke_01.png-5c3d69bc74b2f317eac64d37cd67aed3.s3tc.ctex"]
|
||||
dest_files=["res://.godot/imported/smoke_01.png-5c3d69bc74b2f317eac64d37cd67aed3.s3tc.ctex", "res://.godot/imported/smoke_01.png-5c3d69bc74b2f317eac64d37cd67aed3.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@ importer="texture"
|
|||
type="CompressedTexture2D"
|
||||
uid="uid://doxo4vfn0bjlp"
|
||||
path.s3tc="res://.godot/imported/smoke_02.png-a5343a97ff1cefeebcd82e41218739ca.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/smoke_02.png-a5343a97ff1cefeebcd82e41218739ca.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://entities/player/assets/jetpackfx/smoke_02.png"
|
||||
dest_files=["res://.godot/imported/smoke_02.png-a5343a97ff1cefeebcd82e41218739ca.s3tc.ctex"]
|
||||
dest_files=["res://.godot/imported/smoke_02.png-a5343a97ff1cefeebcd82e41218739ca.s3tc.ctex", "res://.godot/imported/smoke_02.png-a5343a97ff1cefeebcd82e41218739ca.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ importer="texture"
|
|||
type="CompressedTexture2D"
|
||||
uid="uid://ia3bdpe4rm1m"
|
||||
path.bptc="res://.godot/imported/vanguard_0.png-601f36bc664114e126d425d5f45085ef.bptc.ctex"
|
||||
path.astc="res://.godot/imported/vanguard_0.png-601f36bc664114e126d425d5f45085ef.astc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={}
|
||||
|
|
@ -13,7 +14,7 @@ generator_parameters={}
|
|||
[deps]
|
||||
|
||||
source_file="res://entities/player/assets/vanguard_0.png"
|
||||
dest_files=["res://.godot/imported/vanguard_0.png-601f36bc664114e126d425d5f45085ef.bptc.ctex"]
|
||||
dest_files=["res://.godot/imported/vanguard_0.png-601f36bc664114e126d425d5f45085ef.bptc.ctex", "res://.godot/imported/vanguard_0.png-601f36bc664114e126d425d5f45085ef.astc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ importer="texture"
|
|||
type="CompressedTexture2D"
|
||||
uid="uid://bvgfmpb2l1juf"
|
||||
path.bptc="res://.godot/imported/vanguard_1.png-39b5712c4c4119a42b3540a159f8b3f2.bptc.ctex"
|
||||
path.astc="res://.godot/imported/vanguard_1.png-39b5712c4c4119a42b3540a159f8b3f2.astc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={}
|
||||
|
|
@ -13,7 +14,7 @@ generator_parameters={}
|
|||
[deps]
|
||||
|
||||
source_file="res://entities/player/assets/vanguard_1.png"
|
||||
dest_files=["res://.godot/imported/vanguard_1.png-39b5712c4c4119a42b3540a159f8b3f2.bptc.ctex"]
|
||||
dest_files=["res://.godot/imported/vanguard_1.png-39b5712c4c4119a42b3540a159f8b3f2.bptc.ctex", "res://.godot/imported/vanguard_1.png-39b5712c4c4119a42b3540a159f8b3f2.astc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ importer="texture"
|
|||
type="CompressedTexture2D"
|
||||
uid="uid://vtrbc3eja3df"
|
||||
path.bptc="res://.godot/imported/vanguard_2.png-986e3665904b0d4758f584d5d3b7b726.bptc.ctex"
|
||||
path.astc="res://.godot/imported/vanguard_2.png-986e3665904b0d4758f584d5d3b7b726.astc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={}
|
||||
|
|
@ -13,7 +14,7 @@ generator_parameters={}
|
|||
[deps]
|
||||
|
||||
source_file="res://entities/player/assets/vanguard_2.png"
|
||||
dest_files=["res://.godot/imported/vanguard_2.png-986e3665904b0d4758f584d5d3b7b726.bptc.ctex"]
|
||||
dest_files=["res://.godot/imported/vanguard_2.png-986e3665904b0d4758f584d5d3b7b726.bptc.ctex", "res://.godot/imported/vanguard_2.png-986e3665904b0d4758f584d5d3b7b726.astc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
|
|||
|
|
@ -12,79 +12,81 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
extends Node
|
||||
class_name PlayerInputController extends Node
|
||||
|
||||
@export var jetting : bool = false
|
||||
@export var skiing : bool = false
|
||||
@export var direction : Vector2 = Vector2.ZERO
|
||||
@export var camera_rotation : Vector2
|
||||
@export var jetting:bool = false
|
||||
@export var skiing:bool = false
|
||||
@export var direction:Vector2
|
||||
@export var camera_rotation:Vector2
|
||||
|
||||
signal jumped
|
||||
signal fired_primary
|
||||
signal throwed_flag
|
||||
signal jump
|
||||
signal primary(pressed: bool)
|
||||
signal throw(pressed: bool)
|
||||
|
||||
var _mouse_sensitivity:float = .0
|
||||
|
||||
func _enter_tree() -> void:
|
||||
update_multiplayer_authority(0)
|
||||
|
||||
func update_multiplayer_authority(player_id : int) -> void:
|
||||
set_multiplayer_authority(player_id)
|
||||
# @NOTE: This is a workaround for player node being too integrated with the
|
||||
# multiplayer layer and blocks player inputs in singleplayer if not set.
|
||||
set_multiplayer_authority(1)
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if is_multiplayer_authority():
|
||||
var mouse_mode : Input.MouseMode = Input.get_mouse_mode()
|
||||
var mouse_mode: Input.MouseMode = Input.get_mouse_mode()
|
||||
direction = Input.get_vector("left", "right", "forward", "backward")
|
||||
jetting = Input.is_action_pressed("secondary")
|
||||
skiing = Input.is_action_pressed("ski")
|
||||
# isolate mouse events
|
||||
if event is InputEventMouseMotion:
|
||||
if mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||
# update camera with mouse position relative to last frame
|
||||
_update_camera(event.relative)
|
||||
if event is InputEventMouseButton:
|
||||
elif event is InputEventMouseButton:
|
||||
# @NOTE: there could be a control setting for direction
|
||||
if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed:
|
||||
_cycle.rpc(1)
|
||||
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed:
|
||||
_cycle.rpc(-1)
|
||||
if event is InputEventKey:
|
||||
elif event is InputEventKey:
|
||||
match event.keycode:
|
||||
KEY_1: _select.rpc(0)
|
||||
KEY_2: _select.rpc(1)
|
||||
KEY_3: _select.rpc(2)
|
||||
direction = Input.get_vector("left", "right", "forward", "backward")
|
||||
if Input.is_action_just_pressed("jump_and_jet"):
|
||||
if event.is_action("primary"):
|
||||
_primary.rpc(event.pressed)
|
||||
if Input.is_action_just_pressed("secondary"):
|
||||
_jump.rpc()
|
||||
if Input.is_action_just_pressed("fire_primary"):
|
||||
_fire_primary.rpc()
|
||||
if Input.is_action_just_pressed("throw_flag"):
|
||||
_throw_flag.rpc()
|
||||
jetting = Input.is_action_pressed("jump_and_jet")
|
||||
skiing = Input.is_action_pressed("ski")
|
||||
|
||||
func _update_camera(relative_motion : Vector2) -> void:
|
||||
if event.is_action("throw") and not event.echo:
|
||||
_throw.rpc(event.pressed)
|
||||
|
||||
func _update_camera(relative_motion:Vector2) -> void:
|
||||
# reverse y axis
|
||||
if Settings.get_value("controls", "inverted_y_axis"):
|
||||
relative_motion.y *= -1.0
|
||||
_mouse_sensitivity = Settings.get_value("controls", "mouse_sensitivity")
|
||||
# clamp vertical rotation (head motion)
|
||||
camera_rotation.y -= relative_motion.y * Settings.get_value("controls", "mouse_sensitivity") / 100
|
||||
camera_rotation.y = clamp(camera_rotation.y, deg_to_rad(-90.0), deg_to_rad(90.0))
|
||||
camera_rotation.x -= relative_motion.y * _mouse_sensitivity / 100
|
||||
camera_rotation.x = clamp(camera_rotation.x, deg_to_rad(-90.0), deg_to_rad(90.0))
|
||||
# wrap horizontal rotation (to prevent accumulation)
|
||||
camera_rotation.x -= relative_motion.x * Settings.get_value("controls", "mouse_sensitivity") / 100
|
||||
camera_rotation.x = wrapf(camera_rotation.x, deg_to_rad(0.0), deg_to_rad(360.0))
|
||||
camera_rotation.y -= relative_motion.x * _mouse_sensitivity / 100
|
||||
camera_rotation.y = wrapf(camera_rotation.y, deg_to_rad(0.0), deg_to_rad(360.0))
|
||||
|
||||
@rpc("call_local", "reliable")
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func _jump() -> void:
|
||||
jumped.emit()
|
||||
jump.emit()
|
||||
|
||||
@rpc("call_local", "reliable")
|
||||
func _fire_primary() -> void:
|
||||
fired_primary.emit()
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func _primary(pressed: bool) -> void:
|
||||
primary.emit(pressed)
|
||||
|
||||
@rpc("call_local", "reliable")
|
||||
func _cycle(shift : int) -> void:
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func _cycle(shift:int) -> void:
|
||||
%Inventory.cycle(shift)
|
||||
|
||||
@rpc("call_local", "reliable")
|
||||
func _select(index : int) -> void:
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func _select(index:int) -> void:
|
||||
%Inventory.select(index)
|
||||
|
||||
@rpc("call_local", "reliable")
|
||||
func _throw_flag() -> void:
|
||||
throwed_flag.emit()
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func _throw(pressed: bool) -> void:
|
||||
throw.emit(pressed)
|
||||
|
|
|
|||
|
|
@ -14,140 +14,230 @@
|
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name Player extends RigidBody3D
|
||||
|
||||
enum PlayerState { PLAYER_ALIVE, PLAYER_DEAD }
|
||||
signal killed(victim: Player, killer:int)
|
||||
|
||||
signal died(player : Player, killer_id : int)
|
||||
## Emitted when a source wants to damage this body
|
||||
signal damage(source: Node, target: Node, amount: int)
|
||||
|
||||
@export var iff : IFF
|
||||
@export var health_component : HealthComponent
|
||||
@export var flag_carry_component : FlagCarryComponent
|
||||
@export var walkable_surface_sensor : ShapeCast3D
|
||||
@export var match_participant : MatchParticipant
|
||||
## Emitted when the player respawns, see [member Player.respawn].
|
||||
signal respawned(player: Player)
|
||||
|
||||
## The inventory component can store up to [member Inventory.slots] objects. It
|
||||
## also has specific slots to hold grenades, packs and a flag.
|
||||
@export var inventory : Inventory
|
||||
|
||||
@export var tp_mesh : Vanguard
|
||||
@export var iff:IFF
|
||||
## This is the player [Health] component.
|
||||
@export var health:Health
|
||||
@export var flag_carry_component:FlagCarryComponent
|
||||
@export var walkable_surface_sensor:ShapeCast3D
|
||||
@export var hud:HUD
|
||||
## The inventory component can store up to [member Inventory.slots] nodes.
|
||||
@export var inventory:Inventory
|
||||
@export var tp_mesh:Vanguard
|
||||
@export var third_person:Node3D
|
||||
@export var pivot : Node3D
|
||||
@export var camera : Camera3D
|
||||
|
||||
@export_category("Parameters")
|
||||
@export var ground_speed : float = 48 / 3.6 # m/s
|
||||
@export var aerial_control_force : int = 400
|
||||
@export var jump_height : float = 2.0
|
||||
@export var max_floor_angle : float = 60
|
||||
@export var ground_speed:float = 48 / 3.6 # m/s
|
||||
@export var aerial_control_force:int = 400
|
||||
@export var jump_height:float = 1.0
|
||||
@export var max_floor_angle:float = 60
|
||||
|
||||
@export_group("Jetpack")
|
||||
@export var energy: float = 100.0
|
||||
@export var energy_charge_rate : float = 20 # energy per second
|
||||
@export var energy_drain_rate : float = 25 # energy per second
|
||||
@export var energy_max : float = 100.
|
||||
@export var jetpack_force_factor : float = 2.
|
||||
@export var jetpack_horizontal_force : float = 600
|
||||
@export var jetpack_vertical_force : float = 800
|
||||
@export var energy_charge_rate:float = 25 # energy per second
|
||||
@export var energy_drain_rate:float = 25 # energy per second
|
||||
@export var energy_max:float = 100.
|
||||
@export var jetpack_force_factor:float = 2.
|
||||
@export var jetpack_horizontal_force:float = 600
|
||||
@export var jetpack_vertical_force:float = 1200
|
||||
|
||||
@export_group("State")
|
||||
@export var player_state : PlayerState = PlayerState.PLAYER_ALIVE
|
||||
@export var input : Node
|
||||
@export var input: PlayerInputController
|
||||
|
||||
@onready var camera : Camera3D = %Pivot/Camera3D
|
||||
@onready var hud : CanvasLayer = $HUD
|
||||
@onready var animation_player : AnimationPlayer = $AnimationPlayer
|
||||
@onready var collision_shape : CollisionShape3D = $CollisionShape3D
|
||||
@onready var jetpack_particles : Array = $ThirdPerson/Mesh/JetpackFX.get_children()
|
||||
@onready var animation_player:AnimationPlayer = $AnimationPlayer
|
||||
@onready var collision_shape:CollisionShape3D = $CollisionShape3D
|
||||
@onready var _jetpack_particles:Array = tp_mesh.get_node("JetpackFX").get_children()
|
||||
|
||||
var g : float = ProjectSettings.get_setting("physics/3d/default_gravity") # in m/s²
|
||||
var gravity : Vector3 = g * ProjectSettings.get_setting("physics/3d/default_gravity_vector")
|
||||
var _jumping : bool = false
|
||||
var g:float = ProjectSettings.get_setting("physics/3d/default_gravity") # in m/s²
|
||||
var gravity:Vector3 = g * ProjectSettings.get_setting("physics/3d/default_gravity_vector")
|
||||
var _jumping:bool = false
|
||||
|
||||
static var pawn_player : Player
|
||||
static var pawn_player:Player
|
||||
|
||||
@export var username:String = "Newblood":
|
||||
set = set_username
|
||||
|
||||
@export var peer_id:int = 1:
|
||||
set = set_peer_id
|
||||
|
||||
@export var team_id:int = -1
|
||||
|
||||
signal username_changed(new_username:int)
|
||||
signal peer_id_changed(new_peer_id:int)
|
||||
|
||||
func set_username(new_username:String) -> void:
|
||||
username = new_username
|
||||
username_changed.emit(username)
|
||||
|
||||
func set_peer_id(new_peer_id:int) -> void:
|
||||
remove_from_group(str(peer_id))
|
||||
peer_id = new_peer_id
|
||||
add_to_group(str(peer_id))
|
||||
peer_id_changed.emit(peer_id)
|
||||
|
||||
# Maximum duration for pumping force when throwing a flag, in seconds.
|
||||
@export var throw_duration_max := 1.2
|
||||
var throw_timer := Timer.new()
|
||||
|
||||
func _ready() -> void:
|
||||
match_participant.player_id_changed.connect(_setup_pawn)
|
||||
match_participant.player_id_changed.connect(input.update_multiplayer_authority)
|
||||
match_participant.username_changed.connect(iff._on_username_changed)
|
||||
input.set_multiplayer_authority(peer_id)
|
||||
|
||||
health_component.health_changed.connect(hud.health_bar.set_value)
|
||||
health_component.health_changed.connect(iff.health_bar.set_value)
|
||||
health_component.health_changed.emit(health_component.health)
|
||||
health_component.health_zeroed.connect(die)
|
||||
username_changed.connect(iff.set_username)
|
||||
username_changed.emit(username) # trigger initial signal
|
||||
|
||||
inventory.selection_changed.connect(_on_inventory_selection_changed)
|
||||
health.updated.connect(hud.health_bar.set_value)
|
||||
health.updated.connect(iff.health_bar.set_value)
|
||||
health.killed.connect(_on_killed)
|
||||
|
||||
input.jump.connect(_jump)
|
||||
input.throw.connect(_on_throw)
|
||||
throw_timer.one_shot = true
|
||||
add_child(throw_timer)
|
||||
|
||||
# bind inventory
|
||||
inventory.selected.connect(func(node: Node) -> void:
|
||||
if node is Weapon:
|
||||
if not input.primary.is_connected(node._on_primary):
|
||||
input.primary.connect(node._on_primary)
|
||||
if not node.ammo_changed.is_connected(hud._on_ammo_changed):
|
||||
node.ammo_changed.connect(hud._on_ammo_changed)
|
||||
node.ammo_changed.emit(node.ammo)
|
||||
)
|
||||
|
||||
input.fired_primary.connect(_trigger)
|
||||
input.jumped.connect(_jump)
|
||||
input.throwed_flag.connect(_throw_flag)
|
||||
|
||||
func _process(_delta : float) -> void:
|
||||
%Pivot.global_transform.basis = Basis.from_euler(Vector3(input.camera_rotation.y, input.camera_rotation.x, 0.0))
|
||||
if not _is_pawn():
|
||||
tp_mesh.global_transform.basis = Basis.from_euler(Vector3(.0, input.camera_rotation.x + PI, 0.0))
|
||||
if match_participant and pawn_player:
|
||||
if pawn_player.match_participant.team_id == match_participant.team_id:
|
||||
iff.fill = Color.GREEN
|
||||
else:
|
||||
iff.fill = Color.RED
|
||||
|
||||
func _physics_process(delta : float) -> void:
|
||||
_update_jetpack_energy(delta)
|
||||
|
||||
func _setup_pawn(_new_player_id : int) -> void:
|
||||
inventory.unselected.connect(func(node: Node) -> void:
|
||||
if node is Weapon:
|
||||
if input.primary.is_connected(node._on_primary):
|
||||
input.primary.disconnect(node._on_primary)
|
||||
if node is AutomaticWeapon: node._on_primary(false) # release trigger
|
||||
if node.ammo_changed.is_connected(hud._on_ammo_changed):
|
||||
node.ammo_changed.disconnect(hud._on_ammo_changed)
|
||||
hud.ammo_label.text = ""
|
||||
)
|
||||
|
||||
inventory.select(0)
|
||||
|
||||
if Game.type is Singleplayer:
|
||||
damage.connect(
|
||||
func(_source: Node, target: Node, amount: int) -> void:
|
||||
target.health.damage(amount, 0))
|
||||
|
||||
if _is_pawn():
|
||||
pawn_player = self
|
||||
camera.current = true
|
||||
camera.fov = Settings.get_value("video", "fov")
|
||||
pawn_player = self
|
||||
iff.hide()
|
||||
# hide hud on pawn when scoreboard is visible in multiplayer
|
||||
if Game.type is Multiplayer:
|
||||
Game.type.scoreboard.visibility_changed.connect(
|
||||
func() -> void: hud.set_visible(!Game.type.scoreboard.visible))
|
||||
# forward this peer env settings to current viewport world env
|
||||
var world: World3D = get_viewport().find_world_3d()
|
||||
if not world.environment:
|
||||
world.environment = Game.environment
|
||||
world.environment.sdfgi_enabled = Game.environment.sdfgi_enabled
|
||||
world.environment.glow_enabled = Game.environment.glow_enabled
|
||||
world.environment.ssao_enabled = Game.environment.ssao_enabled
|
||||
world.environment.ssr_enabled = Game.environment.ssr_enabled
|
||||
world.environment.ssr_max_steps = Game.environment.ssr_max_steps
|
||||
world.environment.ssil_enabled = Game.environment.ssil_enabled
|
||||
world.environment.volumetric_fog_enabled = Game.environment.volumetric_fog_enabled
|
||||
else:
|
||||
$ThirdPerson.show()
|
||||
third_person.show()
|
||||
%Inventory.hide()
|
||||
hud.hide()
|
||||
|
||||
func _process(_delta:float) -> void:
|
||||
if not _is_pawn():
|
||||
if Game.type is Multiplayer and Game.type.mode == Multiplayer.Mode.FREE_FOR_ALL:
|
||||
iff.fill = Color.RED
|
||||
elif is_instance_valid(pawn_player) and team_id:
|
||||
iff.fill = Color.GREEN if team_id == pawn_player.team_id else Color.RED
|
||||
_update_third_person_animations()
|
||||
|
||||
if not is_alive():
|
||||
return
|
||||
|
||||
# compute target rotation from input.camera_rotation
|
||||
var target_euler := Vector3(input.camera_rotation.x, input.camera_rotation.y, 0.0)
|
||||
# smoothly interpolate rotation of pivot node towards target rotation
|
||||
pivot.global_transform.basis = pivot.global_transform.basis.slerp(Basis.from_euler(target_euler), .6)
|
||||
|
||||
if not _is_pawn():
|
||||
# compute target rotation from input.camera_rotation
|
||||
var tp_target_euler := Vector3(.0, input.camera_rotation.y + PI, 0.0)
|
||||
# smoothly interpolate rotation of third person node towards target rotation
|
||||
tp_mesh.global_transform.basis = tp_mesh.global_transform.basis.slerp(Basis.from_euler(tp_target_euler), .6)
|
||||
else:
|
||||
if hud.throw_progress.is_visible_in_tree():
|
||||
var time_elapsed: float = throw_duration_max - throw_timer.time_left
|
||||
hud.throw_progress.set_value(time_elapsed / throw_duration_max * 100)
|
||||
|
||||
func _physics_process(delta:float) -> void:
|
||||
_update_jetpack_energy(delta)
|
||||
|
||||
func _is_pawn() -> bool:
|
||||
var peer_is_pawn : bool = multiplayer.get_unique_id() == match_participant.player_id
|
||||
return Global.type is Singleplayer or peer_is_pawn
|
||||
if Game.type is Multiplayer:
|
||||
if Game.type.is_peer_connected():
|
||||
return multiplayer.get_unique_id() == peer_id
|
||||
else:
|
||||
queue_free()
|
||||
return true
|
||||
|
||||
func _on_throw(pressed: bool) -> void:
|
||||
if pressed:
|
||||
throw_timer.start(throw_duration_max)
|
||||
hud.throw_progress.visible = true
|
||||
else:
|
||||
var time_left: float = throw_timer.time_left
|
||||
throw_timer.stop()
|
||||
var time_elapsed: float = throw_duration_max - time_left
|
||||
var throw_force: float = \
|
||||
time_elapsed / throw_duration_max * flag_carry_component.throw_force
|
||||
flag_carry_component.throw(linear_velocity,
|
||||
clamp(throw_force, 5., flag_carry_component.throw_force))
|
||||
hud.throw_progress.visible = false
|
||||
|
||||
|
||||
# @NOTE: this method works only because `tp_mesh` duplicates weapons meshes from the inventory
|
||||
func _on_inventory_selection_changed(_selected : Node3D, index : int) -> void:
|
||||
func _on_inventory_selection_changed(_selected:Node3D, index:int) -> void:
|
||||
# hide any visible weapon
|
||||
for child in tp_mesh.hand_attachment.get_children():
|
||||
child.hide()
|
||||
# get corresponding selected weapon mesh for third person
|
||||
var tp_weapon : Node3D = tp_mesh.hand_attachment.get_child(index)
|
||||
var tp_weapon:Node3D = tp_mesh.hand_attachment.get_child(index)
|
||||
if tp_weapon:
|
||||
tp_weapon.show()
|
||||
|
||||
func _trigger() -> void:
|
||||
if not _is_player_dead() and inventory.selected:
|
||||
if inventory.selected.has_method("trigger"):
|
||||
inventory.selected.trigger()
|
||||
else:
|
||||
push_warning("cannot trigger weapon")
|
||||
|
||||
func _jump() -> void:
|
||||
if _is_player_dead():
|
||||
if not is_alive():
|
||||
return
|
||||
_jumping = true
|
||||
|
||||
func _throw_flag() -> void:
|
||||
flag_carry_component.throw(linear_velocity, self)
|
||||
|
||||
func is_on_floor() -> bool:
|
||||
return walkable_surface_sensor.is_colliding()
|
||||
|
||||
func _is_skiing() -> bool:
|
||||
return input.skiing
|
||||
|
||||
func _handle_aerial_control(direction : Vector3) -> void:
|
||||
func _handle_aerial_control(direction:Vector3) -> void:
|
||||
if not input.jetting and not is_on_floor():
|
||||
apply_force(direction * aerial_control_force)
|
||||
|
||||
func _handle_jetpack(direction : Vector3) -> void:
|
||||
func _handle_jetpack(direction:Vector3) -> void:
|
||||
if input.jetting and energy > 0:
|
||||
var up_vector : Vector3 = Vector3.UP * jetpack_vertical_force * jetpack_force_factor
|
||||
var side_vector : Vector3 = direction * jetpack_horizontal_force * jetpack_force_factor
|
||||
var up_vector:Vector3 = Vector3.UP * jetpack_vertical_force * jetpack_force_factor
|
||||
var side_vector:Vector3 = direction * jetpack_horizontal_force * jetpack_force_factor
|
||||
apply_force(up_vector + side_vector)
|
||||
display_jetpack_particles()
|
||||
for particle: GPUParticles3D in _jetpack_particles:
|
||||
particle.emitting = true
|
||||
|
||||
func _update_jetpack_energy(delta : float) -> void:
|
||||
func _update_jetpack_energy(delta:float) -> void:
|
||||
if input.jetting and energy > 0:
|
||||
energy -= energy_drain_rate * delta
|
||||
else:
|
||||
|
|
@ -155,35 +245,32 @@ func _update_jetpack_energy(delta : float) -> void:
|
|||
energy = clamp(energy, 0, energy_max)
|
||||
hud.energy_bar.value = energy
|
||||
|
||||
func _integrate_forces(_state : PhysicsDirectBodyState3D) -> void:
|
||||
# retrieve user's direction vector
|
||||
var _input_dir : Vector2 = input.direction
|
||||
# compute direction in local space
|
||||
var _direction : Vector3 = (transform.basis * Vector3(_input_dir.x, 0, _input_dir.y)).normalized()
|
||||
|
||||
_update_third_person_animations()
|
||||
|
||||
if _is_player_dead():
|
||||
func _integrate_forces(_state:PhysicsDirectBodyState3D) -> void:
|
||||
if not is_alive():
|
||||
return
|
||||
# retrieve user's direction vector
|
||||
var _input_dir:Vector2 = input.direction
|
||||
# compute direction in local space
|
||||
var _direction:Vector3 = (transform.basis * Vector3(_input_dir.x, 0, _input_dir.y)).normalized()
|
||||
|
||||
# adjust direction based on spring arm rotation
|
||||
_direction = _direction.rotated(Vector3.UP, %Pivot.rotation.y)
|
||||
# adjust direction based on pivot rotation
|
||||
_direction = _direction.rotated(Vector3.UP, pivot.rotation.y)
|
||||
|
||||
_handle_aerial_control(_direction)
|
||||
_handle_jetpack(_direction)
|
||||
|
||||
# handle ski
|
||||
if _is_skiing():
|
||||
physics_material_override.friction = 0
|
||||
# zero-damping ski
|
||||
if is_on_floor() and input.skiing:
|
||||
linear_damp = lerp(linear_damp, .0, .1)
|
||||
else:
|
||||
physics_material_override.friction = 1
|
||||
|
||||
linear_damp = lerp(
|
||||
linear_damp,
|
||||
ProjectSettings.get_setting("physics/3d/default_linear_damp") + .05,
|
||||
.2)
|
||||
|
||||
if is_on_floor():
|
||||
if not _direction.is_zero_approx() and not _is_skiing():
|
||||
if not _direction.is_zero_approx() and not input.skiing:
|
||||
# retrieve collision normal
|
||||
var normal : Vector3 = walkable_surface_sensor.get_collision_normal(0)
|
||||
var normal:Vector3 = walkable_surface_sensor.get_collision_normal(0)
|
||||
# calculate the angle between the ground normal and the up vector
|
||||
var slope_angle : float = rad_to_deg(acos(normal.dot(Vector3.UP)))
|
||||
var slope_angle:float = rad_to_deg(acos(normal.dot(Vector3.UP)))
|
||||
# check if the slope angle exceeds the maximum slope angle
|
||||
if slope_angle <= max_floor_angle:
|
||||
# adjust direction based on the floor normal to align with the slope
|
||||
|
|
@ -192,16 +279,19 @@ func _integrate_forces(_state : PhysicsDirectBodyState3D) -> void:
|
|||
linear_velocity = lerp(linear_velocity, _direction * ground_speed, .1)
|
||||
|
||||
if _jumping:
|
||||
var v : float = sqrt(2 * g * jump_height)
|
||||
apply_central_impulse(Vector3(0, mass * v, 0))
|
||||
|
||||
var v:float = sqrt(2. * g * jump_height)
|
||||
apply_central_impulse(Vector3(0., mass * v, 0.))
|
||||
|
||||
_jumping = false
|
||||
|
||||
# set ski state
|
||||
physics_material_override.friction = !input.skiing
|
||||
|
||||
_handle_aerial_control(_direction)
|
||||
_handle_jetpack(_direction)
|
||||
|
||||
func _update_third_person_animations() -> void:
|
||||
if _is_pawn():
|
||||
return
|
||||
|
||||
if _is_player_dead():
|
||||
if not is_alive():
|
||||
tp_mesh.set_ground_state(Vanguard.GroundState.GROUND_STATE_DEAD)
|
||||
return
|
||||
|
||||
|
|
@ -209,32 +299,34 @@ func _update_third_person_animations() -> void:
|
|||
tp_mesh.set_ground_state(Vanguard.GroundState.GROUND_STATE_GROUNDED)
|
||||
else:
|
||||
tp_mesh.set_ground_state(Vanguard.GroundState.GROUND_STATE_MID_AIR)
|
||||
var local_velocity : Vector3 = (tp_mesh.global_basis.inverse() * linear_velocity)
|
||||
const bias : float = 1.2 # Basically match feet speed with ground speed
|
||||
|
||||
var local_velocity:Vector3 = (tp_mesh.global_basis.inverse() * linear_velocity)
|
||||
const bias:float = 1.2 # Basically match feet speed with ground speed
|
||||
|
||||
tp_mesh.set_locomotion(Vector2(local_velocity.x, local_velocity.z), bias)
|
||||
|
||||
func _is_player_dead() -> bool:
|
||||
return player_state != PlayerState.PLAYER_ALIVE
|
||||
func is_alive() -> bool:
|
||||
assert(health)
|
||||
return health.state == health.HealthState.ALIVE
|
||||
|
||||
func die(killer_id : int) -> void:
|
||||
flag_carry_component.drop(self)
|
||||
player_state = PlayerState.PLAYER_DEAD
|
||||
func has_flag() -> bool:
|
||||
return flag_carry_component._flag != null
|
||||
|
||||
func _on_killed(by_peer_id:int) -> void:
|
||||
flag_carry_component.drop(linear_velocity)
|
||||
if _is_pawn():
|
||||
animation_player.play("death")
|
||||
died.emit(self, killer_id)
|
||||
killed.emit(self, by_peer_id)
|
||||
|
||||
@rpc("call_local", "reliable")
|
||||
func respawn(location : Vector3) -> void:
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func respawn(location: Vector3) -> void:
|
||||
animation_player.stop()
|
||||
player_state = PlayerState.PLAYER_ALIVE
|
||||
linear_velocity = Vector3()
|
||||
health_component.heal_full()
|
||||
position = location
|
||||
linear_velocity = Vector3.ZERO
|
||||
health.heal()
|
||||
global_position = location
|
||||
for weapon: Weapon in inventory.get_children():
|
||||
weapon.set_ammo.rpc(weapon.max_ammo)
|
||||
respawned.emit(self)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
player_state = PlayerState.PLAYER_DEAD
|
||||
flag_carry_component.drop(self)
|
||||
|
||||
func display_jetpack_particles() -> void:
|
||||
for particle: GPUParticles3D in jetpack_particles:
|
||||
particle.emitting = true
|
||||
flag_carry_component.drop(linear_velocity)
|
||||
|
|
|
|||
|
|
@ -1,22 +1,20 @@
|
|||
[gd_scene load_steps=45 format=3 uid="uid://cbhx1xme0sb7k"]
|
||||
[gd_scene load_steps=44 format=3 uid="uid://cbhx1xme0sb7k"]
|
||||
|
||||
[ext_resource type="Script" path="res://entities/player/player.gd" id="1_mk68k"]
|
||||
[ext_resource type="Script" path="res://entities/player/player.gd" id="1_y2i7h"]
|
||||
[ext_resource type="PackedScene" uid="uid://bbeecp3jusppn" path="res://interfaces/hud/iffs/IFF.tscn" id="2_s5wgp"]
|
||||
[ext_resource type="PackedScene" uid="uid://bcv81ku26xo" path="res://interfaces/hud/hud.tscn" id="3_ccety"]
|
||||
[ext_resource type="Shape3D" uid="uid://cb8esdlnottdn" path="res://entities/player/resources/collider.tres" id="4_8kvcy"]
|
||||
[ext_resource type="Script" path="res://entities/components/inventory.gd" id="8_768qh"]
|
||||
[ext_resource type="Script" path="res://entities/components/health.gd" id="4_55muf"]
|
||||
[ext_resource type="Script" path="res://entities/components/inventory.gd" id="6_du54c"]
|
||||
[ext_resource type="PackedScene" uid="uid://drbefw6akui2v" path="res://entities/player/vanguard.tscn" id="8_eiy7q"]
|
||||
[ext_resource type="Script" path="res://entities/components/flag_carry_component.gd" id="8_pdfbn"]
|
||||
[ext_resource type="Texture2D" uid="uid://ct1v5iadtpadm" path="res://entities/player/assets/jetpackfx/smoke_01.png" id="9_4pant"]
|
||||
[ext_resource type="PackedScene" uid="uid://c8co0qa2omjmh" path="res://entities/weapons/space_gun/space_gun.tscn" id="9_achlo"]
|
||||
[ext_resource type="Texture2D" uid="uid://doxo4vfn0bjlp" path="res://entities/player/assets/jetpackfx/smoke_02.png" id="10_5b1bx"]
|
||||
[ext_resource type="Texture2D" uid="uid://dmf12llra7aq5" path="res://entities/player/assets/jetpackfx/Particle01.png" id="11_6ndfi"]
|
||||
[ext_resource type="Script" path="res://addons/smoothing/smoothing.gd" id="11_k330l"]
|
||||
[ext_resource type="Script" path="res://entities/components/health_component.gd" id="14_ctgxn"]
|
||||
[ext_resource type="PackedScene" uid="uid://b0xql5hi0b52y" path="res://entities/weapons/chaingun/chaingun.tscn" id="15_io0a3"]
|
||||
[ext_resource type="PackedScene" uid="uid://cstl7yxc75572" path="res://entities/weapons/grenade_launcher/grenade_launcher.tscn" id="16_4xs2j"]
|
||||
[ext_resource type="PackedScene" uid="uid://d3l7fvbdg6m5g" path="res://entities/flag/assets/flag.glb" id="18_7nkei"]
|
||||
[ext_resource type="Script" path="res://entities/components/match_participant.gd" id="18_t2jrs"]
|
||||
[ext_resource type="Script" path="res://entities/player/inputs_sync.gd" id="18_v4iu1"]
|
||||
|
||||
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_clur0"]
|
||||
|
|
@ -24,8 +22,35 @@ resource_local_to_scene = true
|
|||
bounce = 1.0
|
||||
absorbent = true
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_hwe6e"]
|
||||
radius = 0.2
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_h0a20"]
|
||||
radius = 0.25
|
||||
|
||||
[sub_resource type="Animation" id="Animation_eoxkv"]
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Pivot:position")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0.625, 0)]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath("Pivot:rotation")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0, 0)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_yqgrk"]
|
||||
resource_name = "death"
|
||||
|
|
@ -51,11 +76,12 @@ tracks/1/keys = {
|
|||
"times": PackedFloat32Array(0, 0.5),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0.5, 0), Vector3(0, -0.114794, 0)]
|
||||
"values": [Vector3(0, 0.625, 0), Vector3(0, 0.625, 0)]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_hg307"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_eoxkv"),
|
||||
"death": SubResource("Animation_yqgrk")
|
||||
}
|
||||
|
||||
|
|
@ -169,83 +195,88 @@ material = SubResource("StandardMaterial3D_2jwv2")
|
|||
|
||||
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_rqdp6"]
|
||||
properties/0/path = NodePath(".:linear_velocity")
|
||||
properties/0/spawn = false
|
||||
properties/0/spawn = true
|
||||
properties/0/replication_mode = 1
|
||||
properties/1/path = NodePath(".:position")
|
||||
properties/1/spawn = false
|
||||
properties/1/spawn = true
|
||||
properties/1/replication_mode = 1
|
||||
properties/2/path = NodePath("HealthComponent:health")
|
||||
properties/2/spawn = false
|
||||
properties/2/path = NodePath(".:peer_id")
|
||||
properties/2/spawn = true
|
||||
properties/2/replication_mode = 2
|
||||
properties/3/path = NodePath(".:player_state")
|
||||
properties/3/spawn = false
|
||||
properties/3/path = NodePath(".:username")
|
||||
properties/3/spawn = true
|
||||
properties/3/replication_mode = 2
|
||||
properties/4/path = NodePath(".:team_id")
|
||||
properties/4/spawn = true
|
||||
properties/4/replication_mode = 2
|
||||
properties/5/path = NodePath("Health:value")
|
||||
properties/5/spawn = true
|
||||
properties/5/replication_mode = 2
|
||||
properties/6/path = NodePath("Pivot/FlagCarryComponent:visible")
|
||||
properties/6/spawn = true
|
||||
properties/6/replication_mode = 2
|
||||
properties/7/path = NodePath("Health:state")
|
||||
properties/7/spawn = true
|
||||
properties/7/replication_mode = 1
|
||||
|
||||
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_5j4ew"]
|
||||
properties/0/path = NodePath(".:direction")
|
||||
properties/0/spawn = true
|
||||
properties/0/replication_mode = 1
|
||||
properties/0/replication_mode = 2
|
||||
properties/1/path = NodePath(".:jetting")
|
||||
properties/1/spawn = true
|
||||
properties/1/replication_mode = 2
|
||||
properties/2/path = NodePath(".:camera_rotation")
|
||||
properties/2/spawn = true
|
||||
properties/2/replication_mode = 1
|
||||
properties/2/replication_mode = 2
|
||||
properties/3/path = NodePath(".:skiing")
|
||||
properties/3/spawn = true
|
||||
properties/3/replication_mode = 2
|
||||
|
||||
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_7na0c"]
|
||||
properties/0/path = NodePath(".:username")
|
||||
properties/0/spawn = true
|
||||
properties/0/replication_mode = 1
|
||||
properties/1/path = NodePath(".:player_id")
|
||||
properties/1/spawn = true
|
||||
properties/1/replication_mode = 1
|
||||
properties/2/path = NodePath(".:team_id")
|
||||
properties/2/spawn = true
|
||||
properties/2/replication_mode = 1
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_hwe6e"]
|
||||
radius = 0.2
|
||||
|
||||
[node name="Player" type="RigidBody3D" node_paths=PackedStringArray("iff", "health_component", "flag_carry_component", "walkable_surface_sensor", "match_participant", "inventory", "tp_mesh", "input") groups=["Player"]]
|
||||
[node name="Player" type="RigidBody3D" node_paths=PackedStringArray("iff", "health", "flag_carry_component", "walkable_surface_sensor", "hud", "inventory", "tp_mesh", "third_person", "pivot", "camera", "input")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||
collision_mask = 2147483649
|
||||
collision_priority = 9.0
|
||||
axis_lock_angular_x = true
|
||||
axis_lock_angular_y = true
|
||||
axis_lock_angular_z = true
|
||||
mass = 75.0
|
||||
mass = 80.0
|
||||
physics_material_override = SubResource("PhysicsMaterial_clur0")
|
||||
gravity_scale = 1.2
|
||||
can_sleep = false
|
||||
continuous_cd = true
|
||||
script = ExtResource("1_mk68k")
|
||||
linear_damp_mode = 1
|
||||
linear_damp = 0.15
|
||||
script = ExtResource("1_y2i7h")
|
||||
iff = NodePath("IFF")
|
||||
health_component = NodePath("HealthComponent")
|
||||
health = NodePath("Health")
|
||||
flag_carry_component = NodePath("Pivot/FlagCarryComponent")
|
||||
walkable_surface_sensor = NodePath("WalkableSurfaceSensor")
|
||||
match_participant = NodePath("MatchParticipant")
|
||||
hud = NodePath("HUD")
|
||||
inventory = NodePath("Pivot/Inventory")
|
||||
tp_mesh = NodePath("ThirdPerson/Mesh")
|
||||
jump_height = 1.5
|
||||
third_person = NodePath("ThirdPerson")
|
||||
pivot = NodePath("Pivot")
|
||||
camera = NodePath("Pivot/Camera3D")
|
||||
max_floor_angle = 80.0
|
||||
input = NodePath("Inputs")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
shape = ExtResource("4_8kvcy")
|
||||
|
||||
[node name="IFF" parent="." instance=ExtResource("2_s5wgp")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.1, 0)
|
||||
fill = Color(0, 1, 0, 1)
|
||||
|
||||
[node name="HealthComponent" type="Area3D" parent="." node_paths=PackedStringArray("match_participant")]
|
||||
script = ExtResource("14_ctgxn")
|
||||
match_participant = NodePath("../MatchParticipant")
|
||||
[node name="Health" type="Area3D" parent="." node_paths=PackedStringArray("collider")]
|
||||
script = ExtResource("4_55muf")
|
||||
collider = NodePath("CollisionShape3D")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="HealthComponent"]
|
||||
shape = ExtResource("4_8kvcy")
|
||||
|
||||
[node name="WalkableSurfaceSensor" type="ShapeCast3D" parent="."]
|
||||
transform = Transform3D(1.05, 0, 0, 0, 1.05, 0, 0, 0, 1.05, 0, -0.85, 0)
|
||||
shape = SubResource("SphereShape3D_hwe6e")
|
||||
target_position = Vector3(0, 0, 0)
|
||||
collision_mask = 2147483648
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
shape = ExtResource("4_8kvcy")
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Health"]
|
||||
shape = SubResource("CapsuleShape3D_h0a20")
|
||||
|
||||
[node name="HUD" parent="." instance=ExtResource("3_ccety")]
|
||||
|
||||
|
|
@ -253,21 +284,19 @@ shape = ExtResource("4_8kvcy")
|
|||
libraries = {
|
||||
"": SubResource("AnimationLibrary_hg307")
|
||||
}
|
||||
autoplay = "shoot"
|
||||
autoplay = "RESET"
|
||||
playback_default_blend_time = 0.05
|
||||
|
||||
[node name="Pivot" type="Node3D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.625, 0)
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="Pivot"]
|
||||
fov = 90.0
|
||||
near = 0.1
|
||||
|
||||
[node name="Inventory" type="Node3D" parent="Pivot"]
|
||||
unique_name_in_owner = true
|
||||
transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0.15, -0.2, -0.45)
|
||||
script = ExtResource("8_768qh")
|
||||
script = ExtResource("6_du54c")
|
||||
|
||||
[node name="SpaceGun" parent="Pivot/Inventory" instance=ExtResource("9_achlo")]
|
||||
|
||||
|
|
@ -297,84 +326,55 @@ transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0,
|
|||
spine_ik_target_attachment = NodePath("../../Pivot/SpineIKTarget")
|
||||
|
||||
[node name="Skeleton3D" parent="ThirdPerson/Mesh/Node" index="0"]
|
||||
bones/0/position = Vector3(-0.0048219, 0.946668, 0.00678214)
|
||||
bones/0/rotation = Quaternion(-0.0341192, -0.409249, -0.0209221, 0.911545)
|
||||
bones/2/rotation = Quaternion(-0.00595832, -0.0014545, 0.0101407, 0.99993)
|
||||
bones/4/rotation = Quaternion(0.0691268, 0.00227406, 0.0147948, 0.997496)
|
||||
bones/6/rotation = Quaternion(0.160951, 0.00623474, 0.0096637, 0.986895)
|
||||
bones/8/rotation = Quaternion(0.675761, -0.399581, 0.409695, 0.464577)
|
||||
bones/10/rotation = Quaternion(0.212344, -0.0870591, -0.287653, 0.929831)
|
||||
bones/12/rotation = Quaternion(0.0811501, 0.206806, -0.754068, 0.618084)
|
||||
bones/14/rotation = Quaternion(0.00943715, 0.0971687, -0.145046, 0.984597)
|
||||
bones/20/rotation = Quaternion(-0.123455, 0.0248346, 0.23344, 0.964183)
|
||||
bones/24/rotation = Quaternion(0.0450683, -0.000817796, 0.0508488, 0.997689)
|
||||
bones/26/rotation = Quaternion(0.100545, -1.16532e-07, 0.00792588, 0.994901)
|
||||
bones/28/rotation = Quaternion(0.288532, -1.83796e-07, 0.0227447, 0.9572)
|
||||
bones/32/rotation = Quaternion(0.00674081, -0.0316083, 0.105474, 0.993897)
|
||||
bones/34/rotation = Quaternion(0.780684, 4.23384e-08, 0.0615407, 0.621889)
|
||||
bones/36/rotation = Quaternion(0.580978, -1.56224e-07, 0.0457976, 0.81263)
|
||||
bones/40/rotation = Quaternion(0.0913739, 0.113691, 0.100265, 0.984211)
|
||||
bones/42/rotation = Quaternion(0.836841, 4.40546e-07, 0.0659669, 0.543457)
|
||||
bones/44/rotation = Quaternion(0.633142, 6.48257e-09, 0.04991, 0.772425)
|
||||
bones/50/rotation = Quaternion(0.729888, -4.88266e-08, 0.0575362, 0.681141)
|
||||
bones/52/rotation = Quaternion(0.624011, -9.63141e-08, 0.04919, 0.779865)
|
||||
bones/56/rotation = Quaternion(-0.0254885, 0.0439755, -0.00910637, 0.998666)
|
||||
bones/58/rotation = Quaternion(-0.0119802, 0.289956, -0.0527053, 0.955513)
|
||||
bones/62/rotation = Quaternion(0.689943, 0.375565, -0.410267, 0.463261)
|
||||
bones/64/rotation = Quaternion(0.337746, -0.290519, 0.159479, 0.880961)
|
||||
bones/66/rotation = Quaternion(0.540371, -0.580679, 0.406779, 0.453147)
|
||||
bones/68/position = Vector3(-1.77636e-17, 0.240718, 0)
|
||||
bones/68/rotation = Quaternion(0.0150529, -0.289001, 0.0720108, 0.954498)
|
||||
bones/70/rotation = Quaternion(0.155965, 0.0109114, -0.00107202, 0.987702)
|
||||
bones/72/rotation = Quaternion(0.563923, 4.19095e-08, -0.0577906, 0.823803)
|
||||
bones/74/rotation = Quaternion(0.285209, 0.0197164, -0.0936782, 0.953673)
|
||||
bones/78/rotation = Quaternion(0.057484, -0.0698946, -0.0100642, 0.995846)
|
||||
bones/80/rotation = Quaternion(0.433127, 5.44824e-08, -0.0443866, 0.900239)
|
||||
bones/82/rotation = Quaternion(0.274138, -1.97906e-08, -0.0280937, 0.96128)
|
||||
bones/86/rotation = Quaternion(0.244152, 0.0521336, 0.176446, 0.952123)
|
||||
bones/88/rotation = Quaternion(0.0146391, -1.48637e-07, -0.121951, 0.992428)
|
||||
bones/90/rotation = Quaternion(-0.147655, -0.0737763, 0.197847, 0.966236)
|
||||
bones/94/rotation = Quaternion(0.180682, 0.0832945, -0.00387314, 0.980001)
|
||||
bones/96/rotation = Quaternion(0.245651, -6.35919e-08, -0.0251742, 0.969032)
|
||||
bones/98/rotation = Quaternion(0.246432, 7.6252e-08, -0.0252543, 0.968831)
|
||||
bones/102/rotation = Quaternion(0.179829, 0.0890365, -0.000307644, 0.97966)
|
||||
bones/104/rotation = Quaternion(0.388149, 1.28057e-07, -0.0397774, 0.920738)
|
||||
bones/106/rotation = Quaternion(0.372324, -1.37021e-07, -0.0381557, 0.927318)
|
||||
bones/110/rotation = Quaternion(-0.167577, 0.223934, 0.958827, 0.0492099)
|
||||
bones/112/rotation = Quaternion(-0.466474, -0.0088339, -0.0232928, 0.884184)
|
||||
bones/114/rotation = Quaternion(0.575696, 0.0793941, -0.0250592, 0.813414)
|
||||
bones/116/rotation = Quaternion(0.355062, 0.0493655, 0.0246355, 0.933213)
|
||||
bones/120/rotation = Quaternion(0.115252, 0.282473, 0.945749, -0.111737)
|
||||
bones/122/rotation = Quaternion(-0.494906, -0.0647935, 0.0183973, 0.866332)
|
||||
bones/124/rotation = Quaternion(0.417677, -0.0431149, 0.00625689, 0.90755)
|
||||
bones/126/rotation = Quaternion(0.397818, -0.0427722, -0.00601182, 0.916447)
|
||||
|
||||
[node name="HandAttachment" parent="ThirdPerson/Mesh/Node/Skeleton3D" index="0"]
|
||||
transform = Transform3D(-0.152214, 0.0548832, 0.986823, 0.933991, 0.334546, 0.125459, -0.323252, 0.94078, -0.102183, -0.261612, 1.14328, 0.0896011)
|
||||
transform = Transform3D(-0.152213, 0.0548833, 0.986823, 0.933991, 0.334546, 0.125458, -0.323252, 0.94078, -0.102183, -0.261612, 1.14328, 0.0896012)
|
||||
|
||||
[node name="grip" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/SpaceGun/Mesh/Armature/Skeleton3D" index="0"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.84217e-14, 1.19209e-07, 2.38419e-07)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.6068e-14, -1.19209e-07, 9.53674e-07)
|
||||
|
||||
[node name="grip" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/SpaceGun/Mesh/Armature/Skeleton3D/grip" index="0"]
|
||||
layers = 2
|
||||
|
||||
[node name="main" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/SpaceGun/Mesh/Armature/Skeleton3D" index="1"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.84217e-14, 1.19209e-07, 2.38419e-07)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.6068e-14, -1.19209e-07, 9.53674e-07)
|
||||
|
||||
[node name="main" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/SpaceGun/Mesh/Armature/Skeleton3D/main" index="0"]
|
||||
layers = 2
|
||||
|
||||
[node name="sides" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/SpaceGun/Mesh/Armature/Skeleton3D" index="2"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.84217e-14, 1.19209e-07, 2.38419e-07)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.6068e-14, -1.19209e-07, 9.53674e-07)
|
||||
|
||||
[node name="sides" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/SpaceGun/Mesh/Armature/Skeleton3D/sides" index="0"]
|
||||
layers = 2
|
||||
|
||||
[node name="BarrelsInner" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/ChainGun/Armature/Skeleton3D" index="0"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.692504, 1.30946)
|
||||
|
||||
[node name="BarrelsInner" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/ChainGun/Armature/Skeleton3D/BarrelsInner" index="0"]
|
||||
layers = 2
|
||||
|
||||
[node name="BarrelsOuter" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/ChainGun/Armature/Skeleton3D" index="1"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.692504, 1.30946)
|
||||
|
||||
[node name="BarrelsOuter" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/ChainGun/Armature/Skeleton3D/BarrelsOuter" index="0"]
|
||||
layers = 2
|
||||
|
||||
[node name="Base" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/ChainGun/Armature/Skeleton3D" index="2"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.692504, 1.30946)
|
||||
|
||||
[node name="Base" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/ChainGun/Armature/Skeleton3D/Base" index="0"]
|
||||
layers = 2
|
||||
|
||||
[node name="Grip" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/ChainGun/Armature/Skeleton3D" index="3"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.692504, 1.30946)
|
||||
|
||||
[node name="Barrels" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/ChainGun" index="3"]
|
||||
transform = Transform3D(-1, 0, 8.9407e-08, 8.47504e-08, 0, 1, 0, 1, -3.72529e-09, -5.96046e-08, 0, -0.55315)
|
||||
[node name="Grip" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/ChainGun/Armature/Skeleton3D/Grip" index="0"]
|
||||
layers = 2
|
||||
|
||||
[node name="Barrels" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/ChainGun" index="2"]
|
||||
transform = Transform3D(-1, 0, 8.74227e-08, 8.74227e-08, 0, 1, 0, 1, 0, -2.32831e-10, 0.692504, 0.756307)
|
||||
|
||||
[node name="Skeleton3D" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/GrenadeLauncher/Armature" index="0"]
|
||||
bones/0/rotation = Quaternion(0, 0.707107, 0.707107, 0)
|
||||
|
|
@ -385,17 +385,33 @@ bones/3/rotation = Quaternion(0, 0.707107, 0.707107, 0)
|
|||
[node name="barrel" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/GrenadeLauncher/Armature/Skeleton3D" index="0"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.19209e-07, 0)
|
||||
|
||||
[node name="barrel" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/GrenadeLauncher/Armature/Skeleton3D/barrel" index="0"]
|
||||
layers = 2
|
||||
|
||||
[node name="grip" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/GrenadeLauncher/Armature/Skeleton3D" index="1"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.082471, -0.0653242)
|
||||
|
||||
[node name="grip" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/GrenadeLauncher/Armature/Skeleton3D/grip" index="0"]
|
||||
layers = 2
|
||||
|
||||
[node name="main" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/GrenadeLauncher/Armature/Skeleton3D" index="2"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.19209e-07, 0)
|
||||
|
||||
[node name="main" parent="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/GrenadeLauncher/Armature/Skeleton3D/main" index="0"]
|
||||
layers = 2
|
||||
|
||||
[node name="vanguard_Mesh" parent="ThirdPerson/Mesh/Node/Skeleton3D" index="1"]
|
||||
layers = 2
|
||||
|
||||
[node name="vanguard_visor" parent="ThirdPerson/Mesh/Node/Skeleton3D" index="2"]
|
||||
layers = 2
|
||||
|
||||
[node name="JetpackFX" type="Node3D" parent="ThirdPerson/Mesh"]
|
||||
transform = Transform3D(0.467164, -0.312366, -0.827155, -0.12476, 0.902867, -0.41142, 0.875325, 0.295397, 0.382816, 0.143745, 0.353192, -0.140279)
|
||||
|
||||
[node name="Smoke1" type="GPUParticles3D" parent="ThirdPerson/Mesh/JetpackFX"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.150648, 0)
|
||||
layers = 2
|
||||
emitting = false
|
||||
lifetime = 0.5
|
||||
one_shot = true
|
||||
|
|
@ -405,6 +421,7 @@ draw_pass_1 = SubResource("QuadMesh_hegkl")
|
|||
|
||||
[node name="Smoke2" type="GPUParticles3D" parent="ThirdPerson/Mesh/JetpackFX"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.108846, 0)
|
||||
layers = 2
|
||||
emitting = false
|
||||
lifetime = 0.5
|
||||
one_shot = true
|
||||
|
|
@ -413,6 +430,7 @@ process_material = SubResource("ParticleProcessMaterial_l8e6j")
|
|||
draw_pass_1 = SubResource("QuadMesh_aeure")
|
||||
|
||||
[node name="Fire1" type="GPUParticles3D" parent="ThirdPerson/Mesh/JetpackFX"]
|
||||
layers = 2
|
||||
emitting = false
|
||||
amount = 16
|
||||
lifetime = 0.3
|
||||
|
|
@ -421,12 +439,7 @@ fixed_fps = 60
|
|||
process_material = SubResource("ParticleProcessMaterial_q1vdw")
|
||||
draw_pass_1 = SubResource("QuadMesh_uc7ts")
|
||||
|
||||
[node name="Smoothing" type="Node3D" parent="."]
|
||||
script = ExtResource("11_k330l")
|
||||
target = NodePath("..")
|
||||
flags = 3
|
||||
|
||||
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
|
||||
[node name="MultiplayerSync" type="MultiplayerSynchronizer" parent="."]
|
||||
replication_config = SubResource("SceneReplicationConfig_rqdp6")
|
||||
|
||||
[node name="Inputs" type="Node" parent="."]
|
||||
|
|
@ -435,11 +448,14 @@ script = ExtResource("18_v4iu1")
|
|||
[node name="InputsSync" type="MultiplayerSynchronizer" parent="Inputs"]
|
||||
replication_config = SubResource("SceneReplicationConfig_5j4ew")
|
||||
|
||||
[node name="MatchParticipant" type="Node" parent="."]
|
||||
script = ExtResource("18_t2jrs")
|
||||
[node name="WalkableSurfaceSensor" type="ShapeCast3D" parent="."]
|
||||
transform = Transform3D(1.05, 0, 0, 0, 1.05, 0, 0, 0, 1.05, 0, -0.85, 0)
|
||||
shape = SubResource("SphereShape3D_hwe6e")
|
||||
target_position = Vector3(0, 0, 0)
|
||||
max_results = 16
|
||||
collision_mask = 2147483648
|
||||
|
||||
[node name="MatchParticipantSync" type="MultiplayerSynchronizer" parent="MatchParticipant"]
|
||||
replication_config = SubResource("SceneReplicationConfig_7na0c")
|
||||
[connection signal="child_entered_tree" from="Pivot/Inventory" to="Pivot/Inventory" method="_on_child_entered_tree"]
|
||||
|
||||
[editable path="ThirdPerson/Mesh"]
|
||||
[editable path="ThirdPerson/Mesh/Node/Skeleton3D/HandAttachment/SpaceGun"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
[gd_resource type="CapsuleShape3D" format=3 uid="uid://cb8esdlnottdn"]
|
||||
|
||||
[resource]
|
||||
resource_local_to_scene = true
|
||||
radius = 0.25
|
||||
|
|
|
|||
|
|
@ -1,3 +1,17 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name Vanguard extends Node
|
||||
|
||||
@export var spine_ik_target_attachment : Marker3D
|
||||
|
|
|
|||
31
entities/projectiles/arc_projectile.gd
Normal file
31
entities/projectiles/arc_projectile.gd
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name ArcProjectile extends ExplosiveProjectile
|
||||
|
||||
var _g: float = ProjectSettings.get_setting("physics/3d/default_gravity") # in m/s²
|
||||
var _gravity: Vector3 = _g * ProjectSettings.get_setting("physics/3d/default_gravity_vector")
|
||||
|
||||
func _physics_process(delta : float) -> void:
|
||||
# compute motion
|
||||
var previous_global_position : Vector3 = global_position
|
||||
# apply gravity
|
||||
velocity += _gravity * delta
|
||||
# compute new position
|
||||
global_position += velocity * delta
|
||||
# handle collision
|
||||
if shape_cast:
|
||||
shape_cast.target_position = to_local(previous_global_position)
|
||||
if shape_cast.is_colliding():
|
||||
destroy(shape_cast.collision_result[0].point)
|
||||
63
entities/projectiles/damages/explosive_damage.gd
Normal file
63
entities/projectiles/damages/explosive_damage.gd
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
## This class defines an explosive damage area.
|
||||
##
|
||||
## It detects nearby bodies within a radius defined by a child [CollisionShape3D]
|
||||
## or [CollisionPolygon3D] and also initiates a blast that applies impulse and
|
||||
## damages to detected bodies based on their distance from the explosion.
|
||||
class_name ExplosiveDamage extends Area3D
|
||||
|
||||
## The base amount of damage.
|
||||
@export var damage: int = 1
|
||||
## The magnitude of blast force, in Newtons.
|
||||
@export var blast_force: int = 1500
|
||||
## A factor that determines how damage decreases with distance from the explosion center.
|
||||
@export var falloff: Curve
|
||||
## The entity responsible for dealing damage.
|
||||
var source: Node = null
|
||||
# The number of physics frames to process for blast detection.
|
||||
var _blast_frames: int = 0
|
||||
|
||||
func _init() -> void:
|
||||
if not body_shape_entered.is_connected(_on_body_shape_entered):
|
||||
body_shape_entered.connect(_on_body_shape_entered)
|
||||
|
||||
# queue free area after processing few frames (enough for body detection)
|
||||
func _physics_process(_delta: float) -> void:
|
||||
_blast_frames += 1
|
||||
if _blast_frames >= 2:
|
||||
queue_free()
|
||||
|
||||
# overlapping bodies signal handler
|
||||
func _on_body_shape_entered(_body_rid: RID, body: Node, _body_shape_idx: int, local_shape_idx: int) -> void:
|
||||
# retrieve area shape
|
||||
var shape : SphereShape3D = shape_owner_get_shape(
|
||||
shape_find_owner(local_shape_idx), local_shape_idx)
|
||||
# retrieve vector from current node origin to body global center of mass
|
||||
var direction: Vector3 = (body.global_transform.origin + body.center_of_mass) - global_transform.origin
|
||||
# sample curve texture if any to get falloff value at current distance
|
||||
var weight := 1.0
|
||||
if falloff:
|
||||
weight = falloff.sample(direction.length() / shape.radius)
|
||||
# handle blast
|
||||
if body is RigidBody3D:
|
||||
var impulse: Vector3 = direction.normalized() * (1000 * weight)
|
||||
# apply body impulse based on distance from explosion origin
|
||||
body.apply_impulse(impulse, global_transform.origin)
|
||||
# handle blast damages
|
||||
if body is Player:
|
||||
# deal damage based on distance from explosion origin
|
||||
body.damage.emit(source, body, damage * weight)
|
||||
|
||||
|
|
@ -12,20 +12,13 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name ProjectileSpawner extends Node3D
|
||||
class_name ExplosiveProjectile extends Projectile
|
||||
|
||||
@export_category("Projectile")
|
||||
@export var PROJECTILE : PackedScene
|
||||
@export_range(0., 1., .01) var inheritance : float = .5 # ratio
|
||||
@export var EXPLOSION:PackedScene
|
||||
|
||||
func new_projectile(
|
||||
projectile_type : Object,
|
||||
owner_velocity : Vector3,
|
||||
shooter : MatchParticipant
|
||||
) -> Node3D:
|
||||
return projectile_type.new_projectile(
|
||||
self,
|
||||
owner_velocity * inheritance,
|
||||
shooter,
|
||||
PROJECTILE
|
||||
)
|
||||
func destroy(location:Vector3 = global_position) -> void:
|
||||
var explosion : Node3D = EXPLOSION.instantiate()
|
||||
add_sibling(explosion)
|
||||
explosion.global_position = location
|
||||
explosion.source = source
|
||||
queue_free()
|
||||
63
entities/projectiles/projectile.gd
Normal file
63
entities/projectiles/projectile.gd
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
## This class defines a projectile.
|
||||
class_name Projectile extends Node3D
|
||||
|
||||
# @NOTE: while it's the correct fundamental constant, we're really looking
|
||||
# for « terminal velocity » here instead because earth is not a vacuum
|
||||
const _speed_of_light := 299792458 # in m/s
|
||||
|
||||
## The initial speed when launched, measured in meters per second (m/s or mps).
|
||||
@export_range(0, _speed_of_light) var speed : float = 42
|
||||
## The time before it automatically self-destructs (never by default), measured in seconds (s).
|
||||
@export var lifespan : float = 0.
|
||||
## A knockback impulse applied to bodies it collides with, in Newtons.
|
||||
@export var knockback: int
|
||||
## The base amount of damage.
|
||||
@export var damage: int
|
||||
## The [param source] that launched this projectile.
|
||||
var source: Node
|
||||
|
||||
var deflectable:bool = false
|
||||
var inheritance:Vector3 = Vector3.ZERO
|
||||
var shape_cast : ShapeCast3D = null
|
||||
var velocity:Vector3 = Vector3.ZERO
|
||||
|
||||
func _init() -> void:
|
||||
# do not inherit transform from parent
|
||||
top_level = true
|
||||
# ensure projectile has shape
|
||||
ready.connect(func() -> void:
|
||||
for child in get_children():
|
||||
if child is ShapeCast3D:
|
||||
shape_cast = child
|
||||
assert(shape_cast)
|
||||
if lifespan > 0:
|
||||
get_tree().create_timer(lifespan).timeout.connect(destroy)
|
||||
)
|
||||
|
||||
func destroy(_location: Vector3 = global_position) -> void:
|
||||
queue_free()
|
||||
|
||||
func _physics_process(delta : float) -> void:
|
||||
# compute motion
|
||||
var previous_global_position : Vector3 = global_position
|
||||
# compute new position
|
||||
global_position += velocity * delta
|
||||
# handle collision
|
||||
if shape_cast:
|
||||
shape_cast.target_position = to_local(previous_global_position)
|
||||
if shape_cast.is_colliding():
|
||||
destroy(shape_cast.collision_result[0].point)
|
||||
29
entities/weapons/automatic_weapon.gd
Normal file
29
entities/weapons/automatic_weapon.gd
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name AutomaticWeapon extends Weapon
|
||||
|
||||
var _auto_trigger := Timer.new()
|
||||
|
||||
func _init() -> void:
|
||||
super._init()
|
||||
_auto_trigger.timeout.connect(trigger)
|
||||
add_child(_auto_trigger)
|
||||
|
||||
func _on_primary(pressed: bool) -> void:
|
||||
if pressed:
|
||||
trigger()
|
||||
_auto_trigger.start(cooldown)
|
||||
else:
|
||||
_auto_trigger.stop()
|
||||
|
|
@ -12,33 +12,44 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name ChainGun extends Node3D
|
||||
class_name ChainGun extends AutomaticWeapon
|
||||
|
||||
@export_range(0, 250) var ammo : int = 250
|
||||
@export var fire_rate : float = .1 # seconds
|
||||
@export var reload_time : float = 0. # seconds
|
||||
@export var _PROJECTILE : PackedScene
|
||||
## The factor of velocity from the owner of this [Weapon] inherited by this [Projectile].
|
||||
@export_range(0., 1., .01) var inheritance : float = 1.
|
||||
|
||||
@export_category("Animations")
|
||||
@export var anim_player : AnimationPlayer
|
||||
## The angular deviation from ideal line of fire in degrees
|
||||
@export var min_spread: float
|
||||
## The maximum angle the spread will increase to during heatup.
|
||||
@export var max_spread: float
|
||||
## The amount of time it takes for the chaingun to spin up or spin down.
|
||||
@export var spin_period: float
|
||||
## The amount of time it takes for the chaingun to overheat.
|
||||
@export var heat_period: float
|
||||
#@export var heat_time: float
|
||||
## The fraction of the [member heat_period] at which the gun can start firing again
|
||||
@export var cooldown_threshold: float
|
||||
## Multiplier for the speed when calculating cooldown/heatup
|
||||
@export var speed_cooldown_factor: float
|
||||
## The [Shader] responsible for rendering heat while firing.
|
||||
@export var heat_shader: Shader
|
||||
# heat flag
|
||||
var overheated: bool
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
pass # Replace with function body.
|
||||
|
||||
func trigger() -> void:
|
||||
func _on_triggered() -> void:
|
||||
# play the fire animation
|
||||
anim_player.play("fire")
|
||||
# init projectile
|
||||
var projectile : Node3D = $ProjectileSpawner.new_projectile(
|
||||
ChainGunProjectile,
|
||||
owner.linear_velocity,
|
||||
owner.match_participant
|
||||
)
|
||||
# add to owner of player for now
|
||||
Global.type.add_child(projectile)
|
||||
var projectile : ChainGunProjectile = _PROJECTILE.instantiate()
|
||||
projectile.velocity = projectile.speed * $Nozzle.global_basis.z.normalized() + owner.linear_velocity * inheritance
|
||||
projectile.source = owner
|
||||
owner.add_child(projectile)
|
||||
projectile.global_transform = $Nozzle.global_transform
|
||||
projectile.shape_cast.add_exception(owner)
|
||||
|
||||
func _on_visibility_changed() -> void:
|
||||
if self.visible:
|
||||
anim_player.play("equip")
|
||||
#anim_player.play("equip")
|
||||
#self.sounds.play("equip")
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://b0xql5hi0b52y"]
|
||||
[gd_scene load_steps=4 format=3 uid="uid://b0xql5hi0b52y"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://cumv8wij5uufk" path="res://entities/weapons/chaingun/assets/chaingun.glb" id="1_d8tdv"]
|
||||
[ext_resource type="Script" path="res://entities/weapons/chaingun/chaingun.gd" id="2_qsqeh"]
|
||||
[ext_resource type="Script" path="res://entities/components/projectile_spawner.gd" id="3_knyrc"]
|
||||
[ext_resource type="PackedScene" uid="uid://b0sfwgilfbpx5" path="res://entities/weapons/chaingun/projectile.tscn" id="4_p63ts"]
|
||||
|
||||
[node name="ChainGun" node_paths=PackedStringArray("anim_player") instance=ExtResource("1_d8tdv")]
|
||||
script = ExtResource("2_qsqeh")
|
||||
_PROJECTILE = ExtResource("4_p63ts")
|
||||
anim_player = NodePath("AnimationPlayer")
|
||||
|
||||
[node name="ProjectileSpawner" type="Marker3D" parent="." index="0"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.124544, 0.476417)
|
||||
script = ExtResource("3_knyrc")
|
||||
PROJECTILE = ExtResource("4_p63ts")
|
||||
inheritance = 1.0
|
||||
min_spread = 1.0
|
||||
max_spread = 4.0
|
||||
spin_period = 0.5
|
||||
heat_period = 3.0
|
||||
cooldown_threshold = 1.7
|
||||
speed_cooldown_factor = 0.001
|
||||
ammo = 150
|
||||
max_ammo = 150
|
||||
ammo_usage = 1
|
||||
cooldown = 0.1
|
||||
|
||||
[node name="Skeleton3D" parent="Armature" index="0"]
|
||||
bones/1/rotation = Quaternion(3.09086e-08, 0.707107, 0.707107, 3.09086e-08)
|
||||
|
|
@ -32,14 +36,18 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.692504, 1.30946)
|
|||
[node name="Grip" parent="Armature/Skeleton3D" index="3"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.692504, 1.30946)
|
||||
|
||||
[node name="AnimationPlayer" parent="." index="2"]
|
||||
[node name="AnimationPlayer" parent="." index="1"]
|
||||
autoplay = "idle"
|
||||
|
||||
[node name="Barrels" type="BoneAttachment3D" parent="." index="3"]
|
||||
[node name="Barrels" type="BoneAttachment3D" parent="." index="2"]
|
||||
transform = Transform3D(-1, 0, 8.74227e-08, 8.74227e-08, 0, 1, 0, 1, 0, -2.32831e-10, 0.692504, 0.756307)
|
||||
bone_name = "barrels"
|
||||
bone_idx = 1
|
||||
use_external_skeleton = true
|
||||
external_skeleton = NodePath("../Armature/Skeleton3D")
|
||||
|
||||
[node name="Nozzle" type="Marker3D" parent="." index="3"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.98023e-08, 0.123522, 0.477262)
|
||||
|
||||
[connection signal="triggered" from="." to="." method="_on_triggered"]
|
||||
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
|
||||
|
|
|
|||
|
|
@ -12,44 +12,19 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name ChainGunProjectile extends Node3D
|
||||
class_name ChainGunProjectile extends Projectile
|
||||
|
||||
@export var speed : float = 180. # m/s
|
||||
@export var lifespan : float = .5 # in seconds
|
||||
@export var build_up_time : float = .6 # seconds
|
||||
|
||||
var shooter : MatchParticipant
|
||||
var velocity : Vector3 = Vector3.ZERO
|
||||
|
||||
@onready var shape_cast : ShapeCast3D = $ShapeCast3D
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
$Timer.wait_time = lifespan
|
||||
|
||||
func _on_timer_timeout() -> void:
|
||||
queue_free()
|
||||
|
||||
func _physics_process(delta : float) -> void:
|
||||
func _physics_process(delta: float) -> void:
|
||||
var previous_global_position: Vector3 = global_position
|
||||
global_position += velocity * delta
|
||||
if shape_cast.is_colliding():
|
||||
for collision_id in shape_cast.get_collision_count():
|
||||
var collider : Object = shape_cast.get_collider(collision_id)
|
||||
# handle collision
|
||||
if shape_cast:
|
||||
var local_global_position: Vector3 = to_local(previous_global_position)
|
||||
shape_cast.target_position = Vector3(
|
||||
local_global_position.x, -local_global_position.z, local_global_position.y)
|
||||
shape_cast.target_position = shape_cast.target_position
|
||||
if shape_cast.is_colliding():
|
||||
var collider: Node = shape_cast.get_collider(0)
|
||||
if collider is Player:
|
||||
if is_multiplayer_authority():
|
||||
assert(shooter)
|
||||
collider.health_component.damage.rpc(25, shooter.player_id, shooter.team_id)
|
||||
queue_free()
|
||||
|
||||
## This method is a parameterized constructor for the [ChainGunProjectile] scene of the [ChainGun]
|
||||
static func new_projectile(
|
||||
origin : Marker3D,
|
||||
inherited_velocity : Vector3,
|
||||
_shooter : MatchParticipant,
|
||||
scene : PackedScene
|
||||
) -> ChainGunProjectile:
|
||||
var projectile : ChainGunProjectile = scene.instantiate()
|
||||
projectile.shooter = _shooter
|
||||
projectile.transform = origin.global_transform
|
||||
projectile.velocity = origin.global_basis.z.normalized() * projectile.speed + inherited_velocity
|
||||
return projectile
|
||||
collider.damage.emit(source, collider, damage)
|
||||
destroy(shape_cast.collision_result[0].point)
|
||||
|
|
|
|||
|
|
@ -9,17 +9,15 @@ height = 2.5
|
|||
|
||||
[node name="ChainGunProjectile" instance=ExtResource("1_p7mmn")]
|
||||
script = ExtResource("2_jjfwk")
|
||||
speed = 128.0
|
||||
lifespan = 3.0
|
||||
damage = 15
|
||||
|
||||
[node name="ShapeCast3D" type="ShapeCast3D" parent="." index="0"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 4.29628)
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 1.25)
|
||||
shape = SubResource("CapsuleShape3D_dbodg")
|
||||
target_position = Vector3(0, -3, 0)
|
||||
target_position = Vector3(0, 0, 0)
|
||||
collision_mask = 2147483653
|
||||
|
||||
[node name="tracerinner" parent="." index="1"]
|
||||
transform = Transform3D(0.0505494, 0, 0, 0, 0.0505494, 0, 0, 0, 0.0505494, 0, 0, 2.49628)
|
||||
|
||||
[node name="Timer" type="Timer" parent="." index="2"]
|
||||
autostart = true
|
||||
|
||||
[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"]
|
||||
transform = Transform3D(0.0505494, 0, 0, 0, 0.0505494, 0, 0, 0, 0.0505494, 0, 0, 2.5)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
[gd_resource type="ShaderMaterial" load_steps=4 format=3 uid="uid://c80t026c2gpxx"]
|
||||
|
||||
[ext_resource type="Shader" uid="uid://d2aeepgs6xnsg" path="res://entities/weapons/grenade_launcher/assets/resources/explosion_shader.tres" id="1_dmawi"]
|
||||
[ext_resource type="Texture2D" uid="uid://dt6c44uknktrg" path="res://entities/weapons/grenade_launcher/assets/textures/explosion_texture_alb.png" id="2_4up7w"]
|
||||
|
||||
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_itmuc"]
|
||||
load_path = "res://.godot/imported/explosion_texture_flowmap.png-3137b307308713f172b3be796ef584b9.s3tc.ctex"
|
||||
|
||||
[resource]
|
||||
render_priority = 0
|
||||
shader = ExtResource("1_dmawi")
|
||||
shader_parameter/albedo = Color(0.941176, 0.941176, 0.941176, 1)
|
||||
shader_parameter/particles_anim_h_frames = 5
|
||||
shader_parameter/particles_anim_v_frames = 5
|
||||
shader_parameter/particles_anim_loop = false
|
||||
shader_parameter/smoothing = true
|
||||
shader_parameter/flow_strength = 0.015
|
||||
shader_parameter/texture_albedo = ExtResource("2_4up7w")
|
||||
shader_parameter/texture_flow = SubResource("CompressedTexture2D_itmuc")
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
[gd_resource type="Shader" format=3 uid="uid://d2aeepgs6xnsg"]
|
||||
|
||||
[resource]
|
||||
code = "shader_type spatial;
|
||||
render_mode unshaded;
|
||||
|
||||
// @TODO: add support for `Keep scale` billboard option so that we can spawn
|
||||
// different scale using a curve in the particles process material
|
||||
|
||||
uniform vec4 albedo : source_color;
|
||||
uniform sampler2D texture_albedo : source_color,filter_linear_mipmap,repeat_disable;
|
||||
uniform sampler2D texture_flow : hint_normal,filter_linear_mipmap,repeat_disable;
|
||||
uniform int particles_anim_h_frames;
|
||||
uniform int particles_anim_v_frames;
|
||||
uniform bool particles_anim_loop;
|
||||
uniform bool smoothing = false;
|
||||
uniform float flow_strength = 0.015;
|
||||
|
||||
varying vec2 next_UV;
|
||||
varying float timer;
|
||||
|
||||
void vertex() {
|
||||
mat4 mat_world = mat4(normalize(INV_VIEW_MATRIX[0]), normalize(INV_VIEW_MATRIX[1]) ,normalize(INV_VIEW_MATRIX[2]), MODEL_MATRIX[3]);
|
||||
mat_world = mat_world * mat4(vec4(cos(INSTANCE_CUSTOM.x), -sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
|
||||
MODELVIEW_MATRIX = VIEW_MATRIX * mat_world;
|
||||
MODELVIEW_NORMAL_MATRIX = mat3(MODELVIEW_MATRIX);
|
||||
float h_frames = float(particles_anim_h_frames);
|
||||
float v_frames = float(particles_anim_v_frames);
|
||||
float particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);
|
||||
float particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));
|
||||
if (!particles_anim_loop) {
|
||||
particle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);
|
||||
} else {
|
||||
particle_frame = mod(particle_frame, particle_total_frames);
|
||||
}
|
||||
UV /= vec2(h_frames, v_frames);
|
||||
next_UV = UV;
|
||||
UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);
|
||||
next_UV += vec2(mod(particle_frame + 1.0, h_frames) / h_frames, floor((particle_frame + 1.0 + 0.5) / h_frames) / v_frames);
|
||||
timer = fract(INSTANCE_CUSTOM.y * h_frames * v_frames);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 albedo_tex;
|
||||
if (smoothing) {
|
||||
vec2 flow_tex = texture(texture_flow, UV).rg;
|
||||
flow_tex -= 0.5;
|
||||
flow_tex *= 2.0;
|
||||
vec2 flow_uv = UV + flow_tex * timer * -flow_strength;
|
||||
vec2 reverse_flow_uv = next_UV + flow_tex * (1.0 - timer) * flow_strength;
|
||||
albedo_tex = mix(texture(texture_albedo, flow_uv), texture(texture_albedo, reverse_flow_uv), timer);
|
||||
} else {
|
||||
albedo_tex = texture(texture_albedo, UV);
|
||||
}
|
||||
albedo_tex *= COLOR;
|
||||
ALBEDO = albedo.rgb * albedo_tex.rgb;
|
||||
ALPHA *= albedo.a * albedo_tex.a;
|
||||
}"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[gd_resource type="CompressedTexture2D" format=3 uid="uid://b0003clv8jv3n"]
|
||||
|
||||
[resource]
|
||||
load_path = "res://.godot/imported/explosion_texture_alb.png-1ff7964ed65c51040c0956ec9ef6a893.s3tc.ctex"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[gd_resource type="CompressedTexture2D" format=3 uid="uid://ddlahr6k2hg5g"]
|
||||
|
||||
[resource]
|
||||
load_path = "res://.godot/imported/explosion_texture_flowmap.png-3137b307308713f172b3be796ef584b9.s3tc.ctex"
|
||||
|
|
@ -10,4 +10,3 @@ shading_mode = 0
|
|||
vertex_color_use_as_albedo = true
|
||||
albedo_color = Color(0.860369, 0.860369, 0.860369, 1)
|
||||
albedo_texture = SubResource("CompressedTexture2D_uiuwk")
|
||||
billboard_keep_scale = true
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
[gd_resource type="ParticleProcessMaterial" load_steps=5 format=3 uid="uid://c78xg30ynapmt"]
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_q75yo"]
|
||||
offsets = PackedFloat32Array(0.00806452, 1)
|
||||
colors = PackedColorArray(1, 1, 1, 0, 1, 1, 1, 0)
|
||||
|
||||
[sub_resource type="GradientTexture1D" id="GradientTexture1D_gt2l8"]
|
||||
gradient = SubResource("Gradient_q75yo")
|
||||
|
||||
[sub_resource type="Curve" id="Curve_jt11q"]
|
||||
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(0.980263, 0), 0.0, 0.0, 0, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="CurveTexture" id="CurveTexture_nb67t"]
|
||||
curve = SubResource("Curve_jt11q")
|
||||
|
||||
[resource]
|
||||
lifetime_randomness = 1.0
|
||||
emission_shape = 1
|
||||
emission_sphere_radius = 0.3
|
||||
spread = 180.0
|
||||
gravity = Vector3(0, -2, 0)
|
||||
scale_min = 0.75
|
||||
scale_max = 1.5
|
||||
scale_curve = SubResource("CurveTexture_nb67t")
|
||||
color = Color(5, 2, 1, 1)
|
||||
color_ramp = SubResource("GradientTexture1D_gt2l8")
|
||||
|
|
@ -9,23 +9,28 @@ max_value = 5.0
|
|||
_data = [Vector2(0, 5), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="Curve" id="Curve_1s1qr"]
|
||||
[sub_resource type="Curve" id="Curve_26exw"]
|
||||
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="CurveXYZTexture" id="CurveXYZTexture_eoauk"]
|
||||
curve_x = SubResource("Curve_ei0ls")
|
||||
curve_y = SubResource("Curve_q68ud")
|
||||
curve_z = SubResource("Curve_1s1qr")
|
||||
curve_z = SubResource("Curve_26exw")
|
||||
|
||||
[resource]
|
||||
particle_flag_align_y = true
|
||||
emission_shape = 1
|
||||
emission_sphere_radius = 0.2
|
||||
spread = 180.0
|
||||
initial_velocity_min = 20.0
|
||||
direction = Vector3(0, 1, 0)
|
||||
spread = 90.0
|
||||
initial_velocity_min = 15.0
|
||||
initial_velocity_max = 25.0
|
||||
gravity = Vector3(0, -19.6, 0)
|
||||
radial_velocity_min = 10.0
|
||||
radial_velocity_max = 10.0
|
||||
gravity = Vector3(0, 0, 0)
|
||||
scale_min = 0.2
|
||||
scale_curve = SubResource("CurveXYZTexture_eoauk")
|
||||
color = Color(5, 2, 1, 1)
|
||||
turbulence_influence_min = 0.01
|
||||
turbulence_influence_max = 0.01
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
[gd_resource type="SphereShape3D" format=3 uid="uid://b8d4jwso2dcdu"]
|
||||
|
||||
[resource]
|
||||
radius = 0.062
|
||||
|
|
@ -4,15 +4,16 @@ importer="texture"
|
|||
type="CompressedTexture2D"
|
||||
uid="uid://chchhrpwmho2c"
|
||||
path.s3tc="res://.godot/imported/Flare00.png-75515c1e8df86aba86a9e55bb8ca7901.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/Flare00.png-75515c1e8df86aba86a9e55bb8ca7901.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://entities/weapons/grenade_launcher/assets/textures/Flare00.png"
|
||||
dest_files=["res://.godot/imported/Flare00.png-75515c1e8df86aba86a9e55bb8ca7901.s3tc.ctex"]
|
||||
dest_files=["res://.godot/imported/Flare00.png-75515c1e8df86aba86a9e55bb8ca7901.s3tc.ctex", "res://.godot/imported/Flare00.png-75515c1e8df86aba86a9e55bb8ca7901.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
|
|
@ -0,0 +1,36 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dt6c44uknktrg"
|
||||
path.s3tc="res://.godot/imported/explosion_texture_alb.png-1ff7964ed65c51040c0956ec9ef6a893.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/explosion_texture_alb.png-1ff7964ed65c51040c0956ec9ef6a893.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://entities/weapons/grenade_launcher/assets/textures/explosion_texture_alb.png"
|
||||
dest_files=["res://.godot/imported/explosion_texture_alb.png-1ff7964ed65c51040c0956ec9ef6a893.s3tc.ctex", "res://.godot/imported/explosion_texture_alb.png-1ff7964ed65c51040c0956ec9ef6a893.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=true
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
|
|
@ -0,0 +1,36 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b6pjaen40x34u"
|
||||
path.s3tc="res://.godot/imported/explosion_texture_flowmap.png-3137b307308713f172b3be796ef584b9.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/explosion_texture_flowmap.png-3137b307308713f172b3be796ef584b9.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://entities/weapons/grenade_launcher/assets/textures/explosion_texture_flowmap.png"
|
||||
dest_files=["res://.godot/imported/explosion_texture_flowmap.png-3137b307308713f172b3be796ef584b9.s3tc.ctex", "res://.godot/imported/explosion_texture_flowmap.png-3137b307308713f172b3be796ef584b9.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=1
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
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=0
|
||||
|
|
@ -1,13 +1,29 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name GrenadeLauncherProjectileExplosion extends Node3D
|
||||
|
||||
## The component responsible for explosion damages
|
||||
@export var explosive_damage : ExplosiveDamageComponent
|
||||
@export var explosive_damage : ExplosiveDamage
|
||||
|
||||
## The component responsible for owner identification
|
||||
var shooter : MatchParticipant:
|
||||
set(value):
|
||||
shooter = value
|
||||
explosive_damage.damage_dealer = value
|
||||
## The source of explosion.
|
||||
var source: Node:
|
||||
set = set_source
|
||||
|
||||
func set_source(new_source: Node) -> void:
|
||||
source = new_source
|
||||
explosive_damage.source = new_source
|
||||
|
||||
## This is increment by 1 when a particle emitter is finished
|
||||
var _finished_count : int = 0
|
||||
|
|
@ -25,14 +41,3 @@ func _on_finished() -> void:
|
|||
if _finished_count == $Particles.get_child_count():
|
||||
queue_free()
|
||||
|
||||
## This method is a static factory constructor for the
|
||||
## [GrenadeLauncherProjectileExplosion] scene of the [GrenadeLauncherProjectile].
|
||||
static func new_explosion(
|
||||
origin : Vector3,
|
||||
_shooter : MatchParticipant,
|
||||
scene : PackedScene
|
||||
) -> GrenadeLauncherProjectileExplosion:
|
||||
var explosion : GrenadeLauncherProjectileExplosion = scene.instantiate()
|
||||
explosion.position = origin
|
||||
explosion.shooter = _shooter
|
||||
return explosion
|
||||
|
|
|
|||
|
|
@ -1,11 +1,37 @@
|
|||
[gd_scene load_steps=9 format=3 uid="uid://bb43ae1f5j8lw"]
|
||||
[gd_scene load_steps=13 format=3 uid="uid://bb43ae1f5j8lw"]
|
||||
|
||||
[ext_resource type="Script" path="res://entities/weapons/grenade_launcher/explosion.gd" id="1_xb4jo"]
|
||||
[ext_resource type="PackedScene" uid="uid://qb5sf7awdeui" path="res://entities/components/explosive_damage/explosive_damage.tscn" id="2_rq8du"]
|
||||
[ext_resource type="Script" path="res://entities/projectiles/damages/explosive_damage.gd" id="2_r5wb7"]
|
||||
[ext_resource type="Material" uid="uid://v7s5w3uw4hmb" path="res://entities/weapons/grenade_launcher/assets/resources/particle_process_material_sparks.tres" id="3_4u6ue"]
|
||||
[ext_resource type="Material" uid="uid://c80t026c2gpxx" path="res://entities/weapons/grenade_launcher/assets/resources/explosion_material.tres" id="3_tsixm"]
|
||||
[ext_resource type="Material" uid="uid://bbqjhgs44rnss" path="res://entities/weapons/grenade_launcher/assets/resources/material_flare.tres" id="4_634rq"]
|
||||
[ext_resource type="Material" uid="uid://cykwajkj5aaqx" path="res://entities/weapons/grenade_launcher/assets/resources/particle_process_material_flash.tres" id="5_mntrp"]
|
||||
[ext_resource type="Material" uid="uid://c78xg30ynapmt" path="res://entities/weapons/grenade_launcher/assets/resources/particle_process_material_fire.tres" id="6_s25g3"]
|
||||
|
||||
[sub_resource type="Curve" id="Curve_m5722"]
|
||||
_data = [Vector2(0.247387, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_1htx7"]
|
||||
radius = 10.6
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_dlm7y"]
|
||||
emission_shape_offset = Vector3(0, 0.25, 0)
|
||||
angle_min = -20.0
|
||||
angle_max = 20.0
|
||||
direction = Vector3(0, 1, 0)
|
||||
initial_velocity_min = 2.0
|
||||
initial_velocity_max = 2.0
|
||||
gravity = Vector3(0, 0, 0)
|
||||
damping_min = 1.0
|
||||
damping_max = 1.0
|
||||
color = Color(1.5, 1.5, 1.5, 1)
|
||||
hue_variation_max = 0.05
|
||||
anim_speed_min = 1.0
|
||||
anim_speed_max = 1.0
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_35tvq"]
|
||||
material = ExtResource("3_tsixm")
|
||||
size = Vector2(5, 5)
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_wt5ts"]
|
||||
material = ExtResource("4_634rq")
|
||||
|
|
@ -17,18 +43,39 @@ material = ExtResource("4_634rq")
|
|||
script = ExtResource("1_xb4jo")
|
||||
explosive_damage = NodePath("ExplosiveDamage")
|
||||
|
||||
[node name="ExplosiveDamage" parent="." instance=ExtResource("2_rq8du")]
|
||||
[node name="ExplosiveDamage" type="Area3D" parent="."]
|
||||
collision_mask = 13
|
||||
script = ExtResource("2_r5wb7")
|
||||
damage = 128
|
||||
blast_force = 2000
|
||||
falloff = SubResource("Curve_m5722")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="ExplosiveDamage"]
|
||||
shape = SubResource("SphereShape3D_1htx7")
|
||||
|
||||
[node name="Particles" type="Node3D" parent="."]
|
||||
|
||||
[node name="Explosion" type="GPUParticles3D" parent="Particles"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||
emitting = false
|
||||
amount = 1
|
||||
lifetime = 2.0
|
||||
one_shot = true
|
||||
speed_scale = 3.0
|
||||
fixed_fps = 60
|
||||
draw_order = 3
|
||||
process_material = SubResource("ParticleProcessMaterial_dlm7y")
|
||||
draw_pass_1 = SubResource("QuadMesh_35tvq")
|
||||
|
||||
[node name="Sparks" type="GPUParticles3D" parent="Particles"]
|
||||
process_priority = 1
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.00311279, 0.0152655, -0.000196457)
|
||||
emitting = false
|
||||
amount = 20
|
||||
amount = 12
|
||||
lifetime = 0.3
|
||||
one_shot = true
|
||||
explosiveness = 1.0
|
||||
fixed_fps = 60
|
||||
randomness = 0.3
|
||||
process_material = ExtResource("3_4u6ue")
|
||||
draw_pass_1 = SubResource("QuadMesh_wt5ts")
|
||||
|
||||
|
|
@ -41,16 +88,3 @@ explosiveness = 1.0
|
|||
fixed_fps = 60
|
||||
process_material = ExtResource("5_mntrp")
|
||||
draw_pass_1 = SubResource("QuadMesh_mv2qw")
|
||||
|
||||
[node name="Fire" type="GPUParticles3D" parent="Particles"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.00311279, 0.0152655, -0.000196457)
|
||||
emitting = false
|
||||
amount = 13
|
||||
lifetime = 0.55
|
||||
one_shot = true
|
||||
explosiveness = 1.0
|
||||
fixed_fps = 60
|
||||
process_material = ExtResource("6_s25g3")
|
||||
draw_pass_1 = SubResource("QuadMesh_wt5ts")
|
||||
|
||||
[editable path="ExplosiveDamage"]
|
||||
|
|
|
|||
|
|
@ -1,29 +1,39 @@
|
|||
class_name GrenadeLauncher extends Node3D
|
||||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name GrenadeLauncher extends Weapon
|
||||
|
||||
@export_range(0, 24) var ammo : int = 24
|
||||
@export var fire_rate : float = 1. # seconds
|
||||
@export var reload_time : float = 1. # seconds
|
||||
|
||||
@export_category("Animations")
|
||||
@export var _PROJECTILE : PackedScene
|
||||
## The factor of velocity from the owner of this [Weapon] inherited by this [Projectile].
|
||||
@export_range(0., 1., .01) var inheritance : float = 1.
|
||||
@export var anim_player : AnimationPlayer
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
pass # Replace with function body.
|
||||
|
||||
func trigger() -> void:
|
||||
func _on_triggered() -> void:
|
||||
# play the fire animation
|
||||
anim_player.play("fire")
|
||||
# init projectile
|
||||
var projectile : Node3D = $ProjectileSpawner.new_projectile(
|
||||
GrenadeLauncherProjectile,
|
||||
owner.linear_velocity,
|
||||
owner.match_participant
|
||||
)
|
||||
# add to owner of player for now
|
||||
Global.type.add_child(projectile)
|
||||
var projectile : GrenadeLauncherProjectile = _PROJECTILE.instantiate()
|
||||
var direction: Vector3 = $Nozzle.global_basis.z.normalized()
|
||||
projectile.velocity = direction * projectile.speed + owner.linear_velocity * inheritance
|
||||
projectile.source = owner
|
||||
owner.add_child(projectile)
|
||||
projectile.global_position = $Nozzle.global_position
|
||||
projectile.global_basis = $Nozzle.global_basis
|
||||
projectile.shape_cast.add_exception(owner)
|
||||
|
||||
func _on_visibility_changed() -> void:
|
||||
if self.visible:
|
||||
anim_player.play("equip")
|
||||
#anim_player.play("equip")
|
||||
#self.sounds.play("equip")
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://cstl7yxc75572"]
|
||||
[gd_scene load_steps=4 format=3 uid="uid://cstl7yxc75572"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bbp260t2qivxk" path="res://entities/weapons/grenade_launcher/assets/models/grenade_launcher.glb" id="1_keuur"]
|
||||
[ext_resource type="Script" path="res://entities/weapons/grenade_launcher/grenade_launcher.gd" id="2_38xn3"]
|
||||
[ext_resource type="PackedScene" uid="uid://dak767xehqa6x" path="res://entities/weapons/grenade_launcher/projectile.tscn" id="3_rg5nk"]
|
||||
[ext_resource type="Script" path="res://entities/components/projectile_spawner.gd" id="4_5h5sw"]
|
||||
|
||||
[node name="GrenadeLauncher" node_paths=PackedStringArray("anim_player") instance=ExtResource("1_keuur")]
|
||||
script = ExtResource("2_38xn3")
|
||||
_PROJECTILE = ExtResource("3_rg5nk")
|
||||
inheritance = 0.8
|
||||
anim_player = NodePath("AnimationPlayer")
|
||||
|
||||
[node name="ProjectileSpawner" type="Node3D" parent="." index="0"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.266945)
|
||||
script = ExtResource("4_5h5sw")
|
||||
PROJECTILE = ExtResource("3_rg5nk")
|
||||
inheritance = 0.75
|
||||
ammo = 12
|
||||
max_ammo = 12
|
||||
ammo_usage = 1
|
||||
|
||||
[node name="barrel" parent="Armature/Skeleton3D" index="0"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.19209e-07, 0)
|
||||
|
|
@ -24,4 +22,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.082471, -0.0653242)
|
|||
[node name="main" parent="Armature/Skeleton3D" index="2"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.19209e-07, 0)
|
||||
|
||||
[node name="Nozzle" type="Marker3D" parent="." index="2"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.259886)
|
||||
|
||||
[connection signal="triggered" from="." to="." method="_on_triggered"]
|
||||
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
|
||||
|
|
|
|||
|
|
@ -12,46 +12,48 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name GrenadeLauncherProjectile extends RigidBody3D
|
||||
class_name GrenadeLauncherProjectile extends ArcProjectile
|
||||
|
||||
@export_category("Parameters")
|
||||
@export var EXPLOSION : PackedScene
|
||||
@export var collider : CollisionShape3D
|
||||
@export var speed : float = 44. # m/s
|
||||
@export var lifespan : float = 1.6 # in seconds
|
||||
## The time before
|
||||
@export var fuse_time : float = 1.
|
||||
@export_range(0., 1.) var bounce_velocity_modifier: float = .24
|
||||
|
||||
var shooter : MatchParticipant
|
||||
var pending_explosion : bool = false
|
||||
var fuse_timer := Timer.new()
|
||||
|
||||
## Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
$Timer.wait_time = lifespan
|
||||
$Timer.start()
|
||||
fuse_timer.wait_time = fuse_time
|
||||
fuse_timer.one_shot = true
|
||||
fuse_timer.autostart = true
|
||||
add_child(fuse_timer)
|
||||
# remove exceptions after a short time so that it can collide again with owner
|
||||
get_tree().create_timer(.3).timeout.connect(func() -> void:
|
||||
shape_cast.clear_exceptions())
|
||||
|
||||
## This method enable collision signals to be emitted when the lifespan timer
|
||||
## is finished.
|
||||
func _on_timer_timeout() -> void:
|
||||
max_contacts_reported = 1
|
||||
set_contact_monitor(true)
|
||||
func _physics_process(delta : float) -> void:
|
||||
if not shape_cast: set_physics_process(false)
|
||||
velocity += _gravity * delta
|
||||
shape_cast.target_position = to_local(global_position + velocity * delta)
|
||||
shape_cast.force_shapecast_update()
|
||||
if shape_cast.is_colliding():
|
||||
var collider: Node = shape_cast.get_collider(0)
|
||||
var hit_point: Vector3 = shape_cast.get_collision_point(0)
|
||||
# explode on player hit or when fuse timer is exhausted
|
||||
if collider is Player or fuse_timer.is_stopped():
|
||||
destroy(hit_point)
|
||||
return
|
||||
# bounce projectile
|
||||
var hit_normal : Vector3 = shape_cast.get_collision_normal(0)
|
||||
velocity = calc_bounce_velocity(velocity, hit_normal.normalized())
|
||||
global_position = hit_point + hit_normal.normalized() * shape_cast.shape.radius
|
||||
global_position += velocity * delta
|
||||
else:
|
||||
# move projectile
|
||||
global_position += velocity * delta
|
||||
|
||||
## This method is responsible for spawning the projectile explosion and freeing
|
||||
## it when a collision is detected once the lifespan timer is finished.
|
||||
func _on_body_entered(_body : Node) -> void:
|
||||
var explosion : GrenadeLauncherProjectileExplosion = \
|
||||
GrenadeLauncherProjectileExplosion.new_explosion(position, shooter, EXPLOSION)
|
||||
add_sibling(explosion)
|
||||
queue_free()
|
||||
## This method computes projectile bounce velocity.
|
||||
func calc_bounce_velocity(_velocity: Vector3, hit_normal: Vector3) -> Vector3:
|
||||
return mirror_vector_by_normal(_velocity, hit_normal.normalized()) * bounce_velocity_modifier
|
||||
|
||||
## This method is a static factory constructor for the [GrenadeLauncherProjectile]
|
||||
## scene of the [GrenadeLauncher].
|
||||
static func new_projectile(
|
||||
origin : Node3D,
|
||||
inherited_velocity : Vector3,
|
||||
_shooter : MatchParticipant,
|
||||
scene : PackedScene
|
||||
) -> GrenadeLauncherProjectile:
|
||||
var projectile : GrenadeLauncherProjectile = scene.instantiate()
|
||||
projectile.shooter = _shooter
|
||||
projectile.transform = origin.global_transform
|
||||
projectile.linear_velocity = origin.global_basis.z.normalized() * projectile.speed + inherited_velocity
|
||||
return projectile
|
||||
## This method computes reflected vector using the formula: R = V - 2 * (V dot N) * N
|
||||
func mirror_vector_by_normal(_velocity: Vector3, hit_normal_unit_vector: Vector3) -> Vector3:
|
||||
return _velocity - 2 * _velocity.dot(hit_normal_unit_vector) * hit_normal_unit_vector
|
||||
|
|
|
|||
|
|
@ -1,46 +1,30 @@
|
|||
[gd_scene load_steps=7 format=3 uid="uid://dak767xehqa6x"]
|
||||
[gd_scene load_steps=5 format=3 uid="uid://dak767xehqa6x"]
|
||||
|
||||
[ext_resource type="Script" path="res://entities/weapons/grenade_launcher/projectile.gd" id="1_i8v2u"]
|
||||
[ext_resource type="PackedScene" uid="uid://bb43ae1f5j8lw" path="res://entities/weapons/grenade_launcher/explosion.tscn" id="2_cxty7"]
|
||||
[ext_resource type="Script" path="res://addons/smoothing/smoothing.gd" id="3_wyge0"]
|
||||
|
||||
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_r263x"]
|
||||
rough = true
|
||||
bounce = 0.5
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_kipwv"]
|
||||
radius = 0.062
|
||||
[ext_resource type="Shape3D" uid="uid://b8d4jwso2dcdu" path="res://entities/weapons/grenade_launcher/assets/resources/projectile_shape.tres" id="3_fxdq6"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_miu4v"]
|
||||
albedo_color = Color(0.2, 0.2, 0.2, 1)
|
||||
|
||||
[node name="GrenadeLauncherProjectile" type="RigidBody3D" node_paths=PackedStringArray("collider")]
|
||||
collision_layer = 2147483648
|
||||
collision_mask = 2147483648
|
||||
mass = 5.0
|
||||
physics_material_override = SubResource("PhysicsMaterial_r263x")
|
||||
continuous_cd = true
|
||||
[node name="GrenadeLauncherProjectile" type="Node3D"]
|
||||
script = ExtResource("1_i8v2u")
|
||||
EXPLOSION = ExtResource("2_cxty7")
|
||||
collider = NodePath("CollisionShape3D")
|
||||
speed = 54.0
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
shape = SubResource("SphereShape3D_kipwv")
|
||||
[node name="ShapeCast3D" type="ShapeCast3D" parent="."]
|
||||
shape = ExtResource("3_fxdq6")
|
||||
target_position = Vector3(0, 0, 0)
|
||||
collision_mask = 2147483649
|
||||
|
||||
[node name="Timer" type="Timer" parent="."]
|
||||
one_shot = true
|
||||
|
||||
[node name="Smoothing" type="Node3D" parent="."]
|
||||
[node name="Head" type="CSGSphere3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.000792712, 0, -0.000432983)
|
||||
script = ExtResource("3_wyge0")
|
||||
flags = 3
|
||||
|
||||
[node name="Mesh" type="CSGSphere3D" parent="Smoothing"]
|
||||
radius = 0.062
|
||||
radial_segments = 24
|
||||
rings = 12
|
||||
material = SubResource("StandardMaterial3D_miu4v")
|
||||
|
||||
[connection signal="body_entered" from="." to="." method="_on_body_entered"]
|
||||
[connection signal="body_shape_entered" from="." to="." method="_on_body_shape_entered"]
|
||||
[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"]
|
||||
[node name="Body" type="CSGCylinder3D" parent="."]
|
||||
transform = Transform3D(0.125, 0, -2.64698e-23, 2.64698e-23, -2.71011e-09, 0.125, 0, -0.062, -5.46392e-09, 0, 0, -0.062)
|
||||
sides = 12
|
||||
material = SubResource("StandardMaterial3D_miu4v")
|
||||
|
|
|
|||
33
entities/weapons/space_gun/explosion.gd
Normal file
33
entities/weapons/space_gun/explosion.gd
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name SpaceGunProjectileExplosion extends Node3D
|
||||
|
||||
@export var explosive_damage : ExplosiveDamage
|
||||
|
||||
var source: Node:
|
||||
set = set_source
|
||||
|
||||
func set_source(new_source: Node) -> void:
|
||||
source = new_source
|
||||
explosive_damage.source = new_source
|
||||
|
||||
@onready var particles : GPUParticles3D = $Fire
|
||||
|
||||
func _ready() -> void:
|
||||
top_level = true
|
||||
particles.emitting = true
|
||||
particles.finished.connect(func() -> void: queue_free())
|
||||
if source:
|
||||
explosive_damage.source = source
|
||||
68
entities/weapons/space_gun/explosion.tscn
Normal file
68
entities/weapons/space_gun/explosion.tscn
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
[gd_scene load_steps=12 format=3 uid="uid://00uv1dfxlv47"]
|
||||
|
||||
[ext_resource type="Script" path="res://entities/weapons/space_gun/explosion.gd" id="1_3xpsk"]
|
||||
[ext_resource type="Script" path="res://entities/projectiles/damages/explosive_damage.gd" id="2_beh8d"]
|
||||
|
||||
[sub_resource type="Curve" id="Curve_l54ao"]
|
||||
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="CurveTexture" id="CurveTexture_08dbu"]
|
||||
curve = SubResource("Curve_l54ao")
|
||||
|
||||
[sub_resource type="Curve" id="Curve_21akj"]
|
||||
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="CurveTexture" id="CurveTexture_b4xy8"]
|
||||
curve = SubResource("Curve_21akj")
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_i271t"]
|
||||
direction = Vector3(0, 1, 0)
|
||||
spread = 180.0
|
||||
initial_velocity_min = 24.0
|
||||
initial_velocity_max = 24.0
|
||||
scale_curve = SubResource("CurveTexture_08dbu")
|
||||
turbulence_enabled = true
|
||||
turbulence_influence_over_life = SubResource("CurveTexture_b4xy8")
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wpu51"]
|
||||
albedo_color = Color(0.819608, 0, 0, 1)
|
||||
emission_enabled = true
|
||||
emission = Color(1, 0.254902, 0.109804, 1)
|
||||
emission_energy_multiplier = 4.0
|
||||
|
||||
[sub_resource type="SphereMesh" id="SphereMesh_k7x87"]
|
||||
material = SubResource("StandardMaterial3D_wpu51")
|
||||
|
||||
[sub_resource type="Curve" id="Curve_ck840"]
|
||||
_data = [Vector2(0.254355, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_s611t"]
|
||||
radius = 7.2
|
||||
|
||||
[node name="SpaceGunProjectileExplosion" type="Node3D" node_paths=PackedStringArray("explosive_damage")]
|
||||
top_level = true
|
||||
script = ExtResource("1_3xpsk")
|
||||
explosive_damage = NodePath("ExplosiveDamage")
|
||||
|
||||
[node name="Fire" type="GPUParticles3D" parent="."]
|
||||
emitting = false
|
||||
amount = 24
|
||||
lifetime = 0.5
|
||||
one_shot = true
|
||||
explosiveness = 1.0
|
||||
fixed_fps = 60
|
||||
process_material = SubResource("ParticleProcessMaterial_i271t")
|
||||
draw_pass_1 = SubResource("SphereMesh_k7x87")
|
||||
|
||||
[node name="ExplosiveDamage" type="Area3D" parent="."]
|
||||
collision_layer = 0
|
||||
collision_mask = 13
|
||||
script = ExtResource("2_beh8d")
|
||||
damage = 102
|
||||
falloff = SubResource("Curve_ck840")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="ExplosiveDamage"]
|
||||
shape = SubResource("SphereShape3D_s611t")
|
||||
|
|
@ -12,63 +12,5 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name SpaceGunProjectile extends Node3D
|
||||
|
||||
@export_category("Parameters")
|
||||
@export var EXPLOSION : PackedScene
|
||||
@export var speed : float = 78.4 # m/s
|
||||
@export var lifespan : float = 5.0 # in seconds
|
||||
@export var _time_to_trail_enable : float = 0.05 # in seconds
|
||||
|
||||
@onready var shape_cast : ShapeCast3D = $ShapeCast3D
|
||||
@onready var game : Node3D = get_tree().get_current_scene()
|
||||
@onready var projectile_trail : Trail3D = $Smoothing/ProjectileTrail
|
||||
|
||||
var velocity : Vector3 = Vector3.ZERO
|
||||
var shooter : MatchParticipant
|
||||
|
||||
func _ready() -> void:
|
||||
var lifespan_timer : Timer = Timer.new()
|
||||
lifespan_timer.wait_time = lifespan
|
||||
lifespan_timer.one_shot = true
|
||||
lifespan_timer.autostart = true
|
||||
lifespan_timer.timeout.connect(self_destruct)
|
||||
add_child(lifespan_timer)
|
||||
projectile_trail._trail_enabled = false
|
||||
var trail_enable_timer : Timer = Timer.new()
|
||||
trail_enable_timer.wait_time = _time_to_trail_enable
|
||||
trail_enable_timer.one_shot = true
|
||||
trail_enable_timer.autostart = true
|
||||
trail_enable_timer.timeout.connect(_enable_trail)
|
||||
add_child(trail_enable_timer)
|
||||
|
||||
func self_destruct() -> void:
|
||||
explode(position)
|
||||
|
||||
func explode(spawn_position : Vector3) -> void:
|
||||
game.add_child(SpaceGunProjectileExplosion.new_explosion(spawn_position, shooter, EXPLOSION))
|
||||
queue_free()
|
||||
|
||||
func _physics_process(delta : float) -> void:
|
||||
var previous_position : Vector3 = global_position
|
||||
global_position += velocity * delta
|
||||
shape_cast.target_position = to_local(previous_position)
|
||||
if shape_cast.is_colliding():
|
||||
var contact_point : Vector3 = shape_cast.collision_result[0].point
|
||||
explode(contact_point)
|
||||
|
||||
## This method is a parameterized constructor for the [SpaceGunProjectile] scene of the [SpaceGun]
|
||||
static func new_projectile(
|
||||
origin : Marker3D,
|
||||
inherited_velocity : Vector3,
|
||||
_shooter : MatchParticipant,
|
||||
scene : PackedScene
|
||||
) -> SpaceGunProjectile:
|
||||
var projectile : SpaceGunProjectile = scene.instantiate()
|
||||
projectile.shooter = _shooter
|
||||
projectile.transform = origin.global_transform
|
||||
projectile.velocity = origin.global_basis.z.normalized() * projectile.speed + inherited_velocity
|
||||
return projectile
|
||||
|
||||
func _enable_trail() -> void:
|
||||
projectile_trail._trail_enabled = true
|
||||
## This class defines the projectile of a [SpaceGun].
|
||||
class_name SpaceGunProjectile extends ExplosiveProjectile
|
||||
|
|
|
|||
|
|
@ -1,53 +1,47 @@
|
|||
[gd_scene load_steps=8 format=3 uid="uid://dn1tcakam5egs"]
|
||||
[gd_scene load_steps=7 format=3 uid="uid://61mogxsei3rq"]
|
||||
|
||||
[ext_resource type="Script" path="res://entities/weapons/space_gun/projectile.gd" id="1_4j1dp"]
|
||||
[ext_resource type="PackedScene" path="res://entities/weapons/space_gun/projectile_explosion.tscn" id="2_llml6"]
|
||||
[ext_resource type="Script" path="res://addons/smoothing/smoothing.gd" id="3_dmi64"]
|
||||
[ext_resource type="Script" path="res://entities/weapons/space_gun/projectile_trail.gd" id="3_ygqbh"]
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_umtte"]
|
||||
radius = 0.15
|
||||
[ext_resource type="Script" path="res://entities/weapons/space_gun/projectile.gd" id="2_n6e2j"]
|
||||
[ext_resource type="PackedScene" uid="uid://00uv1dfxlv47" path="res://entities/weapons/space_gun/explosion.tscn" id="2_s8tpq"]
|
||||
[ext_resource type="Shape3D" uid="uid://dkhoeg2bwfbga" path="res://entities/weapons/space_gun/projectile_shape.tres" id="3_j3hyk"]
|
||||
[ext_resource type="Script" path="res://entities/weapons/space_gun/projectile_trail.gd" id="4_ojig2"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_o6j55"]
|
||||
albedo_color = Color(0, 0.498039, 0.854902, 1)
|
||||
emission_enabled = true
|
||||
emission = Color(0.482353, 0.65098, 1, 1)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_4a265"]
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wqkw2"]
|
||||
transparency = 1
|
||||
blend_mode = 1
|
||||
cull_mode = 2
|
||||
shading_mode = 0
|
||||
vertex_color_use_as_albedo = true
|
||||
|
||||
[node name="Projectile" type="Node3D"]
|
||||
script = ExtResource("1_4j1dp")
|
||||
EXPLOSION = ExtResource("2_llml6")
|
||||
[node name="SpaceGunProjectile" type="Node3D"]
|
||||
top_level = true
|
||||
script = ExtResource("2_n6e2j")
|
||||
EXPLOSION = ExtResource("2_s8tpq")
|
||||
speed = 78.4
|
||||
lifespan = 6.0
|
||||
|
||||
[node name="ShapeCast3D" type="ShapeCast3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.2, 0, 0, 0, 1, 0, 0, 0)
|
||||
shape = SubResource("SphereShape3D_umtte")
|
||||
shape = ExtResource("3_j3hyk")
|
||||
target_position = Vector3(0, 0, 0)
|
||||
margin = 0.1
|
||||
max_results = 1
|
||||
collision_mask = 2147483649
|
||||
collide_with_areas = true
|
||||
debug_shape_custom_color = Color(1, 0, 0, 1)
|
||||
|
||||
[node name="Smoothing" type="Node3D" parent="."]
|
||||
script = ExtResource("3_dmi64")
|
||||
target = NodePath("..")
|
||||
|
||||
[node name="Mesh" type="CSGSphere3D" parent="Smoothing"]
|
||||
[node name="Mesh" type="CSGSphere3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.2, 0, 0, 0, 1, 0, 0, 0)
|
||||
radius = 0.15
|
||||
radial_segments = 24
|
||||
rings = 8
|
||||
material = SubResource("StandardMaterial3D_o6j55")
|
||||
|
||||
[node name="ProjectileTrail" type="MeshInstance3D" parent="Smoothing"]
|
||||
material_override = SubResource("StandardMaterial3D_4a265")
|
||||
skeleton = NodePath("../..")
|
||||
script = ExtResource("3_ygqbh")
|
||||
[node name="ProjectileTrail" type="MeshInstance3D" parent="."]
|
||||
material_override = SubResource("StandardMaterial3D_wqkw2")
|
||||
script = ExtResource("4_ojig2")
|
||||
_start_width = 0.15
|
||||
_lifespan = 0.25
|
||||
_start_color = Color(0, 0.498039, 0.854902, 1)
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name SpaceGunProjectileExplosion extends Node3D
|
||||
|
||||
@export var explosive_damage : ExplosiveDamageComponent
|
||||
|
||||
@onready var fire : GPUParticles3D = $Fire
|
||||
|
||||
var explosion_effect_pending : bool = false
|
||||
|
||||
var shooter : MatchParticipant:
|
||||
set(value):
|
||||
shooter = value
|
||||
assert(explosive_damage)
|
||||
explosive_damage.damage_dealer = value
|
||||
|
||||
func _ready() -> void:
|
||||
fire.emitting = true
|
||||
fire.finished.connect(func() -> void: queue_free())
|
||||
|
||||
## This method is a parameterized constructor for the [SpaceGunProjectileExplosion] scene of the [SpaceGunProjectile]
|
||||
static func new_explosion(spawn_position : Vector3, _shooter : MatchParticipant, scene : PackedScene) -> SpaceGunProjectileExplosion:
|
||||
var explosion : SpaceGunProjectileExplosion = scene.instantiate()
|
||||
explosion.shooter = _shooter
|
||||
explosion.position = spawn_position
|
||||
return explosion
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
[gd_scene load_steps=8 format=3 uid="uid://8atq41j7wd55"]
|
||||
|
||||
[ext_resource type="Script" path="res://entities/weapons/space_gun/projectile_explosion.gd" id="1_fp5td"]
|
||||
[ext_resource type="PackedScene" uid="uid://qb5sf7awdeui" path="res://entities/components/explosive_damage/explosive_damage.tscn" id="2_js0ht"]
|
||||
|
||||
[sub_resource type="Curve" id="Curve_rg204"]
|
||||
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="CurveTexture" id="CurveTexture_08dbu"]
|
||||
curve = SubResource("Curve_rg204")
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_3mf41"]
|
||||
spread = 180.0
|
||||
initial_velocity_min = 12.0
|
||||
initial_velocity_max = 12.0
|
||||
scale_curve = SubResource("CurveTexture_08dbu")
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wpu51"]
|
||||
albedo_color = Color(0.819608, 0, 0, 1)
|
||||
emission_enabled = true
|
||||
emission = Color(1, 0.254902, 0.109804, 1)
|
||||
emission_energy_multiplier = 4.0
|
||||
|
||||
[sub_resource type="SphereMesh" id="SphereMesh_k3pnh"]
|
||||
material = SubResource("StandardMaterial3D_wpu51")
|
||||
|
||||
[node name="ProjectileExplosion" type="Node3D" node_paths=PackedStringArray("explosive_damage")]
|
||||
script = ExtResource("1_fp5td")
|
||||
explosive_damage = NodePath("ExplosiveDamage")
|
||||
|
||||
[node name="Fire" type="GPUParticles3D" parent="."]
|
||||
emitting = false
|
||||
amount = 24
|
||||
lifetime = 0.5
|
||||
one_shot = true
|
||||
explosiveness = 1.0
|
||||
fixed_fps = 60
|
||||
process_material = SubResource("ParticleProcessMaterial_3mf41")
|
||||
draw_pass_1 = SubResource("SphereMesh_k3pnh")
|
||||
|
||||
[node name="ExplosiveDamage" parent="." instance=ExtResource("2_js0ht")]
|
||||
|
||||
[editable path="ExplosiveDamage"]
|
||||
4
entities/weapons/space_gun/projectile_shape.tres
Normal file
4
entities/weapons/space_gun/projectile_shape.tres
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[gd_resource type="SphereShape3D" format=3 uid="uid://dkhoeg2bwfbga"]
|
||||
|
||||
[resource]
|
||||
radius = 0.15
|
||||
|
|
@ -12,40 +12,26 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
class_name SpaceGun extends Node3D
|
||||
## This class defines a SpaceGun.
|
||||
class_name SpaceGun extends Weapon
|
||||
|
||||
@export var fire_rate : float = 1. # seconds
|
||||
@export var reload_time : float = 1. # seconds
|
||||
@export var _PROJECTILE : PackedScene
|
||||
## The factor of velocity from the owner of this [Weapon] inherited by this [Projectile].
|
||||
@export_range(0., 1., .01) var inheritance : float = 1.
|
||||
@export var anim_player : AnimationPlayer
|
||||
|
||||
enum WeaponState { WEAPON_READY, WEAPON_RELOADING }
|
||||
var state : WeaponState = WeaponState.WEAPON_READY
|
||||
|
||||
func can_fire() -> bool:
|
||||
return state == WeaponState.WEAPON_READY
|
||||
|
||||
func trigger() -> void:
|
||||
# check permission
|
||||
if not can_fire():
|
||||
return
|
||||
func _on_triggered() -> void:
|
||||
# play the fire animation
|
||||
anim_player.play("fire")
|
||||
var projectile : Node3D = $ProjectileSpawner.new_projectile(
|
||||
SpaceGunProjectile,
|
||||
owner.linear_velocity,
|
||||
owner.match_participant
|
||||
)
|
||||
# add to owner of player for now
|
||||
Global.type.add_child(projectile)
|
||||
# ensure projectile does not collide with owner
|
||||
var collider : ShapeCast3D = projectile.shape_cast
|
||||
collider.add_exception(owner)
|
||||
# update states
|
||||
state = WeaponState.WEAPON_RELOADING
|
||||
await get_tree().create_timer(reload_time).timeout
|
||||
state = WeaponState.WEAPON_READY
|
||||
var projectile : SpaceGunProjectile = _PROJECTILE.instantiate()
|
||||
projectile.velocity = projectile.speed * $Nozzle.global_basis.z.normalized() + owner.linear_velocity * inheritance
|
||||
projectile.source = owner
|
||||
owner.add_child(projectile)
|
||||
projectile.global_transform = $Nozzle.global_transform
|
||||
projectile.shape_cast.add_exception(owner)
|
||||
|
||||
func _on_visibility_changed() -> void:
|
||||
if self.visible:
|
||||
anim_player.play("equip")
|
||||
#self.sounds.play("equip")
|
||||
#anim_player.play("equip")
|
||||
#sounds.play("equip")
|
||||
pass
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
64
entities/weapons/weapon.gd
Normal file
64
entities/weapons/weapon.gd
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# This file is part of open-fpsz.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
## This class defines a [Weapon].
|
||||
class_name Weapon extends Node3D
|
||||
|
||||
## Emitted after [member Weapon.trigger] is called.
|
||||
signal triggered
|
||||
|
||||
## Emitted when [member ammo] is changed (useful for [HUD] display).
|
||||
signal ammo_changed(new_ammo: int)
|
||||
|
||||
# enum WeaponState { EQUIPPED, UNEQUIPPED }
|
||||
|
||||
## The ammunition count.
|
||||
@export_range(0, 9223372036854775295) var ammo: int:
|
||||
set = set_ammo
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func set_ammo(new_ammo: int) -> void:
|
||||
ammo = new_ammo
|
||||
ammo_changed.emit(new_ammo)
|
||||
|
||||
## The maxmimum ammunition count.
|
||||
@export_range(0, 9223372036854775295) var max_ammo: int
|
||||
## The number of ammo used when [member triggered] is emitted.
|
||||
@export_range(0, 9223372036854775295) var ammo_usage: int
|
||||
## The cooldown until this [Weapon] can be triggered again.
|
||||
@export var cooldown: float = 1.
|
||||
|
||||
## The internal timer to handle trigger cooldown
|
||||
var _cooldown_timer: Timer
|
||||
|
||||
func _init() -> void:
|
||||
_cooldown_timer = Timer.new()
|
||||
_cooldown_timer.one_shot = true
|
||||
_cooldown_timer.wait_time = cooldown
|
||||
add_child(_cooldown_timer)
|
||||
|
||||
## This methods triggers the [Weapon].
|
||||
func trigger() -> void:
|
||||
if not _cooldown_timer.is_stopped() or ammo < ammo_usage:
|
||||
return
|
||||
# deduct ammo_usage from ammo count
|
||||
ammo -= ammo_usage
|
||||
# start cooldown timer
|
||||
_cooldown_timer.start(cooldown)
|
||||
# emit triggered signal
|
||||
triggered.emit()
|
||||
|
||||
# primary action handler
|
||||
func _on_primary(pressed: bool) -> void:
|
||||
if pressed: trigger()
|
||||
Loading…
Add table
Add a link
Reference in a new issue