-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPlayer.ts
235 lines (225 loc) · 5.97 KB
/
Player.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
import PlayerGroup from "./PlayerGroup";
import { IIndexable, applyDefault, filterObjectByKey } from "./data/util";
import Member from "./data/Member";
import Character from "./Character";
import Position, { IPosition } from "./Position";
import FieldDef from "./data/FieldDef";
import Game, { IResetPhase } from "./Game";
import {
IDidApplyEvent,
IDidSetUpdateEvent,
IWillGetUpdateEvent,
} from "./data/DataHolder";
/**
* Essential data for creating a new character object.
*/
export interface IPlayerData extends IIndexable {
name?: string;
color?: string;
characterID: number;
target?: IPosition | null;
isOccupied?: boolean;
}
function getFieldDef(game: Game, data: IPlayerData) {
return new FieldDef<IPlayerData>(
{
type: "object",
acceptNull: false,
children: {
name: {
type: "string",
},
color: {
type: "string",
regExp: /#[0-f]{6}/i,
inputType: "color",
acceptUndefined: true,
},
characterID: {
type: "number",
editable: false,
},
target: {
type: "object",
acceptNull: true,
acceptUndefined: true,
children: {
col: {
type: "number",
minNum: 0,
maxNum: game.tileManager.colCount,
},
row: {
type: "number",
minNum: 0,
maxNum: game.tileManager.rowCount,
},
},
},
isOccupied: {
type: "boolean",
acceptUndefined: true,
},
},
},
data,
"playerData"
);
}
export default class Player extends Member<PlayerGroup, IPlayerData> {
public static readonly ID_RANDOM = -999;
public static readonly ID_UNSET = -1;
private _isSelected: boolean = false;
/**
* The name of the player.
*/
public get name(): string {
return this.data.name || "Player " + this.id;
}
public set name(name: string) {
this.data.name = name;
}
/**
* The color of the player.
*/
public get color(): string {
return this.data.color as string;
}
public set color(color: string) {
this.data.color = color;
}
/**
* Indicates whether the player is the host of the game.
*/
public get isHost(): boolean {
return this.id === this.group.hostPlayerID;
}
/**
* Indicates whether the player is the main player.
*/
public get isMainPlayer(): boolean {
return this.id === this.group.mainPlayerID;
}
/**
* The ID of the character that the player is using.
*/
public get characterID(): number {
return this.data.characterID;
}
public set characterID(characterID: number) {
if (characterID != this.data.characterID) {
this._disableCharacter();
this.data.characterID = characterID;
this.updateCharacter();
}
}
/**
* The target position of the player.
* When reading, the value is from current data.
* When writing, the value is written to staged data.
*/
public get target(): Position | null {
return this.data.target ? new Position(this.data.target) : null;
}
public set target(target: IPosition | null) {
if (target && !this.group.game.tileManager.isInRange(target)) {
throw new Error(`Position out of range: ${target.col} - ${target.row}`);
}
this.data.target = target ? { col: target.col, row: target.row } : null;
}
/**
* The staged target position of the player.
* The value is from staged data.
*/
public get stagedTarget(): Position | null {
return this.stagedData.target ? new Position(this.stagedData.target) : null;
}
/**
* The character object that the player is using.
*/
public get character(): Character {
return this.group.game.characterGroup.get(this.characterID) as Character;
}
/**
* Whether the player is taken by a user session.
* When reading, the value is from current data.
* When writing, the value is written to staged data.
*/
public get isOccupied(): boolean {
return this.data.isOccupied || false;
}
public set isOccupied(isOccupied: boolean) {
this.data.isOccupied = isOccupied;
}
/**
* Whether the player is taken by a user session.
* The value is from staged data.
*/
public get stagedIsOccupied(): boolean {
return this.stagedData.isOccupied || false;
}
/**
* Whether the player is selected.
*/
public get isSelected(): boolean {
return this._isSelected;
}
public set isSelected(isSelected: boolean) {
this.character.isSelected = isSelected;
this._isSelected = isSelected;
}
/**
* Do not create a new Player instance with "new Player()" statement.
* Use PlayerGroup.new() instead.
*/
constructor(group: PlayerGroup, id: number, data: IPlayerData) {
data = applyDefault(data, {
name: "Player " + id,
color: "#999999",
target: null,
isOccupied: false,
});
super(group, id, data);
this.onUpdate<IResetPhase>("reset", () => {
this.setData({
name: "Player " + id,
target: null,
isOccupied: false,
});
});
this.on<IWillGetUpdateEvent>("willGetUpdate", (event) => {
this.updateCharacter();
});
}
// Making the method public
public setData(data: Partial<IPlayerData>) {
super.setData(data);
this.updateCharacter();
}
/**
* Sync player data with its character.
*/
public updateCharacter() {
if (!this.stagedData.isOccupied) {
this._disableCharacter();
return;
}
if (!this.character) {
return;
}
let target = this.stagedData.target;
this.character.target = target ? new Position(target) : null;
this.character.color = this.stagedData.color as string;
this.character.isEnabled = true;
}
public getFieldDef(): FieldDef<IPlayerData> {
return getFieldDef(this.group.game, this.data);
}
private _disableCharacter() {
if (!this.character) {
return;
}
this.character.target = null;
this.character.isEnabled = false;
}
}