fix six critical bugs from audit sprint
save/load round-trip: workbench bills, crop static-method, bed owner, wolf target now all survive reload via Bill.from_dict reconstruction, _spawn_crop using setup(), and a new _post_load_resolve_references pass. PlantProvider: sow path added; consumes 1 grain on a TILLED crop tile. CraftingProvider: ingredient2 supported via new KIND_DEPOSIT_AT_WB toil and Workbench.deposited_inputs buffer. Cremation pyre now actually consumes wood. HaulingProvider: per-item haul_retry_count + haul_rejected after 3 orphan passes; new EventBus.stockpile_layout_changed resets rejects on any player stockpile edit. Storyteller: 14 stubbed event effects implemented. New buff registry (add_buff/get_buff_multiplier/has_buff, day-prune, save/load) drives seasonal/resource events. New request_pawn_spawn signal + WANDERER table for arrivals. New SICK status + 3 mood thoughts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
00cf8f445d
commit
d9638a4ea4
19 changed files with 711 additions and 101 deletions
|
|
@ -40,6 +40,17 @@ func _init() -> void:
|
|||
|
||||
## Returns a craft Job for `pawn`, or null if no valid work exists.
|
||||
## Pawn must expose: .carried_item, .tile (Vector2i), .get_skill(StringName) -> int.
|
||||
##
|
||||
## Single-ingredient recipes produce a 4-toil job:
|
||||
## walk_to(ing1) → pickup → walk_to(wb) → craft_at(wb, bill_index)
|
||||
##
|
||||
## Two-ingredient recipes (ingredient2_type != &"") produce a 7-toil job:
|
||||
## walk_to(ing1) → pickup → walk_to(wb) → deposit_at_wb(wb)
|
||||
## → walk_to(ing2) → pickup → craft_at(wb, bill_index)
|
||||
## The first ingredient is stashed in wb.deposited_inputs; _tick_craft consumes both.
|
||||
##
|
||||
## No-ingredient recipes (ingredient_type == &"") produce a 2-toil job:
|
||||
## walk_to(wb) → craft_at(wb, bill_index)
|
||||
func find_best_for(pawn) -> Job:
|
||||
# Skip if pawn is already carrying something — deposit first.
|
||||
if pawn.get("carried_item") != null:
|
||||
|
|
@ -48,7 +59,8 @@ func find_best_for(pawn) -> Job:
|
|||
var best_wb = null
|
||||
var best_bill = null
|
||||
var best_bill_index: int = -1
|
||||
var best_src = null
|
||||
var best_src1 = null
|
||||
var best_src2 = null
|
||||
var best_dist: int = 999999
|
||||
|
||||
for wb in World.workbenches:
|
||||
|
|
@ -69,48 +81,70 @@ func find_best_for(pawn) -> Job:
|
|||
_emit_bill_blocked(b.recipe.label, &"skill_too_low", wb)
|
||||
continue
|
||||
|
||||
# If ingredient_count is 0, no ingredient is required; proceed directly.
|
||||
# Otherwise, confirm a qualifying ingredient exists on the floor.
|
||||
var src = null
|
||||
if b.recipe.ingredient_count > 0:
|
||||
src = _find_ingredient_item(b.recipe.ingredient_type)
|
||||
if src == null:
|
||||
# Ingredient availability check.
|
||||
# Gate on ingredient_type being non-empty (ingredient_count is informational;
|
||||
# the canonical "no ingredient" signal is ingredient_type == &"").
|
||||
var src1 = null
|
||||
var src2 = null
|
||||
if b.recipe.ingredient_type != &"":
|
||||
src1 = _find_ingredient_item(b.recipe.ingredient_type)
|
||||
if src1 == null:
|
||||
_emit_bill_blocked(b.recipe.label, &"missing_ingredient", wb)
|
||||
continue
|
||||
|
||||
# Score: total Manhattan travel distance.
|
||||
# If no ingredient (count==0), distance is just pawn → workbench.
|
||||
# Otherwise, distance is pawn → ingredient → workbench.
|
||||
var d: int
|
||||
if b.recipe.ingredient_count > 0:
|
||||
d = _manhattan(pawn.tile, src.tile) + _manhattan(src.tile, wb.tile)
|
||||
else:
|
||||
d = _manhattan(pawn.tile, wb.tile)
|
||||
# Two-ingredient check: secondary ingredient must also be on the floor.
|
||||
if b.recipe.ingredient2_type != &"":
|
||||
src2 = _find_ingredient_item(b.recipe.ingredient2_type)
|
||||
if src2 == null:
|
||||
_emit_bill_blocked(b.recipe.label, &"missing_ingredient2", wb)
|
||||
continue
|
||||
|
||||
# Score: total Manhattan travel distance including both ingredient trips.
|
||||
# No-ingredient: pawn → wb.
|
||||
# One ingredient: pawn → ing1 → wb.
|
||||
# Two ingredients: pawn → ing1 → wb → ing2 → wb.
|
||||
var d: int = _manhattan(pawn.tile, wb.tile)
|
||||
if src1 != null:
|
||||
d = _manhattan(pawn.tile, src1.tile) + _manhattan(src1.tile, wb.tile)
|
||||
if src2 != null:
|
||||
d += _manhattan(wb.tile, src2.tile) + _manhattan(src2.tile, wb.tile)
|
||||
|
||||
if d < best_dist:
|
||||
best_dist = d
|
||||
best_wb = wb
|
||||
best_bill = b
|
||||
best_bill_index = i
|
||||
best_src = src
|
||||
best_src1 = src1
|
||||
best_src2 = src2
|
||||
|
||||
if best_wb == null:
|
||||
return null
|
||||
|
||||
var src_item = null
|
||||
# If ingredient_count > 0, re-resolve the source item in case multiple bills tied on the same item.
|
||||
if best_bill.recipe.ingredient_count > 0:
|
||||
src_item = _find_ingredient_item(best_bill.recipe.ingredient_type)
|
||||
if src_item == null:
|
||||
# Re-resolve ingredient items to guard against concurrent assignment races.
|
||||
if best_bill.recipe.ingredient_type != &"":
|
||||
best_src1 = _find_ingredient_item(best_bill.recipe.ingredient_type)
|
||||
if best_src1 == null:
|
||||
return null
|
||||
if best_bill.recipe.ingredient2_type != &"":
|
||||
best_src2 = _find_ingredient_item(best_bill.recipe.ingredient2_type)
|
||||
if best_src2 == null:
|
||||
return null
|
||||
|
||||
var j := Job.new()
|
||||
j.label = "Craft %s at %s" % [best_bill.recipe.label, best_wb.get("label_text") if best_wb.get("label_text") != null else "workbench"]
|
||||
j.target_node = best_wb
|
||||
|
||||
# Only add ingredient-haul toils if ingredient is required.
|
||||
if best_bill.recipe.ingredient_count > 0:
|
||||
j.toils.append(Toil.walk_to(src_item.tile))
|
||||
if best_src1 != null and best_src2 != null:
|
||||
# Two-ingredient path: deposit ing1 at wb, then fetch ing2 and craft.
|
||||
j.toils.append(Toil.walk_to(best_src1.tile))
|
||||
j.toils.append(Toil.pickup())
|
||||
j.toils.append(Toil.walk_to(best_wb.tile))
|
||||
j.toils.append(Toil.deposit_at_wb(best_wb.get_path()))
|
||||
j.toils.append(Toil.walk_to(best_src2.tile))
|
||||
j.toils.append(Toil.pickup())
|
||||
elif best_src1 != null:
|
||||
# Single-ingredient path: carry ing1 directly to craft.
|
||||
j.toils.append(Toil.walk_to(best_src1.tile))
|
||||
j.toils.append(Toil.pickup())
|
||||
|
||||
j.toils.append(Toil.walk_to(best_wb.tile))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue