class_name CleaningProvider extends WorkProvider ## Phase 13 — WorkProvider for the Cleaning work category. ## ## Priority 2: fires when there is nothing more productive to do ## (below hauling=3, above rest=0). ## ## make_job / find_best_for logic: ## Scan DirtinessSystem.dirt_map for tiles with dirt >= DIRTY_THRESHOLD. ## Pick the nearest to the pawn by Manhattan distance. ## Build a 2-toil job: walk_to(tile) → kind_clean(tile). ## ## There is no Cleaning skill yet (Phase 17+ skill matrix). The clean toil ## takes a flat CLEAN_TICKS sim ticks to reduce dirt from any level to 0. ## ## JobRunner._tick_clean reduces DirtinessSystem.dirt_at(tile) by ## DIRT_REDUCTION_PER_TICK each sim tick until dirt <= 0. ## ## Audit.log fires on job start and on toil completion (via JobRunner). ## Dirt level at or above which a tile is worth cleaning. const DIRTY_THRESHOLD: float = 25.0 ## Base number of sim ticks to clean a tile from any level to 0. ## No skill modifier for now; Phase 17 wires skill × speed. const CLEAN_TICKS: int = 40 func _init() -> void: category = &"clean" priority = 2 # ── WorkProvider override ───────────────────────────────────────────────────── ## Returns a cleaning Job for `pawn`, or null if no dirty tiles exist. ## Picks the tile closest to `pawn` (Manhattan distance) with dirt >= DIRTY_THRESHOLD. func find_best_for(pawn) -> Job: # Safety — dirtiness system may not be wired yet (Agent A rooms arrive slightly later). if World.get("dirtiness_system") == null: return null var ds = World.dirtiness_system if ds == null: return null var best_tile: Vector2i = Vector2i(-1, -1) var best_dist: int = 999999 for tile in ds.dirt_map.keys(): var dirt_val: float = float(ds.dirt_map[tile]) if dirt_val < DIRTY_THRESHOLD: continue # target_node stores the Vector2i tile coordinate (field is untyped — accepts # non-Node values). Dirty tiles have no scene Node; the tile position itself # is the unique claim key. if Job.is_target_taken_by_other(tile, pawn): continue var d: int = abs(tile.x - pawn.tile.x) + abs(tile.y - pawn.tile.y) if d < best_dist: best_dist = d best_tile = tile if best_tile == Vector2i(-1, -1): return null var j := Job.new() j.label = "Clean (%d,%d)" % [best_tile.x, best_tile.y] j.target_node = best_tile j.toils.append(Toil.walk_to(best_tile)) j.toils.append(Toil.clean_at(best_tile)) return j