1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | 'use strict';
|
10 |
|
11 | var i18n = new (require('i18n-2'))({ locales: ['en']});
|
12 | var _ = require('underscore');
|
13 | var Hoek = require('hoek');
|
14 | var Parser = require('./parser');
|
15 | var events = require('events');
|
16 | var util = require('util');
|
17 | var async = require('async');
|
18 | var debug = require('debug')('advtxt');
|
19 |
|
20 | var advtxt = {};
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | exports = module.exports = advtxt.Server = function() {
|
30 | var self = this;
|
31 |
|
32 | events.EventEmitter.call(self);
|
33 |
|
34 | self.db = null;
|
35 | self.initialized = false;
|
36 | }
|
37 |
|
38 |
|
39 | util.inherits(advtxt.Server, events.EventEmitter);
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 | advtxt.Server.prototype.initialize = function(db) {
|
48 | var self = this;
|
49 |
|
50 | self.db = db;
|
51 | self.initialized = true;
|
52 | };
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 | advtxt.Server.prototype.reply = function(command, reply) {
|
64 | var self = this;
|
65 |
|
66 | command.replies.push(reply);
|
67 | self.emit('reply', reply);
|
68 |
|
69 | return;
|
70 | };
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | advtxt.Server.prototype.calculateMove = function(direction) {
|
81 | var self = this;
|
82 |
|
83 | var move = [];
|
84 |
|
85 | if (direction === i18n.__('north') || direction === 'n')
|
86 | move = [0, -1];
|
87 | else if (direction === i18n.__('south') || direction === 's')
|
88 | move = [0, 1];
|
89 | else if (direction === i18n.__('east') || direction === 'e')
|
90 | move = [1, 0];
|
91 | else if (direction === i18n.__('west') || direction === 'w')
|
92 | move = [-1, 0];
|
93 | else
|
94 | move = [0, 0];
|
95 |
|
96 |
|
97 | return move;
|
98 | }
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 | advtxt.Server.prototype.resetPlayer = function(command, clearItems, next) {
|
110 | var self = this;
|
111 |
|
112 |
|
113 | command.player.x = 0;
|
114 | command.player.y = 0;
|
115 |
|
116 | command.player.status = "alive";
|
117 | command.move = true;
|
118 |
|
119 |
|
120 | if (typeof clearItems !== 'undefined' && clearItems) {
|
121 | command.player.items = {};
|
122 | command.items = true;
|
123 | self.reply(command, i18n.__("Giving you a blank slate…"));
|
124 | }
|
125 | else {
|
126 | self.reply(command, i18n.__("Moving you to the origin room…"));
|
127 | }
|
128 |
|
129 | debug('Now resetting player.');
|
130 |
|
131 | self.updatePlayerPosition(command, function(err, command) {
|
132 | var self = this;
|
133 | debug('Position reset.');
|
134 |
|
135 | self.updatePlayerItems(command, function(err, command) {
|
136 | debug('Items cleared.');
|
137 | next(null, command);
|
138 | });
|
139 | }.bind(self));
|
140 | }
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 | advtxt.Server.prototype.updatePlayerPosition = function(command, next) {
|
159 | var self = this;
|
160 |
|
161 | debug("Updating Position");
|
162 | var selector = {_id: command.player._id};
|
163 | var data = {x: command.player.x, y: command.player.y};
|
164 |
|
165 |
|
166 | self.db.update('player', selector, data, function(err, success){
|
167 | if (err) next(err, command);
|
168 |
|
169 | if (!success) {
|
170 | console.error("Couldn't update player position.");
|
171 | console.error(command.player);
|
172 | }
|
173 |
|
174 | debug("Successfully updated position.");
|
175 | next(null, command);
|
176 | }.bind(self));
|
177 | }
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 | advtxt.Server.prototype.updatePlayerItems = function(command, next) {
|
187 | var self = this;
|
188 |
|
189 | var selector = {_id: command.player._id};
|
190 | var data = {items: command.player.items};
|
191 |
|
192 | self.db.update('player', selector, data, function(err, success) {
|
193 | if (err) next(err, command);
|
194 |
|
195 |
|
196 | if (!success) {
|
197 | console.error("Couldn't save player: " + command.player.username);
|
198 | console.error(command.player);
|
199 | }
|
200 |
|
201 | next(null, command);
|
202 | }.bind(self));
|
203 | }
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 | advtxt.Server.prototype.updatePlayerStatus = function(command, next) {
|
213 | var self = this;
|
214 |
|
215 | var selector = {_id: command.player._id};
|
216 | var data = {status: command.player.status};
|
217 |
|
218 | self.db.update('player', selector, data, function(err, success) {
|
219 | if (err) next(err, command);
|
220 |
|
221 | if (!success) {
|
222 | console.error("Couldn't save player: " + command.player.username);
|
223 | console.error(command.player);
|
224 | }
|
225 |
|
226 | if (typeof next === 'function' && next) next(null, command);
|
227 | }.bind(self));
|
228 | }
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 | advtxt.Server.prototype.doCommand = function(command, next) {
|
239 | var self = this;
|
240 |
|
241 | var parser = new Parser(command);
|
242 | if (!parser.fail) {
|
243 | command.command = parser.command;
|
244 | }
|
245 | else {
|
246 | next(new Error(__.i18n("I don't know what you mean.")), command);
|
247 | }
|
248 |
|
249 |
|
250 | var verb = command.command.verb;
|
251 | var object = command.command.object;
|
252 | var player = command.player;
|
253 | var room = player.room;
|
254 | var commands = parser.commands;
|
255 |
|
256 |
|
257 | if (command.performed) {
|
258 | next(null, command);
|
259 | }
|
260 |
|
261 |
|
262 | command.performed = true;
|
263 |
|
264 |
|
265 |
|
266 | if (verb === commands.RESET && object === '') {
|
267 | self.resetPlayer(command, false, next);
|
268 | return;
|
269 | }
|
270 | else if (verb === commands.RESET && object === i18n.__('all')) {
|
271 | self.resetPlayer(command, true, next);
|
272 | return;
|
273 | }
|
274 | else if (verb === commands.GET) {
|
275 |
|
276 | if (typeof room.items[object] !== 'undefined') {
|
277 | var available = room.items[object].available(player);
|
278 | if (available === true) {
|
279 |
|
280 | player.items[object] = room.items[object].name;
|
281 | self.reply(command, room.items[object].get(player));
|
282 |
|
283 | command.items = true;
|
284 | }
|
285 |
|
286 | else {
|
287 | self.reply(command, available);
|
288 | }
|
289 | }
|
290 |
|
291 | else {
|
292 | self.reply(command, i18n.__('You can\'t find a "%s" in this room!', object));
|
293 | }
|
294 | }
|
295 | else if (verb === commands.GO) {
|
296 |
|
297 | if (typeof room.exits[object] !== 'undefined') {
|
298 | var available = room.exits[object].available(player);
|
299 | if (available === true) {
|
300 |
|
301 | self.reply(command, room.exits[object].go(player));
|
302 |
|
303 |
|
304 | var move = self.calculateMove(object);
|
305 |
|
306 | command.player.x += move[0];
|
307 | command.player.y += move[1];
|
308 |
|
309 | command.move = true;
|
310 |
|
311 |
|
312 | command.announceRoom = true;
|
313 | }
|
314 |
|
315 | else {
|
316 | self.reply(command, available);
|
317 | }
|
318 | }
|
319 |
|
320 | else {
|
321 |
|
322 | self.reply(command, i18n.__('You can\'t go "%s", it just doesn\'t work.', object));
|
323 | }
|
324 | }
|
325 |
|
326 | else if (typeof room.commands[verb] !== 'undefined') {
|
327 | self.reply(command, room.commands[verb](player));
|
328 | }
|
329 |
|
330 | else if (verb === commands.EXITS) {
|
331 | var exits = [];
|
332 | for (var key in room.exits) {
|
333 | exits.push(room.exits[key].name);
|
334 | }
|
335 |
|
336 | var exitNames = i18n.__('Available exits: ');
|
337 | for (var i = 0; i < exits.length; i++) {
|
338 | exitNames += exits[i];
|
339 | if (i !== exits.length - 1) exitNames += i18n.__(", ");
|
340 | }
|
341 | self.reply(command, exitNames);
|
342 | }
|
343 | else {
|
344 | self.reply(command, i18n.__('Sorry, I don\'t know how to "%s" in this room.', verb));
|
345 | }
|
346 |
|
347 | next(null, command);
|
348 | }
|
349 |
|
350 |
|
351 |
|
352 |
|
353 |
|
354 |
|
355 |
|
356 |
|
357 |
|
358 | advtxt.Server.prototype.getCurrentLocation = function(command, next) {
|
359 | var self = this;
|
360 |
|
361 |
|
362 |
|
363 | if (typeof command.status === 'undefined')
|
364 | command.status = Hoek.clone(command.player.status);
|
365 |
|
366 | self.db.findOne('room', {x: command.player.x, y: command.player.y, map: command.player.map}, function (err, room) {
|
367 | if (err) next(err, command);
|
368 |
|
369 | if (room) {
|
370 |
|
371 |
|
372 | command.player.room = room;
|
373 |
|
374 |
|
375 |
|
376 | var player = command.player;
|
377 |
|
378 |
|
379 | eval(player.room.commands);
|
380 | eval(player.room.items);
|
381 | eval(player.room.exits);
|
382 |
|
383 |
|
384 |
|
385 | if (command.announceRoom) {
|
386 | command.announceRoom = false;
|
387 | self.reply(command, player.room.description);
|
388 | }
|
389 |
|
390 | next(null, command);
|
391 | }
|
392 | });
|
393 | }
|
394 |
|
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 | advtxt.Server.prototype.getPlayer = function(command, next) {
|
403 | var self = this;
|
404 |
|
405 | self.db.findOne('player', {username: command.player, map: "default"}, function(err, player) {
|
406 | if (err) next(err, command);
|
407 |
|
408 | if (player) {
|
409 | command.player = player;
|
410 | next(null, command);
|
411 | }
|
412 | else {
|
413 | self.db.insertOne('player', {
|
414 | username: command.player,
|
415 | map: "default",
|
416 | x: 0,
|
417 | y: 0,
|
418 | status: "alive",
|
419 | room: {},
|
420 | items: {}
|
421 | }, function(err, player) {
|
422 | if (err) throw err;
|
423 |
|
424 | command.player = player;
|
425 | command.announceRoom = true;
|
426 |
|
427 | next(null, command);
|
428 | });
|
429 | }
|
430 | });
|
431 | };
|
432 |
|
433 |
|
434 |
|
435 |
|
436 |
|
437 |
|
438 |
|
439 | advtxt.Server.prototype.processCommand = function(command) {
|
440 | var self = this;
|
441 |
|
442 |
|
443 | command.performed = false;
|
444 |
|
445 | async.waterfall([
|
446 | function(next) {
|
447 |
|
448 | self.getPlayer(command, next);
|
449 | },
|
450 |
|
451 | self.getCurrentLocation.bind(self),
|
452 |
|
453 | self.doCommand.bind(self),
|
454 |
|
455 | function(command, next) {
|
456 | if (command.items) {
|
457 | command.items = false;
|
458 | self.updatePlayerItems(command, next);
|
459 | }
|
460 | else {
|
461 | next(null, command);
|
462 | }
|
463 | },
|
464 |
|
465 | function(command, next) {
|
466 | if (command.move) {
|
467 | command.move = false;
|
468 | self.updatePlayerPosition(command, next);
|
469 | }
|
470 | else {
|
471 | next(null, command);
|
472 | }
|
473 | },
|
474 |
|
475 | function(command, next) {
|
476 | if (command.announceRoom) {
|
477 | self.getCurrentLocation(command, next);
|
478 | }
|
479 | else {
|
480 | next(null, command);
|
481 | }
|
482 | }
|
483 | ], self.finalize.bind(self));
|
484 |
|
485 |
|
486 | }
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 |
|
493 |
|
494 | advtxt.Server.prototype.finalize = function(err, command) {
|
495 | var self = this;
|
496 |
|
497 | debug("Command Finalized");
|
498 |
|
499 | if (err) {
|
500 | console.error("An error occurred. What follows is the error, and the command");
|
501 | console.error(err);
|
502 | console.error(JSON.stringify(command));
|
503 | }
|
504 | else {
|
505 |
|
506 |
|
507 | if (command.status !== command.player.status) {
|
508 | if (command.player.status === 'dead') {
|
509 | self.reply(command, i18n.__('You\'ve died! Send the command `reset all` to try again!'));
|
510 | }
|
511 | else if (command.player.status === 'win') {
|
512 | self.reply(command, i18n.__('You\'ve won the game! How impressive! Send the command `reset all` to play again!'));
|
513 | }
|
514 | self.updatePlayerStatus(command);
|
515 | }
|
516 | else if (command.player.status === 'dead') {
|
517 | self.reply(command, i18n.__('You\'re still dead! Send the command `reset all` to try again!'));
|
518 | }
|
519 | else if (command.player.status === 'win') {
|
520 | self.reply(command, i18n.__('Yes, your win was glorious, but send the command `reset all` to play again!'));
|
521 | }
|
522 | }
|
523 |
|
524 |
|
525 | command.done(command);
|
526 | }
|
527 |
|