More work on maps and multiplayer

This commit is contained in:
2026-01-15 11:10:12 -05:00
parent dc8585b1f0
commit bc48e9cea2
25 changed files with 3740 additions and 136 deletions

View File

@@ -57,7 +57,11 @@ func switch_scene(packed_scene : PackedScene, spawn : bool = true) -> void:
tween.tween_property(blinder, "self_modulate:a", 1, .15)
if spawn:
var spawner = get_tree().get_first_node_in_group("level_spawner") as MultiplayerSpawner
tween.tween_callback(spawner.add_child.bind(packed_scene.instantiate(),true))
tween.tween_callback(spawner.add_child.bind(packed_scene.instantiate(), true))
var current_scene = get_tree().get_first_node_in_group("scenes")
if current_scene:
tween.tween_callback(current_scene.queue_free)
current_scene.process_mode=Node.PROCESS_MODE_DISABLED
else:
tween.tween_callback(get_tree().change_scene_to_packed.bind(packed_scene))
tween.tween_property(blinder, "self_modulate:a", 0, .15)

View File

@@ -79,10 +79,10 @@ func add_trap(trap : Trap, crd : Vector3i) -> void:
trap.square = crd
traps[crd] = trap
trap.position = Vector3(crd) + Vector3(.5, 0, .5)
add_child(trap)
add_child(trap, true)
func add_projectile(shot : Projectile) -> void:
add_child(shot)
add_child(shot, true)
func remove_trap_square(crd : Vector3i) -> void:
traps.erase(crd)
@@ -104,7 +104,7 @@ func is_valid_trap_square(crd : Vector3i) -> bool:
func add_vfx(vfx, crd : Vector3i) -> void:
vfx.position = Vector3(crd) #+ Vector3(0.5, 0, 0.5)
add_child(vfx)
add_child(vfx, true)
func activate_trap(crd : Vector3i) -> void:
if traps.has(crd):

View File

@@ -73,3 +73,7 @@ func _on_joining_back_button_pressed() -> void:
func _on_client_added(peer_handle : String, peer_id : int) -> void:
%ParticipantsText.text = "[color=FFFF00][b]*%s[/b][/color]\n[color=FFFFFF]*%s[/color]" % [handle, peer_handle]
%StartButton.disabled = false
func _on_level_spawned(node: Node) -> void:
if node is Level:
Game.level_spawned.rpc_id(1, Multiplayer.id)

View File

@@ -8,9 +8,11 @@ class_name PawnBody extends Node3D
@onready var struggle_ui : Sprite3D = %StruggleUI
@onready var struggle_bar : ProgressBar = %StruggleBar
@rpc("call_local")
func play_animation(anim_name : String) -> void:
anim_player.play(anim_name)
@rpc("call_local")
func show_struggle() -> void:
struggle_ui.visible = true

View File

