P4 hazards: food spoilage with rot speed setting, smoking rack and salt, food poisoning, harsher blizzards, hard winters with lake ice, lightning on trees, snow on roofs (protocol 11)
This commit is contained in:
@@ -26,6 +26,7 @@ const PIECES := {
|
||||
"bed": preload("res://scripts/building/bed.gd"),
|
||||
"kiln": preload("res://scripts/building/processor.gd"),
|
||||
"smelter": preload("res://scripts/building/processor.gd"),
|
||||
"smoker": preload("res://scripts/building/processor.gd"),
|
||||
"forge": preload("res://scripts/building/forge.gd"),
|
||||
"hearth": preload("res://scripts/building/hearth.gd"),
|
||||
"rug": preload("res://scripts/building/rug.gd"),
|
||||
@@ -65,6 +66,7 @@ var bobber: Node3D ## bobber.gd: the cast fishing float, if any
|
||||
var wolves: Node3D ## wolf_pack.gd (null if this seed has no den site)
|
||||
var bears: Node3D ## bear_range.gd: the island's bear (null online as a client)
|
||||
var thieves: Node3D ## thieves.gd: crows and foxes after food left out
|
||||
var hazards: Node3D ## hazards.gd: lightning on trees, hard-winter ice (P4)
|
||||
var barrow: Node3D ## barrow.gd: the burial mound and its guards (scenery; guards where creatures run)
|
||||
var _saved_bear: Dictionary = {} ## its saved state, kept until bears exists (and re-saved if it never does)
|
||||
var net_sync: Node ## net_sync.gd when playing on a server, else null
|
||||
@@ -165,7 +167,7 @@ func air_warmth(pos: Vector3, sheltered: bool = false) -> float:
|
||||
if sheltered:
|
||||
w += ww * 0.25 + 8.0
|
||||
else:
|
||||
w += ww
|
||||
w += ww - weather.blizzard_chill(false) # (P4: the wind cuts through you)
|
||||
return w
|
||||
|
||||
## 0..1 heat from fires and a held torch.
|
||||
@@ -326,6 +328,7 @@ func skip_time(secs: float) -> void:
|
||||
for f in get_tree().get_nodes_in_group("heat"):
|
||||
if f.has_method("burn"):
|
||||
f.burn(secs)
|
||||
spoil_all() # (food goes off overnight too)
|
||||
|
||||
func _on_terrain_ready() -> void:
|
||||
var t0 := Time.get_ticks_msec()
|
||||
@@ -357,6 +360,12 @@ func _on_terrain_ready() -> void:
|
||||
print("[world] scenery placed in %d ms" % (Time.get_ticks_msec() - t0))
|
||||
if menu_mode:
|
||||
return
|
||||
# Weather hazards (P4): lightning on trees, the hard winter's ice. Decided
|
||||
# here unless we're a player's game online (then the server tells us).
|
||||
hazards = preload("res://scripts/world/hazards.gd").new()
|
||||
hazards.name = "Hazards"
|
||||
add_child(hazards)
|
||||
hazards.setup(self, not is_online())
|
||||
if server_mode:
|
||||
if Saves.has_world():
|
||||
var saved: Dictionary = Saves.load_state(Saves.current_id)
|
||||
@@ -376,6 +385,7 @@ func _on_terrain_ready() -> void:
|
||||
_setup_bear()
|
||||
apply_rules()
|
||||
return
|
||||
_apply_world_rot()
|
||||
_respawn()
|
||||
if Saves.has_world():
|
||||
var state: Dictionary = Saves.load_state(Saves.current_id)
|
||||
@@ -430,6 +440,48 @@ func apply_rules() -> void:
|
||||
wolves.pack_bonus = int(R.difficulty("pack"))
|
||||
for p in PlayerNet.proxies.values():
|
||||
p.survival.drain_mult = R.difficulty("drain")
|
||||
Inventory.rot_speed = ROT_SPEEDS.get(R.rot(), 1.0)
|
||||
|
||||
# --- food spoilage (P4, docs/PROGRESSION_DESIGN.md §5) ----------------------------
|
||||
|
||||
const ROT_SPEEDS := {"normal": 1.0, "slow": 0.5, "off": 0.0}
|
||||
const SPOIL_EVERY := 2.0
|
||||
var _spoil_left := 0.0
|
||||
|
||||
## Keeps Inventory's clock (food's "rot" stamps) on world time. Players'
|
||||
## games online run it too (for showing freshness; the server does the rotting).
|
||||
func _tick_clock() -> void:
|
||||
Inventory.day_seconds = float(day_night.day_length)
|
||||
Inventory.clock = weather.world_seconds()
|
||||
|
||||
## The single-player world's rot setting, from its meta.json (a server's
|
||||
## comes from its rules: apply_rules()).
|
||||
func _apply_world_rot() -> void:
|
||||
var r := String(Saves.read_meta(Saves.current_id).get("rot", "normal")) if Saves.has_world() else "normal"
|
||||
Inventory.rot_speed = ROT_SPEEDS.get(r, 1.0)
|
||||
|
||||
## Food rots: in everyone's bags, in chests, graves and racks, and lying on
|
||||
## the ground (where rotten food just goes). Where the game decides (single
|
||||
## player, a server); players' games online are sent the result.
|
||||
func spoil_all() -> void:
|
||||
if net_sync != null or menu_mode:
|
||||
return
|
||||
_tick_clock()
|
||||
for p in players():
|
||||
if p.get("inventory") != null:
|
||||
p.inventory.spoil()
|
||||
for piece in all_pieces():
|
||||
if piece.get("inventory") != null:
|
||||
piece.inventory.spoil()
|
||||
if Inventory.rot_speed <= 0.0:
|
||||
return
|
||||
for d in all_dropped():
|
||||
if Inventory.keeps_days(d.item_id) <= 0.0:
|
||||
continue
|
||||
if not d.extra.has("rot"):
|
||||
d.extra.rot = Inventory.fresh_rot(d.item_id)
|
||||
elif Inventory.clock >= float(d.extra.rot):
|
||||
take_dropped(d)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if menu_mode:
|
||||
@@ -442,6 +494,11 @@ func _process(delta: float) -> void:
|
||||
var lake: Vector2 = terrain.lake_center
|
||||
_menu_cam.look_at(Vector3(lake.x * 0.5, 2.0, lake.y * 0.5), Vector3.UP)
|
||||
return
|
||||
_tick_clock()
|
||||
_spoil_left -= delta
|
||||
if _spoil_left <= 0.0:
|
||||
_spoil_left = SPOIL_EVERY
|
||||
spoil_all()
|
||||
if not Saves.has_world() or get_tree().paused:
|
||||
return
|
||||
_session_time += delta
|
||||
@@ -619,8 +676,8 @@ func add_dropped_entry(e: Dictionary) -> Node3D:
|
||||
if ItemDB.get_item(e.get("item", "")).is_empty():
|
||||
return null
|
||||
var ex: Dictionary = {}
|
||||
if e.get("extra") is Dictionary and e.extra.has("dur"):
|
||||
ex = {"dur": int(e.extra.dur)}
|
||||
if e.get("extra") is Dictionary:
|
||||
ex = Inventory.extra_of(e.extra) # (a worn tool's "dur", food's "rot")
|
||||
var d := _make_dropped(e.item, int(e.get("count", 1)), _array_to_xf(e.xf), ex)
|
||||
if e.has("nid"):
|
||||
d.set_meta("nid", int(e.nid))
|
||||
@@ -781,6 +838,8 @@ func _update_grass_cuts() -> void:
|
||||
func water_at(x: float, z: float) -> Dictionary:
|
||||
var ground: float = terrain.sample_height(x, z)
|
||||
if terrain.inside_boundary(x, z, -2.0) and ground < terrain.water_level:
|
||||
if hazards != null and hazards.iced(x, z):
|
||||
return {} # (frozen over in a hard winter: cast further out)
|
||||
return {"type": "lake", "surface": terrain.water_level, "depth": terrain.water_level - ground}
|
||||
if terrain.sea_weight(x, z) > 0.5 and ground < terrain.sea_level:
|
||||
return {"type": "sea", "surface": terrain.sea_level, "depth": terrain.sea_level - ground}
|
||||
|
||||
Reference in New Issue
Block a user