1 |
/* |
2 |
CrossFire, A Multiplayer game for X-windows |
3 |
|
4 |
Copyright (C) 2002 Mark Wedel & Crossfire Development Team |
5 |
Copyright (C) 1992 Frank Tore Johansen |
6 |
|
7 |
This program is free software; you can redistribute it and/or modify |
8 |
it under the terms of the GNU General Public License as published by |
9 |
the Free Software Foundation; either version 2 of the License, or |
10 |
(at your option) any later version. |
11 |
|
12 |
This program is distributed in the hope that it will be useful, |
13 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
GNU General Public License for more details. |
16 |
|
17 |
You should have received a copy of the GNU General Public License |
18 |
along with this program; if not, write to the Free Software |
19 |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
20 |
|
21 |
The authors can be reached via e-mail at <crossfire@schmorp.de> |
22 |
*/ |
23 |
|
24 |
#include <global.h> |
25 |
#ifndef __CEXTRACT__ |
26 |
# include <sproto.h> |
27 |
#endif |
28 |
#include <spells.h> |
29 |
#include <loader.h> |
30 |
#include <define.h> |
31 |
|
32 |
extern void sub_weight (object *, signed long); |
33 |
extern void add_weight (object *, signed long); |
34 |
extern long pticks; |
35 |
|
36 |
/* If flag is non zero, it means that we want to try and save everyone, but |
37 |
* keep the game running. Thus, we don't want to free any information. |
38 |
*/ |
39 |
void |
40 |
emergency_save (int flag) |
41 |
{ |
42 |
player *pl; |
43 |
|
44 |
#ifndef NO_EMERGENCY_SAVE |
45 |
trying_emergency_save = 1; |
46 |
if (editor) |
47 |
return; |
48 |
LOG (llevError, "Emergency save: "); |
49 |
for (pl = first_player; pl != NULL; pl = pl->next) |
50 |
{ |
51 |
if (!pl->ob) |
52 |
{ |
53 |
LOG (llevError, "No name, ignoring this.\n"); |
54 |
continue; |
55 |
} |
56 |
LOG (llevError, "%s ", pl->ob->name); |
57 |
new_draw_info (NDI_UNIQUE, 0, pl->ob, "Emergency save..."); |
58 |
|
59 |
/* If we are not exiting the game (ie, this is sort of a backup save), then |
60 |
* don't change the location back to the village. Note that there are other |
61 |
* options to have backup saves be done at the starting village |
62 |
*/ |
63 |
if (!flag) |
64 |
{ |
65 |
strcpy (pl->maplevel, first_map_path); |
66 |
if (pl->ob->map != NULL) |
67 |
pl->ob->map = NULL; |
68 |
pl->ob->x = -1; |
69 |
pl->ob->y = -1; |
70 |
} |
71 |
if (!save_player (pl->ob, flag)) |
72 |
{ |
73 |
LOG (llevError, "(failed) "); |
74 |
new_draw_info (NDI_UNIQUE, 0, pl->ob, "Emergency save failed, checking score..."); |
75 |
} |
76 |
check_score (pl->ob); |
77 |
} |
78 |
LOG (llevError, "\n"); |
79 |
#else |
80 |
LOG (llevInfo, "Emergency saves disabled, no save attempted\n"); |
81 |
#endif |
82 |
/* If the game is exiting, remove the player locks */ |
83 |
if (!flag) |
84 |
{ |
85 |
for (pl = first_player; pl != NULL; pl = pl->next) |
86 |
{ |
87 |
if (pl->ob) |
88 |
{ |
89 |
} |
90 |
} |
91 |
} |
92 |
} |
93 |
|
94 |
/* Delete character with name. if new is set, also delete the new |
95 |
* style directory, otherwise, just delete the old style playfile |
96 |
* (needed for transition) |
97 |
*/ |
98 |
void |
99 |
delete_character (const char *name, int newchar) |
100 |
{ |
101 |
char buf[MAX_BUF]; |
102 |
|
103 |
sprintf (buf, "%s/%s/%s.pl", settings.localdir, settings.playerdir, name); |
104 |
if (unlink (buf) == -1) |
105 |
LOG (llevDebug, "Cannot delete character file %s: %s\n", buf, strerror (errno)); |
106 |
if (newchar) |
107 |
{ |
108 |
sprintf (buf, "%s/%s/%s", settings.localdir, settings.playerdir, name); |
109 |
/* this effectively does an rm -rf on the directory */ |
110 |
remove_directory (buf); |
111 |
} |
112 |
} |
113 |
|
114 |
/* This verify that a character of name exits, and that it matches |
115 |
* password. It return 0 if there is match, 1 if no such player, |
116 |
* 2 if incorrect password. |
117 |
*/ |
118 |
|
119 |
int |
120 |
verify_player (const char *name, char *password) |
121 |
{ |
122 |
char buf[MAX_BUF]; |
123 |
int comp; |
124 |
FILE *fp; |
125 |
|
126 |
if (strpbrk (name, "/.\\") != NULL) |
127 |
{ |
128 |
LOG (llevError, "Username contains illegal characters: %s\n", name); |
129 |
return 1; |
130 |
} |
131 |
|
132 |
snprintf (buf, sizeof (buf), "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, name, name); |
133 |
if (strlen (buf) >= sizeof (buf) - 1) |
134 |
{ |
135 |
LOG (llevError, "Username too long: %s\n", name); |
136 |
return 1; |
137 |
} |
138 |
|
139 |
if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL) |
140 |
return 1; |
141 |
|
142 |
/* Read in the file until we find the password line. Our logic could |
143 |
* be a bit better on cleaning up the password from the file, but since |
144 |
* it is written by the program, I think it is fair to assume that the |
145 |
* syntax should be pretty standard. |
146 |
*/ |
147 |
while (fgets (buf, MAX_BUF - 1, fp) != NULL) |
148 |
{ |
149 |
if (!strncmp (buf, "password ", 9)) |
150 |
{ |
151 |
buf[strlen (buf) - 1] = 0; /* remove newline */ |
152 |
if (check_password (password, buf + 9)) |
153 |
{ |
154 |
close_and_delete (fp, comp); |
155 |
return 0; |
156 |
} |
157 |
else |
158 |
{ |
159 |
close_and_delete (fp, comp); |
160 |
return 2; |
161 |
} |
162 |
} |
163 |
} |
164 |
LOG (llevDebug, "Could not find a password line in player %s\n", name); |
165 |
close_and_delete (fp, comp); |
166 |
return 1; |
167 |
} |
168 |
|
169 |
/* Checks to see if anyone else by 'name' is currently playing. |
170 |
* If we find that file or another character of some name is already in the |
171 |
* game, we don't let this person join (we should really let the new player |
172 |
* enter the password, and if correct, disconnect that socket and attach it to |
173 |
* the players current session. |
174 |
* If no one by that name is currently playing, we then make sure the name |
175 |
* doesn't include any bogus characters. |
176 |
* We return 0 if the name is in use/bad, 1 if it is OK to use this name. |
177 |
*/ |
178 |
|
179 |
int |
180 |
check_name (player *me, const char *name) |
181 |
{ |
182 |
player *pl; |
183 |
|
184 |
for (pl = first_player; pl != NULL; pl = pl->next) |
185 |
if (pl != me && pl->ob->name != NULL && !strcmp (pl->ob->name, name)) |
186 |
{ |
187 |
new_draw_info (NDI_UNIQUE, 0, me->ob, "That name is already in use."); |
188 |
return 0; |
189 |
} |
190 |
|
191 |
if (*name == '\0') |
192 |
{ |
193 |
new_draw_info (NDI_UNIQUE, 0, me->ob, "Null names are not allowed."); |
194 |
return 0; |
195 |
} |
196 |
|
197 |
if (!playername_ok (name)) |
198 |
{ |
199 |
new_draw_info (NDI_UNIQUE, 0, me->ob, "That name contains illegal characters."); |
200 |
return 0; |
201 |
} |
202 |
if (strlen (name) >= MAX_NAME) |
203 |
{ |
204 |
new_draw_info (NDI_UNIQUE, 0, me->ob, "That name is too long."); |
205 |
return 0; |
206 |
} |
207 |
|
208 |
return 1; |
209 |
} |
210 |
|
211 |
int |
212 |
create_savedir_if_needed (char *savedir) |
213 |
{ |
214 |
struct stat *buf; |
215 |
|
216 |
if ((buf = (struct stat *) malloc (sizeof (struct stat))) == NULL) |
217 |
{ |
218 |
LOG (llevError, "Unable to save playerfile... out of memory.\n"); |
219 |
return 0; |
220 |
} |
221 |
else |
222 |
{ |
223 |
stat (savedir, buf); |
224 |
if (!S_ISDIR (buf->st_mode)) |
225 |
if (mkdir (savedir, SAVE_DIR_MODE)) |
226 |
{ |
227 |
LOG (llevError, "Unable to create player savedir %s: %s\n", savedir, strerror (errno)); |
228 |
return 0; |
229 |
} |
230 |
free (buf); |
231 |
} |
232 |
return 1; |
233 |
} |
234 |
|
235 |
/* |
236 |
* If flag is set, it's only backup, ie dont remove objects from inventory |
237 |
* If BACKUP_SAVE_AT_HOME is set, and the flag is set, then the player |
238 |
* will be saved at the emergency save location. |
239 |
* Returns non zero if successful. |
240 |
*/ |
241 |
|
242 |
int |
243 |
save_player (object *op, int flag) |
244 |
{ |
245 |
object *tmp, *container = NULL; |
246 |
player *pl = op->contr; |
247 |
int i, wiz = QUERY_FLAG (op, FLAG_WIZ); |
248 |
|
249 |
#ifdef BACKUP_SAVE_AT_HOME |
250 |
sint16 backup_x, backup_y; |
251 |
#endif |
252 |
|
253 |
if (!op->stats.exp) |
254 |
return 0; /* no experience, no save */ |
255 |
|
256 |
flag &= 1; |
257 |
|
258 |
if (!pl->name_changed || (!flag && !op->stats.exp)) |
259 |
{ |
260 |
if (!flag) |
261 |
{ |
262 |
new_draw_info (NDI_UNIQUE, 0, op, "Your game is not valid,"); |
263 |
new_draw_info (NDI_UNIQUE, 0, op, "Game not saved."); |
264 |
} |
265 |
return 0; |
266 |
} |
267 |
|
268 |
/* Sanity check - some stuff changes this when player is exiting */ |
269 |
if (op->type != PLAYER) |
270 |
return 0; |
271 |
|
272 |
/* Prevent accidental saves if connection is reset after player has |
273 |
* mostly exited. |
274 |
*/ |
275 |
if (pl->state != ST_PLAYING && pl->state != ST_GET_PARTY_PASSWORD) |
276 |
return 0; |
277 |
|
278 |
INVOKE_PLAYER (SAVE, op->contr); |
279 |
|
280 |
if (flag == 0) |
281 |
terminate_all_pets (op); |
282 |
|
283 |
object_freezer freezer; |
284 |
|
285 |
/* Eneq(@csd.uu.se): If we have an open container hide it. */ |
286 |
if (op->container) |
287 |
{ |
288 |
container = op->container; |
289 |
op->container = NULL; |
290 |
} |
291 |
|
292 |
fprintf (freezer, "password %s\n", pl->password); |
293 |
|
294 |
if (settings.set_title == TRUE) |
295 |
if (pl->own_title[0] != '\0') |
296 |
fprintf (freezer, "title %s\n", pl->own_title); |
297 |
|
298 |
fprintf (freezer, "explore %d\n", pl->explore); |
299 |
fprintf (freezer, "gen_hp %d\n", pl->gen_hp); |
300 |
fprintf (freezer, "gen_sp %d\n", pl->gen_sp); |
301 |
fprintf (freezer, "gen_grace %d\n", pl->gen_grace); |
302 |
fprintf (freezer, "listening %d\n", pl->listening); |
303 |
fprintf (freezer, "shoottype %d\n", pl->shoottype); |
304 |
fprintf (freezer, "bowtype %d\n", pl->bowtype); |
305 |
fprintf (freezer, "petmode %d\n", pl->petmode); |
306 |
fprintf (freezer, "peaceful %d\n", pl->peaceful); |
307 |
fprintf (freezer, "no_shout %d\n", pl->no_shout); |
308 |
fprintf (freezer, "digestion %d\n", pl->digestion); |
309 |
fprintf (freezer, "pickup %d\n", pl->mode); |
310 |
fprintf (freezer, "outputs_sync %d\n", pl->outputs_sync); |
311 |
fprintf (freezer, "outputs_count %d\n", pl->outputs_count); |
312 |
/* Match the enumerations but in string form */ |
313 |
fprintf (freezer, "usekeys %s\n", pl->usekeys == key_inventory ? "key_inventory" : (pl->usekeys == keyrings ? "keyrings" : "containers")); |
314 |
/* Match the enumerations but in string form */ |
315 |
fprintf (freezer, "unapply %s\n", pl->unapply == unapply_nochoice ? "unapply_nochoice" : |
316 |
(pl->unapply == unapply_never ? "unapply_never" : "unapply_always")); |
317 |
|
318 |
#ifdef BACKUP_SAVE_AT_HOME |
319 |
if (op->map != NULL && flag == 0) |
320 |
#else |
321 |
if (op->map != NULL) |
322 |
#endif |
323 |
fprintf (freezer, "map %s\n", op->map->path); |
324 |
else |
325 |
fprintf (freezer, "map %s\n", settings.emergency_mapname); |
326 |
|
327 |
fprintf (freezer, "savebed_map %s\n", pl->savebed_map); |
328 |
fprintf (freezer, "bed_x %d\nbed_y %d\n", pl->bed_x, pl->bed_y); |
329 |
fprintf (freezer, "weapon_sp %f\n", pl->weapon_sp); |
330 |
fprintf (freezer, "Str %d\n", pl->orig_stats.Str); |
331 |
fprintf (freezer, "Dex %d\n", pl->orig_stats.Dex); |
332 |
fprintf (freezer, "Con %d\n", pl->orig_stats.Con); |
333 |
fprintf (freezer, "Int %d\n", pl->orig_stats.Int); |
334 |
fprintf (freezer, "Pow %d\n", pl->orig_stats.Pow); |
335 |
fprintf (freezer, "Wis %d\n", pl->orig_stats.Wis); |
336 |
fprintf (freezer, "Cha %d\n", pl->orig_stats.Cha); |
337 |
|
338 |
fprintf (freezer, "lev_array %d\n", op->level > 10 ? 10 : op->level); |
339 |
for (i = 1; i <= pl->last_level && i <= 10; i++) |
340 |
{ |
341 |
fprintf (freezer, "%d\n", pl->levhp[i]); |
342 |
fprintf (freezer, "%d\n", pl->levsp[i]); |
343 |
fprintf (freezer, "%d\n", pl->levgrace[i]); |
344 |
} |
345 |
|
346 |
freezer.put (op->contr); |
347 |
|
348 |
fprintf (freezer, "endplst\n"); |
349 |
|
350 |
SET_FLAG (op, FLAG_NO_FIX_PLAYER); |
351 |
CLEAR_FLAG (op, FLAG_WIZ); |
352 |
#ifdef BACKUP_SAVE_AT_HOME |
353 |
if (flag) |
354 |
{ |
355 |
backup_x = op->x; |
356 |
backup_y = op->y; |
357 |
op->x = -1; |
358 |
op->y = -1; |
359 |
} |
360 |
/* Save objects, but not unpaid objects. Don't remove objects from |
361 |
* inventory. |
362 |
*/ |
363 |
save_object (freezer, op, 2); |
364 |
if (flag) |
365 |
{ |
366 |
op->x = backup_x; |
367 |
op->y = backup_y; |
368 |
} |
369 |
#else |
370 |
save_object (freezer, op, 3); /* don't check and don't remove */ |
371 |
#endif |
372 |
|
373 |
char filename[MAX_BUF]; |
374 |
|
375 |
sprintf (filename, "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, &op->name, &op->name); |
376 |
make_path_to_file (filename); |
377 |
freezer.save (filename); |
378 |
|
379 |
CLEAR_FLAG (op, FLAG_NO_FIX_PLAYER); |
380 |
|
381 |
if (!flag) |
382 |
while ((tmp = op->inv)) |
383 |
tmp->destroy (1); |
384 |
|
385 |
/* Eneq(@csd.uu.se): Reveal the container if we have one. */ |
386 |
if (flag && container != NULL) |
387 |
op->container = container; |
388 |
|
389 |
if (wiz) |
390 |
SET_FLAG (op, FLAG_WIZ); |
391 |
|
392 |
if (!flag) |
393 |
esrv_send_inventory (op, op); |
394 |
|
395 |
return 1; |
396 |
} |
397 |
|
398 |
void |
399 |
check_login (object *op) |
400 |
{ |
401 |
char filename[MAX_BUF]; |
402 |
char buf[MAX_BUF], bufall[MAX_BUF]; |
403 |
int i, value; |
404 |
player *pl = op->contr; |
405 |
int correct = 0; |
406 |
|
407 |
strcpy (pl->maplevel, first_map_path); |
408 |
sprintf (filename, "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, &op->name, &op->name); |
409 |
|
410 |
object_thawer thawer (filename); |
411 |
|
412 |
/* If no file, must be a new player, so lets get confirmation of |
413 |
* the password. Return control to the higher level dispatch, |
414 |
* since the rest of this just deals with loading of the file. |
415 |
*/ |
416 |
if (!thawer) |
417 |
{ |
418 |
confirm_password (op); |
419 |
return; |
420 |
} |
421 |
|
422 |
if (fgets (bufall, MAX_BUF, thawer) != NULL) |
423 |
{ |
424 |
if (sscanf (bufall, "password %s\n", buf)) |
425 |
{ |
426 |
/* New password scheme: */ |
427 |
correct = check_password (pl->write_buf + 1, buf); |
428 |
} |
429 |
} |
430 |
|
431 |
if (!correct) |
432 |
{ |
433 |
new_draw_info (NDI_UNIQUE, 0, op, " "); |
434 |
new_draw_info (NDI_UNIQUE, 0, op, "Wrong Password!"); |
435 |
new_draw_info (NDI_UNIQUE, 0, op, " "); |
436 |
op->name = op->name_pl = "noname"; |
437 |
op->contr->socket.password_fails++; |
438 |
if (op->contr->socket.password_fails >= MAX_PASSWORD_FAILURES) |
439 |
{ |
440 |
new_draw_info (NDI_UNIQUE, 0, op, "You gave an incorrect password too many times, you will now be dropped from the server."); |
441 |
LOG (llevInfo, "A player connecting from %s has been dropped for password failure\n", op->contr->socket.host); |
442 |
op->contr->socket.status = Ns_Dead; /* the socket loop should handle the rest for us */ |
443 |
} |
444 |
else |
445 |
get_name (op); |
446 |
|
447 |
return; /* Once again, rest of code just loads the char */ |
448 |
} |
449 |
|
450 |
#ifdef SAVE_INTERVAL |
451 |
pl->last_save_time = time (NULL); |
452 |
#endif /* SAVE_INTERVAL */ |
453 |
pl->party = NULL; |
454 |
if (settings.search_items == TRUE) |
455 |
pl->search_str[0] = '\0'; |
456 |
pl->name_changed = 1; |
457 |
pl->orig_stats.Str = 0; |
458 |
pl->orig_stats.Dex = 0; |
459 |
pl->orig_stats.Con = 0; |
460 |
pl->orig_stats.Int = 0; |
461 |
pl->orig_stats.Pow = 0; |
462 |
pl->orig_stats.Wis = 0; |
463 |
pl->orig_stats.Cha = 0; |
464 |
strcpy (pl->savebed_map, first_map_path); |
465 |
pl->bed_x = 0, pl->bed_y = 0; |
466 |
pl->spellparam[0] = '\0'; |
467 |
|
468 |
/* Loop through the file, loading the rest of the values */ |
469 |
while (fgets (bufall, MAX_BUF, thawer) != NULL) |
470 |
{ |
471 |
sscanf (bufall, "%s %d\n", buf, &value); |
472 |
if (!strcmp (buf, "endplst")) |
473 |
break; |
474 |
else if (!strcmp (buf, "oid")) |
475 |
thawer.get (pl, value); |
476 |
else if (!strcmp (buf, "title") && settings.set_title == TRUE) |
477 |
sscanf (bufall, "title %[^\n]", pl->own_title); |
478 |
else if (!strcmp (buf, "explore")) |
479 |
pl->explore = value; |
480 |
else if (!strcmp (buf, "gen_hp")) |
481 |
pl->gen_hp = value; |
482 |
else if (!strcmp (buf, "shoottype")) |
483 |
pl->shoottype = (rangetype) value; |
484 |
else if (!strcmp (buf, "bowtype")) |
485 |
pl->bowtype = (bowtype_t) value; |
486 |
else if (!strcmp (buf, "petmode")) |
487 |
pl->petmode = (petmode_t) value; |
488 |
else if (!strcmp (buf, "gen_sp")) |
489 |
pl->gen_sp = value; |
490 |
else if (!strcmp (buf, "gen_grace")) |
491 |
pl->gen_grace = value; |
492 |
else if (!strcmp (buf, "listening")) |
493 |
pl->listening = value; |
494 |
else if (!strcmp (buf, "peaceful")) |
495 |
pl->peaceful = value; |
496 |
else if (!strcmp (buf, "no_shout")) |
497 |
pl->no_shout = value; |
498 |
else if (!strcmp (buf, "digestion")) |
499 |
pl->digestion = value; |
500 |
else if (!strcmp (buf, "pickup")) |
501 |
pl->mode = value; |
502 |
else if (!strcmp (buf, "outputs_sync")) |
503 |
pl->outputs_sync = value; |
504 |
else if (!strcmp (buf, "outputs_count")) |
505 |
pl->outputs_count = value; |
506 |
else if (!strcmp (buf, "map")) |
507 |
sscanf (bufall, "map %s", pl->maplevel); |
508 |
else if (!strcmp (buf, "savebed_map")) |
509 |
sscanf (bufall, "savebed_map %s", pl->savebed_map); |
510 |
else if (!strcmp (buf, "bed_x")) |
511 |
pl->bed_x = value; |
512 |
else if (!strcmp (buf, "bed_y")) |
513 |
pl->bed_y = value; |
514 |
else if (!strcmp (buf, "weapon_sp")) |
515 |
sscanf (buf, "weapon_sp %f", &pl->weapon_sp); |
516 |
else if (!strcmp (buf, "Str")) |
517 |
pl->orig_stats.Str = value; |
518 |
else if (!strcmp (buf, "Dex")) |
519 |
pl->orig_stats.Dex = value; |
520 |
else if (!strcmp (buf, "Con")) |
521 |
pl->orig_stats.Con = value; |
522 |
else if (!strcmp (buf, "Int")) |
523 |
pl->orig_stats.Int = value; |
524 |
else if (!strcmp (buf, "Pow")) |
525 |
pl->orig_stats.Pow = value; |
526 |
else if (!strcmp (buf, "Wis")) |
527 |
pl->orig_stats.Wis = value; |
528 |
else if (!strcmp (buf, "Cha")) |
529 |
pl->orig_stats.Cha = value; |
530 |
else if (!strcmp (buf, "usekeys")) |
531 |
{ |
532 |
if (!strcmp (bufall + 8, "key_inventory\n")) |
533 |
pl->usekeys = key_inventory; |
534 |
else if (!strcmp (bufall + 8, "keyrings\n")) |
535 |
pl->usekeys = keyrings; |
536 |
else if (!strcmp (bufall + 8, "containers\n")) |
537 |
pl->usekeys = containers; |
538 |
else |
539 |
LOG (llevDebug, "load_player: got unknown usekeys type: %s\n", bufall + 8); |
540 |
} |
541 |
else if (!strcmp (buf, "unapply")) |
542 |
{ |
543 |
if (!strcmp (bufall + 8, "unapply_nochoice\n")) |
544 |
pl->unapply = unapply_nochoice; |
545 |
else if (!strcmp (bufall + 8, "unapply_never\n")) |
546 |
pl->unapply = unapply_never; |
547 |
else if (!strcmp (bufall + 8, "unapply_always\n")) |
548 |
pl->unapply = unapply_always; |
549 |
else |
550 |
LOG (llevDebug, "load_player: got unknown unapply type: %s\n", bufall + 8); |
551 |
} |
552 |
else if (!strcmp (buf, "lev_array")) |
553 |
{ |
554 |
for (i = 1; i <= value; i++) |
555 |
{ |
556 |
char line[128]; |
557 |
|
558 |
fgets (line, 128, thawer); |
559 |
pl->levhp[i] = atoi (line); |
560 |
fgets (line, 128, thawer); |
561 |
pl->levsp[i] = atoi (line); |
562 |
fgets (line, 128, thawer); |
563 |
pl->levgrace[i] = atoi (line); |
564 |
} |
565 |
/* spell_array code removed - don't know when that was last used. |
566 |
* Even the load code below will someday be replaced by spells being |
567 |
* objects. |
568 |
*/ |
569 |
} |
570 |
else if (!strcmp (buf, "known_spell")) |
571 |
{ |
572 |
#if 0 |
573 |
/* Logic is left here in case someone wants to try |
574 |
* and write code to update to spell objects. |
575 |
*/ |
576 |
char *cp = strchr (bufall, '\n'); |
577 |
|
578 |
*cp = '\0'; |
579 |
cp = strchr (bufall, ' '); |
580 |
cp++; |
581 |
for (i = 0; i < NROFREALSPELLS; i++) |
582 |
if (!strcmp (spells[i].name, cp)) |
583 |
{ |
584 |
pl->known_spells[pl->nrofknownspells++] = i; |
585 |
break; |
586 |
} |
587 |
if (i == NROFREALSPELLS) |
588 |
LOG (llevDebug, "Error: unknown spell (%s)\n", cp); |
589 |
#endif |
590 |
} |
591 |
} /* End of loop loading the character file */ |
592 |
|
593 |
leave_map (op); |
594 |
op->speed = 0; |
595 |
update_ob_speed (op); |
596 |
|
597 |
clear_object (op); |
598 |
|
599 |
op->contr = pl; |
600 |
pl->ob = op; |
601 |
|
602 |
/* this loads the standard objects values. */ |
603 |
load_object (thawer, op, 0); |
604 |
|
605 |
CLEAR_FLAG (op, FLAG_NO_FIX_PLAYER); |
606 |
|
607 |
assign (pl->title, op->arch->clone.name); |
608 |
|
609 |
/* If the map where the person was last saved does not exist, |
610 |
* restart them on their home-savebed. This is good for when |
611 |
* maps change between versions |
612 |
* First, we check for partial path, then check to see if the full |
613 |
* path (for unique player maps) |
614 |
*/ |
615 |
if (check_path (pl->maplevel, 1) == -1) |
616 |
{ |
617 |
if (check_path (pl->maplevel, 0) == -1) |
618 |
{ |
619 |
strcpy (pl->maplevel, pl->savebed_map); |
620 |
op->x = pl->bed_x, op->y = pl->bed_y; |
621 |
} |
622 |
} |
623 |
|
624 |
/* make sure he's a player--needed because of class change. */ |
625 |
op->type = PLAYER; |
626 |
|
627 |
enter_exit (op, NULL); |
628 |
|
629 |
pl->name_changed = 1; |
630 |
pl->state = ST_PLAYING; |
631 |
#ifdef AUTOSAVE |
632 |
pl->last_save_tick = pticks; |
633 |
#endif |
634 |
op->carrying = sum_weight (op); |
635 |
/* Need to call fix_player now - program modified so that it is not |
636 |
* called during the load process (FLAG_NO_FIX_PLAYER set when |
637 |
* saved) |
638 |
* Moved ahead of the esrv functions, so proper weights will be |
639 |
* sent to the client. |
640 |
*/ |
641 |
link_player_skills (op); |
642 |
|
643 |
if (!legal_range (op, op->contr->shoottype)) |
644 |
op->contr->shoottype = range_none; |
645 |
|
646 |
fix_player (op); |
647 |
|
648 |
/* if it's a dragon player, set the correct title here */ |
649 |
if (is_dragon_pl (op) && op->inv != NULL) |
650 |
{ |
651 |
object *tmp, *abil = NULL, *skin = NULL; |
652 |
|
653 |
for (tmp = op->inv; tmp != NULL; tmp = tmp->below) |
654 |
{ |
655 |
if (tmp->type == FORCE) |
656 |
{ |
657 |
if (strcmp (tmp->arch->name, "dragon_ability_force") == 0) |
658 |
abil = tmp; |
659 |
else if (strcmp (tmp->arch->name, "dragon_skin_force") == 0) |
660 |
skin = tmp; |
661 |
} |
662 |
} |
663 |
set_dragon_name (op, abil, skin); |
664 |
} |
665 |
|
666 |
new_draw_info (NDI_UNIQUE, 0, op, "Welcome Back!"); |
667 |
new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s has entered the game.", &pl->ob->name); |
668 |
|
669 |
INVOKE_PLAYER (LOAD, pl, ARG_STRING (filename)); |
670 |
INVOKE_PLAYER (LOGIN, pl); |
671 |
|
672 |
op->contr->socket.update_look = 1; |
673 |
/* If the player should be dead, call kill_player for them |
674 |
* Only check for hp - if player lacks food, let the normal |
675 |
* logic for that to take place. If player is permanently |
676 |
* dead, and not using permadeath mode, the kill_player will |
677 |
* set the play_again flag, so return. |
678 |
*/ |
679 |
if (op->stats.hp < 0) |
680 |
{ |
681 |
new_draw_info (NDI_UNIQUE, 0, op, "Your character was dead last your played."); |
682 |
kill_player (op); |
683 |
if (pl->state != ST_PLAYING) |
684 |
return; |
685 |
} |
686 |
LOG (llevInfo, "LOGIN: Player named %s from ip %s\n", &op->name, op->contr->socket.host); |
687 |
|
688 |
/* Do this after checking for death - no reason sucking up bandwidth if |
689 |
* the data isn't needed. |
690 |
*/ |
691 |
esrv_new_player (op->contr, op->weight + op->carrying); |
692 |
esrv_send_inventory (op, op); |
693 |
esrv_add_spells (op->contr, NULL); |
694 |
|
695 |
CLEAR_FLAG (op, FLAG_FRIENDLY); |
696 |
|
697 |
/* can_use_shield is a new flag. However, the can_use.. seems to largely come |
698 |
* from the class, and not race. I don't see any way to get the class information |
699 |
* to then update this. I don't think this will actually break anything - anyone |
700 |
* that can use armour should be able to use a shield. What this may 'break' |
701 |
* are features new characters get, eg, if someone starts up with a Q, they |
702 |
* should be able to use a shield. However, old Q's won't get that advantage. |
703 |
*/ |
704 |
if (QUERY_FLAG (op, FLAG_USE_ARMOUR)) |
705 |
SET_FLAG (op, FLAG_USE_SHIELD); |
706 |
|
707 |
return; |
708 |
} |