extends Resource class_name Weapon ## One weapon: its stats, its swing, and how it sits in the hands. ## ## Replaces MeleeAttack. The important new part is `vs` — the WEAPON vs MATERIAL ## matrix the founding doc asked for. A weapon no longer just "breaks things"; it does ## `power * vs[material]` damage against that material's toughness (Smashable.PROFILES ## .hp). That is what makes the loadout a decision instead of a skin: ## ## box cutter — shreds cardboard and paper, pings uselessly off a filing cabinet ## cricket bat — the all-rounder; snaps vinyl and glass, struggles with steel ## crowbar — the only mid-speed answer to steel ## sledgehammer— ends anything, but you swing it once a second ## ## Swing timing matters as much as damage: `contact` is how far into the animation the ## hit actually lands, so the sledge connects late and heavy while the cutter is instant. ## ## Godot 4.7 GDScript 2.0. @export var id: String = "fists" @export var display_name: String = "Fists" @export_multiline var blurb: String = "" ## Viewmodel mesh; "" means bare hands. @export var mesh_path: String = "" # ---------------------------------------------------------------- hit shape @export var reach: float = 1.7 ## metres in front of the camera the hit sphere sits @export var radius: float = 0.34 ## fat sphere — melee is forgiving, not pixel-precise @export var impulse: float = 6.0 ## physics shove applied to whatever is hit @export var knockback: float = 2.5 ## off-centre impulse for plain (non-Smashable) bodies # ---------------------------------------------------------------- damage @export var power: float = 1.0 ## base damage, before the material multiplier @export var vs: Dictionary = {} ## material -> multiplier (missing = 1.0) # ---------------------------------------------------------------- feel @export var cooldown: float = 0.30 ## seconds between swings @export var swing: String = "horizontal" ## horizontal | overhead | thrust | jab @export var swing_time: float = 0.42 ## full animation length @export var contact: float = 0.34 ## fraction of swing_time at which the hit lands @export var two_handed: bool = false @export var heft: float = 1.0 ## scales bob/sway amplitude and landing dip @export var shake: float = 0.20 ## trauma added to the camera on a connecting hit ## Damage this weapon does to one material. func damage_against(kind: String) -> float: return power * float(vs.get(kind, 1.0)) ## True when this weapon is essentially useless against `kind` — drives the comedy ## "clank, nothing happened" feedback instead of a silent non-event. func is_futile_against(kind: String, hp: float) -> bool: return damage_against(kind) < hp * 0.10 # ---------------------------------------------------------------- the loadout ## Built in code (not .tres) so the whole matrix reads as one table you can tune in ## a single place. Order here is the order of the number keys. static func loadout() -> Array[Weapon]: var out: Array[Weapon] = [] var fists := Weapon.new() fists.id = "fists" fists.display_name = "Bare Hands" fists.blurb = "Free. Fast. Regrettable." fists.mesh_path = "" fists.reach = 1.65 fists.radius = 0.34 fists.power = 1.0 fists.impulse = 5.0 fists.knockback = 2.2 fists.cooldown = 0.26 fists.swing = "jab" fists.swing_time = 0.30 fists.contact = 0.30 fists.heft = 0.5 fists.shake = 0.14 fists.vs = {"cardboard": 1.6, "paper": 2.0, "glass": 1.2, "vinyl": 1.0, "wood": 0.45, "steel": 0.25, "plastic": 0.8} out.append(fists) var cutter := Weapon.new() cutter.id = "cutter" cutter.display_name = "Box Cutter" cutter.blurb = "Shreds paper and card. Does nothing to furniture. Nothing." cutter.mesh_path = "res://assets/viewmodel/cutter.glb" cutter.reach = 1.45 cutter.radius = 0.28 cutter.power = 0.55 cutter.impulse = 2.0 cutter.knockback = 1.0 cutter.cooldown = 0.15 cutter.swing = "jab" cutter.swing_time = 0.22 cutter.contact = 0.36 cutter.heft = 0.35 cutter.shake = 0.06 cutter.vs = {"cardboard": 9.0, "paper": 14.0, "vinyl": 2.6, "plastic": 2.0, "glass": 0.30, "wood": 0.14, "steel": 0.04} out.append(cutter) var bat := Weapon.new() bat.id = "bat" bat.display_name = "Cricket Bat" bat.blurb = "The all-rounder. Vinyl and glass do not enjoy it." bat.mesh_path = "res://assets/viewmodel/bat.glb" bat.reach = 2.10 bat.radius = 0.40 bat.power = 2.4 bat.impulse = 8.0 bat.knockback = 4.0 bat.cooldown = 0.40 bat.swing = "horizontal" bat.swing_time = 0.44 bat.contact = 0.36 bat.two_handed = false bat.heft = 1.0 bat.shake = 0.22 bat.vs = {"vinyl": 2.1, "glass": 1.7, "cardboard": 1.4, "wood": 1.2, "plastic": 1.3, "steel": 0.55, "paper": 0.6} out.append(bat) var crowbar := Weapon.new() crowbar.id = "crowbar" crowbar.display_name = "Crowbar" crowbar.blurb = "Bites metal. The filing cabinet has met its match." crowbar.mesh_path = "res://assets/viewmodel/crowbar.glb" crowbar.reach = 1.95 crowbar.radius = 0.34 crowbar.power = 3.0 crowbar.impulse = 9.0 crowbar.knockback = 4.5 crowbar.cooldown = 0.46 crowbar.swing = "overhead" crowbar.swing_time = 0.50 crowbar.contact = 0.40 crowbar.heft = 1.15 crowbar.shake = 0.26 crowbar.vs = {"steel": 1.9, "wood": 1.4, "glass": 1.3, "cardboard": 1.2, "vinyl": 1.2, "plastic": 1.5, "paper": 0.5} out.append(crowbar) var sledge := Weapon.new() sledge.id = "sledge" sledge.display_name = "Sledgehammer" sledge.blurb = "One swing a second. Worth the wait." sledge.mesh_path = "res://assets/viewmodel/sledge.glb" sledge.reach = 2.25 sledge.radius = 0.46 sledge.power = 6.5 sledge.impulse = 15.0 sledge.knockback = 8.0 sledge.cooldown = 0.85 sledge.swing = "overhead" sledge.swing_time = 0.78 sledge.contact = 0.46 sledge.two_handed = true sledge.heft = 1.9 sledge.shake = 0.46 sledge.vs = {"wood": 1.7, "glass": 1.5, "steel": 1.35, "plastic": 1.5, "vinyl": 1.2, "cardboard": 0.75, "paper": 0.4} out.append(sledge) var ext := Weapon.new() ext.id = "extinguisher" ext.display_name = "Fire Extinguisher" ext.blurb = "Heavy, awkward, and the best thing in the room to throw." ext.mesh_path = "res://assets/viewmodel/extinguisher.glb" ext.reach = 1.85 ext.radius = 0.44 ext.power = 4.5 ext.impulse = 13.0 ext.knockback = 7.0 ext.cooldown = 0.70 ext.swing = "horizontal" ext.swing_time = 0.66 ext.contact = 0.42 ext.two_handed = true ext.heft = 1.6 ext.shake = 0.38 ext.vs = {"glass": 1.9, "steel": 1.1, "wood": 1.0, "plastic": 1.4, "vinyl": 1.1, "cardboard": 0.9, "paper": 0.4} out.append(ext) return out