P3 alpha: wolf moon every 7th night, the alpha breaks wooden doors, iron-banded door, broken-door repair, alpha pelt and cloak (protocol 7)
This commit is contained in:
@@ -15,8 +15,13 @@ extends Node3D
|
||||
## While hunting the pack circles you and lets one wolf at a time dart in
|
||||
## (every few seconds, slower if you hold a torch). Near a lit campfire it
|
||||
## circles wide around the fire and never closes in.
|
||||
##
|
||||
## Every 7th night is a wolf moon (day_night.gd turns the moon red): an
|
||||
## alpha comes down from the hills with the pack. It leaves at dawn whether
|
||||
## or not you killed it, and comes back the next wolf moon (wolf.gd).
|
||||
|
||||
const WolfScript = preload("res://scripts/creatures/wolf.gd")
|
||||
const DayNight = preload("res://scripts/world/day_night.gd")
|
||||
|
||||
const DEN_DAY := 3 ## wolves arrive in the den
|
||||
const HUNT_DAY := 4 ## first night they hunt
|
||||
@@ -42,6 +47,14 @@ var _warned := {} ## day -> true once that night's toast was shown
|
||||
var _howl_left := 0.0
|
||||
var _audio: AudioStreamPlayer3D
|
||||
static var _howl: AudioStreamWAV
|
||||
var alpha: Node = null ## tonight's alpha (wolf moon)
|
||||
var _alpha_day := -1 ## the night it came
|
||||
var _told := {} ## "door id:outcome" -> true: door toasts already shown
|
||||
|
||||
## Night `day` is a wolf moon (once the pack hunts at all). The same rule as
|
||||
## day_night.gd's red moon.
|
||||
func wolf_moon(day: int) -> bool:
|
||||
return day >= hunt_day and DayNight.wolf_moon_day(day)
|
||||
|
||||
func setup(w: Node3D, d: Node3D) -> void:
|
||||
world = w
|
||||
@@ -139,6 +152,10 @@ func step(dt: float) -> void:
|
||||
|
||||
func _enter(m: String, day: int) -> void:
|
||||
mode = m
|
||||
if m == "hunt" and wolf_moon(day) and _alpha_day != day:
|
||||
spawn_alpha(day)
|
||||
elif m != "hunt":
|
||||
_alpha_leaves()
|
||||
if m == "none":
|
||||
for w in members:
|
||||
if is_instance_valid(w):
|
||||
@@ -160,6 +177,8 @@ func _warn(day: int) -> void:
|
||||
Actions.tell_all("Wolves are howling up in the hills. They'll come hunting soon.")
|
||||
elif day == hunt_day:
|
||||
Actions.tell_all("The wolves are hunting tonight. Stay near a fire or behind closed walls.")
|
||||
elif wolf_moon(day):
|
||||
Actions.tell_all("The moon is red: a wolf moon. Something big is hunting with the pack tonight.")
|
||||
_howl_left = _rng.randf_range(2.0, 6.0)
|
||||
|
||||
## Brings the pack up to size, wolves lying in the den. `force` ignores
|
||||
@@ -189,6 +208,65 @@ func fill_den(force: bool) -> void:
|
||||
need -= 1
|
||||
i += 1
|
||||
|
||||
## The alpha comes out of the den's mouth with the pack.
|
||||
func spawn_alpha(day: int) -> Node:
|
||||
_alpha_leaves()
|
||||
_alpha_day = day
|
||||
var w: AnimatableBody3D = WolfScript.new()
|
||||
w.setup("alpha", world, den.mouth_pos(), _rng.randi())
|
||||
w.pack = self
|
||||
w.slot = 4
|
||||
w.state = "stalk"
|
||||
w.yaw = den_yaw
|
||||
w.rotation.y = w.yaw
|
||||
add_child(w)
|
||||
members.append(w)
|
||||
alpha = w
|
||||
return w
|
||||
|
||||
## At dawn it goes back into the hills (a carcass stays for butchering).
|
||||
func _alpha_leaves() -> void:
|
||||
if alpha != null and is_instance_valid(alpha) and not alpha.is_dead():
|
||||
members.erase(alpha)
|
||||
alpha.queue_free()
|
||||
alpha = null
|
||||
|
||||
func alpha_alive() -> bool:
|
||||
return alpha != null and is_instance_valid(alpha) and not alpha.is_dead()
|
||||
|
||||
## The door the alpha `w` should go for to get at `target`: the closed door
|
||||
## nearest `target` (within `r`), a little biased toward ones nearer `w`.
|
||||
## `open_ones`: the nearest open or broken door instead (a way in).
|
||||
func door_for(w: Node3D, target: Node3D, r: float, open_ones: bool = false) -> Node3D:
|
||||
var best: Node3D = null
|
||||
var best_s := INF
|
||||
var tp: Vector3 = target.global_position
|
||||
for p in world.build.build_pieces():
|
||||
if not p.has_method("bash") or p.blocks() == open_ones:
|
||||
continue
|
||||
var d: float = p.global_position.distance_to(tp)
|
||||
if d > r:
|
||||
continue
|
||||
var score: float = d + 0.3 * p.global_position.distance_to(w.global_position)
|
||||
if score < best_s:
|
||||
best_s = score
|
||||
best = p
|
||||
return best
|
||||
|
||||
## The alpha hits `door`. Tells the player it's after (once per door and
|
||||
## outcome); on a server, every game hears the door broke.
|
||||
func bash_door(w: Node, door: Node3D, damage: float) -> void:
|
||||
var res: String = door.bash(damage)
|
||||
var key := "%d:%s" % [door.get_instance_id(), res]
|
||||
if not _told.has(key):
|
||||
_told[key] = true
|
||||
match res:
|
||||
"hit": Actions.tell(w.player, "Something is battering at your door!")
|
||||
"held": Actions.tell(w.player, "Something is battering at your door, but the iron bands hold.")
|
||||
"broke": Actions.tell(w.player, "The alpha has smashed your door in!")
|
||||
if res == "broke" and world.server_mode:
|
||||
WorldNet.piece_state(door)
|
||||
|
||||
func home_slot(w: Node) -> Vector3:
|
||||
return den.slot_pos(w.slot)
|
||||
|
||||
@@ -320,8 +398,9 @@ func howl() -> void:
|
||||
return
|
||||
if _audio == null:
|
||||
return
|
||||
_audio.global_position = (live[0] as Node3D).global_position + Vector3(0, 1, 0)
|
||||
_audio.pitch_scale = _rng.randf_range(0.9, 1.1)
|
||||
var from: Node3D = alpha if alpha_alive() else live[0] # (the alpha's howl is deeper)
|
||||
_audio.global_position = from.global_position + Vector3(0, 1, 0)
|
||||
_audio.pitch_scale = _rng.randf_range(0.68, 0.76) if alpha_alive() else _rng.randf_range(0.9, 1.1)
|
||||
_audio.play()
|
||||
|
||||
## A synthesised wolf howl: a rising then falling tone with vibrato and a
|
||||
|
||||
Reference in New Issue
Block a user