class_name MoveValidator extends RefCounted ## Pure §6 validation: an emitted move is legal iff its canonical key is in the ## turn's available_moves (which NpcContent already computed from live state). ## No state reads here — membership is the whole test. Invalid moves are not an ## error; they are dropped and returned separately so the caller can log them. const ID_MOVES := ["reveal", "offer_quest", "give_item", "accept_item"] const BARE_MOVES := ["adjust_disposition", "refuse", "end_conversation", "become_hostile"] static func _key(move: Dictionary) -> String: var name: String = move.get("name", "") var args: Array = move.get("args", []) if name in ID_MOVES: return "%s(%s)" % [name, str(args[0])] if not args.is_empty() else name return name # bare/value moves key on the name alone static func validate(moves: Array, available_moves: Array) -> Dictionary: var valid: Array = [] var dropped: Array = [] for move in moves: var name: String = move.get("name", "") if (name in ID_MOVES or name in BARE_MOVES) and _key(move) in available_moves: valid.append(move) else: dropped.append(move) return {"valid": valid, "dropped": dropped}