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