awesome!!!

This commit is contained in:
wanp 2025-02-11 08:00:26 -03:00
commit 07577d3fd3
29 changed files with 1538 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
# Godot 4+ specific ignores
.godot/
# Godot-specific ignores
.import/
export.cfg
export_presets.cfg
# Imported translations (automatically generated from CSV files)
*.translation
# Mono-specific ignores
.mono/
data_*/
mono_crash.*.json

BIN
assets/drop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

34
assets/drop.png.import Normal file
View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cwbl0r1e26eja"
path="res://.godot/imported/drop.png-46e76e0a1a49ec3b9e6c8f19494b9486.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/drop.png"
dest_files=["res://.godot/imported/drop.png-46e76e0a1a49ec3b9e6c8f19494b9486.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=0

BIN
assets/sfx/boom.wav Normal file

Binary file not shown.

View File

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://3dlhs18w1fa2"
path="res://.godot/imported/boom.wav-b7dc2436738b092118bc6a9ac139ed1b.sample"
[deps]
source_file="res://assets/sfx/boom.wav"
dest_files=["res://.godot/imported/boom.wav-b7dc2436738b092118bc6a9ac139ed1b.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View File

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://blgrl2wl05feq"
path="res://.godot/imported/splash-small-quiet.wav-5d79804a4ce6d654b376652fbb9b2033.sample"
[deps]
source_file="res://assets/sfx/splash-small-quiet.wav"
dest_files=["res://.godot/imported/splash-small-quiet.wav-5d79804a4ce6d654b376652fbb9b2033.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

BIN
assets/sfx/splash-small.wav Normal file

Binary file not shown.

View File

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://dtjpv2b74g24m"
path="res://.godot/imported/splash-small.wav-395e6cb2278f19594f3004dfba45fa36.sample"
[deps]
source_file="res://assets/sfx/splash-small.wav"
dest_files=["res://.godot/imported/splash-small.wav-395e6cb2278f19594f3004dfba45fa36.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

BIN
assets/sprout.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

34
assets/sprout.png.import Normal file
View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d35y5ckne72qe"
path="res://.godot/imported/sprout.png-98a4ab515d3bfdbd6f6db7d14b52ead7.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprout.png"
dest_files=["res://.godot/imported/sprout.png-98a4ab515d3bfdbd6f6db7d14b52ead7.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=0

9
default_bus_layout.tres Normal file
View File

@ -0,0 +1,9 @@
[gd_resource type="AudioBusLayout" format=3 uid="uid://dcxpomrfumov"]
[resource]
bus/1/name = &"SoundEffects"
bus/1/solo = false
bus/1/mute = false
bus/1/bypass_fx = false
bus/1/volume_db = 0.0
bus/1/send = &"Master"

1
icon.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

After

Width:  |  Height:  |  Size: 994 B

37
icon.svg.import Normal file
View File

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cb6qv3c0iojfl"
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

82
project.godot Normal file
View File

@ -0,0 +1,82 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[application]
config/name="Soil Tower"
run/main_scene="res://src/main/main.tscn"
config/features=PackedStringArray("4.3", "GL Compatibility")
config/icon="res://icon.svg"
[display]
window/size/viewport_width=640
window/size/viewport_height=360
window/size/window_width_override=1280
window/size/window_height_override=720
window/stretch/mode="canvas_items"
window/stretch/aspect="expand"
[global_group]
spawn_points=""
voids=""
[input]
move_forward={
"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":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
]
}
move_backward={
"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":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
]
}
strafe_left={
"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":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
]
}
strafe_right={
"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":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
]
}
jump={
"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":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
]
}
chat={
"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":84,"key_label":0,"unicode":116,"location":0,"echo":false,"script":null)
, 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":89,"key_label":0,"unicode":121,"location":0,"echo":false,"script":null)
]
}
fire={
"deadzone": 0.5,
"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)
]
}
[layer_names]
3d_physics/layer_1="solid"
3d_physics/layer_2="players"
3d_physics/layer_3="projectiles"
3d_physics/layer_4="plants"
3d_physics/layer_5="pickups"
[rendering]
renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility"

10
src/ingame/chlorophyll.gd Normal file
View File

@ -0,0 +1,10 @@
extends Node3D
func _ready() -> void:
set_multiplayer_authority(1)
@rpc("any_peer", "call_local", "reliable")
func pick_up() -> void:
queue_free()

View File

