Files
net-gunner/scripts/hack.gd

216 lines
5.7 KiB
GDScript

class_name Hack extends Area3D
enum Type{
DESTROY,
PURGE,
INFECT,
REDIRECT,
TRIGGER,
CONTAIN
}
const range_radius : Dictionary = {
Hack.Type.DESTROY : 2,
Hack.Type.INFECT : .5,
Hack.Type.CONTAIN : .5,
Hack.Type.REDIRECT : .5,
Hack.Type.TRIGGER : 1,
Hack.Type.PURGE : 1.5,
}
const hack_icons : Dictionary = {
Hack.Type.DESTROY : preload("res://external/destroy-icon.png"),
Hack.Type.INFECT : preload("res://external/infect-icon.png"),
Hack.Type.CONTAIN : preload("res://external/contain-icon.png"),
Hack.Type.REDIRECT : preload("res://external/redirect-icon.png"),
Hack.Type.TRIGGER : preload("res://external/trigger-icon.png"),
Hack.Type.PURGE : preload("res://external/purge-icon.png"),
}
const destroy_explosion_template = preload("res://templates/explosion.tscn")
const purge_explosion_template = preload("res://templates/explosion.tscn")
const contain_template = preload("res://templates/pitfall.tscn")
const trigger_explosion_template = preload("res://templates/explosion.tscn")
const infect_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 : SphereShape3D = %RangeShape.shape
@onready var model : MeshInstance3D = %Model
@onready var icon : Sprite3D = %Icon
@onready var force_strip : Sprite3D = %ForceStrip
@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 hack_owner : int
var direction : Vector3
var expended : bool = false
var decompiling : bool
var decompile_id : int
var delayed_trigger_tween : Tween = null
var damage : int = 10
var just_revealed : bool = false
signal removed(type : Hack.Type)
signal decompiled(type : Hack.Type)
signal activated(type : Hack.Type)
signal revealed(is_visible : bool)
func _enter_tree() -> void:
Game.level.add_map_marker(self)
func _exit_tree() -> void:
Game.level.remove_map_marker(self)
func setup(type : Type, direction : Vector3, hack_owner : int) -> void:
self.type = type
self.hack_owner = hack_owner
if type == Type.REDIRECT:
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 decompile() -> void:
decompiled.emit(type)
queue_free()
func reveal() -> void:
if model.visible:
return
model.visible = true
reveal_timer.start(5)
just_revealed = true
revealed.emit(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 decompiling:
reveal_timer.start(5)
else:
model.visible = false
revealed.emit(false)
func _ready() -> void:
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.radius = range_radius[type]
match(type):
Type.DESTROY:
damage = 15
Type.PURGE:
damage = 10
Type.TRIGGER:
damage = 5
Type.INFECT:
activation_timer.start()
Type.REDIRECT:
var r : float = atan2(direction.z, -direction.x) + PI/2
force_strip.rotate_y(r)
force_strip.visible = owns_hack
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.CONTAIN:
generate_contain(square)
for body in get_overlapping_bodies():
body.start_pitfall(square, 6.0)
#Generate the circle here
Type.REDIRECT:
Game.oneshot(force_activate_sound)
for body in get_overlapping_bodies():
body.fling(direction, 5.0)
Type.PURGE:
var exp = purge_explosion_template.instantiate()
Game.level.add_vfx(exp, square)
explode = true
Type.TRIGGER:
var exp = trigger_explosion_template.instantiate()
Game.level.add_vfx(exp, square)
explode = true
Type.DESTROY:
var exp = destroy_explosion_template.instantiate()
Game.level.add_vfx(exp, square)
explode = true
Type.INFECT:
var emitter = infect_emitter_template.instantiate()
emitter.hack_owner = hack_owner
emitter.square = square
emitter.damage = 4
Game.level.add_vfx(emitter, square)
if explode:
trigger_adjacent_bombs()
blast_players()
expended = true
Game.level.uninstall_hack_square(square)
activated.emit(type)
queue_free()
func trigger_adjacent_bombs() -> void:
for hack : Hack in range_area.get_overlapping_areas():
if hack.type == Type.DESTROY and hack != self:
hack.delay_trigger()
func generate_contain(square : Vector3i) -> void:
var contain = contain_template.instantiate()
contain.duration = 6.0
Game.level.add_vfx(contain, 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.INFECT or type == Type.DESTROY:
return
if body.id == hack_owner:
return
if !decompiling or body.id != decompile_id:
if !body.detecting:
activate()
func _on_activation_timer_timeout() -> void:
activate()