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,9 +10,9 @@ enum Modes{
const blinder_template = preload("res://templates/blinder.tscn")
const one_shot_template = preload("res://templates/one_shot.tscn")
const story_level = preload("res://scenes/test_level.tscn")
const story_level = preload("res://scenes/manufactory.tscn")
const vs_com_level = preload("res://scenes/test_level.tscn")
const vs_man_level = preload("res://scenes/test_level.tscn")
const vs_man_level = preload("res://scenes/manufactory.tscn")
const pawn_select_scene = preload("res://scenes/character_select.tscn")
var player : PawnController

View File

@@ -19,7 +19,7 @@ func become_host() -> void:
func(error_msg):
push_error("NodeTunnel Error: ", error_msg)
)
server_peer.connect_to_relay("us_east.nodetunnel.io:8080", "1dl00j5zgo2uxk0")
server_peer.connect_to_relay("74.208.202.81:8080", "net-gunner")
multiplayer.multiplayer_peer = server_peer
print("Authenticating")
await server_peer.authenticated
@@ -35,10 +35,10 @@ func become_host() -> void:
var room_id = await server_peer.room_connected
print("Connected to room: ", server_peer.room_id)
server_peer.get_rooms()
print("GETTING ROOMS!")
var rooms = await server_peer.rooms_received
for r in rooms:
var meta = JSON.parse_string(r.metadata)
#can now use meta.players, meta.name, meta.password_req, etc.
print("GOT ROOMS!")
print(rooms)
players[handle] = 1
player_readiness[1] = false
@@ -48,13 +48,23 @@ func become_host() -> void:
multiplayer.peer_disconnected.connect(_remove_player_from_game)
func join_game(new_handle : String, ip : String = "") -> Error:
print("Player 2 joining.")
if ip == "":
ip = SERVER_IP
var client_peer = ENetMultiplayerPeer.new()
var error = client_peer.create_client(ip, SERVER_PORT)
func join_game(new_handle : String, room : String = "") -> Error:
var client_peer = NodeTunnelPeer.new()
client_peer.error.connect(
func(error_msg):
push_error("NodeTunnel Error: ", error_msg)
)
client_peer.connect_to_relay("74.208.202.81:8080", "net-gunner")
multiplayer.multiplayer_peer = client_peer
print("Authenticating")
await client_peer.authenticated
print("Authenticated")
var error = client_peer.join_room(room)
print("Player 2 joining room :", room)
await client_peer.room_connected
print("Player 2 joined.")
handle = new_handle
id = multiplayer.get_unique_id()
players[handle] = id

View File

@@ -117,7 +117,7 @@ func change_display(player : int, selection: int) -> void:
var pd : PawnDisplay = displays[player]
pd.set_pawn_name(pawns[selection].name)
pd.set_portrait(pawns[selection].portrait)
pd.set_traps(pawns[selection].starting_traps)
pd.set_hacks(pawns[selection].starting_hacks)
func _on_selector_start_child_entered_tree(node: Node) -> void:

View File

@@ -2,7 +2,7 @@ class_name ControlDisplay extends Control
@onready var range_attack_button : TextureButton = $RangedAttack
@onready var melee_attack_button : TextureButton = $MeleeAttack
@onready var trap_button : TextureButton = $Trap
@onready var hack_button : TextureButton = $Hack
@onready var detect_button : TextureButton = $Detect
@onready var switch_button : TextureButton = $Switch
@@ -11,7 +11,7 @@ func _on_melee_range_changed(melee : bool) -> void:
melee_attack_button.visible = melee
func _process(delta: float) -> void:
trap_button.set_pressed_no_signal(Input.is_action_pressed("lay trap"))
hack_button.set_pressed_no_signal(Input.is_action_pressed("install"))
detect_button.set_pressed_no_signal(Input.is_action_pressed("detect"))
switch_button.set_pressed_no_signal(Input.is_action_pressed("detonate"))
melee_attack_button.set_pressed_no_signal(Input.is_action_pressed("attack"))

View File

@@ -2,6 +2,6 @@ extends TextureRect
@onready var qty_label : Label = $Label
func setup(type : Trap.Type, qty : int) -> void:
texture = Trap.trap_icons[type]
func setup(type : Hack.Type, qty : int) -> void:
texture = Hack.hack_icons[type]
qty_label.text = str(qty)

View File

@@ -1,9 +1,9 @@
class_name DisarmTrapModal extends Control
class_name DecompileHackModal extends Control
const action_value : Dictionary[String, int] = {
"detonate" : 0,
"lay trap" : 1,
"install" : 1,
"detect" : 2,
"attack" : 3
}
@@ -14,8 +14,8 @@ const icon_textures : Array = [
"pressed" : preload("res://visuals/images/icons/button-detonate-hit.png")
},
{
"normal" : preload("res://visuals/images/icons/button-trap.png"),
"pressed" : preload("res://visuals/images/icons/button-trap-hit.png")
"normal" : preload("res://visuals/images/icons/button-hack.png"),
"pressed" : preload("res://visuals/images/icons/button-hack-hit.png")
},
{
"normal" : preload("res://visuals/images/icons/button-detect.png"),
@@ -27,52 +27,52 @@ const icon_textures : Array = [
},
]
const disarm_icon_template = preload("res://templates/disarm_icon.tscn")
const decompile_icon_template = preload("res://templates/decompile_icon.tscn")
const success_sound = preload("res://audio/sounds/TomWinandySFX_UI_ScifiTech_Notification_13.wav")
const fail_sound = preload("res://audio/sounds/TomWinandySFX_UI_ScifiTech_Cancel_05.wav")
const progress_sound = preload("res://audio/sounds/metal_tiny_hit_impact_01.wav")
@onready var confirmation_dialog : ConfirmationDialog = %ConfirmationDialog
@onready var disarm_window : PanelContainer = %DisarmWindow
@onready var disarm_button_container : HBoxContainer = %DisarmContainer
@onready var decompile_window : PanelContainer = %DecompileWindow
@onready var decompile_button_container : HBoxContainer = %DecompileContainer
@onready var timer : Timer = %Timer
@onready var timer_label : Label = %TimerLabel
var square : Vector3i
var disarm_icons : Array[TextureButton]
var disarm_buttons : Array[int]
var decompile_icons : Array[TextureButton]
var decompile_buttons : Array[int]
var progress : int
var disarming : bool
var decompiling : bool
var failed : bool
var difficulty : int = 1
func _process(delta: float) -> void:
timer_label.text = "%02f.3" % timer.time_left
func start_disarming() -> void:
disarming = true
func start_decompiling() -> void:
decompiling = true
progress = 0
disarm_icons = []
disarm_buttons = []
decompile_icons = []
decompile_buttons = []
for i in range(Game.level.difficulty):
var btn = randi_range(0,3)
var icon : TextureButton = disarm_icon_template.instantiate()
var icon : TextureButton = decompile_icon_template.instantiate()
icon.texture_normal = icon_textures[btn].normal
icon.texture_pressed = icon_textures[btn].pressed
disarm_buttons.append(btn)
disarm_icons.append(icon)
disarm_button_container.add_child(icon)
decompile_buttons.append(btn)
decompile_icons.append(icon)
decompile_button_container.add_child(icon)
confirmation_dialog.visible = false
disarm_window.visible = true
timer.start(2 + (len(disarm_buttons) * randf_range(.75, 1.25) ))
decompile_window.visible = true
timer.start(2 + (len(decompile_buttons) * randf_range(.75, 1.25) ))
func try_advance(btn : int) -> void:
if btn == disarm_buttons[progress]:
disarm_icons[progress].button_pressed = true
if btn == decompile_buttons[progress]:
decompile_icons[progress].button_pressed = true
progress += 1
if progress >= len(disarm_buttons):
if progress >= len(decompile_buttons):
succeed()
else:
Game.oneshot(progress_sound)
@@ -80,16 +80,16 @@ func try_advance(btn : int) -> void:
fail()
func button_pressed(event : InputEventAction) -> void:
if disarming:
try_advance(action_value[event.action])
func button_pressed(event : String) -> void:
if decompiling:
try_advance(action_value[event])
else:
match(event.action):
match(event):
"detonate":
start_disarming()
start_decompiling()
"attack":
var trap = Game.level.get_square_trap(square)
trap.disarming = false
var hack = Game.level.get_square_hack(square)
hack.decompiling = false
Game.player.close_modal()
_: return
@@ -99,15 +99,15 @@ func fail() -> void:
failed = true
Game.player.close_modal()
Game.oneshot(fail_sound)
Game.level.activate_trap(square)
Game.level.activate_hack(square)
func succeed() -> void:
Game.player.close_modal()
Game.oneshot(success_sound)
Game.level.disarm_trap(square)
Game.level.decompile_hack(square)
func _on_trap_failed() -> void:
func _on_hack_failed() -> void:
fail()
func _on_trap_removed(type : Trap.Type) -> void:
func _on_hack_removed(type : Hack.Type) -> void:
Game.player.close_modal()

View File

