서버 스크립트

Chain2D.Maps

한 채널이 쥐고 있는 맵들. 맵은 이름으로 다룹니다 — 여기서 돌려주는 손잡이는 그 이름을 닫아 쥔 함수들의 표이고, 맵 자체를 쥐지 않습니다. 언로드된 맵을 쥔 손잡이는 채널이 더는 갖고 있지 않은 메모리에 쓰는 일이 되고, 그 사이에도 참인 채로 남는 것은 이름뿐이기 때문입니다. 그래서 손잡이의 함수는 부를 때마다 이름을 첫 인자로 넘겨 맵을 다시 찾습니다. 이 표의 함수들은 그렇지 않습니다 — `Maps.of` 와 `Maps.move` 는 사람으로 묻고, `Maps.get` 과 `Maps.list` 는 채널이 아는 이름 전부를 훑습니다.

Maps.load

선언
Chain2D.Maps.load(name: string) -> Map
매개변수
namestring창작자가 붙이는 이름. 1 바이트 이상 64 바이트 이하입니다 — 빈 이름은 "a map needs a name", 긴 이름은 "that map name is too long" 오류입니다. 이름은 네이티브를 부를 때마다 비교되므로, 길이를 두지 않으면 아무도 원하지 않은 비용을 매 틱 내게 됩니다.
반환

Map그 이름의 맵 손잡이. `Chain2D.Map` 페이지에 있는 것과 같은 모양입니다.

이름 붙인 맵을 만들거나, 이미 있으면 그것을 돌려줍니다. 같은 이름은 같은 맵입니다 — 사람이 들어올 때마다 마을을 새로 짓지 않습니다. 내려간 맵의 이름을 부르면 빈 맵으로 되살아납니다. 내려간 맵을 부르는 것은 그것을 다시 달라는 뜻이고, 그것이 load 가 늘 뜻하던 것입니다. 되살아난 맵은 비어 있고 `Maps.define` 이 등록한 짓는 함수는 여기서 불리지 않으므로, 칠하는 것은 부른 쪽의 몫입니다. 그리고 되살린 뒤로는 그 맵이 더는 다시 지어야 할 맵으로 세어지지 않아 `Maps.move` 도 짓는 함수를 부르지 않습니다 — 짓는 함수에 맡길 생각이면 여기서 부르지 말고 `Maps.move` 가 하게 두는 편이 낫습니다.

local dungeon = Chain2D.Maps.load("dungeon")
dungeon.loadTiles("dungeon")
print(dungeon.name, Chain2D.Maps.stateOf(dungeon.name))

Maps.define

선언
Chain2D.Maps.define(name: string, builder: (map: Map) -> ()) -> Map
매개변수
namestring맵 이름. 문자열이 아니면 오류가 납니다.
builder(map: Map) -> ()이 맵을 짓는 함수. 등록하는 즉시 손잡이를 받아 한 번 불리고, 그 뒤로는 내려간 이 맵으로 사람을 보낼 때 다시 불립니다. 함수가 아니면 오류가 납니다.
반환

Map그 맵의 손잡이. 짓는 함수가 이미 한 번 돌고 난 뒤입니다.

짓는 법을 알려 준 맵을 만듭니다. 알려 주면 채널이 그 맵을 비었을 때 내려도 됩니다. 알려 주지 않은 맵은 재우기만 하고 내리지는 않습니다 — 맵은 이 스크립트가 실행 중에 칠한 것이라 엔진에는 그것을 되살릴 방법이 없고, 되살릴 수 없는 것을 버리는 것은 메모리를 아끼는 일이 아니라 장소를 잃는 일이기 때문입니다. 만드는 함수는 Luau 에 남습니다. 엔진은 그것이 있다는 사실만 알고, 다시 짓는 일은 스크립트가 이미 돌고 있는 자리 — 내려간 맵으로 사람을 보내는 순간 — 에서 일어납니다.

Chain2D.Maps.define("cellar", function(map)
    map.loadTiles("cellar")
    map.setCapacity(8)
end)

Maps.get

선언
Chain2D.Maps.get(name: string) -> Map?
매개변수
namestring찾을 맵 이름. 문자열이 아니면 오류가 납니다.
반환

Map?그 이름의 맵을 이 채널이 알고 있으면 손잡이, 모르면 `nil`.

이미 있는 맵만 찾습니다. `Maps.load` 와 달리 없는 이름을 만들지 않습니다. 한 번 만들어진 이름은 맵이 내려간 뒤에도 남습니다. 그래서 손잡이가 왔다는 것이 지금 그 맵이 서 있다는 뜻은 아닙니다 — 그것은 `Maps.stateOf` 가 답합니다.

local cellar = Chain2D.Maps.get("cellar")
if cellar == nil then
    print("지하실은 아직 없습니다")
end

Maps.list

선언
Chain2D.Maps.list() -> {Map}
반환

{Map}이 채널이 이름을 알고 있는 모든 맵의 손잡이. 만들어진 순서입니다.

이 채널의 맵을 전부 돌려줍니다. 내려간 맵도 이름은 남아 있으므로 함께 옵니다. 어느 것이 지금 돌고 있는지는 `Maps.stateOf` 로 갈라 봅니다.

