Merge branch 'develop' into feat/cicd-tests

This commit is contained in:
anyreso 2024-04-22 20:17:35 -04:00
commit 445fe8d7ee
95 changed files with 4820 additions and 2980 deletions

1
.gitignore vendored
View file

@ -2,7 +2,6 @@
.godot/
# Godot-specific ignores
*.import
export.cfg
export_presets.cfg

View file

@ -1,5 +1,8 @@
image: $CI_REGISTRY/open-fpsz/docker-godot/godot
variables:
EXPORT_NAME: open-fpsz
stages:
- addons
- tests
@ -15,66 +18,45 @@ cache: &global_cache
addons:
stage: addons
script:
- timeout 10 godot --headless --editor --verbose || true
- timeout 10 godot --headless --editor --verbose || true
only:
- main
- develop
# run tests
tests:
stage: tests
needs: [ "addons" ]
script:
- godot --headless --path "$PWD" -s addons/gut/gut_cmdln.gd
only:
- main
- develop
- timeout 15 godot --headless --editor --verbose || true
- timeout 15 godot --headless --editor --verbose || true
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d\.\d\.\d$/'
# export binaries
.export_presets:
needs: [ "tests" ]
before_script:
- cp "$EXPORT_PRESETS" export_presets.cfg
only:
- main
variables:
EXPORT_NAME: open-fpsz
- export ZIP_ARCHIVE_NAME="$EXPORT_NAME-$CI_COMMIT_TAG-$CI_JOB_NAME.zip"
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d\.\d\.\d$/'
linux:
stage: export
extends: [ .export_presets ]
when: manual
script:
- mkdir -v -p build/linux
- godot --headless --verbose --export-release "Linux/X11" build/linux/$EXPORT_NAME.x86_64
artifacts:
name: $EXPORT_NAME-$CI_JOB_NAME
paths:
- build/linux
- mkdir -vp build/$CI_JOB_NAME
- godot --headless --verbose --export-release "Linux/X11" build/$CI_JOB_NAME/$EXPORT_NAME.x86_64
- cd build && zip -r $ZIP_ARCHIVE_NAME $CI_JOB_NAME/*
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file $ZIP_ARCHIVE_NAME "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/$EXPORT_NAME/${CI_COMMIT_TAG:1}/$ZIP_ARCHIVE_NAME"'
windows:
stage: export
extends: [ .export_presets ]
when: manual
script:
- mkdir -v -p build/windows
- godot --headless --verbose --export-release "Windows Desktop" build/windows/$EXPORT_NAME.exe
artifacts:
name: $EXPORT_NAME-$CI_JOB_NAME
paths:
- build/windows
- mkdir -vp build/$CI_JOB_NAME
- godot --headless --verbose --export-release "Windows Desktop" build/$CI_JOB_NAME/$EXPORT_NAME.exe
- cd build && zip -r $ZIP_ARCHIVE_NAME $CI_JOB_NAME/*
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file $ZIP_ARCHIVE_NAME "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/$EXPORT_NAME/${CI_COMMIT_TAG:1}/$ZIP_ARCHIVE_NAME"'
mac:
stage: export
extends: [ .export_presets ]
when: manual
script:
- mkdir -v -p build/mac
- godot --headless --verbose --export-release "Mac OSX" build/mac/$EXPORT_NAME.zip
artifacts:
name: $EXPORT_NAME-$CI_JOB_NAME
paths:
- build/mac
- mkdir -vp build/$CI_JOB_NAME
- godot --headless --verbose --export-release "macOS" build/$CI_JOB_NAME/$ZIP_ARCHIVE_NAME
- 'cd build/$CI_JOB_NAME && curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file $ZIP_ARCHIVE_NAME "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/$EXPORT_NAME/${CI_COMMIT_TAG:1}/$ZIP_ARCHIVE_NAME"'

21
addons/smoothing/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Lawnjelly
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,7 @@
[plugin]
name="Smoothing"
description="Smoothing nodes for fixed timestep interpolation."
author="Lawnjelly"
version="1.2.1"
script="smoothing_plugin.gd"

View file

@ -0,0 +1,235 @@
# Copyright (c) 2019 Lawnjelly
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
extends Node3D
@export
var target: NodePath:
get:
return target
set(v):
target = v
set_target()
var _m_Target: Node3D
var _m_trCurr: Transform3D
var _m_trPrev: Transform3D
const SF_ENABLED = 1 << 0
const SF_TRANSLATE = 1 << 1
const SF_BASIS = 1 << 2
const SF_SLERP = 1 << 3
const SF_INVISIBLE = 1 << 4
@export_flags("enabled", "translate", "basis", "slerp") var flags: int = SF_ENABLED | SF_TRANSLATE | SF_BASIS:
set(v):
flags = v
# we may have enabled or disabled
_SetProcessing()
get:
return flags
##########################################################################################
# USER FUNCS
# call this checked e.g. starting a level, AFTER moving the target
# so we can update both the previous and current values
func teleport():
var temp_flags = flags
_SetFlags(SF_TRANSLATE | SF_BASIS)
_RefreshTransform()
_m_trPrev = _m_trCurr
# do one frame update to make sure all components are updated
_process(0)
# resume old flags
flags = temp_flags
func set_enabled(bEnable: bool):
_ChangeFlags(SF_ENABLED, bEnable)
_SetProcessing()
func is_enabled():
return _TestFlags(SF_ENABLED)
##########################################################################################
func _ready():
_m_trCurr = Transform3D()
_m_trPrev = Transform3D()
set_process_priority(100)
set_as_top_level(true)
Engine.set_physics_jitter_fix(0.0)
func set_target():
if is_inside_tree():
_FindTarget()
func _set_flags(new_value):
flags = new_value
# we may have enabled or disabled
_SetProcessing()
func _get_flags():
return flags
func _SetProcessing():
var bEnable = _TestFlags(SF_ENABLED)
if _TestFlags(SF_INVISIBLE):
bEnable = false
set_process(bEnable)
set_physics_process(bEnable)
pass
func _enter_tree():
# might have been moved
_FindTarget()
pass
func _notification(what):
match what:
# invisible turns unchecked processing
NOTIFICATION_VISIBILITY_CHANGED:
_ChangeFlags(SF_INVISIBLE, is_visible_in_tree() == false)
_SetProcessing()
func _RefreshTransform():
if _HasTarget() == false:
return
_m_trPrev = _m_trCurr
_m_trCurr = _m_Target.global_transform
func _FindTarget():
_m_Target = null
# If no target has been assigned in the property,
# default to using the parent as the target.
if target.is_empty():
var parent = get_parent_node_3d()
if parent:
_m_Target = parent
return
var targ = get_node(target)
if ! targ:
printerr("ERROR SmoothingNode : Target " + str(target) + " not found")
return
if not targ is Node3D:
printerr("ERROR SmoothingNode : Target " + str(target) + " is not node 3D")
target = ""
return
# if we got to here targ is a spatial
_m_Target = targ
# certain targets are disallowed
if _m_Target == self:
var msg = str(_m_Target.get_name()) + " assigned to " + str(self.get_name()) + "]"
printerr("ERROR SmoothingNode : Target should not be self [", msg)
# error message
#OS.alert("Target cannot be a parent or grandparent in the scene tree.", "SmoothingNode")
_m_Target = null
target = ""
return
func _HasTarget() -> bool:
if _m_Target == null:
return false
# has not been deleted?
if is_instance_valid(_m_Target):
return true
_m_Target = null
return false
func _process(_delta):
var f = Engine.get_physics_interpolation_fraction()
var tr: Transform3D = Transform3D()
# translate
if _TestFlags(SF_TRANSLATE):
var ptDiff = _m_trCurr.origin - _m_trPrev.origin
tr.origin = _m_trPrev.origin + (ptDiff * f)
# rotate
if _TestFlags(SF_BASIS):
if _TestFlags(SF_SLERP):
tr.basis = _m_trPrev.basis.slerp(_m_trCurr.basis, f)
else:
tr.basis = _LerpBasis(_m_trPrev.basis, _m_trCurr.basis, f)
transform = tr
func _physics_process(_delta):
_RefreshTransform()
func _LerpBasis(from: Basis, to: Basis, f: float) -> Basis:
var res: Basis = Basis()
res.x = from.x.lerp(to.x, f)
res.y = from.y.lerp(to.y, f)
res.z = from.z.lerp(to.z, f)
return res
func _SetFlags(f):
flags |= f
func _ClearFlags(f):
flags &= ~f
func _TestFlags(f):
return (flags & f) == f
func _ChangeFlags(f, bSet):
if bSet:
_SetFlags(f)
else:
_ClearFlags(f)

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

View file

@ -2,16 +2,16 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b30y3pq4u5r0h"
path="res://.godot/imported/albedo0.png-8dedb9e51f95ee8e69f9a00bccf9fb16.ctex"
uid="uid://bm44ebnxqc82"
path="res://.godot/imported/smoothing.png-6b454a779e636eaa20b6c6ac618bf82a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://weapons/space_gun/assets/textures/albedo0.png"
dest_files=["res://.godot/imported/albedo0.png-8dedb9e51f95ee8e69f9a00bccf9fb16.ctex"]
source_file="res://addons/smoothing/smoothing.png"
dest_files=["res://.godot/imported/smoothing.png-6b454a779e636eaa20b6c6ac618bf82a.ctex"]
[params]

View file

@ -0,0 +1,212 @@
# Copyright (c) 2019 Lawnjelly
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
extends Node2D
@export
var target: NodePath:
get:
return target
set(v):
target = v
if is_inside_tree():
_FindTarget()
var _m_Target: Node2D
var _m_Flip:bool = false
var _m_Trans_curr: Transform2D = Transform2D()
var _m_Trans_prev: Transform2D = Transform2D()
const SF_ENABLED = 1 << 0
const SF_GLOBAL_IN = 1 << 1
const SF_GLOBAL_OUT = 1 << 2
const SF_TOP_LEVEL = 1 << 3
const SF_INVISIBLE = 1 << 4
@export_flags("enabled", "global in", "global out", "top level") var flags: int = SF_ENABLED | SF_GLOBAL_IN | SF_GLOBAL_OUT:
set(v):
flags = v
# we may have enabled or disabled
_SetProcessing()
get:
return flags
##########################################################################################
# USER FUNCS
# call this checked e.g. starting a level, AFTER moving the target
# so we can update both the previous and current values
func teleport():
_RefreshTransform()
_m_Trans_prev = _m_Trans_curr
# call frame upate to make sure all components of the node are set
_process(0)
func set_enabled(bEnable: bool):
_ChangeFlags(SF_ENABLED, bEnable)
_SetProcessing()
func is_enabled():
return _TestFlags(SF_ENABLED)
##########################################################################################
func _ready():
set_process_priority(100)
Engine.set_physics_jitter_fix(0.0)
set_as_top_level(_TestFlags(SF_TOP_LEVEL))
func _SetProcessing():
var bEnable = _TestFlags(SF_ENABLED)
if _TestFlags(SF_INVISIBLE):
bEnable = false
set_process(bEnable)
set_physics_process(bEnable)
set_as_top_level(_TestFlags(SF_TOP_LEVEL))
func _enter_tree():
# might have been moved
_FindTarget()
func _notification(what):
match what:
# invisible turns unchecked processing
NOTIFICATION_VISIBILITY_CHANGED:
_ChangeFlags(SF_INVISIBLE, is_visible_in_tree() == false)
_SetProcessing()
func _RefreshTransform():
if _HasTarget() == false:
return
_m_Trans_prev = _m_Trans_curr
if _TestFlags(SF_GLOBAL_IN):
_m_Trans_curr = _m_Target.get_global_transform()
else:
_m_Trans_curr = _m_Target.get_transform()
_m_Flip = false
# Ideally we would use determinant core function, as in commented line below, but we
# need to workaround for backward compat.
# if (_m_Trans_prev.determinant() < 0) != (_m_Trans_curr.determinant() < 0):
if (_Determinant_Sign(_m_Trans_prev) != _Determinant_Sign(_m_Trans_curr)):
_m_Flip = true
func _Determinant_Sign(t:Transform2D)->bool:
# Workaround Transform2D determinant function not being available
# until 3.6 / 4.1.
# We calculate determinant manually, slower but compatible to lower
# godot versions.
var d = (t.x.x * t.y.y) - (t.x.y * t.y.x)
return d >= 0.0
func _FindTarget():
_m_Target = null
# If no target has been assigned in the property,
# default to using the parent as the target.
if target.is_empty():
var parent = get_parent()
if parent and (parent is Node2D):
_m_Target = parent
return
var targ = get_node(target)
if ! targ:
printerr("ERROR SmoothingNode2D : Target " + str(target) + " not found")
return
if not targ is Node2D:
printerr("ERROR SmoothingNode2D : Target " + str(target) + " is not Node2D")
target = ""
return
# if we got to here targ is correct type
_m_Target = targ
func _HasTarget() -> bool:
if _m_Target == null:
return false
# has not been deleted?
if is_instance_valid(_m_Target):
return true
_m_Target = null
return false
func _process(_delta):
var f = Engine.get_physics_interpolation_fraction()
var tr = Transform2D()
tr.origin = lerp(_m_Trans_prev.origin, _m_Trans_curr.origin, f)
tr.x = lerp(_m_Trans_prev.x, _m_Trans_curr.x, f)
tr.y = lerp(_m_Trans_prev.y, _m_Trans_curr.y, f)
# When a sprite flip is detected, turn off interpolation for that tick.
if _m_Flip:
tr = _m_Trans_curr
if _TestFlags(SF_GLOBAL_OUT) and not _TestFlags(SF_TOP_LEVEL):
set_global_transform(tr)
else:
set_transform(tr)
func _physics_process(_delta):
_RefreshTransform()
func _SetFlags(f):
flags |= f
func _ClearFlags(f):
flags &= ~f
func _TestFlags(f):
return (flags & f) == f
func _ChangeFlags(f, bSet):
if bSet:
_SetFlags(f)
else:
_ClearFlags(f)

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://i3h8ng5fyd1m"
path="res://.godot/imported/smoothing_2d.png-4942c58db397caab18506104d957cac1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/smoothing/smoothing_2d.png"
dest_files=["res://.godot/imported/smoothing_2d.png-4942c58db397caab18506104d957cac1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -0,0 +1,17 @@
@tool
extends EditorPlugin
func _enter_tree():
# Initialization of the plugin goes here
# Add the new type with a name, a parent type, a script and an icon
add_custom_type("Smoothing", "Node3D", preload("smoothing.gd"), preload("smoothing.png"))
add_custom_type("Smoothing2D", "Node2D", preload("smoothing_2d.gd"), preload("smoothing_2d.png"))
pass
func _exit_tree():
# Clean-up of the plugin goes here
# Always remember to remove_at it from the engine when deactivated
remove_custom_type("Smoothing")
remove_custom_type("Smoothing2D")

View file

@ -1,30 +1,32 @@
# 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
@export var damage : int = 100
@export var impulse_force : int = 1000
var damage_dealer : Player
func _physics_process(_delta):
func _physics_process(_delta : float) -> void:
for body in get_overlapping_bodies():
if body is RigidBody3D:
var direction = (body.global_position - global_position).normalized()
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)
for area in get_overlapping_areas():
if area is HealthComponent and is_multiplayer_authority():
area.damage.rpc(damage)
area.damage.rpc(damage, damage_dealer.player_id)
set_physics_process(false)

View file

@ -4,6 +4,6 @@
[node name="ExplosiveDamage" type="Area3D"]
collision_layer = 0
collision_mask = 5
collision_mask = 13
monitorable = false
script = ExtResource("1_2uehk")

View file

@ -0,0 +1,60 @@
# 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 FlagCarryComponent extends Node
## This component allows its entity to grab, carry and throw flags
##
## To work correctly the owner MUST set an attachment point for carried flags
## and a sensor to detect when flags are within grab range
@export var max_throw_speed : float = 10.0
@export var attachment : Node3D
@export var sensor : Area3D
var _carried_flag : Flag
func _ready() -> void:
sensor.body_entered.connect(_sensor_on_body_entered)
func _process(_delta : float) -> void:
if _is_carrying():
_carried_flag.global_position = attachment.global_position
_carried_flag.global_rotation = attachment.global_rotation
func _grab(flag : Flag) -> void:
if not _is_carrying():
flag.grab()
_carried_flag = flag
func _release(inherited_velocity : Vector3, throw_speed : float) -> void:
if _is_carrying():
_carried_flag.drop()
_carried_flag.rotation_degrees.x = 0.0
_carried_flag.linear_velocity = inherited_velocity + (attachment.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 = _carried_flag.global_position + (attachment.global_basis.z * 1.5)
_carried_flag = null
func _is_carrying() -> bool:
return _carried_flag != null
func _sensor_on_body_entered(collider : Flag) -> void:
if collider is Flag:
_grab(collider)
func drop() -> void:
_release(Vector3.ZERO, 0.0)
func throw(inherited_velocity : Vector3) -> void:
_release(inherited_velocity, max_throw_speed)

View file

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://2t8ql8pkxv6c"]
[ext_resource type="Script" path="res://components/flag_carry_component.gd" id="1_b6ee8"]
[node name="FlagCarryComponent" type="Node"]
script = ExtResource("1_b6ee8")

View file

@ -1,15 +1,15 @@
# 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
@ -20,23 +20,23 @@ class_name HealthComponent extends Area3D
health = value
health_changed.emit(value)
signal health_zeroed
signal health_changed(value : int)
signal health_zeroed(killer_id : int)
signal health_changed(value : float)
func _ready():
func _ready() -> void:
heal_full()
@rpc("call_local")
func damage(amount : int) -> void:
func damage(amount : float, damage_dealer_id : int) -> void:
health = clampf(health - amount, 0.0, max_health)
if health == 0.0:
health_zeroed.emit()
health_zeroed.emit(damage_dealer_id)
@rpc("call_local")
func _heal(amount : int):
func _heal(amount : float) -> void:
health = clampf(health + amount, 0.0, max_health)
func heal_full():
func heal_full() -> void:
if not is_multiplayer_authority():
return

View file

@ -1,12 +1,8 @@
[gd_scene load_steps=3 format=3 uid="uid://bof3mg7wgxrmn"]
[gd_scene load_steps=2 format=3 uid="uid://bof3mg7wgxrmn"]
[ext_resource type="Script" path="res://components/health_component.gd" id="1_00xis"]
[ext_resource type="Shape3D" uid="uid://dkwljsgaflf31" path="res://entities/player/collision_shape.res" id="2_sgbmt"]
[node name="HealthComponent" type="Area3D"]
collision_layer = 4
collision_mask = 0
script = ExtResource("1_00xis")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = ExtResource("2_sgbmt")

Binary file not shown.

View file

@ -3,13 +3,13 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://clq4uym4arpv3"
path="res://.godot/imported/SpaceGun.glb-ee1e940c9dd77150eaf36c6537feefef.scn"
uid="uid://d3l7fvbdg6m5g"
path="res://.godot/imported/flag.glb-0a51d316969707bbb012f3287265227f.scn"
[deps]
source_file="res://weapons/space_gun/assets/SpaceGun.glb"
dest_files=["res://.godot/imported/SpaceGun.glb-ee1e940c9dd77150eaf36c6537feefef.scn"]
source_file="res://entities/flag/assets/flag.glb"
dest_files=["res://.godot/imported/flag.glb-0a51d316969707bbb012f3287265227f.scn"]
[params]
@ -29,17 +29,6 @@ animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={
"materials": {
"_defaultMat": {
"use_external/enabled": true,
"use_external/path": ""
},
"dark": {
"use_external/enabled": true,
"use_external/path": ""
}
}
}
_subresources={}
gltf/naming_version=1
gltf/embedded_image_handling=2
gltf/embedded_image_handling=1

17
entities/flag/flag.gd Normal file
View file

@ -0,0 +1,17 @@
class_name Flag extends Node3D
enum FlagState { FLAG_STATE_ON_STAND, FLAG_STATE_DROPPED, FLAG_STATE_TAKEN }
@export var flag_state : FlagState = FlagState.FLAG_STATE_ON_STAND
func can_be_grabbed() -> bool:
return flag_state != FlagState.FLAG_STATE_TAKEN
func grab() -> void:
if flag_state != FlagState.FLAG_STATE_TAKEN:
flag_state = FlagState.FLAG_STATE_TAKEN
func drop() -> void:
if flag_state == FlagState.FLAG_STATE_TAKEN:
flag_state = FlagState.FLAG_STATE_DROPPED

51
entities/flag/flag.tscn Normal file
View file

@ -0,0 +1,51 @@
[gd_scene load_steps=7 format=3 uid="uid://c88l3h0ph00c7"]
[ext_resource type="Script" path="res://entities/flag/flag.gd" id="1_y7d3d"]
[ext_resource type="PackedScene" path="res://entities/flag/assets/flag.glb" id="2_i78em"]
[ext_resource type="PackedScene" uid="uid://bcgkc5fhhyauv" path="res://entities/flag/waypoint.tscn" id="3_tu6jg"]
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_4ymrw"]
bounce = 0.5
[sub_resource type="BoxShape3D" id="BoxShape3D_fkf1k"]
size = Vector3(0.5, 2, 0.1)
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_lpijf"]
properties/0/path = NodePath(".:position")
properties/0/spawn = true
properties/0/replication_mode = 1
properties/1/path = NodePath(".:linear_velocity")
properties/1/spawn = true
properties/1/replication_mode = 1
properties/2/path = NodePath(".:flag_state")
properties/2/spawn = true
properties/2/replication_mode = 2
[node name="Flag" type="RigidBody3D"]
collision_layer = 8
collision_mask = 2147483656
axis_lock_angular_x = true
axis_lock_angular_y = true
axis_lock_angular_z = true
mass = 40.0
physics_material_override = SubResource("PhysicsMaterial_4ymrw")
center_of_mass_mode = 1
center_of_mass = Vector3(0, 0.5, 0)
continuous_cd = true
script = ExtResource("1_y7d3d")
[node name="Mesh" parent="." instance=ExtResource("2_i78em")]
transform = Transform3D(0.33, 0, 0, 0, 0.421029, 0, 0, 0, 0.33, 0, 0.0744106, 0)
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("BoxShape3D_fkf1k")
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
replication_config = SubResource("SceneReplicationConfig_lpijf")
[node name="WaypointAttachment" type="Marker3D" parent="."]
[node name="Waypoint" parent="WaypointAttachment" node_paths=PackedStringArray("attach_point", "flag") instance=ExtResource("3_tu6jg")]
attach_point = NodePath("..")
flag = NodePath("../..")

49
entities/flag/waypoint.gd Normal file
View file

@ -0,0 +1,49 @@
# 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/>.
extends Control
@export var attach_point : Node3D
@export var flag : Flag
@onready var _iff_container : Control = $IFFContainer
func _ready() -> void:
attach_point.transform = attach_point.transform.translated(
Vector3(0, flag.get_node("CollisionShape3D").shape.size.y, 0))
func _process(_delta : float) -> void:
var camera : Camera3D = get_viewport().get_camera_3d()
var viewport_rect : Rect2 = get_viewport_rect()
# if the player is NOT infront of the camera or camera does NOT have LOS to player
if camera.is_position_behind(attach_point.global_position):
_iff_container.hide() # hide the IFF and exit function
return
else:
_iff_container.show() # display the IFF
# get the screen location of the players head
var new_iff_position : Vector2 = camera.unproject_position(attach_point.global_position)
# check if the unprojected point lies inside the viewport
if !Rect2(Vector2(0, 0), viewport_rect.size).has_point(new_iff_position):
_iff_container.hide() # hide the IFF and exit function
return
# move the IFF up so the bottom of it is at the players head
new_iff_position.y -= _iff_container.size.y
# move the IFF left so it's centered horizontally above players head
new_iff_position.x -= _iff_container.size.x / 2
# set the position of the IFF
position = new_iff_position

View file

@ -0,0 +1,27 @@
[gd_scene load_steps=2 format=3 uid="uid://bcgkc5fhhyauv"]
[ext_resource type="Script" path="res://entities/flag/waypoint.gd" id="1_gmnl6"]
[node name="IFF" type="Control"]
layout_mode = 3
anchors_preset = 0
script = ExtResource("1_gmnl6")
[node name="IFFContainer" type="MarginContainer" parent="."]
layout_mode = 0
offset_right = 11.0
offset_bottom = 20.0
mouse_filter = 2
[node name="Chevron" type="Label" parent="IFFContainer"]
layout_direction = 2
layout_mode = 2
size_flags_vertical = 1
theme_override_colors/font_color = Color(0, 1, 1, 0.784314)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_constants/line_spacing = -20
theme_override_font_sizes/font_size = 14
text = "▼"
horizontal_alignment = 1
vertical_alignment = 2

View file

@ -3,23 +3,23 @@ class_name Vanguard extends Node
@export var spine_ik_target_attachment : Node3D
@onready var animation_tree : AnimationTree = $AnimationTree
@onready var spine_ik = $Node/Skeleton3D/SpineIK
@onready var spine_ik_target = $SpineIKTarget
@onready var spine_ik : SkeletonIK3D = $Node/Skeleton3D/SpineIK
@onready var spine_ik_target : Node3D = $SpineIKTarget
enum GroundState { GROUND_STATE_GROUNDED, GROUND_STATE_MID_AIR, GROUND_STATE_DEAD }
func _ready():
func _ready() -> void:
spine_ik.start()
func _process(_delta):
func _process(_delta : float) -> void:
spine_ik_target.global_transform = spine_ik_target_attachment.global_transform
func set_locomotion(locomotion : Vector2, ground_speed_factor : float) -> void:
animation_tree.set("parameters/Locomotion/blend_position", locomotion)
animation_tree.set("parameters/GroundSpeed/scale", ground_speed_factor)
func set_ground_state(ground_state : GroundState):
var transition_name = "grounded"
func set_ground_state(ground_state : GroundState) -> void:
var transition_name : String = "grounded"
if ground_state == GroundState.GROUND_STATE_MID_AIR:
transition_name = "mid_air"
if ground_state == GroundState.GROUND_STATE_DEAD:

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://ia3bdpe4rm1m"
path.s3tc="res://.godot/imported/vanguard_0.png-601f36bc664114e126d425d5f45085ef.s3tc.ctex"
path.bptc="res://.godot/imported/vanguard_0.png-601f36bc664114e126d425d5f45085ef.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
@ -13,12 +13,12 @@ generator_parameters={}
[deps]
source_file="res://entities/player/assets/vanguard_0.png"
dest_files=["res://.godot/imported/vanguard_0.png-601f36bc664114e126d425d5f45085ef.s3tc.ctex"]
dest_files=["res://.godot/imported/vanguard_0.png-601f36bc664114e126d425d5f45085ef.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/high_quality=true
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0

View file

@ -3,7 +3,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bvgfmpb2l1juf"
path.s3tc="res://.godot/imported/vanguard_1.png-39b5712c4c4119a42b3540a159f8b3f2.s3tc.ctex"
path.bptc="res://.godot/imported/vanguard_1.png-39b5712c4c4119a42b3540a159f8b3f2.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
@ -13,12 +13,12 @@ generator_parameters={}
[deps]
source_file="res://entities/player/assets/vanguard_1.png"
dest_files=["res://.godot/imported/vanguard_1.png-39b5712c4c4119a42b3540a159f8b3f2.s3tc.ctex"]
dest_files=["res://.godot/imported/vanguard_1.png-39b5712c4c4119a42b3540a159f8b3f2.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/high_quality=true
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=1

View file

@ -3,7 +3,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://vtrbc3eja3df"
path.s3tc="res://.godot/imported/vanguard_2.png-986e3665904b0d4758f584d5d3b7b726.s3tc.ctex"
path.bptc="res://.godot/imported/vanguard_2.png-986e3665904b0d4758f584d5d3b7b726.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
@ -13,12 +13,12 @@ generator_parameters={}
[deps]
source_file="res://entities/player/assets/vanguard_2.png"
dest_files=["res://.godot/imported/vanguard_2.png-986e3665904b0d4758f584d5d3b7b726.s3tc.ctex"]
dest_files=["res://.godot/imported/vanguard_2.png-986e3665904b0d4758f584d5d3b7b726.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/high_quality=true
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=1

Binary file not shown.

View file

@ -0,0 +1,4 @@
[gd_resource type="CapsuleShape3D" format=3 uid="uid://cb8esdlnottdn"]
[resource]
radius = 0.25

View file

@ -1,24 +1,27 @@
# 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 Player extends RigidBody3D
enum PlayerState { PLAYER_ALIVE, PLAYER_DEAD }
@export var iff : Control
@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_group("Jetpack")
@ -31,97 +34,130 @@ enum PlayerState { PLAYER_ALIVE, PLAYER_DEAD }
@export var jetpack_vertical_force : float = 800
@export_group("State")
@export var player_state = PlayerState.PLAYER_ALIVE
@export var player_id = 1:
@export var player_state : PlayerState = PlayerState.PLAYER_ALIVE
@export var player_id : int = 1:
set(id):
player_id = id
$PlayerInput.set_multiplayer_authority(id)
@export var nickname : String
@onready var input : PlayerInput = $PlayerInput
@onready var camera = $SpringArm3D/Camera3D
@onready var hud = $HUD
@onready var iff = $ThirdPerson/IFF
@onready var shape_cast = $ShapeCast3D
@onready var weapon = $SpringArm3D/Inventory/SpaceGun
@onready var camera : Camera3D = $Smoothing/SpringArm3D/Camera3D
@onready var hud : CanvasLayer = $HUD
@onready var shape_cast : ShapeCast3D = $ShapeCast3D
@onready var weapon : Node3D = $Smoothing/SpringArm3D/Inventory/SpaceGun
@onready var animation_player : AnimationPlayer = $AnimationPlayer
@onready var health_component = $HealthComponent
@onready var spring_arm_height = $SpringArm3D.position.y
@onready var health_component : Area3D = $HealthComponent
@onready var collision_shape : CollisionShape3D = $CollisionShape3D
@onready var flag_carry_component : FlagCarryComponent = $FlagCarryComponent
@onready var spring_arm_height : float = $Smoothing/SpringArm3D.position.y
@onready var _original_weapon_transform : Transform3D = weapon.transform
@onready var flag_carry_attachment : Node3D = $Smoothing/SpringArm3D/FlagCarryAttachment
@onready var _game_settings : Settings = get_node("/root/GlobalSettings")
signal died(player)
signal energy_changed(energy)
signal died(player : Player, killer_id : int)
signal energy_changed(energy : float)
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 = false
var _jumping : bool = false
func _ready():
@rpc("call_local")
func set_nickname(value : String) -> void:
nickname = value
func _ready() -> void:
energy_changed.connect(hud._on_energy_changed)
health_component.health_changed.connect(hud._on_health_changed)
health_component.health_changed.connect(iff._on_health_changed)
health_component.health_changed.emit(health_component.health)
health_component.health_zeroed.connect(_die)
if _is_first_person():
camera.current = true
remove_child($ThirdPerson)
else:
remove_child($HUD)
weapon.hide()
health_component.health_zeroed.connect(die)
input.fired_primary.connect(_fire_primary)
input.jumped.connect(_jump)
input.throwed_flag.connect(_throw_flag)
func _is_first_person():
input.MOUSE_SENSITIVITY = _game_settings.mouse_sensitivity
input.inverted_y_axis = _game_settings.inverted_y_axis
if _is_pawn():
camera.current = true
camera.fov = _game_settings.fov
# set the spring arm translation to be about head height level
$Smoothing/SpringArm3D.transform = Transform3D().translated(Vector3(0, collision_shape.shape.height / 2, 0) * 0.9)
flag_carry_attachment.hide()
$Smoothing.remove_child($Smoothing/ThirdPerson)
else:
# set the iff attachment translation to be about head height level
$Smoothing/ThirdPerson/IFFAttachment.transform = Transform3D().translated(Vector3(0, collision_shape.shape.height / 2, 0) * 0.9)
remove_child(hud)
weapon.hide()
func _is_pawn() -> bool:
return player_id == multiplayer.get_unique_id()
func _fire_primary():
func _fire_primary() -> void:
if _is_player_dead():
return
if not weapon.can_fire():
return
var current_weapon_transform = weapon.transform
var current_weapon_transform : Transform3D = weapon.transform
weapon.transform = _original_weapon_transform
weapon.fire_primary()
weapon.transform = current_weapon_transform
if _is_first_person():
if _is_pawn():
animation_player.stop()
animation_player.play("shoot")
func _jump():
func _jump() -> void:
if _is_player_dead():
return
_jumping = true
func _throw_flag() -> void:
flag_carry_component.throw(linear_velocity)
func is_on_floor() -> bool:
return shape_cast.is_colliding()
if shape_cast.is_colliding():
for i in shape_cast.get_collision_count():
var collider : Object = shape_cast.get_collider(i)
if collider is Terrain3D:
return true
return false
func _is_skiing() -> bool:
return input.skiing
func _handle_aerial_control(delta, direction):
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(delta, direction):
func _handle_jetpack(direction : Vector3) -> void:
if input.jetting:
if energy > 0:
var up_vector = Vector3.UP * jetpack_vertical_force * jetpack_force_factor
var side_vector = 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)
energy -= energy_drain_rate * delta
func _update_jetpack_energy(delta : float) -> void:
if input.jetting:
if energy > 0:
energy -= energy_drain_rate * delta
else:
energy += energy_charge_rate * delta
energy = clamp(energy, 0, energy_max)
energy_changed.emit(energy)
func _process(_delta):
func _process(_delta : float) -> void:
if _is_player_dead():
iff.hide()
return
else:
iff.show()
if not _is_first_person():
$ThirdPerson/PlayerMesh.global_transform.basis = Basis.from_euler(Vector3(0.0, input.camera_rotation.x + PI, 0.0))
if not _is_pawn():
$Smoothing/ThirdPerson/PlayerMesh.global_transform.basis = Basis.from_euler(Vector3(0.0, input.camera_rotation.x + PI, 0.0))
else:
if animation_player.current_animation == "shoot":
pass
@ -129,21 +165,25 @@ func _process(_delta):
animation_player.play("idle")
%SpringArm3D.global_transform.basis = Basis.from_euler(Vector3(input.camera_rotation.y, input.camera_rotation.x, 0.0))
func _physics_process(delta):
func _physics_process(delta : float) -> void:
_update_jetpack_energy(delta)
func _handle_movement() -> void:
# retrieve user's direction vector
var _input_dir = input.direction
var _input_dir : Vector2 = input.direction
# compute direction in local space
var _direction = (transform.basis * Vector3(_input_dir.x, 0, _input_dir.y)).normalized()
var _direction : Vector3 = (transform.basis * Vector3(_input_dir.x, 0, _input_dir.y)).normalized()
_update_third_person_animations()
if _is_player_dead():
return
# adjust direction based on spring arm rotation
_direction = _direction.rotated(Vector3.UP, $SpringArm3D.rotation.y)
_direction = _direction.rotated(Vector3.UP, $Smoothing/SpringArm3D.rotation.y)
_handle_aerial_control(delta, _direction)
_handle_jetpack(delta, _direction)
_handle_aerial_control(_direction)
_handle_jetpack(_direction)
# handle ski
if _is_skiing():
@ -154,9 +194,9 @@ func _physics_process(delta):
if is_on_floor():
if not _direction.is_zero_approx() and not _is_skiing():
# retrieve collision normal
var normal = shape_cast.get_collision_normal(0)
var normal : Vector3 = shape_cast.get_collision_normal(0)
# calculate the angle between the ground normal and the up vector
var slope_angle = 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
@ -165,15 +205,19 @@ func _physics_process(delta):
linear_velocity = lerp(linear_velocity, _direction * ground_speed, .1)
if _jumping:
linear_velocity.y = sqrt(2 * abs((mass * gravity * delta).y) * 1)
var v : float = sqrt(2 * g * jump_height)
apply_central_impulse(Vector3(0, mass * v, 0))
_jumping = false
func _update_third_person_animations():
if _is_first_person():
func _integrate_forces(_state : PhysicsDirectBodyState3D) -> void:
_handle_movement()
func _update_third_person_animations() -> void:
if _is_pawn():
return
var tp_player : Vanguard = $ThirdPerson/PlayerMesh
var tp_player : Vanguard = $Smoothing/ThirdPerson/PlayerMesh
if _is_player_dead():
tp_player.set_ground_state(Vanguard.GroundState.GROUND_STATE_DEAD)
@ -183,28 +227,22 @@ func _update_third_person_animations():
tp_player.set_ground_state(Vanguard.GroundState.GROUND_STATE_GROUNDED)
else:
tp_player.set_ground_state(Vanguard.GroundState.GROUND_STATE_MID_AIR)
var local_velocity = (tp_player.global_basis.inverse() * linear_velocity)
var local_velocity : Vector3 = (tp_player.global_basis.inverse() * linear_velocity)
const bias : float = 1.2 # Basically match feet speed with ground speed
tp_player.set_locomotion(Vector2(local_velocity.x, local_velocity.z), bias)
func _is_player_dead():
return player_state == PlayerState.PLAYER_DEAD
func _is_player_dead() -> bool:
return player_state != PlayerState.PLAYER_ALIVE
func _die():
func die(killer_id : int) -> void:
flag_carry_component.drop()
player_state = PlayerState.PLAYER_DEAD
if _is_first_person():
animation_player.stop()
if _is_pawn():
animation_player.play("death")
var tween = create_tween()
tween.tween_interval(4)
tween.tween_callback(func():
died.emit(self)
if _is_first_person():
animation_player.stop()
)
died.emit(self, killer_id)
func respawn(location):
func respawn(location : Vector3) -> void:
player_state = PlayerState.PLAYER_ALIVE
linear_velocity = Vector3()
health_component.heal_full()
position = location
player_state = PlayerState.PLAYER_ALIVE

View file

@ -1,14 +1,15 @@
[gd_scene load_steps=20 format=3 uid="uid://cbhx1xme0sb7k"]
[gd_scene load_steps=21 format=3 uid="uid://cbhx1xme0sb7k"]
[ext_resource type="Script" path="res://entities/player/player.gd" id="1_mk68k"]
[ext_resource type="Shape3D" uid="uid://dkwljsgaflf31" path="res://entities/player/collision_shape.res" id="2_8rtw3"]
[ext_resource type="PackedScene" uid="uid://drbefw6akui2v" path="res://entities/player/assets/vanguard.tscn" id="2_beyex"]
[ext_resource type="Shape3D" uid="uid://cb8esdlnottdn" path="res://entities/player/collision_shape.tres" id="2_vjqny"]
[ext_resource type="PackedScene" uid="uid://bcv81ku26xo" path="res://interfaces/hud/hud.tscn" id="3_ccety"]
[ext_resource type="PackedScene" uid="uid://c8co0qa2omjmh" path="res://weapons/space_gun/space_gun.tscn" id="4_lhn5w"]
[ext_resource type="PackedScene" uid="uid://dn1tcakam5egs" path="res://weapons/space_gun/projectile.tscn" id="5_lvaut"]
[ext_resource type="PackedScene" uid="uid://c8co0qa2omjmh" path="res://entities/weapons/space_gun/space_gun.tscn" id="4_6jh57"]
[ext_resource type="PackedScene" uid="uid://bof3mg7wgxrmn" path="res://components/health_component.tscn" id="5_t6i6e"]
[ext_resource type="Script" path="res://entities/player/player_input.gd" id="6_ymcrr"]
[ext_resource type="PackedScene" uid="uid://dsysi2rd3bu76" path="res://interfaces/hud/iff.tscn" id="7_8hc80"]
[ext_resource type="PackedScene" uid="uid://dsysi2rd3bu76" path="res://interfaces/hud/iffs/iff.tscn" id="7_8hc80"]
[ext_resource type="PackedScene" uid="uid://2t8ql8pkxv6c" path="res://components/flag_carry_component.tscn" id="7_e7s1a"]
[ext_resource type="Script" path="res://addons/smoothing/smoothing.gd" id="11_k330l"]
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_clur0"]
resource_local_to_scene = true
@ -26,50 +27,50 @@ length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("SpringArm3D/Inventory/SpaceGun:position")
tracks/0/path = NodePath("Smoothing/SpringArm3D:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"times": PackedFloat32Array(0, 0.0001),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Vector3(0.244668, -0.229311, -0.30332)]
"values": [Vector3(0, 0.5, 0), Vector3(0, 0.5, 0)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("SpringArm3D/Inventory/SpaceGun:rotation")
tracks/1/path = NodePath("Smoothing/SpringArm3D:rotation")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector3(0, 3.14159, 0)]
"values": [Vector3(0, 0, 0)]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("SpringArm3D:position")
tracks/2/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:position")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector3(0, 0.5, 0)]
"values": [Vector3(0.15, -0.3, -0.2)]
}
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath("SpringArm3D:rotation")
tracks/3/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:rotation")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector3(0, 0, 0)]
"values": [Vector3(0, 3.14159, 0)]
}
[sub_resource type="Animation" id="Animation_yqgrk"]
@ -77,7 +78,7 @@ resource_name = "death"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("SpringArm3D:position")
tracks/0/path = NodePath("Smoothing/SpringArm3D:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
@ -89,7 +90,7 @@ tracks/0/keys = {
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("SpringArm3D:rotation")
tracks/1/path = NodePath("Smoothing/SpringArm3D:rotation")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
@ -106,26 +107,26 @@ loop_mode = 1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("SpringArm3D/Inventory/SpaceGun:position")
tracks/0/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 1.5),
"transitions": PackedFloat32Array(-2, -2),
"update": 0,
"values": [Vector3(0.244668, -0.229311, -0.30332), Vector3(0.244668, -0.26057, -0.30332)]
"values": [Vector3(0.15, -0.3, -0.2), Vector3(0.15, -0.3, -0.2)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("SpringArm3D/Inventory/SpaceGun:rotation")
tracks/1/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:rotation")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 1.5),
"transitions": PackedFloat32Array(-2, -2),
"update": 0,
"values": [Vector3(0, 3.14159, 0), Vector3(0.114371, 3.14159, -1.73453e-08)]
"values": [Vector3(0, 3.14159, 0), Vector3(0.0698132, 3.14159, 0)]
}
[sub_resource type="Animation" id="Animation_p84l0"]
@ -133,19 +134,19 @@ resource_name = "shoot"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("SpringArm3D/Inventory/SpaceGun:position")
tracks/0/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(0.5, 1),
"update": 0,
"values": [Vector3(0.244668, -0.204488, -0.268482), Vector3(0.244668, -0.229311, -0.30332)]
"values": [Vector3(0.15, -0.3, -0.2), Vector3(0.15, -0.3, -0.2)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("SpringArm3D/Inventory/SpaceGun:rotation")
tracks/1/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:rotation")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
@ -179,6 +180,9 @@ properties/3/replication_mode = 2
properties/4/path = NodePath(".:player_state")
properties/4/spawn = true
properties/4/replication_mode = 2
properties/5/path = NodePath(".:nickname")
properties/5/spawn = true
properties/5/replication_mode = 2
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_5j4ew"]
properties/0/path = NodePath(".:direction")
@ -194,55 +198,57 @@ properties/3/path = NodePath(".:skiing")
properties/3/spawn = false
properties/3/replication_mode = 2
[node name="Player" type="RigidBody3D"]
[node name="Player" type="RigidBody3D" node_paths=PackedStringArray("iff")]
collision_mask = 2147483649
axis_lock_angular_x = true
axis_lock_angular_y = true
axis_lock_angular_z = true
mass = 75.0
physics_material_override = SubResource("PhysicsMaterial_clur0")
can_sleep = false
continuous_cd = true
script = ExtResource("1_mk68k")
iff = NodePath("Smoothing/ThirdPerson/IFF")
jump_height = 1.5
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
visible = false
mesh = SubResource("CapsuleMesh_vmqfq")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = ExtResource("2_8rtw3")
shape = ExtResource("2_vjqny")
[node name="ShapeCast3D" type="ShapeCast3D" parent="."]
transform = Transform3D(1.05, 0, 0, 0, 1.05, 0, 0, 0, 1.05, 0, 0, 0)
shape = ExtResource("2_8rtw3")
shape = ExtResource("2_vjqny")
target_position = Vector3(0, 0, 0)
collision_mask = 2147483648
collide_with_areas = true
[node name="Sensor" type="Area3D" parent="."]
collision_layer = 0
collision_mask = 8
monitorable = false
[node name="CollisionShape3D" type="CollisionShape3D" parent="Sensor"]
shape = ExtResource("2_vjqny")
[node name="HUD" parent="." instance=ExtResource("3_ccety")]
[node name="SpringArm3D" type="SpringArm3D" parent="."]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
spring_length = 0.0
[node name="Camera3D" type="Camera3D" parent="SpringArm3D"]
fov = 100.0
near = 0.1
[node name="Inventory" type="Node3D" parent="SpringArm3D"]
[node name="SpaceGun" parent="SpringArm3D/Inventory" instance=ExtResource("4_lhn5w")]
transform = Transform3D(-1, 0, 2.53518e-06, 0, 1, 0, -2.53518e-06, 0, -1, 0.244668, -0.229311, -0.30332)
PROJECTILE = ExtResource("5_lvaut")
[node name="SpineIKTarget" type="Node3D" parent="SpringArm3D"]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
[node name="HealthComponent" parent="." instance=ExtResource("5_t6i6e")]
[node name="CollisionShape3D" type="CollisionShape3D" parent="HealthComponent"]
shape = ExtResource("2_vjqny")
[node name="FlagCarryComponent" parent="." node_paths=PackedStringArray("attachment", "sensor") instance=ExtResource("7_e7s1a")]
attachment = NodePath("../Smoothing/SpringArm3D/FlagCarryAttachment")
sensor = NodePath("../Sensor")
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
"": SubResource("AnimationLibrary_hg307")
}
autoplay = "idle"
autoplay = "shoot"
playback_default_blend_time = 0.05
[node name="ServerSynchronizer" type="MultiplayerSynchronizer" parent="."]
@ -253,15 +259,41 @@ root_path = NodePath(".")
replication_config = SubResource("SceneReplicationConfig_5j4ew")
script = ExtResource("6_ymcrr")
[node name="ThirdPerson" type="Node3D" parent="."]
[node name="Smoothing" type="Node3D" parent="."]
script = ExtResource("11_k330l")
target = NodePath("..")
flags = 3
[node name="PlayerMesh" parent="ThirdPerson" node_paths=PackedStringArray("spine_ik_target_attachment") instance=ExtResource("2_beyex")]
[node name="SpringArm3D" type="SpringArm3D" parent="Smoothing"]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
spring_length = 0.0
[node name="Camera3D" type="Camera3D" parent="Smoothing/SpringArm3D"]
fov = 90.0
near = 0.1
[node name="Inventory" type="Node3D" parent="Smoothing/SpringArm3D"]
[node name="SpaceGun" parent="Smoothing/SpringArm3D/Inventory" node_paths=PackedStringArray("holder") instance=ExtResource("4_6jh57")]
transform = Transform3D(-1, 0, 2.53518e-06, 0, 1, 0, -2.53518e-06, 0, -1, 0.15, -0.3, -0.2)
holder = NodePath("../../../..")
[node name="SpineIKTarget" type="Node3D" parent="Smoothing/SpringArm3D"]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
[node name="FlagCarryAttachment" type="Node3D" parent="Smoothing/SpringArm3D"]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, -0.620994, -0.287925)
[node name="ThirdPerson" type="Node3D" parent="Smoothing"]
[node name="PlayerMesh" parent="Smoothing/ThirdPerson" node_paths=PackedStringArray("spine_ik_target_attachment") instance=ExtResource("2_beyex")]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
spine_ik_target_attachment = NodePath("../../SpringArm3D/SpineIKTarget")
[node name="IFFAttachment" type="Node3D" parent="ThirdPerson"]
[node name="IFFAttachment" type="Marker3D" parent="Smoothing/ThirdPerson"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.27457, 0)
[node name="IFF" parent="ThirdPerson" node_paths=PackedStringArray("attach_point", "player") instance=ExtResource("7_8hc80")]
[node name="IFF" parent="Smoothing/ThirdPerson" node_paths=PackedStringArray("player", "attach_point") instance=ExtResource("7_8hc80")]
player = NodePath("../../..")
attach_point = NodePath("../IFFAttachment")
player = NodePath("../..")

View file

@ -1,71 +1,73 @@
# 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 PlayerInput extends MultiplayerSynchronizer
@export var jetting = false
@export var skiing = false
@export var direction = Vector2.ZERO
@export var jetting : bool = false
@export var skiing : bool = false
@export var direction : Vector2 = Vector2.ZERO
@export var camera_rotation : Vector2
@export var MOUSE_SENSITIVITY : float = .6
@export var inverted_y_axis : bool = false
signal jumped
signal fired_primary
signal throwed_flag
var _mouse_position: Vector2
func _ready():
var has_authority = is_multiplayer_authority()
func _ready() -> void:
var has_authority : bool = is_multiplayer_authority()
set_process(has_authority)
set_process_unhandled_input(has_authority)
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _unhandled_input(event: InputEvent) -> void:
var mouse_mode = Input.get_mouse_mode()
var mouse_mode : Input.MouseMode = Input.get_mouse_mode()
# isolate mouse events
if event is InputEventMouseMotion:
if mouse_mode == Input.MOUSE_MODE_CAPTURED:
# retrieve mouse position relative to last frame
_mouse_position = event.relative * MOUSE_SENSITIVITY
_update_camera(event.relative)
func _process(delta):
func _update_camera(relative_motion : Vector2) -> void:
# clamp vertical rotation (head motion)
if inverted_y_axis:
relative_motion.y *= -1.0
camera_rotation.y -= relative_motion.y * MOUSE_SENSITIVITY / 100
camera_rotation.y = clamp(camera_rotation.y, deg_to_rad(-90.0),deg_to_rad(90.0))
# wrap horizontal rotation (to prevent accumulation)
camera_rotation.x -= relative_motion.x * MOUSE_SENSITIVITY / 100
camera_rotation.x = wrapf(camera_rotation.x, deg_to_rad(0.0),deg_to_rad(360.0))
func _process(_delta : float) -> void:
direction = Input.get_vector("left", "right", "forward", "backward")
if Input.is_action_just_pressed("jump_and_jet"):
_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 _physics_process(delta):
_update_camera(delta)
@rpc("call_local")
func _jump():
func _jump() -> void:
jumped.emit()
@rpc("call_local")
func _fire_primary():
func _fire_primary() -> void:
fired_primary.emit()
func _update_camera(delta):
# clamp vertical rotation (head motion)
camera_rotation.y -= _mouse_position.y * delta
camera_rotation.y = clamp(camera_rotation.y, deg_to_rad(-90.0),deg_to_rad(90.0))
# wrap horizontal rotation (to prevent accumulation)
camera_rotation.x -= _mouse_position.x * delta
camera_rotation.x = wrapf(camera_rotation.x, deg_to_rad(0.0),deg_to_rad(360.0))
# reset mouse motion until next input event
_mouse_position = Vector2.ZERO
@rpc("call_local")
func _throw_flag() -> void:
throwed_flag.emit()

View file

@ -1,50 +0,0 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://b83leo1ca47d7"
path="res://.godot/imported/player_mesh.blend-b5697f48af9161c855a7a3326936546b.scn"
[deps]
source_file="res://entities/target_dummy/assets/player_mesh.blend"
dest_files=["res://.godot/imported/player_mesh.blend-b5697f48af9161c855a7a3326936546b.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
meshes/ensure_tangents=true
meshes/generate_lods=false
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=1
gltf/embedded_image_handling=1
blender/nodes/visible=0
blender/nodes/punctual_lights=true
blender/nodes/cameras=true
blender/nodes/custom_properties=true
blender/nodes/modifiers=1
blender/meshes/colors=false
blender/meshes/uvs=true
blender/meshes/normals=true
blender/meshes/tangents=true
blender/meshes/skins=2
blender/meshes/export_bones_deforming_mesh_only=false
blender/materials/unpack_enabled=true
blender/materials/export_materials=1
blender/animation/limit_playback=true
blender/animation/always_sample=true
blender/animation/group_tracks=true

Binary file not shown.

View file

@ -0,0 +1,34 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://chuein4frnvwt"
path="res://.godot/imported/player_mesh.glb-a0e04e00b0469c9659174448aceb10a7.scn"
[deps]
source_file="res://entities/target_dummy/assets/player_mesh.glb"
dest_files=["res://.godot/imported/player_mesh.glb-a0e04e00b0469c9659174448aceb10a7.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=1
gltf/embedded_image_handling=1

View file

@ -1,15 +1,15 @@
# 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 DummyTarget extends RigidBody3D
@ -24,8 +24,9 @@ var start_pos : Vector3
func _ready() -> void:
health_component.health_zeroed.connect(spawn)
start_pos = global_position
$TargetMesh/AnimationPlayer.play("gunTwoHanded")
func spawn():
func spawn(_killer_id : int) -> void:
hide()
collision_shape_3d.disabled = true
await get_tree().create_timer(respawn_time).timeout

View file

@ -1,103 +1,25 @@
[gd_scene load_steps=5 format=3 uid="uid://dpnu1lvfncx6q"]
[ext_resource type="Script" path="res://entities/target_dummy/target_dummy.gd" id="1_r02ch"]
[ext_resource type="PackedScene" uid="uid://b83leo1ca47d7" path="res://entities/target_dummy/assets/player_mesh.blend" id="2_x73k8"]
[ext_resource type="PackedScene" uid="uid://bof3mg7wgxrmn" path="res://components/health_component.tscn" id="3_g1ap4"]
[ext_resource type="Script" path="res://entities/target_dummy/target_dummy.gd" id="1_iup5v"]
[ext_resource type="Shape3D" uid="uid://cb8esdlnottdn" path="res://entities/player/collision_shape.tres" id="2_i5k5j"]
[ext_resource type="PackedScene" uid="uid://chuein4frnvwt" path="res://entities/target_dummy/assets/player_mesh.glb" id="4_fuync"]
[ext_resource type="PackedScene" uid="uid://bof3mg7wgxrmn" path="res://components/health_component.tscn" id="4_l1exy"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_rk1ox"]
radius = 0.3
[node name="TargetDummy" type="RigidBody3D"]
[node name="DummyTarget" type="RigidBody3D"]
collision_layer = 2147483649
collision_mask = 2147483649
axis_lock_angular_x = true
axis_lock_angular_y = true
axis_lock_angular_z = true
mass = 75.0
continuous_cd = true
script = ExtResource("1_r02ch")
[node name="PlayerMesh" parent="." instance=ExtResource("2_x73k8")]
transform = Transform3D(0.75, 0, 0, 0, 0.75, 0, 0, 0, 0.75, 0, -0.916438, 0)
[node name="Skeleton3D" parent="PlayerMesh/playerrig" index="0"]
bones/0/rotation = Quaternion(0.12582, -1.79059e-15, -1.33409e-23, 0.992053)
bones/0/scale = Vector3(1, 1, 1)
bones/1/rotation = Quaternion(-0.0644764, 1.78006e-15, -6.67523e-16, 0.997919)
bones/2/rotation = Quaternion(-0.0774641, 0, 0, 0.996995)
bones/4/rotation = Quaternion(0.201738, -3.62729e-15, 4.53412e-16, 0.97944)
bones/6/rotation = Quaternion(-0.605155, -0.345862, -0.356135, 0.622363)
bones/7/rotation = Quaternion(-0.0464025, 0.68865, -0.38959, 0.609777)
bones/7/scale = Vector3(1, 1, 1)
bones/8/rotation = Quaternion(0.185291, 0.0266179, 0.00595018, 0.982305)
bones/8/scale = Vector3(1, 1, 1)
bones/9/rotation = Quaternion(0.126866, -0.0346058, -0.00662176, 0.991294)
bones/9/scale = Vector3(1, 1, 1)
bones/10/rotation = Quaternion(0.0557877, -0.312038, -0.196665, 0.927816)
bones/11/rotation = Quaternion(-0.0630718, -0.16283, 0.0971492, 0.979832)
bones/13/rotation = Quaternion(0.0299552, -0.545663, -0.00259936, 0.837465)
bones/13/scale = Vector3(1, 1, 1)
bones/14/rotation = Quaternion(-0.0636278, -0.0624373, 0.0299354, 0.995569)
bones/15/rotation = Quaternion(0.01399, 0.0251671, 0.0278526, 0.999197)
bones/16/rotation = Quaternion(-0.1, -0.561252, 0.0324573, 0.82094)
bones/17/rotation = Quaternion(0.0486079, -0.0407852, 0.014286, 0.997883)
bones/19/rotation = Quaternion(-0.12453, -0.526036, 0.129609, 0.831252)
bones/20/rotation = Quaternion(-0.0225172, -0.0668488, 0.0231689, 0.99724)
bones/20/scale = Vector3(1, 1, 1)
bones/21/rotation = Quaternion(-0.00114936, 0.0252308, -0.0174874, 0.999528)
bones/21/scale = Vector3(1, 1, 1)
bones/22/rotation = Quaternion(-0.192536, -0.535774, 0.0761884, 0.818579)
bones/23/rotation = Quaternion(0.0104723, -0.0704384, 0.0293146, 0.99703)
bones/23/scale = Vector3(1, 1, 1)
bones/24/rotation = Quaternion(0.0334518, 0.0259883, -0.0178538, 0.998943)
bones/25/rotation = Quaternion(-0.605155, 0.345863, 0.356135, 0.622363)
bones/25/scale = Vector3(1, 1, 1)
bones/26/rotation = Quaternion(-0.0464025, -0.68865, 0.38959, 0.609777)
bones/26/scale = Vector3(1, 1, 1)
bones/27/rotation = Quaternion(0.185291, -0.0266179, -0.00595018, 0.982305)
bones/27/scale = Vector3(1, 1, 1)
bones/28/rotation = Quaternion(-0.144176, 0.0367847, -0.00247504, 0.988865)
bones/28/scale = Vector3(1, 1, 1)
bones/30/rotation = Quaternion(-0.0630717, 0.16283, -0.0971492, 0.979832)
bones/30/scale = Vector3(1, 1, 1)
bones/31/rotation = Quaternion(0.0237032, 0.0123211, -0.0433656, 0.998702)
bones/31/scale = Vector3(1, 1, 1)
bones/32/rotation = Quaternion(0.243143, 0.523041, 0.164477, 0.800161)
bones/32/scale = Vector3(1, 1, 1)
bones/33/rotation = Quaternion(-0.0636278, 0.0624373, -0.0299354, 0.995569)
bones/34/rotation = Quaternion(0.0139901, -0.0251671, -0.0278526, 0.999197)
bones/35/rotation = Quaternion(0.113348, 0.545076, 0.134205, 0.819776)
bones/36/rotation = Quaternion(0.0486079, 0.0407853, -0.0142859, 0.997883)
bones/38/rotation = Quaternion(0.0934222, 0.536709, 0.031309, 0.837995)
bones/39/rotation = Quaternion(-0.0225172, 0.0668488, -0.023169, 0.99724)
bones/40/rotation = Quaternion(-0.00114935, -0.0252307, 0.0174875, 0.999528)
bones/40/scale = Vector3(1, 1, 1)
bones/41/rotation = Quaternion(0.0241847, 0.530538, 0.0849425, 0.843048)
bones/41/scale = Vector3(1, 1, 1)
bones/42/rotation = Quaternion(0.0104724, 0.0704385, -0.0293146, 0.99703)
bones/43/rotation = Quaternion(0.0334518, -0.0259883, 0.0178538, 0.998943)
bones/44/rotation = Quaternion(0.986515, -3.3762e-16, 5.03093e-24, 0.163672)
bones/44/scale = Vector3(1, 1, 0.999997)
bones/45/rotation = Quaternion(0.143207, -4.28623e-07, 5.19845e-06, 0.989693)
bones/45/scale = Vector3(1, 1, 1)
bones/46/rotation = Quaternion(-0.574131, 2.92207e-06, -4.58343e-06, 0.818763)
bones/47/rotation = Quaternion(1.99048e-05, 0.961249, -0.27568, 4.3124e-06)
bones/47/scale = Vector3(1, 1, 1)
bones/48/rotation = Quaternion(0.679706, 0.679706, -0.194936, 0.194936)
bones/49/rotation = Quaternion(0.986515, -3.3762e-16, 5.03093e-24, 0.163672)
bones/49/scale = Vector3(1, 1, 0.999997)
bones/50/rotation = Quaternion(0.143207, 4.28623e-07, -5.19845e-06, 0.989693)
bones/50/scale = Vector3(1, 1, 1)
bones/51/rotation = Quaternion(-0.574131, -2.92207e-06, 4.58343e-06, 0.818763)
bones/52/rotation = Quaternion(-1.99048e-05, 0.961249, -0.27568, -4.3124e-06)
bones/52/scale = Vector3(1, 1, 1)
bones/53/rotation = Quaternion(0.679706, -0.679706, 0.194936, 0.194936)
[node name="AnimationPlayer" parent="PlayerMesh" index="4"]
autoplay = "gunOneHanded"
script = ExtResource("1_iup5v")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("CapsuleShape3D_rk1ox")
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = ExtResource("2_i5k5j")
[node name="HealthComponent" parent="." instance=ExtResource("3_g1ap4")]
max_health = 300
[node name="HealthComponent" parent="." instance=ExtResource("4_l1exy")]
[editable path="PlayerMesh"]
[node name="TargetMesh" parent="." instance=ExtResource("4_fuync")]
transform = Transform3D(0.75, 0, 0, 0, 0.75, 0, 0, 0, 0.75, 0, 0, 0)

Binary file not shown.

View file

@ -0,0 +1,34 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bjcn37ops3bro"
path="res://.godot/imported/disclauncher.glb-e58edebbe444fad4554f8c76e8b18f06.scn"
[deps]
source_file="res://entities/weapons/space_gun/assets/disclauncher.glb"
dest_files=["res://.godot/imported/disclauncher.glb-e58edebbe444fad4554f8c76e8b18f06.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=1
gltf/embedded_image_handling=1

View file

@ -1,15 +1,15 @@
# 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 Projectile extends Node3D
@ -19,32 +19,34 @@ class_name Projectile extends Node3D
@export var speed : float = 78.4 # m/s
@export var lifespan : float = 5.0 # in seconds
@onready var shape_cast = $ShapeCast3D
@onready var game = get_tree().get_current_scene()
@onready var shape_cast : ShapeCast3D = $ShapeCast3D
@onready var game : Node3D = get_tree().get_current_scene()
var velocity : Vector3 = Vector3.ZERO
var shooter : Player
func _ready():
var lifespan_timer = Timer.new()
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)
func self_destruct():
func self_destruct() -> void:
explode(position)
func explode(spawn_location):
var spawned_explosion = EXPLOSION.instantiate()
func explode(spawn_location : Vector3) -> void:
var spawned_explosion : Node = EXPLOSION.instantiate()
spawned_explosion.position = spawn_location
spawned_explosion.shooter = shooter
game.add_child(spawned_explosion)
queue_free()
func _physics_process(delta):
var previous_position = global_position
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 = shape_cast.collision_result[0].point
var contact_point : Vector3 = shape_cast.collision_result[0].point
explode(contact_point)

View file

@ -1,8 +1,8 @@
[gd_scene load_steps=7 format=3 uid="uid://dn1tcakam5egs"]
[ext_resource type="Script" path="res://weapons/space_gun/projectile.gd" id="1_4j1dp"]
[ext_resource type="PackedScene" uid="uid://8atq41j7wd55" path="res://weapons/space_gun/projectile_explosion.tscn" id="2_llml6"]
[ext_resource type="Script" path="res://weapons/space_gun/projectile_trail.gd" id="3_ygqbh"]
[ext_resource type="Script" path="res://entities/weapons/space_gun/projectile.gd" id="1_4j1dp"]
[ext_resource type="PackedScene" uid="uid://8atq41j7wd55" path="res://entities/weapons/space_gun/projectile_explosion.tscn" id="2_llml6"]
[ext_resource type="Script" path="res://entities/weapons/space_gun/projectile_trail.gd" id="3_ygqbh"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_4a265"]
transparency = 1
@ -45,4 +45,5 @@ shape = SubResource("SphereShape3D_umtte")
target_position = Vector3(0, 0, 0)
margin = 0.1
max_results = 1
collision_mask = 2147483649
debug_shape_custom_color = Color(1, 0, 0, 1)

View file

@ -1,23 +1,26 @@
# 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/>.
extends Node3D
@onready var fire = $Fire
var shooter : Player
@onready var fire : GPUParticles3D = $Fire
@onready var explosive_damage : ExplosiveDamageComponent = $ExplosiveDamageComponent
var explosion_effect_pending : bool = false
func _ready():
func _ready() -> void:
explosive_damage.damage_dealer = shooter
fire.emitting = true
fire.finished.connect(func(): queue_free())
fire.finished.connect(func() -> void: queue_free())

View file

@ -1,6 +1,6 @@
[gd_scene load_steps=9 format=3 uid="uid://8atq41j7wd55"]
[ext_resource type="Script" path="res://weapons/space_gun/projectile_explosion.gd" id="1_fp5td"]
[ext_resource type="Script" path="res://entities/weapons/space_gun/projectile_explosion.gd" id="1_fp5td"]
[ext_resource type="PackedScene" uid="uid://ds1hekx1dq1bg" path="res://components/explosive_damage_component.tscn" id="2_d4sf4"]
[sub_resource type="Curve" id="Curve_rg204"]

View file

@ -1,22 +1,22 @@
# 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 Trail3D extends MeshInstance3D
var _points = [] # 3D points of trail
var _widths = [] # Calculated widths of trail
var _lifespans = [] # Trail point lifespans
var _points : Array = [] # 3D points of trail
var _widths : Array = [] # Calculated widths of trail
var _lifespans : Array = [] # Trail point lifespans
@export var _trail_enabled : bool = true # Is trail shown?
@ -33,7 +33,7 @@ var _lifespans = [] # Trail point lifespans
var _old_pos : Vector3 # Previous pos
func _append_point():
func _append_point() -> void:
_points.append(get_global_transform().origin)
_widths.append([
get_global_transform().basis.x * _from_width,
@ -41,52 +41,52 @@ func _append_point():
])
_lifespans.append(0.0)
func _remove_point(i):
func _remove_point(i : int) -> void:
_points.remove_at(i)
_widths.remove_at(i)
_lifespans.remove_at(i)
func _ready():
func _ready() -> void:
_old_pos = get_global_transform().origin
mesh = ImmediateMesh.new()
func _process(delta):
func _process(delta : float) -> void:
if(_old_pos - get_global_transform().origin).length() > _motion_delta and _trail_enabled:
_append_point() # Create new point
_old_pos = get_global_transform().origin # Update previous position to current position coordinates
# Update the lifespan
var p = 0
var max_points = _points.size()
var p : int = 0
var max_points : int = _points.size()
while p < max_points:
_lifespans[p] += delta
if _lifespans[p] > _lifespan:
_remove_point(p)
p -= 1
if (p < 0): p = 0
max_points = _points.size()
p += 1
mesh.clear_surfaces()
# If less than 2 points, stop rendering trail
if _points.size() < 2:
return
# Render new mesh for each point
mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
for i in range(_points.size()):
var t = float(i) / (_points.size() - 1.0)
var curr_color = _start_color.lerp(_end_color, 1 - t)
var t : float = float(i) / (_points.size() - 1.0)
var curr_color : Color = _start_color.lerp(_end_color, 1 - t)
mesh.surface_set_color(curr_color)
var curr_width = _widths[i][0] - pow(1 - t, _scale_acceleration) * _widths[i][1]
var t0 = i / _points.size()
var t1 = t
var curr_width : Vector3 = _widths[i][0] - pow(1 - t, _scale_acceleration) * _widths[i][1]
var t0 : float = i / _points.size()
var t1 : float = t
mesh.surface_set_uv(Vector2(t0, 0))
mesh.surface_add_vertex(to_local(_points[i] + curr_width))
mesh.surface_set_uv(Vector2(t1, 1))

View file

@ -1,24 +1,25 @@
# 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/>.
extends Node3D
class_name SpaceGun
@export var PROJECTILE : PackedScene
@export var holder : Player
@onready var nozzle = $Nozzle
@onready var inventory = get_parent()
@onready var nozzle : Node3D = $Nozzle
@onready var inventory : Node3D = get_parent()
enum WeaponState { WEAPON_READY, WEAPON_RELOADING }
@ -27,20 +28,21 @@ var weapon_state : WeaponState = WeaponState.WEAPON_READY
const inheritance : float = .5 # ratio
const reload_time : float = 1. # seconds
func can_fire():
func can_fire() -> bool:
return weapon_state == WeaponState.WEAPON_READY
func fire_primary():
func fire_primary() -> void:
if not can_fire():
return
var projectile = PROJECTILE.instantiate()
var projectile : Node = PROJECTILE.instantiate()
projectile.transform = nozzle.global_transform
projectile.velocity = nozzle.global_basis.z.normalized() * projectile.speed
var inheritance_factor = clamp(inheritance, 0., 1.)
projectile.shooter = holder
var inheritance_factor : float = clamp(inheritance, 0., 1.)
projectile.velocity += (inventory.owner.linear_velocity * inheritance_factor)
inventory.owner.add_sibling(projectile)
var collider = projectile.shape_cast
var collider : ShapeCast3D = projectile.shape_cast
collider.add_exception(inventory.owner)
weapon_state = WeaponState.WEAPON_RELOADING
await get_tree().create_timer(reload_time).timeout

View file

@ -0,0 +1,14 @@
[gd_scene load_steps=4 format=3 uid="uid://c8co0qa2omjmh"]
[ext_resource type="Script" path="res://entities/weapons/space_gun/space_gun.gd" id="1_6sm4s"]
[ext_resource type="PackedScene" uid="uid://dn1tcakam5egs" path="res://entities/weapons/space_gun/projectile.tscn" id="2_wvneg"]
[ext_resource type="PackedScene" uid="uid://bjcn37ops3bro" path="res://entities/weapons/space_gun/assets/disclauncher.glb" id="3_5k2xm"]
[node name="SpaceGun" type="Node3D"]
script = ExtResource("1_6sm4s")
PROJECTILE = ExtResource("2_wvneg")
[node name="disclauncher" parent="." instance=ExtResource("3_5k2xm")]
[node name="Nozzle" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.111355, 0.540839)

41
environments/default.tres Normal file
View file

@ -0,0 +1,41 @@
[gd_resource type="Environment" load_steps=4 format=3 uid="uid://d2ahijqqspw5f"]
[ext_resource type="Texture2D" uid="uid://btdbu0qbe1646" path="res://environments/skyboxes/kloppenheim_06_puresky_2k.exr" id="1_k44rf"]
[sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_7tawh"]
panorama = ExtResource("1_k44rf")
[sub_resource type="Sky" id="Sky_iloxd"]
sky_material = SubResource("PanoramaSkyMaterial_7tawh")
process_mode = 1
radiance_size = 4
[resource]
background_mode = 2
background_energy_multiplier = 0.2
sky = SubResource("Sky_iloxd")
ambient_light_source = 3
tonemap_mode = 3
tonemap_exposure = 0.6
tonemap_white = 0.6
ssr_enabled = true
ssao_intensity = 5.0
ssil_enabled = true
ssil_intensity = 5.0
ssil_sharpness = 0.7
sdfgi_cascades = 6
sdfgi_max_distance = 819.2
sdfgi_y_scale = 0
glow_normalized = true
glow_intensity = 2.0
glow_strength = 0.96
fog_enabled = true
fog_light_color = Color(1, 1, 1, 1)
fog_light_energy = 0.15
fog_density = 0.0025
fog_sky_affect = 0.3
volumetric_fog_density = 0.003
volumetric_fog_albedo = Color(1, 0.380392, 0.380392, 1)
volumetric_fog_emission_energy = 192.23
volumetric_fog_length = 551.32
volumetric_fog_ambient_inject = 0.37

20
environments/desert.tres Normal file
View file

@ -0,0 +1,20 @@
[gd_resource type="Environment" load_steps=3 format=3 uid="uid://nw62ce5cglvs"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_8mbvu"]
sky_top_color = Color(0, 0.584314, 0.670588, 1)
sky_horizon_color = Color(0.819608, 0.592157, 0.556863, 1)
sky_curve = 0.235375
ground_bottom_color = Color(0.819608, 0.592157, 0.556863, 1)
ground_horizon_color = Color(0.819608, 0.592157, 0.556863, 1)
ground_curve = 0.02
[sub_resource type="Sky" id="Sky_mobku"]
sky_material = SubResource("ProceduralSkyMaterial_8mbvu")
[resource]
background_mode = 2
sky = SubResource("Sky_mobku")
tonemap_mode = 3
tonemap_exposure = 0.85
volumetric_fog_density = 0.005
adjustment_brightness = 0.85

Binary file not shown.

View file

@ -2,8 +2,8 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cvtqt0k2ewd07"
path.s3tc="res://.godot/imported/albedo.png-7f543d432ab75668956abd5bcc51a13b.s3tc.ctex"
uid="uid://btdbu0qbe1646"
path.bptc="res://.godot/imported/kloppenheim_06_puresky_2k.exr-ec828ffc9b05b2c03fb35e84cd230d2d.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
@ -11,18 +11,18 @@ metadata={
[deps]
source_file="res://weapons/space_gun/assets/albedo.png"
dest_files=["res://.godot/imported/albedo.png-7f543d432ab75668956abd5bcc51a13b.s3tc.ctex"]
source_file="res://environments/skyboxes/kloppenheim_06_puresky_2k.exr"
dest_files=["res://.godot/imported/kloppenheim_06_puresky_2k.exr-ec828ffc9b05b2c03fb35e84cd230d2d.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/high_quality=true
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""

37
icon.svg.import Normal file
View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://detphn5aabvf3"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -1,4 +0,0 @@
[gd_resource type="StyleBoxFlat" format=3 uid="uid://cl7dmo2bg4153"]
[resource]
bg_color = Color(1, 0.356863, 0.145098, 1)

View file

@ -1,124 +1,145 @@
[gd_scene load_steps=6 format=3 uid="uid://bcv81ku26xo"]
[gd_scene load_steps=8 format=3 uid="uid://bcv81ku26xo"]
[ext_resource type="StyleBox" uid="uid://cl7dmo2bg4153" path="res://interfaces/hud/healthbar_fill_style.tres" id="1_gh51l"]
[ext_resource type="StyleBox" uid="uid://dcn1ll2ra4lwn" path="res://interfaces/hud/vitals/background.tres" id="1_gmv44"]
[ext_resource type="StyleBox" uid="uid://bq7rjpm38pao7" path="res://interfaces/hud/vitals/health_foreground.tres" id="2_6ejsl"]
[sub_resource type="GDScript" id="GDScript_2vxif"]
script/source = "extends CanvasLayer
class_name HUD
script/source = "class_name HUD extends CanvasLayer
@onready var _health_bar = $HealthBar
@onready var _energy_bar = $EnergyBar
@export_category(\"Vitals\")
@export var _health_bar : ProgressBar
@export var _energy_bar : ProgressBar
func _ready():
_update_health_bar(100)
_update_energy_bar(100)
func _ready() -> void:
_update_health_bar(100.)
_update_energy_bar(100.)
func _update_energy_bar(energy) -> void:
func _update_energy_bar(energy : float) -> void:
_energy_bar.value = energy
func _on_energy_changed(new_energy) -> void:
func _on_energy_changed(new_energy : float) -> void:
_update_energy_bar(new_energy)
func _update_health_bar(health) -> void:
func _update_health_bar(health : float) -> void:
_health_bar.value = health
func _on_health_changed(new_health) -> void:
func _on_health_changed(new_health : float) -> void:
_update_health_bar(new_health)
"
[sub_resource type="Theme" id="Theme_irfqb"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_f23s3"]
bg_color = Color(0, 0.454902, 0.992157, 1)
[sub_resource type="GDScript" id="GDScript_w8l21"]
script/source = "extends Label
@onready var player = get_parent().owner
@onready var player : Player = get_parent().owner
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
func _ready() -> void:
if not OS.is_debug_build():
queue_free()
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
func _process(_delta : float) -> void:
text = \"\"
text += \"fps: %s\\n\" % str(Engine.get_frames_per_second())
text += \"position: %d, %d, %d\\n\" % [player.position.x, player.position.y, player.position.z]
text += \"speed: %d km/h\\n\" % (player.linear_velocity.length() * 3.6)
"
[node name="HUD" type="CanvasLayer"]
[sub_resource type="Shader" id="Shader_gaah5"]
code = "shader_type canvas_item;
uniform vec4 color : source_color;
float circle(vec2 pos, float radius, float feather) {
return smoothstep(radius, radius - feather, length(pos - vec2(.5)));
}
void fragment() {
// draw reticle
float alpha = circle(UV, .5, .04);
COLOR = vec4(texture(TEXTURE, UV).rgb, alpha);
}"
[sub_resource type="ShaderMaterial" id="ShaderMaterial_7blp5"]
shader = SubResource("Shader_gaah5")
shader_parameter/color = Color(1, 1, 1, 1)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_f23s3"]
bg_color = Color(0.0901961, 0.87451, 0.760784, 0.65098)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
anti_aliasing = false
[node name="HUD" type="CanvasLayer" node_paths=PackedStringArray("_health_bar", "_energy_bar")]
script = SubResource("GDScript_2vxif")
[node name="HealthBar" type="ProgressBar" parent="."]
offset_left = 32.0
offset_top = 32.0
offset_right = 288.0
offset_bottom = 40.0
size_flags_horizontal = 0
mouse_filter = 2
theme = SubResource("Theme_irfqb")
theme_override_styles/fill = ExtResource("1_gh51l")
value = 60.0
show_percentage = false
[node name="EnergyBar" type="ProgressBar" parent="."]
offset_left = 32.0
offset_top = 48.0
offset_right = 288.0
offset_bottom = 56.0
size_flags_horizontal = 0
mouse_filter = 2
theme = SubResource("Theme_irfqb")
theme_override_styles/fill = SubResource("StyleBoxFlat_f23s3")
value = 60.0
show_percentage = false
_health_bar = NodePath("MarginContainer/HBoxContainer/VBoxContainer/HealthBar")
_energy_bar = NodePath("MarginContainer/HBoxContainer/VBoxContainer/EnergyBar")
[node name="DebugLabel" type="Label" parent="."]
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = 20.0
offset_top = -33.0
offset_top = 20.0
offset_right = 21.0
offset_bottom = -10.0
grow_vertical = 0
offset_bottom = 43.0
script = SubResource("GDScript_w8l21")
[node name="Reticle" type="Control" parent="."]
layout_mode = 3
[node name="Reticle" type="ColorRect" parent="."]
material = SubResource("ShaderMaterial_7blp5")
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -20.0
offset_top = -20.0
offset_right = 20.0
offset_bottom = 20.0
offset_left = -1.5
offset_top = -1.5
offset_right = 1.5
offset_bottom = 1.5
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="LineBottom" type="Line2D" parent="Reticle"]
position = Vector2(13, 4)
points = PackedVector2Array(7, 19, 7, 31)
width = 2.0
[node name="MarginContainer" type="MarginContainer" parent="."]
anchors_preset = 12
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -42.0
grow_horizontal = 2
grow_vertical = 0
theme_override_constants/margin_left = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="LineLeft" type="Line2D" parent="Reticle"]
position = Vector2(36, 13)
rotation = 1.5708
points = PackedVector2Array(7, 19, 7, 31)
width = 2.0
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="LineTop" type="Line2D" parent="Reticle"]
position = Vector2(13, -14)
points = PackedVector2Array(7, 19, 7, 31)
width = 2.0
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.2
theme_override_constants/separation = 6
[node name="LineRight" type="Line2D" parent="Reticle"]
position = Vector2(54, 13)
rotation = 1.5708
points = PackedVector2Array(7, 19, 7, 31)
width = 2.0
[node name="HealthBar" type="ProgressBar" parent="MarginContainer/HBoxContainer/VBoxContainer"]
custom_minimum_size = Vector2(0, 8)
layout_mode = 2
mouse_filter = 2
theme_override_styles/background = ExtResource("1_gmv44")
theme_override_styles/fill = ExtResource("2_6ejsl")
value = 60.0
show_percentage = false
[node name="EnergyBar" type="ProgressBar" parent="MarginContainer/HBoxContainer/VBoxContainer"]
custom_minimum_size = Vector2(0, 8)
layout_mode = 2
mouse_filter = 2
theme_override_styles/background = ExtResource("1_gmv44")
theme_override_styles/fill = SubResource("StyleBoxFlat_f23s3")
value = 60.0
show_percentage = false
[node name="Spacer" type="Control" parent="MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.8

View file

@ -1,41 +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/>.
extends Node2D
@export var attach_point : Node3D
@export var player : Player
@onready var _player_name_label = $Offset/VBoxContainer/PlayerNameLabel
@onready var _health_bar = $Offset/VBoxContainer/HealthBar
@onready var _v_box_container = $Offset/VBoxContainer
func _process(_delta):
var camera : Camera3D = get_viewport().get_camera_3d()
if camera.is_position_behind(attach_point.global_position):
_v_box_container.hide()
else:
_v_box_container.show()
_player_name_label.text = player.name
position = camera.unproject_position(attach_point.global_position)
var viewport_size : Vector2 = get_viewport_rect().size
position.x = clampf(position.x, 0, viewport_size.x - _v_box_container.size.x)
position.y = clampf(position.y, _v_box_container.size.y, viewport_size.y)
func _update_health_bar(health) -> void:
_health_bar.value = health
func _on_health_changed(new_health) -> void:
_update_health_bar(new_health)

View file

@ -1,26 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://dsysi2rd3bu76"]
[ext_resource type="Script" path="res://interfaces/hud/iff.gd" id="1_75yi1"]
[ext_resource type="StyleBox" uid="uid://cl7dmo2bg4153" path="res://interfaces/hud/healthbar_fill_style.tres" id="2_x7k8o"]
[node name="IFF" type="Node2D"]
script = ExtResource("1_75yi1")
[node name="Offset" type="Node2D" parent="."]
position = Vector2(0, -32)
[node name="VBoxContainer" type="VBoxContainer" parent="Offset"]
offset_right = 100.0
offset_bottom = 40.0
mouse_filter = 2
[node name="PlayerNameLabel" type="Label" parent="Offset/VBoxContainer"]
layout_mode = 2
metadata/_edit_use_anchors_ = true
[node name="HealthBar" type="ProgressBar" parent="Offset/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_styles/fill = ExtResource("2_x7k8o")
value = 60.0
show_percentage = false

View file

@ -0,0 +1,9 @@
[gd_resource type="StyleBoxFlat" format=3 uid="uid://cgv081wfih7la"]
[resource]
bg_color = Color(1, 0, 0, 0.25)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
anti_aliasing = false

View file

@ -0,0 +1,91 @@
# 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/>.
extends Control
@export var player : Player
@export var attach_point : Marker3D
@export var healthbar_low_health_color : Color = Color(1 ,0 ,0, 0.78)
@export var healthbar_mid_health_color : Color = Color(1, 1, 0, 0.78)
@export var healthbar_high_health_color : Color = Color(0, 1, 0, 0.78)
@export var iff_in_range_radius_ratio : float = 0.05
@onready var _iff_container : Control = $IFFContainer
@onready var _iff_in_range_container : Control = $IFFContainer/IFFInRangeContainer
@onready var _player_name_label : Control = $IFFContainer/IFFInRangeContainer/PlayerNameLabel
@onready var _health_bar : ProgressBar = $IFFContainer/IFFInRangeContainer/HealthBar
@onready var is_within_los : bool = false
@onready var healthbar_fill_stylebox : StyleBoxFlat = _health_bar.get_theme_stylebox("fill")
func _ready() -> void:
_player_name_label.text = player.name
func _process(_delta : float) -> void:
# retrieve camera for current viewport
var camera : Camera3D = get_viewport().get_camera_3d()
# if the player is NOT infront of the camera or camera does NOT have LOS to player
if camera.is_position_behind(attach_point.global_position) or !is_within_los:
_iff_container.hide() # hide the IFF and exit function
return
else:
_iff_container.show() # display the IFF
func _physics_process(_delta : float) -> void:
# https://docs.godotengine.org/en/stable/tutorials/physics/ray-casting.html
var camera : Camera3D = get_viewport().get_camera_3d()
is_within_los = false # always initialise trace_success as false
var space_state : PhysicsDirectSpaceState3D = camera.get_world_3d().direct_space_state
# do a trace check from camera to towards the player
var query : PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(camera.global_position, attach_point.global_position)
# exclude the camera owner nodes from intersecting with the trace check
query.exclude = [camera.owner]
# do trace check and store result
var result : Dictionary = space_state.intersect_ray(query)
# if a result was returned and the hit result is the player
if result and result["collider"] == player:
# the player is in camera LOS
is_within_los = true
var new_iff_position : Vector2 = camera.unproject_position(attach_point.global_position) # get the screen location of the players head
var viewport_size : Vector2 = get_viewport_rect().size
# check if the unprojected point lies inside the viewport
if !Rect2(Vector2(0, 0), viewport_size).has_point(new_iff_position):
_iff_container.hide() # hide the IFF and exit function
return
# if player is NOT in range to have its healthbar and name drawn
if (new_iff_position - viewport_size / 2).length() > iff_in_range_radius_ratio * viewport_size.length():
_iff_in_range_container.hide() # hide the in range IFF
else:
_iff_in_range_container.show()
new_iff_position.y -= _iff_container.size.y # move the IFF up so the bottom of it is at the players head
new_iff_position.x -= _iff_container.size.x / 2 # move the IFF left so it's centered horizontally above players head
position = new_iff_position # set the position of the IFF
func _update_health_bar(health : float) -> void:
_health_bar.value = health
# modify health_box stylebox depending health value
if health < 75 and health > 40:
healthbar_fill_stylebox.bg_color = healthbar_mid_health_color
elif health <= 40:
healthbar_fill_stylebox.bg_color = healthbar_low_health_color
else:
healthbar_fill_stylebox.bg_color = healthbar_high_health_color
func _on_health_changed(new_health : float) -> void:
_update_health_bar(new_health)

View file

@ -0,0 +1,54 @@
[gd_scene load_steps=4 format=3 uid="uid://dsysi2rd3bu76"]
[ext_resource type="Script" path="res://interfaces/hud/iffs/iff.gd" id="1_g1wra"]
[ext_resource type="StyleBox" uid="uid://cgv081wfih7la" path="res://interfaces/hud/iffs/health_foreground.tres" id="2_e3xla"]
[ext_resource type="StyleBox" uid="uid://dcn1ll2ra4lwn" path="res://interfaces/hud/vitals/background.tres" id="2_jtos4"]
[node name="IFF" type="Control"]
layout_mode = 3
anchors_preset = 0
script = ExtResource("1_g1wra")
[node name="IFFContainer" type="MarginContainer" parent="."]
layout_mode = 0
offset_right = 63.0
offset_bottom = 42.0
mouse_filter = 2
[node name="IFFInRangeContainer" type="Control" parent="IFFContainer"]
layout_mode = 2
mouse_filter = 2
[node name="PlayerNameLabel" type="Label" parent="IFFContainer/IFFInRangeContainer"]
layout_mode = 2
offset_right = 63.0
offset_bottom = 17.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_font_sizes/font_size = 12
text = "Player"
horizontal_alignment = 1
metadata/_edit_use_anchors_ = true
[node name="HealthBar" type="ProgressBar" parent="IFFContainer/IFFInRangeContainer"]
layout_mode = 2
offset_top = 20.0
offset_right = 63.0
offset_bottom = 24.0
mouse_filter = 2
theme_override_styles/background = ExtResource("2_jtos4")
theme_override_styles/fill = ExtResource("2_e3xla")
value = 60.0
show_percentage = false
[node name="Chevron" type="Label" parent="IFFContainer"]
layout_direction = 2
layout_mode = 2
size_flags_vertical = 1
theme_override_colors/font_color = Color(1, 0, 0, 0.2)
theme_override_colors/font_outline_color = Color(0, 0, 0, 0.2)
theme_override_constants/outline_size = 3
theme_override_constants/line_spacing = -20
theme_override_font_sizes/font_size = 14
text = "▼"
horizontal_alignment = 1
vertical_alignment = 2

View file

@ -0,0 +1,9 @@
[gd_resource type="StyleBoxFlat" format=3 uid="uid://dcn1ll2ra4lwn"]
[resource]
bg_color = Color(0, 0, 0, 0.25)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
anti_aliasing = false

View file

@ -0,0 +1,10 @@
[gd_resource type="StyleBoxFlat" format=3 uid="uid://bq7rjpm38pao7"]
[resource]
resource_local_to_scene = true
bg_color = Color(0.025, 0.75, 0.1, 0.65)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
anti_aliasing = false

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=7 format=3 uid="uid://bjctlqvs33nqy"]
[gd_scene load_steps=9 format=3 uid="uid://bjctlqvs33nqy"]
[ext_resource type="Texture2D" uid="uid://c1tjamjm8qjog" path="res://interfaces/menus/boot/background.webp" id="1_ph586"]
@ -8,88 +8,144 @@ script/source = "class_name BootMenu extends CanvasLayer
signal start_demo
func _on_demo_pressed():
func _on_demo_pressed() -> void:
start_demo.emit()
func _on_multiplayer_pressed() -> void:
_show_menu($Multiplayer)
func _on_settings_pressed() -> void:
_show_menu($Settings)
func _on_quit_pressed() -> void:
get_tree().quit()
func _on_main_menu_pressed() -> void:
_show_menu($Main)
func _show_menu(menu : PanelContainer) -> void:
$Multiplayer.hide()
$Multiplayer.tab_container.current_tab = 0
$Settings.hide()
$Settings.tab_container.current_tab = 0
$Main.hide()
menu.show()
"
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_krqeq"]
bg_color = Color(0.5, 0.5, 0.5, 0.25)
bg_color = Color(0.501961, 0.501961, 0.501961, 0.25098)
[sub_resource type="GDScript" id="GDScript_tc1bm"]
script/source = "class_name MultiplayerPanel extends PanelContainer
script/source = "class_name Multiplayer extends PanelContainer
const DEFAULT_HOST : String = \"localhost\"
const DEFAULT_PORT : int = 9000
const CONFIG_FILE_PATH : String = \"user://profile.cfg\"
var _join_address = RegEx.new()
var _registered_ports = RegEx.new()
var _join_address : RegEx = RegEx.new()
var _registered_ports : RegEx = RegEx.new()
var _config_file : ConfigFile = ConfigFile.new()
signal start_server(port)
signal join_server(host, port)
signal start_server(port : int)
signal join_server(host : String, port : int)
@onready var modal : Control = $Modal
@onready var menu : CanvasLayer = get_parent()
@export var tab_container : TabContainer
func _ready():
func _ready() -> void:
# see https://datatracker.ietf.org/doc/html/rfc1700
_registered_ports.compile(r'^(?:102[4-9]|10[3-9]\\d|1[1-9]\\d{2}|[2-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$')
_join_address.compile(r'^(?<host>[a-zA-Z0-9.-]+)(:(?<port>:102[4-9]|10[3-9]\\d|1[1-9]\\d{2}|[2-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5]))?$')
_load_config()
hide() # start hidden
func _on_menu_pressed():
func _load_config() -> void:
var error : Error = _config_file.load(CONFIG_FILE_PATH)
if error != OK:
return
var profile_name : String = _config_file.get_value(\"profile\", \"name\", \"Newblood\")
%ProfileName.text = profile_name
func _on_save_pressed() -> void:
_config_file.set_value(\"profile\", \"name\", %ProfileName.text)
_config_file.save(CONFIG_FILE_PATH)
func _on_menu_pressed() -> void:
hide()
owner.get_node(\"Main\").show()
func _on_quit_pressed():
func _on_quit_pressed() -> void:
get_tree().quit()
func _on_host_pressed():
var port = DEFAULT_PORT
func _on_host_pressed() -> void:
var port : int = DEFAULT_PORT
# check for registered ports number matches
if %ServerPort.text:
var result = _registered_ports.search(%ServerPort.text)
var result : RegExMatch = _registered_ports.search(%ServerPort.text)
if result: # port is valid
port = int(result.get_string())
else: # port is not valid
push_warning(\"A valid port number in the range 1024-65535 is required.\")
return
start_server.emit(port)
func _on_join_pressed():
var addr = [DEFAULT_HOST, DEFAULT_PORT]
start_server.emit(port, %ProfileName.text)
func _on_join_pressed() -> void:
var addr : Array = [DEFAULT_HOST, DEFAULT_PORT]
# validate join address input
var result = _join_address.search(%JoinAddress.text)
var result : RegExMatch = _join_address.search(%JoinAddress.text)
if result: # address is valid
addr[0] = result.get_string(\"host\")
var rport = result.get_string(\"port\")
var rport : String = result.get_string(\"port\")
if rport: addr[1] = int(rport)
$Modal.show()
join_server.emit(addr[0], addr[1])
join_server.emit(addr[0], addr[1], %ProfileName.text)
func _on_connected_to_server():
func _on_connected_to_server() -> void:
$Modal.hide()
menu.hide()
func _on_connection_failed():
func _on_connection_failed() -> void:
$Modal.hide()
"
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_snh7i"]
bg_color = Color(0, 0.5, 0.5, 0.25)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ky5sv"]
bg_color = Color(0.5, 0.5, 0.5, 0.25)
[sub_resource type="GDScript" id="GDScript_yqbr7"]
[sub_resource type="GDScript" id="GDScript_gbnwv"]
resource_name = "Settings"
script/source = "extends PanelContainer
func _on_multiplayer_pressed():
hide()
owner.get_node(\"Multiplayer\").show()
func _on_quit_pressed():
get_tree().quit()
const WINDOW_MODE_OPTIONS = [DisplayServer.WINDOW_MODE_WINDOWED, DisplayServer.WINDOW_MODE_FULLSCREEN]
@onready var _game_settings : Settings = get_node(\"/root/GlobalSettings\")
@export var tab_container : TabContainer
func _ready() -> void:
%FOVSpinBox.value = _game_settings.fov
%SensitivitySpinBox.value = _game_settings.mouse_sensitivity
var window_mode : int = 1 if _game_settings.fullscreen else 0
%InvertedYCheckbox.button_pressed = _game_settings.inverted_y_axis
%WindowModeOptionButton.select(window_mode)
func _on_save_pressed() -> void:
_game_settings.fov = %FOVSpinBox.value
_game_settings.mouse_sensitivity = %SensitivitySpinBox.value
_game_settings.inverted_y_axis = %InvertedYCheckbox.button_pressed
_game_settings.fullscreen = %WindowModeOptionButton.selected == 1
_game_settings.save_to_file()
"
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_c4ymk"]
bg_color = Color(0, 0, 0, 0)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dq4me"]
bg_color = Color(0, 0.5, 0.5, 0.2)
[node name="BootMenu" type="CanvasLayer"]
script = SubResource("GDScript_jd8xf")
@ -103,7 +159,7 @@ mouse_filter = 2
texture = ExtResource("1_ph586")
stretch_mode = 6
[node name="Multiplayer" type="PanelContainer" parent="."]
[node name="Multiplayer" type="PanelContainer" parent="." node_paths=PackedStringArray("tab_container")]
visible = false
anchors_preset = 15
anchor_right = 1.0
@ -112,6 +168,7 @@ grow_horizontal = 2
grow_vertical = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_krqeq")
script = SubResource("GDScript_tc1bm")
tab_container = NodePath("MarginContainer/VBoxContainer/TabContainer")
[node name="MarginContainer" type="MarginContainer" parent="Multiplayer"]
layout_mode = 2
@ -120,15 +177,19 @@ theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="TabContainer" type="TabContainer" parent="Multiplayer/MarginContainer"]
[node name="VBoxContainer" type="VBoxContainer" parent="Multiplayer/MarginContainer"]
layout_mode = 2
[node name="TabContainer" type="TabContainer" parent="Multiplayer/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/side_margin = 0
[node name="Profile" type="TabBar" parent="Multiplayer/MarginContainer/TabContainer"]
[node name="Profile" type="TabBar" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer"]
layout_mode = 2
theme_override_constants/h_separation = 0
[node name="MarginContainer" type="MarginContainer" parent="Multiplayer/MarginContainer/TabContainer/Profile"]
[node name="MarginContainer" type="MarginContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile"]
layout_mode = 0
offset_right = 1116.0
offset_bottom = 577.0
@ -137,58 +198,80 @@ theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="HBoxContainer" type="HBoxContainer" parent="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer"]
[node name="HBoxContainer" type="HBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer"]
layout_mode = 2
theme_override_constants/separation = 20
[node name="LeftBox" type="VBoxContainer" parent="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer/HBoxContainer"]
[node name="LeftBox" type="Control" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Create" type="Button" parent="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox"]
[node name="Top" type="VBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox"]
layout_mode = 1
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 101.0
grow_horizontal = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Create" type="Button" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox/Top"]
layout_mode = 2
disabled = true
text = "Create"
[node name="Delete" type="Button" parent="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox"]
[node name="Delete" type="Button" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox/Top"]
layout_mode = 2
disabled = true
text = "Delete"
[node name="Spacer" type="MarginContainer" parent="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox"]
[node name="Save" type="Button" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox/Top"]
layout_mode = 2
size_flags_vertical = 3
text = "Save"
[node name="Menu" type="Button" parent="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox"]
[node name="Bottom" type="VBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox"]
layout_mode = 1
anchors_preset = 12
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -31.0
grow_horizontal = 2
grow_vertical = 0
size_flags_horizontal = 0
[node name="MainMenu" type="Button" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
text = "Menu
size_flags_horizontal = 3
text = "Main Menu
"
[node name="Quit" type="Button" parent="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox"]
[node name="Quit" type="Button" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Quit"
[node name="RightBox" type="VBoxContainer" parent="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer/HBoxContainer"]
[node name="RightBox" type="VBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Label" type="Label" parent="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer/HBoxContainer/RightBox"]
[node name="Label" type="Label" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/RightBox"]
layout_mode = 2
text = "Current Profile:"
[node name="ProfileName" type="LineEdit" parent="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer/HBoxContainer/RightBox"]
[node name="ProfileName" type="LineEdit" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/RightBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
text = "Newblood"
[node name="Join" type="TabBar" parent="Multiplayer/MarginContainer/TabContainer"]
[node name="Join" type="TabBar" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer"]
visible = false
layout_mode = 2
select_with_rmb = true
[node name="MarginContainer" type="MarginContainer" parent="Multiplayer/MarginContainer/TabContainer/Join"]
[node name="MarginContainer" type="MarginContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@ -200,47 +283,65 @@ theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="HBoxContainer" type="HBoxContainer" parent="Multiplayer/MarginContainer/TabContainer/Join/MarginContainer"]
[node name="HBoxContainer" type="HBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer"]
layout_mode = 2
theme_override_constants/separation = 10
[node name="LeftBox" type="VBoxContainer" parent="Multiplayer/MarginContainer/TabContainer/Join/MarginContainer/HBoxContainer"]
[node name="LeftBox" type="Control" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Join" type="Button" parent="Multiplayer/MarginContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox"]
[node name="Top" type="VBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox"]
layout_mode = 1
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 101.0
grow_horizontal = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Join" type="Button" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox/Top"]
layout_mode = 2
text = "Join"
[node name="Spacer" type="MarginContainer" parent="Multiplayer/MarginContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox"]
layout_mode = 2
size_flags_vertical = 3
[node name="Bottom" type="VBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox"]
layout_mode = 1
anchors_preset = 12
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -31.0
grow_horizontal = 2
grow_vertical = 0
size_flags_horizontal = 0
[node name="Menu" type="Button" parent="Multiplayer/MarginContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox"]
[node name="MainMenu" type="Button" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
text = "Menu
size_flags_horizontal = 3
text = "Main Menu
"
[node name="Quit" type="Button" parent="Multiplayer/MarginContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox"]
[node name="Quit" type="Button" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Quit"
[node name="RightBox" type="VBoxContainer" parent="Multiplayer/MarginContainer/TabContainer/Join/MarginContainer/HBoxContainer"]
[node name="RightBox" type="VBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="JoinAddress" type="LineEdit" parent="Multiplayer/MarginContainer/TabContainer/Join/MarginContainer/HBoxContainer/RightBox"]
[node name="JoinAddress" type="LineEdit" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer/HBoxContainer/RightBox"]
unique_name_in_owner = true
layout_mode = 2
placeholder_text = "localhost"
[node name="Host" type="TabBar" parent="Multiplayer/MarginContainer/TabContainer"]
[node name="Host" type="TabBar" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer"]
visible = false
layout_mode = 2
select_with_rmb = true
[node name="MarginContainer" type="MarginContainer" parent="Multiplayer/MarginContainer/TabContainer/Host"]
[node name="MarginContainer" type="MarginContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@ -252,37 +353,55 @@ theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="HBoxContainer" type="HBoxContainer" parent="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer"]
[node name="HBoxContainer" type="HBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer"]
layout_mode = 2
theme_override_constants/separation = 10
[node name="LeftBox" type="VBoxContainer" parent="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer"]
[node name="LeftBox" type="Control" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Host" type="Button" parent="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox"]
[node name="Top" type="VBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox"]
layout_mode = 1
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 101.0
grow_horizontal = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Host" type="Button" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox/Top"]
layout_mode = 2
text = "Host"
[node name="Spacer" type="MarginContainer" parent="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox"]
layout_mode = 2
size_flags_vertical = 3
[node name="Bottom" type="VBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox"]
layout_mode = 1
anchors_preset = 12
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -66.0
grow_horizontal = 2
grow_vertical = 0
size_flags_horizontal = 0
[node name="Menu" type="Button" parent="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox"]
[node name="MainMenu" type="Button" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
text = "Menu
size_flags_horizontal = 3
text = "Main Menu
"
[node name="Quit" type="Button" parent="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox"]
[node name="Quit" type="Button" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Quit"
[node name="RightBox" type="VBoxContainer" parent="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer"]
[node name="RightBox" type="VBoxContainer" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="ServerPort" type="LineEdit" parent="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer/RightBox"]
[node name="ServerPort" type="LineEdit" parent="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer/RightBox"]
unique_name_in_owner = true
layout_mode = 2
placeholder_text = "9000"
@ -306,51 +425,250 @@ grow_horizontal = 2
grow_vertical = 2
text = "CONNECTING..."
[node name="Main" type="PanelContainer" parent="."]
anchors_preset = 9
[node name="Settings" type="PanelContainer" parent="." node_paths=PackedStringArray("tab_container")]
visible = false
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_right = 313.0
grow_horizontal = 2
grow_vertical = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_ky5sv")
script = SubResource("GDScript_gbnwv")
tab_container = NodePath("MarginContainer/VBoxContainer/TabContainer")
[node name="MarginContainer" type="MarginContainer" parent="Settings"]
layout_mode = 2
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="VBoxContainer" type="VBoxContainer" parent="Settings/MarginContainer"]
layout_mode = 2
[node name="TabContainer" type="TabContainer" parent="Settings/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/side_margin = 0
[node name="Gameplay" type="TabBar" parent="Settings/MarginContainer/VBoxContainer/TabContainer"]
layout_mode = 2
theme_override_constants/h_separation = 0
[node name="MarginContainer" type="MarginContainer" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="GridContainer" type="GridContainer" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer"]
layout_mode = 2
columns = 2
[node name="FOVLabel" type="Label" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/GridContainer"]
layout_mode = 2
text = "Field of view"
[node name="FOVSpinBox" type="SpinBox" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
min_value = 60.0
max_value = 120.0
value = 60.0
[node name="SensitivityLabel" type="Label" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/GridContainer"]
layout_mode = 2
text = "Mouse sensitivity"
[node name="SensitivitySpinBox" type="SpinBox" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
max_value = 1.0
step = 0.01
value = 1.0
[node name="InvertedYLabel" type="Label" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/GridContainer"]
layout_mode = 2
text = "Inverted Y"
[node name="InvertedYCheckbox" type="CheckBox" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="Buttons" type="HBoxContainer" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer"]
layout_mode = 2
size_flags_vertical = 8
[node name="Bottom" type="VBoxContainer" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/Buttons"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.2
[node name="Save" type="Button" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/Buttons/Bottom"]
layout_mode = 2
text = "Save
"
[node name="MainMenu" type="Button" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/Buttons/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Main Menu
"
[node name="Quit" type="Button" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/Buttons/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Quit"
[node name="Spacer" type="Control" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/Buttons"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.8
[node name="Graphics" type="TabBar" parent="Settings/MarginContainer/VBoxContainer/TabContainer"]
visible = false
layout_mode = 2
select_with_rmb = true
[node name="MarginContainer" type="MarginContainer" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="GridContainer" type="GridContainer" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer"]
layout_mode = 2
columns = 2
[node name="WindowModeLabel" type="Label" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer/GridContainer"]
layout_mode = 2
text = "Window mode"
[node name="WindowModeOptionButton" type="OptionButton" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
item_count = 2
selected = 0
popup/item_0/text = "Windowed"
popup/item_0/id = 0
popup/item_1/text = "Fullscreen"
popup/item_1/id = 1
[node name="Buttons" type="HBoxContainer" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer"]
layout_mode = 2
size_flags_vertical = 8
[node name="Bottom" type="VBoxContainer" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer/Buttons"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.2
[node name="Save" type="Button" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer/Buttons/Bottom"]
layout_mode = 2
text = "Save
"
[node name="MainMenu" type="Button" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer/Buttons/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Main Menu
"
[node name="Quit" type="Button" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer/Buttons/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Quit"
[node name="Spacer" type="Control" parent="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer/Buttons"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.8
[node name="Main" type="PanelContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 0
theme_override_styles/panel = SubResource("StyleBoxFlat_snh7i")
script = SubResource("GDScript_yqbr7")
theme_override_styles/panel = SubResource("StyleBoxFlat_c4ymk")
[node name="MarginContainer" type="MarginContainer" parent="Main"]
[node name="HBoxContainer" type="HBoxContainer" parent="Main"]
layout_mode = 2
[node name="PanelContainer" type="PanelContainer" parent="Main/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.3
theme_override_styles/panel = SubResource("StyleBoxFlat_dq4me")
[node name="MarginContainer" type="MarginContainer" parent="Main/HBoxContainer/PanelContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/margin_left = 20
theme_override_constants/margin_right = 20
[node name="VBoxContainer" type="VBoxContainer" parent="Main/MarginContainer"]
[node name="VBoxContainer" type="VBoxContainer" parent="Main/HBoxContainer/PanelContainer/MarginContainer"]
layout_mode = 2
theme_override_constants/separation = 10
alignment = 1
[node name="Demo" type="Button" parent="Main/MarginContainer/VBoxContainer"]
[node name="Demo" type="Button" parent="Main/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 32
text = "Demo"
[node name="Multiplayer" type="Button" parent="Main/MarginContainer/VBoxContainer"]
[node name="Multiplayer" type="Button" parent="Main/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 32
text = "Multiplayer"
[node name="Quit" type="Button" parent="Main/MarginContainer/VBoxContainer"]
[node name="Settings" type="Button" parent="Main/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 32
text = "Settings"
[node name="Quit" type="Button" parent="Main/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 32
text = "Quit"
[node name="Control" type="Control" parent="Main/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.8
[connection signal="join_server" from="Multiplayer" to="." method="_on_multiplayer_join_server"]
[connection signal="start_server" from="Multiplayer" to="." method="_on_multiplayer_start_server"]
[connection signal="pressed" from="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox/Menu" to="Multiplayer" method="_on_menu_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox/Quit" to="Multiplayer" method="_on_quit_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox/Join" to="Multiplayer" method="_on_join_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox/Menu" to="Multiplayer" method="_on_menu_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox/Quit" to="Multiplayer" method="_on_quit_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox/Host" to="Multiplayer" method="_on_host_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox/Menu" to="Multiplayer" method="_on_menu_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox/Quit" to="Multiplayer" method="_on_quit_pressed"]
[connection signal="text_changed" from="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer/RightBox/ServerPort" to="Multiplayer/MarginContainer/TabContainer/Host/MarginContainer/HBoxContainer/RightBox/ServerPort" method="_on_text_changed"]
[connection signal="pressed" from="Main/MarginContainer/VBoxContainer/Demo" to="." method="_on_demo_pressed"]
[connection signal="pressed" from="Main/MarginContainer/VBoxContainer/Multiplayer" to="Main" method="_on_multiplayer_pressed"]
[connection signal="pressed" from="Main/MarginContainer/VBoxContainer/Quit" to="Main" method="_on_quit_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox/Top/Save" to="Multiplayer" method="_on_save_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox/Bottom/MainMenu" to="." method="_on_main_menu_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Profile/MarginContainer/HBoxContainer/LeftBox/Bottom/Quit" to="." method="_on_quit_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox/Top/Join" to="Multiplayer" method="_on_join_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox/Bottom/MainMenu" to="." method="_on_main_menu_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Join/MarginContainer/HBoxContainer/LeftBox/Bottom/Quit" to="." method="_on_quit_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox/Top/Host" to="Multiplayer" method="_on_host_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox/Bottom/MainMenu" to="." method="_on_main_menu_pressed"]
[connection signal="pressed" from="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer/LeftBox/Bottom/Quit" to="." method="_on_quit_pressed"]
[connection signal="text_changed" from="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer/RightBox/ServerPort" to="Multiplayer/MarginContainer/VBoxContainer/TabContainer/Host/MarginContainer/HBoxContainer/RightBox/ServerPort" method="_on_text_changed"]
[connection signal="pressed" from="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/Buttons/Bottom/Save" to="Settings" method="_on_save_pressed"]
[connection signal="pressed" from="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/Buttons/Bottom/MainMenu" to="." method="_on_main_menu_pressed"]
[connection signal="pressed" from="Settings/MarginContainer/VBoxContainer/TabContainer/Gameplay/MarginContainer/Buttons/Bottom/Quit" to="." method="_on_quit_pressed"]
[connection signal="pressed" from="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer/Buttons/Bottom/Save" to="Settings" method="_on_save_pressed"]
[connection signal="pressed" from="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer/Buttons/Bottom/MainMenu" to="." method="_on_main_menu_pressed"]
[connection signal="pressed" from="Settings/MarginContainer/VBoxContainer/TabContainer/Graphics/MarginContainer/Buttons/Bottom/Quit" to="." method="_on_quit_pressed"]
[connection signal="pressed" from="Main/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer/Demo" to="." method="_on_demo_pressed"]
[connection signal="pressed" from="Main/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer/Multiplayer" to="." method="_on_multiplayer_pressed"]
[connection signal="pressed" from="Main/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer/Settings" to="." method="_on_settings_pressed"]
[connection signal="pressed" from="Main/HBoxContainer/PanelContainer/MarginContainer/VBoxContainer/Quit" to="." method="_on_quit_pressed"]

View file

@ -2,7 +2,7 @@
[ext_resource type="PackedScene" uid="uid://boviiugcnfyrj" path="res://modes/demo.tscn" id="1_50a80"]
[ext_resource type="PackedScene" uid="uid://bjctlqvs33nqy" path="res://interfaces/menus/boot/boot.tscn" id="1_acy5o"]
[ext_resource type="PackedScene" uid="uid://c7ae4jw5d8mue" path="res://modes/multiplayer.tscn" id="2_g8xeb"]
[ext_resource type="PackedScene" uid="uid://bvwxfgygm2xb8" path="res://modes/multiplayer.tscn" id="2_g8xeb"]
[sub_resource type="GDScript" id="GDScript_e61dq"]
script/source = "class_name Game extends Node3D
@ -13,43 +13,50 @@ script/source = "class_name Game extends Node3D
var mode : Node
func _ready():
func _ready() -> void:
$BootMenu.start_demo.connect(_start_demo)
$BootMenu/Multiplayer.start_server.connect(_start_server)
$BootMenu/Multiplayer.join_server.connect(_join_server)
func _start_demo():
if mode: mode.queue_free()
mode = SINGLEPLAYER.instantiate() # demo scene
add_child(mode)
func _start_demo() -> void:
_set_game_mode(SINGLEPLAYER.instantiate())
$BootMenu.hide()
func _start_server(port):
if mode: mode.queue_free()
mode = MULTIPLAYER.instantiate() # server scene
add_child(mode)
mode.start_server(port)
func _start_server(port : int, nickname : String) -> void:
_set_game_mode(MULTIPLAYER.instantiate())
mode.start_server(port, nickname)
$BootMenu.hide()
func _join_server(host, port):
if mode: mode.queue_free()
mode = MULTIPLAYER.instantiate() # client scene
add_child(mode)
func _join_server(host : String, port : int, nickname : String) -> void:
_set_game_mode(MULTIPLAYER.instantiate())
mode.connected_to_server.connect($BootMenu/Multiplayer._on_connected_to_server)
mode.connection_failed.connect($BootMenu/Multiplayer._on_connection_failed)
mode.join_server(host, port)
mode.join_server(host, port, nickname)
func _unhandled_input(event):
# exit the program
func _input(event : InputEvent) -> void:
if event.is_action_pressed(\"exit\"):
get_tree().quit()
_set_game_mode(null)
multiplayer.multiplayer_peer.close()
$BootMenu.show()
func _set_game_mode(new_mode : Node) -> void:
if mode != null:
remove_child(mode)
mode.queue_free()
mode = new_mode
if mode == null:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
else:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
add_child(mode)
func _unhandled_input(event : InputEvent) -> void:
# switch window mode
if event.is_action_pressed(\"window_mode\"):
if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
else:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
# switch mouse mode
if OS.is_debug_build() and Input.is_action_just_pressed(\"toggle_mouse_capture\"):
if Input.mouse_mode == Input.MOUSE_MODE_VISIBLE:

22
maps/desert/desert.tscn Normal file
View file

@ -0,0 +1,22 @@
[gd_scene load_steps=5 format=3 uid="uid://btlkog4b87p4x"]
[ext_resource type="Terrain3DStorage" uid="uid://wgmg245njt8e" path="res://maps/desert/resources/storage.res" id="1_7nbyj"]
[ext_resource type="Environment" uid="uid://nw62ce5cglvs" path="res://environments/desert.tres" id="1_e1o3u"]
[ext_resource type="Terrain3DMaterial" uid="uid://c3isipd4wqxpk" path="res://maps/desert/resources/material.tres" id="2_psdr0"]
[ext_resource type="Terrain3DTextureList" uid="uid://d1j24k8sq8qpj" path="res://maps/desert/resources/textures.tres" id="3_aowug"]
[node name="Desert" type="Node3D"]
[node name="Sunlight" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.5, 0.55667, -0.663414, 0, 0.766044, 0.642788, 0.866025, -0.321394, 0.383022, -1613.38, 2730.74, 446.64)
shadow_enabled = true
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = ExtResource("1_e1o3u")
[node name="Terrain3D" type="Terrain3D" parent="."]
storage = ExtResource("1_7nbyj")
material = ExtResource("2_psdr0")
texture_list = ExtResource("3_aowug")
collision_layer = 2147483648
collision_mask = 2147483648

View file

@ -0,0 +1,58 @@
[gd_resource type="Terrain3DMaterial" load_steps=4 format=3 uid="uid://c3isipd4wqxpk"]
[sub_resource type="Gradient" id="Gradient_rr0fn"]
offsets = PackedFloat32Array(0.2, 1)
colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1)
[sub_resource type="FastNoiseLite" id="FastNoiseLite_e3unr"]
noise_type = 2
frequency = 0.03
cellular_jitter = 3.0
cellular_return_type = 0
domain_warp_enabled = true
domain_warp_type = 1
domain_warp_amplitude = 50.0
domain_warp_fractal_type = 2
domain_warp_fractal_lacunarity = 1.5
domain_warp_fractal_gain = 1.0
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_esvkc"]
seamless = true
color_ramp = SubResource("Gradient_rr0fn")
noise = SubResource("FastNoiseLite_e3unr")
[resource]
_shader_parameters = {
"_mouse_layer": 2147483648,
"auto_base_texture": 0,
"auto_height_reduction": 0.085,
"auto_overlay_texture": 1,
"auto_slope": 1.0,
"blend_sharpness": 0.85,
"dual_scale_far": 350.0,
"dual_scale_near": 0.0,
"dual_scale_reduction": 1.0,
"dual_scale_texture": 1,
"height_blending": true,
"macro_variation1": Color(1, 1, 1, 1),
"macro_variation2": Color(0.968627, 0.929412, 0.905882, 1),
"noise1_angle": 0.0,
"noise1_offset": Vector2(0.5, 0.5),
"noise1_scale": 0.4,
"noise2_scale": 0.5,
"noise3_scale": 0.225,
"noise_texture": SubResource("NoiseTexture2D_esvkc"),
"tri_scale_reduction": 0.075,
"world_noise_blend_far": 1.0,
"world_noise_blend_near": 0.75,
"world_noise_height": 40.0,
"world_noise_lod_distance": 500.0,
"world_noise_max_octaves": 6,
"world_noise_min_octaves": 5,
"world_noise_offset": Vector3(0, 0, 0),
"world_noise_scale": 7.5
}
world_background = 2
texture_filtering = 1
auto_shader = true
dual_scaling = true

Binary file not shown.

View file

@ -0,0 +1,24 @@
[gd_resource type="Terrain3DTextureList" load_steps=7 format=3 uid="uid://d1j24k8sq8qpj"]
[ext_resource type="Texture2D" uid="uid://cngjywcua4vv8" path="res://maps/desert/textures/ground054_alb_ht.png" id="1_rh7of"]
[ext_resource type="Texture2D" uid="uid://cbk7l6hs0yrcc" path="res://maps/desert/textures/ground054_nrm_rgh.png" id="2_xt1tc"]
[ext_resource type="Texture2D" uid="uid://dwk8islw7ebab" path="res://maps/desert/textures/rock029_alb_ht.png" id="3_elstl"]
[ext_resource type="Texture2D" uid="uid://lgfhdcsb2ryx" path="res://maps/desert/textures/rock029_nrm_rgh.png" id="4_6h5db"]
[sub_resource type="Terrain3DTexture" id="Terrain3DTexture_wue72"]
name = "Rock"
albedo_color = Color(0.921569, 0.890196, 0.933333, 1)
albedo_texture = ExtResource("3_elstl")
normal_texture = ExtResource("4_6h5db")
uv_scale = 0.05
[sub_resource type="Terrain3DTexture" id="Terrain3DTexture_dkh73"]
name = "Sand"
texture_id = 1
albedo_color = Color(0.792157, 0.72549, 0.694118, 1)
albedo_texture = ExtResource("1_rh7of")
normal_texture = ExtResource("2_xt1tc")
uv_scale = 0.15
[resource]
textures = Array[Terrain3DTexture]([SubResource("Terrain3DTexture_wue72"), SubResource("Terrain3DTexture_dkh73")])

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

View file

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cngjywcua4vv8"
path.bptc="res://.godot/imported/ground054_alb_ht.png-c1e6560e1b831d8f5772408a395cdf1b.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://maps/desert/textures/ground054_alb_ht.png"
dest_files=["res://.godot/imported/ground054_alb_ht.png-c1e6560e1b831d8f5772408a395cdf1b.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=true
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=2
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=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

View file

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cbk7l6hs0yrcc"
path.bptc="res://.godot/imported/ground054_nrm_rgh.png-70f079690ff8219938f9079109780fc1.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://maps/desert/textures/ground054_nrm_rgh.png"
dest_files=["res://.godot/imported/ground054_nrm_rgh.png-70f079690ff8219938f9079109780fc1.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=true
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=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

View file

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dwk8islw7ebab"
path.bptc="res://.godot/imported/rock029_alb_ht.png-a5e0a4f98f29bdeb159f1e749d7737b1.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://maps/desert/textures/rock029_alb_ht.png"
dest_files=["res://.godot/imported/rock029_alb_ht.png-a5e0a4f98f29bdeb159f1e749d7737b1.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=true
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=2
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=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

View file

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://lgfhdcsb2ryx"
path.bptc="res://.godot/imported/rock029_nrm_rgh.png-21134283f9adb86493d1d72a4f30e20a.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://maps/desert/textures/rock029_nrm_rgh.png"
dest_files=["res://.godot/imported/rock029_nrm_rgh.png-21134283f9adb86493d1d72a4f30e20a.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=true
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=1

View file

@ -1,47 +1,64 @@
[gd_scene load_steps=7 format=3 uid="uid://chbno00ugl6te"]
[gd_scene load_steps=8 format=3 uid="uid://chbno00ugl6te"]
[ext_resource type="Environment" uid="uid://d2ahijqqspw5f" path="res://environments/default.tres" id="1_3nr12"]
[ext_resource type="Script" path="res://maps/map.gd" id="1_6ysiv"]
[ext_resource type="Terrain3DStorage" uid="uid://dyon4xda4k40d" path="res://maps/genesis/resources/storage.res" id="1_a88qe"]
[ext_resource type="Terrain3DMaterial" uid="uid://bd4lr5sxu8xu" path="res://maps/genesis/resources/material.res" id="2_o2y3d"]
[ext_resource type="Terrain3DTextureList" uid="uid://dnqa8kxgm3xuw" path="res://maps/genesis/resources/textures.res" id="3_1cww7"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_gi1uj"]
sky_top_color = Color(0.384314, 0.454902, 0.54902, 1)
sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)
ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)
[sub_resource type="CapsuleMesh" id="CapsuleMesh_k2vhq"]
[sub_resource type="Sky" id="Sky_t5qlv"]
sky_material = SubResource("ProceduralSkyMaterial_gi1uj")
process_mode = 1
radiance_size = 4
[sub_resource type="CapsuleMesh" id="CapsuleMesh_5ytwx"]
[sub_resource type="Environment" id="Environment_6whfw"]
background_mode = 2
sky = SubResource("Sky_t5qlv")
tonemap_mode = 3
glow_normalized = true
glow_intensity = 2.0
glow_strength = 0.96
fog_light_color = Color(0.721569, 0.494118, 0.294118, 1)
fog_sun_scatter = 0.27
fog_density = 0.0001
fog_aerial_perspective = 0.602
fog_sky_affect = 0.518
volumetric_fog_density = 0.003
volumetric_fog_albedo = Color(1, 0.380392, 0.380392, 1)
volumetric_fog_emission_energy = 192.23
volumetric_fog_length = 551.32
volumetric_fog_ambient_inject = 0.37
[node name="Map" type="Node3D"]
[node name="Sunlight" type="DirectionalLight3D" parent="."]
transform = Transform3D(-0.866024, -0.433016, 0.250001, 0, 0.499998, 0.866026, -0.500003, 0.749999, -0.43301, 0, 0, 0)
shadow_enabled = true
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_6whfw")
[node name="Map" type="Node" node_paths=PackedStringArray("_flagstand", "_player_spawns_root")]
script = ExtResource("1_6ysiv")
_flagstand = NodePath("Terrain3D/FlagPillar/CSGCylinder3D/FlagStand")
_player_spawns_root = NodePath("Terrain3D/PlayerSpawns")
[node name="Terrain3D" type="Terrain3D" parent="."]
storage = ExtResource("1_a88qe")
material = ExtResource("2_o2y3d")
texture_list = ExtResource("3_1cww7")
collision_layer = 2147483648
collision_mask = 2147483648
mesh_size = 24
[node name="PlayerSpawns" type="Node" parent="Terrain3D"]
[node name="Spawn1" type="Marker3D" parent="Terrain3D/PlayerSpawns"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 95.5378, -32.735)
visible = false
[node name="MeshInstance3D" type="MeshInstance3D" parent="Terrain3D/PlayerSpawns/Spawn1"]
mesh = SubResource("CapsuleMesh_k2vhq")
[node name="Spawn2" type="Marker3D" parent="Terrain3D/PlayerSpawns"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9, 98.3078, 18.061)
visible = false
[node name="MeshInstance3D" type="MeshInstance3D" parent="Terrain3D/PlayerSpawns/Spawn2"]
mesh = SubResource("CapsuleMesh_5ytwx")
[node name="FlagPillar" type="CSGCombiner3D" parent="Terrain3D"]
transform = Transform3D(1, 0, 0, 0, 2.83726, 0, 0, 0, 1, 0, 94.6411, 0)
use_collision = true
collision_layer = 2147483648
collision_mask = 0
[node name="CSGBox3D" type="CSGBox3D" parent="Terrain3D/FlagPillar"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.9311, 0)
size = Vector3(0.5, 6, 0.5)
[node name="CSGCylinder3D" type="CSGCylinder3D" parent="Terrain3D/FlagPillar"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.42967, 0)
height = 5.0
[node name="FlagStand" type="Marker3D" parent="Terrain3D/FlagPillar/CSGCylinder3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3.42315, 0)
[node name="Sunlight" type="DirectionalLight3D" parent="."]
transform = Transform3D(-0.866024, -0.433016, 0.25, 0, 0.499998, 0.866027, -0.500003, 0.75, -0.43301, 0, 100, 0)
shadow_enabled = true
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = ExtResource("1_3nr12")

16
maps/map.gd Normal file
View file

@ -0,0 +1,16 @@
class_name Map extends Node
@export var _flagstand : Node3D
@export var _player_spawns_root : Node
var _rng : RandomNumberGenerator = RandomNumberGenerator.new()
func get_flagstand() -> Node3D:
return _flagstand
func get_player_spawn() -> Node3D:
var spawn_count : int = _player_spawns_root.get_child_count()
var spawn_index : int = _rng.randi_range(0, spawn_count - 1)
var spawn_selected : Node3D = _player_spawns_root.get_child(spawn_index)
return spawn_selected

View file

@ -1,21 +1,22 @@
[gd_scene load_steps=6 format=3 uid="uid://boviiugcnfyrj"]
[gd_scene load_steps=7 format=3 uid="uid://boviiugcnfyrj"]
[ext_resource type="PackedScene" uid="uid://cbhx1xme0sb7k" path="res://entities/player/player.tscn" id="2_6wbjq"]
[ext_resource type="PackedScene" uid="uid://chbno00ugl6te" path="res://maps/genesis/genesis.tscn" id="2_lsep7"]
[ext_resource type="PackedScene" uid="uid://dpnu1lvfncx6q" path="res://entities/target_dummy/target_dummy.tscn" id="3_fkq5v"]
[ext_resource type="PackedScene" uid="uid://c88l3h0ph00c7" path="res://entities/flag/flag.tscn" id="4_1j2pw"]
[ext_resource type="PackedScene" uid="uid://btlkog4b87p4x" path="res://maps/desert/desert.tscn" id="4_dogmv"]
[sub_resource type="GDScript" id="GDScript_iv0l6"]
script/source = "extends Node
@onready var player_node : Player = $Player
@onready var target_dummy = $TargetDummy
@onready var target_dummy : RigidBody3D = $TargetDummy
@onready var player_respawn_location = player_node.position
@onready var player_respawn_location : Vector3 = player_node.position
func _ready():
func _ready() -> void:
player_node.died.connect(respawn_player)
func respawn_player(player):
func respawn_player(player : Player) -> void:
player.respawn(player_respawn_location)
"
@ -27,11 +28,15 @@ absorbent = true
[node name="Demo" type="Node"]
script = SubResource("GDScript_iv0l6")
[node name="Map" parent="." instance=ExtResource("2_lsep7")]
[node name="Player" parent="." instance=ExtResource("2_6wbjq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 92.7007, 0)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 589.786, 210.119, 853.632)
physics_material_override = SubResource("PhysicsMaterial_c5jqv")
[node name="TargetDummy" parent="." instance=ExtResource("3_fkq5v")]
transform = Transform3D(-0.995746, 0, -0.0921446, 0, 1, 0, 0.0921446, 0, -0.995746, 15.757, 95.1745, 0)
transform = Transform3D(-0.789567, 0, 0.613666, 0, 1, 0, -0.613666, 0, -0.789567, 418.132, 195.484, 802.593)
[node name="Flag" parent="." instance=ExtResource("4_1j2pw")]
transform = Transform3D(0.405891, 0, -0.913922, 0, 1, 0, 0.913922, 0, 0.405891, 890.762, 179.326, 1005.33)
[node name="Desert" parent="." instance=ExtResource("4_dogmv")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0566101, 1456.67, 0.597462)

View file

@ -1,7 +1,9 @@
[gd_scene load_steps=4 format=3 uid="uid://bvwxfgygm2xb8"]
[gd_scene load_steps=6 format=3 uid="uid://bvwxfgygm2xb8"]
[ext_resource type="PackedScene" uid="uid://chbno00ugl6te" path="res://maps/genesis/genesis.tscn" id="1_nulvv"]
[ext_resource type="PackedScene" uid="uid://cbhx1xme0sb7k" path="res://entities/player/player.tscn" id="2_og1vb"]
[ext_resource type="PackedScene" uid="uid://c88l3h0ph00c7" path="res://entities/flag/flag.tscn" id="3_h0rie"]
[ext_resource type="Script" path="res://modes/scoreboard.gd" id="4_n0mhp"]
[sub_resource type="GDScript" id="GDScript_1qrbp"]
script/source = "class_name Multiplayer extends Node
@ -9,68 +11,114 @@ script/source = "class_name Multiplayer extends Node
@export_category(\"Parameters\")
@export var MAP : PackedScene
@export var PLAYER : PackedScene
@export var FLAG : PackedScene
@export var MAX_CLIENTS : int = 24
@export var RESPAWN_TIME : float = 3.0
@onready var map = $Map
@onready var players = $Players
@onready var players : Node = $Players
@onready var objectives : Node = $Objectives
@onready var map : Node = $Map
@onready var scoreboard : Scoreboard = $Scoreboard
@onready var scoreboard_ui : Node = $ScoreboardUI
var _map_manager : Map
signal connected_to_server
signal connection_failed
func load_map(scene : PackedScene):
map.add_child(scene.instantiate())
func start_server(port):
var peer = ENetMultiplayerPeer.new()
func start_server(port : int, nickname : String) -> void:
var peer : ENetMultiplayerPeer = ENetMultiplayerPeer.new()
peer.create_server(port, MAX_CLIENTS)
multiplayer.multiplayer_peer = peer
load_map.call_deferred(MAP)
_load_map.call_deferred(MAP, nickname)
multiplayer.peer_connected.connect(add_player)
multiplayer.peer_disconnected.connect(remove_player)
for id in multiplayer.get_peers():
add_player(id)
if DisplayServer.get_name() != \"headless\":
add_player(1)
func join_server(host, port):
var peer = ENetMultiplayerPeer.new()
func join_server(host : String, port : int, nickname : String) -> void:
var peer : ENetMultiplayerPeer = ENetMultiplayerPeer.new()
peer.create_client(host, port)
multiplayer.connected_to_server.connect(_on_connected_to_server)
multiplayer.connected_to_server.connect(_on_connected_to_server.bind(nickname))
multiplayer.connection_failed.connect(_on_connection_failed)
multiplayer.multiplayer_peer = peer
func _on_connected_to_server():
func _on_connected_to_server(nickname : String) -> void:
connected_to_server.emit()
scoreboard.request_scoreboard_from_authority.rpc()
_join_match.rpc(nickname)
func _on_connection_failed():
func _on_connection_failed() -> void:
connection_failed.emit()
func respawn_player(player):
player.respawn(Vector3(0.0, 150.0, 0.0))
func _on_player_died(player : Player, killer_id : int) -> void:
if player.player_id != killer_id:
var node_name : String = str(killer_id)
if players.has_node(node_name):
var killer : Player = players.get_node(node_name)
scoreboard.increment_kill_count(killer)
scoreboard.add_score_to_player(killer, 10)
scoreboard.broadcast_player_score_update(killer)
await get_tree().create_timer(RESPAWN_TIME).timeout
respawn_player(player)
func add_player(peer_id : int):
func respawn_player(player : Player) -> void:
var spawn_location : Vector3 = _map_manager.get_player_spawn().position
player.respawn(spawn_location)
func add_player(peer_id : int, nickname : String) -> void:
var player : Player = PLAYER.instantiate()
player.name = str(peer_id)
player.player_id = peer_id
player.position = Vector3(0.0, 150.0, 0.0)
player.nickname = nickname
player.global_position = _map_manager.get_player_spawn().position
players.add_child(player)
player.died.connect(respawn_player)
player.died.connect(_on_player_died)
scoreboard.add_entry(player)
print(\"Peer `%s` connected\" % player.name)
func remove_player(peer_id : int):
var node_name = str(peer_id)
func remove_player(peer_id : int) -> void:
var node_name : String = str(peer_id)
if players.has_node(node_name):
players.get_node(node_name).queue_free()
var player : Player = players.get_node(node_name)
scoreboard.remove_entry(player)
player.die(-1)
player.queue_free()
print(\"Peer `%s` disconnected\" % node_name)
func _exit_tree():
if multiplayer.is_server():
multiplayer.peer_connected.disconnect(add_player)
func _load_map(scene : PackedScene, nickname : String) -> void:
var map_scene : Node = scene.instantiate()
_map_manager = map_scene
map_scene.ready.connect(_add_flag)
if DisplayServer.get_name() != \"headless\":
add_player(1, nickname)
map.add_child(map_scene)
func _add_flag() -> void:
var flag : Flag = FLAG.instantiate()
flag.global_position = _map_manager.get_flagstand().global_position
objectives.add_child(flag)
func _unhandled_input(event : InputEvent) -> void:
if event.is_action_pressed(\"scoreboard\"):
var entries : Array = scoreboard.get_entries()
for entry : Scoreboard.ScoreboardEntry in entries:
var entry_label : Label = Label.new()
entry_label.text = \"%s | kills: %s | score: %s\" % [entry.nickname, entry.kills, entry.score]
%Scores.add_child(entry_label)
scoreboard_ui.show()
elif event.is_action_released(\"scoreboard\"):
scoreboard_ui.hide()
for score_label in %Scores.get_children():
score_label.queue_free()
@rpc(\"any_peer\")
func _join_match(nickname : String) -> void:
if is_multiplayer_authority():
add_player(multiplayer.get_remote_sender_id(), nickname)
func _exit_tree() -> void:
if is_multiplayer_authority():
multiplayer.peer_disconnected.disconnect(remove_player)
"
@ -78,6 +126,7 @@ func _exit_tree():
script = SubResource("GDScript_1qrbp")
MAP = ExtResource("1_nulvv")
PLAYER = ExtResource("2_og1vb")
FLAG = ExtResource("3_h0rie")
[node name="Map" type="Node" parent="."]
@ -91,3 +140,42 @@ spawn_limit = 1
[node name="PlayersSpawner" type="MultiplayerSpawner" parent="."]
_spawnable_scenes = PackedStringArray("res://entities/player/player.tscn")
spawn_path = NodePath("../Players")
[node name="Objectives" type="Node" parent="."]
[node name="ObjectivesSpawner" type="MultiplayerSpawner" parent="."]
_spawnable_scenes = PackedStringArray("res://entities/flag/flag.tscn")
spawn_path = NodePath("../Objectives")
[node name="Scoreboard" type="Node" parent="."]
script = ExtResource("4_n0mhp")
[node name="ScoreboardUI" type="Control" parent="."]
visible = false
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="PanelContainer" type="PanelContainer" parent="ScoreboardUI"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="ScoreboardUI/PanelContainer"]
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 0
[node name="Label" type="Label" parent="ScoreboardUI/PanelContainer/VBoxContainer"]
layout_mode = 2
text = "Scoreboard"
[node name="Scores" type="VBoxContainer" parent="ScoreboardUI/PanelContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2

77
modes/scoreboard.gd Normal file
View file

@ -0,0 +1,77 @@
# 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 Scoreboard extends Node
@export var _entries : Dictionary = {}
class ScoreboardEntry:
var nickname : String
var kills : int
var score : int
func add_entry(player : Player) -> void:
var new_entry : ScoreboardEntry = ScoreboardEntry.new()
new_entry.nickname = player.nickname
new_entry.kills = 0
new_entry.score = 0
_entries[player.player_id] = new_entry
_send_scoreboard_entry.rpc(player.player_id, new_entry.nickname, new_entry.kills, new_entry.score)
func remove_entry(player : Player) -> void:
_entries.erase(player.player_id)
_broadcast_player_removed(player)
func add_score_to_player(player : Player, amount : int) -> void:
_entries[player.player_id].score += amount
func increment_kill_count(player : Player) -> void:
_entries[player.player_id].kills += 1
func get_entries() -> Array:
return _entries.values()
@rpc("any_peer", "call_remote", "reliable")
func request_scoreboard_from_authority() -> void:
if is_multiplayer_authority():
var recipient_id : int = multiplayer.get_remote_sender_id()
_clear_scoreboard.rpc_id(recipient_id)
for entry_key : int in _entries:
var entry : ScoreboardEntry = _entries[entry_key]
_send_scoreboard_entry.rpc_id(recipient_id, entry_key, entry.nickname, entry.kills, entry.score)
@rpc("authority", "reliable")
func _clear_scoreboard() -> void:
_entries.clear()
@rpc("authority", "reliable")
func _send_scoreboard_entry(player_id : int, nickname : String, kills : int, score : int) -> void:
var new_entry : ScoreboardEntry = ScoreboardEntry.new()
new_entry.nickname = nickname
new_entry.kills = kills
new_entry.score = score
_entries[player_id] = new_entry
@rpc("authority", "reliable")
func _remove_scoreboard_entry(player_id : int) -> void:
_entries.erase(player_id)
func broadcast_player_score_update(player : Player) -> void:
var player_id : int = player.player_id
var player_score_entry : ScoreboardEntry = _entries[player_id]
_send_scoreboard_entry.rpc(player_id, player_score_entry.nickname, player_score_entry.kills, player_score_entry.score)
func _broadcast_player_removed(player : Player) -> void:
var player_id : int = player.player_id
_remove_scoreboard_entry.rpc(player_id)

View file

@ -15,13 +15,43 @@ run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.2", "Forward Plus")
config/icon="res://icon.svg"
[autoload]
GlobalSettings="*res://systems/settings.gd"
[debug]
gdscript/warnings/unassigned_variable=2
gdscript/warnings/unassigned_variable_op_assign=2
gdscript/warnings/unused_variable=2
gdscript/warnings/unused_local_constant=2
gdscript/warnings/unused_private_class_variable=2
gdscript/warnings/unused_parameter=2
gdscript/warnings/unused_signal=2
gdscript/warnings/shadowed_variable=2
gdscript/warnings/shadowed_variable_base_class=2
gdscript/warnings/shadowed_global_identifier=2
gdscript/warnings/unreachable_code=2
gdscript/warnings/unreachable_pattern=2
gdscript/warnings/standalone_expression=2
gdscript/warnings/standalone_ternary=2
gdscript/warnings/incompatible_ternary=2
gdscript/warnings/property_used_as_function=2
gdscript/warnings/constant_used_as_function=2
gdscript/warnings/function_used_as_property=2
gdscript/warnings/untyped_declaration=2
[editor]
movie_writer/disable_vsync=true
[editor_plugins]
enabled=PackedStringArray("res://addons/gut/plugin.cfg", "res://addons/terrain_3d/plugin.cfg")
enabled=PackedStringArray("res://addons/gut/plugin.cfg", "res://addons/smoothing/plugin.cfg", "res://addons/terrain_3d/plugin.cfg")
[filesystem]
import/blender/enabled=false
[input]
@ -75,15 +105,27 @@ fire_primary={
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
]
}
throw_flag={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"echo":false,"script":null)
]
}
toggle_mouse_capture={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}
scoreboard={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194306,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}
[layer_names]
3d_physics/layer_3="Damage"
3d_physics/layer_4="Objective"
3d_physics/layer_32="Terrain"
[physics]

View file

@ -1,8 +0,0 @@
shader_type canvas_item;
uniform sampler2D background : source_color;
void fragment() {
COLOR = texture(background, UV);
}

36
systems/settings.gd Normal file
View file

@ -0,0 +1,36 @@
class_name Settings extends Node
var _config_file : ConfigFile = ConfigFile.new()
var fov : int
var mouse_sensitivity : float
var inverted_y_axis : bool
var fullscreen : bool
const SETTINGS_FILE_PATH : String = "user://settings.cfg"
func _ready() -> void:
var error : Error = _config_file.load(SETTINGS_FILE_PATH)
if error != OK:
print("Settings not found, using defaults")
return
fov = _config_file.get_value("gameplay", "fov", 90)
mouse_sensitivity = _config_file.get_value("gameplay", "mouse_sensitivity", 0.6)
inverted_y_axis = _config_file.get_value("gameplay", "inverted_y_axis", false)
fullscreen = _config_file.get_value("graphics", "fullscreen", false)
_update_screen()
func save_to_file() -> void:
_config_file.set_value("gameplay", "fov", fov)
_config_file.set_value("gameplay", "mouse_sensitivity", mouse_sensitivity)
_config_file.set_value("gameplay", "inverted_y_axis", inverted_y_axis)
_config_file.set_value("graphics", "fullscreen", fullscreen)
_update_screen()
_config_file.save(SETTINGS_FILE_PATH)
func _update_screen() -> void:
if fullscreen:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
else:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)

View file

@ -1,21 +1,21 @@
# 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/>.
extends GutTest
func _test_projectile_class():
var projectile = Projectile.new()
assert(projectile != null, "Projectile class should be instantiated")
assert(projectile.speed == 78.4, "Projectile damage should be initialized to 78.4")
projectile.queue_free()
func test_projectile_class() -> void:
var projectile : Projectile = Projectile.new()
assert_true(projectile != null, "Projectile class should be instantiated")
assert_true(projectile.speed == 78.4, "Projectile damage should be initialized to 78.4")
projectile.free()

View file

@ -0,0 +1,52 @@
# 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/>.
extends GutTest
var _subject : HealthComponent
const TEST_MAX_HEALTH : float = 100.0
func before_each() -> void:
_subject = HealthComponent.new()
watch_signals(_subject)
_subject.max_health = TEST_MAX_HEALTH
add_child(_subject)
func after_each() -> void:
_subject.free()
func test_that_it_has_max_health_when_ready() -> void:
assert_eq(_subject.health, _subject.max_health)
func test_that_it_takes_damage() -> void:
var damage_amount : float = 10
_subject.damage(damage_amount, -1)
assert_eq(_subject.health, TEST_MAX_HEALTH - damage_amount)
func test_that_it_emits_health_changed_after_damage() -> void:
_subject.damage(1, -1)
assert_signal_emitted(_subject, 'health_changed')
func test_that_it_emits_health_zeroed() -> void:
_subject.damage(TEST_MAX_HEALTH, -1)
assert_signal_emitted_with_parameters(_subject, 'health_zeroed', [-1])
func test_that_it_heals_fully() -> void:
_subject.health = 10
_subject.heal_full()
assert_eq(_subject.health, TEST_MAX_HEALTH)
func test_that_it_emits_health_changed_after_heal_full() -> void:
_subject.heal_full()
assert_signal_emitted(_subject, 'health_changed')

57
tests/test_scoreboard.gd Normal file
View file

@ -0,0 +1,57 @@
# 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/>.
extends GutTest
var PLAYER : PackedScene = preload("res://entities/player/player.tscn")
var _subject : Scoreboard
func before_each() -> void:
_subject = Scoreboard.new()
add_child(_subject)
func after_each() -> void:
_subject.free()
func test_that_new_scoreboard_is_empty() -> void:
assert_eq(_subject.get_entries(), [])
func test_that_added_entry_is_added_correctly() -> void:
var player : Player = PLAYER.instantiate()
player.nickname = "test_nickname"
_subject.add_entry(player)
var entries : Array = _subject.get_entries()
assert_eq(1, entries.size())
var tested_entry : Scoreboard.ScoreboardEntry = entries[0]
assert_eq("test_nickname", tested_entry.nickname)
assert_eq(0, tested_entry.kills)
assert_eq(0, tested_entry.score)
player.free()
func test_that_scores_are_added_correctly() -> void:
var player : Player = PLAYER.instantiate()
_subject.add_entry(player)
_subject.add_score_to_player(player, 10)
var tested_entry : Scoreboard.ScoreboardEntry = _subject.get_entries()[0]
assert_eq(10, tested_entry.score)
player.free()
func test_that_kill_counts_are_incremented_correctly() -> void:
var player : Player = PLAYER.instantiate()
_subject.add_entry(player)
_subject.increment_kill_count(player)
var tested_entry : Scoreboard.ScoreboardEntry = _subject.get_entries()[0]
assert_eq(1, tested_entry.kills)
player.free()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

View file

@ -1,3 +0,0 @@
[gd_resource type="ShaderMaterial" format=3 uid="uid://de6t4olk7hrs1"]
[resource]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

File diff suppressed because one or more lines are too long