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, "digestion %d\n", pl->digestion); |
308 |
fprintf (freezer, "pickup %d\n", pl->mode); |
309 |
fprintf (freezer, "outputs_sync %d\n", pl->outputs_sync); |
310 |
fprintf (freezer, "outputs_count %d\n", pl->outputs_count); |
311 |
/* Match the enumerations but in string form */ |
312 |
fprintf (freezer, "usekeys %s\n", pl->usekeys == key_inventory ? "key_inventory" : (pl->usekeys == keyrings ? "keyrings" : "containers")); |
313 |
/* Match the enumerations but in string form */ |
314 |
fprintf (freezer, "unapply %s\n", pl->unapply == unapply_nochoice ? "unapply_nochoice" : |
315 |
(pl->unapply == unapply_never ? "unapply_never" : "unapply_always")); |
316 |
|
317 |
#ifdef BACKUP_SAVE_AT_HOME |
318 |
if (op->map != NULL && flag == 0) |
319 |
#else |
320 |
if (op->map != NULL) |
321 |
#endif |
322 |
fprintf (freezer, "map %s\n", op->map->path); |
323 |
else |
324 |
fprintf (freezer, "map %s\n", settings.emergency_mapname); |
325 |
|
326 |
fprintf (freezer, "savebed_map %s\n", pl->savebed_map); |
327 |
fprintf (freezer, "bed_x %d\nbed_y %d\n", pl->bed_x, pl->bed_y); |
328 |
fprintf (freezer, "weapon_sp %f\n", pl->weapon_sp); |
329 |
fprintf (freezer, "Str %d\n", pl->orig_stats.Str); |
330 |
fprintf (freezer, "Dex %d\n", pl->orig_stats.Dex); |
331 |
fprintf (freezer, "Con %d\n", pl->orig_stats.Con); |
332 |
fprintf (freezer, "Int %d\n", pl->orig_stats.Int); |
333 |
fprintf (freezer, "Pow %d\n", pl->orig_stats.Pow); |
334 |
fprintf (freezer, "Wis %d\n", pl->orig_stats.Wis); |
335 |
fprintf (freezer, "Cha %d\n", pl->orig_stats.Cha); |
336 |
|
337 |
fprintf (freezer, "lev_array %d\n", op->level > 10 ? 10 : op->level); |
338 |
for (i = 1; i <= pl->last_level && i <= 10; i++) |
339 |
{ |
340 |
fprintf (freezer, "%d\n", pl->levhp[i]); |
341 |
fprintf (freezer, "%d\n", pl->levsp[i]); |
342 |
fprintf (freezer, "%d\n", pl->levgrace[i]); |
343 |
} |
344 |
|
345 |
freezer.put (op->contr); |
346 |
|
347 |
fprintf (freezer, "endplst\n"); |
348 |
|
349 |
SET_FLAG (op, FLAG_NO_FIX_PLAYER); |
350 |
CLEAR_FLAG (op, FLAG_WIZ); |
351 |
#ifdef BACKUP_SAVE_AT_HOME |
352 |
if (flag) |
353 |
{ |
354 |
backup_x = op->x; |
355 |
backup_y = op->y; |
356 |
op->x = -1; |
357 |
op->y = -1; |
358 |
} |
359 |
/* Save objects, but not unpaid objects. Don't remove objects from |
360 |
* inventory. |
361 |
*/ |
362 |
save_object (freezer, op, 2); |
363 |
if (flag) |
364 |
{ |
365 |
op->x = backup_x; |
366 |
op->y = backup_y; |
367 |
} |
368 |
#else |
369 |
save_object (freezer, op, 3); /* don't check and don't remove */ |
370 |
#endif |
371 |
|
372 |
char filename[MAX_BUF]; |
373 |
|
374 |
sprintf (filename, "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, &op->name, &op->name); |
375 |
make_path_to_file (filename); |
376 |
freezer.save (filename); |
377 |
|
378 |
CLEAR_FLAG (op, FLAG_NO_FIX_PLAYER); |
379 |
|
380 |
if (!flag) |
381 |
while ((tmp = op->inv)) |
382 |
tmp->destroy (1); |
383 |
|
384 |
/* Eneq(@csd.uu.se): Reveal the container if we have one. */ |
385 |
if (flag && container != NULL) |
386 |
op->container = container; |
387 |
|
388 |
if (wiz) |
389 |
SET_FLAG (op, FLAG_WIZ); |
390 |
|
391 |
if (!flag) |
392 |
esrv_send_inventory (op, op); |
393 |
|
394 |
return 1; |
395 |
} |
396 |
|
397 |
void |
398 |
check_login (object *op) |
399 |
{ |
400 |
char filename[MAX_BUF]; |
401 |
char buf[MAX_BUF], bufall[MAX_BUF]; |
402 |
int i, value; |
403 |
player *pl = op->contr; |
404 |
int correct = 0; |
405 |
|
406 |
strcpy (pl->maplevel, first_map_path); |
407 |
sprintf (filename, "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, &op->name, &op->name); |
408 |
|
409 |
object_thawer thawer (filename); |
410 |
|
411 |
/* If no file, must be a new player, so lets get confirmation of |
412 |
* the password. Return control to the higher level dispatch, |
413 |
* since the rest of this just deals with loading of the file. |
414 |
*/ |
415 |
if (!thawer) |
416 |
{ |
417 |
confirm_password (op); |
418 |
return; |
419 |
} |
420 |
|
421 |
if (fgets (bufall, MAX_BUF, thawer) != NULL) |
422 |
{ |
423 |
if (sscanf (bufall, "password %s\n", buf)) |
424 |
{ |
425 |
/* New password scheme: */ |
426 |
correct = check_password (pl->write_buf + 1, buf); |
427 |
} |
428 |
} |
429 |
|
430 |
if (!correct) |
431 |
{ |
432 |
new_draw_info (NDI_UNIQUE, 0, op, " "); |
433 |
new_draw_info (NDI_UNIQUE, 0, op, "Wrong Password!"); |
434 |
new_draw_info (NDI_UNIQUE, 0, op, " "); |
435 |
op->name = op->name_pl = "noname"; |
436 |
op->contr->socket->password_fails++; |
437 |
if (op->contr->socket->password_fails >= MAX_PASSWORD_FAILURES) |
438 |
{ |
439 |
new_draw_info (NDI_UNIQUE, 0, op, "You gave an incorrect password too many times, you will now be dropped from the server."); |
440 |
LOG (llevInfo, "A player connecting from %s has been dropped for password failure\n", op->contr->socket->host); |
441 |
op->contr->socket->status = Ns_Dead; /* the socket loop should handle the rest for us */ |
442 |
} |
443 |
else |
444 |
get_name (op); |
445 |
|
446 |
return; /* Once again, rest of code just loads the char */ |
447 |
} |
448 |
|
449 |
#ifdef SAVE_INTERVAL |
450 |
pl->last_save_time = time (NULL); |
451 |
#endif /* SAVE_INTERVAL */ |
452 |
pl->party = NULL; |
453 |
if (settings.search_items == TRUE) |
454 |
pl->search_str[0] = '\0'; |
455 |
pl->name_changed = 1; |
456 |
pl->orig_stats.Str = 0; |
457 |
pl->orig_stats.Dex = 0; |
458 |
pl->orig_stats.Con = 0; |
459 |
pl->orig_stats.Int = 0; |
460 |
pl->orig_stats.Pow = 0; |
461 |
pl->orig_stats.Wis = 0; |
462 |
pl->orig_stats.Cha = 0; |
463 |
strcpy (pl->savebed_map, first_map_path); |
464 |
pl->bed_x = 0, pl->bed_y = 0; |
465 |
pl->spellparam[0] = '\0'; |
466 |
|
467 |
/* Loop through the file, loading the rest of the values */ |
468 |
while (fgets (bufall, MAX_BUF, thawer) != NULL) |
469 |
{ |
470 |
sscanf (bufall, "%s %d\n", buf, &value); |
471 |
if (!strcmp (buf, "endplst")) |
472 |
break; |
473 |
else if (!strcmp (buf, "oid")) |
474 |
thawer.get (pl, value); |
475 |
else if (!strcmp (buf, "title") && settings.set_title == TRUE) |
476 |
sscanf (bufall, "title %[^\n]", pl->own_title); |
477 |
else if (!strcmp (buf, "explore")) |
478 |
pl->explore = value; |
479 |
else if (!strcmp (buf, "gen_hp")) |
480 |
pl->gen_hp = value; |
481 |
else if (!strcmp (buf, "shoottype")) |
482 |
pl->shoottype = (rangetype) value; |
483 |
else if (!strcmp (buf, "bowtype")) |
484 |
pl->bowtype = (bowtype_t) value; |
485 |
else if (!strcmp (buf, "petmode")) |
486 |
pl->petmode = (petmode_t) value; |
487 |
else if (!strcmp (buf, "gen_sp")) |
488 |
pl->gen_sp = value; |
489 |
else if (!strcmp (buf, "gen_grace")) |
490 |
pl->gen_grace = value; |
491 |
else if (!strcmp (buf, "listening")) |
492 |
pl->listening = value; |
493 |
else if (!strcmp (buf, "peaceful")) |
494 |
pl->peaceful = value; |
495 |
else if (!strcmp (buf, "digestion")) |
496 |
pl->digestion = value; |
497 |
else if (!strcmp (buf, "pickup")) |
498 |
pl->mode = value; |
499 |
else if (!strcmp (buf, "outputs_sync")) |
500 |
pl->outputs_sync = value; |
501 |
else if (!strcmp (buf, "outputs_count")) |
502 |
pl->outputs_count = value; |
503 |
else if (!strcmp (buf, "map")) |
504 |
sscanf (bufall, "map %s", pl->maplevel); |
505 |
else if (!strcmp (buf, "savebed_map")) |
506 |
sscanf (bufall, "savebed_map %s", pl->savebed_map); |
507 |
else if (!strcmp (buf, "bed_x")) |
508 |
pl->bed_x = value; |
509 |
else if (!strcmp (buf, "bed_y")) |
510 |
pl->bed_y = value; |
511 |
else if (!strcmp (buf, "weapon_sp")) |
512 |
sscanf (buf, "weapon_sp %f", &pl->weapon_sp); |
513 |
else if (!strcmp (buf, "Str")) |
514 |
pl->orig_stats.Str = value; |
515 |
else if (!strcmp (buf, "Dex")) |
516 |
pl->orig_stats.Dex = value; |
517 |
else if (!strcmp (buf, "Con")) |
518 |
pl->orig_stats.Con = value; |
519 |
else if (!strcmp (buf, "Int")) |
520 |
pl->orig_stats.Int = value; |
521 |
else if (!strcmp (buf, "Pow")) |
522 |
pl->orig_stats.Pow = value; |
523 |
else if (!strcmp (buf, "Wis")) |
524 |
pl->orig_stats.Wis = value; |
525 |
else if (!strcmp (buf, "Cha")) |
526 |
pl->orig_stats.Cha = value; |
527 |
else if (!strcmp (buf, "usekeys")) |
528 |
{ |
529 |
if (!strcmp (bufall + 8, "key_inventory\n")) |
530 |
pl->usekeys = key_inventory; |
531 |
else if (!strcmp (bufall + 8, "keyrings\n")) |
532 |
pl->usekeys = keyrings; |
533 |
else if (!strcmp (bufall + 8, "containers\n")) |
534 |
pl->usekeys = containers; |
535 |
else |
536 |
LOG (llevDebug, "load_player: got unknown usekeys type: %s\n", bufall + 8); |
537 |
} |
538 |
else if (!strcmp (buf, "unapply")) |
539 |
{ |
540 |
if (!strcmp (bufall + 8, "unapply_nochoice\n")) |
541 |
pl->unapply = unapply_nochoice; |
542 |
else if (!strcmp (bufall + 8, "unapply_never\n")) |
543 |
pl->unapply = unapply_never; |
544 |
else if (!strcmp (bufall + 8, "unapply_always\n")) |
545 |
pl->unapply = unapply_always; |
546 |
else |
547 |
LOG (llevDebug, "load_player: got unknown unapply type: %s\n", bufall + 8); |
548 |
} |
549 |
else if (!strcmp (buf, "lev_array")) |
550 |
{ |
551 |
for (i = 1; i <= value; i++) |
552 |
{ |
553 |
char line[128]; |
554 |
|
555 |
fgets (line, 128, thawer); |
556 |
pl->levhp[i] = atoi (line); |
557 |
fgets (line, 128, thawer); |
558 |
pl->levsp[i] = atoi (line); |
559 |
fgets (line, 128, thawer); |
560 |
pl->levgrace[i] = atoi (line); |
561 |
} |
562 |
/* spell_array code removed - don't know when that was last used. |
563 |
* Even the load code below will someday be replaced by spells being |
564 |
* objects. |
565 |
*/ |
566 |
} |
567 |
else if (!strcmp (buf, "known_spell")) |
568 |
{ |
569 |
#if 0 |
570 |
/* Logic is left here in case someone wants to try |
571 |
* and write code to update to spell objects. |
572 |
*/ |
573 |
char *cp = strchr (bufall, '\n'); |
574 |
|
575 |
*cp = '\0'; |
576 |
cp = strchr (bufall, ' '); |
577 |
cp++; |
578 |
for (i = 0; i < NROFREALSPELLS; i++) |
579 |
if (!strcmp (spells[i].name, cp)) |
580 |
{ |
581 |
pl->known_spells[pl->nrofknownspells++] = i; |
582 |
break; |
583 |
} |
584 |
if (i == NROFREALSPELLS) |
585 |
LOG (llevDebug, "Error: unknown spell (%s)\n", cp); |
586 |
#endif |
587 |
} |
588 |
} /* End of loop loading the character file */ |
589 |
|
590 |
leave_map (op); |
591 |
op->speed = 0; |
592 |
update_ob_speed (op); |
593 |
|
594 |
op->clear (); |
595 |
|
596 |
op->contr = pl; |
597 |
pl->ob = op; |
598 |
|
599 |
/* this loads the standard objects values. */ |
600 |
load_object (thawer, op, 0); |
601 |
|
602 |
CLEAR_FLAG (op, FLAG_NO_FIX_PLAYER); |
603 |
|
604 |
assign (pl->title, op->arch->clone.name); |
605 |
|
606 |
/* If the map where the person was last saved does not exist, |
607 |
* restart them on their home-savebed. This is good for when |
608 |
* maps change between versions |
609 |
* First, we check for partial path, then check to see if the full |
610 |
* path (for unique player maps) |
611 |
*/ |
612 |
if (check_path (pl->maplevel, 1) == -1) |
613 |
{ |
614 |
if (check_path (pl->maplevel, 0) == -1) |
615 |
{ |
616 |
strcpy (pl->maplevel, pl->savebed_map); |
617 |
op->x = pl->bed_x, op->y = pl->bed_y; |
618 |
} |
619 |
} |
620 |
|
621 |
/* make sure he's a player--needed because of class change. */ |
622 |
op->type = PLAYER; |
623 |
|
624 |
enter_exit (op, NULL); |
625 |
|
626 |
pl->name_changed = 1; |
627 |
pl->state = ST_PLAYING; |
628 |
#ifdef AUTOSAVE |
629 |
pl->last_save_tick = pticks; |
630 |
#endif |
631 |
op->carrying = sum_weight (op); |
632 |
/* Need to call fix_player now - program modified so that it is not |
633 |
* called during the load process (FLAG_NO_FIX_PLAYER set when |
634 |
* saved) |
635 |
* Moved ahead of the esrv functions, so proper weights will be |
636 |
* sent to the client. |
637 |
*/ |
638 |
link_player_skills (op); |
639 |
|
640 |
if (!legal_range (op, op->contr->shoottype)) |
641 |
op->contr->shoottype = range_none; |
642 |
|
643 |
fix_player (op); |
644 |
|
645 |
/* if it's a dragon player, set the correct title here */ |
646 |
if (is_dragon_pl (op) && op->inv != NULL) |
647 |
{ |
648 |
object *tmp, *abil = NULL, *skin = NULL; |
649 |
|
650 |
for (tmp = op->inv; tmp != NULL; tmp = tmp->below) |
651 |
{ |
652 |
if (tmp->type == FORCE) |
653 |
{ |
654 |
if (strcmp (tmp->arch->name, "dragon_ability_force") == 0) |
655 |
abil = tmp; |
656 |
else if (strcmp (tmp->arch->name, "dragon_skin_force") == 0) |
657 |
skin = tmp; |
658 |
} |
659 |
} |
660 |
set_dragon_name (op, abil, skin); |
661 |
} |
662 |
|
663 |
new_draw_info (NDI_UNIQUE, 0, op, "Welcome Back!"); |
664 |
new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s has entered the game.", &pl->ob->name); |
665 |
|
666 |
INVOKE_PLAYER (LOAD, pl, ARG_STRING (filename)); |
667 |
INVOKE_PLAYER (LOGIN, pl); |
668 |
|
669 |
op->contr->socket->update_look = 1; |
670 |
|
671 |
/* If the player should be dead, call kill_player for them |
672 |
* Only check for hp - if player lacks food, let the normal |
673 |
* logic for that to take place. If player is permanently |
674 |
* dead, and not using permadeath mode, the kill_player will |
675 |
* set the play_again flag, so return. |
676 |
*/ |
677 |
if (op->stats.hp < 0) |
678 |
{ |
679 |
new_draw_info (NDI_UNIQUE, 0, op, "Your character was dead last your played."); |
680 |
kill_player (op); |
681 |
if (pl->state != ST_PLAYING) |
682 |
return; |
683 |
} |
684 |
|
685 |
LOG (llevInfo, "LOGIN: Player named %s from ip %s\n", &op->name, op->contr->socket->host); |
686 |
|
687 |
/* Do this after checking for death - no reason sucking up bandwidth if |
688 |
* the data isn't needed. |
689 |
*/ |
690 |
esrv_new_player (op->contr, op->weight + op->carrying); |
691 |
esrv_send_inventory (op, op); |
692 |
esrv_add_spells (op->contr, NULL); |
693 |
|
694 |
CLEAR_FLAG (op, FLAG_FRIENDLY); |
695 |
|
696 |
/* can_use_shield is a new flag. However, the can_use.. seems to largely come |
697 |
* from the class, and not race. I don't see any way to get the class information |
698 |
* to then update this. I don't think this will actually break anything - anyone |
699 |
* that can use armour should be able to use a shield. What this may 'break' |
700 |
* are features new characters get, eg, if someone starts up with a Q, they |
701 |
* should be able to use a shield. However, old Q's won't get that advantage. |
702 |
*/ |
703 |
if (QUERY_FLAG (op, FLAG_USE_ARMOUR)) |
704 |
SET_FLAG (op, FLAG_USE_SHIELD); |
705 |
|
706 |
return; |
707 |
} |