for _, map in ipairs(Chain2D.Maps.list()) do
    print(map.name, Chain2D.Maps.stateOf(map.name), map.playerCount())
end

Maps.of

선언
Chain2D.Maps.of(player: Player) -> Map?
매개변수
playerPlayer접속해 있는 Player. 표가 아니거나 `id` 가 문자열이 아니면 오류가 납니다.
반환

Map?이 사람이 지금 있는 맵의 손잡이. 어느 맵에도 없으면 `nil`.

이 사람이 지금 어느 맵에 있는지 묻습니다. 사람이 나가는 순간에도 답합니다 — `Players.playerRemoving` 이 몸을 내리는 자리이고, 어느 맵인지 모르면 내릴 수가 없습니다. 마을만 지우면 지하에 있던 사람의 몸이 아무도 몰지 않는 채로 남습니다.

local map = Chain2D.Maps.of(player)
if map ~= nil then
    local body = map.bodyOf(player)
    if body ~= nil then map.despawn(body) end
end

Maps.move

선언
Chain2D.Maps.move(player: Player, destination: string, params: table?, x: number, y: number) -> (boolean, string)
매개변수
playerPlayer접속해 있는 Player. 표가 아니거나 `id` 가 문자열이 아니면 오류가 납니다.
destinationstring갈 맵의 이름. 그 이름의 맵이 없으면 거부됩니다.
paramstable?목적지에서 설 몸의 값. `Map.spawn` 의 params 와 같은 표이지만 움직임에 대한 값만 읽습니다 — `sprite` 는 여기서 읽지 않으므로 옮겨 간 몸은 그림 없이(색칠한 사각형으로) 섭니다. 그림이 필요하면 목적지에서 그 몸을 `Map.despawn` 으로 내리고 `Map.spawn` 으로 다시 세운 뒤 `Map.setOwner` 로 넘깁니다. `nil` 이면 빈 표이고 그때는 전부 기본값입니다. 인자 자리는 비울 수 없습니다 — 다섯 개를 받으므로 하나를 빠뜨리면 스폰 자리가 어긋나 오류가 납니다.
xnumber목적지에서 설 자리. 유한한 수여야 합니다.
ynumber목적지에서 설 자리. 유한한 수여야 합니다.
반환

(boolean, string)갔으면 `true` 와 빈 문자열. 못 갔으면 `false` 와 이유가 옵니다: "no map by that name is loaded", "the player is not in a map", "the destination is full", "the destination has not been built", "the game refused the move".

사람을 다른 맵으로 옮깁니다. 원래 맵의 몸은 사라지고 목적지에 새로 하나 서며 주인도 따라갑니다 — 두 맵은 서로 다른 시뮬레이션이고, 한 몸이 양쪽에 있을 수는 없습니다. 그래서 캐릭터에 대해 넘어가는 것은 여기 넘긴 움직임 값이 전부입니다. 실패는 오류가 아니라 값으로 돌아옵니다. 방이 꽉 찼다거나 문이 잠겼다는 것은 스크립트의 결함이 아니라 게임이 할 말이 있는 평범한 대답입니다. 거부할 수 있는 것은 전부 몸을 해체하기 전에 물으므로, 거부는 아무것도 바꾸지 않고 사람은 있던 자리에 그대로 있습니다. 목적지가 내려간 맵이면 `Maps.define` 이 등록한 함수로 먼저 다시 짓습니다. 짓지 못하면 보내지 않습니다 — 타일 없는 곳에 서면 영원히 떨어지는 것 말고는 화면에 아무 설명도 없습니다. 정원은 떠나는 맵과 목적지가 다를 때만 봅니다.

local ok, why = Chain2D.Maps.move(player, "dungeon", params, 200, 150)
if not ok then
    Chain2D.Network.send(player, "door", { refused = why })
end

Maps.stateOf

선언
Chain2D.Maps.stateOf(name: string) -> string
매개변수
namestring맵 이름. 문자열이 아니면 오류가 납니다.
반환

string"active" 는 돌고 있는 것, "sleeping" 은 올라와 있지만 돌지 않는 것, "unloaded" 는 타일도 몸도 놓아 버린 것입니다. 한 번도 만든 적 없는 이름도 "unloaded" 라고 답합니다.

맵이 지금 무엇을 하고 있는지. 비어 있는 채로 30초가 지나면 재우고, 그 뒤로 300초가 더 지나면 `Maps.define` 으로 짓는 법을 알려 준 맵만 내립니다. 잠든 맵에서는 몸이 움직이지 않습니다. 엔진 안에 숨긴 것이 아니라 창작자가 볼 수 있게 한 결정입니다 — 아무도 보지 않는 동안에도 순찰을 돌아야 하는 매복은 깨어 있는 맵을 원합니다. 방금 빈 맵은 30초 동안 계속 도는데, 걸어 나갔다 곧장 돌아온 사람이 두고 간 것과 다른 방을 보아서는 안 되기 때문입니다.

if Chain2D.Maps.stateOf("cellar") == "unloaded" then
    print("지하실은 내려가 있습니다")
end