class_name Trap extends Area3D enum Type{ BOMB, MINE, GAS, FORCE_PANEL, SWITCH, PITFALL } 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), } 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 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") @onready var range_area : Area3D = %RangeArea @onready var range_shape : BoxShape3D = %RangeShape.shape @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 reveal_timer : Timer = %RevealTimer @onready var activation_timer : Timer = %ActivationTimer var type : Type var square : Vector3i var trap_owner : int var direction : Vector3 var expended : bool = false var disarming : bool var disarm_id : int var delayed_trigger_tween : Tween = null var damage : int = 10 var just_revealed : bool = false signal removed(type : Trap.Type) signal disarmed(type : Trap.Type) signal activated(type : Trap.Type) func setup(type : Type, direction : Vector3, trap_owner : int) -> void: self.type = type self.trap_owner = trap_owner if type == Type.FORCE_PANEL: var r : float = atan2(direction.z, direction.x) 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() func reveal() -> void: if model.visible: return model.visible = true reveal_timer.start(5) just_revealed = true func is_just_revealed() -> bool: return just_revealed func is_revealed() -> bool: return model.visible func _on_reveal_timeout() -> void: if Game.level.is_square_detected(square) or disarming: reveal_timer.start(5) else: model.visible = false func _ready() -> void: var owns_trap = trap_owner == Multiplayer.id icon.texture = trap_icons[type] model.visible = owns_trap icon.visible = owns_trap range_shape.size = range_shapes[type] material.albedo_color = Color.YELLOW if owns_trap else Color.RED match(type): Type.BOMB: damage = 15 Type.MINE: damage = 10 Type.SWITCH: damage = 5 Type.GAS: activation_timer.start() Type.FORCE_PANEL: var r : float = atan2(direction.z, -direction.x) + PI/2 force_strip.rotate_y(r) force_strip.visible = owns_trap func _process(delta: float) -> void: just_revealed = false 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(): body.fling(direction, 5.0) Type.MINE: var exp = mine_explosion_template.instantiate() Game.level.add_vfx(exp, square) explode = true Type.SWITCH: var exp = switch_explosion_template.instantiate() Game.level.add_vfx(exp, square) explode = true Type.BOMB: var exp = bomb_explosion_template.instantiate() Game.level.add_vfx(exp, square) explode = true Type.GAS: var emitter = gas_emitter_template.instantiate() emitter.trap_owner = trap_owner emitter.square = square emitter.damage = 4 Game.level.add_vfx(emitter, square) if explode: 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 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) func delay_trigger() -> void: if delayed_trigger_tween != null: return delayed_trigger_tween = create_tween() delayed_trigger_tween.tween_interval(.25) delayed_trigger_tween.tween_callback(activate) func _on_body_entered(body: Node3D) -> void: if type == Type.GAS or type == Type.BOMB: return if body.id == trap_owner: return if !disarming or body.id != disarm_id: if !body.detecting: activate() func _on_activation_timer_timeout() -> void: activate()