class_name Items extends RefCounted ## Every item in the game, defined in code for the same reasons [Content] is. ## ## [constant ORDER] is load-bearing: an item's position in it is the byte that ## rides the snapshot and every item event. Item ids are strings and a slot is ## sent 20 times a second per player, so the wire carries the index instead. ## Append to the list, never reorder it -- and if you do reorder it, bump ## [constant Protocol.VERSION], because every client would otherwise decode a ## potion as a ration. const NONE := &"" const HEALTH_POTION := &"health_potion" const WARDENS_RATION := &"wardens_ration" ## Wire order. Index 0 is reserved for "empty slot" / "no item", so an id's ## wire value is its position here plus one. const ORDER: Array[StringName] = [ HEALTH_POTION, WARDENS_RATION, ] static func get_def(id: StringName) -> ItemDef: match id: HEALTH_POTION: return health_potion() WARDENS_RATION: return wardens_ration() return null ## Wire value for an id. 0 for an empty slot or an id this build does not know, ## which is the safe direction: an unknown item reads as nothing rather than as ## whatever happens to sit at that index. static func index_of(id: StringName) -> int: var at := ORDER.find(id) return at + 1 if at >= 0 else 0 static func by_index(index: int) -> StringName: if index <= 0 or index > ORDER.size(): return NONE return ORDER[index - 1] static func display_name_of(id: StringName) -> String: var def := get_def(id) return def.display_name if def != null else "" # --- The items -------------------------------------------------------------- ## The only item with an effect. Percentage-based so it stays meaningful across ## the whole level range -- a flat 40 hp would be a full heal at level 1 and ## noise at level 15. static func health_potion() -> ItemDef: var d := ItemDef.new() d.id = HEALTH_POTION d.display_name = "Health Potion" d.effect = ItemDef.Effect.HEAL d.effect_value = 40.0 d.visual = 0 d.tint = Color(0.95, 0.4, 0.42) return d ## Dropped by every boss, one per player who was alive for the kill, and does ## nothing whatsoever when used. ## ## It is not filler. Player-instanced loot is a different code path from shared ## loot -- a separate entity per owner, filtered per peer on the wire -- and the ## cheapest way to keep that path honest is to have something in the game that ## uses it every single boss kill. A trophy nobody has to fight over is exactly ## the right shape for that job. static func wardens_ration() -> ItemDef: var d := ItemDef.new() d.id = WARDENS_RATION d.display_name = "Warden's Ration" d.effect = ItemDef.Effect.NONE d.visual = 1 d.tint = Color(0.95, 0.82, 0.35) return d