9 Commits
Author SHA1 Message Date
admin a66c39e9f8 Adding simple dialog system 2026-02-13 11:41:39 -06:00
pillboxstyx 2d27e4254e Merge pull request 'Starting to add a sprint, will figure out how to hook in the run animation next' (#7) from pillboxstyx/addingSprint into main
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 44s
Deploy Promiscuity Locations API / deploy (push) Successful in 45s
k8s smoke test / test (push) Successful in 7s
Reviewed-on: #7
2026-02-13 02:50:06 -06:00
pillboxstyx b3287ccf6f Starting to add a sprint, will figure out how to hook in the run animation next 2026-02-13 02:48:07 -06:00
admin d1fade919c Adding beginning of world block generation
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 57s
Deploy Promiscuity Locations API / deploy (push) Successful in 58s
k8s smoke test / test (push) Successful in 7s
2026-01-27 21:01:29 -06:00
admin b2eb85fdf1 Hooking up animations, and tweaking car behavior 2026-01-27 20:53:53 -06:00
admin d8138fbe69 sync assets and microservice updates 2026-01-27 20:41:28 -06:00
admin e54dd191f1 Merge branch 'car' 2026-01-27 20:37:17 -06:00
admin aef117bcf9 enhancing car behavior 2026-01-21 18:16:04 -06:00
admin 582b2c43cb Adding basic car 2026-01-21 17:59:14 -06:00
30 changed files with 1164 additions and 540 deletions
+8 -1
View File
@@ -11,4 +11,11 @@
- `SUPER/SUPER` - Super User - `SUPER/SUPER` - Super User
- `test1/test1` - Super User - `test1/test1` - Super User
- `test3/test3` - User - `test3/test3` - User
## Controls
- Move: WASD
- Jump: Space
- Interact (enter/exit car): E
- Flashlight: F
- Phone: Tab
- Pause menu: Esc
+19 -19
View File
@@ -1,19 +1,19 @@
[remap] [remap]
importer="oggvorbisstr" importer="oggvorbisstr"
type="AudioStreamOggVorbis" type="AudioStreamOggVorbis"
uid="uid://de2e8sy4x724m" uid="uid://de2e8sy4x724m"
path="res://.godot/imported/jump.ogg-09aff86a6f79a8fce2febb69902962cf.oggvorbisstr" path="res://.godot/imported/jump.ogg-09aff86a6f79a8fce2febb69902962cf.oggvorbisstr"
[deps] [deps]
source_file="res://assets/audio/jump.ogg" source_file="res://assets/audio/jump.ogg"
dest_files=["res://.godot/imported/jump.ogg-09aff86a6f79a8fce2febb69902962cf.oggvorbisstr"] dest_files=["res://.godot/imported/jump.ogg-09aff86a6f79a8fce2febb69902962cf.oggvorbisstr"]
[params] [params]
loop=false loop=false
loop_offset=0 loop_offset=0
bpm=0 bpm=0
beat_count=0 beat_count=0
bar_beats=4 bar_beats=4
@@ -1,19 +1,19 @@
[remap] [remap]
importer="oggvorbisstr" importer="oggvorbisstr"
type="AudioStreamOggVorbis" type="AudioStreamOggVorbis"
uid="uid://64dplcgx2icb" uid="uid://64dplcgx2icb"
path="res://.godot/imported/silly-menu-hover-test.ogg-101de051c9810c756b28483653a4c618.oggvorbisstr" path="res://.godot/imported/silly-menu-hover-test.ogg-101de051c9810c756b28483653a4c618.oggvorbisstr"
[deps] [deps]
source_file="res://assets/audio/silly-menu-hover-test.ogg" source_file="res://assets/audio/silly-menu-hover-test.ogg"
dest_files=["res://.godot/imported/silly-menu-hover-test.ogg-101de051c9810c756b28483653a4c618.oggvorbisstr"] dest_files=["res://.godot/imported/silly-menu-hover-test.ogg-101de051c9810c756b28483653a4c618.oggvorbisstr"]
[params] [params]
loop=false loop=false
loop_offset=0 loop_offset=0
bpm=0 bpm=0
beat_count=0 beat_count=0
bar_beats=4 bar_beats=4
+19 -19
View File
@@ -1,19 +1,19 @@
[remap] [remap]
importer="oggvorbisstr" importer="oggvorbisstr"
type="AudioStreamOggVorbis" type="AudioStreamOggVorbis"
uid="uid://txgki0ijeuud" uid="uid://txgki0ijeuud"
path="res://.godot/imported/silly-test.ogg-4a08df8a26b9ee1e5d13235e013c7cfc.oggvorbisstr" path="res://.godot/imported/silly-test.ogg-4a08df8a26b9ee1e5d13235e013c7cfc.oggvorbisstr"
[deps] [deps]
source_file="res://assets/audio/silly-test.ogg" source_file="res://assets/audio/silly-test.ogg"
dest_files=["res://.godot/imported/silly-test.ogg-4a08df8a26b9ee1e5d13235e013c7cfc.oggvorbisstr"] dest_files=["res://.godot/imported/silly-test.ogg-4a08df8a26b9ee1e5d13235e013c7cfc.oggvorbisstr"]
[params] [params]
loop=false loop=false
loop_offset=0 loop_offset=0
bpm=0 bpm=0
beat_count=0 beat_count=0
bar_beats=4 bar_beats=4
@@ -1,36 +1,36 @@
[remap] [remap]
importer="font_data_dynamic" importer="font_data_dynamic"
type="FontFile" type="FontFile"
uid="uid://m5ceou0rk6j6" uid="uid://m5ceou0rk6j6"
path="res://.godot/imported/PlayfairDisplay-VariableFont_wght.ttf-fbc765a7962e1c71b0eb2c53d6eb2a10.fontdata" path="res://.godot/imported/PlayfairDisplay-VariableFont_wght.ttf-fbc765a7962e1c71b0eb2c53d6eb2a10.fontdata"
[deps] [deps]
source_file="res://assets/fonts/PlayfairDisplay-VariableFont_wght.ttf" source_file="res://assets/fonts/PlayfairDisplay-VariableFont_wght.ttf"
dest_files=["res://.godot/imported/PlayfairDisplay-VariableFont_wght.ttf-fbc765a7962e1c71b0eb2c53d6eb2a10.fontdata"] dest_files=["res://.godot/imported/PlayfairDisplay-VariableFont_wght.ttf-fbc765a7962e1c71b0eb2c53d6eb2a10.fontdata"]
[params] [params]
Rendering=null Rendering=null
antialiasing=1 antialiasing=1
generate_mipmaps=false generate_mipmaps=false
disable_embedded_bitmaps=true disable_embedded_bitmaps=true
multichannel_signed_distance_field=false multichannel_signed_distance_field=false
msdf_pixel_range=8 msdf_pixel_range=8
msdf_size=48 msdf_size=48
allow_system_fallback=true allow_system_fallback=true
force_autohinter=false force_autohinter=false
modulate_color_glyphs=false modulate_color_glyphs=false
hinting=1 hinting=1
subpixel_positioning=4 subpixel_positioning=4
keep_rounding_remainders=true keep_rounding_remainders=true
oversampling=0.0 oversampling=0.0
Fallbacks=null Fallbacks=null
fallbacks=[] fallbacks=[]
Compress=null Compress=null
compress=true compress=true
preload=[] preload=[]
language_support={} language_support={}
script_support={} script_support={}
opentype_features={} opentype_features={}
+40 -40
View File
@@ -1,40 +1,40 @@
[remap] [remap]
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://dhuosr0p605gj" uid="uid://dhuosr0p605gj"
path="res://.godot/imported/pp_start_bg.png-8fb0f850edd45e79935f992c58fa8ca2.ctex" path="res://.godot/imported/pp_start_bg.png-8fb0f850edd45e79935f992c58fa8ca2.ctex"
metadata={ metadata={
"vram_texture": false "vram_texture": false
} }
[deps] [deps]
source_file="res://assets/images/pp_start_bg.png" source_file="res://assets/images/pp_start_bg.png"
dest_files=["res://.godot/imported/pp_start_bg.png-8fb0f850edd45e79935f992c58fa8ca2.ctex"] dest_files=["res://.godot/imported/pp_start_bg.png-8fb0f850edd45e79935f992c58fa8ca2.ctex"]
[params] [params]
compress/mode=0 compress/mode=0
compress/high_quality=false compress/high_quality=false
compress/lossy_quality=0.7 compress/lossy_quality=0.7
compress/uastc_level=0 compress/uastc_level=0
compress/rdo_quality_loss=0.0 compress/rdo_quality_loss=0.0
compress/hdr_compression=1 compress/hdr_compression=1
compress/normal_map=0 compress/normal_map=0
compress/channel_pack=0 compress/channel_pack=0
mipmaps/generate=false mipmaps/generate=false
mipmaps/limit=-1 mipmaps/limit=-1
roughness/mode=0 roughness/mode=0
roughness/src_normal="" roughness/src_normal=""
process/channel_remap/red=0 process/channel_remap/red=0
process/channel_remap/green=1 process/channel_remap/green=1
process/channel_remap/blue=2 process/channel_remap/blue=2
process/channel_remap/alpha=3 process/channel_remap/alpha=3
process/fix_alpha_border=true process/fix_alpha_border=true
process/premult_alpha=false process/premult_alpha=false
process/normal_map_invert_y=false process/normal_map_invert_y=false
process/hdr_as_srgb=false process/hdr_as_srgb=false
process/hdr_clamp_exposure=false process/hdr_clamp_exposure=false
process/size_limit=0 process/size_limit=0
detect_3d/compress_to=1 detect_3d/compress_to=1
+7
View File
@@ -25,6 +25,8 @@ MenuMusic="*res://scenes/UI/menu_music.tscn"
MenuSfx="*res://scenes/UI/menu_sfx.tscn" MenuSfx="*res://scenes/UI/menu_sfx.tscn"
AuthState="*res://scenes/UI/auth_state.gd" AuthState="*res://scenes/UI/auth_state.gd"
CharacterService="*res://scenes/UI/character_service.gd" CharacterService="*res://scenes/UI/character_service.gd"
SelectedCharacter="*res://scenes/UI/selected_character.gd"
DialogSystem="*res://scenes/UI/dialog_system.gd"
[dotnet] [dotnet]
@@ -74,3 +76,8 @@ player_phone={
"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":4194306,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) "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":4194306,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
] ]
} }
interact={
"deadzone": 0.2,
"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":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
]
}
+25 -16
View File
@@ -3,8 +3,9 @@ extends Node3D
@export var left_pupil_path: NodePath = NodePath("Body/HeadPivot/EyeLeft/Pupil") @export var left_pupil_path: NodePath = NodePath("Body/HeadPivot/EyeLeft/Pupil")
@export var right_pupil_path: NodePath = NodePath("Body/HeadPivot/EyeRight/Pupil") @export var right_pupil_path: NodePath = NodePath("Body/HeadPivot/EyeRight/Pupil")
@export var camera_path: NodePath @export var camera_path: NodePath
@export var look_origin_path: NodePath = NodePath("Body/HeadPivot") @export var look_origin_path: NodePath = NodePath("Body/HeadPivot")
@export var look_reference_path: NodePath = NodePath("Body") @export var look_reference_path: NodePath = NodePath("Body")
@export var look_target_path: NodePath = NodePath("")
@export var lock_vertical: bool = true @export var lock_vertical: bool = true
@export var vertical_unlock_height: float = 0.6 @export var vertical_unlock_height: float = 0.6
@export var vertical_lock_smooth_speed: float = 6.0 @export var vertical_lock_smooth_speed: float = 6.0
@@ -28,10 +29,11 @@ var _head: Node3D
var _head_base_rot: Vector3 var _head_base_rot: Vector3
var _vertical_lock_factor: float = 1.0 var _vertical_lock_factor: float = 1.0
var _vertical_hold_timer: float = 0.0 var _vertical_hold_timer: float = 0.0
var _look_reference: Node3D var _look_reference: Node3D
var _look_target: Node3D
func _ready() -> void: func _ready() -> void:
_left_pupil = get_node_or_null(left_pupil_path) as Node3D _left_pupil = get_node_or_null(left_pupil_path) as Node3D
_right_pupil = get_node_or_null(right_pupil_path) as Node3D _right_pupil = get_node_or_null(right_pupil_path) as Node3D
if _left_pupil: if _left_pupil:
@@ -39,9 +41,10 @@ func _ready() -> void:
if _right_pupil: if _right_pupil:
_right_base = _right_pupil.position _right_base = _right_pupil.position
_camera = _resolve_camera() _camera = _resolve_camera()
_look_origin = get_node_or_null(look_origin_path) as Node3D _look_origin = get_node_or_null(look_origin_path) as Node3D
_look_reference = get_node_or_null(look_reference_path) as Node3D _look_reference = get_node_or_null(look_reference_path) as Node3D
_head = get_node_or_null(head_path) as Node3D _look_target = get_node_or_null(look_target_path) as Node3D
_head = get_node_or_null(head_path) as Node3D
if _head: if _head:
_head_base_rot = _head.rotation _head_base_rot = _head.rotation
@@ -54,15 +57,21 @@ func _process(_delta: float) -> void:
_update_pupils() _update_pupils()
func _update_pupils() -> void: func _update_pupils() -> void:
if _camera == null or not _camera.is_inside_tree(): if _look_target == null and look_target_path != NodePath(""):
_camera = _resolve_camera() _look_target = get_node_or_null(look_target_path) as Node3D
if _camera == null: if _look_target == null:
return var viewport_cam := get_viewport().get_camera_3d()
var origin := _look_origin if viewport_cam != null and viewport_cam != _camera:
if origin == null: _camera = viewport_cam
origin = self elif _camera == null or not _camera.is_inside_tree():
var target := _camera.global_position _camera = _resolve_camera()
if _look_target == null and _camera == null:
return
var origin := _look_origin
if origin == null:
origin = self
var target := _look_target.global_position if _look_target != null else _camera.global_position
var dir_world := target - origin.global_position var dir_world := target - origin.global_position
if dir_world.length_squared() <= 0.0001: if dir_world.length_squared() <= 0.0001:
return return
+30 -10
View File
@@ -1,6 +1,7 @@
[gd_scene load_steps=14 format=3] [gd_scene load_steps=14 format=3]
[ext_resource type="Script" path="res://scenes/Characters/repo_bot.gd" id="1_repo_bot"] [ext_resource type="Script" path="res://scenes/Characters/repo_bot.gd" id="1_repo_bot"]
[ext_resource type="Script" path="res://scenes/Interaction/dialog_trigger_area.gd" id="2_dialog"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_body"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_body"]
albedo_color = Color(0.78, 0.8, 0.82, 1) albedo_color = Color(0.78, 0.8, 0.82, 1)
@@ -50,14 +51,22 @@ material = SubResource("StandardMaterial3D_body")
size = Vector3(0.26, 0.3, 0.12) size = Vector3(0.26, 0.3, 0.12)
material = SubResource("StandardMaterial3D_accent") material = SubResource("StandardMaterial3D_accent")
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
radius = 0.3 radius = 0.3
height = 1.1 height = 1.1
[sub_resource type="SphereShape3D" id="SphereShape3D_interact"]
radius = 1.7
[node name="RepoBot" type="Node3D"] [node name="RepoBot" type="Node3D"]
script = ExtResource("1_repo_bot") script = ExtResource("1_repo_bot")
[node name="Body" type="StaticBody3D" parent="."] [node name="Body" type="RigidBody3D" parent="."]
mass = 1.5
axis_lock_angular_x = true
axis_lock_angular_z = true
angular_damp = 8.0
linear_damp = 0.5
[node name="CollisionShape3D" type="CollisionShape3D" parent="Body"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.55, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.55, 0)
@@ -106,6 +115,17 @@ mesh = SubResource("CylinderMesh_limb")
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.12, 0.15, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.12, 0.15, 0)
mesh = SubResource("CylinderMesh_limb") mesh = SubResource("CylinderMesh_limb")
[node name="Backpack" type="MeshInstance3D" parent="Body"] [node name="Backpack" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0.22) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0.22)
mesh = SubResource("BoxMesh_pack") mesh = SubResource("BoxMesh_pack")
[node name="InteractArea" type="Area3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8, 0)
script = ExtResource("2_dialog")
collision_layer = 2
collision_mask = 1
prompt_text = "Press E to talk"
dialog_text = "Repo Bot: Welcome to Promiscuity.\n\nPress E again to close this dialog."
[node name="CollisionShape3D" type="CollisionShape3D" parent="InteractArea"]
shape = SubResource("SphereShape3D_interact")
@@ -0,0 +1,49 @@
extends Area3D
@export var target_group: StringName = &"player"
@export var prompt_text := "Press E to talk"
@export_multiline var dialog_text := ""
@export var auto_popup := false
@export_enum("Every Entry", "Once") var auto_popup_mode := 0
var _has_triggered := false
func _ready() -> void:
collision_layer = 2
collision_mask = 1
body_entered.connect(_on_body_entered)
body_exited.connect(_on_body_exited)
func _exit_tree() -> void:
if not auto_popup and DialogSystem:
DialogSystem.unregister_interactable(self)
func _on_body_entered(body: Node) -> void:
if not (target_group == StringName() or body.is_in_group(target_group)):
return
if auto_popup:
if auto_popup_mode == 1 and _has_triggered:
return
_has_triggered = true
if DialogSystem and DialogSystem.has_method("show_text"):
DialogSystem.show_text(dialog_text)
return
if DialogSystem:
DialogSystem.register_interactable(self)
func _on_body_exited(body: Node) -> void:
if auto_popup:
return
if target_group == StringName() or body.is_in_group(target_group):
DialogSystem.unregister_interactable(self)
func get_dialog_prompt() -> String:
return prompt_text
func get_dialog_text() -> String:
return dialog_text
@@ -0,0 +1 @@
uid://bk53njt7i3kmv
+17 -5
View File
@@ -1,13 +1,25 @@
extends Node3D extends Node3D
@export var day_length := 120.0 # seconds for full rotation @export var day_length := 120.0 # seconds for full rotation
@export var start_light_angle := -90.0 @export var start_light_angle := -90.0
var end_light_angle = start_light_angle + 360.0 @export var show_spawn_dialog := true
var start_radians = start_light_angle * PI / 180 @export_multiline var spawn_dialog_text := "Welcome to Promiscuity.\n\nPress E to close this message."
var time := 0.0 @export var spawn_dialog_auto_close_seconds := 4.0
var end_light_angle = start_light_angle + 360.0
var start_radians = start_light_angle * PI / 180
var time := 0.0
@onready var sun := $DirectionalLight3D @onready var sun := $DirectionalLight3D
func _ready() -> void:
if show_spawn_dialog and DialogSystem and DialogSystem.has_method("show_text"):
await get_tree().process_frame
DialogSystem.show_text(spawn_dialog_text)
if spawn_dialog_auto_close_seconds > 0.0:
await get_tree().create_timer(spawn_dialog_auto_close_seconds).timeout
if DialogSystem and DialogSystem.has_method("close_if_text"):
DialogSystem.close_if_text(spawn_dialog_text)
func _process(delta): func _process(delta):
time = fmod((time + delta), day_length) time = fmod((time + delta), day_length)
var t = time / day_length var t = time / day_length
+71 -23
View File
@@ -1,4 +1,4 @@
[gd_scene load_steps=18 format=3 uid="uid://dchj6g2i8ebph"] [gd_scene load_steps=23 format=3 uid="uid://dchj6g2i8ebph"]
[ext_resource type="Script" uid="uid://brgmxhhhtakja" path="res://scenes/Levels/level.gd" id="1_a4mo8"] [ext_resource type="Script" uid="uid://brgmxhhhtakja" path="res://scenes/Levels/level.gd" id="1_a4mo8"]
[ext_resource type="PackedScene" uid="uid://bb6hj6l23043x" path="res://assets/models/human.blend" id="1_eg4yq"] [ext_resource type="PackedScene" uid="uid://bb6hj6l23043x" path="res://assets/models/human.blend" id="1_eg4yq"]
@@ -6,7 +6,9 @@
[ext_resource type="PackedScene" uid="uid://c5of6aaxop1hl" path="res://scenes/block.tscn" id="2_tc7dm"] [ext_resource type="PackedScene" uid="uid://c5of6aaxop1hl" path="res://scenes/block.tscn" id="2_tc7dm"]
[ext_resource type="Script" uid="uid://b7fopt7sx74g8" path="res://scenes/Levels/menu.gd" id="3_tc7dm"] [ext_resource type="Script" uid="uid://b7fopt7sx74g8" path="res://scenes/Levels/menu.gd" id="3_tc7dm"]
[ext_resource type="PackedScene" path="res://scenes/Characters/repo_bot.tscn" id="4_repo"] [ext_resource type="PackedScene" path="res://scenes/Characters/repo_bot.tscn" id="4_repo"]
[ext_resource type="PackedScene" path="res://scenes/Vehicles/car.tscn" id="5_car"]
[ext_resource type="PackedScene" uid="uid://bnqaqbgynoyys" path="res://assets/models/TestCharAnimated.glb" id="5_fi66n"] [ext_resource type="PackedScene" uid="uid://bnqaqbgynoyys" path="res://assets/models/TestCharAnimated.glb" id="5_fi66n"]
[ext_resource type="Script" uid="uid://bk53njt7i3kmv" path="res://scenes/Interaction/dialog_trigger_area.gd" id="6_dialog"]
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_2q6dc"] [sub_resource type="PhysicsMaterial" id="PhysicsMaterial_2q6dc"]
bounce = 0.5 bounce = 0.5
@@ -20,6 +22,20 @@ bounce = 0.5
[sub_resource type="SphereShape3D" id="SphereShape3D_mx8sn"] [sub_resource type="SphereShape3D" id="SphereShape3D_mx8sn"]
[sub_resource type="SphereShape3D" id="SphereShape3D_dialog_zone"]
radius = 2.5
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_dialog_zone"]
transparency = 1
cull_mode = 2
shading_mode = 0
albedo_color = Color(0.2, 0.8, 0.35, 0.18)
[sub_resource type="SphereMesh" id="SphereMesh_dialog_zone"]
material = SubResource("StandardMaterial3D_dialog_zone")
radius = 2.5
height = 5.0
[sub_resource type="BoxShape3D" id="BoxShape3D_2q6dc"] [sub_resource type="BoxShape3D" id="BoxShape3D_2q6dc"]
size = Vector3(1080, 2, 1080) size = Vector3(1080, 2, 1080)
@@ -43,6 +59,7 @@ script = ExtResource("1_a4mo8")
[node name="RepoBot" parent="." instance=ExtResource("4_repo")] [node name="RepoBot" parent="." instance=ExtResource("4_repo")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.9426608, 0, -4.4451966) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.9426608, 0, -4.4451966)
look_target_path = NodePath("../Player")
[node name="Thing" type="RigidBody3D" parent="."] [node name="Thing" type="RigidBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -3.7986288) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -3.7986288)
@@ -60,6 +77,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
mesh = SubResource("SphereMesh_w7c3h") mesh = SubResource("SphereMesh_w7c3h")
[node name="Player" type="RigidBody3D" parent="."] [node name="Player" type="RigidBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.6563287, 0, 0)
physics_material_override = SubResource("PhysicsMaterial_w8frs") physics_material_override = SubResource("PhysicsMaterial_w8frs")
script = ExtResource("1_muv8p") script = ExtResource("1_muv8p")
camera_path = NodePath("Camera3D") camera_path = NodePath("Camera3D")
@@ -69,27 +87,27 @@ phone_path = NodePath("../PhoneUI")
transform = Transform3D(-0.9998549, 0, 0.01703362, 0, 1, 0, -0.01703362, 0, -0.9998549, 0, 0, 0) transform = Transform3D(-0.9998549, 0, 0.01703362, 0, 1, 0, -0.01703362, 0, -0.9998549, 0, 0, 0)
[node name="Skeleton3D" parent="Player/TestCharAnimated/Armature" index="0"] [node name="Skeleton3D" parent="Player/TestCharAnimated/Armature" index="0"]
bones/0/position = Vector3(0.0026170756, -0.0012418391, -0.98996276) bones/0/position = Vector3(0.002616825, -0.0012634661, -0.98995954)
bones/0/rotation = Quaternion(-0.7278173, -0.05346041, 0.012412205, 0.6835714) bones/0/rotation = Quaternion(-0.7278332, -0.05342924, 0.012382713, 0.6835575)
bones/1/position = Vector3(8.593209e-06, 0.09923458, -0.012273359) bones/1/position = Vector3(8.593209e-06, 0.09923458, -0.012273359)
bones/1/rotation = Quaternion(-0.031655744, 0.011362745, 0.022744056, 0.9991754) bones/1/rotation = Quaternion(-0.03163086, 0.011397719, 0.022761386, 0.9991754)
bones/2/position = Vector3(-2.2989244e-10, 0.117319785, 2.9730852e-08) bones/2/position = Vector3(-2.2989244e-10, 0.117319785, 2.9730852e-08)
bones/2/rotation = Quaternion(0.010291794, 0.001169461, 0.013815534, 0.9998509) bones/2/rotation = Quaternion(0.0102874795, 0.0011603195, 0.013812066, 0.99985105)
bones/3/position = Vector3(-2.3358266e-09, 0.13458829, -1.382244e-08) bones/3/position = Vector3(-2.3358266e-09, 0.13458829, -1.382244e-08)
bones/3/rotation = Quaternion(0.067557335, 0.0021622085, 0.013776206, 0.99761796) bones/3/rotation = Quaternion(0.06755522, 0.0021543663, 0.013774819, 0.99761814)
bones/4/position = Vector3(2.508626e-07, 0.15027755, 0.008779066) bones/4/position = Vector3(2.508626e-07, 0.15027755, 0.008779066)
bones/4/rotation = Quaternion(0.051882792, -0.00090027065, 0.0039363643, 0.998645) bones/4/rotation = Quaternion(0.051899955, -0.00089839776, 0.0039267424, 0.9986442)
bones/5/position = Vector3(2.152588e-08, 0.103218146, 0.03142428) bones/5/position = Vector3(2.152588e-08, 0.103218146, 0.03142428)
bones/5/rotation = Quaternion(-0.06595954, 0.011917545, -0.0014885183, 0.99775004) bones/5/rotation = Quaternion(-0.06595736, 0.011938445, -0.0014933152, 0.9977499)
bones/6/position = Vector3(1.5477327e-06, 0.18474667, 0.06636399) bones/6/position = Vector3(1.5477327e-06, 0.18474667, 0.06636399)
bones/7/position = Vector3(0.061058242, 0.09106279, 0.0075706206) bones/7/position = Vector3(0.061058242, 0.09106279, 0.0075706206)
bones/7/rotation = Quaternion(-0.5982132, -0.43452233, 0.50765175, -0.4422909) bones/7/rotation = Quaternion(-0.598225, -0.43451372, 0.5076822, -0.44224826)
bones/8/position = Vector3(1.1842036e-08, 0.12922287, 5.2578955e-08) bones/8/position = Vector3(1.1842036e-08, 0.12922287, 5.2578955e-08)
bones/8/rotation = Quaternion(0.5434686, 0.18407291, 0.20301422, 0.7934383) bones/8/rotation = Quaternion(0.543452, 0.18399428, 0.20302251, 0.79346573)
bones/9/position = Vector3(1.5054144e-07, 0.2740467, 2.2048344e-08) bones/9/position = Vector3(1.5054144e-07, 0.2740467, 2.2048344e-08)
bones/9/rotation = Quaternion(-5.192343e-09, -1.1239315e-06, 0.01273731, 0.99991894) bones/9/rotation = Quaternion(-5.2907154e-09, -1.1239725e-06, 0.012734968, 0.999919)
bones/10/position = Vector3(1.9432857e-08, 0.27614468, 9.6609995e-08) bones/10/position = Vector3(1.9432857e-08, 0.27614468, 9.6609995e-08)
bones/10/rotation = Quaternion(0.048323337, -0.28487536, 0.026756868, 0.9569718) bones/10/rotation = Quaternion(0.04830448, -0.2848921, 0.026799988, 0.9569665)
bones/11/position = Vector3(-0.030029751, 0.037888147, 0.021671427) bones/11/position = Vector3(-0.030029751, 0.037888147, 0.021671427)
bones/11/rotation = Quaternion(0.2098604, -0.059559863, 0.20750383, 0.9536002) bones/11/rotation = Quaternion(0.2098604, -0.059559863, 0.20750383, 0.9536002)
bones/12/position = Vector3(-3.3527822e-08, 0.0474497, -1.48403245e-08) bones/12/position = Vector3(-3.3527822e-08, 0.0474497, -1.48403245e-08)
@@ -126,13 +144,13 @@ bones/29/position = Vector3(1.195453e-08, 0.02594832, 1.5542597e-08)
bones/29/rotation = Quaternion(0.14350323, 3.322942e-05, -0.014478063, 0.98954403) bones/29/rotation = Quaternion(0.14350323, 3.322942e-05, -0.014478063, 0.98954403)
bones/30/position = Vector3(-2.4944999e-08, 0.029238665, 6.2325825e-08) bones/30/position = Vector3(-2.4944999e-08, 0.029238665, 6.2325825e-08)
bones/31/position = Vector3(-0.06105696, 0.09106397, 0.007570758) bones/31/position = Vector3(-0.06105696, 0.09106397, 0.007570758)
bones/31/rotation = Quaternion(0.60009927, -0.4346891, 0.52418536, 0.4197095) bones/31/rotation = Quaternion(0.60012215, -0.43466684, 0.5242142, 0.41966385)
bones/32/position = Vector3(2.116817e-08, 0.12922288, 7.016769e-08) bones/32/position = Vector3(2.116817e-08, 0.12922288, 7.016769e-08)
bones/32/rotation = Quaternion(0.46432823, -0.26300937, -0.23269261, 0.813068) bones/32/rotation = Quaternion(0.46436408, -0.26298717, -0.23268588, 0.8130566)
bones/33/position = Vector3(-2.350565e-08, 0.27404687, 1.5161348e-08) bones/33/position = Vector3(-2.350565e-08, 0.27404687, 1.5161348e-08)
bones/33/rotation = Quaternion(7.667138e-08, -5.0620065e-06, -0.058376648, 0.99829465) bones/33/rotation = Quaternion(7.69042e-08, -5.0622584e-06, -0.058380682, 0.9982944)
bones/34/position = Vector3(1.3629875e-07, 0.27614468, -9.158248e-09) bones/34/position = Vector3(1.3629875e-07, 0.27614468, -9.158248e-09)
bones/34/rotation = Quaternion(0.059040155, 0.17509013, -0.005968382, 0.9827626) bones/34/rotation = Quaternion(0.059025347, 0.17513894, -0.005969527, 0.9827547)
bones/35/position = Vector3(0.030029776, 0.03788806, 0.021671649) bones/35/position = Vector3(0.030029776, 0.03788806, 0.021671649)
bones/35/rotation = Quaternion(0.21378386, 0.065372065, -0.22564878, 0.94821185) bones/35/rotation = Quaternion(0.21378386, 0.065372065, -0.22564878, 0.94821185)
bones/36/position = Vector3(-2.7939748e-09, 0.04744962, -1.7240383e-08) bones/36/position = Vector3(-2.7939748e-09, 0.04744962, -1.7240383e-08)
@@ -169,25 +187,26 @@ bones/53/position = Vector3(1.4340932e-08, 0.025948457, 3.3946527e-08)
bones/53/rotation = Quaternion(0.14377311, 5.8452275e-07, 0.011701506, 0.98954153) bones/53/rotation = Quaternion(0.14377311, 5.8452275e-07, 0.011701506, 0.98954153)
bones/54/position = Vector3(4.750415e-09, 0.029238641, -1.2388978e-09) bones/54/position = Vector3(4.750415e-09, 0.029238641, -1.2388978e-09)
bones/55/position = Vector3(0.09123873, -0.06657194, -0.0005540352) bones/55/position = Vector3(0.09123873, -0.06657194, -0.0005540352)
bones/55/rotation = Quaternion(0.13601132, 0.07838244, 0.9844897, -0.07834015) bones/55/rotation = Quaternion(0.136, 0.07837879, 0.98449296, -0.07832257)
bones/56/position = Vector3(-1.3288446e-09, 0.40599436, 1.0443015e-08) bones/56/position = Vector3(-1.3288446e-09, 0.40599436, 1.0443015e-08)
bones/56/rotation = Quaternion(-0.13252214, -0.014519276, 0.03215593, 0.99055195) bones/56/rotation = Quaternion(-0.1325433, -0.014470397, 0.03215335, 0.9905499)
bones/57/position = Vector3(5.707578e-09, 0.42099008, -1.5425995e-08) bones/57/position = Vector3(5.707578e-09, 0.42099008, -1.5425995e-08)
bones/57/rotation = Quaternion(0.5344839, -0.021701135, -0.027458949, 0.8444537) bones/57/rotation = Quaternion(0.5344709, -0.021695312, -0.027452054, 0.8444624)
bones/58/position = Vector3(-6.5650525e-09, 0.15721555, -2.6694579e-08) bones/58/position = Vector3(-6.5650525e-09, 0.15721555, -2.6694579e-08)
bones/58/rotation = Quaternion(0.2693438, -0.032380052, -0.014592491, 0.96238893) bones/58/rotation = Quaternion(0.2693438, -0.032380052, -0.014592491, 0.96238893)
bones/59/position = Vector3(2.4854705e-08, 0.09999996, -3.1755625e-09) bones/59/position = Vector3(2.4854705e-08, 0.09999996, -3.1755625e-09)
bones/60/position = Vector3(-0.091250315, -0.066556, -0.0005535231) bones/60/position = Vector3(-0.091250315, -0.066556, -0.0005535231)
bones/60/rotation = Quaternion(-0.09026457, -0.016758345, 0.9954146, -0.026857648) bones/60/rotation = Quaternion(-0.09026675, -0.016730117, 0.9954149, -0.026855767)
bones/61/position = Vector3(1.3129254e-08, 0.40599442, 3.8557886e-09) bones/61/position = Vector3(1.3129254e-08, 0.40599442, 3.8557886e-09)
bones/61/rotation = Quaternion(-0.07401098, 0.084030755, -0.024547135, 0.99340767) bones/61/rotation = Quaternion(-0.07409451, 0.08402694, -0.024555309, 0.9934016)
bones/62/position = Vector3(-7.3885964e-10, 0.42099023, -5.226429e-09) bones/62/position = Vector3(-7.3885964e-10, 0.42099023, -5.226429e-09)
bones/62/rotation = Quaternion(0.56540585, 0.011310213, -0.008501466, 0.8246915) bones/62/rotation = Quaternion(0.5654398, 0.01134551, -0.008516049, 0.8246677)
bones/63/position = Vector3(7.93632e-09, 0.1572156, -2.6982683e-10) bones/63/position = Vector3(7.93632e-09, 0.1572156, -2.6982683e-10)
bones/63/rotation = Quaternion(0.28907102, 0.031904068, 0.014082117, 0.9566723) bones/63/rotation = Quaternion(0.2890854, 0.031904142, 0.014081784, 0.9566679)
bones/64/position = Vector3(7.567915e-10, 0.099999994, -3.2595668e-09) bones/64/position = Vector3(7.567915e-10, 0.099999994, -3.2595668e-09)
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Player"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
shape = SubResource("SphereShape3D_mx8sn") shape = SubResource("SphereShape3D_mx8sn")
[node name="Camera3D" type="Camera3D" parent="Player"] [node name="Camera3D" type="Camera3D" parent="Player"]
@@ -197,6 +216,34 @@ fov = 49.0
[node name="SpotLight3D" type="SpotLight3D" parent="Player"] [node name="SpotLight3D" type="SpotLight3D" parent="Player"]
[node name="Car" parent="." instance=ExtResource("5_car")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 0, -3)
[node name="DialogZone" type="Area3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.5, 0, -2.5)
script = ExtResource("6_dialog")
prompt_text = "Press E to inspect area"
dialog_text = "Dialog trigger area"
[node name="CollisionShape3D" type="CollisionShape3D" parent="DialogZone"]
shape = SubResource("SphereShape3D_dialog_zone")
[node name="Visual" type="MeshInstance3D" parent="DialogZone"]
mesh = SubResource("SphereMesh_dialog_zone")
[node name="AutoDialogZone" type="Area3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4, 0, -6.5)
script = ExtResource("6_dialog")
dialog_text = "Auto dialog trigger"
auto_popup = true
[node name="CollisionShape3D" type="CollisionShape3D" parent="AutoDialogZone"]
shape = SubResource("SphereShape3D_dialog_zone")
[node name="Visual" type="MeshInstance3D" parent="AutoDialogZone"]
transform = Transform3D(0.8, 0, 0, 0, 0.8, 0, 0, 0, 0.8, 0, 0, 0)
mesh = SubResource("SphereMesh_dialog_zone")
[node name="Ground" type="StaticBody3D" parent="."] [node name="Ground" type="StaticBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
@@ -204,6 +251,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
shape = SubResource("BoxShape3D_2q6dc") shape = SubResource("BoxShape3D_2q6dc")
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"] [node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.00053596497, 0.0075991154, -0.0019865036)
mesh = SubResource("BoxMesh_w7c3h") mesh = SubResource("BoxMesh_w7c3h")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
+15
View File
@@ -0,0 +1,15 @@
extends Node3D
@export var tile_size := 4.0
@export var block_height := 1.0
@onready var _block: MeshInstance3D = $TerrainBlock
@onready var _camera: Camera3D = $Camera3D
func _ready() -> void:
var coord := SelectedCharacter.get_coord()
var block_pos := Vector3(coord.x * tile_size, block_height * 0.5, coord.y * tile_size)
_block.position = block_pos
_block.scale = Vector3(tile_size, block_height, tile_size)
if _camera:
_camera.look_at(block_pos, Vector3.UP)
+1
View File
@@ -0,0 +1 @@
uid://1fico5npv6dy
+23
View File
@@ -0,0 +1,23 @@
[gd_scene load_steps=4 format=3 uid="uid://b7p7k1i4t0m2l"]
[ext_resource type="Script" path="res://scenes/Levels/location_level.gd" id="1_6y4q1"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_yu2x4"]
albedo_color = Color(0.2, 0.6, 0.2, 1)
[sub_resource type="BoxMesh" id="BoxMesh_t2a5k"]
material = SubResource("StandardMaterial3D_yu2x4")
[node name="LocationLevel" type="Node3D"]
script = ExtResource("1_6y4q1")
[node name="TerrainBlock" type="MeshInstance3D" parent="."]
mesh = SubResource("BoxMesh_t2a5k")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.819152, 0.573576, 0, -0.573576, 0.819152, 0, 6, 0)
shadow_enabled = true
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.92388, 0.382683, 0, -0.382683, 0.92388, 0, 6, 10)
current = true
+12 -9
View File
@@ -14,13 +14,15 @@ func _input(event):
else: else:
pause_game() pause_game()
func pause_game(): func pause_game():
get_tree().paused = true get_tree().paused = true
visible = true visible = true
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
func resume_game(): func resume_game():
get_tree().paused = false get_tree().paused = false
visible = false visible = false
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _on_quit_button_pressed(): func _on_quit_button_pressed():
get_tree().quit() get_tree().quit()
@@ -28,9 +30,10 @@ func _on_quit_button_pressed():
func _on_continue_button_pressed(): func _on_continue_button_pressed():
resume_game() resume_game()
func _on_main_menu_button_pressed(): func _on_main_menu_button_pressed():
resume_game() resume_game()
get_tree().change_scene_to_file(START_SCREEN_SCENE) Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
get_tree().change_scene_to_file(START_SCREEN_SCENE)
func _register_focus_sounds() -> void: func _register_focus_sounds() -> void:
if pause_menu == null: if pause_menu == null:
+22 -7
View File
@@ -69,11 +69,11 @@ func _on_add_button_pressed() -> void:
else: else:
_status_label.text = "Character created, but response was unexpected." _status_label.text = "Character created, but response was unexpected."
func _on_delete_button_pressed() -> void: func _on_delete_button_pressed() -> void:
var selected := _character_list.get_selected_items() var selected := _character_list.get_selected_items()
if selected.is_empty(): if selected.is_empty():
_status_label.text = "Select a character to delete." _status_label.text = "Select a character to delete."
return return
var index := selected[0] var index := selected[0]
if index < 0 or index >= _characters.size(): if index < 0 or index >= _characters.size():
@@ -94,8 +94,23 @@ func _on_delete_button_pressed() -> void:
return return
_characters.remove_at(index) _characters.remove_at(index)
_character_list.remove_item(index) _character_list.remove_item(index)
_status_label.text = "Character deleted." _status_label.text = "Character deleted."
func _on_select_button_pressed() -> void:
var selected := _character_list.get_selected_items()
if selected.is_empty():
_status_label.text = "Select a character first."
return
var index := selected[0]
if index < 0 or index >= _characters.size():
_status_label.text = "Invalid selection."
return
var character: Dictionary = _characters[index]
SelectedCharacter.set_character(character)
get_tree().change_scene_to_file("res://scenes/Levels/location_level.tscn")
func _on_refresh_button_pressed() -> void: func _on_refresh_button_pressed() -> void:
_load_characters() _load_characters()
+23 -15
View File
@@ -87,16 +87,23 @@ theme = ExtResource("4_5b3b7")
text = "ADD" text = "ADD"
text_alignment = 1 text_alignment = 1
[node name="ActionHBox" type="HBoxContainer" parent="MarginContainer/ContentCenter/ContentVBox"] [node name="ActionHBox" type="HBoxContainer" parent="MarginContainer/ContentCenter/ContentVBox"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 4 size_flags_horizontal = 4
theme_override_constants/separation = 10 theme_override_constants/separation = 10
[node name="RefreshButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"] [node name="SelectButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 4 size_flags_horizontal = 4
theme = ExtResource("4_5b3b7") theme = ExtResource("4_5b3b7")
text = "REFRESH" text = "SELECT"
text_alignment = 1
[node name="RefreshButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
layout_mode = 2
size_flags_horizontal = 4
theme = ExtResource("4_5b3b7")
text = "REFRESH"
text_alignment = 1 text_alignment = 1
[node name="DeleteButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"] [node name="DeleteButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
@@ -123,9 +130,10 @@ text_alignment = 1
[node name="LogoutRequest" type="HTTPRequest" parent="."] [node name="LogoutRequest" type="HTTPRequest" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/AddHBox/AddButton" to="." method="_on_add_button_pressed"] [connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/AddHBox/AddButton" to="." method="_on_add_button_pressed"]
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/RefreshButton" to="." method="_on_refresh_button_pressed"] [connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/SelectButton" to="." method="_on_select_button_pressed"]
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/DeleteButton" to="." method="_on_delete_button_pressed"] [connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/RefreshButton" to="." method="_on_refresh_button_pressed"]
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/BackButton" to="." method="_on_back_button_pressed"] [connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/DeleteButton" to="." method="_on_delete_button_pressed"]
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/LogoutButton" to="." method="_on_logout_button_pressed"] [connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/BackButton" to="." method="_on_back_button_pressed"]
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/LogoutButton" to="." method="_on_logout_button_pressed"]
[connection signal="request_completed" from="LogoutRequest" to="." method="_on_logout_request_completed"] [connection signal="request_completed" from="LogoutRequest" to="." method="_on_logout_request_completed"]
+146
View File
@@ -0,0 +1,146 @@
extends CanvasLayer
var _interactables: Array[Node] = []
var _active_interactable: Node = null
var _dialog_visible := false
var _prompt_label: Label
var _dialog_panel: PanelContainer
var _dialog_label: RichTextLabel
func _ready() -> void:
layer = 50
process_mode = Node.PROCESS_MODE_ALWAYS
_setup_ui()
_set_dialog_visible(false)
_update_prompt_visibility()
func _process(_delta: float) -> void:
if Input.is_action_just_pressed("interact"):
if _dialog_visible:
_set_dialog_visible(false)
return
if _active_interactable != null:
_open_dialog_for(_active_interactable)
func register_interactable(interactable: Node) -> void:
if interactable == null:
return
if _interactables.has(interactable):
return
_interactables.append(interactable)
_refresh_active_interactable()
func unregister_interactable(interactable: Node) -> void:
if interactable == null:
return
_interactables.erase(interactable)
if _active_interactable == interactable and _dialog_visible:
_set_dialog_visible(false)
_refresh_active_interactable()
func _refresh_active_interactable() -> void:
while _interactables.size() > 0 and not is_instance_valid(_interactables[_interactables.size() - 1]):
_interactables.pop_back()
_active_interactable = _interactables[_interactables.size() - 1] if _interactables.size() > 0 else null
if _dialog_visible and _active_interactable != null:
_dialog_label.text = _get_dialog_text(_active_interactable)
_update_prompt_visibility()
func _open_dialog_for(interactable: Node) -> void:
if _dialog_label:
_dialog_label.text = _get_dialog_text(interactable)
_set_dialog_visible(true)
func show_text(text: String) -> void:
if _dialog_label:
_dialog_label.text = text
_set_dialog_visible(true)
func close_if_text(expected_text: String) -> void:
if not _dialog_visible:
return
if _dialog_label == null:
return
if _dialog_label.text != expected_text:
return
_set_dialog_visible(false)
func _set_dialog_visible(dialog_open: bool) -> void:
_dialog_visible = dialog_open
if _dialog_panel:
_dialog_panel.visible = _dialog_visible
_update_prompt_visibility()
func _update_prompt_visibility() -> void:
if _prompt_label == null:
return
var has_active := _active_interactable != null
_prompt_label.visible = has_active and not _dialog_visible
if _prompt_label.visible:
_prompt_label.text = _get_prompt_text(_active_interactable)
func _get_prompt_text(interactable: Node) -> String:
if interactable == null:
return ""
if interactable.has_method("get_dialog_prompt"):
return String(interactable.call("get_dialog_prompt"))
return "Press E to interact"
func _get_dialog_text(interactable: Node) -> String:
if interactable == null:
return ""
if interactable.has_method("get_dialog_text"):
return String(interactable.call("get_dialog_text"))
return "..."
func _setup_ui() -> void:
var root := Control.new()
root.name = "DialogRoot"
root.set_anchors_preset(Control.PRESET_FULL_RECT)
root.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(root)
_prompt_label = Label.new()
_prompt_label.name = "PromptLabel"
_prompt_label.set_anchors_preset(Control.PRESET_CENTER_BOTTOM)
_prompt_label.offset_left = -140.0
_prompt_label.offset_top = -64.0
_prompt_label.offset_right = 140.0
_prompt_label.offset_bottom = -36.0
_prompt_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_prompt_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
_prompt_label.text = "Press E to interact"
root.add_child(_prompt_label)
_dialog_panel = PanelContainer.new()
_dialog_panel.name = "DialogPanel"
_dialog_panel.visible = false
_dialog_panel.set_anchors_preset(Control.PRESET_CENTER_BOTTOM)
_dialog_panel.offset_left = -300.0
_dialog_panel.offset_top = -220.0
_dialog_panel.offset_right = 300.0
_dialog_panel.offset_bottom = -80.0
_dialog_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
root.add_child(_dialog_panel)
_dialog_label = RichTextLabel.new()
_dialog_label.name = "DialogText"
_dialog_label.custom_minimum_size = Vector2(560.0, 120.0)
_dialog_label.fit_content = true
_dialog_label.scroll_active = false
_dialog_label.bbcode_enabled = true
_dialog_label.text = ""
_dialog_panel.add_child(_dialog_label)
+1
View File
@@ -0,0 +1 @@
uid://be24usagr8kml
+16
View File
@@ -0,0 +1,16 @@
extends Node
var character: Dictionary = {}
func set_character(data: Dictionary) -> void:
character = data
func clear() -> void:
character = {}
func get_coord() -> Vector2:
var coord: Dictionary = character.get("coord", {})
return Vector2(
float(coord.get("x", 0)),
float(coord.get("y", 0))
)
+1
View File
@@ -0,0 +1 @@
uid://bbl0our5y4k7a
+105
View File
@@ -0,0 +1,105 @@
extends RigidBody3D
@export var drive_speed := 18.0
@export var drive_accel := 20.0
@export var brake_strength := 28.0
@export var turn_speed := 2.0
@export var turn_accel := 8.0
@export var lateral_damp := 10.0
@export var launch_impulse := 28.0
@export var launch_up_impulse := 6.0
@export var seat_path: NodePath
@export var exit_path: NodePath
@export var camera_path: NodePath
@export var interact_area_path: NodePath
@onready var seat: Node3D = get_node(seat_path) if seat_path != NodePath("") else null
@onready var exit_point: Node3D = get_node(exit_path) if exit_path != NodePath("") else null
@onready var car_camera: Camera3D = get_node(camera_path) if camera_path != NodePath("") else null
@onready var interact_area: Area3D = get_node(interact_area_path) if interact_area_path != NodePath("") else null
var _nearby_driver: Node = null
var _driver: Node = null
func _ready() -> void:
if interact_area:
interact_area.collision_layer = 2
interact_area.collision_mask = 1
interact_area.body_entered.connect(_on_interact_body_entered)
interact_area.body_exited.connect(_on_interact_body_exited)
if car_camera:
car_camera.current = false
contact_monitor = true
max_contacts_reported = 8
func _process(_delta: float) -> void:
if _driver == null and _nearby_driver != null and Input.is_action_just_pressed("interact"):
_enter_vehicle(_nearby_driver)
elif _driver != null and Input.is_action_just_pressed("interact"):
_exit_vehicle()
func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
if _driver == null:
return
var input2v := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
var forward := global_transform.basis.z
forward.y = 0.0
forward = forward.normalized()
var current_speed := linear_velocity.dot(forward)
var target_speed := input2v.y * drive_speed
var accel := drive_accel if abs(input2v.y) > 0.01 else brake_strength
var new_forward_speed := move_toward(current_speed, target_speed, accel * state.step)
var forward_vel := forward * new_forward_speed
var lateral_vel := linear_velocity - (forward * current_speed)
lateral_vel = lateral_vel.move_toward(Vector3.ZERO, lateral_damp * state.step)
linear_velocity = forward_vel + lateral_vel
var speed_factor: float = clamp(abs(new_forward_speed) / drive_speed, 0.0, 1.0)
var turn_input := input2v.x
if new_forward_speed < -0.1:
turn_input = -turn_input
var target_turn: float = turn_input * turn_speed * speed_factor
angular_velocity.y = move_toward(angular_velocity.y, target_turn, turn_accel * state.step)
if speed_factor > 0.2:
var hit_ids := {}
for i in state.get_contact_count():
var collider := state.get_contact_collider_object(i)
if collider is RigidBody3D and collider != self:
var collider_id := (collider as Node).get_instance_id()
if hit_ids.has(collider_id):
continue
hit_ids[collider_id] = true
var contact_pos := state.get_contact_collider_position(i)
var launch_dir := (contact_pos - global_position).normalized()
if launch_dir.length() <= 0.001:
var normal_world := (global_transform.basis * state.get_contact_local_normal(i)).normalized()
launch_dir = normal_world if normal_world.length() > 0.001 else forward.normalized()
var impulse := launch_dir * (launch_impulse * speed_factor) + Vector3.UP * launch_up_impulse
(collider as RigidBody3D).apply_central_impulse(impulse)
func _enter_vehicle(player: Node) -> void:
if seat == null:
return
_driver = player
player.call("enter_vehicle", self, seat, car_camera)
if car_camera:
car_camera.current = true
func _exit_vehicle() -> void:
if _driver == null:
return
_driver.call("exit_vehicle", exit_point, car_camera)
_driver = null
if car_camera:
car_camera.current = false
func _on_interact_body_entered(body: Node) -> void:
if body.has_method("enter_vehicle"):
_nearby_driver = body
func _on_interact_body_exited(body: Node) -> void:
if body == _nearby_driver:
_nearby_driver = null
+1
View File
@@ -0,0 +1 @@
uid://4qf5yinepytc
+43
View File
@@ -0,0 +1,43 @@
[gd_scene load_steps=6 format=3]
[ext_resource type="Script" path="res://scenes/Vehicles/car.gd" id="1_kbd20"]
[sub_resource type="BoxShape3D" id="BoxShape3D_7r1j6"]
size = Vector3(1.4, 0.9, 2.6)
[sub_resource type="BoxShape3D" id="BoxShape3D_jk0m1"]
size = Vector3(2.2, 2.0, 3.8)
[sub_resource type="BoxMesh" id="BoxMesh_4y8xk"]
size = Vector3(1.4, 0.9, 2.6)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_red"]
albedo_color = Color(0.85, 0.1, 0.1, 1)
[node name="Car" type="RigidBody3D"]
script = ExtResource("1_kbd20")
seat_path = NodePath("Seat")
exit_path = NodePath("Exit")
camera_path = NodePath("CarCamera")
interact_area_path = NodePath("InteractArea")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("BoxShape3D_7r1j6")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = SubResource("BoxMesh_4y8xk")
surface_material_override/0 = SubResource("StandardMaterial3D_red")
[node name="InteractArea" type="Area3D" parent="."]
[node name="CollisionShape3D" type="CollisionShape3D" parent="InteractArea"]
shape = SubResource("BoxShape3D_jk0m1")
[node name="Seat" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0, 0.6, 0.0)
[node name="Exit" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.3, 0.0, 0.0)
[node name="CarCamera" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0, 2.0, 4.2)
+247 -154
View File
@@ -1,154 +1,247 @@
extends RigidBody3D extends RigidBody3D
# Initially I used a CharacterBody3D, however, I wanted the player to bounce off # Initially I used a CharacterBody3D, however, I wanted the player to bounce off
# other objects in the environment and that would have required manual handling # other objects in the environment and that would have required manual handling
# of collisions. So that's why we're using a RigidBody3D instead. # of collisions. So that's why we're using a RigidBody3D instead.
const MOVE_SPEED := 8.0 const MOVE_SPEED := 8.0
const ACCELLERATION := 30.0 const SPRINT_MOVE_SPEED :=13
const DECELLERATION := 40.0 const ACCELLERATION := 30.0
const JUMP_SPEED := 4.0 const DECELLERATION := 40.0
const MAX_NUMBER_OF_JUMPS := 2 const JUMP_SPEED := 4.0
const MAX_NUMBER_OF_JUMPS := 2
const MIN_FOV := 10 const MIN_FOV := 10
const MAX_FOV := 180 const MAX_FOV := 180
const ZOOM_FACTOR := 1.1 # Zoom out when >1, in when < 1 const ZOOM_FACTOR := 1.1 # Zoom out when >1, in when < 1
var mouse_sensitivity := 0.005 var mouse_sensitivity := 0.005
var rotation_x := 0.0 var rotation_x := 0.0
var rotation_y := 0.0 var rotation_y := 0.0
var cameraMoveMode := false var cameraMoveMode := false
var current_number_of_jumps := 0 var current_number_of_jumps := 0
var _pending_mouse_delta := Vector2.ZERO var _pending_mouse_delta := Vector2.ZERO
var _last_move_forward := Vector3(0, 0, 1) var _last_move_forward := Vector3(0, 0, 1)
var _last_move_right := Vector3(1, 0, 0) var _last_move_right := Vector3(1, 0, 0)
var _camera_offset_local := Vector3.ZERO var _camera_offset_local := Vector3.ZERO
var _camera_yaw := 0.0 var _camera_yaw := 0.0
var _camera_pitch := 0.0 var _camera_pitch := 0.0
var _in_vehicle := false
@export var camera_follow_speed := 10.0 var _vehicle_collision_layer := 0
var _vehicle_collision_mask := 0
var jump_sound = preload("res://assets/audio/jump.ogg") var _vehicle_original_parent: Node = null
var audio_player = AudioStreamPlayer.new() var _light_was_on := false
var _jump_triggered := false
@export var camera_path: NodePath @onready var _flashlight: SpotLight3D = $SpotLight3D
@onready var cam: Camera3D = get_node(camera_path) if camera_path != NodePath("") else null @onready var _anim_player: AnimationPlayer = find_child("AnimationPlayer", true, false) as AnimationPlayer
@export var phone_path: NodePath @onready var _model_root: Node3D = find_child("TestCharAnimated", true, false) as Node3D
@onready var phone: CanvasLayer = get_node(phone_path) if phone_path != NodePath("") else null
var phone_visible := false @export var camera_follow_speed := 10.0
@export var anim_idle_name := "Idle"
func _ready() -> void: @export var anim_walk_name := "Walk"
axis_lock_angular_x = true @export var anim_jump_name := "Jump"
axis_lock_angular_z = true @export var anim_run_name := "Run"
angular_damp = 6.0 @export var anim_walk_speed_threshold := 0.25
contact_monitor = true @export var anim_sprint_speed_threshold := 10.0
max_contacts_reported = 4
add_child(audio_player) var jump_sound = preload("res://assets/audio/jump.ogg")
audio_player.stream = jump_sound var audio_player = AudioStreamPlayer.new()
audio_player.volume_db = -20
if cam: @export var camera_path: NodePath
_camera_offset_local = cam.transform.origin @onready var cam: Camera3D = get_node(camera_path) if camera_path != NodePath("") else null
_camera_pitch = cam.rotation.x @export var phone_path: NodePath
_camera_yaw = global_transform.basis.get_euler().y @onready var phone: CanvasLayer = get_node(phone_path) if phone_path != NodePath("") else null
cam.set_as_top_level(true) var phone_visible := false
cam.global_position = global_position + (Basis(Vector3.UP, _camera_yaw) * _camera_offset_local)
cam.global_rotation = Vector3(_camera_pitch, _camera_yaw, 0.0) func _ready() -> void:
var move_basis := cam.global_transform.basis if cam else global_transform.basis add_to_group("player")
var forward := move_basis.z axis_lock_angular_x = true
var right := move_basis.x axis_lock_angular_z = true
forward.y = 0.0 angular_damp = 6.0
right.y = 0.0 contact_monitor = true
if forward.length() > 0.0001: max_contacts_reported = 4
_last_move_forward = forward.normalized() add_child(audio_player)
if right.length() > 0.0001: audio_player.stream = jump_sound
_last_move_right = right.normalized() audio_player.volume_db = -20
if cam:
func _integrate_forces(state): _camera_offset_local = cam.transform.origin
if cameraMoveMode and _pending_mouse_delta != Vector2.ZERO: _camera_pitch = cam.rotation.x
rotation_x -= _pending_mouse_delta.y * mouse_sensitivity _camera_yaw = global_transform.basis.get_euler().y
rotation_y -= _pending_mouse_delta.x * mouse_sensitivity cam.set_as_top_level(true)
rotation_x = clamp(rotation_x, deg_to_rad(-90), deg_to_rad(90)) # Prevent flipping cam.global_position = global_position + (Basis(Vector3.UP, _camera_yaw) * _camera_offset_local)
_camera_pitch = rotation_x cam.global_rotation = Vector3(_camera_pitch, _camera_yaw, 0.0)
rotation.y = rotation_y var move_basis := cam.global_transform.basis if cam else global_transform.basis
_pending_mouse_delta = Vector2.ZERO var forward := move_basis.z
var right := move_basis.x
# Input as 2D vector forward.y = 0.0
var input2v := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") right.y = 0.0
if forward.length() > 0.0001:
if Input.is_action_just_pressed("player_phone"): _last_move_forward = forward.normalized()
phone_visible = !phone_visible if right.length() > 0.0001:
if phone: _last_move_right = right.normalized()
phone.visible = phone_visible _vehicle_collision_layer = collision_layer
_vehicle_collision_mask = collision_mask
# Camera based movement
var forward := Vector3.FORWARD * -1.0 func _integrate_forces(state):
var right := Vector3.RIGHT if _in_vehicle:
if cam: linear_velocity = Vector3.ZERO
forward = cam.global_transform.basis.z return
right = cam.global_transform.basis.x if cameraMoveMode and _pending_mouse_delta != Vector2.ZERO:
# Project onto ground plane so looking up/down doesn't kill movement. rotation_x -= _pending_mouse_delta.y * mouse_sensitivity
forward.y = 0.0 rotation_y -= _pending_mouse_delta.x * mouse_sensitivity
right.y = 0.0 rotation_x = clamp(rotation_x, deg_to_rad(-90), deg_to_rad(90)) # Prevent flipping
if forward.length() > 0.0001: _camera_pitch = rotation_x
forward = forward.normalized() rotation.y = rotation_y
_last_move_forward = forward _pending_mouse_delta = Vector2.ZERO
else:
forward = _last_move_forward # Input as 2D vector
if right.length() > 0.0001: var input2v := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
right = right.normalized()
_last_move_right = right if Input.is_action_just_pressed("player_phone"):
else: phone_visible = !phone_visible
right = _last_move_right if phone:
phone.visible = phone_visible
var dir := (right * input2v.x + forward * input2v.y).normalized()
var target_v := dir * MOVE_SPEED # Camera based movement
var forward := Vector3.FORWARD * -1.0
var ax := ACCELLERATION if dir != Vector3.ZERO else DECELLERATION var right := Vector3.RIGHT
linear_velocity.x = move_toward(linear_velocity.x, target_v.x, ax * state.step) if cam:
linear_velocity.z = move_toward(linear_velocity.z, target_v.z, ax * state.step) forward = cam.global_transform.basis.z
right = cam.global_transform.basis.x
# Jump Logic # Project onto ground plane so looking up/down doesn't kill movement.
var on_floor = false forward.y = 0.0
for i in state.get_contact_count(): right.y = 0.0
var normal = state.get_contact_local_normal(i) if forward.length() > 0.0001:
if normal.y > 0.5: forward = forward.normalized()
on_floor = true _last_move_forward = forward
break else:
forward = _last_move_forward
if Input.is_action_just_pressed("ui_accept") and (on_floor or current_number_of_jumps == 1): if right.length() > 0.0001:
current_number_of_jumps = (current_number_of_jumps + 1) % 2 right = right.normalized()
linear_velocity.y = JUMP_SPEED _last_move_right = right
audio_player.play() else:
right = _last_move_right
if cam:
var target_yaw := global_transform.basis.get_euler().y var dir := (right * input2v.x + forward * input2v.y).normalized()
_camera_yaw = lerp_angle(_camera_yaw, target_yaw, camera_follow_speed * state.step)
var target_basis := Basis(Vector3.UP, _camera_yaw) var target_v := dir * MOVE_SPEED
var target_pos := global_position + (target_basis * _camera_offset_local)
cam.global_position = cam.global_position.lerp(target_pos, camera_follow_speed * state.step) # Sprinting
cam.global_rotation = Vector3(_camera_pitch, _camera_yaw, 0.0) if Input.is_key_pressed(KEY_SHIFT):
target_v = dir * SPRINT_MOVE_SPEED
func _input(event):
if event is InputEventMouseButton: var ax := ACCELLERATION if dir != Vector3.ZERO else DECELLERATION
if event.button_index == MOUSE_BUTTON_MIDDLE: linear_velocity.x = move_toward(linear_velocity.x, target_v.x, ax * state.step)
if event.pressed: linear_velocity.z = move_toward(linear_velocity.z, target_v.z, ax * state.step)
cameraMoveMode = true
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) # Jump Logic
else: var on_floor = false
cameraMoveMode = false for i in state.get_contact_count():
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) var normal = state.get_contact_local_normal(i)
if normal.y > 0.5:
if event is InputEventMouseMotion and cameraMoveMode: on_floor = true
_pending_mouse_delta += event.relative break
if event is InputEventMouseButton and event.pressed: if Input.is_action_just_pressed("ui_accept") and (on_floor or current_number_of_jumps == 1):
if event.button_index == MOUSE_BUTTON_WHEEL_UP: current_number_of_jumps = (current_number_of_jumps + 1) % 2
zoom_camera(1.0 / ZOOM_FACTOR) # Zoom in linear_velocity.y = JUMP_SPEED
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN: audio_player.play()
zoom_camera(ZOOM_FACTOR) # Zoom out _jump_triggered = true
if event.is_action_pressed("player_light"): if cam:
$SpotLight3D.visible = !$SpotLight3D.visible var target_yaw := global_transform.basis.get_euler().y
_camera_yaw = lerp_angle(_camera_yaw, target_yaw, camera_follow_speed * state.step)
func zoom_camera(factor): var target_basis := Basis(Vector3.UP, _camera_yaw)
var new_fov = cam.fov * factor var target_pos := global_position + (target_basis * _camera_offset_local)
cam.fov = clamp(new_fov, MIN_FOV, MAX_FOV) cam.global_position = cam.global_position.lerp(target_pos, camera_follow_speed * state.step)
cam.global_rotation = Vector3(_camera_pitch, _camera_yaw, 0.0)
_update_animation(on_floor, state.linear_velocity)
_jump_triggered = false
func _input(event):
if _in_vehicle:
return
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_MIDDLE:
if event.pressed:
cameraMoveMode = true
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
else:
cameraMoveMode = false
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
if event is InputEventMouseMotion and cameraMoveMode:
_pending_mouse_delta += event.relative
if event is InputEventMouseButton and event.pressed:
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
zoom_camera(1.0 / ZOOM_FACTOR) # Zoom in
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
zoom_camera(ZOOM_FACTOR) # Zoom out
if event.is_action_pressed("player_light"):
_flashlight.visible = !_flashlight.visible
func zoom_camera(factor):
var new_fov = cam.fov * factor
cam.fov = clamp(new_fov, MIN_FOV, MAX_FOV)
func _update_animation(on_floor: bool, velocity: Vector3) -> void:
if _anim_player == null:
return
var horizontal_speed := Vector3(velocity.x, 0.0, velocity.z).length()
if _jump_triggered and _anim_player.has_animation(anim_jump_name):
if _anim_player.current_animation != anim_jump_name:
_anim_player.play(anim_jump_name)
return
if not on_floor and _anim_player.has_animation(anim_jump_name):
if _anim_player.current_animation != anim_jump_name:
_anim_player.play(anim_jump_name)
return
if on_floor and horizontal_speed > anim_sprint_speed_threshold and _anim_player.has_animation(anim_run_name):
if _anim_player.current_animation != anim_walk_name:
_anim_player.play(anim_walk_name)
return
if horizontal_speed > anim_walk_speed_threshold and _anim_player.has_animation(anim_walk_name):
if _anim_player.current_animation != anim_walk_name:
_anim_player.play(anim_walk_name)
return
if _anim_player.has_animation(anim_idle_name):
if _anim_player.current_animation != anim_idle_name:
_anim_player.play(anim_idle_name)
func enter_vehicle(_vehicle: Node, seat: Node3D, vehicle_camera: Camera3D) -> void:
_in_vehicle = true
freeze = true
sleeping = true
collision_layer = 0
collision_mask = 0
_vehicle_original_parent = get_parent()
_light_was_on = _flashlight.visible
_flashlight.visible = false
if _model_root:
_model_root.visible = false
if seat:
reparent(seat, true)
global_transform = seat.global_transform
if cam:
cam.current = false
if vehicle_camera:
vehicle_camera.current = true
func exit_vehicle(exit_point: Node3D, vehicle_camera: Camera3D) -> void:
_in_vehicle = false
freeze = false
sleeping = false
collision_layer = _vehicle_collision_layer
collision_mask = _vehicle_collision_mask
if _vehicle_original_parent:
reparent(_vehicle_original_parent, true)
_vehicle_original_parent = null
_flashlight.visible = _light_was_on
if _model_root:
_model_root.visible = true
if exit_point:
global_transform = exit_point.global_transform
if vehicle_camera:
vehicle_camera.current = false
if cam:
cam.current = true
+27 -27
View File
@@ -1,27 +1,27 @@
# CharacterApi document shapes # CharacterApi document shapes
This service expects JSON request bodies for character creation and stores This service expects JSON request bodies for character creation and stores
character documents in MongoDB. character documents in MongoDB.
Inbound JSON documents Inbound JSON documents
- CreateCharacterRequest (`POST /api/characters`) - CreateCharacterRequest (`POST /api/characters`)
```json ```json
{ {
"name": "string" "name": "string"
} }
``` ```
Stored documents (MongoDB) Stored documents (MongoDB)
- Character - Character
```json ```json
{ {
"id": "string (ObjectId)", "id": "string (ObjectId)",
"ownerUserId": "string", "ownerUserId": "string",
"name": "string", "name": "string",
"coord": { "coord": {
"x": "number", "x": "number",
"y": "number" "y": "number"
}, },
"createdUtc": "string (ISO-8601 datetime)" "createdUtc": "string (ISO-8601 datetime)"
} }
``` ```
+8 -8
View File
@@ -1,8 +1,8 @@
namespace CharacterApi.Models; namespace CharacterApi.Models;
public class Coord public class Coord
{ {
public int X { get; set; } public int X { get; set; }
public int Y { get; set; } public int Y { get; set; }
} }
@@ -1,132 +1,132 @@
using LocationsApi.Models; using LocationsApi.Models;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
namespace LocationsApi.Services; namespace LocationsApi.Services;
public class LocationStore public class LocationStore
{ {
private readonly IMongoCollection<Location> _col; private readonly IMongoCollection<Location> _col;
public LocationStore(IConfiguration cfg) public LocationStore(IConfiguration cfg)
{ {
var cs = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017"; var cs = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017";
var dbName = cfg["MongoDB:DatabaseName"] ?? "GameDb"; var dbName = cfg["MongoDB:DatabaseName"] ?? "GameDb";
var client = new MongoClient(cs); var client = new MongoClient(cs);
var db = client.GetDatabase(dbName); var db = client.GetDatabase(dbName);
var collectionName = "Locations"; var collectionName = "Locations";
EnsureLocationSchema(db, collectionName); EnsureLocationSchema(db, collectionName);
_col = db.GetCollection<Location>(collectionName); _col = db.GetCollection<Location>(collectionName);
var coordIndex = Builders<Location>.IndexKeys var coordIndex = Builders<Location>.IndexKeys
.Ascending(l => l.Coord.X) .Ascending(l => l.Coord.X)
.Ascending(l => l.Coord.Y); .Ascending(l => l.Coord.Y);
var coordIndexOptions = new CreateIndexOptions { Unique = true }; var coordIndexOptions = new CreateIndexOptions { Unique = true };
_col.Indexes.CreateOne(new CreateIndexModel<Location>(coordIndex, coordIndexOptions)); _col.Indexes.CreateOne(new CreateIndexModel<Location>(coordIndex, coordIndexOptions));
EnsureOriginLocation(); EnsureOriginLocation();
} }
private static void EnsureLocationSchema(IMongoDatabase db, string collectionName) private static void EnsureLocationSchema(IMongoDatabase db, string collectionName)
{ {
var validator = new BsonDocument var validator = new BsonDocument
{ {
{ {
"$jsonSchema", new BsonDocument "$jsonSchema", new BsonDocument
{ {
{ "bsonType", "object" }, { "bsonType", "object" },
{ "required", new BsonArray { "name", "coord", "createdUtc" } }, { "required", new BsonArray { "name", "coord", "createdUtc" } },
{ {
"properties", new BsonDocument "properties", new BsonDocument
{ {
{ "name", new BsonDocument { { "bsonType", "string" } } }, { "name", new BsonDocument { { "bsonType", "string" } } },
{ {
"coord", new BsonDocument "coord", new BsonDocument
{ {
{ "bsonType", "object" }, { "bsonType", "object" },
{ "required", new BsonArray { "x", "y" } }, { "required", new BsonArray { "x", "y" } },
{ {
"properties", new BsonDocument "properties", new BsonDocument
{ {
{ "x", new BsonDocument { { "bsonType", "int" } } }, { "x", new BsonDocument { { "bsonType", "int" } } },
{ "y", new BsonDocument { { "bsonType", "int" } } } { "y", new BsonDocument { { "bsonType", "int" } } }
} }
} }
} }
}, },
{ "createdUtc", new BsonDocument { { "bsonType", "date" } } } { "createdUtc", new BsonDocument { { "bsonType", "date" } } }
} }
} }
} }
} }
}; };
var collections = db.ListCollectionNames().ToList(); var collections = db.ListCollectionNames().ToList();
if (!collections.Contains(collectionName)) if (!collections.Contains(collectionName))
{ {
var createCommand = new BsonDocument var createCommand = new BsonDocument
{ {
{ "create", collectionName }, { "create", collectionName },
{ "validator", validator }, { "validator", validator },
{ "validationAction", "error" } { "validationAction", "error" }
}; };
db.RunCommand<BsonDocument>(createCommand); db.RunCommand<BsonDocument>(createCommand);
return; return;
} }
var command = new BsonDocument var command = new BsonDocument
{ {
{ "collMod", collectionName }, { "collMod", collectionName },
{ "validator", validator }, { "validator", validator },
{ "validationAction", "error" } { "validationAction", "error" }
}; };
db.RunCommand<BsonDocument>(command); db.RunCommand<BsonDocument>(command);
} }
public Task CreateAsync(Location location) => _col.InsertOneAsync(location); public Task CreateAsync(Location location) => _col.InsertOneAsync(location);
public Task<List<Location>> GetAllAsync() => public Task<List<Location>> GetAllAsync() =>
_col.Find(Builders<Location>.Filter.Empty).ToListAsync(); _col.Find(Builders<Location>.Filter.Empty).ToListAsync();
public async Task<bool> DeleteAsync(string id) public async Task<bool> DeleteAsync(string id)
{ {
var filter = Builders<Location>.Filter.Eq(l => l.Id, id); var filter = Builders<Location>.Filter.Eq(l => l.Id, id);
var result = await _col.DeleteOneAsync(filter); var result = await _col.DeleteOneAsync(filter);
return result.DeletedCount > 0; return result.DeletedCount > 0;
} }
public async Task<bool> UpdateNameAsync(string id, string name) public async Task<bool> UpdateNameAsync(string id, string name)
{ {
var filter = Builders<Location>.Filter.Eq(l => l.Id, id); var filter = Builders<Location>.Filter.Eq(l => l.Id, id);
var update = Builders<Location>.Update.Set(l => l.Name, name); var update = Builders<Location>.Update.Set(l => l.Name, name);
var result = await _col.UpdateOneAsync(filter, update); var result = await _col.UpdateOneAsync(filter, update);
return result.ModifiedCount > 0; return result.ModifiedCount > 0;
} }
private void EnsureOriginLocation() private void EnsureOriginLocation()
{ {
var filter = Builders<Location>.Filter.And( var filter = Builders<Location>.Filter.And(
Builders<Location>.Filter.Eq(l => l.Coord.X, 0), Builders<Location>.Filter.Eq(l => l.Coord.X, 0),
Builders<Location>.Filter.Eq(l => l.Coord.Y, 0) Builders<Location>.Filter.Eq(l => l.Coord.Y, 0)
); );
var existing = _col.Find(filter).FirstOrDefault(); var existing = _col.Find(filter).FirstOrDefault();
if (existing is not null) if (existing is not null)
return; return;
var origin = new Location var origin = new Location
{ {
Name = "Origin", Name = "Origin",
Coord = new Coord { X = 0, Y = 0 }, Coord = new Coord { X = 0, Y = 0 },
CreatedUtc = DateTime.UtcNow CreatedUtc = DateTime.UtcNow
}; };
try try
{ {
_col.InsertOne(origin); _col.InsertOne(origin);
} }
catch (MongoWriteException ex) when (ex.WriteError.Category == ServerErrorCategory.DuplicateKey) catch (MongoWriteException ex) when (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{ {
// Another instance seeded it first. // Another instance seeded it first.
} }
} }
} }