Files
Blockline/scripts/killstreaks.gd
T

150 lines
4.8 KiB
GDScript

class_name Killstreaks
extends Node3D
const ULT_KILLS := 4
var game: Node3D
var mines: Dictionary = {}
var visuals: Dictionary = {}
var serial := 0
var nuke_left := 0.0
var nuke_owner := 0
var nuke_name := ""
var nuke_team := 0
var flash_left := 0.0
func earned(p: Fighter) -> void:
if not game.is_host or game.match_over or p.hp <= 0: return
p.streak += 1
p.ult_charge = mini(ULT_KILLS, p.ult_charge + 1)
match p.streak:
5:
p.infinite_left = 60
p.reload_left = 0
p.magazines = Arsenal.ammunition()
10: p.recon_left = 30
15:
p.hp = 100
p.shield_charges = 1
20:
if nuke_left <= 0:
nuke_left = 8
nuke_owner = p.peer_id
nuke_name = p.nickname
nuke_team = p.team
func place(p: Fighter) -> bool:
if not game.is_host or not game.active or game.match_over or p.hp <= 0 or p.ult_charge < ULT_KILLS or p.rope_active or p.mantle_left > 0: return false
var direction := Arsenal.direction(p.yaw, 0)
var probe := p.eye() + direction * 1.2
if not game.visible_between(p.eye(), probe, [p.get_rid()]): return false
var ground: Dictionary = game.burn_zones.ground_at(probe, 2.2)
if ground.is_empty(): return false
var pos: Vector3 = ground.position + Vector3.UP * 0.06
for m: Dictionary in mines.values():
if pos.distance_to(m.p) < 0.65: return false
var owned := []
for id in mines:
if mines[id].owner == p.peer_id: owned.append(id)
if owned.size() >= 2: remove_mine(owned[0])
serial += 1
mines[serial] = {"id": serial, "owner": p.peer_id, "p": pos, "yaw": p.yaw, "arm": 1.0}
p.ult_charge = 0
p.protection = 0
show_mine(mines[serial])
return true
func triggers(m: Dictionary, p: Fighter) -> bool:
var source: Fighter = game.players.get(m.owner)
if source == null or p.hp <= 0 or p.protection > 0 or not game.enemies(source, p): return false
var delta: Vector3 = p.global_position - m.p
var flat := Vector3(delta.x, 0, delta.z)
return absf(delta.y) < 1.3 and flat.length() < 5 and flat.length() > 0.05 and Arsenal.direction(m.yaw, 0).dot(flat.normalized()) > 0.5 and game.visible_between(m.p + Vector3.UP * 0.3, p.eye(), [p.get_rid()])
func advance(dt: float) -> void:
if not game.is_host or not game.active or game.match_over: return
if nuke_left > 0:
nuke_left = maxf(0, nuke_left - dt)
if nuke_left == 0:
wipe()
return
for id in mines.keys():
var m: Dictionary = mines[id]
if not game.players.has(m.owner):
remove_mine(id)
continue
m.arm = maxf(0, m.arm - dt)
if m.arm > 0: continue
for p: Fighter in game.players.values():
if triggers(m, p):
remove_mine(id)
game.detonate.rpc(m.p)
var source: Fighter = game.players[m.owner]
for target: Fighter in game.players.values():
var distance: float = (target.eye() - m.p).length()
if distance < 6 and game.visible_between(m.p + Vector3.UP * 0.3, target.eye(), [target.get_rid()]):
game.combat.damage(target, source, lerpf(180, 0, distance / 6), "CLAYMORE", false, m.p)
break
func wipe() -> void:
# A round-ending event, not directional damage: no shield, spawn guard or kill farming.
game.match_over = true
game.winner = nuke_name + " / NUKE VICTORY" if game.config.mode == "FFA" else ("TEAM RELAY / NUKE VICTORY" if nuke_team == 0 else "TEAM EMBER / NUKE VICTORY")
for p: Fighter in game.players.values():
if p.hp <= 0: continue
p.hp = 0
p.deaths += 1
p.streak = 0
p.infinite_left = 0
p.recon_left = 0
p.shield_left = 0
p.killer = nuke_name
p.killer_weapon = "NUKE"
p.respawn_left = 3
p.shape.set_deferred("disabled", true)
for id in mines.keys(): remove_mine(id)
game.nuke_effect.rpc()
func snapshot() -> Dictionary:
return {"mines": mines.values(), "nuke": nuke_left, "name": nuke_name}
func sync(state: Dictionary) -> void:
if game.is_host: return
nuke_left = state.get("nuke", 0)
nuke_name = state.get("name", "")
var seen := []
for row: Dictionary in state.get("mines", []):
seen.append(row.id)
mines[row.id] = row
show_mine(row)
for id in mines.keys():
if not id in seen: remove_mine(id)
func show_mine(m: Dictionary) -> void:
if game.headless or visuals.has(m.id): return
var model := Node3D.new()
add_child(model)
model.position = m.p
model.rotation.y = m.yaw
WeaponGeometry.block(model, Vector3(0, 0.15, 0), Vector3(0.32, 0.20, 0.09), "cloth")
for side in [-1, 1]:
WeaponGeometry.block(model, Vector3(side * 0.12, 0.03, 0), Vector3(0.015, 0.16, 0.015), "steel")
WeaponGeometry.block(model, Vector3(side * 0.11, 0.19, -0.053), Vector3(0.018, 0.018, 0.01), "red")
WeaponGeometry.batch(model)
visuals[m.id] = model
func remove_mine(id: int) -> void:
mines.erase(id)
if visuals.has(id):
visuals[id].queue_free()
visuals.erase(id)
func clear() -> void:
for id in mines.keys(): remove_mine(id)
nuke_left = 0
nuke_owner = 0
nuke_name = ""
flash_left = 0
func _process(dt: float) -> void:
flash_left = maxf(0, flash_left - dt)