126 lines
3.9 KiB
GDScript
126 lines
3.9 KiB
GDScript
extends "res://scripts/building/build_piece.gd"
|
|
## Door: a wall-sized frame (snaps like a wall) with a hinged panel. E opens
|
|
## or closes it. The panel is an AnimatableBody3D so it pushes the player
|
|
## out of the way rather than trapping them.
|
|
##
|
|
## An alpha wolf can break a closed wooden door (bash()): after DOOR_HP worth
|
|
## of blows the panel gives way and hangs open, and the door shows as broken
|
|
## until you repair it (E, two wood). An iron-banded door (items.json
|
|
## place.sturdy) can't be broken. The frame stays either way, so whatever it
|
|
## holds up keeps standing.
|
|
|
|
const OPEN_ANGLE := 1.75 ## radians: swings toward -Z, away from where you stood to place it
|
|
const DOOR_HP := 100.0
|
|
const REPAIR_COST := {"wood": 2}
|
|
|
|
var open := false
|
|
var broken := false
|
|
var door_hp := DOOR_HP
|
|
var _hinge: Node3D
|
|
var _panel: AnimatableBody3D
|
|
var _panel_shape: CollisionShape3D
|
|
var _tween: Tween
|
|
|
|
func _visual_mesh() -> Mesh:
|
|
return ItemDB.mesh_for(String(def.get("mesh", item_id)) + "_frame") # (wood_door_frame, stone_doorway_frame...)
|
|
|
|
func _build() -> void:
|
|
_hinge = Node3D.new()
|
|
_hinge.name = "Hinge"
|
|
_hinge.position = Vector3(-0.54, 0.0, 0.0)
|
|
add_child(_hinge)
|
|
_panel = AnimatableBody3D.new()
|
|
_panel.name = "Panel"
|
|
# Follow the hinge directly (sync_to_physics would only track the body's
|
|
# own motion, not its parent rotating).
|
|
_panel.sync_to_physics = false
|
|
_panel.collision_layer = 1 | BUILD_LAYER
|
|
_panel.set_meta("owner_piece", self)
|
|
_hinge.add_child(_panel)
|
|
var mi := MeshInstance3D.new()
|
|
mi.mesh = ItemDB.mesh_for(String(def.place.get("panel", "wood_door_panel")))
|
|
mi.material_override = ItemDB.item_material()
|
|
_panel.add_child(mi)
|
|
_panel_shape = CollisionShape3D.new()
|
|
var box := BoxShape3D.new()
|
|
box.size = Vector3(1.06, 1.86, 0.08)
|
|
_panel_shape.shape = box
|
|
_panel_shape.position = Vector3(0.54, 0.95, 0.0)
|
|
_panel.add_child(_panel_shape)
|
|
|
|
## Iron-banded: nothing breaks it.
|
|
func sturdy() -> bool:
|
|
return bool(def.place.get("sturdy", false))
|
|
|
|
## Shut and in one piece (something trying to get in has to get past it).
|
|
func blocks() -> bool:
|
|
return not open and not broken
|
|
|
|
func interact_prompt(_player: Node) -> String:
|
|
if broken:
|
|
return "Broken door [E] Repair (2 wood)"
|
|
return "Door [E] %s" % ("Close" if open else "Open")
|
|
|
|
func interact(player: Node) -> void:
|
|
if broken:
|
|
Actions.repair_door(player, self)
|
|
else:
|
|
Actions.toggle_door(player, self)
|
|
|
|
func set_open(on: bool, animate: bool = true) -> void:
|
|
if broken:
|
|
on = true # (it hangs open off one hinge)
|
|
open = on
|
|
var target := OPEN_ANGLE if on else 0.0
|
|
if _tween != null:
|
|
_tween.kill()
|
|
if animate and is_inside_tree():
|
|
_tween = create_tween()
|
|
_tween.tween_property(_hinge, "rotation:y", target, 0.45).set_trans(Tween.TRANS_SINE)
|
|
else:
|
|
_hinge.rotation.y = target
|
|
|
|
## A blow from something trying to get in. Returns "hit", "held" (iron
|
|
## bands), "broke" (this blow broke it) or "" (open or already broken).
|
|
func bash(damage: float) -> String:
|
|
if not blocks():
|
|
return ""
|
|
if sturdy():
|
|
return "held"
|
|
door_hp -= damage
|
|
if door_hp > 0.0:
|
|
return "hit"
|
|
set_broken(true)
|
|
return "broke"
|
|
|
|
## Broken: the panel hangs open, askew, and stops anything. Repaired: shut
|
|
## and whole again.
|
|
func set_broken(on: bool) -> void:
|
|
broken = on
|
|
if on:
|
|
door_hp = 0.0
|
|
set_open(true, false)
|
|
_hinge.rotation = Vector3(0.0, OPEN_ANGLE + 0.35, 0.18)
|
|
_panel_shape.disabled = true
|
|
else:
|
|
door_hp = DOOR_HP
|
|
_hinge.rotation = Vector3.ZERO
|
|
_panel_shape.disabled = false
|
|
set_open(false, false)
|
|
|
|
func save_extra() -> Dictionary:
|
|
return {"open": open, "broken": broken}
|
|
|
|
func load_extra(d: Dictionary) -> void:
|
|
set_broken(bool(d.get("broken", false)))
|
|
if not broken:
|
|
set_open(bool(d.get("open", false)), false)
|
|
|
|
## A server telling us this door's state: swing it if it only opened or
|
|
## closed, snap it if it broke or was mended.
|
|
func net_state(d: Dictionary) -> void:
|
|
if bool(d.get("broken", false)) != broken:
|
|
load_extra(d)
|
|
elif not broken:
|
|
set_open(bool(d.get("open", false)))
|