@amxts/semiclip 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +140 -2
- package/README.ru.md +141 -0
- package/package.json +63 -5
- package/prebuilt/linux/semiclip.aot +0 -0
- package/prebuilt/manifest.json +64 -0
- package/prebuilt/windows/semiclip.aot +0 -0
- package/scripts/docs/src/index.ts +66 -0
- package/src/index.ts +146 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kukson777
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,141 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="assets/logo.svg" width="96" alt="Semiclip">
|
|
4
|
+
|
|
5
|
+
# Semiclip
|
|
6
|
+
|
|
7
|
+
*Who walks through whom, as a rule over two players*
|
|
8
|
+
|
|
9
|
+
[](https://amxts.github.io/)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
[Features](#features) • [Installation](#installation) • [Usage](#usage) • [API](#api)
|
|
13
|
+
|
|
14
|
+
**English** | [Русский](README.ru.md)
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
Semiclip lets players walk through each other. amxts does it on the server itself, as [pairs of players](https://amxts.github.io/docs/game/players#walking-through-each-other) (`player.walksThrough`); this module lets a plugin say who walks through whom as a rule over two players, `(player, target) => boolean`, and keeps the pairs up to date with it.
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **Works as soon as it is installed.** Teammates walk through each other, see-through up close and not drawn right against you, before any plugin says a word.
|
|
23
|
+
- **A rule, not pairs by hand.** One function says whether one player walks through another; the module works every pair out.
|
|
24
|
+
- **Kept up to date by itself.** A spawn, a death, a team change, a player coming in, and a change of any field plugins add to `Player` work the pairs out again - only the pairs of the player it happened to.
|
|
25
|
+
- **Nothing else on the server.** No AMX Mod X module is needed: the pairs are amxts's own.
|
|
26
|
+
- **One instance per server.** Every plugin that uses it sets the same rule.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
The project needs `@amxts/core` 0.3.3 or later.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx amxts module add semiclip
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
It installs the package and adds it to your project's `amxts.config.ts`:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
export default defineConfig({
|
|
40
|
+
modules: ["@amxts/semiclip"],
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
A plugin uses the module as `semiclip`, without an import line: the build adds the import to the plugins that use it, and builds the module only when some plugin does.
|
|
47
|
+
|
|
48
|
+
Installed, the module needs no plugin: teammates walk through each other, and a partner is see-through closer than 250 units and not drawn closer than 48 (the core's [`server.walkThroughLook`](#how-a-partner-looks)). A plugin changes who walks through whom with a rule - everyone through everyone, on a KZ server:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
semiclip.rule = () => true;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Setting the rule is all it takes: the module works every pair out, and works a player's pairs out again when he spawns, dies, changes sides or comes in. `semiclip.rule = null` makes everyone solid; when the plugin that set the rule stops - unloaded or reloaded - teammates walk through each other again, until a rule is set again.
|
|
55
|
+
|
|
56
|
+
### A rule that changes in play
|
|
57
|
+
|
|
58
|
+
`spawnProtected` below is the plugin's own [field on `Player`](https://amxts.github.io/docs/game/players#shared-player-fields), declared with `declare module`: it starts `false`, every plugin on the server reads and writes it, and it is cleared when the player leaves. A player who has just spawned walks through everyone alive for 3 seconds, and teammates walk through each other:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
declare module "@amxts/core" {
|
|
62
|
+
interface Player {
|
|
63
|
+
spawnProtected: boolean;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
semiclip.rule = (player, target) =>
|
|
68
|
+
player.isAlive && target.isAlive && (player.spawnProtected || player.team == target.team);
|
|
69
|
+
|
|
70
|
+
game.addEventListener("spawn", ({ player }) => {
|
|
71
|
+
player.spawnProtected = true; // walks through everyone for 3 seconds
|
|
72
|
+
setTimeout(() => (player.spawnProtected = false), 3000);
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The module works the pairs out again by itself when the field changes - on the spawn, and when the timer clears it - with no `update()` call.
|
|
77
|
+
|
|
78
|
+
### When the pairs are worked out
|
|
79
|
+
|
|
80
|
+
| When | Which pairs |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `rule` is set | every pair |
|
|
83
|
+
| a player comes into the game | his |
|
|
84
|
+
| a player spawns, dies or changes sides | his |
|
|
85
|
+
| a field plugins add to `Player` changes on a player - any plugin's, or Pawn's | his |
|
|
86
|
+
| a player leaves | his pairs go with him |
|
|
87
|
+
| `semiclip.update(player)` | `player`'s |
|
|
88
|
+
| `semiclip.update()` | every pair |
|
|
89
|
+
|
|
90
|
+
`update()` is for what the rule reads that the module does not hear - a round's mode kept in a variable, a plugin's own record of a player:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
let everyone = false;
|
|
94
|
+
|
|
95
|
+
semiclip.rule = (player, target) => everyone || player.team == target.team;
|
|
96
|
+
|
|
97
|
+
server.addCommand("/party", () => {
|
|
98
|
+
everyone = !everyone;
|
|
99
|
+
semiclip.update();
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Either way lets a pair through
|
|
104
|
+
|
|
105
|
+
`rule(player, target)` says whether `player` walks through `target`, but a pair is one: two players walk through each other, or neither does. The pair walks through when the rule lets either one - `rule(a, b) || rule(b, a)`. A rule that says yes for a protected player lets him through everyone, and everyone through him, with no `|| target.spawnProtected` needed.
|
|
106
|
+
|
|
107
|
+
### Who walks through whom now
|
|
108
|
+
|
|
109
|
+
The pairs are the core's: `player.walksThrough` is the players he walks through, as a `Set`:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
player.walksThrough.has(other); // whether the two walk through each other
|
|
113
|
+
for (const partner of player.walksThrough) { } // each of them
|
|
114
|
+
const partners = [...player.walksThrough];
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### How a partner looks
|
|
118
|
+
|
|
119
|
+
A player one walks through can be see-through up close, or not drawn at all, so he does not block the view. That is the core's `server.walkThroughLook`, for every player: off in the core, and the module turns it on as below. A plugin sets its own, or `{ fadeDistance: 0, hideDistance: 0 }` to turn it off:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
server.walkThroughLook = {
|
|
123
|
+
fadeDistance: 250, // closer than 250 units a partner is see-through
|
|
124
|
+
opacity: 0.4, // 40% opaque right up close, whole at 250
|
|
125
|
+
hideDistance: 48, // closer than 48 units he is not drawn at all, and no target of your aim
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
> [!WARNING]
|
|
130
|
+
> On a server without ReGameDLL a partner standing between a player and a flash blocks its light as anyone does. Walking through and the look work on every server; a grenade bounces off a partner.
|
|
131
|
+
|
|
132
|
+
### One rule
|
|
133
|
+
|
|
134
|
+
The rule is one for the server. A plugin that sets it over another plugin's replaces it, and the console warns: the last one set is used. Plugins that each have a say write it into fields (`player.spawnProtected`) that one rule reads.
|
|
135
|
+
|
|
136
|
+
## API
|
|
137
|
+
|
|
138
|
+
| Member | What it is |
|
|
139
|
+
| --- | --- |
|
|
140
|
+
| `semiclip.rule` | `(player, target) => boolean` - whether `player` walks through `target`; a pair walks through when either way says so. Setting it works every pair out; `null` makes everyone solid again. |
|
|
141
|
+
| `semiclip.update(player?)` | Works `player`'s pairs out again, or everyone's, after something the rule reads changed that the module does not hear. |
|
package/README.ru.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/logo.svg" width="96" alt="Semiclip">
|
|
4
|
+
|
|
5
|
+
# Semiclip
|
|
6
|
+
|
|
7
|
+
*Кто сквозь кого проходит — правилом над двумя игроками*
|
|
8
|
+
|
|
9
|
+
[](https://amxts.github.io/)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
[Возможности](#возможности) • [Установка](#установка) • [Использование](#использование) • [API](#api)
|
|
13
|
+
|
|
14
|
+
[English](README.md) | **Русский**
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
Семиклип позволяет игрокам проходить сквозь друг друга. amxts делает это на сервере сам, [парами игроков](https://amxts.github.io/ru/docs/game/players#проход-сквозь-друг-друга) (`player.walksThrough`); этот модуль даёт плагину сказать, кто сквозь кого проходит, правилом над двумя игроками, `(player, target) => boolean`, и держит пары в курсе.
|
|
19
|
+
|
|
20
|
+
## Возможности
|
|
21
|
+
|
|
22
|
+
- **Работает сразу после установки.** Союзники проходят друг сквозь друга, вблизи полупрозрачны, а вплотную не рисуются — ещё до того, как какой-то плагин что-то сказал.
|
|
23
|
+
- **Правило, а не пары вручную.** Одна функция говорит, проходит ли один игрок сквозь другого; модуль рассчитывает каждую пару.
|
|
24
|
+
- **Держится в курсе сам.** Появление, смерть, смена команды, вход игрока и изменение любого поля, которое плагины добавили в `Player`, заново рассчитывают пары — только пары того игрока, с которым это случилось.
|
|
25
|
+
- **Больше ничего на сервере.** Модуль AMX Mod X не нужен: пары — собственные у amxts.
|
|
26
|
+
- **Один экземпляр на сервер.** Все плагины, которые им пользуются, задают одно и то же правило.
|
|
27
|
+
|
|
28
|
+
## Установка
|
|
29
|
+
|
|
30
|
+
Проекту нужен `@amxts/core` 0.3.3 или новее.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx amxts module add semiclip
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Команда ставит пакет и добавляет его в `amxts.config.ts` проекта:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
export default defineConfig({
|
|
40
|
+
modules: ["@amxts/semiclip"],
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Использование
|
|
45
|
+
|
|
46
|
+
Плагин пользуется модулем как `semiclip`, без строки импорта: сборка добавляет импорт в те плагины, которые им пользуются, и собирает модуль, только когда им пользуется хоть один.
|
|
47
|
+
|
|
48
|
+
Установленному модулю плагин не нужен: союзники проходят друг сквозь друга, а партнёр полупрозрачен ближе 250 юнитов и не рисуется ближе 48 (`server.walkThroughLook` ядра, [ниже](#как-выглядит-партнёр)). Кто сквозь кого проходит, плагин меняет правилом — например, все сквозь всех на KZ-сервере:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
semiclip.rule = () => true;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Больше ничего не нужно: модуль рассчитывает каждую пару и заново рассчитывает пары игрока, когда тот появляется, умирает, меняет команду или входит. `semiclip.rule = null` делает всех твёрдыми; когда плагин, который задал правило, останавливается (его выгрузили или перезагрузили), снова проходят союзники — пока правило не задано снова.
|
|
55
|
+
|
|
56
|
+
### Правило, которое меняется в игре
|
|
57
|
+
|
|
58
|
+
`spawnProtected` ниже — собственное [поле плагина в `Player`](https://amxts.github.io/ru/docs/game/players#общие-поля-игрока), объявленное через `declare module`: сначала оно `false`, его читают и пишут все плагины сервера, а когда игрок уходит, оно сбрасывается. Только что появившийся игрок 3 секунды проходит сквозь всех живых, а союзники — сквозь друг друга:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
declare module "@amxts/core" {
|
|
62
|
+
interface Player {
|
|
63
|
+
spawnProtected: boolean;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
semiclip.rule = (player, target) =>
|
|
68
|
+
player.isAlive && target.isAlive && (player.spawnProtected || player.team == target.team);
|
|
69
|
+
|
|
70
|
+
game.addEventListener("spawn", ({ player }) => {
|
|
71
|
+
player.spawnProtected = true; // 3 секунды проходит сквозь всех
|
|
72
|
+
setTimeout(() => (player.spawnProtected = false), 3000);
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Модуль сам заново рассчитывает пары, когда поле меняется, — при появлении и когда таймер его снимает, — без вызова `update()`.
|
|
77
|
+
|
|
78
|
+
### Когда пары рассчитываются
|
|
79
|
+
|
|
80
|
+
| Когда | Какие пары |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| задано `rule` | все пары |
|
|
83
|
+
| игрок вошёл в игру | его |
|
|
84
|
+
| игрок появился, умер или сменил команду | его |
|
|
85
|
+
| у игрока изменилось поле, которое плагины добавили в `Player`, — любым плагином или Pawn | его |
|
|
86
|
+
| игрок ушёл | его пары уходят вместе с ним |
|
|
87
|
+
| `semiclip.update(player)` | пары `player` |
|
|
88
|
+
| `semiclip.update()` | все пары |
|
|
89
|
+
|
|
90
|
+
`update()` — для того, что читает правило, но чего модуль не слышит: режим раунда в переменной, собственная запись плагина об игроке:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
let everyone = false;
|
|
94
|
+
|
|
95
|
+
semiclip.rule = (player, target) => everyone || player.team == target.team;
|
|
96
|
+
|
|
97
|
+
server.addCommand("/party", () => {
|
|
98
|
+
everyone = !everyone;
|
|
99
|
+
semiclip.update();
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Пару пропускает любая сторона
|
|
104
|
+
|
|
105
|
+
`rule(player, target)` говорит, проходит ли `player` сквозь `target`, но пара одна: двое либо проходят сквозь друг друга, либо нет. Пара проходит, когда правило разрешает хотя бы одному — `rule(a, b) || rule(b, a)`. Правило, которое говорит да для защищённого игрока, пропускает его сквозь всех и всех сквозь него, без `|| target.spawnProtected`.
|
|
106
|
+
|
|
107
|
+
### Кто сквозь кого проходит сейчас
|
|
108
|
+
|
|
109
|
+
Пары — это пары ядра: `player.walksThrough` — игроки, сквозь которых он проходит, как `Set`:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
player.walksThrough.has(other); // проходят ли двое сквозь друг друга
|
|
113
|
+
for (const partner of player.walksThrough) { } // каждый из них
|
|
114
|
+
const partners = [...player.walksThrough];
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Как выглядит партнёр
|
|
118
|
+
|
|
119
|
+
Игрок, сквозь которого проходишь, вблизи может быть полупрозрачным или не рисоваться совсем, чтобы не закрывать обзор. Это `server.walkThroughLook` ядра — для всех игроков: в ядре выключено, а модуль включает его, как ниже. Плагин задаёт свой или выключает его через `{ fadeDistance: 0, hideDistance: 0 }`:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
server.walkThroughLook = {
|
|
123
|
+
fadeDistance: 250, // ближе 250 юнитов партнёр полупрозрачный
|
|
124
|
+
opacity: 0.4, // вплотную непрозрачен на 40%, целиком — на 250
|
|
125
|
+
hideDistance: 48, // ближе 48 юнитов его не видно совсем, и прицел сквозь него
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
> [!WARNING]
|
|
130
|
+
> На сервере без ReGameDLL партнёр, который стоит между игроком и флешкой, закрывает её свет, как любой другой. Проход сквозь и вид работают на любом сервере; граната от партнёра отскакивает.
|
|
131
|
+
|
|
132
|
+
### Одно правило
|
|
133
|
+
|
|
134
|
+
Правило на сервере одно. Плагин, который задаёт его поверх правила другого плагина, заменяет его, а консоль предупреждает: действует последнее заданное. Плагины, у каждого из которых есть своё слово, пишут его в поля (`player.spawnProtected`), которые читает одно правило.
|
|
135
|
+
|
|
136
|
+
## API
|
|
137
|
+
|
|
138
|
+
| Член | Что это |
|
|
139
|
+
| --- | --- |
|
|
140
|
+
| `semiclip.rule` | `(player, target) => boolean` — проходит ли `player` сквозь `target`; пара проходит, когда разрешает любая сторона. Присваивание рассчитывает каждую пару; `null` снова делает всех твёрдыми. |
|
|
141
|
+
| `semiclip.update(player?)` | Заново рассчитывает пары `player` или всех, после того как изменилось то, что читает правило и чего модуль не слышит. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@amxts/semiclip",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"description": "Who walks through whom, for amxts plugins: semiclip as a rule over two players, on the core's own pairs.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "kukson777",
|
|
8
|
+
"url": "https://github.com/kukson777"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"homepage": "https://github.com/amxts/semiclip#readme",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/amxts/semiclip.git"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"amxts-module",
|
|
18
|
+
"amxts-semiclip",
|
|
19
|
+
"amxmodx",
|
|
20
|
+
"counter-strike"
|
|
21
|
+
],
|
|
22
|
+
"exports": {
|
|
23
|
+
".": "./src/index.ts"
|
|
24
|
+
},
|
|
25
|
+
"types": "./src/index.ts",
|
|
26
|
+
"files": [
|
|
27
|
+
"README.md",
|
|
28
|
+
"README.ru.md",
|
|
29
|
+
"scripts/docs",
|
|
30
|
+
"src",
|
|
31
|
+
"prebuilt"
|
|
32
|
+
],
|
|
33
|
+
"amxts": {
|
|
34
|
+
"core": "^0.3.3",
|
|
35
|
+
"module": "src/index.ts"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"check": "amxts check",
|
|
40
|
+
"lint": "oxlint && oxfmt --check",
|
|
41
|
+
"lint:fix": "oxfmt && oxlint --fix --silent || oxlint --fix --silent || oxlint --fix"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@amxts/core": "^0.3.3"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@e18e/eslint-plugin": "^0.8.1",
|
|
48
|
+
"@eslint-community/eslint-plugin-eslint-comments": "^4.8.1",
|
|
49
|
+
"@stylistic/eslint-plugin": "^5.10.0",
|
|
50
|
+
"@types/node": "^24.0.0",
|
|
51
|
+
"eslint-plugin-antfu": "^3.2.3",
|
|
52
|
+
"eslint-plugin-command": "^4.0.0",
|
|
53
|
+
"eslint-plugin-n": "^18.4.0",
|
|
54
|
+
"eslint-plugin-perfectionist": "^5.12.1",
|
|
55
|
+
"eslint-plugin-regexp": "^3.3.1",
|
|
56
|
+
"oxfmt": "^0.70.0",
|
|
57
|
+
"oxlint": "^1.85.0",
|
|
58
|
+
"typescript": "^6.0.3",
|
|
59
|
+
"vitest": "^5.0.3"
|
|
60
|
+
},
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"format": 2,
|
|
3
|
+
"module": "@amxts/semiclip",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"from": {
|
|
6
|
+
"@amxts/core": {
|
|
7
|
+
"version": "0.3.3",
|
|
8
|
+
"hash": "dd6ff1554558754384eb03d07c4a688d1d696b91",
|
|
9
|
+
"abi": "0.3.3+abi.1ff73017\nabort (iiii)\namxts_rpc (iiii)i\namxts_rpc_reply (ii)\namxts_rpc_result (i)\namxts_rpc_take (i)\namxts_serve (ii)\narg (i)i\narg_text (iii)i\nconsole.error (i)\nconsole.warn (i)\ncreate_cvar (iiiiiiii)i s,s,v,s,v,v,v,v\ncvar_hook (ii)i\ngame_api ()i\nget_cvar_pointer (i)i s leaf\nget_maxplayers ()i\nget_players (iiii)i [32>,[1>,s,s\nget_user_msgid (i)i s leaf\nham (iiii)i\nhook (iii)i\nhook_direct (iiii)\nhook_on (ii)\nmember_get (iii)i\nmember_slot (ii)i\nmsg_argc ()i\nmsg_hook (ii)i\nmsg_number (i)F\non (iii)\non_direct (iiiiii)\nperformance.now ()F\nplayer_change_get (i)F\nplayer_change_get_text (iii)i\nplayer_change_listen (ii)\nplayer_data_set (iiF)\nplayer_names (i)\nplayer_slots (i)\nplugin (iiii)\nprecache_generic (i)i s\nprecache_model (i)i s\nprecache_sound (i)i s\nstack_frames (ii)i\ntag (i)\nwalk_look (iF)\nwalk_through (iii)\nwalk_through_get (i)i"
|
|
10
|
+
},
|
|
11
|
+
"@amxts/semiclip": {
|
|
12
|
+
"version": "0.2.0",
|
|
13
|
+
"hash": "9eb0fb0c9aa7785a7e0bbefa716eb6dff46ce7a7"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"options": {
|
|
17
|
+
"@amxts/semiclip": {}
|
|
18
|
+
},
|
|
19
|
+
"forwards": {},
|
|
20
|
+
"places": [
|
|
21
|
+
"constants.ts",
|
|
22
|
+
"database.ts",
|
|
23
|
+
"effects.ts",
|
|
24
|
+
"entities.ts",
|
|
25
|
+
"events.ts",
|
|
26
|
+
"facade.ts",
|
|
27
|
+
"fetch.ts",
|
|
28
|
+
"flags.ts",
|
|
29
|
+
"fs.ts",
|
|
30
|
+
"hlds.ts",
|
|
31
|
+
"hooks.ts",
|
|
32
|
+
"jobs.ts",
|
|
33
|
+
"kit.ts",
|
|
34
|
+
"modules/semiclip.ts",
|
|
35
|
+
"natives.ts",
|
|
36
|
+
"promise.ts",
|
|
37
|
+
"semiclip.ts",
|
|
38
|
+
"sql.ts",
|
|
39
|
+
"vector.ts"
|
|
40
|
+
],
|
|
41
|
+
"natives": [],
|
|
42
|
+
"beside": {
|
|
43
|
+
"enums": [],
|
|
44
|
+
"contract": null,
|
|
45
|
+
"author": null
|
|
46
|
+
},
|
|
47
|
+
"systems": {
|
|
48
|
+
"windows": {
|
|
49
|
+
"file": "prebuilt/windows/semiclip.aot",
|
|
50
|
+
"size": 479672,
|
|
51
|
+
"sha256": "c0fc85a9fc3e242fe522521b8c32e6d2bee818f07e19c0d41987e5fbf87a3f0c"
|
|
52
|
+
},
|
|
53
|
+
"linux": {
|
|
54
|
+
"file": "prebuilt/linux/semiclip.aot",
|
|
55
|
+
"size": 484848,
|
|
56
|
+
"sha256": "681b91517feef63531201bf4279897997432defb4dd9faa28eacbf6273632df2"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"surface": {
|
|
60
|
+
"proxy": "// GENERATED by scripts/shared-modules.ts: ~/modules/semiclip in a plugin that does not own it.\n// Each function runs in semiclip.aot, the one instance on the server.\nimport { Reader as __Reader, Writer as __Writer, beginCallback as __beginCallback, callBack as __callBack, receiveFunction as __receiveFunction, remoteTarget as __remoteTarget, sendFunction as __sendFunction, Service as __Service } from \"~/remote\";\nimport { Player as __Player } from \"~/facade\";\nimport { Player } from \"~/facade\";\n\nexport type SemiclipRule = (player: Player, target: Player) => boolean;\n\n/** Semiclip in semiclip's plugin: each field is read and written there, each method runs there. */\nexport class Semiclip {\n\tconstructor(public __handle: i32) {}\n\n\tget rule(): ((a0: __Player, a1: __Player) => bool) | null {\n\t\tconst w = __service.begin(0);\n\t\tw.i32(this.__handle);\n\t\treturn __r0(__service.call(w));\n\t}\n\tset rule(value: ((a0: __Player, a1: __Player) => bool) | null) {\n\t\tconst w = __service.begin(1);\n\t\tw.i32(this.__handle);\n\t\t__w0(w, value);\n\t\t__service.call(w);\n\t}\n\n\tupdate(player?: Player): void {\n\t\tconst __w = __service.begin(2);\n\t\t__w.i32(this.__handle);\n\t\t__w2(__w, player);\n\t\tconst __r = __service.call(__w);\n\t}\n}\n\nexport const semiclip = new Semiclip(1);\n\nconst __service = new __Service(\"semiclip\", 1612802220);\n\nfunction __w1(w: __Writer, v: (a0: __Player, a1: __Player) => bool): void { w.i32(__sendFunction(changetype<usize>(v), 0, __invoke)); }\n\nfunction __r1(r: __Reader): (a0: __Player, a1: __Player) => bool { return changetype<(a0: __Player, a1: __Player) => bool>(__receiveFunction(r, changetype<usize>(__stub0))); }\n\nfunction __w0(w: __Writer, v: ((a0: __Player, a1: __Player) => bool) | null): void {\n\tif (v == null) {\n\t\tw.bool(false);\n\t\treturn;\n\t}\n\tw.bool(true);\n\t__w1(w, v!);\n}\n\nfunction __r0(r: __Reader): ((a0: __Player, a1: __Player) => bool) | null {\n\tif (!r.bool()) return null;\n\treturn __r1(r);\n}\n\nfunction __w3(w: __Writer, v: __Player): void { w.i32(<i32>v.id); }\n\nfunction __r3(r: __Reader): __Player { return new __Player(r.i32()); }\n\nfunction __w2(w: __Writer, v: __Player | null): void {\n\tif (v == null) {\n\t\tw.bool(false);\n\t\treturn;\n\t}\n\tw.bool(true);\n\t__w3(w, v!);\n}\n\nfunction __r2(r: __Reader): __Player | null {\n\tif (!r.bool()) return null;\n\treturn __r3(r);\n}\n\nfunction __w4(w: __Writer, v: bool): void { w.bool(v); }\n\nfunction __r4(r: __Reader): bool { return r.bool(); }\n\nfunction __stub0(a0: __Player, a1: __Player): bool {\n\tconst target = __remoteTarget();\n\tconst w = __beginCallback(target);\n\t__w3(w, a0);\n\t__w3(w, a1);\n\tconst r = __callBack(target, w);\n\treturn __r4(r);\n}\n\nfunction __invoke(fn: usize, sig: i32, r: __Reader, w: __Writer): void {\n\tswitch (sig) {\n\t\tcase 0: {\n\t\t\tconst a0 = __r3(r);\n\t\t\tconst a1 = __r3(r);\n\t\t\tconst f = changetype<(a0: __Player, a1: __Player) => bool>(fn);\n\t\t\tconst result = f(a0, a1);\n\t\t\t__w4(w, result);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n",
|
|
61
|
+
"serve": "// GENERATED by scripts/shared-modules.ts: ~/modules/semiclip, run by this plugin for every other.\nimport { Reader as __Reader, Writer as __Writer, beginCallback as __beginCallback, callBack as __callBack, receiveFunction as __receiveFunction, remoteTarget as __remoteTarget, sendFunction as __sendFunction, Handles as __Handles, serve as __serve } from \"~/remote\";\nimport * as __m from \"~/modules/semiclip\";\nimport { Player as __Player } from \"~/facade\";\nimport { Semiclip as __M_Semiclip } from \"~/modules/semiclip\";\n\nconst __handles = new __Handles();\n// The exported objects are handles 1, 2, ... - the numbers the proxies have.\n__handles.id(changetype<usize>(__m.semiclip));\n\nfunction __dispatch(op: i32, r: __Reader, w: __Writer): void {\n\tswitch (op) {\n\t\tcase 0: {\n\t\t\tconst o = __r0(r);\n\t\t\t__w1(w, o.rule);\n\t\t\tbreak;\n\t\t}\n\t\tcase 1: {\n\t\t\tconst o = __r0(r);\n\t\t\to.rule = __r1(r);\n\t\t\tbreak;\n\t\t}\n\t\tcase 2: {\n\t\t\tconst o = __r0(r);\n\t\t\tconst a0 = __r3(r);\n\t\t\to.update(a0);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nfunction __stub0(a0: __Player, a1: __Player): bool {\n\tconst target = __remoteTarget();\n\tconst w = __beginCallback(target);\n\t__w4(w, a0);\n\t__w4(w, a1);\n\tconst r = __callBack(target, w);\n\treturn __r5(r);\n}\n\nfunction __invoke(fn: usize, sig: i32, r: __Reader, w: __Writer): void {\n\tswitch (sig) {\n\t\tcase 0: {\n\t\t\tconst a0 = __r4(r);\n\t\t\tconst a1 = __r4(r);\n\t\t\tconst f = changetype<(a0: __Player, a1: __Player) => bool>(fn);\n\t\t\tconst result = f(a0, a1);\n\t\t\t__w5(w, result);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nfunction __w0(w: __Writer, v: __M_Semiclip): void { w.i32(__handles.id(changetype<usize>(v))); }\n\nfunction __r0(r: __Reader): __M_Semiclip { return changetype<__M_Semiclip>(__handles.at(r.i32())); }\n\nfunction __w2(w: __Writer, v: (a0: __Player, a1: __Player) => bool): void { w.i32(__sendFunction(changetype<usize>(v), 0, __invoke)); }\n\nfunction __r2(r: __Reader): (a0: __Player, a1: __Player) => bool { return changetype<(a0: __Player, a1: __Player) => bool>(__receiveFunction(r, changetype<usize>(__stub0))); }\n\nfunction __w1(w: __Writer, v: ((a0: __Player, a1: __Player) => bool) | null): void {\n\tif (v == null) {\n\t\tw.bool(false);\n\t\treturn;\n\t}\n\tw.bool(true);\n\t__w2(w, v!);\n}\n\nfunction __r1(r: __Reader): ((a0: __Player, a1: __Player) => bool) | null {\n\tif (!r.bool()) return null;\n\treturn __r2(r);\n}\n\nfunction __w4(w: __Writer, v: __Player): void { w.i32(<i32>v.id); }\n\nfunction __r4(r: __Reader): __Player { return new __Player(r.i32()); }\n\nfunction __w3(w: __Writer, v: __Player | null): void {\n\tif (v == null) {\n\t\tw.bool(false);\n\t\treturn;\n\t}\n\tw.bool(true);\n\t__w4(w, v!);\n}\n\nfunction __r3(r: __Reader): __Player | null {\n\tif (!r.bool()) return null;\n\treturn __r4(r);\n}\n\nfunction __w5(w: __Writer, v: bool): void { w.bool(v); }\n\nfunction __r5(r: __Reader): bool { return r.bool(); }\n\n__serve(\"semiclip\", 1612802220, __dispatch);\n",
|
|
62
|
+
"callbacks": true
|
|
63
|
+
}
|
|
64
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// The tooltips of src/index.ts, in both languages: scripts/apply-docs.ts writes the
|
|
2
|
+
// one AMXTS_DOCS_LANG picks into the JSDoc above each element.
|
|
3
|
+
export default {
|
|
4
|
+
'SemiclipRule': {
|
|
5
|
+
en: `
|
|
6
|
+
Whether \`player\` walks through \`target\`. Two players walk through each
|
|
7
|
+
other when the rule lets either one: \`rule(a, b) || rule(b, a)\`.
|
|
8
|
+
`,
|
|
9
|
+
ru: `
|
|
10
|
+
Проходит ли \`player\` сквозь \`target\`. Два игрока проходят друг сквозь
|
|
11
|
+
друга, когда правило разрешает это хотя бы в одну сторону:
|
|
12
|
+
\`rule(a, b) || rule(b, a)\`.
|
|
13
|
+
`,
|
|
14
|
+
},
|
|
15
|
+
'Semiclip': {
|
|
16
|
+
en: `
|
|
17
|
+
Who walks through whom, set as a rule over two players - \`semiclip\`, which
|
|
18
|
+
plugins use without an import:
|
|
19
|
+
|
|
20
|
+
\`\`\`ts
|
|
21
|
+
semiclip.rule = (player, target) => player.isAlive && target.isAlive && player.team == target.team;
|
|
22
|
+
\`\`\`
|
|
23
|
+
`,
|
|
24
|
+
ru: `
|
|
25
|
+
Кто сквозь кого проходит, заданное правилом над двумя игроками, — \`semiclip\`,
|
|
26
|
+
которым плагины пользуются без импорта:
|
|
27
|
+
|
|
28
|
+
\`\`\`ts
|
|
29
|
+
semiclip.rule = (player, target) => player.isAlive && target.isAlive && player.team == target.team;
|
|
30
|
+
\`\`\`
|
|
31
|
+
`,
|
|
32
|
+
},
|
|
33
|
+
'Semiclip.rule': {
|
|
34
|
+
en: `
|
|
35
|
+
Whether \`player\` walks through \`target\`; a pair walks through when either
|
|
36
|
+
way says so. Until a plugin sets it, teammates walk through each other.
|
|
37
|
+
Setting it works every pair out; \`null\` makes everyone solid again, and the
|
|
38
|
+
rule of a plugin that stops goes back to teammates. The pairs are worked out again when a player comes, spawns, dies or
|
|
39
|
+
changes sides, and when a field plugins added to \`Player\` changes.
|
|
40
|
+
`,
|
|
41
|
+
ru: `
|
|
42
|
+
Проходит ли \`player\` сквозь \`target\`; пара проходит, когда это разрешает
|
|
43
|
+
хотя бы одна сторона. Пока плагин его не задал, друг сквозь друга проходят
|
|
44
|
+
союзники. Присваивание рассчитывает каждую пару; \`null\` снова делает всех
|
|
45
|
+
твёрдыми, а правило остановленного плагина снова сменяется союзниками. Пары рассчитываются заново, когда игрок заходит,
|
|
46
|
+
появляется, умирает или меняет команду и когда меняется поле, которое плагины
|
|
47
|
+
добавили в \`Player\`.
|
|
48
|
+
`,
|
|
49
|
+
},
|
|
50
|
+
'Semiclip.update': {
|
|
51
|
+
en: `
|
|
52
|
+
Works the pairs out again - \`player\`'s, or everyone's - after something
|
|
53
|
+
the rule reads changed that the module does not hear: a round's mode, a
|
|
54
|
+
plugin's own record.
|
|
55
|
+
`,
|
|
56
|
+
ru: `
|
|
57
|
+
Рассчитывает пары заново — пары \`player\` или всех — после того, как
|
|
58
|
+
изменилось то, что читает правило и чего модуль не слышит: режим раунда,
|
|
59
|
+
собственная запись плагина.
|
|
60
|
+
`,
|
|
61
|
+
},
|
|
62
|
+
'semiclip': {
|
|
63
|
+
en: `Who walks through whom: the rule, and the pairs worked out again.`,
|
|
64
|
+
ru: `Кто сквозь кого проходит: правило и повторный расчёт пар.`,
|
|
65
|
+
},
|
|
66
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semiclip for amxts plugins: who walks through whom, as a rule over two
|
|
3
|
+
* players. The core keeps the pairs (`player.walksThrough`); this module asks
|
|
4
|
+
* the rule and sets them, again whenever what the rule reads may have changed.
|
|
5
|
+
* How to use it: README.md.
|
|
6
|
+
*/
|
|
7
|
+
import { ClientPutinserverEvent, Player, TeamInfoMessage, game, server } from "@amxts/core";
|
|
8
|
+
import { callingPlugin, onPluginStop } from "@amxts/core/kit";
|
|
9
|
+
|
|
10
|
+
export default defineModule({
|
|
11
|
+
meta: { name: "semiclip" },
|
|
12
|
+
imports: [{ from: "@amxts/semiclip", name: "semiclip" }],
|
|
13
|
+
setup() {
|
|
14
|
+
// The look until a plugin sets its own: see-through up close, not drawn right against him.
|
|
15
|
+
server.walkThroughLook = { fadeDistance: 250, opacity: 0.4, hideDistance: 48 };
|
|
16
|
+
server.addEventListener("init", start);
|
|
17
|
+
server.addEventListener("putInServer", joined);
|
|
18
|
+
server.addMessageListener("team", teamChanged);
|
|
19
|
+
server.addEventListener("playerChange", event => changed(event.player));
|
|
20
|
+
game.addEventListener("spawn", event => changed(event.player), true);
|
|
21
|
+
game.addEventListener("killed", event => changed(event.player), true);
|
|
22
|
+
onPluginStop(stopped);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Whether `player` walks through `target`. Two players walk through each
|
|
28
|
+
* other when the rule lets either one: `rule(a, b) || rule(b, a)`.
|
|
29
|
+
*/
|
|
30
|
+
export type SemiclipRule = (player: Player, target: Player) => boolean;
|
|
31
|
+
|
|
32
|
+
/** The rule until a plugin sets one: teammates walk through each other. */
|
|
33
|
+
const teammates: SemiclipRule = (player, target) => player.team == target.team;
|
|
34
|
+
|
|
35
|
+
let current: SemiclipRule | null = teammates;
|
|
36
|
+
/** The plugin that set the rule, as `callingPlugin()` numbers it; 0 for the module's own. */
|
|
37
|
+
let currentFrom = 0;
|
|
38
|
+
|
|
39
|
+
/** The server is up. A rule set while the plugins are still loading waits for it. */
|
|
40
|
+
let started = false;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Who walks through whom, set as a rule over two players - `semiclip`, which
|
|
44
|
+
* plugins use without an import:
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* semiclip.rule = (player, target) => player.isAlive && target.isAlive && player.team == target.team;
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export class Semiclip {
|
|
51
|
+
/**
|
|
52
|
+
* Whether `player` walks through `target`; a pair walks through when either
|
|
53
|
+
* way says so. Until a plugin sets it, teammates walk through each other.
|
|
54
|
+
* Setting it works every pair out; `null` makes everyone solid again, and the
|
|
55
|
+
* rule of a plugin that stops goes back to teammates. The pairs are worked out again when a player comes, spawns, dies or
|
|
56
|
+
* changes sides, and when a field plugins added to `Player` changes.
|
|
57
|
+
*/
|
|
58
|
+
get rule() {
|
|
59
|
+
return current;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
set rule(rule: SemiclipRule | null) {
|
|
63
|
+
if (rule != null && currentFrom != 0 && current != null && rule != current) console.warn("[semiclip] a rule was set over another one: the last one set is used");
|
|
64
|
+
const had = current != null;
|
|
65
|
+
current = rule;
|
|
66
|
+
currentFrom = callingPlugin();
|
|
67
|
+
if (!started) return;
|
|
68
|
+
if (rule != null) updateAll(rule);
|
|
69
|
+
else if (had) clearAll();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Works the pairs out again - `player`'s, or everyone's - after something
|
|
74
|
+
* the rule reads changed that the module does not hear: a round's mode, a
|
|
75
|
+
* plugin's own record.
|
|
76
|
+
*/
|
|
77
|
+
update(player?: Player) {
|
|
78
|
+
const rule = activeRule();
|
|
79
|
+
if (rule == null) return;
|
|
80
|
+
if (player) updatePlayer(player, rule);
|
|
81
|
+
else updateAll(rule);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Who walks through whom: the rule, and the pairs worked out again. */
|
|
86
|
+
export const semiclip = new Semiclip();
|
|
87
|
+
|
|
88
|
+
/** The plugin that set the rule stopped: its rule goes, and the module's own, teammates, comes back. */
|
|
89
|
+
function stopped(plugin: number) {
|
|
90
|
+
if (currentFrom == 0 || currentFrom != plugin) return;
|
|
91
|
+
current = teammates;
|
|
92
|
+
currentFrom = 0;
|
|
93
|
+
if (started) updateAll(teammates);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function start() {
|
|
97
|
+
started = true;
|
|
98
|
+
const rule = current;
|
|
99
|
+
if (rule != null) updateAll(rule);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The rule, while the module sets the pairs: the server is up and a rule is set. */
|
|
103
|
+
function activeRule() {
|
|
104
|
+
return started ? current : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function joined(event: ClientPutinserverEvent) {
|
|
108
|
+
const player = new Player(event.player.id);
|
|
109
|
+
changed(player);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** A team change is sent to everyone; a TeamInfo to one player only brings him up to date. */
|
|
113
|
+
function teamChanged(event: TeamInfoMessage) {
|
|
114
|
+
const target = event.target;
|
|
115
|
+
if (event.player == null && target != null) changed(target);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Something the rule may read changed on `player`: his pairs. */
|
|
119
|
+
function changed(player: Player) {
|
|
120
|
+
const rule = activeRule();
|
|
121
|
+
if (rule != null) updatePlayer(player, rule);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function updateAll(rule: SemiclipRule) {
|
|
125
|
+
const players = server.players;
|
|
126
|
+
for (let i = 0; i < players.length; i++) {
|
|
127
|
+
for (let j = i + 1; j < players.length; j++) pair(players[i], players[j], rule);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function updatePlayer(player: Player, rule: SemiclipRule) {
|
|
132
|
+
for (const other of server.players) {
|
|
133
|
+
if (other.id != player.id) pair(player, other, rule);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Sets whether `a` and `b` walk through each other: when the rule lets either one. */
|
|
138
|
+
function pair(a: Player, b: Player, rule: SemiclipRule) {
|
|
139
|
+
if (rule(a, b) || rule(b, a)) a.walksThrough.add(b);
|
|
140
|
+
else a.walksThrough.delete(b);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** No rule any more: everyone is solid again. */
|
|
144
|
+
function clearAll() {
|
|
145
|
+
for (const player of server.players) player.walksThrough.clear();
|
|
146
|
+
}
|