@ -0,0 +1,25 @@
[gd_scene load_steps=4 format=3 uid="uid://byfbrru6pl1ue"]
[ext_resource type="Script" path="res://src/ingame/chlorophyll.gd" id="1_t6bqw"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_l1bir"]
albedo_color = Color(0, 0.894118, 0.211765, 1)
emission_enabled = true
emission = Color(0, 0.894118, 0.211765, 1)
emission_energy_multiplier = 2.0
[sub_resource type="SphereShape3D" id="SphereShape3D_so6gf"]
radius = 0.3
[node name="Chlorophyll" type="Node3D"]
script = ExtResource("1_t6bqw")
[node name="CSGSphere3D" type="CSGSphere3D" parent="."]
material_override = SubResource("StandardMaterial3D_l1bir")
radius = 0.1
[node name="PickupArea" type="Area3D" parent="."]
collision_layer = 16
[node name="CollisionShape3D" type="CollisionShape3D" parent="PickupArea"]
shape = SubResource("SphereShape3D_so6gf")

133
src/ingame/ingame.gd Normal file
View File

@ -0,0 +1,133 @@
extends Node3D
@export var _player_scene: PackedScene
@export var _players: Node3D
@export var _chat_panel: Panel
@export var _chat_input: LineEdit
@export var _chat_history_scroll: ScrollContainer
@export var _chat_history: VBoxContainer
@export var _chat_panel_inactive: Panel
@export var _chat_history_inactive: VBoxContainer
@export var _chat_history_scroll_inactive: ScrollContainer
func _ready() -> void:
print("ingame ready")
_chat_history_scroll.get_v_scroll_bar().changed.connect(
_on_chat_history_scroll_changed.bind(_chat_history_scroll)
)
_chat_history_scroll_inactive.get_v_scroll_bar().changed.connect(
_on_chat_history_scroll_changed.bind(_chat_history_scroll_inactive)
)
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
if not multiplayer.is_server():
return
multiplayer.peer_connected.connect(_add_player)
multiplayer.peer_disconnected.connect(_remove_player)
for id in multiplayer.get_peers():
_add_player.call_deferred(id)
if not OS.has_feature("dedicated_server"):
_add_player.call_deferred(1)
func _exit_tree() -> void:
if not multiplayer.is_server():
return
multiplayer.peer_connected.disconnect(_add_player)
multiplayer.peer_disconnected.disconnect(_remove_player)
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("chat"):
get_viewport().set_input_as_handled()
_activate_chat()
func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel"):
get_viewport().set_input_as_handled()
_deactivate_chat()
func _activate_chat() -> void:
_chat_panel.show()
_chat_panel_inactive.hide()
_chat_input.grab_focus.call_deferred()
_players.get_node(str(multiplayer.get_unique_id())).controls_disabled = true
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
func _deactivate_chat() -> void:
_chat_panel.hide()
_chat_panel_inactive.show()
_players.get_node(str(multiplayer.get_unique_id())).controls_disabled = false
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _add_player(id: int) -> void:
print("add player %d" % id)
var character: CharacterBody3D = _player_scene.instantiate()
character.id = id
character.name = str(id)
_players.add_child(character, true)
_set_character_authority.rpc(id)
func _remove_player(id: int) -> void:
if not _players.has_node(str(id)):
return
print("remove player %d" % id)
_players.get_node(str(id)).queue_free()
@rpc("authority", "call_local", "reliable")
func _set_character_authority(id: int) -> void:
if not _players.has_node(str(id)):
return
_players.get_node(str(id)).set_multiplayer_authority(id)
@rpc("any_peer", "call_local", "reliable", 1)
func _submit_chat_message(text: String) -> void:
var id := multiplayer.get_remote_sender_id()
var username: String = GameState.fetch().player_data[id].username
_add_chat_message.rpc(username, text)
@rpc("authority", "call_local", "reliable", 1)
func _add_chat_message(username: String, text: String) -> void:
_chat_history.add_child(_make_chat_message(username, text))
_chat_history_inactive.add_child(_make_chat_message(username, text))
func _on_chat_history_scroll_changed(history: ScrollContainer) -> void:
# keep history scrolled to the bottom
history.scroll_vertical = history.get_v_scroll_bar().max_value
func _make_chat_message(username: String, text: String) -> RichTextLabel:
var label := RichTextLabel.new()
label.bbcode_enabled = true
label.fit_content = true
label.append_text("[color=red]%s[/color] %s" % [username.replace("[", "[lb]"), text.replace("[", "[lb]")])
return label
func _on_chat_message_submitted(new_text := "") -> void:
if _chat_input.text.is_empty():
_deactivate_chat()
return
_submit_chat_message.rpc_id(1, _chat_input.text)
_chat_input.clear()
_deactivate_chat()

261
src/ingame/ingame.tscn Normal file
View File

@ -0,0 +1,261 @@
[gd_scene load_steps=11 format=3 uid="uid://oyvhcwq60v2"]
[ext_resource type="Script" path="res://src/ingame/ingame.gd" id="1_akuuj"]
[ext_resource type="PackedScene" uid="uid://cs8c570bxh6u" path="res://src/ingame/player.tscn" id="2_w1gjc"]
[ext_resource type="PackedScene" uid="uid://bysgtksvovyur" path="res://src/ingame/sprout.tscn" id="3_2xvqq"]
[sub_resource type="BoxShape3D" id="BoxShape3D_l0cfx"]
size = Vector3(8, 0.2, 17)
[sub_resource type="BoxShape3D" id="BoxShape3D_8v8cv"]
size = Vector3(4, 0.6, 17)
[sub_resource type="BoxShape3D" id="BoxShape3D_0k58u"]
size = Vector3(2.2, 0.6, 3)
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_eu7fm"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_e4ynx"]
ground_bottom_color = Color(0.647059, 0.654902, 0.670588, 1)
sun_angle_max = 8.0
sun_curve = 8.81744
energy_multiplier = 1.5
[sub_resource type="Sky" id="Sky_ygvd3"]
sky_material = SubResource("ProceduralSkyMaterial_e4ynx")
[sub_resource type="Environment" id="Environment_ijk14"]
background_mode = 2
sky = SubResource("Sky_ygvd3")
ambient_light_color = Color(0, 0.164706, 0.278431, 1)
ambient_light_energy = 2.0
[node name="Ingame" type="Node3D" node_paths=PackedStringArray("_players", "_chat_panel", "_chat_input", "_chat_history_scroll", "_chat_history", "_chat_panel_inactive", "_chat_history_inactive", "_chat_history_scroll_inactive")]
script = ExtResource("1_akuuj")
_player_scene = ExtResource("2_w1gjc")
_players = NodePath("Players")
_chat_panel = NodePath("UI/ChatPanel")
_chat_input = NodePath("UI/ChatPanel/ChatInput")
_chat_history_scroll = NodePath("UI/ChatPanel/ChatHistoryScroll")
_chat_history = NodePath("UI/ChatPanel/ChatHistoryScroll/ChatHistory")
_chat_panel_inactive = NodePath("UI/ChatPanelInactive")
_chat_history_inactive = NodePath("UI/ChatPanelInactive/ChatHistoryScroll/ChatHistory")
_chat_history_scroll_inactive = NodePath("UI/ChatPanelInactive/ChatHistoryScroll")
[node name="UI" type="CanvasLayer" parent="."]
[node name="ChatPanel" type="Panel" parent="UI"]
visible = false
self_modulate = Color(1, 1, 1, 0.498039)
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -312.0
offset_top = 12.0
offset_bottom = 172.0
grow_horizontal = 2
grow_vertical = 2
[node name="ChatHistoryScroll" type="ScrollContainer" parent="UI/ChatPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 8.0
offset_top = 8.0
offset_right = -8.0
offset_bottom = -48.0
grow_horizontal = 2
grow_vertical = 2
[node name="ChatHistory" type="VBoxContainer" parent="UI/ChatPanel/ChatHistoryScroll"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="ChatInput" type="LineEdit" parent="UI/ChatPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 8.0
offset_top = 120.0
offset_right = -72.0
offset_bottom = -9.0
grow_horizontal = 2
grow_vertical = 2
placeholder_text = "say something"
max_length = 300
context_menu_enabled = false
clear_button_enabled = true
middle_mouse_paste_enabled = false
[node name="ChatSendButton" type="Button" parent="UI/ChatPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 248.0
offset_top = 120.0
offset_right = -8.0
offset_bottom = -9.0
grow_horizontal = 2
grow_vertical = 2
text = "send"
[node name="ChatPanelInactive" type="Panel" parent="UI"]
self_modulate = Color(1, 1, 1, 0)
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -312.0
offset_top = 12.0
offset_bottom = 172.0
grow_horizontal = 2
grow_vertical = 2
[node name="ChatHistoryScroll" type="ScrollContainer" parent="UI/ChatPanelInactive"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 8.0
offset_top = 8.0
offset_right = -8.0
offset_bottom = -9.0
grow_horizontal = 2
grow_vertical = 2
vertical_scroll_mode = 3
[node name="ChatHistory" type="VBoxContainer" parent="UI/ChatPanelInactive/ChatHistoryScroll"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 7.45058e-09, 0)
collision_mask = 0
size = Vector3(8, 0.2, 17)
[node name="StaticBody3D" type="StaticBody3D" parent="CSGBox3D"]
collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="CSGBox3D/StaticBody3D"]
shape = SubResource("BoxShape3D_l0cfx")
[node name="GPUParticlesCollisionBox3D" type="GPUParticlesCollisionBox3D" parent="CSGBox3D"]
size = Vector3(8, 0.2, 17)
[node name="CSGBox3D2" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.03442, 0.2, 0)
collision_mask = 0
size = Vector3(4, 0.6, 17)
[node name="StaticBody3D" type="StaticBody3D" parent="CSGBox3D2"]
collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="CSGBox3D2/StaticBody3D"]
shape = SubResource("BoxShape3D_8v8cv")
[node name="GPUParticlesCollisionBox3D" type="GPUParticlesCollisionBox3D" parent="CSGBox3D2"]
size = Vector3(4, 0.6, 17)
[node name="CSGBox3D3" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.23442, 0.8, -2.4)
collision_mask = 0
size = Vector3(2.2, 0.6, 3)
[node name="StaticBody3D" type="StaticBody3D" parent="CSGBox3D3"]
collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="CSGBox3D3/StaticBody3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.76837e-07, 0, 0)
shape = SubResource("BoxShape3D_0k58u")
[node name="GPUParticlesCollisionBox3D" type="GPUParticlesCollisionBox3D" parent="CSGBox3D3"]
size = Vector3(2.2, 0.6, 3)
[node name="Void" type="StaticBody3D" parent="." groups=["voids"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -8, 0)
[node name="CollisionShape3D" type="CollisionShape3D" parent="Void"]
shape = SubResource("WorldBoundaryShape3D_eu7fm")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_ijk14")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.943957, 0.3178, 0.0891561, 0, -0.270113, 0.962829, 0.33007, -0.908868, -0.254975, 0, 0, 0)
light_energy = 2.0
[node name="SpawnPoint" type="Marker3D" parent="." groups=["spawn_points"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
[node name="PlayerSpawner" type="MultiplayerSpawner" parent="."]
_spawnable_scenes = PackedStringArray("res://src/ingame/player.tscn")
spawn_path = NodePath("../Players")
[node name="Players" type="Node3D" parent="."]
[node name="Plants" type="Node3D" parent="."]
[node name="Sprout" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.00401986, 0.396167, -3.22022)
[node name="Sprout2" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.60402, 0.396167, -3.22022)
[node name="Sprout3" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.59598, 0.396167, -3.22022)
[node name="Sprout4" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.00401986, 0.396167, 2.77978)
[node name="Sprout5" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.60402, 0.396167, 2.77978)
[node name="Sprout6" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.59598, 0.396167, 2.77978)
[node name="Sprout7" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.00401986, 0.396167, 3.37978)
[node name="Sprout8" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.60402, 0.396167, 3.37978)
[node name="Sprout9" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.59598, 0.396167, 3.37978)
[node name="Sprout10" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.99598, 1.39617, -2.22022)
[node name="Sprout11" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.39598, 1.39617, -2.22022)
[node name="Sprout12" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.59598, 1.39617, -2.22022)
[node name="Sprout16" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.99598, 1.39617, -2.82022)
[node name="Sprout17" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.39598, 1.39617, -2.82022)
[node name="Sprout18" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.59598, 1.39617, -2.82022)
[node name="Sprout13" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.99598, 1.39617, -1.62022)
[node name="Sprout14" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.39598, 1.39617, -1.62022)
[node name="Sprout15" parent="Plants" instance=ExtResource("3_2xvqq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.59598, 1.39617, -1.62022)
[connection signal="text_submitted" from="UI/ChatPanel/ChatInput" to="." method="_on_chat_message_submitted"]
[connection signal="pressed" from="UI/ChatPanel/ChatSendButton" to="." method="_on_chat_message_submitted"]

105
src/ingame/player.gd Normal file
View File

@ -0,0 +1,105 @@
extends CharacterBody3D
const SPEED = 5.0
const JUMP_VELOCITY = 4.5
@export var _projectile_scene: PackedScene
@export var _projectile_holder: Node
@export var _projectile_point: Marker3D
@export var _camera_pivot: Node3D
@export var _camera: Camera3D
@export var _shot_sound: AudioStreamPlayer3D
@export var id: int
@export var input_dir := Vector2()
@export var input_jumped := false
var _max_speed := 12
var _mouse_sensitivity := 0.008 # radians/pixel
var _projectile_speed := 12.0
var controls_disabled := false
func _ready() -> void:
if id != multiplayer.get_unique_id():
return
$Model.hide()
_camera.make_current()
var spawn_point: Marker3D = get_tree().get_nodes_in_group("spawn_points").pick_random()
global_position = spawn_point.global_position
reset_physics_interpolation()
func _process(_delta: float) -> void:
if id != multiplayer.get_unique_id():
return
_process_input()
func _physics_process(delta: float) -> void:
# Add the gravity.
if not is_on_floor():
velocity += get_gravity() * delta
# Handle jump.
if input_jumped and is_on_floor():
velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED)
move_and_slide()
func _unhandled_input(event: InputEvent) -> void:
if controls_disabled or id != multiplayer.get_unique_id():
return
if event is InputEventMouseMotion:
rotate_y(-event.relative.x * _mouse_sensitivity)
_camera_pivot.rotate_x(-event.relative.y * _mouse_sensitivity)
_camera_pivot.rotation.x = clamp(_camera_pivot.rotation.x, -1.2, 1.2)
return
if event.is_action_pressed("fire"):
var new_projectile: Node3D = _projectile_scene.instantiate()
_projectile_holder.add_child(new_projectile, true)
_set_projectile_authority.rpc(new_projectile.get_path(), id)
new_projectile.set_global_pos.rpc(_projectile_point.global_position)
new_projectile.body.process_mode = Node.PROCESS_MODE_INHERIT
new_projectile.body.linear_velocity = -_camera.global_basis.z * _projectile_speed
_shot_sound.play()
@rpc("authority", "call_local", "reliable")
func _set_projectile_authority(path: NodePath, authority_id: int) -> void:
if not _projectile_holder.has_node(path):
return
_projectile_holder.get_node(path).set_multiplayer_authority(authority_id)
func _process_input() -> void:
if controls_disabled:
input_dir = Vector2.ZERO
input_jumped = false
return
input_dir = Input.get_vector("strafe_left", "strafe_right", "move_forward", "move_backward")
input_jumped = Input.is_action_just_pressed("jump")

71
src/ingame/player.tscn Normal file
View File

@ -0,0 +1,71 @@
[gd_scene load_steps=7 format=3 uid="uid://cs8c570bxh6u"]
[ext_resource type="Script" path="res://src/ingame/player.gd" id="1_isrmf"]
[ext_resource type="PackedScene" uid="uid://tdsbo3e5ic86" path="res://src/ingame/water_bomb.tscn" id="2_naek4"]
[ext_resource type="AudioStream" uid="uid://3dlhs18w1fa2" path="res://assets/sfx/boom.wav" id="3_u2hxa"]
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_2xotl"]
properties/0/path = NodePath(".:position")
properties/0/spawn = true
properties/0/replication_mode = 1
properties/1/path = NodePath(".:id")
properties/1/spawn = true
properties/1/replication_mode = 0
properties/2/path = NodePath(".:rotation")
properties/2/spawn = true
properties/2/replication_mode = 1
properties/3/path = NodePath(".:input_dir")
properties/3/spawn = true
properties/3/replication_mode = 1
properties/4/path = NodePath(".:input_jumped")
properties/4/spawn = true
properties/4/replication_mode = 2
properties/5/path = NodePath("ShotSound:playing")
properties/5/spawn = true
properties/5/replication_mode = 2
[sub_resource type="CapsuleMesh" id="CapsuleMesh_hi8jw"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_f4jjl"]
[node name="Player" type="CharacterBody3D" node_paths=PackedStringArray("_projectile_holder", "_projectile_point", "_camera_pivot", "_camera", "_shot_sound")]
collision_layer = 2
script = ExtResource("1_isrmf")
_projectile_scene = ExtResource("2_naek4")
_projectile_holder = NodePath("ProjectileHolder")
_projectile_point = NodePath("ProjectilePoint")
_camera_pivot = NodePath("CameraPivot")
_camera = NodePath("CameraPivot/Camera3D")
_shot_sound = NodePath("ShotSound")
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
replication_config = SubResource("SceneReplicationConfig_2xotl")
[node name="ProjectileSpawner" type="MultiplayerSpawner" parent="."]
_spawnable_scenes = PackedStringArray("res://src/ingame/water_bomb.tscn")
spawn_path = NodePath("../ProjectileHolder")
[node name="ProjectileHolder" type="Node" parent="."]
[node name="ProjectilePoint" type="Marker3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.3, -0.095)
[node name="Model" type="MeshInstance3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
mesh = SubResource("CapsuleMesh_hi8jw")
[node name="CSGBox3D" type="CSGBox3D" parent="Model"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.5)
size = Vector3(0.8, 0.8, 0.8)
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("CapsuleShape3D_f4jjl")
[node name="CameraPivot" type="Node3D" parent="."]
[node name="Camera3D" type="Camera3D" parent="CameraPivot"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
[node name="ShotSound" type="AudioStreamPlayer3D" parent="."]
stream = ExtResource("3_u2hxa")
bus = &"SoundEffects"

25
src/ingame/sprout.gd Normal file
View File

@ -0,0 +1,25 @@
extends Node3D
@export var _need_water_drop: Sprite3D
@export var _production_timer: Timer
var needs_water := true
func _ready() -> void:
set_multiplayer_authority(1)
_need_water_drop.show()
func _on_production_timer_timeout() -> void:
needs_water = true
_need_water_drop.show()
@rpc("any_peer", "call_local", "reliable")
func water() -> void:
if needs_water:
needs_water = false
_need_water_drop.hide()
_production_timer.start()

53
src/ingame/sprout.tscn Normal file
View File

@ -0,0 +1,53 @@
[gd_scene load_steps=6 format=3 uid="uid://bysgtksvovyur"]
[ext_resource type="Script" path="res://src/ingame/sprout.gd" id="1_snma1"]
[ext_resource type="Texture2D" uid="uid://d35y5ckne72qe" path="res://assets/sprout.png" id="2_ipgad"]
[ext_resource type="Texture2D" uid="uid://cwbl0r1e26eja" path="res://assets/drop.png" id="3_kghdv"]
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_rs2qp"]
properties/0/path = NodePath(".:needs_water")
properties/0/spawn = true
properties/0/replication_mode = 2
properties/1/path = NodePath("NeedWaterDrop:visible")
properties/1/spawn = true
properties/1/replication_mode = 2
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwbye"]
radius = 0.3
height = 0.8
[node name="Sprout" type="Node3D" node_paths=PackedStringArray("_need_water_drop", "_production_timer")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.00401986, -0.00383317, 0.00119042)
script = ExtResource("1_snma1")
_need_water_drop = NodePath("NeedWaterDrop")
_production_timer = NodePath("ProductionTimer")
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
replication_config = SubResource("SceneReplicationConfig_rs2qp")
[node name="Sprite3D" type="Sprite3D" parent="."]
pixel_size = 0.02
billboard = 2
shaded = true
texture_filter = 2
texture = ExtResource("2_ipgad")
[node name="NeedWaterDrop" type="Sprite3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.3, 0)
visible = false
pixel_size = 0.02
billboard = 1
texture_filter = 2
texture = ExtResource("3_kghdv")
[node name="Area3D" type="Area3D" parent="."]
collision_layer = 8
collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Area3D"]
shape = SubResource("CapsuleShape3D_cwbye")
[node name="ProductionTimer" type="Timer" parent="."]
one_shot = true
[connection signal="timeout" from="ProductionTimer" to="." method="_on_production_timer_timeout"]

66
src/ingame/water_bomb.gd Normal file
View File

@ -0,0 +1,66 @@
extends Node3D
@export var _splash_small_sound: AudioStreamPlayer3D
@export var _splash_small_quiet_sound: AudioStreamPlayer3D
@export var _model: Node3D
@export var _splash_particles: GPUParticles3D
@export var body: RigidBody3D
## no longer exists and shouldn't be considered, but not ready to be freed yet
@export var is_dead := false
var _in_splash_range := {}
func _ready() -> void:
body.process_mode = Node.PROCESS_MODE_DISABLED
func _process(delta: float) -> void:
if not is_multiplayer_authority():
return
global_position = body.global_position
@rpc("authority", "call_local", "reliable")
func _disable_body() -> void:
# not using synchronizer for this because it ends up enabling processing
# on other clients
(func() -> void: body.process_mode = Node.PROCESS_MODE_DISABLED).call_deferred()
func _on_body_entered(p_body: Node3D) -> void:
if p_body.is_in_group("voids"):
queue_free()
return
for area: Area3D in _in_splash_range:
area.get_parent_node_3d().water.rpc_id(1)
_splash_small_sound.play()
_splash_small_quiet_sound.play()
_splash_particles.emitting = true
is_dead = true
_model.hide()
_disable_body.rpc()
await _splash_small_sound.finished
if _splash_small_quiet_sound.playing:
await _splash_small_quiet_sound.finished
queue_free()
func _on_splash_area_entered(area: Area3D) -> void:
_in_splash_range[area] = true
func _on_splash_area_exited(area: Area3D) -> void:
_in_splash_range.erase(area)
@rpc("authority", "call_local", "reliable")
func set_global_pos(new: Vector3) -> void:
global_position = new
body.global_position = new
body.reset_physics_interpolation()

110
src/ingame/water_bomb.tscn Normal file
View File

@ -0,0 +1,110 @@
[gd_scene load_steps=11 format=3 uid="uid://tdsbo3e5ic86"]
[ext_resource type="Script" path="res://src/ingame/water_bomb.gd" id="1_lk5fq"]
[ext_resource type="AudioStream" uid="uid://dtjpv2b74g24m" path="res://assets/sfx/splash-small.wav" id="2_0wk8g"]
[ext_resource type="AudioStream" uid="uid://blgrl2wl05feq" path="res://assets/sfx/splash-small-quiet.wav" id="3_hgy7l"]
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_0ebrr"]
properties/0/path = NodePath(".:position")
properties/0/spawn = true
properties/0/replication_mode = 1
properties/1/path = NodePath("SplashSmallSound:playing")
properties/1/spawn = true
properties/1/replication_mode = 2
properties/2/path = NodePath("SplashSmallQuietSound:playing")
properties/2/spawn = true
properties/2/replication_mode = 2
properties/3/path = NodePath(".:is_dead")
properties/3/spawn = true
properties/3/replication_mode = 2
properties/4/path = NodePath("Model:visible")
properties/4/spawn = true
properties/4/replication_mode = 2
properties/5/path = NodePath("SplashParticles:emitting")
properties/5/spawn = true
properties/5/replication_mode = 2
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ptaep"]
albedo_color = Color(0.0936238, 0.825356, 1, 1)
metallic = 0.8
roughness = 0.4
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_v7dnr"]
albedo_color = Color(0, 1, 1, 1)
roughness = 0.2
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_rjbgm"]
direction = Vector3(0, 1, 0)
initial_velocity_min = 5.0
initial_velocity_max = 5.0
gravity = Vector3(0, -8, 0)
collision_mode = 2
[sub_resource type="SphereMesh" id="SphereMesh_bhdh4"]
radius = 0.1
height = 0.2
[sub_resource type="SphereShape3D" id="SphereShape3D_6c830"]
radius = 0.2
[sub_resource type="SphereShape3D" id="SphereShape3D_y6453"]
radius = 1.5
[node name="WaterBomb" type="Node3D" node_paths=PackedStringArray("_splash_small_sound", "_splash_small_quiet_sound", "_model", "_splash_particles", "body")]
script = ExtResource("1_lk5fq")
_splash_small_sound = NodePath("SplashSmallSound")
_splash_small_quiet_sound = NodePath("SplashSmallQuietSound")
_model = NodePath("Model")
_splash_particles = NodePath("SplashParticles")
body = NodePath("RigidBody3D")
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
replication_config = SubResource("SceneReplicationConfig_0ebrr")
[node name="Model" type="CSGSphere3D" parent="."]
material_override = SubResource("StandardMaterial3D_ptaep")
radius = 0.2
radial_segments = 24
rings = 12
[node name="SplashParticles" type="GPUParticles3D" parent="."]
material_override = SubResource("StandardMaterial3D_v7dnr")
emitting = false
amount = 32
lifetime = 2.0
one_shot = true
explosiveness = 1.0
randomness = 1.0
process_material = SubResource("ParticleProcessMaterial_rjbgm")
draw_pass_1 = SubResource("SphereMesh_bhdh4")
[node name="RigidBody3D" type="RigidBody3D" parent="."]
top_level = true
collision_layer = 4
contact_monitor = true
max_contacts_reported = 1
linear_damp_mode = 1
linear_damp = 0.3
[node name="CollisionShape3D" type="CollisionShape3D" parent="RigidBody3D"]
shape = SubResource("SphereShape3D_6c830")
[node name="SplashArea" type="Area3D" parent="RigidBody3D"]
collision_layer = 0
collision_mask = 8
monitorable = false
[node name="CollisionShape3D" type="CollisionShape3D" parent="RigidBody3D/SplashArea"]
shape = SubResource("SphereShape3D_y6453")
[node name="SplashSmallSound" type="AudioStreamPlayer3D" parent="."]
stream = ExtResource("2_0wk8g")
bus = &"SoundEffects"
[node name="SplashSmallQuietSound" type="AudioStreamPlayer3D" parent="."]
stream = ExtResource("3_hgy7l")
bus = &"SoundEffects"
[connection signal="body_entered" from="RigidBody3D" to="." method="_on_body_entered"]
[connection signal="area_entered" from="RigidBody3D/SplashArea" to="." method="_on_splash_area_entered"]
[connection signal="area_exited" from="RigidBody3D/SplashArea" to="." method="_on_splash_area_exited"]

14
src/lib/game_state.gd Normal file
View File

@ -0,0 +1,14 @@
class_name GameState
extends Resource
@export var player_data := {}
static var _instance := GameState.new()
static func save() -> void:
ResourceSaver.save(_instance, "user://game_state.tres")
static func fetch() -> GameState:
return _instance

4
src/lib/player_data.gd Normal file
View File

@ -0,0 +1,4 @@
class_name PlayerData
extends Resource
@export var username: String

164
src/main/main.gd Normal file
View File

@ -0,0 +1,164 @@
extends Node
@export var _start_menu: Control
@export var _username_input: LineEdit
@export var _ip_input: LineEdit
@export var _player_list: VBoxContainer
@export var _chat_menu: Control
@export var _chat_input: LineEdit
@export var _chat_history_scroll: ScrollContainer
@export var _chat_history: VBoxContainer
@export var _start_button: Button
@export var _ingame_scene: PackedScene
var _game_started := false
func _ready() -> void:
_start_menu.show()
_chat_menu.hide()
func _on_player_joined(id: int) -> void:
for player_id in GameState.fetch().player_data.keys():
var username = GameState.fetch().player_data[player_id].username
_register_player.rpc_id(id, username)
if _game_started:
_start_game.rpc_id(id)
func _on_player_left(id: int) -> void:
_unregister_player.rpc(id)
@rpc("any_peer", "call_local", "reliable")
func _register_player(username: String) -> void:
var id = multiplayer.get_remote_sender_id()
GameState.fetch().player_data[id] = PlayerData.new()
GameState.fetch().player_data[id].username = username
var list_label := RichTextLabel.new()
list_label.bbcode_enabled = false
list_label.fit_content = true
list_label.text = username
_player_list.add_child(list_label)
@rpc("authority", "call_local", "reliable")
func _unregister_player(id: int) -> void:
var labels := _player_list.get_children()
for label: RichTextLabel in labels:
if label.text == GameState.fetch().player_data[id].username:
label.queue_free()
break
GameState.fetch().player_data.erase(id)
@rpc("any_peer", "call_local", "reliable", 1)
func _submit_chat_message(text: String) -> void:
var id := multiplayer.get_remote_sender_id()
var username: String = GameState.fetch().player_data[id].username
_add_chat_message.rpc(username, text)
@rpc("authority", "call_local", "reliable", 1)
func _add_chat_message(username: String, text: String) -> void:
var label := RichTextLabel.new()
label.bbcode_enabled = true
label.fit_content = true
label.append_text("[color=red]%s[/color] %s" % [username.replace("[", "[lb]"), text.replace("[", "[lb]")])
_chat_history.add_child(label)
# keep chat scrolled to the bottom
await _chat_history_scroll.get_v_scroll_bar().changed
_chat_history_scroll.scroll_vertical = _chat_history_scroll.get_v_scroll_bar().max_value
@rpc("authority", "call_local", "reliable")
func _start_game() -> void:
_start_menu.hide()
_chat_menu.hide()
_game_started = true
# START MENU #
func _on_host_button_pressed() -> void:
var peer := ENetMultiplayerPeer.new()
var error = peer.create_server(20106, 8, 2)
if error != OK:
match error:
ERR_ALREADY_IN_USE:
push_error("server ERR_ALREADY_IN_USE")
ERR_CANT_CREATE:
push_error("server ERR_CANT_CREATE")
return
multiplayer.multiplayer_peer = peer
multiplayer.multiplayer_peer.peer_connected.connect(_on_player_joined)
multiplayer.multiplayer_peer.peer_disconnected.connect(_on_player_left)
var username := _username_input.text
if username.is_empty():
username = "schlorpy"
_register_player.rpc_id(1, username)
_start_button.disabled = false
_start_menu.hide()
_chat_menu.show()
print("hosted!")
func _on_join_button_pressed() -> void:
var peer := ENetMultiplayerPeer.new()
var ip := ""
if _ip_input.text.is_empty():
ip = "127.0.0.1"
else:
ip = _ip_input.text
var error = peer.create_client(ip, 20106, 2)
if error != OK:
match error:
ERR_ALREADY_IN_USE:
push_error("client ERR_ALREADY_IN_USE")
ERR_CANT_CREATE:
push_error("client ERR_CANT_CREATE")
return
multiplayer.multiplayer_peer = peer
multiplayer.connected_to_server.connect(
func() -> void:
var username := _username_input.text
if username.is_empty():
username = "schlorpy"
_register_player.rpc(username)
_start_button.disabled = true
_start_menu.hide()
_chat_menu.show()
print("joined!")
)
# CHAT MENU #
func _on_start_button_pressed() -> void:
_start_game.rpc()
$IngameContainer.add_child.call_deferred(_ingame_scene.instantiate())
func _on_chat_message_submitted(text := "") -> void:
if _chat_input.text.is_empty():
return
_submit_chat_message.rpc_id(1, _chat_input.text)
_chat_input.clear()

213
src/main/main.tscn Normal file
View File

@ -0,0 +1,213 @@
[gd_scene load_steps=3 format=3 uid="uid://dk6pok1gk6ick"]
[ext_resource type="Script" path="res://src/main/main.gd" id="1_fqmow"]
[ext_resource type="PackedScene" uid="uid://oyvhcwq60v2" path="res://src/ingame/ingame.tscn" id="2_5ajgq"]
[node name="Main" type="Node" node_paths=PackedStringArray("_start_menu", "_username_input", "_ip_input", "_player_list", "_chat_menu", "_chat_input", "_chat_history_scroll", "_chat_history", "_start_button")]
script = ExtResource("1_fqmow")
_start_menu = NodePath("StartMenu")
_username_input = NodePath("StartMenu/UsernameInput")
_ip_input = NodePath("StartMenu/IPInput")
_player_list = NodePath("ChatMenu/PlayersPanel/ScrollContainer/PlayerList")
_chat_menu = NodePath("ChatMenu")
_chat_input = NodePath("ChatMenu/ChatPanel/ChatInput")
_chat_history_scroll = NodePath("ChatMenu/ChatPanel/ChatHistoryScroll")
_chat_history = NodePath("ChatMenu/ChatPanel/ChatHistoryScroll/ChatHistory")
_start_button = NodePath("ChatMenu/StartButton")
_ingame_scene = ExtResource("2_5ajgq")
[node name="IngameSpawner" type="MultiplayerSpawner" parent="."]
_spawnable_scenes = PackedStringArray("res://src/ingame/ingame.tscn")
spawn_path = NodePath("../IngameContainer")
spawn_limit = 1
[node name="IngameContainer" type="Node" parent="."]
[node name="StartMenu" type="Control" parent="."]
layout_mode = 3
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
grow_horizontal = 2
grow_vertical = 2
[node name="UsernameInput" type="LineEdit" parent="StartMenu"]
layout_mode = 0
offset_left = -44.0
offset_top = -128.0
offset_right = 84.0
offset_bottom = -97.0
placeholder_text = "username"
alignment = 1
max_length = 12
context_menu_enabled = false
middle_mouse_paste_enabled = false
[node name="IPInput" type="LineEdit" parent="StartMenu"]
layout_mode = 0
offset_left = -180.0
offset_top = 112.0
offset_right = 220.0
offset_bottom = 143.0
placeholder_text = "127.0.0.1"
alignment = 1
max_length = 39
context_menu_enabled = false
middle_mouse_paste_enabled = false
[node name="HostButton" type="Button" parent="StartMenu"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -64.0
offset_top = -100.0
offset_right = 64.0
offset_bottom = -36.0
grow_horizontal = 2
grow_vertical = 2
text = "host"
[node name="JoinButton" type="Button" parent="StartMenu"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -64.0
offset_top = 12.0
offset_right = 64.0
offset_bottom = 76.0
grow_horizontal = 2
grow_vertical = 2
text = "join"
[node name="ChatMenu" type="Control" parent="."]
visible = false
layout_mode = 3
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
grow_horizontal = 2
grow_vertical = 2
[node name="PlayersPanel" type="Panel" parent="ChatMenu"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -312.0
offset_top = -172.0
offset_right = -192.0
offset_bottom = 4.0
grow_horizontal = 2
grow_vertical = 2
[node name="ScrollContainer" type="ScrollContainer" parent="ChatMenu/PlayersPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 8.0
offset_top = 8.0
offset_right = -8.0
offset_bottom = -8.0
grow_horizontal = 2
grow_vertical = 2
[node name="PlayerList" type="VBoxContainer" parent="ChatMenu/PlayersPanel/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="ChatPanel" type="Panel" parent="ChatMenu"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -312.0
offset_top = 12.0
offset_right = 312.0
offset_bottom = 172.0
grow_horizontal = 2
grow_vertical = 2
[node name="ChatHistoryScroll" type="ScrollContainer" parent="ChatMenu/ChatPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 8.0
offset_top = 8.0
offset_right = -8.0
offset_bottom = -48.0
grow_horizontal = 2
grow_vertical = 2
[node name="ChatHistory" type="VBoxContainer" parent="ChatMenu/ChatPanel/ChatHistoryScroll"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="ChatInput" type="LineEdit" parent="ChatMenu/ChatPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 8.0
offset_top = 120.0
offset_right = -72.0
offset_bottom = -9.0
grow_horizontal = 2
grow_vertical = 2
placeholder_text = "say something"
max_length = 300
context_menu_enabled = false
clear_button_enabled = true
middle_mouse_paste_enabled = false
[node name="ChatSendButton" type="Button" parent="ChatMenu/ChatPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 560.0
offset_top = 120.0
offset_right = -8.0
offset_bottom = -9.0
grow_horizontal = 2
grow_vertical = 2
text = "send"
[node name="StartButton" type="Button" parent="ChatMenu"]
layout_mode = 0
offset_left = -164.0
offset_top = -32.0
offset_right = -100.0
offset_bottom = 23.0
text = "Start!"
[connection signal="pressed" from="StartMenu/HostButton" to="." method="_on_host_button_pressed"]
[connection signal="pressed" from="StartMenu/JoinButton" to="." method="_on_join_button_pressed"]
[connection signal="text_submitted" from="ChatMenu/ChatPanel/ChatInput" to="." method="_on_chat_message_submitted"]
[connection signal="pressed" from="ChatMenu/ChatPanel/ChatSendButton" to="." method="_on_chat_message_submitted"]
[connection signal="pressed" from="ChatMenu/StartButton" to="." method="_on_start_button_pressed"]