From 654486288593ed33d2ed03e0856e7b387b276d95 Mon Sep 17 00:00:00 2001 From: Paul Bailey Date: Fri, 6 Nov 2026 22:33:14 +0000 Subject: [PATCH] Map: other players in your land shown on a co-op server, never with PvP on --- scripts/ui/map_screen.gd | 25 +++++++++++++++++++++++++ tests/test_net_rules.gd | 5 +++++ tools/site/guide_sections.md | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/scripts/ui/map_screen.gd b/scripts/ui/map_screen.gd index 0087d57..dc12ec5 100644 --- a/scripts/ui/map_screen.gd +++ b/scripts/ui/map_screen.gd @@ -31,6 +31,7 @@ var _tex: TextureRect var _marks: Control var _title: Label var _facing: Label +var _others_key: Label func setup(w: Node3D, p: Node3D) -> void: world = w @@ -75,6 +76,9 @@ func setup(w: Node3D, p: Node3D) -> void: var you := UITheme.label("● You", 14) you.add_theme_color_override("font_color", Color(1, 1, 1)) legend.add_child(you) + _others_key = UITheme.label("● Other players (co-op)", 14) + _others_key.add_theme_color_override("font_color", Color(0.6, 1.0, 0.65)) + legend.add_child(_others_key) for k in MARKS: var l := UITheme.label("■ %s" % MARKS[k][1], 14) l.add_theme_color_override("font_color", MARKS[k][0]) @@ -92,6 +96,7 @@ func is_open() -> bool: func open() -> void: _tex.texture = ImageTexture.create_from_image(render(world)) _title.text = "The %s" % preload("res://scripts/world/terrain.gd").worldlet_name(world.worldlet_id()) + _others_key.visible = world.is_online() and not bool(Net.server_info.get("pvp", true)) visible = true player.ui_open = true Input.mouse_mode = Input.MOUSE_MODE_VISIBLE @@ -132,6 +137,13 @@ func _draw_marks() -> void: _marks.draw_rect(Rect2(at - Vector2(r, r), Vector2(r, r) * 2.0), Color(0, 0, 0, 0.7), false, 1.0) if m.kind in ["den", "barrow", "stones", "seam", "pier", "cave"]: _marks.draw_string(font, at + Vector2(9, 5), String(m.label), HORIZONTAL_ALIGNMENT_LEFT, -1, 13, col) + # Other players in this land: only on a co-op server (with PvP on, the + # map never gives anyone away). Green dots with their names. + for o in others(world): + var at2 := _to_map(o.pos) + _marks.draw_circle(at2, 5.5, Color(0, 0, 0, 0.8)) + _marks.draw_circle(at2, 4.0, Color(0.45, 0.95, 0.5)) + _marks.draw_string(font, at2 + Vector2(8, 5), String(o.name), HORIZONTAL_ALIGNMENT_LEFT, -1, 13, Color(0.6, 1.0, 0.65)) # You: a dot (the way you're facing is on the compass). var p := player.global_position if world.underground_at(p) and world.tunnel != null: @@ -143,6 +155,19 @@ func _draw_marks() -> void: _marks.draw_string(font, Vector2(SIDE - 26, 24), "N", HORIZONTAL_ALIGNMENT_LEFT, -1, 18, Color(1.0, 0.55, 0.35)) _marks.draw_line(Vector2(SIDE - 20, 30), Vector2(SIDE - 20, 48), Color(1.0, 0.55, 0.35), 2.0) +## The other players to show: [{name, pos}], everyone in this land on a +## server with PvP off; nobody single-player or with PvP on (rivals and +## adversarial), so the map can't be used to hunt anyone down. +static func others(w: Node3D) -> Array: + var out: Array = [] + if not w.is_online() or bool(Net.server_info.get("pvp", true)) or w.net_sync == null: + return out + for peer in w.net_sync.avatars: + var a: Node3D = w.net_sync.avatars[peer] + if is_instance_valid(a) and a.visible: + out.append({"name": String(a.info.get("name", "?")), "pos": a.global_position}) + return out + ## The map image: the terrain, coloured and shaded, blank where unexplored. static func render(w: Node3D) -> Image: var t: Node3D = w.terrain diff --git a/tests/test_net_rules.gd b/tests/test_net_rules.gd index dbae562..ae960b9 100644 --- a/tests/test_net_rules.gd +++ b/tests/test_net_rules.gd @@ -194,9 +194,14 @@ func _pvp() -> void: await _bob({"do": "ask", "what": "hit_player", "args": [Net.local_peer]}) await _sleep(0.6) check(PN.hurts == hurts0 and player.survival.health >= h0 - 0.5, "with PvP off, Bob's club does nothing to Alice") + # The map (issue #1): with PvP off, Bob is on Alice's map... + var MS: Script = load("res://scripts/ui/map_screen.gd") + await _until(func(): return not MS.others(world).is_empty(), 3.0) + check(MS.others(world).any(func(o): return o.name == "Bob"), "co-op: Bob shows on Alice's map (%s)" % [MS.others(world).map(func(o): return o.name)]) C.say("/set pvp true") await _until(func(): return bool(Net.server_info.get("pvp", false)), 2.0) check(bool(Net.server_info.get("pvp", false)), "Alice turns PvP on, and every game hears the new rules") + check(MS.others(world).is_empty(), "...and with PvP on, nobody's on the map but you") await _bob({"do": "ask", "what": "hit_player", "args": [Net.local_peer]}) await _until(func(): return PN.hurts > hurts0, 2.0) await _sleep(0.3) diff --git a/tools/site/guide_sections.md b/tools/site/guide_sections.md index b9566a6..dbb8a5e 100644 --- a/tools/site/guide_sections.md +++ b/tools/site/guide_sections.md @@ -43,7 +43,7 @@ The tutorial island is calm: no wolf pack (just one demonstration wolf), no bear - `1` to `8` or the mouse wheel pick a hotbar slot. - `K` and `M` move the hotbar up and down your bag's rows, so `1` to `8` pick from the row above or below. The hotbar shows which row you're on. - `Q` drops what you're holding at your feet; `Shift` and `Q` drops just one. -- `N` opens the map. It starts blank and fills in as you explore, marking the places you've found, your camp and your bed. You're the white dot. +- `N` opens the map. It starts blank and fills in as you explore, marking the places you've found, your camp and your bed. You're the white dot. On a co-op server, the other players in your land show as green dots with their names; with PvP on (friendly rivals or adversarial), the map shows nobody but you. - The compass across the top of the screen shows which way you're facing. - `Tab` or `I` opens your inventory and crafting. - In the inventory: `Left click` picks up or puts down a stack, `Right click` splits a stack or puts down one, `Shift` + click moves a stack to or from a chest, `Shift` + click on **Craft** makes up to five.