@@ -15,6 +15,7 @@ const disarm_trap_modal = preload("res://templates/disarm_trap_modal.tscn")
@export var speed : float = 10
@onready var body : PawnBody = $PawnBody
@onready var input : PawnInput = $PawnInput
@onready var data : PawnLevelData = $Data
@onready var trap_sound : AudioStreamPlayer3D = $TrapSound
@onready var detonate_sound : AudioStreamPlayer3D = $DetonateSound
@@ -26,11 +27,11 @@ const disarm_trap_modal = preload("res://templates/disarm_trap_modal.tscn")
var id : int = 1
var state : State
@export var state : State
var button_actions : Dictionary[int, String]
var current_square : Vector3i
var facing : Vector3
var detecting : bool = false
@export var detecting : bool = false
var detect_squares : Dictionary[Vector3i, bool] = {}
var detect_tween : Tween = null
var fling_direction : Vector3
@@ -77,32 +78,14 @@ func _physics_process(delta: float) -> void:
if attack_timer > 0:
attack_timer -= delta
check_attack_target()
if is_poisoned():
poison_time_remaining -= delta
poison_pulse_timer += delta
if poison_pulse_timer >= 1.0:
poison_pulse_timer -= 1.0
hurt(poison_strength)
if poison_time_remaining <= 0:
var pshader : ShaderMaterial = body.material.next_pass
pshader.set_shader_parameter("strength", 0)
poison_status_changed.emit(false)
var dir = Input.get_vector("west", "east", "north", "south")
dir = Vector3(dir.x, 0, dir.y)
if struggle_timer > 0:
struggle_timer -= delta
var snapped_angle = round(dir.angle_to(Vector3(1,0,0)) * 4 / PI) * PI * .25
if abs(snapped_angle - struggle_angle) >= PI * .25:
struggle_angle = snapped_angle
struggle_timer -= 2 * delta
struggling.emit(struggle_timer)
if struggle_timer <= 0:
hurt(1)
knockdown(facing)
else:
return
update_poison(delta)
if !update_struggle(delta):
return
var dir = input.dir
if input_locked or modal != null:
dir = Vector3.ZERO
@@ -163,12 +146,11 @@ func _physics_process(delta: float) -> void:
move_and_slide()
if detecting:
update_detecting()
update_detect_region.rpc(true)
#Deal with the rest of the buttons
if !input_locked:
if modal:
var evt = InputEventAction.new()
var buttons = [
"lay trap",
"detect",
@@ -176,31 +158,16 @@ func _physics_process(delta: float) -> void:
"detonate"
]
for button in buttons:
if Input.is_action_just_pressed(button):
evt.action = button
evt.pressed = true
modal.button_pressed(evt)
if input.is_action_just_pressed(button):
modal.button_pressed(button)
elif state == State.NORMAL:
if Input.is_action_just_pressed("left cycle trap"):
cycle_active_trap(-1)
if Input.is_action_just_pressed("right cycle trap"):
cycle_active_trap(1)
if Input.is_action_just_pressed("detonate"):
detonate()
elif Input.is_action_just_pressed("detect"):
start_detecting()
elif Input.is_action_just_released("detect"):
stop_detecting()
elif !detecting and Input.is_action_just_pressed("lay trap"):
try_lay_trap()
elif Input.is_action_pressed("attack"):
attack()
update_actions()
func attack() -> void:
if attack_timer > 0:
return
if meleeing:
body.play_animation("melee")
body.play_animation.rpc("melee")
attack_timer = melee_recovery_time
else:
if ammo <= 0:
@@ -219,6 +186,50 @@ func attack() -> void:
v.y = 0
body.look_at(body.global_position - v)
func update_actions() -> void:
if input.is_action_just_pressed("left cycle trap"):
cycle_active_trap(-1)
if input.is_action_just_pressed("right cycle trap"):
cycle_active_trap(1)
if input.is_action_just_pressed("detonate"):
detonate()
elif input.is_action_just_pressed("detect"):
start_detecting()
elif input.is_action_just_released("detect"):
stop_detecting()
elif !detecting and input.is_action_just_pressed("lay trap"):
try_lay_trap()
elif input.is_action_pressed("attack"):
attack()
func update_struggle(delta : float) -> bool:
if struggle_timer > 0:
struggle_timer -= delta
var snapped_angle = round(input.dir.angle_to(Vector3(1,0,0)) * 4 / PI) * PI * .25
if abs(snapped_angle - struggle_angle) >= PI * .25:
struggle_angle = snapped_angle
struggle_timer -= 2 * delta
struggling.emit(struggle_timer)
if struggle_timer <= 0:
hurt(1)
knockdown(facing)
else:
return false
return true
func update_poison(delta : float) -> void:
if is_poisoned():
poison_time_remaining -= delta
poison_pulse_timer += delta
if poison_pulse_timer >= 1.0:
poison_pulse_timer -= 1.0
hurt(poison_strength)
if poison_time_remaining <= 0:
var pshader : ShaderMaterial = body.material.next_pass
pshader.set_shader_parameter("strength", 0)
poison_status_changed.emit(false)
func fire_ranged() -> void:
var shot = body.projectile_template.instantiate()
var tdir : Vector3 = Vector3.ZERO
@@ -308,52 +319,6 @@ func clear_action() -> void:
input_locked = false
action_tween = null
func update_detecting() -> void:
var new_square : Vector3i = (global_position - Vector3.ONE * .5).round()
if new_square == current_square:
return
current_square = new_square
var new_squares : Dictionary[Vector3i, bool] = {}
for i in range(-2, 3):
for j in range(-2, 3):
for k in range(-2, 2):
if abs(i) + abs(j) < 3:
var sq = current_square + Vector3i(i, k, j)
new_squares[sq] = true
for sq in detect_squares.keys():
if !new_squares.has(sq):
Game.level.detect_square(sq, false)
var remove_list = []
var trap_detected : bool = false
for sq in new_squares.keys():
if detect_squares.has(sq):
continue
if !Game.level.detect_square(sq, true):
remove_list.append(sq)
else:
var trap = Game.level.get_square_trap(sq + Vector3i(0,1,0))
if trap and trap.is_just_revealed():
trap_detected = true
if trap_detected:
detect_alert()
detect_squares = new_squares
for key in remove_list:
detect_squares.erase(key)
var trap : Trap = Game.level.get_square_trap(current_square)
if trap != null:
if trap.trap_owner == Multiplayer.id:
show_remove_trap_modal()
else:
trap.disarming = true
show_disarm_trap_modal()
func detect_alert() -> void:
detect_sound.play()
@@ -392,26 +357,44 @@ func show_disarm_trap_modal() -> void:
func start_detecting() -> void:
detecting = true
current_square = (global_position - Vector3.ONE * .5).round()
detect_squares = {}
update_detect_region.rpc(false)
@rpc("authority")
func update_detect_region(update : bool) -> void:
var new_square : Vector3i = (global_position - Vector3.ONE * .5).round()
if update and new_square == current_square:
return
current_square = new_square
var new_squares : Dictionary[Vector3i, bool] = {}
for i in range(-2, 3):
for j in range(-2, 3):
for k in range(-2, 2):
if abs(i) + abs(j) < 3:
var sq = current_square + Vector3i(i, k, j)
detect_squares[sq] = true
new_squares[sq] = true
if update:
for sq in detect_squares.keys():
if !new_squares.has(sq):
Game.level.detect_square(sq, false)
var remove_list = []
var trap_detected : bool = false
for sq in detect_squares.keys():
for sq in new_squares.keys():
if update and detect_squares.has(sq):
continue
if !Game.level.detect_square(sq, true):
remove_list.append(sq)
else:
var trap = Game.level.get_square_trap(sq + Vector3i(0,1,0))
if trap and trap.is_just_revealed():
trap_detected = true
if trap_detected:
detect_alert()
detect_squares = new_squares
for key in remove_list:
detect_squares.erase(key)
@@ -419,9 +402,16 @@ func start_detecting() -> void:
if trap != null:
if trap.trap_owner == Multiplayer.id:
show_remove_trap_modal()
else:
trap.disarming = true
show_disarm_trap_modal()
func stop_detecting() -> void:
detecting = false
clear_detect_region()
@rpc("authority")
func clear_detect_region() -> void:
for sq in detect_squares.keys():
Game.level.detect_square(sq, false)
detect_squares = {}
@@ -434,7 +424,7 @@ func setup(id : int, traps : Array, pos : Vector3) -> void:
for trap in traps:
traplist.append(PawnLevelData.TrapData.new(trap.type, trap.qty, trap.qty))
$Data.traps = traplist
set_multiplayer_authority(id)
input.set_multiplayer_authority(id)
struggling.connect(body._on_struggle_changed)
Game.setup_player(self)

