104 lines
3.3 KiB
GDScript
104 lines
3.3 KiB
GDScript
class_name BurnZones
|
|||
|
|
extends Node3D
|
||
|
|
|
||
|
|
const RADIUS := 3.5
|
||
|
|
const DURATION := 3.0
|
||
|
|
const DAMAGE_PER_SECOND := 8.0
|
||
|
|
const TICK := 0.25
|
||
|
|
var game: Node3D
|
||
|
|
var zones: Dictionary = {}
|
||
|
|
var visuals: Dictionary = {}
|
||
|
|
var serial := 0
|
||
|
|
|
||
|
|
func ignite(pos: Vector3, owner_id: int) -> void:
|
||
|
|
if not game.is_host:
|
||
|
|
return
|
||
|
|
var hit := ground_at(pos + Vector3.UP * 0.2, 14)
|
||
|
|
if hit.is_empty():
|
||
|
|
return
|
||
|
|
serial += 1
|
||
|
|
var point: Vector3 = hit.position + Vector3.UP * 0.035
|
||
|
|
zones[serial] = {"id": serial, "p": point, "owner": owner_id, "left": DURATION, "clock": 0.0}
|
||
|
|
show_zone(zones[serial])
|
||
|
|
|
||
|
|
func ground_at(pos: Vector3, depth: float) -> Dictionary:
|
||
|
|
var query := PhysicsRayQueryParameters3D.create(pos, pos + Vector3.DOWN * depth, 1)
|
||
|
|
var hit := get_world_3d().direct_space_state.intersect_ray(query)
|
||
|
|
return hit if not hit.is_empty() and hit.normal.y > 0.65 else {}
|
||
|
|
|
||
|
|
func advance(dt: float) -> void:
|
||
|
|
if not game.is_host:
|
||
|
|
return
|
||
|
|
for id in zones.keys():
|
||
|
|
var zone: Dictionary = zones[id]
|
||
|
|
var alive_time := minf(maxf(0, dt), zone.left)
|
||
|
|
zone.left -= alive_time
|
||
|
|
zone.clock += alive_time
|
||
|
|
while zone.clock + 0.00001 >= TICK:
|
||
|
|
zone.clock -= TICK
|
||
|
|
if not game.match_over and game.players.has(zone.owner):
|
||
|
|
var source: Fighter = game.players[zone.owner]
|
||
|
|
for target: Fighter in game.players.values():
|
||
|
|
if affects(zone.p, target):
|
||
|
|
game.combat.damage(target, source, DAMAGE_PER_SECOND * TICK, "FRAG / FIRE", false, zone.p)
|
||
|
|
if zone.left <= 0 or not game.players.has(zone.owner):
|
||
|
|
remove_zone(id)
|
||
|
|
|
||
|
|
func affects(pos: Vector3, target: Fighter) -> bool:
|
||
|
|
# Flames occupy one floor, not a vertical cylinder through the entire house.
|
||
|
|
var offset := target.global_position - pos
|
||
|
|
return absf(offset.y) < 1.5 and Vector2(offset.x, offset.z).length() < RADIUS \
|
||
|
|
and game.visible_between(pos + Vector3.UP * 0.25, target.eye(), [target.get_rid()])
|
||
|
|
|
||
|
|
func snapshot() -> Array:
|
||
|
|
var result := []
|
||
|
|
for zone: Dictionary in zones.values():
|
||
|
|
result.append({"id": zone.id, "p": zone.p, "owner": zone.owner, "left": zone.left})
|
||
|
|
return result
|
||
|
|
|
||
|
|
func sync(rows: Array) -> void:
|
||
|
|
if game.is_host:
|
||
|
|
return
|
||
|
|
var seen := []
|
||
|
|
for row: Dictionary in rows:
|
||
|
|
seen.append(row.id)
|
||
|
|
zones[row.id] = row.duplicate()
|
||
|
|
show_zone(row)
|
||
|
|
for id in zones.keys():
|
||
|
|
if not id in seen:
|
||
|
|
remove_zone(id)
|
||
|
|
|
||
|
|
func show_zone(zone: Dictionary) -> void:
|
||
|
|
if game.headless:
|
||
|
|
return
|
||
|
|
if visuals.has(zone.id) and is_instance_valid(visuals[zone.id]):
|
||
|
|
visuals[zone.id].remaining = zone.left
|
||
|
|
return
|
||
|
|
var points: Array[Vector3] = []
|
||
|
|
var count: int = [10, 16, 20][game.graphics_quality]
|
||
|
|
for i in count:
|
||
|
|
var angle := i * 2.399963
|
||
|
|
var radius := sqrt(float(i) / count) * (RADIUS - 0.8)
|
||
|
|
var sample: Vector3 = zone.p + Vector3(cos(angle), 0, sin(angle)) * radius
|
||
|
|
if not game.visible_between(zone.p + Vector3.UP * 0.3, sample + Vector3.UP * 0.3):
|
||
|
|
continue
|
||
|
|
var floor_hit := ground_at(sample + Vector3.UP * 0.6, 1.2)
|
||
|
|
if not floor_hit.is_empty():
|
||
|
|
points.append(floor_hit.position + Vector3.UP * 0.04)
|
||
|
|
var effect := BurnVisual.new()
|
||
|
|
effect.points = points
|
||
|
|
effect.remaining = zone.left
|
||
|
|
add_child(effect)
|
||
|
|
visuals[zone.id] = effect
|
||
|
|
|
||
|
|
func remove_zone(id: int) -> void:
|
||
|
|
zones.erase(id)
|
||
|
|
if visuals.has(id):
|
||
|
|
if is_instance_valid(visuals[id]):
|
||
|
|
visuals[id].queue_free()
|
||
|
|
visuals.erase(id)
|
||
|
|
|
||
|
|
func clear() -> void:
|
||
|
|
for id in zones.keys():
|
||
|
|
remove_zone(id)
|