class_name PlacementReport extends RefCounted const ERROR_INVALID_DEFINITION := &"INVALID_DEFINITION" const ERROR_MISSING_DEPENDENCY := &"MISSING_DEPENDENCY" const ERROR_OUT_OF_BOUNDS := &"OUT_OF_BOUNDS" const ERROR_ZONE_MISMATCH := &"ZONE_MISMATCH" const ERROR_LAYER_MISMATCH := &"LAYER_MISMATCH" const ERROR_OVERLAP := &"OVERLAP" const ERROR_NOT_EXCAVATED := &"NOT_EXCAVATED" const ERROR_UNSUPPORTED := &"UNSUPPORTED" const ERROR_INSUFFICIENT_RESOURCES := &"INSUFFICIENT_RESOURCES" var can_build := false var error_codes: Array[StringName] = [] var reasons: Array[Dictionary] = [] var cells: Array[Vector3i] = [] var out_of_bounds_cells: Array[Vector3i] = [] var zone_mismatch_cells: Array[Vector3i] = [] var layer_mismatch_cells: Array[Vector3i] = [] var occupied_cells: Array[Vector3i] = [] var unexcavated_cells: Array[Vector3i] = [] var unsupported_cells: Array[Vector3i] = [] var access_points: Array[Vector3i] = [] var mana_connection := false var total_cost: Dictionary = {} var missing_resources: Dictionary = {} var build_time := 0 var predicted_environment_delta: Dictionary = {} var predicted_operating_cost: Dictionary = {} var predicted_heat := 0 func add_error( code: StringName, reason: String, affected_cells: Array = [], details: Dictionary = {} ) -> void: if not error_codes.has(code): error_codes.append(code) var normalized_cells: Array[Vector3i] = [] for cell: Variant in affected_cells: if cell is Vector3i: normalized_cells.append(cell) reasons.append({ &"code": code, &"message": reason, &"cells": normalized_cells, &"details": details.duplicate(true), }) can_build = false func finalize() -> PlacementReport: can_build = error_codes.is_empty() return self func has_error(code: StringName) -> bool: return error_codes.has(code) func reason_for(code: StringName) -> Dictionary: for reason: Dictionary in reasons: if StringName(reason.get(&"code", &"")) == code: return reason.duplicate(true) return {} func to_dictionary() -> Dictionary: var serialized_reasons: Array[Dictionary] = [] for reason: Dictionary in reasons: var serialized := reason.duplicate(true) serialized[&"code"] = String(reason.get(&"code", &"")) serialized[&"cells"] = _serialize_cells(reason.get(&"cells", [])) serialized_reasons.append(serialized) return { &"can_build": can_build, &"error_codes": _string_names_to_strings(error_codes), &"reasons": serialized_reasons, &"cells": _serialize_cells(cells), &"out_of_bounds_cells": _serialize_cells(out_of_bounds_cells), &"zone_mismatch_cells": _serialize_cells(zone_mismatch_cells), &"layer_mismatch_cells": _serialize_cells(layer_mismatch_cells), &"occupied_cells": _serialize_cells(occupied_cells), &"unexcavated_cells": _serialize_cells(unexcavated_cells), &"unsupported_cells": _serialize_cells(unsupported_cells), &"access_points": _serialize_cells(access_points), &"mana_connection": mana_connection, &"total_cost": total_cost.duplicate(true), &"missing_resources": missing_resources.duplicate(true), &"build_time": build_time, &"predicted_environment_delta": predicted_environment_delta.duplicate(true), &"predicted_operating_cost": predicted_operating_cost.duplicate(true), &"predicted_heat": predicted_heat, } static func _serialize_cells(source: Variant) -> Array[Array]: var serialized: Array[Array] = [] if not source is Array: return serialized for cell: Variant in source: if cell is Vector3i: serialized.append([cell.x, cell.y, cell.z]) return serialized static func _string_names_to_strings(source: Array[StringName]) -> Array[String]: var serialized: Array[String] = [] for value: StringName in source: serialized.append(String(value)) return serialized