Extensive work on virtually all of the visuals and the net code

This commit is contained in:
2026-03-01 21:26:31 -05:00
parent e7570c78c3
commit bed068eafc
180 changed files with 46533 additions and 913 deletions

View File

@@ -10,16 +10,17 @@ enum State {
}
const trap_template = preload("res://templates/trap.tscn")
const remove_trap_modal = preload("res://templates/remove_trap_modal.tscn")
const disarm_trap_modal = preload("res://templates/disarm_trap_modal.tscn")
const hack_template = preload("res://templates/hack.tscn")
const uninstall_hack_modal = preload("res://templates/uninstall_hack_modal.tscn")
const decompile_hack_modal = preload("res://templates/decompile_hack_modal.tscn")
const range_sphere_template = preload("res://templates/range_sphere.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 hack_sound : AudioStreamPlayer3D = $HackSound
@onready var detonate_sound : AudioStreamPlayer3D = $DetonateSound
@onready var detect_sound : AudioStreamPlayer3D = $DetectSound
@onready var fling_sound : AudioStreamPlayer3D = $FlingSound
@@ -34,6 +35,9 @@ var button_actions : Dictionary[int, String]
var current_square : Vector3i
var facing : Vector3
@export var detecting : bool = false
var installing : bool = false
var uninstalling : bool = false
var range_sphere : RangeSphere
var detect_squares : Dictionary[Vector3i, bool] = {}
var detect_tween : Tween = null
var fling_direction : Vector3
@@ -57,6 +61,9 @@ var ammo = 5
var max_ammo = 5
var combat_target
var meleeing : bool = false
var shooting : bool = false
var flinch : float = 0
var input_locked : bool = false
var action_tween : Tween = null
@@ -66,9 +73,9 @@ var camera : PawnCamera = null
var modal = null
signal trap_cycled(trap_index : int)
signal trap_quantity_changed(trap_index : int, quantity : int)
signal trap_list_changed(traps)
signal hack_cycled(hack_index : int)
signal hack_quantity_changed(hack_index : int, quantity : int)
signal hack_list_changed(hacks)
signal health_changed(current : int, max : int)
signal harmed()
signal poison_status_changed(poisoned : bool)
@@ -82,6 +89,11 @@ func _enter_tree() -> void:
func _exit_tree() -> void:
Game.level.remove_map_marker(self)
#if id == Multiplayer.id:
#Game.level.show_loss_screen()
#else:
#Game.level.evaluate_outcome()
#Game.evaluate
func _physics_process(delta: float) -> void:
if attack_timer > 0:
@@ -102,6 +114,7 @@ func _physics_process(delta: float) -> void:
var moving : bool = false
match(state):
State.FLUNG:
body.stop_footsteps()
can_fall = true
moving = true
dir = fling_direction
@@ -126,15 +139,26 @@ func _physics_process(delta: float) -> void:
moving = false
State.NORMAL:
can_fall = true
if dir.length_squared() > 0:
moving = true
facing = dir.normalized()
body.look_at(body.global_position - dir)
var y = velocity.y
velocity = speed * dir
if is_poisoned():
velocity *= 0.5
if detecting:
velocity *= .33
velocity.y = y
if !detecting:
body.play_footsteps(lerp(.78, .33, dir.length()))
else:
body.stop_footsteps()
else:
body.stop_footsteps()
body.set_animation_parameter("parameters/Motion/blend_position", dir.length())
body.set_animation_parameter("parameters/Crouch/blend_position", dir.length())
State.KNOCKUP:
if is_on_floor():
knockdown(facing)
@@ -161,7 +185,7 @@ func _physics_process(delta: float) -> void:
if !input_locked:
if modal:
var buttons = [
"lay trap",
"install",
"detect",
"attack",
"detonate"
@@ -189,7 +213,7 @@ func attack() -> void:
ammo-=1
ammo_changed.emit(ammo, max_ammo)
attack_timer = ranged_recovery_time
body.play_animation("shoot")
shooting == true
if combat_target != null:
var v = body.global_position.direction_to(combat_target.global_position)
v.y = 0
@@ -197,20 +221,39 @@ func attack() -> void:
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 attack_timer <= 0 and shooting and !input.is_action_pressed("attack"):
shooting = false
if input.is_action_just_pressed("left cycle hack"):
cycle_active_hack(-1)
if input.is_action_just_pressed("right cycle hack"):
cycle_active_hack(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()
elif !installing:
if input.is_action_just_pressed("detect"):
start_detecting()
elif input.is_action_just_released("detect"):
stop_detecting()
if!detecting:
if input.is_action_just_pressed("install"):
installing = true
if range_sphere != null:
range_sphere.queue_free()
range_sphere = range_sphere_template.instantiate()
#Set the range sphere size based upon the current hack
update_range_sphere()
if input.is_action_just_released("install"):
installing = false
try_install_hack()
elif input.is_action_pressed("attack"):
attack()
elif input.is_action_just_released("install"):
installing = false
try_install_hack()
func update_range_sphere() -> void:
pass
func update_struggle(delta : float) -> bool:
if struggle_timer > 0:
@@ -295,34 +338,35 @@ func check_attack_target() -> void:
combat_target = ranged_closest
combat_target_changed.emit(melee_closest != null)
func try_lay_trap() -> void:
func try_install_hack() -> void:
if !is_on_floor():
return
if data.traps[data.active_trap].quantity < 1:
if data.hacks[data.active_hack].quantity < 1:
return
var square : Vector3i = (global_position - Vector3.ONE * .5).round()
if !Game.level.is_valid_trap_square(square):
if !Game.level.is_valid_hack_square(square):
return
action_tween = create_tween()
input_locked = true
body.travel_animation("Install")
action_tween.tween_interval(.2)
action_tween.tween_callback(Callable(lay_trap).bind(square, data.active_trap))
action_tween.tween_callback(Callable(install_hack).bind(square, data.active_hack))
action_tween.tween_interval(.25)
action_tween.tween_callback(clear_action)
func lay_trap(square : Vector3i, idx : int) -> void:
var type : Trap.Type = data.traps[idx].type
var trap = trap_template.instantiate()
trap.setup(type, facing, id)
trap.disarmed.connect(_on_trap_disarmed)
trap.activated.connect(_on_trap_activated)
data.traps[idx].quantity -= 1
trap_quantity_changed.emit(idx, data.traps[idx].quantity)
Game.level.add_trap(trap, square)
trap_sound.play()
func install_hack(square : Vector3i, idx : int) -> void:
var type : Hack.Type = data.hacks[idx].type
var hack = hack_template.instantiate()
hack.setup(type, facing, id)
hack.decompiled.connect(_on_hack_decompiled)
hack.activated.connect(_on_hack_activated)
data.hacks[idx].quantity -= 1
hack_quantity_changed.emit(idx, data.hacks[idx].quantity)
Game.level.add_hack(hack, square)
hack_sound.play()
func clear_action() -> void:
input_locked = false
@@ -340,28 +384,31 @@ func detect_alert() -> void:
detect_tween.tween_callback(func(): detect_tween = null)
func close_modal() -> void:
uninstalling = false
if modal != null:
modal.queue_free()
modal = null
func show_remove_trap_modal() -> void:
func show_uninstall_hack_modal() -> void:
stop_detecting()
modal = remove_trap_modal.instantiate()
uninstalling = true
modal = uninstall_hack_modal.instantiate()
modal.square = current_square
Game.level.add_child(modal)
func show_disarm_trap_modal() -> void:
func show_decompile_hack_modal() -> void:
stop_detecting()
modal = disarm_trap_modal.instantiate()
uninstalling = true
modal = decompile_hack_modal.instantiate()
modal.difficulty = Game.level.difficulty
modal.square = current_square
var trap = Game.level.traps[current_square]
trap.disarming = true
trap.disarm_id = Multiplayer.id
trap.removed.connect(modal._on_trap_removed)
trap.activated.connect(modal._on_trap_failed)
harmed.connect(modal._on_trap_failed)
var hack = Game.level.hacks[current_square]
hack.decompiling = true
hack.decompile_id = Multiplayer.id
hack.removed.connect(modal._on_hack_removed)
hack.activated.connect(modal._on_hack_failed)
harmed.connect(modal._on_hack_failed)
Game.level.add_child(modal)
func start_detecting() -> void:
@@ -390,18 +437,18 @@ func update_detect_region(update : bool) -> void:
Game.level.detect_square(sq, false)
var remove_list = []
var trap_detected : bool = false
var hack_detected : bool = false
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
var hack = Game.level.get_square_hack(sq + Vector3i(0,1,0))
if hack and hack.is_just_revealed():
hack_detected = true
if trap_detected:
if hack_detected:
detect_alert()
detect_squares = new_squares
@@ -409,13 +456,13 @@ func update_detect_region(update : bool) -> void:
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()
var hack : Hack = Game.level.get_square_hack(current_square)
if hack != null:
if hack.hack_owner == Multiplayer.id:
show_uninstall_hack_modal()
else:
trap.disarming = true
show_disarm_trap_modal()
hack.decompiling = true
show_decompile_hack_modal()
func stop_detecting() -> void:
detecting = false
@@ -430,39 +477,39 @@ func clear_detect_region() -> void:
detect_squares = {}
@rpc("authority", "call_local", "reliable")
func setup(id : int, traps : Array, pos : Vector3) -> void:
func setup(id : int, hacks : Array, pos : Vector3) -> void:
self.id = id
self.global_position = pos
var traplist : Array[PawnLevelData.TrapData] = []
for trap in traps:
traplist.append(PawnLevelData.TrapData.new(trap.type, trap.qty, trap.qty))
$Data.traps = traplist
var hacklist : Array[PawnLevelData.HackData] = []
for hack in hacks:
hacklist.append(PawnLevelData.HackData.new(hack.type, hack.qty, hack.qty))
$Data.hacks = hacklist
input.set_multiplayer_authority(id)
struggling.connect(body._on_struggle_changed)
Game.setup_player(self)
func remove_trap_at(square) -> void:
var trap : Trap = Game.level.traps[square]
for i in range(len(data.traps)):
var d = data.traps[i]
if d.type == trap.type:
func uninstall_hack_at(square) -> void:
var hack : Hack = Game.level.hacks[square]
for i in range(len(data.hacks)):
var d = data.hacks[i]
if d.type == hack.type:
d.quantity += 1
trap_quantity_changed.emit(i, d.quantity)
hack_quantity_changed.emit(i, d.quantity)
break
trap.remove()
Game.level.traps.erase(square)
hack.remove()
Game.level.hacks.erase(square)
func cycle_active_trap(dir) -> void:
var prev = data.active_trap
data.active_trap += dir
if data.active_trap < 0:
data.active_trap = 0
func cycle_active_hack(dir) -> void:
var prev = data.active_hack
data.active_hack += dir
if data.active_hack < 0:
data.active_hack = 0
if data.active_trap >= len(data.traps):
data.active_trap = len(data.traps) - 1
if data.active_hack >= len(data.hacks):
data.active_hack = len(data.hacks) - 1
if prev != data.active_trap:
trap_cycled.emit(data.active_trap)
if prev != data.active_hack:
hack_cycled.emit(data.active_hack)
func can_hurt() -> bool:
return true
@@ -476,38 +523,49 @@ func hurt(damage : int) -> void:
struggling.emit(struggle_timer)
knockdown(Vector3(0,0,-1))
health_changed.emit(data.life, data.max_life)
if data.life == 0:
die.rpc()
@rpc("any_peer", "call_local")
func die() -> void:
state = State.DEAD
input_locked = true
if id == Multiplayer.id:
var death_tween = create_tween()
death_tween.tween_interval(3)
death_tween.tween_callback(queue_free)
pass
func _on_trap_disarmed(type : Trap.Type) -> void:
for i in range(len(data.traps)):
var d = data.traps[i]
func _on_hack_decompiled(type : Hack.Type) -> void:
for i in range(len(data.hacks)):
var d = data.hacks[i]
if d.type == type:
d.max -= 1
trap_quantity_changed.emit(i, d.quantity)
hack_quantity_changed.emit(i, d.quantity)
break
#hurt
#blast
#blast_players
#activate
#activate_trap
#activate_hack
#fail
#on_trap_failed
func _on_trap_activated(type : Trap.Type) -> void:
for i in range(len(data.traps)):
var d = data.traps[i]
#on_hack_failed
func _on_hack_activated(type : Hack.Type) -> void:
for i in range(len(data.hacks)):
var d = data.hacks[i]
if d.type == type:
d.quantity = min(d.max, d.quantity+ 1)
trap_quantity_changed.emit(i, d.quantity)
hack_quantity_changed.emit(i, d.quantity)
break
func detonate() -> void:
var switch_list = []
for trap : Trap in Game.level.traps.values():
if trap.type == Trap.Type.SWITCH and trap.trap_owner == id:
switch_list.append(trap)
for hack : Hack in Game.level.hacks.values():
if hack.type == Hack.Type.SWITCH and hack.hack_owner == id:
switch_list.append(hack)
detonate_sound.play()
for trap : Trap in switch_list:
trap.activate()
for hack : Hack in switch_list:
hack.activate()
func is_poisoned() -> bool:
return poison_time_remaining > 0
@@ -553,7 +611,13 @@ func knockup(velocity : Vector3) -> void:
move_and_slide()
self.velocity = velocity
func knockback(direction : Vector3, impact : float) -> void:
if state != State.NORMAL:
return
flinch += impact
if flinch > 0:
knockdown(direction)
#ELSE DO A KNOCKBACK ANIMATION
func pitfall(duration : float) -> void:
struggle_timer = duration
@@ -568,3 +632,15 @@ func start_pitfall(square : Vector3, duration : float) -> void:
tween.tween_property(self, "position", square + Vector3(.5,0,.5), 0.25)
tween.tween_property(self, "input_locked", false, 0)
tween.tween_callback(pitfall.bind(duration))
func _on_melee_hit(body : Node3D) -> void:
pass
func is_dead() -> bool:
return state == State.DEAD
func is_detecting() -> bool:
return detecting
func is_crouching() -> bool:
return detecting or installing or uninstalling