@@ -5,7 +5,7 @@ var cycles : int = 3
var square : Vector3i = Vector3i(2,0,0)
var squares : Dictionary[Vector3i, bool] = {}
var last_spread : Array[Vector3i]
var trap_owner : int
var hack_owner : int
var damage : int = 4
func _ready() -> void:
@@ -42,5 +42,5 @@ func spread() -> void:
func emit_gas(square) -> void:
var gas = gas_square_template.instantiate()
gas.setup(trap_owner, damage)
gas.setup(hack_owner, damage)
Game.level.add_vfx(gas, square)

View File

@@ -1,7 +1,7 @@
extends Area3D
@onready var particles : GPUParticles3D = %ParticleFX
var trap_owner : int
var hack_owner : int
var damage : int
var time_remaining : float
@@ -17,7 +17,7 @@ func _physics_process(delta: float) -> void:
queue_free()
func setup(owner, damage) -> void:
self.trap_owner = trap_owner
self.hack_owner = hack_owner
self.damage = damage
time_remaining = 2.0

View File

@@ -1,4 +1,4 @@
class_name Trap extends Area3D
class_name Hack extends Area3D
enum Type{
BOMB,
@@ -10,20 +10,20 @@ enum Type{
}
const range_shapes : Dictionary = {
Trap.Type.BOMB : Vector3(5,1,5),
Trap.Type.GAS : Vector3(1,1,1),
Trap.Type.PITFALL : Vector3(1,1,1),
Trap.Type.FORCE_PANEL : Vector3(1,1,1),
Trap.Type.SWITCH : Vector3(3,1,3),
Trap.Type.MINE : Vector3(5,1,5),
Hack.Type.BOMB : Vector3(4.25,3,4.25),
Hack.Type.GAS : Vector3(1,1,1),
Hack.Type.PITFALL : Vector3(1,1,1),
Hack.Type.FORCE_PANEL : Vector3(1,1,1),
Hack.Type.SWITCH : Vector3(3,1,3),
Hack.Type.MINE : Vector3(4.25,3,4.25),
}
const trap_icons : Dictionary = {
Trap.Type.BOMB : preload("res://visuals/images/icons/t-bomb.png"),
Trap.Type.GAS : preload("res://visuals/images/icons/t-gas.png"),
Trap.Type.PITFALL : preload("res://visuals/images/icons/t-pitfall.png"),
Trap.Type.FORCE_PANEL : preload("res://visuals/images/icons/t-force_panel.png"),
Trap.Type.SWITCH : preload("res://visuals/images/icons/t-switch.png"),
Trap.Type.MINE : preload("res://visuals/images/icons/t-mine.png"),
const hack_icons : Dictionary = {
Hack.Type.BOMB : preload("res://visuals/images/icons/t-bomb.png"),
Hack.Type.GAS : preload("res://visuals/images/icons/t-gas.png"),
Hack.Type.PITFALL : preload("res://visuals/images/icons/t-pitfall.png"),
Hack.Type.FORCE_PANEL : preload("res://visuals/images/icons/t-force_panel.png"),
Hack.Type.SWITCH : preload("res://visuals/images/icons/t-switch.png"),
Hack.Type.MINE : preload("res://visuals/images/icons/t-mine.png"),
}
const bomb_explosion_template = preload("res://templates/explosion.tscn")
@@ -38,18 +38,18 @@ const force_activate_sound = preload("res://audio/sounds/TomWinandySFX_UI_ScifiT
@onready var model : MeshInstance3D = %Model
@onready var icon : Sprite3D = %Icon
@onready var force_strip : Sprite3D = %ForceStrip
@onready var material : StandardMaterial3D = model.get_surface_override_material(0)
@onready var material : ShaderMaterial = model.get_active_material(0)
@onready var reveal_timer : Timer = %RevealTimer
@onready var activation_timer : Timer = %ActivationTimer
var type : Type
var square : Vector3i
var trap_owner : int
var hack_owner : int
var direction : Vector3
var expended : bool = false
var disarming : bool
var disarm_id : int
var decompiling : bool
var decompile_id : int
var delayed_trigger_tween : Tween = null
@@ -58,9 +58,9 @@ var damage : int = 10
var just_revealed : bool = false
signal removed(type : Trap.Type)
signal disarmed(type : Trap.Type)
signal activated(type : Trap.Type)
signal removed(type : Hack.Type)
signal decompiled(type : Hack.Type)
signal activated(type : Hack.Type)
signal revealed(is_visible : bool)
func _enter_tree() -> void:
@@ -69,9 +69,9 @@ func _enter_tree() -> void:
func _exit_tree() -> void:
Game.level.remove_map_marker(self)
func setup(type : Type, direction : Vector3, trap_owner : int) -> void:
func setup(type : Type, direction : Vector3, hack_owner : int) -> void:
self.type = type
self.trap_owner = trap_owner
self.hack_owner = hack_owner
if type == Type.FORCE_PANEL:
var r : float = atan2(direction.z, direction.x)
var cardinal : float = roundi(r * 2 / PI) * PI / 2
@@ -81,8 +81,8 @@ func remove() -> void:
removed.emit(type)
queue_free()
func disarm() -> void:
disarmed.emit(type)
func decompile() -> void:
decompiled.emit(type)
queue_free()
func reveal() -> void:
@@ -100,19 +100,20 @@ func is_revealed() -> bool:
return model.visible
func _on_reveal_timeout() -> void:
if Game.level.is_square_detected(square) or disarming:
if Game.level.is_square_detected(square) or decompiling:
reveal_timer.start(5)
else:
model.visible = false
revealed.emit(false)
func _ready() -> void:
var owns_trap = trap_owner == Multiplayer.id
icon.texture = trap_icons[type]
model.visible = owns_trap
icon.visible = owns_trap
var owns_hack = hack_owner == Multiplayer.id
print("Setup Hack " + name)
material.set_shader_parameter("glow_color", Color.YELLOW if owns_hack else Color.RED)
icon.texture = hack_icons[type]
model.visible = owns_hack
icon.visible = owns_hack
range_shape.size = range_shapes[type]
material.albedo_color = Color.YELLOW if owns_trap else Color.RED
match(type):
Type.BOMB:
damage = 15
@@ -125,7 +126,7 @@ func _ready() -> void:
Type.FORCE_PANEL:
var r : float = atan2(direction.z, -direction.x) + PI/2
force_strip.rotate_y(r)
force_strip.visible = owns_trap
force_strip.visible = owns_hack
func _process(delta: float) -> void:
just_revealed = false
@@ -162,7 +163,7 @@ func activate() -> void:
explode = true
Type.GAS:
var emitter = gas_emitter_template.instantiate()
emitter.trap_owner = trap_owner
emitter.hack_owner = hack_owner
emitter.square = square
emitter.damage = 4
Game.level.add_vfx(emitter, square)
@@ -172,14 +173,14 @@ func activate() -> void:
blast_players()
expended = true
Game.level.remove_trap_square(square)
Game.level.uninstall_hack_square(square)
activated.emit(type)
queue_free()
func trigger_adjacent_bombs() -> void:
for trap : Trap in range_area.get_overlapping_areas():
if trap.type == Type.BOMB and trap != self:
trap.delay_trigger()
for hack : Hack in range_area.get_overlapping_areas():
if hack.type == Type.BOMB and hack != self:
hack.delay_trigger()
func generate_pitfall(suqare : Vector3i) -> void:
var pitfall = pitfall_template.instantiate()
@@ -202,10 +203,10 @@ func _on_body_entered(body: Node3D) -> void:
if type == Type.GAS or type == Type.BOMB:
return
if body.id == trap_owner:
if body.id == hack_owner:
return
if !disarming or body.id != disarm_id:
if !decompiling or body.id != decompile_id:
if !body.detecting:
activate()

28
scripts/hack_display.gd Normal file
View File

@@ -0,0 +1,28 @@
class_name HackDisplay extends PanelContainer
const hack_hud_icon = preload("res://templates/hack_icon.tscn")
@onready var hack_container : HBoxContainer = %HacksContainer
@onready var cycle_sound : AudioStreamPlayer = %CycleSound
func clear_hacks() -> void:
for child in hack_container.get_children():
child.queue_free()
func _on_hack_list_changed(hacks, hack_index) -> void:
clear_hacks()
for hack in hacks:
var ticon = hack_hud_icon.instantiate()
hack_container.add_child(ticon)
ticon.setup(hack.type, hack.quantity)
%LeftArrow.visible = (hack_index != 0)
%RightArrow.visible = (hack_index != hack_container.get_children().size() - 1)
func _on_hack_quantity_changed(hack_index, qty) -> void:
var hicon = hack_container.get_child(hack_index) as HackIcon
hicon.set_quantity(qty)
func _on_hack_cycled(hack_index) -> void:
hack_container.position.x = -hack_index * 150
%LeftArrow.visible = (hack_index != 0)
%RightArrow.visible = (hack_index != hack_container.get_children().size() - 1)
cycle_sound.play()

24
scripts/hack_icon.gd Normal file
View File

@@ -0,0 +1,24 @@
class_name HackIcon extends Control
const hack_icons : Dictionary = {
Hack.Type.BOMB : preload("res://visuals/images/icons/bomb.png"),
Hack.Type.GAS : preload("res://visuals/images/icons/gas.png"),
Hack.Type.PITFALL : preload("res://visuals/images/icons/pitfall.png"),
Hack.Type.FORCE_PANEL : preload("res://visuals/images/icons/force_panel.png"),
Hack.Type.SWITCH : preload("res://visuals/images/icons/switch.png"),
Hack.Type.MINE : preload("res://visuals/images/icons/mine.png"),
}
@onready var icon_image : TextureRect = %Icon
@onready var qty_label : Label = %Label
func setup(type : Hack.Type, qty : int) -> void:
icon_image.texture = hack_icons[type]
set_quantity(qty)
func set_quantity(qty : int) -> void:
qty_label.text = str(qty)
if qty < 1:
icon_image.modulate = Color.DIM_GRAY
else:
icon_image.modulate = Color.WHITE

View File

@@ -1,6 +1,6 @@
class_name HUD extends Control
@onready var trap_display : TrapDisplay = %TrapDisplay
@onready var hack_display : HackDisplay = %HackDisplay
@onready var p1_healthbar : HealthBar = %P1HealthBar
@onready var p2_healthbar : HealthBar = %P2HealthBar
@onready var control_display : ControlDisplay = %ControlDisplay
@@ -13,10 +13,10 @@ func register_pawn(player : PawnController, is_local : bool) -> void:
player.health_changed.connect(p1_healthbar._on_health_changed if is_local else p2_healthbar._on_health_changed)
player.poison_status_changed.connect(p1_healthbar._on_poison_status_changed if is_local else p2_healthbar._on_poison_status_changed)
if is_local:
trap_display._on_trap_list_changed(player.data.traps, player.data.active_trap)
player.trap_cycled.connect(trap_display._on_trap_cycled)
player.trap_quantity_changed.connect(trap_display._on_trap_quantity_changed)
player.trap_list_changed.connect(trap_display._on_trap_list_changed)
hack_display._on_hack_list_changed(player.data.hacks, player.data.active_hack)
player.hack_cycled.connect(hack_display._on_hack_cycled)
player.hack_quantity_changed.connect(hack_display._on_hack_quantity_changed)
player.hack_list_changed.connect(hack_display._on_hack_list_changed)
player.combat_target_changed.connect(control_display._on_melee_range_changed)
player.ammo_changed.connect(_on_ammo_changed)
set_ammo(player.ammo)

View File

@@ -3,14 +3,14 @@ class_name Level extends Node3D
const map_marker_templates : Dictionary[String, PackedScene] = {
"pawn" : preload("res://templates/markers/pawn_marker.tscn"),
"friendly_trap":preload("res://templates/markers/friendly_trap_marker.tscn"),
"enemy_trap": preload("res://templates/markers/enemy_trap_marker.tscn"),
"friendly_hack":preload("res://templates/markers/friendly_hack_marker.tscn"),
"enemy_hack": preload("res://templates/markers/enemy_hack_marker.tscn"),
"item": preload("res://templates/markers/item_marker.tscn")
}
const pawn_controller = preload("res://templates/pawn_controller.tscn")
const trap_template = preload("res://templates/trap.tscn")
const hack_template = preload("res://templates/hack.tscn")
const camera_template = preload("res://templates/pawn_camera.tscn")
const camera_offset = Vector3(0, 10, 5.25)
@@ -20,12 +20,15 @@ const camera_offset = Vector3(0, 10, 5.25)
@onready var ramp_layer : GridMap = %Ramps
@onready var marker_layer : GridMap = %Markers
@onready var pawns_node : Node3D = %Pawns
@onready var traps_node : Node3D = %Traps
@onready var hacks_node : Node3D = %Hacks
@onready var cameras_node : Node3D = %Cameras
@onready var map_markers_node : Node3D = %MapMarkers
@onready var spawn_points : Array[Node] = $PawnSpawner.get_children()
@export var difficulty : int = 1
var traps : Dictionary[Vector3i, Trap] = {}
var hacks : Dictionary[Vector3i, Hack] = {}
var pawns : Dictionary[int, PawnController] = {}
var cameras : Dictionary[int, PawnCamera] = {}
var map_markers : Dictionary[Object, MapMarker] = {}
@@ -34,33 +37,38 @@ func _ready() -> void:
Game.level = self
if Game.mode == Game.Modes.STORY:
setup()
add_level_trap.call_deferred(Trap.Type.MINE, Vector3.ZERO, Vector3i(5,0,3))
add_level_hack.call_deferred(Hack.Type.MINE, Vector3.ZERO, Vector3i(-2,2,0))
func setup() -> void:
if Multiplayer.is_host():
spawn_players()
func spawn_players() -> void:
var count = 0
for key in Game.pawns_selected:
var pc : PawnController = pawn_controller.instantiate()
var pd = Game.pawns_selected[key]
pawns_node.add_child(pc,true)
var traps : Array = []
for trap : TrapSet in pd.starting_traps:
var hacks : Array = []
for hack : HackSet in pd.starting_hacks:
var dict = {
"type":trap.type,
"qty":trap.qty
"type":hack.type,
"qty":hack.qty
}
traps.append(dict)
hacks.append(dict)
var position : Vector3
if key == Multiplayer.id:
position = Vector3(0,0,0)
if(spawn_points == null
or len(spawn_points) <= count):
if key == Multiplayer.id:
position = Vector3(0,0,0)
else:
position = Vector3(3,0,0)
else:
position = Vector3(3,0,0)
pc.setup.rpc(key,traps,position)
position = spawn_points[count].global_position
pc.setup.rpc(key,hacks,position)
add_pawn_camera(pc)
count += 1
func is_square_detected(crd) -> bool:
@@ -73,11 +81,11 @@ func detect_square(crd : Vector3i, mark : bool) -> bool:
marker_layer.set_cell_item(crd, 0 if mark else GridMap.INVALID_CELL_ITEM)
if mark:
var trap_crd = crd + Vector3i(0,1,0)
if traps.has(trap_crd):
var trap : Trap = traps[trap_crd]
if trap.trap_owner != Multiplayer.id:
trap.reveal()
var hack_crd = crd + Vector3i(0,1,0)
if hacks.has(hack_crd):
var hack : Hack = hacks[hack_crd]
if hack.hack_owner != Multiplayer.id:
hack.reveal()
return true
@rpc("any_peer", "call_local", "reliable")
@@ -90,30 +98,30 @@ func add_pawn_camera(pawn : PawnController) -> void:
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, true)
func add_hack(hack : Hack, crd : Vector3i) -> void:
hack.square = crd
hacks[crd] = hack
hack.position = Vector3(crd) + Vector3(.5, 0, .5)
add_child(hack, true)
func add_projectile(shot : Projectile) -> void:
add_child(shot, true)
func remove_trap_square(crd : Vector3i) -> void:
traps.erase(crd)
func uninstall_hack_square(crd : Vector3i) -> void:
hacks.erase(crd)
func get_square_trap(crd : Vector3i) -> Trap:
if traps.has(crd):
return traps[crd]
func get_square_hack(crd : Vector3i) -> Hack:
if hacks.has(crd):
return hacks[crd]
else:
return null
func is_valid_trap_square(crd : Vector3i) -> bool:
func is_valid_hack_square(crd : Vector3i) -> bool:
if floor_layer.get_cell_item(crd + Vector3i(0,-1,0)) == GridMap.INVALID_CELL_ITEM:
return false
if traps.has(crd):
if hacks.has(crd):
return false
return true
@@ -121,20 +129,20 @@ func add_vfx(vfx, crd : Vector3i) -> void:
vfx.position = Vector3(crd) #+ Vector3(0.5, 0, 0.5)
add_child(vfx, true)
func activate_trap(crd : Vector3i) -> void:
if traps.has(crd):
traps[crd].activate()
func activate_hack(crd : Vector3i) -> void:
if hacks.has(crd):
hacks[crd].activate()
func generate_trap(type : Trap.Type, dir : Vector3, square : Vector3i):
var trap = trap_template.instantiate()
trap.setup(type, dir, -1)
add_trap(trap, square)
func generate_hack(type : Hack.Type, dir : Vector3, square : Vector3i):
var hack = hack_template.instantiate()
hack.setup(type, dir, -1)
add_hack(hack, square)
func disarm_trap(crd : Vector3i) -> void:
var trap = traps[crd]
trap.disarm()
traps.erase(crd)
func decompile_hack(crd : Vector3i) -> void:
var hack = hacks[crd]
hack.decompile()
hacks.erase(crd)
func remove_map_marker(target) -> void:
if !map_markers.has(target):
@@ -149,13 +157,13 @@ func add_map_marker(target) -> void:
if target is PawnController:
marker = map_marker_templates.pawn.instantiate()
height += 40
elif target is Trap:
if target.trap_owner == Multiplayer.id:
marker = map_marker_templates.friendly_trap.instantiate()
elif target is Hack:
if target.hack_owner == Multiplayer.id:
marker = map_marker_templates.friendly_hack.instantiate()
else:
marker = map_marker_templates.enemy_trap.instantiate()
marker = map_marker_templates.enemy_hack.instantiate()
marker.visible = false
target.revealed.connect(marker._on_trap_revealed)
target.revealed.connect(marker._on_hack_revealed)
height += 30
elif target is Item:
marker = map_marker_templates.item.instantiate()
@@ -165,7 +173,7 @@ func add_map_marker(target) -> void:
map_markers_node.add_child(marker)
marker.position += Vector3(0, height,0)
func add_level_trap(type : Trap.Type, facing : Vector3, crd : Vector3i) -> void:
var trap = trap_template.instantiate()
trap.setup(type, facing, -1)
add_trap(trap, crd)
func add_level_hack(type : Hack.Type, facing : Vector3, crd : Vector3i) -> void:
var hack = hack_template.instantiate()
hack.setup(type, facing, -1)
add_hack(hack, crd)

View File

@@ -14,5 +14,5 @@ func _process(delta: float) -> void:
if target:
reposition()
func _on_trap_revealed(is_visible : bool) -> void:
func _on_hack_revealed(is_visible : bool) -> void:
visible = is_visible

View File

@@ -16,4 +16,4 @@ extends Level
#var pc : PawnController = pawn_controller_template.instantiate()
#var pd = Game.pawns_selected[key]
#player_spawner.add_pawn(pc, key)
#pc.setup(key,pd.starting_traps)
#pc.setup(key,pd.starting_hacks)

View File

@@ -4,20 +4,47 @@ class_name PawnBody extends Node3D
@onready var anim_player : AnimationPlayer = %AnimationPlayer
@onready var ranged_point : Node3D = %RangedPoint
@onready var material : StandardMaterial3D = $Mesh.material
#@onready var material : StandardMaterial3D = $Mesh.material
@onready var struggle_ui : Sprite3D = %StruggleUI
@onready var struggle_bar : ProgressBar = %StruggleBar
@onready var anim_tree : AnimationTree = %AnimationTree
@onready var anim_machine : AnimationNodeStateMachinePlayback = %AnimationTree.get("parameters/playback")
@onready var walk_sound : AudioStreamPlayer3D = $WalkSound
@onready var footstep_timer : Timer = $FootstepTimer
@rpc("call_local")
func play_animation(anim_name : String) -> void:
anim_player.play(anim_name)
@rpc("call_local")
func travel_animation(node_name : String) -> void:
anim_machine.travel(node_name)
@rpc("call_local")
func show_struggle() -> void:
struggle_ui.visible = true
func set_animation_parameter(parameter : String, value : Variant) -> void:
anim_tree.set(parameter, value)
func stop_footsteps() -> void:
footstep_timer.stop()
func play_footsteps(step_time : float = 1) -> void:
if footstep_timer.is_stopped():
walk_sound.play()
footstep_timer.start(step_time)
elif footstep_timer.wait_time != step_time:
footstep_timer.wait_time = step_time
func _on_struggle_changed(value : float) -> void:
if value <= 0:
struggle_ui.visible = false
else:
struggle_bar.value = value
func _on_footstep_timer_timeout() -> void:
walk_sound.play()

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

View File

@@ -1,23 +1,23 @@
class_name PawnDisplay extends Panel
@onready var pawn_name : Label = %PawnName
@onready var trap_container : HBoxContainer = %TrapContainer
@onready var hack_container : HBoxContainer = %HackContainer
@onready var portrait : TextureRect = $Portrait
@onready var portrait_blinder : TextureRect = $PortraitBlinder
var traps : Array = []
var hacks : Array = []
var swap_portrait_tween : Tween = null
func _ready() -> void:
for child in trap_container.get_children():
traps.append(child)
for child in hack_container.get_children():
hacks.append(child)
func set_traps(trap_list : Array[TrapSet]) -> void:
var tcount = 0
if len(trap_list) != 3:
func set_hacks(hack_list : Array[HackSet]) -> void:
var hcount = 0
if len(hack_list) != 3:
return
for trap in traps:
trap.setup(trap_list[tcount].type, trap_list[tcount].qty)
tcount+=1
for hack in hacks:
hack.setup(hack_list[hcount].type, hack_list[hcount].qty)
hcount+=1
func set_portrait(picture : Texture2D) -> void:
if swap_portrait_tween != null and swap_portrait_tween.is_running():

View File

@@ -1,4 +1,4 @@
class_name PawnInput extends Node
class_name PawnInput extends MultiplayerSynchronizer
@export var dir : Vector3
@@ -8,31 +8,33 @@ class_name PawnInput extends Node
func _ready() -> void:
pressed = {
"left cycle trap":false,
"right cycle trap":false,
"left cycle hack":false,
"right cycle hack":false,
"detonate":false,
"detect":false,
"lay trap":false,
"install":false,
"attack":false
}
just_pressed = {
"left cycle trap":false,
"right cycle trap":false,
"left cycle hack":false,
"right cycle hack":false,
"detonate":false,
"detect":false,
"lay trap":false,
"install":false,
"attack":false
}
just_released = {
"left cycle trap":false,
"right cycle trap":false,
"left cycle hack":false,
"right cycle hack":false,
"detonate":false,
"detect":false,
"lay trap":false,
"install":false,
"attack":false
}
func _physics_process(delta: float) -> void:
if get_multiplayer_authority() != Multiplayer.id:
return
var d = Input.get_vector("west", "east", "north", "south")
dir = Vector3(d.x, 0, d.y)
for key : String in pressed:
@@ -45,6 +47,8 @@ func _physics_process(delta: float) -> void:
just_pressed[key] = false
pressed[key] = false
func is_action_pressed(action : String) -> bool:
return pressed.has(action) && pressed[action]

View File

@@ -1,17 +1,17 @@
class_name PawnLevelData extends Node
class TrapData:
var type : Trap.Type
class HackData:
var type : Hack.Type
var quantity : int
var max : int
func _init(type : Trap.Type, quantity : int, max : int) -> void:
func _init(type : Hack.Type, quantity : int, max : int) -> void:
self.type = type
self.quantity = quantity
self.max = max
var traps : Array[TrapData] = []
var active_trap : int = 0
var hacks : Array[HackData] = []
var active_hack : int = 0
var life : int = 100
var max_life : int = 100

5
scripts/range_sphere.gd Normal file
View File

@@ -0,0 +1,5 @@
class_name RangeSphere extends CSGSphere3D
func set_range(range : int) -> void:
radius = range

View File

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

View File

@@ -1,12 +0,0 @@
class_name RemoveTrapModal extends ConfirmationDialog
var square : Vector3i
func button_pressed(event : InputEventAction) -> void:
match(event.action):
"detonate":
Game.player.remove_trap_at(square)
Game.player.close_modal()
"attack":
Game.player.close_modal()
_: return

View File

@@ -1,28 +0,0 @@
class_name TrapDisplay extends PanelContainer
const trap_hud_icon = preload("res://templates/trap_icon.tscn")
@onready var trap_container : HBoxContainer = %TrapsContainer
@onready var cycle_sound : AudioStreamPlayer = %CycleSound
func clear_traps() -> void:
for child in trap_container.get_children():
child.queue_free()
func _on_trap_list_changed(traps, trap_index) -> void:
clear_traps()
for trap in traps:
var ticon = trap_hud_icon.instantiate()
trap_container.add_child(ticon)
ticon.setup(trap.type, trap.quantity)
%LeftArrow.visible = (trap_index != 0)
%RightArrow.visible = (trap_index != trap_container.get_children().size() - 1)
func _on_trap_quantity_changed(trap_index, qty) -> void:
var ticon = trap_container.get_child(trap_index) as TrapIcon
ticon.set_quantity(qty)
func _on_trap_cycled(trap_index) -> void:
trap_container.position.x = -trap_index * 150
%LeftArrow.visible = (trap_index != 0)
%RightArrow.visible = (trap_index != trap_container.get_children().size() - 1)
cycle_sound.play()

View File

@@ -1,24 +0,0 @@
class_name TrapIcon extends Control
const trap_icons : Dictionary = {
Trap.Type.BOMB : preload("res://visuals/images/icons/bomb.png"),
Trap.Type.GAS : preload("res://visuals/images/icons/gas.png"),
Trap.Type.PITFALL : preload("res://visuals/images/icons/pitfall.png"),
Trap.Type.FORCE_PANEL : preload("res://visuals/images/icons/force_panel.png"),
Trap.Type.SWITCH : preload("res://visuals/images/icons/switch.png"),
Trap.Type.MINE : preload("res://visuals/images/icons/mine.png"),
}
@onready var icon_image : TextureRect = %Icon
@onready var qty_label : Label = %Label
func setup(type : Trap.Type, qty : int) -> void:
icon_image.texture = trap_icons[type]
set_quantity(qty)
func set_quantity(qty : int) -> void:
qty_label.text = str(qty)
if qty < 1:
icon_image.modulate = Color.DIM_GRAY
else:
icon_image.modulate = Color.WHITE

View File

@@ -0,0 +1,12 @@
class_name UninstallHackModal extends ConfirmationDialog
var square : Vector3i
func button_pressed(button : String) -> void:
match(button):
"detonate":
Game.player.uninstall_hack_at(square)
Game.player.close_modal()
"attack":
Game.player.close_modal()
_: return