P3 thieves and barrow: crows and foxes steal food left out; the barrow with its guards, coffin loot and key pieces; blunt club vs bone (protocol 10)
This commit is contained in:
115
scripts/creatures/crow.gd
Normal file
115
scripts/creatures/crow.gd
Normal file
@@ -0,0 +1,115 @@
|
||||
extends "res://scripts/creatures/creature.gd"
|
||||
## A crow (docs/PROGRESSION_DESIGN.md §4): sent by thieves.gd, by day, for
|
||||
## food left lying on the ground. It flies in, lands on it and pecks it away
|
||||
## a bit at a time, and takes off again if anyone comes within SCARE. Then
|
||||
## it flies off over the trees and is gone. A closed chest keeps food safe.
|
||||
## An arrow brings it down (feathers).
|
||||
|
||||
const SCARE := 7.0
|
||||
const PECK_EVERY := 3.0
|
||||
const FLY_SPEED := 9.0
|
||||
const CRUISE := 7.0 ## height above the ground while flying in
|
||||
const LEAVE_HEIGHT := 14.0
|
||||
|
||||
var thieves: Node ## thieves.gd
|
||||
var food: Node3D = null
|
||||
var _goal := Vector3.ZERO
|
||||
var _peck_anim := 0.0
|
||||
|
||||
func display_name() -> String:
|
||||
return "Crow"
|
||||
|
||||
func interact_prompt(p: Node) -> String:
|
||||
if is_dead():
|
||||
return "Crow [E] Pluck"
|
||||
return {"peck": "Crow (pecking at your food!)", "fly": "Crow (circling your food)"}.get(state, "Crow")
|
||||
|
||||
func _flying() -> bool:
|
||||
return state in ["fly", "fly_off"]
|
||||
|
||||
func _think(dt: float) -> void:
|
||||
match state:
|
||||
"fly":
|
||||
if not thieves.still_there(food) or thieves.watched(food.global_position, SCARE):
|
||||
_leave()
|
||||
return
|
||||
_goal = food.global_position
|
||||
var ground: float = terrain.sample_height(global_position.x, global_position.z)
|
||||
if Vector2(_goal.x - global_position.x, _goal.z - global_position.z).length() < 0.35 and global_position.y - ground < 0.15:
|
||||
state = "peck"
|
||||
_state_t = PECK_EVERY
|
||||
thieves.landed(self, food)
|
||||
"peck":
|
||||
if not thieves.still_there(food) or thieves.watched(global_position, SCARE):
|
||||
_leave()
|
||||
return
|
||||
_state_t -= dt
|
||||
if _state_t <= 0.0:
|
||||
_state_t = PECK_EVERY
|
||||
_peck_anim = 0.4
|
||||
if thieves.peck(self, food): # the last of it
|
||||
_leave()
|
||||
"fly_off":
|
||||
if thieves.out_of_sight(global_position):
|
||||
thieves.gone(self)
|
||||
_:
|
||||
_leave()
|
||||
|
||||
func _leave() -> void:
|
||||
state = "fly_off"
|
||||
food = null
|
||||
var p: Node3D = world.nearest_player(global_position)
|
||||
var away := Vector3(1, 0, 0)
|
||||
if p != null:
|
||||
away = global_position - p.global_position
|
||||
away.y = 0.0
|
||||
if away.length() < 0.1:
|
||||
away = Vector3(1, 0, 0)
|
||||
_goal = global_position + away.normalized() * 120.0
|
||||
|
||||
## Straight-line flight (no steering round trees: it flies over them).
|
||||
func _move(dt: float) -> void:
|
||||
if not _flying():
|
||||
speed = 0.0
|
||||
return
|
||||
var to := _goal - global_position
|
||||
to.y = 0.0
|
||||
var flat := to.length()
|
||||
var spd := FLY_SPEED
|
||||
if state == "fly" and flat < 6.0:
|
||||
spd = maxf(flat * 1.5, 1.2) # glide in to land
|
||||
var p := global_position
|
||||
if flat > 0.02:
|
||||
yaw = rotate_toward(yaw, atan2(-to.x, -to.z), 8.0 * dt)
|
||||
p += to / flat * minf(spd * dt, flat)
|
||||
var ground: float = terrain.sample_height(p.x, p.z)
|
||||
var alt := LEAVE_HEIGHT
|
||||
if state == "fly":
|
||||
alt = CRUISE * clampf((flat - 0.3) / 8.0, 0.0, 1.0)
|
||||
p.y = maxf(move_toward(global_position.y, maxf(ground, terrain.water_level) + alt, 5.0 * dt), ground)
|
||||
global_position = p
|
||||
rotation.y = yaw
|
||||
speed = spd
|
||||
|
||||
## Wings out and flapping in the air, folded on the ground; a dip of the
|
||||
## head when it pecks.
|
||||
func _animate(dt: float) -> void:
|
||||
var flying := _flying() or (puppet and speed > 0.5)
|
||||
_phase += dt * 16.0
|
||||
for l in _legs:
|
||||
var s := -1.0 if l.position.x < 0.0 else 1.0
|
||||
l.rotation.z = s * ((PI * 0.5 + sin(_phase) * 0.7) if flying else 0.2)
|
||||
_peck_anim = maxf(_peck_anim - dt, 0.0)
|
||||
if is_dead():
|
||||
return
|
||||
_visual.rotation.x = 0.5 * sin(_peck_anim / 0.4 * PI) if state == "peck" else 0.0
|
||||
|
||||
func _react(_from: Vector3) -> void:
|
||||
_leave()
|
||||
|
||||
## Brought down out of the air: it lands where it fell.
|
||||
func _die(from: Vector3) -> void:
|
||||
super._die(from)
|
||||
var p := global_position
|
||||
p.y = terrain.sample_height(p.x, p.z)
|
||||
global_position = p
|
||||
Reference in New Issue
Block a user