The rowing boat: seagrass at the tide line and resin from pines; rope, pitch, and a boat at the workbench (driftwood, rope, pitch, nails); tied up at a berth of its own at the end of the pier, one each, saved; Set sail needs the ship's compass and the Highlands aren't charted yet; online too
This commit is contained in:
158
scripts/world/moorings.gd
Normal file
158
scripts/world/moorings.gd
Normal file
@@ -0,0 +1,158 @@
|
||||
extends Node3D
|
||||
## The Cove's moorings (0.6.0, docs/WORLDLETS_DESIGN.md §5): berths along
|
||||
## the end of the pier, and a mooring post on the deck. Build a rowing boat at
|
||||
## a workbench, carry it down to the pier and press E at the post: it's tied
|
||||
## up at a berth of its own, one boat for each player, and it stays there
|
||||
## (saved with the Cove; online the server decides, and everyone sees
|
||||
## everyone's boat). Your own boat's prompt is "Set sail", but nobody's
|
||||
## charted the way to the Highlands yet: that's the next part of the journey,
|
||||
## and the ship's compass from the pirate chest is what it'll need.
|
||||
|
||||
const ItemMeshes = preload("res://scripts/items/item_meshes.gd")
|
||||
const BoatScript = preload("res://scripts/world/boat.gd")
|
||||
const PostScript = preload("res://scripts/world/mooring_post.gd")
|
||||
const BERTHS := 6 ## three each side (any further in, it shallows)
|
||||
const BOAT_SCALE := 8.0 ## the item's model (0.55 m) up to a boat (4.4 m)
|
||||
const BERTH_GAP := 5.0
|
||||
const BOAT_ITEM := "rowing_boat"
|
||||
|
||||
var world: Node3D
|
||||
var pier: Node3D
|
||||
var moored: Dictionary = {} ## player id -> {"berth": i, "name": who}
|
||||
var boats: Dictionary = {} ## player id -> boat.gd
|
||||
var post: StaticBody3D
|
||||
var _t := 0.0
|
||||
|
||||
func setup(w: Node3D) -> bool:
|
||||
world = w
|
||||
pier = w.pier
|
||||
if pier == null:
|
||||
return false
|
||||
add_to_group("moorings")
|
||||
post = StaticBody3D.new()
|
||||
post.name = "MooringPost"
|
||||
post.collision_layer = 1 | 2
|
||||
post.set_script(PostScript)
|
||||
post.moorings = self
|
||||
var st := SurfaceTool.new()
|
||||
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||
pier._box(st, Vector3(0, 0.45, 0), Vector3(0.24, 0.9, 0.24), pier.POST)
|
||||
pier._box(st, Vector3(0, 0.8, 0), Vector3(0.32, 0.08, 0.32), pier.POST.darkened(0.2))
|
||||
pier._box(st, Vector3(0, 0.62, 0), Vector3(0.27, 0.1, 0.27), Color(0.62, 0.55, 0.38)) # (a coil of rope)
|
||||
st.generate_normals()
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = st.commit()
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.vertex_color_use_as_albedo = true
|
||||
mat.roughness = 0.9
|
||||
mi.material_override = mat
|
||||
post.add_child(mi)
|
||||
var cs := CollisionShape3D.new()
|
||||
var box := BoxShape3D.new()
|
||||
box.size = Vector3(0.3, 0.9, 0.3)
|
||||
cs.shape = box
|
||||
cs.position = Vector3(0, 0.45, 0)
|
||||
post.add_child(cs)
|
||||
add_child(post)
|
||||
post.global_position = pier.end - pier.dir * 1.2 + _right() * (pier.WIDTH * 0.5 - 0.3)
|
||||
return true
|
||||
|
||||
func _right() -> Vector3:
|
||||
return pier.dir.cross(Vector3.UP).normalized()
|
||||
|
||||
## Where berth `i` is: alongside the end of the pier, in pairs, bow out to sea.
|
||||
func berth_xf(i: int) -> Transform3D:
|
||||
var side := 1.0 if i % 2 == 0 else -1.0
|
||||
var along: Vector3 = pier.end - pier.dir * (2.0 + (i / 2) * BERTH_GAP)
|
||||
var p: Vector3 = along + _right() * side * (pier.WIDTH * 0.5 + 1.0)
|
||||
p.y = world.terrain.sea_level - 0.08
|
||||
return Transform3D(Basis.looking_at(pier.dir, Vector3.UP), p)
|
||||
|
||||
func has_boat(id: String) -> bool:
|
||||
return moored.has(id)
|
||||
|
||||
func _free_berth() -> int:
|
||||
var used := moored.values().map(func(m): return int(m.berth))
|
||||
for i in range(BERTHS):
|
||||
if not i in used:
|
||||
return i
|
||||
return -1
|
||||
|
||||
## Player `p` (id `id`) ties up the boat in their bag. Says why not and
|
||||
## returns false when they can't.
|
||||
func moor(p: Node, id: String) -> bool:
|
||||
if has_boat(id):
|
||||
Actions.tell(p, "Your boat's already tied up here")
|
||||
return false
|
||||
if p.inventory.count(BOAT_ITEM) <= 0:
|
||||
Actions.tell(p, "Build a rowing boat at a workbench and bring it here to tie it up")
|
||||
return false
|
||||
var b := _free_berth()
|
||||
if b < 0:
|
||||
Actions.tell(p, "Every berth along the pier is taken")
|
||||
return false
|
||||
p.inventory.remove(BOAT_ITEM, 1)
|
||||
var who := String(p.get("display")) if p.get("display") != null else ""
|
||||
moored[id] = {"berth": b, "name": who}
|
||||
_sync()
|
||||
Actions.tell(p, "You tie your boat up at the end of the pier")
|
||||
Sfx.at("creak", post.global_position)
|
||||
if Net.is_server():
|
||||
WorldNet.mark_changed()
|
||||
return true
|
||||
|
||||
## Your boat: off to the Highlands. Not yet.
|
||||
func set_sail(p: Node) -> void:
|
||||
if p.inventory.count("ship_compass") <= 0:
|
||||
Actions.tell(p, "Past the bay it's open sea: without a ship's compass you'd be lost within the hour")
|
||||
return
|
||||
Actions.tell(p, "The compass needle points north over the water, but nobody's charted the way to the Highlands yet. (That's the next part of the journey: coming in a later version.)")
|
||||
|
||||
func _sync() -> void:
|
||||
for id in boats.keys():
|
||||
if not moored.has(id):
|
||||
boats[id].queue_free()
|
||||
boats.erase(id)
|
||||
for id in moored:
|
||||
if boats.has(id):
|
||||
continue
|
||||
var b: StaticBody3D = StaticBody3D.new()
|
||||
b.name = "Boat_%d" % int(moored[id].berth)
|
||||
b.collision_layer = 1 | 2
|
||||
b.set_script(BoatScript)
|
||||
b.moorings = self
|
||||
b.owner_id = String(id)
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.name = "Hull"
|
||||
mi.mesh = ItemMeshes.build(BOAT_ITEM)
|
||||
mi.material_override = ItemDB.item_material()
|
||||
mi.scale = Vector3.ONE * BOAT_SCALE
|
||||
b.add_child(mi)
|
||||
var cs := CollisionShape3D.new()
|
||||
var box := BoxShape3D.new()
|
||||
box.size = Vector3(1.4, 0.6, 4.2)
|
||||
cs.shape = box
|
||||
cs.position = Vector3(0, 0.3, 0)
|
||||
b.add_child(cs)
|
||||
add_child(b)
|
||||
b.global_transform = berth_xf(int(moored[id].berth))
|
||||
b.rest = b.global_transform
|
||||
boats[id] = b
|
||||
|
||||
## A gentle bob on the swell (just the look).
|
||||
func _process(delta: float) -> void:
|
||||
_t += delta
|
||||
for id in boats:
|
||||
var b: Node3D = boats[id]
|
||||
var k := float(int(moored[id].berth)) * 1.7
|
||||
b.global_transform = b.rest.translated(Vector3(0, sin(_t * 1.1 + k) * 0.05, 0)) * Transform3D(Basis(Vector3(0, 0, 1), sin(_t * 0.8 + k) * 0.03), Vector3.ZERO)
|
||||
|
||||
func to_data() -> Dictionary:
|
||||
return {"moored": moored.duplicate(true)}
|
||||
|
||||
func load_data(d: Dictionary) -> void:
|
||||
moored = {}
|
||||
var m: Dictionary = d.get("moored", {})
|
||||
for id in m:
|
||||
moored[String(id)] = {"berth": int(m[id].get("berth", 0)), "name": String(m[id].get("name", ""))}
|
||||
_sync()
|
||||
Reference in New Issue
Block a user