Renamed a bunch of player stuff to pawn stuff and implemented extensive work on getting single-address 'netplay' code working. Not sure if I've created issues with single player but in theory it should all transfer across as if the player is simply always the host.
This commit is contained in:
@@ -4,16 +4,50 @@ extends Node
|
||||
const blinder_template = preload("res://templates/blinder.tscn")
|
||||
const one_shot_template = preload("res://templates/one_shot.tscn")
|
||||
|
||||
const vs_man_level = preload("res://scenes/multiplayer_test.tscn")
|
||||
const vs_man_level = preload("res://scenes/test_level.tscn")
|
||||
const pawn_select_scene = preload("res://scenes/character_select.tscn")
|
||||
|
||||
var player : Player
|
||||
var player : PawnController
|
||||
var hud : HUD
|
||||
var level : Level
|
||||
var multiplayer_game : bool = false
|
||||
var num_players : int = 1
|
||||
var pawns_selected : Dictionary[int, PawnBaseData] = {}
|
||||
var level_synced : Dictionary[int, bool] = {}
|
||||
var net_test : bool #TODO: Remove later
|
||||
|
||||
func setup_player(player : Player) -> void:
|
||||
self.player = player
|
||||
func _ready() -> void:
|
||||
var args : Dictionary = {}
|
||||
for arg in OS.get_cmdline_args():
|
||||
if arg.begins_with("--"):
|
||||
var flag = arg.lstrip("--")
|
||||
args[flag]=true
|
||||
if args.has("net_test"):
|
||||
net_test = true
|
||||
|
||||
|
||||
func setup_player(pc : PawnController) -> void:
|
||||
var is_local = false
|
||||
Game.level.pawns[pc.id] = pc
|
||||
if pc.id == Multiplayer.id:
|
||||
is_local = true
|
||||
if is_local:
|
||||
player = pc
|
||||
if hud:
|
||||
hud.register_player(player)
|
||||
hud.register_pawn(pc,is_local)
|
||||
|
||||
func is_multiplayer() -> bool:
|
||||
return multiplayer_game
|
||||
|
||||
|
||||
func start_level(pawns : Dictionary[int, PawnBaseData]) -> void:
|
||||
if !Multiplayer.is_host():
|
||||
return
|
||||
pawns_selected = pawns
|
||||
for i in Multiplayer.players.values():
|
||||
level_synced[i] = false
|
||||
level_synced[1] = true
|
||||
switch_scene(vs_man_level)
|
||||
|
||||
func switch_scene(packed_scene : PackedScene) -> void:
|
||||
var blinder = blinder_template.instantiate() as TextureRect
|
||||
@@ -21,21 +55,55 @@ func switch_scene(packed_scene : PackedScene) -> void:
|
||||
blinder.self_modulate.a = 0
|
||||
add_child(blinder)
|
||||
tween.tween_property(blinder, "self_modulate:a", 1, .15)
|
||||
tween.tween_callback(Callable(get_tree().change_scene_to_packed).bind(packed_scene))
|
||||
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_property(blinder, "self_modulate:a", 0, .15)
|
||||
tween.tween_callback(blinder.queue_free)
|
||||
pass
|
||||
|
||||
@rpc("call_local", "any_peer", "reliable")
|
||||
func start_pawn_select(mplayer : bool) -> void:
|
||||
var setup = get_tree().get_first_node_in_group("scenes")
|
||||
multiplayer_game = mplayer
|
||||
setup.queue_free()
|
||||
num_players = Multiplayer.players.size()
|
||||
var blinder = blinder_template.instantiate() as TextureRect
|
||||
var tween = create_tween()
|
||||
blinder.self_modulate.a = 0
|
||||
add_child(blinder)
|
||||
tween.tween_property(blinder, "self_modulate:a", 1, .15)
|
||||
if Multiplayer.is_server():
|
||||
var pawn_select = pawn_select_scene.instantiate()
|
||||
var spawner = get_tree().get_first_node_in_group("level_spawner") as MultiplayerSpawner
|
||||
tween.tween_callback(spawner.add_child.bind(pawn_select))
|
||||
tween.tween_property(blinder, "self_modulate:a", 0, .15)
|
||||
tween.tween_callback(blinder.queue_free)
|
||||
|
||||
@rpc("call_local", "any_peer", "reliable")
|
||||
func start_vs_man() -> void:
|
||||
var setup = get_tree().get_first_node_in_group("scenes")
|
||||
setup.queue_free()
|
||||
if Multiplayer.is_server():
|
||||
var level = vs_man_level.instantiate()
|
||||
var level_scene = vs_man_level.instantiate()
|
||||
var spawner = get_tree().get_first_node_in_group("level_spawner") as MultiplayerSpawner
|
||||
spawner.add_child(level)
|
||||
spawner.add_child(level_scene)
|
||||
|
||||
|
||||
@rpc("call_remote", "any_peer", "reliable")
|
||||
func level_spawned(peer_id : int) -> void:
|
||||
if !Multiplayer.is_host():
|
||||
return
|
||||
level_synced[peer_id] = true
|
||||
check_level_ready()
|
||||
|
||||
|
||||
func check_level_ready() -> void:
|
||||
var ready : bool = true
|
||||
for id in level_synced:
|
||||
if level_synced[id] == false:
|
||||
ready = false
|
||||
if ready:
|
||||
level.setup()
|
||||
|
||||
func oneshot(stream : AudioStream) -> void:
|
||||
var shot = one_shot_template.instantiate()
|
||||
shot.stream = stream
|
||||
|
||||
@@ -7,6 +7,9 @@ var handle : String
|
||||
var id : int = 1
|
||||
var players : Dictionary = {}
|
||||
signal client_added(handle : String, id : int)
|
||||
signal all_ready()
|
||||
var player_readiness : Dictionary[int, bool] = {}
|
||||
var waiting : bool = false
|
||||
|
||||
func become_host() -> void:
|
||||
print("Starting host!")
|
||||
@@ -14,6 +17,7 @@ func become_host() -> void:
|
||||
var server_peer = ENetMultiplayerPeer.new()
|
||||
server_peer.create_server(SERVER_PORT)
|
||||
players[handle] = 1
|
||||
player_readiness[1] = false
|
||||
multiplayer.multiplayer_peer = server_peer
|
||||
id = multiplayer.get_unique_id()
|
||||
print("%s %d t" % [handle, multiplayer.get_unique_id()])
|
||||
@@ -29,13 +33,33 @@ func join_game(new_handle : String) -> Error:
|
||||
multiplayer.multiplayer_peer = client_peer
|
||||
handle = new_handle
|
||||
id = multiplayer.get_unique_id()
|
||||
players[handle] = id
|
||||
player_readiness[id] = false
|
||||
if !error:
|
||||
print("%s %d" % [handle, multiplayer.get_unique_id()])
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
get_handle_from_peer(1)
|
||||
return !error
|
||||
|
||||
func is_server() -> bool:
|
||||
return multiplayer && multiplayer.is_server()
|
||||
|
||||
func is_host() -> bool:
|
||||
return id == 1
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if waiting:
|
||||
var ready : bool = true
|
||||
for pr in player_readiness.values():
|
||||
if !pr:
|
||||
ready = false
|
||||
break
|
||||
if ready:
|
||||
all_ready.emit()
|
||||
waiting = false
|
||||
for pr in player_readiness.keys():
|
||||
player_readiness[pr] = false
|
||||
|
||||
func _add_player_to_game(id : int) -> void:
|
||||
print("Player %s joined the game!" % id)
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
@@ -47,10 +71,15 @@ func _remove_player_from_game(id : int) -> void:
|
||||
func get_handle_from_peer(peer_id) -> void:
|
||||
rpc_id(peer_id, "report_handle_to_peer", id)
|
||||
|
||||
@rpc("call_local", "any_peer", "reliable")
|
||||
func set_player_ready(peer_id : int) -> void:
|
||||
player_readiness[peer_id] = true
|
||||
|
||||
@rpc("any_peer", "call_remote")
|
||||
func receive_peer_handle(peer_id, peer_handle) -> void:
|
||||
print("Player %s identified as %s!" % [peer_id, peer_handle])
|
||||
players[peer_handle] = peer_id
|
||||
player_readiness[peer_id] = false
|
||||
client_added.emit(peer_handle, peer_id)
|
||||
|
||||
@rpc("any_peer", "call_remote")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extends Control
|
||||
|
||||
var pawns : Array[PawnData] = [
|
||||
const pawns : Array[PawnBaseData] = [
|
||||
preload("res://data/pawns/van_reily.tres"),
|
||||
preload("res://data/pawns/lou_riche.tres"),
|
||||
preload("res://data/pawns/tico.tres"),
|
||||
@@ -9,35 +9,83 @@ var pawns : Array[PawnData] = [
|
||||
preload("res://data/pawns/tenrou_ugetsu.tres")
|
||||
]
|
||||
|
||||
const p_colors : Array[Color] = [
|
||||
Color.ROYAL_BLUE,
|
||||
Color.RED
|
||||
]
|
||||
|
||||
|
||||
const selector_template = preload("res://templates/pawn_selector.tscn")
|
||||
|
||||
@onready var pawn_displays : Array[PawnDisplay] = [
|
||||
%P1PawnDisplay,
|
||||
%P2PawnDisplay
|
||||
]
|
||||
|
||||
var selector_wait : float = 0
|
||||
var selection : int = 0
|
||||
var selected : int = -1
|
||||
@onready var selector = %Selector
|
||||
var selectors : Dictionary[int,PawnSelector] = {}
|
||||
var displays : Dictionary[int, PawnDisplay] = {}
|
||||
@onready var selector_start : Control = %SelectorStart
|
||||
@onready var p1_pawn_display : PawnDisplay = %P1PawnDisplay
|
||||
@onready var switch_sound : AudioStreamPlayer = %SwitchSound
|
||||
@onready var select_sound : AudioStreamPlayer = %SelectSound
|
||||
|
||||
func _ready() -> void:
|
||||
Multiplayer.waiting = true
|
||||
var plist
|
||||
if Game.is_multiplayer():
|
||||
plist = Multiplayer.players.values()
|
||||
for i in range(Game.num_players):
|
||||
displays[plist[i]] = pawn_displays[i]
|
||||
change_display(plist[i], 0)
|
||||
else:
|
||||
displays[1] = pawn_displays[0]
|
||||
change_display(1, 0)
|
||||
Multiplayer.all_ready.connect(add_selector, CONNECT_ONE_SHOT)
|
||||
Multiplayer.set_player_ready.rpc(Multiplayer.id)
|
||||
#Set up each pawn on bottom
|
||||
#Highlight P1 pawn as 0
|
||||
change_display(1, 0)
|
||||
pass
|
||||
|
||||
|
||||
func _process(delta : float) -> void:
|
||||
if selected != -1:
|
||||
return
|
||||
|
||||
|
||||
func add_selector() -> void:
|
||||
spawn_selector.rpc_id(1, Multiplayer.id)
|
||||
|
||||
@rpc("call_local","any_peer","reliable")
|
||||
func spawn_selector(peer_id) -> void:
|
||||
var selector = selector_template.instantiate()
|
||||
selector.setup(peer_id, p_colors[0] if Multiplayer.is_host() else p_colors[1])
|
||||
selector.name = selector.name + str(peer_id)
|
||||
selector_start.add_child(selector)
|
||||
selector.set_multiplayer_authority(peer_id)
|
||||
|
||||
#print("Character select spawned selector %s for player %d" % [selector.name, Multiplayer.id])
|
||||
|
||||
func _process(delta : float) -> void:
|
||||
if !selectors.has(Multiplayer.id):
|
||||
return
|
||||
var locked = true
|
||||
var pawns_selected : Dictionary[int, PawnBaseData] = {}
|
||||
for selector : PawnSelector in selectors.values():
|
||||
if !selector.selected:
|
||||
locked = false
|
||||
else:
|
||||
pawns_selected[selector.player_id] = pawns[selector.selection]
|
||||
if locked:
|
||||
Game.start_level(pawns_selected)
|
||||
|
||||
if selectors[Multiplayer.id].selected != false:
|
||||
return
|
||||
var selector = selectors[Multiplayer.id]
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
selected = selection
|
||||
selector.play_animation("selected")
|
||||
select_sound.play()
|
||||
|
||||
lock_selector.rpc(selector.player_id)
|
||||
if selector_wait > 0:
|
||||
selector_wait -= delta
|
||||
return
|
||||
var input_vector = Input.get_vector("west","east","north","south")
|
||||
var input_vector : Vector2
|
||||
if Multiplayer.is_host():
|
||||
input_vector = Input.get_vector("west","east","north","south")
|
||||
else:
|
||||
input_vector = Input.get_vector("ui_focus_prev","ui_focus_next","north","south")
|
||||
var move_dir : Vector2 = Vector2.ZERO
|
||||
|
||||
if input_vector.x < 0:
|
||||
@@ -47,15 +95,35 @@ func _process(delta : float) -> void:
|
||||
move_dir.x += 1
|
||||
|
||||
if move_dir.x != 0:
|
||||
selection = wrapi(selection + sign(move_dir.x),0, len(pawns))
|
||||
change_display(1, selection)
|
||||
selector.position = selector_start.position + Vector2(175 * selection, 0)
|
||||
switch_sound.play()
|
||||
move_selector.rpc(selector.player_id, wrapi(selector.selection + sign(move_dir.x),0, len(pawns)))
|
||||
selector_wait = 0.25
|
||||
|
||||
@rpc("any_peer","call_local","reliable")
|
||||
func move_selector(peer_id : int, selection : int) -> void:
|
||||
var selector : PawnSelector = selectors[peer_id]
|
||||
selector.selection = selection
|
||||
change_display(peer_id, selector.selection)
|
||||
var v = Vector2(175 * selector.selection, 0)
|
||||
selector.position = Vector2(175 * selector.selection, 0)
|
||||
switch_sound.play()
|
||||
|
||||
@rpc("any_peer","call_local","reliable")
|
||||
func lock_selector(peer_id : int) -> void:
|
||||
var selector : PawnSelector = selectors[peer_id]
|
||||
selector.selected = true
|
||||
selector.play_animation("selected")
|
||||
select_sound.play()
|
||||
|
||||
func change_display(player : int, selection: int) -> void:
|
||||
var pd : PawnDisplay
|
||||
if player == 1:
|
||||
pd = p1_pawn_display
|
||||
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)
|
||||
|
||||
|
||||
func _on_selector_start_child_entered_tree(node: Node) -> void:
|
||||
selectors[node.player_id] = node
|
||||
|
||||
|
||||
func _on_selector_spawner_spawned(node: Node) -> void:
|
||||
selectors[node.player_id] = node
|
||||
|
||||
@@ -44,7 +44,7 @@ var disarm_icons : Array[TextureButton]
|
||||
var disarm_buttons : Array[int]
|
||||
var progress : int
|
||||
var disarming : bool
|
||||
|
||||
var failed : bool
|
||||
var difficulty : int = 1
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
@@ -73,15 +73,11 @@ func try_advance(btn : int) -> void:
|
||||
disarm_icons[progress].button_pressed = true
|
||||
progress += 1
|
||||
if progress >= len(disarm_buttons):
|
||||
Game.player.close_modal()
|
||||
Game.oneshot(success_sound)
|
||||
Game.level.disarm_trap(square)
|
||||
succeed()
|
||||
else:
|
||||
Game.oneshot(progress_sound)
|
||||
else:
|
||||
Game.player.close_modal()
|
||||
Game.oneshot(fail_sound)
|
||||
Game.level.activate_trap(square)
|
||||
fail()
|
||||
|
||||
|
||||
func button_pressed(event : InputEventAction) -> void:
|
||||
@@ -97,8 +93,21 @@ func button_pressed(event : InputEventAction) -> void:
|
||||
Game.player.close_modal()
|
||||
_: return
|
||||
|
||||
|
||||
func _on_timer_timeout() -> void:
|
||||
func fail() -> void:
|
||||
if failed:
|
||||
return
|
||||
failed = true
|
||||
Game.player.close_modal()
|
||||
Game.oneshot(fail_sound)
|
||||
Game.level.activate_trap(square)
|
||||
|
||||
func succeed() -> void:
|
||||
Game.player.close_modal()
|
||||
Game.oneshot(success_sound)
|
||||
Game.level.disarm_trap(square)
|
||||
|
||||
func _on_trap_failed() -> void:
|
||||
fail()
|
||||
|
||||
func _on_trap_removed(type : Trap.Type) -> void:
|
||||
Game.player.close_modal()
|
||||
|
||||
@@ -2,21 +2,24 @@ class_name HUD extends Control
|
||||
|
||||
@onready var trap_display : TrapDisplay = %TrapDisplay
|
||||
@onready var p1_healthbar : HealthBar = %P1HealthBar
|
||||
@onready var p2_healthbar : HealthBar = %P2HealthBar
|
||||
@onready var control_display : ControlDisplay = %ControlDisplay
|
||||
@onready var ammo_label : Label = %AmmoLabel
|
||||
func _ready() -> void:
|
||||
Game.hud = self
|
||||
|
||||
func register_player(player : Player) -> void:
|
||||
player.health_changed.connect(p1_healthbar._on_health_changed)
|
||||
player.poison_status_changed.connect(p1_healthbar._on_poison_status_changed)
|
||||
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)
|
||||
player.combat_target_changed.connect(control_display._on_melee_range_changed)
|
||||
player.ammo_changed.connect(_on_ammo_changed)
|
||||
set_ammo(player.ammo)
|
||||
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)
|
||||
player.combat_target_changed.connect(control_display._on_melee_range_changed)
|
||||
player.ammo_changed.connect(_on_ammo_changed)
|
||||
set_ammo(player.ammo)
|
||||
|
||||
func set_ammo(ammo : int) -> void:
|
||||
ammo_label.text = str(ammo)
|
||||
|
||||
@@ -1,33 +1,52 @@
|
||||
class_name Level extends Node3D
|
||||
|
||||
const player_controller = preload("res://templates/singleplayer_pc.tscn")
|
||||
const pawn_controller = preload("res://templates/pawn_controller.tscn")
|
||||
const trap_template = preload("res://templates/trap.tscn")
|
||||
const camera_template = preload("res://templates/camera.tscn")
|
||||
|
||||
const camera_offset = Vector3(0, 10, 5.25)
|
||||
|
||||
|
||||
@onready var floor_layer : GridMap = %Floor
|
||||
@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 cameras_node : Node3D = %Cameras
|
||||
@export var difficulty : int = 1
|
||||
|
||||
var traps : Dictionary[Vector3i, Trap] = {}
|
||||
|
||||
var pawns : Dictionary[int, PawnController] = {}
|
||||
var cameras : Dictionary[int, PawnCamera] = {}
|
||||
func _ready() -> void:
|
||||
Game.level = self
|
||||
var player = player_controller.instantiate()
|
||||
var traps : Array[PlayerData.TrapData] = [
|
||||
PlayerData.TrapData.new(Trap.Type.FORCE_PANEL, 3, 3),
|
||||
PlayerData.TrapData.new(Trap.Type.BOMB, 3, 3),
|
||||
PlayerData.TrapData.new(Trap.Type.SWITCH, 3, 3),
|
||||
PlayerData.TrapData.new(Trap.Type.MINE, 3, 3),
|
||||
PlayerData.TrapData.new(Trap.Type.GAS, 3, 3)
|
||||
]
|
||||
add_child(player)
|
||||
generate_trap(Trap.Type.FORCE_PANEL, Vector3(-1, 0, 0), Vector3i(5,0,2))
|
||||
generate_trap(Trap.Type.FORCE_PANEL, Vector3(0, 0, -1), Vector3i(-3,0,2))
|
||||
generate_trap(Trap.Type.MINE, Vector3(0, 0, 0), Vector3i(-3,0,-4))
|
||||
generate_trap(Trap.Type.BOMB, Vector3(0, 0, 0), Vector3i(-2,0,-4))
|
||||
generate_trap(Trap.Type.BOMB, Vector3(0, 0, 0), Vector3i(-4,0,-4))
|
||||
player.setup(traps)
|
||||
|
||||
func setup() -> void:
|
||||
if Multiplayer.is_host():
|
||||
spawn_players()
|
||||
|
||||
func spawn_players() -> void:
|
||||
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 dict = {
|
||||
"type":trap.type,
|
||||
"qty":trap.qty
|
||||
}
|
||||
traps.append(dict)
|
||||
|
||||
var position : Vector3
|
||||
if key == Multiplayer.id:
|
||||
position = Vector3(0,0,0)
|
||||
else:
|
||||
position = Vector3(3,0,0)
|
||||
|
||||
pc.setup.rpc(key,traps,position)
|
||||
add_pawn_camera(pc)
|
||||
|
||||
|
||||
func is_square_detected(crd) -> bool:
|
||||
return marker_layer.get_cell_item(crd + Vector3i(0,-1,0)) != GridMap.INVALID_CELL_ITEM
|
||||
@@ -46,10 +65,20 @@ func detect_square(crd : Vector3i, mark : bool) -> bool:
|
||||
trap.reveal()
|
||||
return true
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func add_pawn_camera(pawn : PawnController) -> void:
|
||||
if Multiplayer.is_host():
|
||||
var camera : PawnCamera = camera_template.instantiate()
|
||||
camera.position = pawn.global_position + camera_offset
|
||||
cameras_node.add_child(camera,true)
|
||||
camera.register_pawn.rpc(pawn.id)
|
||||
|
||||
|
||||
|
||||
func add_trap(trap : Trap, crd : Vector3i) -> void:
|
||||
trap.square = crd
|
||||
traps[crd] = trap
|
||||
trap.global_position = Vector3(crd) + Vector3(.5, 0, .5)
|
||||
trap.position = Vector3(crd) + Vector3(.5, 0, .5)
|
||||
add_child(trap)
|
||||
|
||||
func add_projectile(shot : Projectile) -> void:
|
||||
@@ -74,12 +103,12 @@ func is_valid_trap_square(crd : Vector3i) -> bool:
|
||||
return true
|
||||
|
||||
func add_vfx(vfx, crd : Vector3i) -> void:
|
||||
vfx.global_position = Vector3(crd) #+ Vector3(0.5, 0, 0.5)
|
||||
vfx.position = Vector3(crd) #+ Vector3(0.5, 0, 0.5)
|
||||
add_child(vfx)
|
||||
|
||||
func activate_trap(crd : Vector3i) -> void:
|
||||
var trap = traps[crd]
|
||||
#if trap:
|
||||
if traps.has(crd):
|
||||
traps[crd].activate()
|
||||
|
||||
|
||||
func generate_trap(type : Trap.Type, dir : Vector3, square : Vector3i):
|
||||
@@ -1,4 +1,4 @@
|
||||
class_name MultiplayerPC extends Player
|
||||
class_name MultiplayerPC extends PawnController
|
||||
|
||||
@export var player_id := 1 :
|
||||
set(id):
|
||||
@@ -12,7 +12,7 @@ func _enter_tree() -> void:
|
||||
func setup(id) -> void:
|
||||
name = "Player " + str(id)
|
||||
player_id = id
|
||||
if Multiplayer.is_server() == (id == 1):
|
||||
if Multiplayer.id == id:
|
||||
Game.player = self
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
extends Node3D
|
||||
extends Level
|
||||
|
||||
const pawn_controller_template = preload("res://templates/pawn_controller.tscn")
|
||||
|
||||
const multiplayer_pc_template = preload("res://templates/multiplayer_pc.tscn")
|
||||
const camera_template = preload("res://templates/camera.tscn")
|
||||
@onready var player_spawner = %Spawner
|
||||
func _ready() -> void:
|
||||
print("LEVEL LOADING, ID: " + str(Multiplayer.id) + " with " + str(Multiplayer.players.size()) + " keys recognized")
|
||||
if Multiplayer.is_server():
|
||||
for key in Multiplayer.players.values():
|
||||
var pc = multiplayer_pc_template.instantiate()
|
||||
player_spawner.add_player(pc, key)
|
||||
pc.setup(key)
|
||||
var camera = camera_template.instantiate()
|
||||
add_child(camera)
|
||||
Game.level = self
|
||||
|
||||
func setup() -> void:
|
||||
if Multiplayer.is_host():
|
||||
spawn_players()
|
||||
|
||||
func spawn_players() -> void:
|
||||
for key in Game.pawns_selected:
|
||||
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)
|
||||
|
||||
@@ -42,7 +42,7 @@ func _on_hosting_back_button_pressed() -> void:
|
||||
|
||||
|
||||
func _on_start_game_button_pressed() -> void:
|
||||
Game.start_vs_man.rpc()
|
||||
Game.start_pawn_select.rpc(true)
|
||||
|
||||
|
||||
func _on_waiting_back_button_pressed() -> void:
|
||||
@@ -61,7 +61,7 @@ func _on_join_button_pressed() -> void:
|
||||
#Check for valid game
|
||||
#If found
|
||||
%Joining.visible = false
|
||||
if Multiplayer.join_game(handle):
|
||||
if await Multiplayer.join_game(handle):
|
||||
%"Host Waiting".visible = true
|
||||
%StartButton.visible = false
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ var spawn_points : Array = []
|
||||
func _enter_tree() -> void:
|
||||
spawn_points = $SpawnPoints.get_children()
|
||||
|
||||
func add_player(player : MultiplayerPC, key) -> void:
|
||||
func add_pawn(player : PawnController, key) -> void:
|
||||
spawn_node.add_child(player)
|
||||
if key == 1: #Server
|
||||
if player.id == 1: #Host
|
||||
player.global_position = spawn_points[0].global_position
|
||||
else:
|
||||
player.global_position = spawn_points[1].global_position
|
||||
|
||||
22
scripts/net_test.gd
Normal file
22
scripts/net_test.gd
Normal file
@@ -0,0 +1,22 @@
|
||||
extends MultiplayerSpawner
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if Game.net_test:
|
||||
await get_tree().create_timer(0.25).timeout
|
||||
Multiplayer.join_game("123")
|
||||
|
||||
else:
|
||||
Multiplayer.handle = "456"
|
||||
Multiplayer.become_host()
|
||||
await get_tree().create_timer(2).timeout
|
||||
var pids = Multiplayer.players.values()
|
||||
if Multiplayer.is_host():
|
||||
var ps : Dictionary[int, PawnBaseData] = {
|
||||
pids[0] : load("res://data/pawns/van_reily.tres"),
|
||||
pids[1] : load("res://data/pawns/lou_riche.tres")
|
||||
}
|
||||
Game.start_level(ps)
|
||||
|
||||
func _on_level_spawned(_node: Node) -> void:
|
||||
Game.level_spawned.rpc_id(1, Multiplayer.id)
|
||||
1
scripts/net_test.gd.uid
Normal file
1
scripts/net_test.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dk2gtt380ld51
|
||||
@@ -5,6 +5,17 @@ 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 struggle_ui : Sprite3D = %StruggleUI
|
||||
@onready var struggle_bar : ProgressBar = %StruggleBar
|
||||
|
||||
func play_animation(anim_name : String) -> void:
|
||||
anim_player.play(anim_name)
|
||||
|
||||
func show_struggle() -> void:
|
||||
struggle_ui.visible = true
|
||||
|
||||
func _on_struggle_changed(value : float) -> void:
|
||||
if value <= 0:
|
||||
struggle_ui.visible = false
|
||||
else:
|
||||
struggle_bar.value = value
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class_name PlayerCamera extends Camera3D
|
||||
class_name PawnCamera extends Camera3D
|
||||
|
||||
|
||||
var target
|
||||
var player_offset : Vector3
|
||||
@@ -11,20 +12,21 @@ var noise_y = 0
|
||||
|
||||
var trauma = 0.0 # Current shake strength.
|
||||
var trauma_power = 2 # Trauma exponent. Use [2, 3].
|
||||
|
||||
func _enter_tree() -> void:
|
||||
call_deferred("register_player")
|
||||
|
||||
func _ready() -> void:
|
||||
randomize()
|
||||
noise = FastNoiseLite.new()
|
||||
noise.seed = randi()
|
||||
|
||||
|
||||
func register_player() -> void:
|
||||
target = Game.player
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func register_pawn(id : int) -> void:
|
||||
set_multiplayer_authority(id)
|
||||
if id == Multiplayer.id:
|
||||
make_current()
|
||||
target = Game.level.pawns[id]
|
||||
target.camera = self
|
||||
if target:
|
||||
player_offset = global_position - target.global_position
|
||||
player_offset = position - target.global_position
|
||||
|
||||
|
||||
func add_trauma(amount):
|
||||
trauma = min(trauma + amount, 1.0)
|
||||
@@ -1,4 +1,4 @@
|
||||
class_name Player extends CharacterBody3D
|
||||
class_name PawnController extends CharacterBody3D
|
||||
|
||||
enum State {
|
||||
NORMAL,
|
||||
@@ -9,13 +9,14 @@ enum State {
|
||||
DEAD
|
||||
}
|
||||
|
||||
const camera_template = preload("res://templates/camera.tscn")
|
||||
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")
|
||||
|
||||
@export var speed : float = 10
|
||||
@onready var body : PawnBody = $PawnBody
|
||||
@onready var data : PlayerData = $Data
|
||||
@onready var data : PawnLevelData = $Data
|
||||
@onready var trap_sound : AudioStreamPlayer3D = $TrapSound
|
||||
@onready var detonate_sound : AudioStreamPlayer3D = $DetonateSound
|
||||
@onready var detect_sound : AudioStreamPlayer3D = $DetectSound
|
||||
@@ -35,6 +36,8 @@ var detect_squares : Dictionary[Vector3i, bool] = {}
|
||||
var detect_tween : Tween = null
|
||||
var fling_direction : Vector3
|
||||
var fling_speed : float
|
||||
var struggle_timer : float
|
||||
var struggle_angle : float
|
||||
|
||||
var poison_strength : int = 0
|
||||
var poison_time_remaining : float = 0
|
||||
@@ -57,7 +60,7 @@ var input_locked : bool = false
|
||||
var action_tween : Tween = null
|
||||
|
||||
|
||||
var camera : PlayerCamera = null
|
||||
var camera : PawnCamera = null
|
||||
|
||||
var modal = null
|
||||
|
||||
@@ -65,9 +68,11 @@ signal trap_cycled(trap_index : int)
|
||||
signal trap_quantity_changed(trap_index : int, quantity : int)
|
||||
signal trap_list_changed(traps)
|
||||
signal health_changed(current : int, max : int)
|
||||
signal harmed()
|
||||
signal poison_status_changed(poisoned : bool)
|
||||
signal combat_target_changed(melee : bool)
|
||||
signal ammo_changed(current : int, max : int)
|
||||
signal struggling(value : float)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if attack_timer > 0:
|
||||
@@ -86,53 +91,82 @@ func _physics_process(delta: float) -> void:
|
||||
|
||||
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
|
||||
|
||||
if input_locked or modal != null:
|
||||
dir = Vector3.ZERO
|
||||
if state == State.FLUNG:
|
||||
dir = fling_direction
|
||||
body.look_at(body.global_position - dir)
|
||||
var down = 0
|
||||
if !is_on_floor():
|
||||
down = velocity.y + get_gravity().y * delta
|
||||
velocity = fling_speed * dir
|
||||
#Raycast for a wall
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
# use global coordinates, not local to node
|
||||
var start = global_position + Vector3(0,0.25,0)
|
||||
var end = start + velocity.normalized() * 0.25 + velocity * 2 * delta
|
||||
var query = PhysicsRayQueryParameters3D.create(start, end, 1|2, [self])
|
||||
var result = space_state.intersect_ray(query)
|
||||
if result:
|
||||
var opp : Player = result.collider as Player
|
||||
if opp:
|
||||
opp.knockdown(fling_direction)
|
||||
opp.hurt(10)
|
||||
knockdown(-fling_direction)
|
||||
hurt(10)
|
||||
else:
|
||||
velocity += Vector3(0, down, 0)
|
||||
|
||||
move_and_slide()
|
||||
elif dir.length_squared() > 0:
|
||||
facing = dir.normalized()
|
||||
var down = 0
|
||||
if !is_on_floor():
|
||||
down = velocity.y + get_gravity().y * delta
|
||||
body.look_at(body.global_position - dir)
|
||||
velocity = speed * dir
|
||||
if is_poisoned():
|
||||
velocity *= 0.5
|
||||
if detecting:
|
||||
velocity *= .33
|
||||
velocity += Vector3(0, down, 0)
|
||||
move_and_slide()
|
||||
elif !is_on_floor():
|
||||
velocity += get_gravity() * delta
|
||||
move_and_slide()
|
||||
|
||||
var can_fall : bool = false
|
||||
var moving : bool = false
|
||||
match(state):
|
||||
State.FLUNG:
|
||||
can_fall = true
|
||||
moving = true
|
||||
dir = fling_direction
|
||||
body.look_at(body.global_position - dir)
|
||||
|
||||
can_fall = true
|
||||
velocity = fling_speed * dir
|
||||
#Raycast for a wall
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
# use global coordinates, not local to node
|
||||
var start = global_position + Vector3(0,0.25,0)
|
||||
var end = start + velocity.normalized() * 0.25 + velocity * 2 * delta
|
||||
var query = PhysicsRayQueryParameters3D.create(start, end, 1|2, [self])
|
||||
var result = space_state.intersect_ray(query)
|
||||
if result:
|
||||
var opp : PawnController = result.collider as PawnController
|
||||
if opp:
|
||||
opp.knockdown(fling_direction)
|
||||
opp.hurt(10)
|
||||
knockdown(-fling_direction)
|
||||
hurt(10)
|
||||
moving = false
|
||||
State.NORMAL:
|
||||
can_fall = true
|
||||
if dir.length_squared() > 0:
|
||||
moving = true
|
||||
facing = dir.normalized()
|
||||
body.look_at(body.global_position - dir)
|
||||
velocity = speed * dir
|
||||
if is_poisoned():
|
||||
velocity *= 0.5
|
||||
if detecting:
|
||||
velocity *= .33
|
||||
State.KNOCKUP:
|
||||
if is_on_floor():
|
||||
knockdown(facing)
|
||||
else:
|
||||
moving = true
|
||||
can_fall = true
|
||||
State.KNOCKDOWN,State.BOUND,State.DEAD:
|
||||
return
|
||||
|
||||
|
||||
if can_fall:
|
||||
var down = 0
|
||||
if !is_on_floor():
|
||||
velocity.y = velocity.y + get_gravity().y * delta
|
||||
moving = true
|
||||
|
||||
if moving:
|
||||
move_and_slide()
|
||||
|
||||
if detecting:
|
||||
update_detecting()
|
||||
|
||||
#Deal with the rest of the buttons
|
||||
if !input_locked:
|
||||
if modal:
|
||||
var evt = InputEventAction.new()
|
||||
@@ -224,7 +258,7 @@ func check_attack_target() -> void:
|
||||
var end = target.global_position + Vector3(0,1,0)
|
||||
var query = PhysicsRayQueryParameters3D.create(start, end, 1|2, [self])
|
||||
var result = space_state.intersect_ray(query)
|
||||
if !result or (result.collider is not Player and result.collider is not CombatTarget):
|
||||
if !result or (result.collider is not PawnController and result.collider is not CombatTarget):
|
||||
return
|
||||
|
||||
if d_sq < melee_sq:
|
||||
@@ -330,11 +364,12 @@ func detect_alert() -> void:
|
||||
detect_tween = create_tween()
|
||||
detect_tween.tween_interval(.75)
|
||||
detect_tween.tween_property(detect_icon, "visible", false, 0)
|
||||
detect_tween.tween_property(self, "detect_tween", null, 0)
|
||||
detect_tween.tween_callback(func(): detect_tween = null)
|
||||
|
||||
func close_modal() -> void:
|
||||
modal.queue_free()
|
||||
modal = null
|
||||
if modal != null:
|
||||
modal.queue_free()
|
||||
modal = null
|
||||
|
||||
func show_remove_trap_modal() -> void:
|
||||
stop_detecting()
|
||||
@@ -347,9 +382,13 @@ func show_disarm_trap_modal() -> void:
|
||||
modal = disarm_trap_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)
|
||||
Game.level.add_child(modal)
|
||||
|
||||
func start_detecting() -> void:
|
||||
@@ -388,8 +427,16 @@ func stop_detecting() -> void:
|
||||
Game.level.detect_square(sq, false)
|
||||
detect_squares = {}
|
||||
|
||||
func setup(traps) -> void:
|
||||
$Data.traps = traps
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func setup(id : int, traps : 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
|
||||
set_multiplayer_authority(id)
|
||||
struggling.connect(body._on_struggle_changed)
|
||||
Game.setup_player(self)
|
||||
|
||||
func remove_trap_at(square) -> void:
|
||||
@@ -400,7 +447,7 @@ func remove_trap_at(square) -> void:
|
||||
d.quantity += 1
|
||||
trap_quantity_changed.emit(i, d.quantity)
|
||||
break
|
||||
trap.queue_free()
|
||||
trap.remove()
|
||||
Game.level.traps.erase(square)
|
||||
|
||||
func cycle_active_trap(dir) -> void:
|
||||
@@ -420,6 +467,12 @@ func can_hurt() -> bool:
|
||||
|
||||
func hurt(damage : int) -> void:
|
||||
data.life = max(0, data.life - damage)
|
||||
if damage > 0:
|
||||
harmed.emit()
|
||||
if struggle_timer > 0:
|
||||
struggle_timer = 0
|
||||
struggling.emit(struggle_timer)
|
||||
knockdown(Vector3(0,0,-1))
|
||||
health_changed.emit(data.life, data.max_life)
|
||||
|
||||
func _on_trap_disarmed(type : Trap.Type) -> void:
|
||||
@@ -429,7 +482,13 @@ func _on_trap_disarmed(type : Trap.Type) -> void:
|
||||
d.max -= 1
|
||||
trap_quantity_changed.emit(i, d.quantity)
|
||||
break
|
||||
|
||||
#hurt
|
||||
#blast
|
||||
#blast_players
|
||||
#activate
|
||||
#activate_trap
|
||||
#fail
|
||||
#on_trap_failed
|
||||
func _on_trap_activated(type : Trap.Type) -> void:
|
||||
for i in range(len(data.traps)):
|
||||
var d = data.traps[i]
|
||||
@@ -484,6 +543,26 @@ func knockdown(direction : Vector3) -> void:
|
||||
knockdown_tween.tween_callback(Callable(body.look_at).bind(facing, Vector3(0,1,0)))
|
||||
|
||||
func knockup(velocity : Vector3) -> void:
|
||||
state = State.KNOCKUP
|
||||
input_locked = true
|
||||
if state != State.KNOCKUP:
|
||||
state = State.KNOCKUP
|
||||
input_locked = true
|
||||
if is_on_floor():
|
||||
self.velocity = Vector3.UP * .1
|
||||
move_and_slide()
|
||||
self.velocity = velocity
|
||||
|
||||
|
||||
|
||||
func pitfall(duration : float) -> void:
|
||||
struggle_timer = duration
|
||||
body.struggle_bar.max_value = struggle_timer
|
||||
body.struggle_bar.value = struggle_timer
|
||||
body.show_struggle()
|
||||
|
||||
func start_pitfall(square : Vector3, duration : float) -> void:
|
||||
input_locked = true
|
||||
velocity = Vector3.ZERO
|
||||
var tween = create_tween()
|
||||
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))
|
||||
@@ -2,7 +2,10 @@ class_name PawnDisplay extends Panel
|
||||
|
||||
@onready var pawn_name : Label = %PawnName
|
||||
@onready var trap_container : HBoxContainer = %TrapContainer
|
||||
@onready var portrait : TextureRect = $Portrait
|
||||
@onready var portrait_blinder : TextureRect = $PortraitBlinder
|
||||
var traps : Array = []
|
||||
var swap_portrait_tween : Tween = null
|
||||
|
||||
func _ready() -> void:
|
||||
for child in trap_container.get_children():
|
||||
@@ -16,5 +19,21 @@ func set_traps(trap_list : Array[TrapSet]) -> void:
|
||||
trap.setup(trap_list[tcount].type, trap_list[tcount].qty)
|
||||
tcount+=1
|
||||
|
||||
func set_portrait(picture : Texture2D) -> void:
|
||||
if swap_portrait_tween != null and swap_portrait_tween.is_running():
|
||||
swap_portrait_tween.stop()
|
||||
swap_portrait_tween = create_tween()
|
||||
|
||||
swap_portrait_tween.tween_property(portrait_blinder, "visible", true, 0)
|
||||
swap_portrait_tween.tween_method(tv_blur, 1.0, 0.0, .1)
|
||||
swap_portrait_tween.tween_property(portrait, "texture", picture, 0)
|
||||
swap_portrait_tween.tween_method(tv_blur, 0.0, 1.0, .1)
|
||||
swap_portrait_tween.tween_property(portrait_blinder, "visible", false, 0)
|
||||
portrait.texture = picture
|
||||
|
||||
func tv_blur(amount : float) -> void:
|
||||
var blinder_mat : ShaderMaterial = portrait_blinder.material
|
||||
blinder_mat.set_shader_parameter("opacity_limit", amount)
|
||||
|
||||
func set_pawn_name(name : String) -> void:
|
||||
pawn_name.text = name
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class_name PlayerData extends Node
|
||||
class_name PawnLevelData extends Node
|
||||
|
||||
class TrapData:
|
||||
var type : Trap.Type
|
||||
@@ -1,6 +1,13 @@
|
||||
extends TextureRect
|
||||
class_name PawnSelector extends TextureRect
|
||||
|
||||
@onready var anim_player : AnimationPlayer = $AnimationPlayer
|
||||
@export var player_id : int
|
||||
@export var selection : int = 0
|
||||
@export var selected : bool
|
||||
|
||||
func setup(id : int, color : Color) -> void:
|
||||
player_id = id
|
||||
modulate = color
|
||||
|
||||
func play_animation(anim : String) -> void:
|
||||
anim_player.play(anim)
|
||||
|
||||
10
scripts/pitfall.gd
Normal file
10
scripts/pitfall.gd
Normal file
@@ -0,0 +1,10 @@
|
||||
extends Node3D
|
||||
|
||||
@onready var timer : Timer = $Timer
|
||||
var duration : float
|
||||
|
||||
func _ready() -> void:
|
||||
timer.start(duration)
|
||||
|
||||
func _on_timer_timeout() -> void:
|
||||
queue_free()
|
||||
1
scripts/pitfall.gd.uid
Normal file
1
scripts/pitfall.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c14c3ogbf7i33
|
||||
@@ -19,5 +19,5 @@ func _on_body_entered(body: Node) -> void:
|
||||
|
||||
|
||||
func _on_hit_area_entered(body: Node3D) -> void:
|
||||
if body is Player:
|
||||
if body is PawnController:
|
||||
body.hurt(damage)
|
||||
|
||||
@@ -1,60 +1,77 @@
|
||||
extends Control
|
||||
|
||||
@onready var menu_list : Array = [
|
||||
{
|
||||
"option": %STORY,
|
||||
"scene" : preload("res://scenes/character_select.tscn")
|
||||
},
|
||||
{
|
||||
"option": %"VS-COM",
|
||||
"scene" : preload("res://scenes/character_select.tscn")
|
||||
},
|
||||
{
|
||||
"option": %"VS-MAN",
|
||||
"scene" : preload("res://scenes/multiplayer_setup.tscn")
|
||||
},
|
||||
{
|
||||
"option": %STORY,
|
||||
"scene" : preload("res://scenes/character_select.tscn")
|
||||
},
|
||||
{
|
||||
"option": %RECORD,
|
||||
"scene" : preload("res://scenes/character_select.tscn")
|
||||
},
|
||||
{
|
||||
"option": %OPTION,
|
||||
"scene" : preload("res://scenes/character_select.tscn")
|
||||
}
|
||||
]
|
||||
@onready var switch_sound : AudioStreamPlayer = %SwitchSound
|
||||
@onready var select_sound : AudioStreamPlayer = %SelectSound
|
||||
@onready var menu : Control = %Menu
|
||||
@onready var start : Label = %START
|
||||
@onready var target : TextureRect = %Target
|
||||
|
||||
var opened : bool = false
|
||||
var menu_choice : int = 0
|
||||
var shot_tween : Tween
|
||||
var glow_tween : Tween
|
||||
@onready var menu_list : Array = [
|
||||
%STORY,
|
||||
%"VS-COM",
|
||||
%"VS-MAN",
|
||||
%RECORD,
|
||||
%OPTION
|
||||
]
|
||||
|
||||
|
||||
var vs_com_scene = preload("res://scenes/character_select.tscn")
|
||||
func _ready() -> void:
|
||||
shot_tween = create_tween()
|
||||
%Target.scale = Vector2(2,2)
|
||||
%Target.modulate.a = 0
|
||||
target.scale = Vector2(2,2)
|
||||
target.modulate.a = 0
|
||||
shot_tween.set_parallel(true)
|
||||
shot_tween.tween_property(%Target, "modulate:a", 1, 1)
|
||||
shot_tween.tween_property(%Target, "scale", Vector2(1,1), 1)
|
||||
shot_tween.tween_property(%Target, "rotation", PI / 4, 1)
|
||||
shot_tween.tween_property(target, "modulate:a", 1, 1)
|
||||
shot_tween.tween_property(target, "scale", Vector2(1,1), 1)
|
||||
shot_tween.tween_property(target, "rotation", PI / 4, 1)
|
||||
glow_tween = create_tween()
|
||||
glow_tween.set_loops(-1)
|
||||
glow_tween.tween_property(%Target, "modulate:a", .8, .5)
|
||||
glow_tween.tween_property(%Target, "modulate:a", 1, .5)
|
||||
glow_tween.tween_property(target, "modulate:a", .8, .5)
|
||||
glow_tween.tween_property(target, "modulate:a", 1, .5)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_menu"):
|
||||
opened = !opened
|
||||
%START.visible = !opened
|
||||
%Menu.visible = opened
|
||||
%SelectSound.play()
|
||||
start.visible = !opened
|
||||
menu.visible = opened
|
||||
select_sound.play()
|
||||
if opened:
|
||||
menu_choice = 0
|
||||
switch_menu()
|
||||
switch_menu(0)
|
||||
|
||||
if opened:
|
||||
if Input.is_action_just_pressed("ui_up"):
|
||||
menu_choice -= 1
|
||||
if menu_choice < 0:
|
||||
menu_choice = len(menu_list) - 1
|
||||
%SwitchSound.play()
|
||||
switch_menu()
|
||||
elif Input.is_action_just_pressed("ui_down"):
|
||||
menu_choice += 1
|
||||
if menu_choice >= len(menu_list):
|
||||
menu_choice = 0
|
||||
%SwitchSound.play()
|
||||
switch_menu()
|
||||
elif Input.is_action_just_pressed("ui_accept"):
|
||||
Game.switch_scene(vs_com_scene)
|
||||
|
||||
var menu_dir = 0
|
||||
menu_dir += 1 if Input.is_action_just_pressed("ui_down") else 0
|
||||
menu_dir -= 1 if Input.is_action_just_pressed("ui_up") else 0
|
||||
if menu_dir != 0:
|
||||
switch_sound.play()
|
||||
switch_menu(wrapi(menu_choice + menu_dir, 0, len(menu_list)))
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
Game.switch_scene(menu_list[menu_choice].scene)
|
||||
|
||||
|
||||
func switch_menu() -> void:
|
||||
for child in %Menu.get_children():
|
||||
child.visible = false
|
||||
menu_list[menu_choice].visible = true
|
||||
func switch_menu(new_choice : int) -> void:
|
||||
menu_list[menu_choice].option.visible = false
|
||||
menu_choice = new_choice
|
||||
menu_list[menu_choice].option.visible = true
|
||||
|
||||
@@ -28,6 +28,7 @@ const trap_icons : Dictionary = {
|
||||
|
||||
const bomb_explosion_template = preload("res://templates/explosion.tscn")
|
||||
const mine_explosion_template = preload("res://templates/explosion.tscn")
|
||||
const pitfall_template = preload("res://templates/pitfall.tscn")
|
||||
const switch_explosion_template = preload("res://templates/explosion.tscn")
|
||||
const gas_emitter_template = preload("res://templates/gas_emitter.tscn")
|
||||
const force_activate_sound = preload("res://audio/sounds/TomWinandySFX_UI_ScifiTech_Start_06.wav")
|
||||
@@ -46,6 +47,7 @@ var square : Vector3i
|
||||
var trap_owner : int
|
||||
var direction : Vector3
|
||||
|
||||
var expended : bool = false
|
||||
var disarming : bool
|
||||
var disarm_id : int
|
||||
|
||||
@@ -55,6 +57,8 @@ var damage : int = 10
|
||||
|
||||
var just_revealed : bool = false
|
||||
|
||||
|
||||
signal removed(type : Trap.Type)
|
||||
signal disarmed(type : Trap.Type)
|
||||
signal activated(type : Trap.Type)
|
||||
|
||||
@@ -66,6 +70,10 @@ func setup(type : Type, direction : Vector3, trap_owner : int) -> void:
|
||||
var cardinal : float = roundi(r * 2 / PI) * PI / 2
|
||||
self.direction = Vector3(cos(cardinal), 0, sin(cardinal))
|
||||
|
||||
func remove() -> void:
|
||||
removed.emit(type)
|
||||
queue_free()
|
||||
|
||||
func disarm() -> void:
|
||||
disarmed.emit(type)
|
||||
queue_free()
|
||||
@@ -113,12 +121,20 @@ func _ready() -> void:
|
||||
func _process(delta: float) -> void:
|
||||
just_revealed = false
|
||||
|
||||
func blast(body : Player) -> void:
|
||||
func blast(body : PawnController) -> void:
|
||||
body.hurt(damage)
|
||||
body.knockup((global_position.direction_to(body.global_position) + Vector3(0,2,0)) * 3)
|
||||
|
||||
func activate() -> void:
|
||||
if expended:
|
||||
return
|
||||
var explode : bool = false
|
||||
match(type):
|
||||
Type.PITFALL:
|
||||
generate_pitfall(square)
|
||||
for body in get_overlapping_bodies():
|
||||
body.start_pitfall(square, 6.0)
|
||||
#Generate the circle here
|
||||
Type.FORCE_PANEL:
|
||||
Game.oneshot(force_activate_sound)
|
||||
for body in get_overlapping_bodies():
|
||||
@@ -146,15 +162,21 @@ func activate() -> void:
|
||||
trigger_adjacent_bombs()
|
||||
blast_players()
|
||||
|
||||
expended = true
|
||||
Game.level.remove_trap_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:
|
||||
if trap.type == Type.BOMB and trap != self:
|
||||
trap.delay_trigger()
|
||||
|
||||
func generate_pitfall(suqare : Vector3i) -> void:
|
||||
var pitfall = pitfall_template.instantiate()
|
||||
pitfall.duration = 6.0
|
||||
Game.level.add_vfx(pitfall, square)
|
||||
|
||||
func blast_players() -> void:
|
||||
for body in range_area.get_overlapping_bodies():
|
||||
blast(body)
|
||||
@@ -168,7 +190,7 @@ func delay_trigger() -> void:
|
||||
|
||||
|
||||
func _on_body_entered(body: Node3D) -> void:
|
||||
if type == Type.GAS:
|
||||
if type == Type.GAS or type == Type.BOMB:
|
||||
return
|
||||
|
||||
if body.id == trap_owner:
|
||||
|
||||
Reference in New Issue
Block a user