-
Notifications
You must be signed in to change notification settings - Fork 7
/
CommandParser.js
179 lines (154 loc) · 4.35 KB
/
CommandParser.js
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
'use strict';
const { CommandType, Room } = require('ranvier');
/**
* Interpreter.. you guessed it, interprets command input
*/
class CommandParser {
/**
* Parse a given string to find the resulting command/arguments
* @param {GameState} state
* @param {string} data
* @param {Player} player
* @return {{
* type: CommandType,
* command: Command,
* skill: Skill,
* channel: Channel,
* args: string,
* originalCommand: string
* }}
*/
static parse(state, data, player) {
data = data.trim();
const parts = data.split(' ');
const command = parts.shift().toLowerCase();
if (!command.length) {
throw new InvalidCommandError();
}
const args = parts.join(' ');
// Kludge so that 'l' alone will always force a look,
// instead of mixing it up with lock or list.
// TODO: replace this a priority list
if (command === 'l') {
return {
type: CommandType.COMMAND,
command: state.CommandManager.get('look'),
args: args
};
}
// Same with 'i' and inventory.
if (command === 'i') {
return {
type: CommandType.COMMAND,
command: state.CommandManager.get('inventory'),
args: args
};
}
// see if they matched a direction for a movement command
const roomDirection = this.checkMovement(player, command);
if (roomDirection) {
const roomExit = this.canGo(player, roomDirection);
return {
type: CommandType.MOVEMENT,
args,
originalCommand: command,
roomExit,
};
}
// see if they matched exactly a command
if (state.CommandManager.get(command)) {
return {
type: CommandType.COMMAND,
command: state.CommandManager.get(command),
args,
originalCommand: command
};
}
// see if they typed at least the beginning of a command and try to match
let found = state.CommandManager.find(command, /* returnAlias: */ true);
if (found) {
return {
type: CommandType.COMMAND,
command: found.command,
args,
originalCommand: found.alias
};
}
// check channels
found = state.ChannelManager.find(command);
if (found) {
return {
type: CommandType.CHANNEL,
channel: found,
args
};
}
// finally check skills
found = state.SkillManager.find(command);
if (found) {
return {
type: CommandType.SKILL,
skill: found,
args
};
}
throw new InvalidCommandError();
}
/**
* Check command for partial match on primary directions, or exact match on secondary name or abbreviation
* @param {Player} player
* @param {string} command
* @return {?string}
*/
static checkMovement(player, command)
{
if (!player.room || !(player.room instanceof Room)) {
return null;
}
const primaryDirections = ['north', 'south', 'east', 'west', 'up', 'down'];
for (const direction of primaryDirections) {
if (direction.indexOf(command) === 0) {
return direction;
}
}
const secondaryDirections = [
{ abbr: 'ne', name: 'northeast' },
{ abbr: 'nw', name: 'northwest' },
{ abbr: 'se', name: 'southeast' },
{ abbr: 'sw', name: 'southwest' }
];
for (const direction of secondaryDirections) {
if (direction.abbr === command || direction.name.indexOf(command) === 0) {
return direction.name;
}
}
const otherExit = player.room.getExits().find(roomExit => roomExit.direction === command);
return otherExit ? otherExit.direction : null;
}
/**
* Determine if a player can leave the current room to a given direction
* @param {Player} player
* @param {string} direction
* @return {boolean}
*/
static canGo(player, direction)
{
if (!player.room) {
return false;
}
return player.room.getExits().find(roomExit => roomExit.direction === direction) || false;
}
}
exports.CommandParser = CommandParser;
/**
* Used when the player enters a bad command
* @extends Error
*/
class InvalidCommandError extends Error {}
/**
* Used when the player tries a command they don't have access to
* @extends Error
*/
class RestrictedCommandError extends Error {}
exports.InvalidCommandError = InvalidCommandError;
exports.RestrictedCommandError = RestrictedCommandError;