wire buff consumers, sick penalty, multi-count cremation
A: Storyteller.multiply_drops() stochastic-rounding helper drives crop_growth, harvest_yield, chop, mine consumption sites. sleep_decay multiplied in Pawn sleep tick. B: Pawn._sick_speed_penalty() (0% healthy → 75% severity 3, clamped to 25% min speed). JobRunner._work_speed_mult coin-flips per-tick progress on INTERACT/BUILD/CRAFT toils. Sleep/eat/treat unaffected. C: CraftingProvider builds N deposit trips for ingredient2_count > 1. JobRunner._tick_craft validates+consumes the full count from buffer. Cremation now actually requires and consumes 5 wood. crop._stage_accum round-trips through save/load to preserve buff- accumulated fractional growth. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d9638a4ea4
commit
2afca16299
9 changed files with 156 additions and 46 deletions
|
|
@ -142,11 +142,17 @@ func on_mine_tick() -> void:
|
|||
## Drop four stone Items (one per footprint tile) and free this node. Called
|
||||
## by on_mine_tick() automatically; can also be called for scripted removal.
|
||||
func mined() -> void:
|
||||
for ft in footprint_tiles():
|
||||
# Apply mine buff (veins_of_iron): multiply total stone drops with stochastic rounding,
|
||||
# then distribute one item per footprint tile (first N tiles get 1 each; extras stack
|
||||
# on the origin tile for simple overflow handling).
|
||||
var total_drops: int = Storyteller.multiply_drops(STONE_DROPS_ON_MINE, Storyteller.get_buff_multiplier(&"mine"))
|
||||
var tiles := footprint_tiles()
|
||||
for i in total_drops:
|
||||
var drop_tile: Vector2i = tiles[mini(i, tiles.size() - 1)]
|
||||
var item: Item = ITEM_SCENE.instantiate()
|
||||
get_parent().add_child(item)
|
||||
item.setup(Item.TYPE_STONE, 1, ft)
|
||||
Audit.log("big_rock", "mined 2×2 at %s; %d stone drops" % [origin_tile, STONE_DROPS_ON_MINE])
|
||||
item.setup(Item.TYPE_STONE, 1, drop_tile)
|
||||
Audit.log("big_rock", "mined 2×2 at %s; %d stone drops (buff mult=%.2f)" % [origin_tile, total_drops, Storyteller.get_buff_multiplier(&"mine")])
|
||||
if Audio != null:
|
||||
Audio.play_sfx(&"mine_tick")
|
||||
# BigRocks span 2×2 — clear the designation stamp from every footprint tile.
|
||||
|
|
|
|||
|
|
@ -49,6 +49,10 @@ const _STAGE_H: int = 32
|
|||
var stage: Stage = Stage.SOWN
|
||||
## Progress within the current growth stage; 0..STAGE_TICKS.
|
||||
var stage_progress: int = 0
|
||||
## Float accumulator for sub-tick growth when crop_growth buff is active.
|
||||
## Carries fractional progress between ticks so the buff has the correct
|
||||
## expected-value effect even though stage_progress is stored as int.
|
||||
var _stage_accum: float = 0.0
|
||||
|
||||
# Phase 13 — "no growth indoors" rule. True once we've logged the first
|
||||
# indoor detection for this crop instance so we don't flood the audit log.
|
||||
|
|
@ -106,9 +110,11 @@ func on_harvest_tick() -> void:
|
|||
if not is_harvestable():
|
||||
return
|
||||
var item_type := _harvest_output_for(crop_kind)
|
||||
var base_qty: int = 1
|
||||
var drop_qty: int = Storyteller.multiply_drops(base_qty, Storyteller.get_buff_multiplier(&"harvest_yield"))
|
||||
var it: Item = ITEM_SCENE.instantiate()
|
||||
get_parent().add_child(it)
|
||||
it.setup(item_type, 1, tile)
|
||||
it.setup(item_type, drop_qty, tile)
|
||||
# Carry the crop_kind through as a visual subtype so wheat / corn /
|
||||
# potato / strawberry each render distinctly, while item_type stays
|
||||
# generic (TYPE_GRAIN / TYPE_VEGETABLE) for stockpile filter purposes.
|
||||
|
|
@ -149,7 +155,13 @@ func _on_sim_tick(_n: int) -> void:
|
|||
# Crop has moved outdoors or was never indoors — reset the log flag so a
|
||||
# future re-roofing produces another audit line.
|
||||
_logged_indoor = false
|
||||
stage_progress += 1
|
||||
# Apply crop_growth buff: accumulate fractional progress each tick so the
|
||||
# expected-value growth rate exactly matches the multiplier.
|
||||
var growth_mult: float = Storyteller.get_buff_multiplier(&"crop_growth")
|
||||
_stage_accum += growth_mult
|
||||
var whole: int = int(floor(_stage_accum))
|
||||
_stage_accum -= float(whole)
|
||||
stage_progress += whole
|
||||
if stage_progress >= STAGE_TICKS:
|
||||
stage_progress = 0
|
||||
stage = (int(stage) + 1) as Stage
|
||||
|
|
@ -169,6 +181,7 @@ func to_dict() -> Dictionary:
|
|||
"crop_kind": String(crop_kind),
|
||||
"stage": int(stage),
|
||||
"stage_progress": stage_progress,
|
||||
"stage_accum": _stage_accum,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -182,6 +195,7 @@ static func from_dict(d: Dictionary) -> Dictionary:
|
|||
"crop_kind": StringName(d.get("crop_kind", "wheat")),
|
||||
"stage": int(d.get("stage", Stage.SOWN)),
|
||||
"stage_progress": int(d.get("stage_progress", 0)),
|
||||
"stage_accum": float(d.get("stage_accum", 0.0)),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -117,11 +117,12 @@ func on_mine_tick() -> void:
|
|||
## Drop stone Item(s) and free this node. Called automatically by on_mine_tick()
|
||||
## but also accessible for scripted removal (debug, storyteller events).
|
||||
func mined() -> void:
|
||||
# Single drop lands on the rock's own tile.
|
||||
# Apply mine buff (veins_of_iron): multiply stone drops with stochastic rounding.
|
||||
var drop_count: int = Storyteller.multiply_drops(STONE_DROPS_ON_MINE, Storyteller.get_buff_multiplier(&"mine"))
|
||||
var item: Item = ITEM_SCENE.instantiate()
|
||||
get_parent().add_child(item)
|
||||
item.setup(Item.TYPE_STONE, 1, tile)
|
||||
Audit.log("rock", "mined at %s; %d stone drop" % [tile, STONE_DROPS_ON_MINE])
|
||||
item.setup(Item.TYPE_STONE, drop_count, tile)
|
||||
Audit.log("rock", "mined at %s; %d stone drop(s) (buff mult=%.2f)" % [tile, drop_count, Storyteller.get_buff_multiplier(&"mine")])
|
||||
if Audio != null:
|
||||
Audio.play_sfx(&"mine_tick")
|
||||
World.clear_designation_at(tile)
|
||||
|
|
|
|||
|
|
@ -241,14 +241,16 @@ func on_chop_tick() -> void:
|
|||
## Drop wood Items and free this node. Called by on_chop_tick() automatically,
|
||||
## but also accessible for scripted felling (debug, storyteller events).
|
||||
func fell() -> void:
|
||||
var drop_tiles := _pick_drop_tiles()
|
||||
# Apply chop buff (lumberjacks_luck): multiply total wood drops with stochastic rounding.
|
||||
var total_drops: int = Storyteller.multiply_drops(WOOD_DROPS_ON_FELL, Storyteller.get_buff_multiplier(&"chop"))
|
||||
var drop_tiles := _pick_drop_tiles_count(total_drops)
|
||||
var drops_count := 0
|
||||
for drop_tile in drop_tiles:
|
||||
var item: Item = ITEM_SCENE.instantiate()
|
||||
get_parent().add_child(item)
|
||||
item.setup(Item.TYPE_WOOD, STACK_SIZE_PER_DROP, drop_tile)
|
||||
drops_count += 1
|
||||
Audit.log("tree", "felled at %s; %d wood drops" % [tile, drops_count])
|
||||
Audit.log("tree", "felled at %s; %d wood drops (buff mult=%.2f)" % [tile, drops_count, Storyteller.get_buff_multiplier(&"chop")])
|
||||
if Audio != null:
|
||||
Audio.play_sfx(&"tree_fell")
|
||||
World.clear_designation_at(tile)
|
||||
|
|
@ -310,10 +312,12 @@ func _draw() -> void:
|
|||
|
||||
# ── helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
## Returns up to WOOD_DROPS_ON_FELL tile positions for wood drops.
|
||||
## Prefers the tree's own tile then walkable 4-neighbours; falls back to the
|
||||
## tree tile for any remaining drops when neighbours are scarce.
|
||||
func _pick_drop_tiles() -> Array[Vector2i]:
|
||||
## Returns `count` tile positions for wood drops.
|
||||
## Prefers the tree's own tile then walkable 4-neighbours; fills any remaining
|
||||
## slots with the tree tile if neighbours are scarce.
|
||||
## Previously took an implicit count of WOOD_DROPS_ON_FELL; now accepts an
|
||||
## explicit count so the chop buff can request more drops.
|
||||
func _pick_drop_tiles_count(count: int) -> Array[Vector2i]:
|
||||
var chosen: Array[Vector2i] = []
|
||||
|
||||
# First drop always goes on the tree's tile itself.
|
||||
|
|
@ -322,14 +326,14 @@ func _pick_drop_tiles() -> Array[Vector2i]:
|
|||
# Remaining drops prefer walkable neighbours.
|
||||
var offsets: Array[Vector2i] = [Vector2i(1, 0), Vector2i(-1, 0), Vector2i(0, 1), Vector2i(0, -1)]
|
||||
for offset in offsets:
|
||||
if chosen.size() >= WOOD_DROPS_ON_FELL:
|
||||
if chosen.size() >= count:
|
||||
break
|
||||
var candidate: Vector2i = tile + offset
|
||||
if World.pathfinder != null and World.pathfinder.is_walkable(candidate):
|
||||
chosen.append(candidate)
|
||||
|
||||
# Fill any remaining slots with the tree tile (all 3 land there if boxed in).
|
||||
while chosen.size() < WOOD_DROPS_ON_FELL:
|
||||
# Fill any remaining slots with the tree tile (all land there if boxed in).
|
||||
while chosen.size() < count:
|
||||
chosen.append(tile)
|
||||
|
||||
return chosen
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue