ttpg-darrell 1.0.9 → 1.0.11
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/build/cjs/garbage/garbage-container.d.ts +37 -0
- package/build/cjs/garbage/garbage-container.js +113 -0
- package/build/cjs/garbage/simple-card-garbage-handler.d.ts +16 -0
- package/build/cjs/garbage/simple-card-garbage-handler.js +130 -0
- package/build/cjs/global/content-menu/leave-seat.d.ts +7 -0
- package/build/cjs/global/content-menu/leave-seat.js +23 -2
- package/build/cjs/index.d.ts +2 -0
- package/build/cjs/index.js +2 -0
- package/build/esm/garbage/garbage-container.d.ts +37 -0
- package/build/esm/garbage/garbage-container.js +108 -0
- package/build/esm/garbage/simple-card-garbage-handler.d.ts +16 -0
- package/build/esm/garbage/simple-card-garbage-handler.js +126 -0
- package/build/esm/global/content-menu/leave-seat.d.ts +7 -0
- package/build/esm/global/content-menu/leave-seat.js +23 -2
- package/build/esm/index.d.ts +2 -0
- package/build/esm/index.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Container, GameObject } from "@tabletop-playground/api";
|
|
2
|
+
export declare abstract class GarbageHandler {
|
|
3
|
+
/**
|
|
4
|
+
* Can recycle this object?
|
|
5
|
+
*
|
|
6
|
+
* @param obj
|
|
7
|
+
*/
|
|
8
|
+
abstract canRecycle(obj: GameObject): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Recycle the object.
|
|
11
|
+
*
|
|
12
|
+
* @param obj
|
|
13
|
+
* @returns true if recycled
|
|
14
|
+
*/
|
|
15
|
+
abstract recycle(obj: GameObject): boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Attempt to recycle deposited objects, break up decks into individual cards.
|
|
19
|
+
*/
|
|
20
|
+
export declare class GarbageContainer {
|
|
21
|
+
private static _garbageHandlers;
|
|
22
|
+
private readonly _container;
|
|
23
|
+
/**
|
|
24
|
+
* Register a new recycler.
|
|
25
|
+
*
|
|
26
|
+
* @param garbageHandler
|
|
27
|
+
*/
|
|
28
|
+
static addHandler(garbageHandler: GarbageHandler): void;
|
|
29
|
+
/**
|
|
30
|
+
* Clear all recycle handlers (for tests).
|
|
31
|
+
*/
|
|
32
|
+
static clearHandlers(): void;
|
|
33
|
+
private static _tryRecycleObj;
|
|
34
|
+
private static _tryRecycleDeck;
|
|
35
|
+
constructor(container: Container);
|
|
36
|
+
_recycle(): void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GarbageContainer = exports.GarbageHandler = void 0;
|
|
4
|
+
const api_1 = require("@tabletop-playground/api");
|
|
5
|
+
class GarbageHandler {
|
|
6
|
+
}
|
|
7
|
+
exports.GarbageHandler = GarbageHandler;
|
|
8
|
+
/**
|
|
9
|
+
* Attempt to recycle deposited objects, break up decks into individual cards.
|
|
10
|
+
*/
|
|
11
|
+
class GarbageContainer {
|
|
12
|
+
/**
|
|
13
|
+
* Register a new recycler.
|
|
14
|
+
*
|
|
15
|
+
* @param garbageHandler
|
|
16
|
+
*/
|
|
17
|
+
static addHandler(garbageHandler) {
|
|
18
|
+
this._garbageHandlers.push(garbageHandler);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Clear all recycle handlers (for tests).
|
|
22
|
+
*/
|
|
23
|
+
static clearHandlers() {
|
|
24
|
+
this._garbageHandlers = [];
|
|
25
|
+
}
|
|
26
|
+
static _tryRecycleObj(obj) {
|
|
27
|
+
for (const handler of this._garbageHandlers) {
|
|
28
|
+
if (handler.canRecycle(obj)) {
|
|
29
|
+
if (handler.recycle(obj)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
static _tryRecycleDeck(deck) {
|
|
37
|
+
let recycleCount = 0;
|
|
38
|
+
const stackSize = deck.getStackSize();
|
|
39
|
+
// Process one card at a time. Assume a handler will exist, cost to
|
|
40
|
+
// extract card worthwhile (and if not, same code path for simplicity).
|
|
41
|
+
for (let offset = stackSize - 1; offset >= 0; offset--) {
|
|
42
|
+
// Get the card (use the deck if only one card left).
|
|
43
|
+
let card;
|
|
44
|
+
if (offset === 0 && deck.getStackSize() === 1) {
|
|
45
|
+
card = deck;
|
|
46
|
+
}
|
|
47
|
+
else if (deck.getStackSize() > offset) {
|
|
48
|
+
const numCards = 1;
|
|
49
|
+
const fromFront = false;
|
|
50
|
+
const keep = false;
|
|
51
|
+
card = deck.takeCards(numCards, fromFront, offset, keep);
|
|
52
|
+
}
|
|
53
|
+
if (!card) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
// Try to recycle, return card to same spot if fails.
|
|
57
|
+
let success = GarbageContainer._tryRecycleObj(card);
|
|
58
|
+
if (success) {
|
|
59
|
+
recycleCount += 1;
|
|
60
|
+
}
|
|
61
|
+
else if (card !== deck) {
|
|
62
|
+
const toFront = false;
|
|
63
|
+
const animate = false;
|
|
64
|
+
const flipped = false;
|
|
65
|
+
deck.addCards(card, toFront, offset, animate, flipped);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return recycleCount === stackSize;
|
|
69
|
+
}
|
|
70
|
+
constructor(container) {
|
|
71
|
+
if (!container) {
|
|
72
|
+
throw new Error("missing container");
|
|
73
|
+
}
|
|
74
|
+
this._container = container;
|
|
75
|
+
container.onInserted.add((container, insertedObjects, player) => {
|
|
76
|
+
process.nextTick(() => {
|
|
77
|
+
this._recycle();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Expose for testing.
|
|
82
|
+
_recycle() {
|
|
83
|
+
const objs = this._container.getItems();
|
|
84
|
+
for (const obj of objs) {
|
|
85
|
+
// Verify object.
|
|
86
|
+
if (!obj.isValid()) {
|
|
87
|
+
continue; // object destroyed
|
|
88
|
+
}
|
|
89
|
+
if (obj.getContainer() !== this._container) {
|
|
90
|
+
continue; // object no longer in this container
|
|
91
|
+
}
|
|
92
|
+
// Remove from container.
|
|
93
|
+
const above = this._container
|
|
94
|
+
.getPosition()
|
|
95
|
+
.add([0, 0, obj.getSize().z + 3]);
|
|
96
|
+
this._container.take(obj, above);
|
|
97
|
+
// Attempt to recyle.
|
|
98
|
+
let success;
|
|
99
|
+
if (obj instanceof api_1.Card && obj.getStackSize() > 1) {
|
|
100
|
+
success = GarbageContainer._tryRecycleDeck(obj);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
success = GarbageContainer._tryRecycleObj(obj);
|
|
104
|
+
}
|
|
105
|
+
// If recycle fails, return to container.
|
|
106
|
+
if (!success) {
|
|
107
|
+
this._container.addObjects([obj]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.GarbageContainer = GarbageContainer;
|
|
113
|
+
GarbageContainer._garbageHandlers = [];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { GameObject } from "@tabletop-playground/api";
|
|
2
|
+
import { GarbageHandler } from "./garbage-container";
|
|
3
|
+
export declare class SimpleCardGarbageHandler implements GarbageHandler {
|
|
4
|
+
private _matNsid;
|
|
5
|
+
private _matSnapPointTag;
|
|
6
|
+
private _cardNsidPrefix;
|
|
7
|
+
private _shuffleAfterDiscard;
|
|
8
|
+
private _discardSnapPoint;
|
|
9
|
+
setMatNsid(matNsid: string): this;
|
|
10
|
+
setMatSnapPointTag(tag: string): this;
|
|
11
|
+
setCardNsidPrefix(cardNsidPrefix: string): this;
|
|
12
|
+
setShuffleAfterDiscard(shuffle: boolean): this;
|
|
13
|
+
canRecycle(obj: GameObject): boolean;
|
|
14
|
+
recycle(obj: GameObject): boolean;
|
|
15
|
+
private _getDiscardSnapPoint;
|
|
16
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SimpleCardGarbageHandler = void 0;
|
|
4
|
+
const api_1 = require("@tabletop-playground/api");
|
|
5
|
+
const nsid_1 = require("../nsid/nsid");
|
|
6
|
+
class SimpleCardGarbageHandler {
|
|
7
|
+
constructor() {
|
|
8
|
+
this._matNsid = "";
|
|
9
|
+
this._matSnapPointTag = "";
|
|
10
|
+
this._cardNsidPrefix = "";
|
|
11
|
+
this._shuffleAfterDiscard = false;
|
|
12
|
+
}
|
|
13
|
+
setMatNsid(matNsid) {
|
|
14
|
+
this._matNsid = matNsid;
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
setMatSnapPointTag(tag) {
|
|
18
|
+
this._matSnapPointTag = tag;
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
setCardNsidPrefix(cardNsidPrefix) {
|
|
22
|
+
this._cardNsidPrefix = cardNsidPrefix;
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
setShuffleAfterDiscard(shuffle) {
|
|
26
|
+
this._shuffleAfterDiscard = shuffle;
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
canRecycle(obj) {
|
|
30
|
+
if (!(obj instanceof api_1.Card)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (obj.getStackSize() !== 1) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const nsid = nsid_1.NSID.get(obj);
|
|
37
|
+
return (this._cardNsidPrefix.length > 0 && nsid.startsWith(this._cardNsidPrefix));
|
|
38
|
+
}
|
|
39
|
+
recycle(obj) {
|
|
40
|
+
// Verify card.
|
|
41
|
+
if (!(obj instanceof api_1.Card)) {
|
|
42
|
+
throw new Error("not a card");
|
|
43
|
+
}
|
|
44
|
+
if (obj.getStackSize() !== 1) {
|
|
45
|
+
throw new Error("not singleton card");
|
|
46
|
+
}
|
|
47
|
+
const nsid = nsid_1.NSID.get(obj);
|
|
48
|
+
if (!nsid.startsWith(this._cardNsidPrefix)) {
|
|
49
|
+
throw new Error("nsid mismatch");
|
|
50
|
+
}
|
|
51
|
+
// Find mat.
|
|
52
|
+
const snapPoint = this._getDiscardSnapPoint();
|
|
53
|
+
if (!snapPoint) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
// Find discard deck.
|
|
57
|
+
let deck = snapPoint === null || snapPoint === void 0 ? void 0 : snapPoint.getSnappedObject();
|
|
58
|
+
if (deck && !(deck instanceof api_1.Card)) {
|
|
59
|
+
deck = undefined;
|
|
60
|
+
}
|
|
61
|
+
// Discard.
|
|
62
|
+
if (deck) {
|
|
63
|
+
const toFront = false;
|
|
64
|
+
const offset = 0;
|
|
65
|
+
const animate = true;
|
|
66
|
+
const flipped = false;
|
|
67
|
+
const success = deck.addCards(obj, toFront, offset, animate, flipped);
|
|
68
|
+
if (!success) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const above = snapPoint.getGlobalPosition().add([0, 0, 10]);
|
|
74
|
+
const animationSpeed = 0;
|
|
75
|
+
obj.setPosition(above, animationSpeed);
|
|
76
|
+
obj.snapToGround();
|
|
77
|
+
obj.snap(); // apply snap point rotation
|
|
78
|
+
}
|
|
79
|
+
// Optionally shuffle. Must wait for discard animation to finish!
|
|
80
|
+
if (this._shuffleAfterDiscard && deck) {
|
|
81
|
+
const finishShuffle = () => {
|
|
82
|
+
if (deck instanceof api_1.Card) {
|
|
83
|
+
deck.shuffle();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const delayedShuffle = () => {
|
|
87
|
+
process.nextTick(finishShuffle);
|
|
88
|
+
};
|
|
89
|
+
if (obj.isValid()) {
|
|
90
|
+
obj.onDestroyed.add(() => {
|
|
91
|
+
process.nextTick(delayedShuffle);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
_getDiscardSnapPoint() {
|
|
98
|
+
var _a;
|
|
99
|
+
// Check cache.
|
|
100
|
+
if (this._discardSnapPoint &&
|
|
101
|
+
((_a = this._discardSnapPoint.getParentObject()) === null || _a === void 0 ? void 0 : _a.isValid())) {
|
|
102
|
+
return this._discardSnapPoint;
|
|
103
|
+
}
|
|
104
|
+
this._discardSnapPoint = undefined;
|
|
105
|
+
// Find mat.
|
|
106
|
+
let mat = undefined;
|
|
107
|
+
const skipContained = true;
|
|
108
|
+
for (const obj of api_1.world.getAllObjects(skipContained)) {
|
|
109
|
+
const nsid = nsid_1.NSID.get(obj);
|
|
110
|
+
if (nsid === this._matNsid) {
|
|
111
|
+
mat = obj;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (!mat) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
// Find snap point.
|
|
119
|
+
for (const snapPoint of mat.getAllSnapPoints()) {
|
|
120
|
+
for (const tag of snapPoint.getTags()) {
|
|
121
|
+
if (tag === this._matSnapPointTag) {
|
|
122
|
+
this._discardSnapPoint = snapPoint; // cache
|
|
123
|
+
return snapPoint;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
exports.SimpleCardGarbageHandler = SimpleCardGarbageHandler;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
import { Player } from "@tabletop-playground/api";
|
|
1
2
|
import { AbstractGlobal } from "../abstract-global";
|
|
3
|
+
/**
|
|
4
|
+
* Content menu item to leave seat. Move to an unused slot, NOT the
|
|
5
|
+
* spectator slot. Spectators cannot interact, preventing them from
|
|
6
|
+
* clicking any "take seat" buttons.
|
|
7
|
+
*/
|
|
2
8
|
export declare class LeaveSeat implements AbstractGlobal {
|
|
3
9
|
init(): void;
|
|
10
|
+
static leaveSeat(clickingPlayer: Player): void;
|
|
4
11
|
}
|
|
@@ -2,16 +2,37 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LeaveSeat = void 0;
|
|
4
4
|
const api_1 = require("@tabletop-playground/api");
|
|
5
|
+
/**
|
|
6
|
+
* Content menu item to leave seat. Move to an unused slot, NOT the
|
|
7
|
+
* spectator slot. Spectators cannot interact, preventing them from
|
|
8
|
+
* clicking any "take seat" buttons.
|
|
9
|
+
*/
|
|
5
10
|
class LeaveSeat {
|
|
6
11
|
init() {
|
|
7
12
|
const actionName = "*Leave seat";
|
|
8
|
-
const tooltip = "Switch to
|
|
13
|
+
const tooltip = "Switch to non-seat player slot";
|
|
9
14
|
api_1.world.addCustomAction(actionName, tooltip);
|
|
10
15
|
api_1.globalEvents.onCustomAction.add((player, identifier) => {
|
|
11
16
|
if (identifier === actionName) {
|
|
12
|
-
|
|
17
|
+
LeaveSeat.leaveSeat(player);
|
|
13
18
|
}
|
|
14
19
|
});
|
|
15
20
|
}
|
|
21
|
+
static leaveSeat(clickingPlayer) {
|
|
22
|
+
const busy = new Set();
|
|
23
|
+
for (const player of api_1.world.getAllPlayers()) {
|
|
24
|
+
busy.add(player.getSlot());
|
|
25
|
+
}
|
|
26
|
+
for (const obj of api_1.world.getAllObjects()) {
|
|
27
|
+
busy.add(obj.getOwningPlayerSlot());
|
|
28
|
+
}
|
|
29
|
+
for (let i = 0; i < 20; i++) {
|
|
30
|
+
if (!busy.has(i)) {
|
|
31
|
+
console.log(`LeaveSeat: moving "${clickingPlayer.getName()}" to open slot ${i}`);
|
|
32
|
+
clickingPlayer.switchSlot(i);
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
16
37
|
}
|
|
17
38
|
exports.LeaveSeat = LeaveSeat;
|
package/build/cjs/index.d.ts
CHANGED
package/build/cjs/index.js
CHANGED
|
@@ -14,6 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./garbage/garbage-container"), exports);
|
|
18
|
+
__exportStar(require("./garbage/simple-card-garbage-handler"), exports);
|
|
17
19
|
__exportStar(require("./global/abstract-global"), exports);
|
|
18
20
|
__exportStar(require("./global/content-menu/leave-seat"), exports);
|
|
19
21
|
__exportStar(require("./locale/locale"), exports);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Container, GameObject } from "@tabletop-playground/api";
|
|
2
|
+
export declare abstract class GarbageHandler {
|
|
3
|
+
/**
|
|
4
|
+
* Can recycle this object?
|
|
5
|
+
*
|
|
6
|
+
* @param obj
|
|
7
|
+
*/
|
|
8
|
+
abstract canRecycle(obj: GameObject): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Recycle the object.
|
|
11
|
+
*
|
|
12
|
+
* @param obj
|
|
13
|
+
* @returns true if recycled
|
|
14
|
+
*/
|
|
15
|
+
abstract recycle(obj: GameObject): boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Attempt to recycle deposited objects, break up decks into individual cards.
|
|
19
|
+
*/
|
|
20
|
+
export declare class GarbageContainer {
|
|
21
|
+
private static _garbageHandlers;
|
|
22
|
+
private readonly _container;
|
|
23
|
+
/**
|
|
24
|
+
* Register a new recycler.
|
|
25
|
+
*
|
|
26
|
+
* @param garbageHandler
|
|
27
|
+
*/
|
|
28
|
+
static addHandler(garbageHandler: GarbageHandler): void;
|
|
29
|
+
/**
|
|
30
|
+
* Clear all recycle handlers (for tests).
|
|
31
|
+
*/
|
|
32
|
+
static clearHandlers(): void;
|
|
33
|
+
private static _tryRecycleObj;
|
|
34
|
+
private static _tryRecycleDeck;
|
|
35
|
+
constructor(container: Container);
|
|
36
|
+
_recycle(): void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Card, } from "@tabletop-playground/api";
|
|
2
|
+
export class GarbageHandler {
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Attempt to recycle deposited objects, break up decks into individual cards.
|
|
6
|
+
*/
|
|
7
|
+
export class GarbageContainer {
|
|
8
|
+
/**
|
|
9
|
+
* Register a new recycler.
|
|
10
|
+
*
|
|
11
|
+
* @param garbageHandler
|
|
12
|
+
*/
|
|
13
|
+
static addHandler(garbageHandler) {
|
|
14
|
+
this._garbageHandlers.push(garbageHandler);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Clear all recycle handlers (for tests).
|
|
18
|
+
*/
|
|
19
|
+
static clearHandlers() {
|
|
20
|
+
this._garbageHandlers = [];
|
|
21
|
+
}
|
|
22
|
+
static _tryRecycleObj(obj) {
|
|
23
|
+
for (const handler of this._garbageHandlers) {
|
|
24
|
+
if (handler.canRecycle(obj)) {
|
|
25
|
+
if (handler.recycle(obj)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
static _tryRecycleDeck(deck) {
|
|
33
|
+
let recycleCount = 0;
|
|
34
|
+
const stackSize = deck.getStackSize();
|
|
35
|
+
// Process one card at a time. Assume a handler will exist, cost to
|
|
36
|
+
// extract card worthwhile (and if not, same code path for simplicity).
|
|
37
|
+
for (let offset = stackSize - 1; offset >= 0; offset--) {
|
|
38
|
+
// Get the card (use the deck if only one card left).
|
|
39
|
+
let card;
|
|
40
|
+
if (offset === 0 && deck.getStackSize() === 1) {
|
|
41
|
+
card = deck;
|
|
42
|
+
}
|
|
43
|
+
else if (deck.getStackSize() > offset) {
|
|
44
|
+
const numCards = 1;
|
|
45
|
+
const fromFront = false;
|
|
46
|
+
const keep = false;
|
|
47
|
+
card = deck.takeCards(numCards, fromFront, offset, keep);
|
|
48
|
+
}
|
|
49
|
+
if (!card) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// Try to recycle, return card to same spot if fails.
|
|
53
|
+
let success = GarbageContainer._tryRecycleObj(card);
|
|
54
|
+
if (success) {
|
|
55
|
+
recycleCount += 1;
|
|
56
|
+
}
|
|
57
|
+
else if (card !== deck) {
|
|
58
|
+
const toFront = false;
|
|
59
|
+
const animate = false;
|
|
60
|
+
const flipped = false;
|
|
61
|
+
deck.addCards(card, toFront, offset, animate, flipped);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return recycleCount === stackSize;
|
|
65
|
+
}
|
|
66
|
+
constructor(container) {
|
|
67
|
+
if (!container) {
|
|
68
|
+
throw new Error("missing container");
|
|
69
|
+
}
|
|
70
|
+
this._container = container;
|
|
71
|
+
container.onInserted.add((container, insertedObjects, player) => {
|
|
72
|
+
process.nextTick(() => {
|
|
73
|
+
this._recycle();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Expose for testing.
|
|
78
|
+
_recycle() {
|
|
79
|
+
const objs = this._container.getItems();
|
|
80
|
+
for (const obj of objs) {
|
|
81
|
+
// Verify object.
|
|
82
|
+
if (!obj.isValid()) {
|
|
83
|
+
continue; // object destroyed
|
|
84
|
+
}
|
|
85
|
+
if (obj.getContainer() !== this._container) {
|
|
86
|
+
continue; // object no longer in this container
|
|
87
|
+
}
|
|
88
|
+
// Remove from container.
|
|
89
|
+
const above = this._container
|
|
90
|
+
.getPosition()
|
|
91
|
+
.add([0, 0, obj.getSize().z + 3]);
|
|
92
|
+
this._container.take(obj, above);
|
|
93
|
+
// Attempt to recyle.
|
|
94
|
+
let success;
|
|
95
|
+
if (obj instanceof Card && obj.getStackSize() > 1) {
|
|
96
|
+
success = GarbageContainer._tryRecycleDeck(obj);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
success = GarbageContainer._tryRecycleObj(obj);
|
|
100
|
+
}
|
|
101
|
+
// If recycle fails, return to container.
|
|
102
|
+
if (!success) {
|
|
103
|
+
this._container.addObjects([obj]);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
GarbageContainer._garbageHandlers = [];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { GameObject } from "@tabletop-playground/api";
|
|
2
|
+
import { GarbageHandler } from "./garbage-container";
|
|
3
|
+
export declare class SimpleCardGarbageHandler implements GarbageHandler {
|
|
4
|
+
private _matNsid;
|
|
5
|
+
private _matSnapPointTag;
|
|
6
|
+
private _cardNsidPrefix;
|
|
7
|
+
private _shuffleAfterDiscard;
|
|
8
|
+
private _discardSnapPoint;
|
|
9
|
+
setMatNsid(matNsid: string): this;
|
|
10
|
+
setMatSnapPointTag(tag: string): this;
|
|
11
|
+
setCardNsidPrefix(cardNsidPrefix: string): this;
|
|
12
|
+
setShuffleAfterDiscard(shuffle: boolean): this;
|
|
13
|
+
canRecycle(obj: GameObject): boolean;
|
|
14
|
+
recycle(obj: GameObject): boolean;
|
|
15
|
+
private _getDiscardSnapPoint;
|
|
16
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Card, world } from "@tabletop-playground/api";
|
|
2
|
+
import { NSID } from "../nsid/nsid";
|
|
3
|
+
export class SimpleCardGarbageHandler {
|
|
4
|
+
constructor() {
|
|
5
|
+
this._matNsid = "";
|
|
6
|
+
this._matSnapPointTag = "";
|
|
7
|
+
this._cardNsidPrefix = "";
|
|
8
|
+
this._shuffleAfterDiscard = false;
|
|
9
|
+
}
|
|
10
|
+
setMatNsid(matNsid) {
|
|
11
|
+
this._matNsid = matNsid;
|
|
12
|
+
return this;
|
|
13
|
+
}
|
|
14
|
+
setMatSnapPointTag(tag) {
|
|
15
|
+
this._matSnapPointTag = tag;
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
setCardNsidPrefix(cardNsidPrefix) {
|
|
19
|
+
this._cardNsidPrefix = cardNsidPrefix;
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
setShuffleAfterDiscard(shuffle) {
|
|
23
|
+
this._shuffleAfterDiscard = shuffle;
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
canRecycle(obj) {
|
|
27
|
+
if (!(obj instanceof Card)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (obj.getStackSize() !== 1) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const nsid = NSID.get(obj);
|
|
34
|
+
return (this._cardNsidPrefix.length > 0 && nsid.startsWith(this._cardNsidPrefix));
|
|
35
|
+
}
|
|
36
|
+
recycle(obj) {
|
|
37
|
+
// Verify card.
|
|
38
|
+
if (!(obj instanceof Card)) {
|
|
39
|
+
throw new Error("not a card");
|
|
40
|
+
}
|
|
41
|
+
if (obj.getStackSize() !== 1) {
|
|
42
|
+
throw new Error("not singleton card");
|
|
43
|
+
}
|
|
44
|
+
const nsid = NSID.get(obj);
|
|
45
|
+
if (!nsid.startsWith(this._cardNsidPrefix)) {
|
|
46
|
+
throw new Error("nsid mismatch");
|
|
47
|
+
}
|
|
48
|
+
// Find mat.
|
|
49
|
+
const snapPoint = this._getDiscardSnapPoint();
|
|
50
|
+
if (!snapPoint) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
// Find discard deck.
|
|
54
|
+
let deck = snapPoint === null || snapPoint === void 0 ? void 0 : snapPoint.getSnappedObject();
|
|
55
|
+
if (deck && !(deck instanceof Card)) {
|
|
56
|
+
deck = undefined;
|
|
57
|
+
}
|
|
58
|
+
// Discard.
|
|
59
|
+
if (deck) {
|
|
60
|
+
const toFront = false;
|
|
61
|
+
const offset = 0;
|
|
62
|
+
const animate = true;
|
|
63
|
+
const flipped = false;
|
|
64
|
+
const success = deck.addCards(obj, toFront, offset, animate, flipped);
|
|
65
|
+
if (!success) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const above = snapPoint.getGlobalPosition().add([0, 0, 10]);
|
|
71
|
+
const animationSpeed = 0;
|
|
72
|
+
obj.setPosition(above, animationSpeed);
|
|
73
|
+
obj.snapToGround();
|
|
74
|
+
obj.snap(); // apply snap point rotation
|
|
75
|
+
}
|
|
76
|
+
// Optionally shuffle. Must wait for discard animation to finish!
|
|
77
|
+
if (this._shuffleAfterDiscard && deck) {
|
|
78
|
+
const finishShuffle = () => {
|
|
79
|
+
if (deck instanceof Card) {
|
|
80
|
+
deck.shuffle();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const delayedShuffle = () => {
|
|
84
|
+
process.nextTick(finishShuffle);
|
|
85
|
+
};
|
|
86
|
+
if (obj.isValid()) {
|
|
87
|
+
obj.onDestroyed.add(() => {
|
|
88
|
+
process.nextTick(delayedShuffle);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
_getDiscardSnapPoint() {
|
|
95
|
+
var _a;
|
|
96
|
+
// Check cache.
|
|
97
|
+
if (this._discardSnapPoint &&
|
|
98
|
+
((_a = this._discardSnapPoint.getParentObject()) === null || _a === void 0 ? void 0 : _a.isValid())) {
|
|
99
|
+
return this._discardSnapPoint;
|
|
100
|
+
}
|
|
101
|
+
this._discardSnapPoint = undefined;
|
|
102
|
+
// Find mat.
|
|
103
|
+
let mat = undefined;
|
|
104
|
+
const skipContained = true;
|
|
105
|
+
for (const obj of world.getAllObjects(skipContained)) {
|
|
106
|
+
const nsid = NSID.get(obj);
|
|
107
|
+
if (nsid === this._matNsid) {
|
|
108
|
+
mat = obj;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (!mat) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
// Find snap point.
|
|
116
|
+
for (const snapPoint of mat.getAllSnapPoints()) {
|
|
117
|
+
for (const tag of snapPoint.getTags()) {
|
|
118
|
+
if (tag === this._matSnapPointTag) {
|
|
119
|
+
this._discardSnapPoint = snapPoint; // cache
|
|
120
|
+
return snapPoint;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
import { Player } from "@tabletop-playground/api";
|
|
1
2
|
import { AbstractGlobal } from "../abstract-global";
|
|
3
|
+
/**
|
|
4
|
+
* Content menu item to leave seat. Move to an unused slot, NOT the
|
|
5
|
+
* spectator slot. Spectators cannot interact, preventing them from
|
|
6
|
+
* clicking any "take seat" buttons.
|
|
7
|
+
*/
|
|
2
8
|
export declare class LeaveSeat implements AbstractGlobal {
|
|
3
9
|
init(): void;
|
|
10
|
+
static leaveSeat(clickingPlayer: Player): void;
|
|
4
11
|
}
|
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
import { globalEvents, world } from "@tabletop-playground/api";
|
|
2
|
+
/**
|
|
3
|
+
* Content menu item to leave seat. Move to an unused slot, NOT the
|
|
4
|
+
* spectator slot. Spectators cannot interact, preventing them from
|
|
5
|
+
* clicking any "take seat" buttons.
|
|
6
|
+
*/
|
|
2
7
|
export class LeaveSeat {
|
|
3
8
|
init() {
|
|
4
9
|
const actionName = "*Leave seat";
|
|
5
|
-
const tooltip = "Switch to
|
|
10
|
+
const tooltip = "Switch to non-seat player slot";
|
|
6
11
|
world.addCustomAction(actionName, tooltip);
|
|
7
12
|
globalEvents.onCustomAction.add((player, identifier) => {
|
|
8
13
|
if (identifier === actionName) {
|
|
9
|
-
|
|
14
|
+
LeaveSeat.leaveSeat(player);
|
|
10
15
|
}
|
|
11
16
|
});
|
|
12
17
|
}
|
|
18
|
+
static leaveSeat(clickingPlayer) {
|
|
19
|
+
const busy = new Set();
|
|
20
|
+
for (const player of world.getAllPlayers()) {
|
|
21
|
+
busy.add(player.getSlot());
|
|
22
|
+
}
|
|
23
|
+
for (const obj of world.getAllObjects()) {
|
|
24
|
+
busy.add(obj.getOwningPlayerSlot());
|
|
25
|
+
}
|
|
26
|
+
for (let i = 0; i < 20; i++) {
|
|
27
|
+
if (!busy.has(i)) {
|
|
28
|
+
console.log(`LeaveSeat: moving "${clickingPlayer.getName()}" to open slot ${i}`);
|
|
29
|
+
clickingPlayer.switchSlot(i);
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
13
34
|
}
|
package/build/esm/index.d.ts
CHANGED
package/build/esm/index.js
CHANGED