55
scripts/pawn_input.gd Normal file
View File

@@ -0,0 +1,55 @@
class_name PawnInput extends Node
@export var dir : Vector3
@export var pressed : Dictionary[String, bool]
@export var just_pressed : Dictionary[String, bool]
@export var just_released : Dictionary[String, bool]
func _ready() -> void:
pressed = {
"left cycle trap":false,
"right cycle trap":false,
"detonate":false,
"detect":false,
"lay trap":false,
"attack":false
}
just_pressed = {
"left cycle trap":false,
"right cycle trap":false,
"detonate":false,
"detect":false,
"lay trap":false,
"attack":false
}
just_released = {
"left cycle trap":false,
"right cycle trap":false,
"detonate":false,
"detect":false,
"lay trap":false,
"attack":false
}
func _physics_process(delta: float) -> void:
var d = Input.get_vector("west", "east", "north", "south")
dir = Vector3(d.x, 0, d.y)
for key : String in pressed:
if Input.is_action_pressed(key):
just_pressed[key] = !pressed[key]
just_released[key] = false
pressed[key] = true
else:
just_released[key] = pressed[key]
just_pressed[key] = false
pressed[key] = false
func is_action_pressed(action : String) -> bool:
return pressed.has(action) && pressed[action]
func is_action_just_pressed(action : String) -> bool:
return just_pressed.has(action) && just_pressed[action]
func is_action_just_released(action : String) -> bool:
return just_released.has(action) && just_released[action]

View File

@@ -0,0 +1 @@
uid://cvuoq81ipid2o

View File

@@ -0,0 +1,8 @@
extends Node3D
@onready var level : Level = $Level
func _ready() -> void:
Game.pawns_selected[1] = load("res://data/pawns/van_reily.tres")
level.setup()

View File

@@ -0,0 +1 @@
uid://6qk4ejmk8nch