1 /* $Id$ */
2 /*
3 * Support for the "party" system.
4 */
5
6 /* added this for consistency in some (unrelated) header-inclusion,
7 it IS a server file, isn't it? */
8 #define SERVER
9
10 #include "angband.h"
11 #include "party.h"
12
13 #ifdef TOMENET_WORLDS
14 #include "../world/world.h"
15 #endif
16
17
18 /*
19 * Give some exp-bonus to encourage partying (aka "C.Blue party bonus") [2]
20 * formula: (PARTY_XP_BOOST+1)/(PARTY_XP_BOOST + (# of applicable players))
21 */
22 #define PARTY_XP_BOOST (cfg.party_xp_boost)
23 #define IDDC_PARTY_XP_BOOST 8
24 /* [7..10] 10-> 2:92%, 3:85%, 4:79%, 5:73%, 6:69%; 8-> 2:90%, 3:82%, 4:75%, 5:69%, 6:64%
25 8 is maybe best, assuming that of a larger group one or two will quickly die anyway xD */
26
27 /* Keep these ANTI_MAXPLV_EXPLOIT.. defines consistent with cmd4.c:do_write_others_attributes()! */
28 /* prevent exploit strategies */
29 #define ANTI_MAXPLV_EXPLOIT /* prevent exploiting by having a powerful char losing levels deliberately to get in range with lowbies to boost */
30 // #define ANTI_MAXPLV_EXPLOIT_SOFTLEV /* be somewhat less strict (average between max_plv and current max_lev) */
31 #define ANTI_MAXPLV_EXPLOIT_SOFTEXP /* be somewhat less strict (use reduced exp instead of preventing any exp) */
32
33 /* Check for illegal party/guild name that could be abused to catch special chat commands */
34 //!(((N)[0] >= 'A' && (N)[0] <= 'Z') || ((N)[0] >= 'a' && (N)[0] <= 'z') || ((N)[0] >= '0' && (N)[0] <= '9'))
35 #define ILLEGAL_GROUP_NAME(N) \
36 ((N)[0] == 0 || (N)[0] == ' ' || \
37 streq(N, "Neutral") || \
38 streq(N, "!") || streq(N, "#") || streq(N, "%") || streq(N, "$") || streq(N, "-"))
39
40
41 #ifdef HAVE_CRYPT
42 #include <unistd.h>
43 #endif // HAVE_CRYPT
44
45
46 static char *t_crypt(char *inbuf, cptr salt);
47 static void del_party(int id);
48 static u32b new_accid(void);
49
50 /* The hash table itself */
51 hash_entry *hash_table[NUM_HASH_ENTRIES];
52
53
54 /* admin only - account edit function */
WriteAccount(struct account * r_acc,bool new)55 bool WriteAccount(struct account *r_acc, bool new) {
56 FILE *fp;
57 short found = 0;
58 struct account c_acc;
59 long delpos = -1L;
60 char buf[1024];
61 size_t retval;
62
63 path_build(buf, 1024, ANGBAND_DIR_SAVE, "tomenet.acc");
64 fp = fopen(buf, "rb+");
65
66 if (!fp) {
67 /* Attempt to create a new file */
68 fp = fopen(buf, "wb+");
69 }
70
71 if (!fp) {
72 s_printf("Could not open tomenet.acc file! (errno = %d)\n", errno);
73 } else {
74 while (!feof(fp) && !found) {
75 retval = fread(&c_acc, sizeof(struct account), 1, fp);
76 if (retval == 0) break; /* EOF reached, nothing read into c_acc - mikaelh */
77 if (c_acc.flags & ACC_DELD) {
78 if (delpos == -1L) delpos = (ftell(fp) - sizeof(struct account));
79 if (new) break;
80 continue;
81 }
82 if (!strcmp(c_acc.name, r_acc->name)) found = 1;
83 }
84 if (found) {
85 fseek(fp, -sizeof(struct account), SEEK_CUR);
86 if (fwrite(r_acc, sizeof(struct account), 1, fp) < 1) {
87 s_printf("Writing to account file failed: %s\n", feof(fp) ? "EOF" : strerror(ferror(fp)));
88 }
89 }
90 if (new) {
91 if (delpos != -1L) {
92 fseek(fp, delpos, SEEK_SET);
93 } else {
94 fseek(fp, 0L, SEEK_END);
95 }
96 if (fwrite(r_acc, sizeof(struct account), 1, fp) < 1) {
97 s_printf("Writing to account file failed: %s\n", feof(fp) ? "EOF" : strerror(ferror(fp)));
98 }
99 found = 1;
100 }
101 }
102 memset(c_acc.pass, 0, sizeof(c_acc.pass));
103 fclose(fp);
104 return(found);
105 }
106
107 /*
108 Get an existing account and set default valid flags on it
109 That will be SCORE on only (hack it for MULTI)
110 Modified to return TRUE on success and FALSE if account can't
111 be found - mikaelh
112 Makes the player valid now too - mikaelh
113 Modified to return 0 if not found, 1 if found but already 100% validated,
114 and -1 if found and there was still something invalid about it - C. Blue
115 */
validate(char * name)116 int validate(char *name) {
117 struct account *c_acc;
118 int i;
119 bool effect = FALSE;
120
121 c_acc = GetAccount(name, NULL, TRUE);
122 if (!c_acc) return(0);
123
124 if (c_acc->flags & ACC_TRIAL) {
125 effect = TRUE;
126 c_acc->flags &= ~(ACC_TRIAL | ACC_NOSCORE);
127 }
128 WriteAccount(c_acc, FALSE);
129 memset(c_acc->pass, 0, sizeof(c_acc->pass));
130 for (i = 1; i <= NumPlayers; i++) {
131 if (Players[i]->account == c_acc->id) {
132 if (Players[i]->inval) effect = TRUE;
133 Players[i]->inval = 0;
134 }
135 }
136
137 KILL(c_acc, struct account);
138 if (effect) return(-1);
139 return(1);
140 }
141
142 /* invalidate - opposite to validate() */
invalidate(char * name,bool admin)143 int invalidate(char *name, bool admin) {
144 struct account *c_acc;
145 int i;
146 bool effect = FALSE;
147
148 c_acc = GetAccount(name, NULL, TRUE);
149 if (!c_acc) return(0);
150
151 if (!admin && (c_acc->flags & ACC_ADMIN)) {
152 KILL(c_acc, struct account);
153 return 2;
154 }
155
156 if (!(c_acc->flags & ACC_TRIAL)) {
157 effect = TRUE;
158 c_acc->flags |= (ACC_TRIAL | ACC_NOSCORE);
159 }
160 WriteAccount(c_acc, FALSE);
161 memset(c_acc->pass, 0, sizeof(c_acc->pass));
162 for (i = 1; i <= NumPlayers; i++) {
163 if (Players[i]->account == c_acc->id) {
164 if (!Players[i]->inval) effect = TRUE;
165 Players[i]->inval = 1;
166 }
167 }
168
169 KILL(c_acc, struct account);
170 if (effect) return(-1);
171 return(1);
172 }
173
makeadmin(char * name)174 int makeadmin(char *name) {
175 struct account *c_acc;
176 int i;
177 c_acc = GetAccount(name, NULL, TRUE);
178 if (!c_acc) return(FALSE);
179 c_acc->flags &= ~(ACC_TRIAL);
180 c_acc->flags |= (ACC_ADMIN | ACC_NOSCORE);
181 WriteAccount(c_acc, FALSE);
182 memset(c_acc->pass, 0, sizeof(c_acc->pass));
183 for (i = 1; i <= NumPlayers; i++) {
184 if (Players[i]->account == c_acc->id) {
185 Players[i]->inval = 0;
186 if (!strcmp(name, Players[i]->name))
187 Players[i]->admin_dm = 1;
188 else
189 Players[i]->admin_wiz = 1;
190 }
191 }
192 KILL(c_acc, struct account);
193 return(TRUE);
194 }
195
196 /* set or clear account flags */
acc_set_flags(char * name,u32b flags,bool set)197 int acc_set_flags(char *name, u32b flags, bool set) {
198 struct account *c_acc;
199
200 c_acc = GetAccount(name, NULL, TRUE);
201 if (!c_acc) return(0);
202
203 if (set) c_acc->flags |= (flags);
204 else c_acc->flags &= ~(flags);
205
206 WriteAccount(c_acc, FALSE);
207 memset(c_acc->pass, 0, sizeof(c_acc->pass));
208
209 KILL(c_acc, struct account);
210 return(1);
211 }
212
213 /* get account flags */
acc_get_flags(char * name)214 u32b acc_get_flags(char *name) {
215 struct account *c_acc;
216 u32b flags;
217
218 c_acc = GetAccount(name, NULL, FALSE);
219 if (!c_acc) return(0);
220
221 flags = c_acc->flags;
222 KILL(c_acc, struct account);
223 return flags;
224 }
225
226 /* set or clear account flags */
acc_set_flags_id(u32b id,u32b flags,bool set)227 int acc_set_flags_id(u32b id, u32b flags, bool set) {
228 struct account *c_acc;
229 char acc_name[MAX_CHARS];
230
231 acc_name[0] = 0;
232 strcpy(acc_name, lookup_accountname(id));
233
234 if (acc_name[0] == 0) return(0);
235 c_acc = GetAccount(acc_name, NULL, TRUE);
236 if (!c_acc) return(0);
237
238 if (set) c_acc->flags |= (flags);
239 else c_acc->flags &= ~(flags);
240
241 WriteAccount(c_acc, FALSE);
242 memset(c_acc->pass, 0, sizeof(c_acc->pass));
243
244 KILL(c_acc, struct account);
245 return(1);
246 }
247
248 /* set account guild info */
acc_set_guild(char * name,s32b id)249 int acc_set_guild(char *name, s32b id) {
250 struct account *c_acc;
251
252 c_acc = GetAccount(name, NULL, TRUE);
253 if (!c_acc) return(0);
254
255 c_acc->guild_id = id;
256
257 WriteAccount(c_acc, FALSE);
258 memset(c_acc->pass, 0, sizeof(c_acc->pass));
259
260 KILL(c_acc, struct account);
261 return(1);
262 }
acc_set_guild_dna(char * name,u32b dna)263 int acc_set_guild_dna(char *name, u32b dna) {
264 struct account *c_acc;
265
266 c_acc = GetAccount(name, NULL, TRUE);
267 if (!c_acc) return(0);
268
269 c_acc->guild_dna = dna;
270
271 WriteAccount(c_acc, FALSE);
272 memset(c_acc->pass, 0, sizeof(c_acc->pass));
273
274 KILL(c_acc, struct account);
275 return(1);
276 }
277
278 /* get account guild info */
acc_get_guild(char * name)279 s32b acc_get_guild(char *name) {
280 struct account *c_acc;
281 s32b guild_id;
282
283 c_acc = GetAccount(name, NULL, FALSE);
284 if (!c_acc) return(0);
285
286 guild_id = c_acc->guild_id;
287 KILL(c_acc, struct account);
288 return guild_id;
289 }
acc_get_guild_dna(char * name)290 u32b acc_get_guild_dna(char *name) {
291 struct account *c_acc;
292 u32b dna;
293
294 c_acc = GetAccount(name, NULL, FALSE);
295 if (!c_acc) return(0);
296
297 dna = c_acc->guild_dna;
298 KILL(c_acc, struct account);
299 return dna;
300 }
301
acc_set_deed_event(char * name,char deed_sval)302 int acc_set_deed_event(char *name, char deed_sval) {
303 struct account *c_acc;
304
305 c_acc = GetAccount(name, NULL, TRUE);
306 if (!c_acc) return(0);
307
308 c_acc->deed_event = deed_sval;
309
310 WriteAccount(c_acc, FALSE);
311 memset(c_acc->pass, 0, sizeof(c_acc->pass));
312
313 KILL(c_acc, struct account);
314 return(1);
315 }
acc_get_deed_event(char * name)316 char acc_get_deed_event(char *name) {
317 struct account *c_acc;
318 char deed_sval;
319
320 c_acc = GetAccount(name, NULL, FALSE);
321 if (!c_acc) return(0);
322
323 deed_sval = c_acc->deed_event;
324 KILL(c_acc, struct account);
325 return deed_sval;
326 }
acc_set_deed_achievement(char * name,char deed_sval)327 int acc_set_deed_achievement(char *name, char deed_sval) {
328 struct account *c_acc;
329
330 c_acc = GetAccount(name, NULL, TRUE);
331 if (!c_acc) return(0);
332
333 c_acc->deed_achievement = deed_sval;
334
335 WriteAccount(c_acc, FALSE);
336 memset(c_acc->pass, 0, sizeof(c_acc->pass));
337
338 KILL(c_acc, struct account);
339 return(1);
340 }
acc_get_deed_achievement(char * name)341 char acc_get_deed_achievement(char *name) {
342 struct account *c_acc;
343 char deed_sval;
344
345 c_acc = GetAccount(name, NULL, FALSE);
346 if (!c_acc) return(0);
347
348 deed_sval = c_acc->deed_achievement;
349 KILL(c_acc, struct account);
350 return deed_sval;
351 }
352
353 /*
354 return player account information (by name)
355 */
356 //void accinfo(char *name){
357 //}
358
359 /* most account type stuff was already in here.
360 a separate file should probably be made in
361 order to split party/guild from account
362 and database handling */
363 /* Note. Accounts will be deleted when empty
364 They will not be subject to their own 90
365 days timeout, but will be removed upon
366 the removal of the last character. */
GetAccount(cptr name,char * pass,bool leavepass)367 struct account *GetAccount(cptr name, char *pass, bool leavepass) {
368 FILE *fp;
369 char buf[1024];
370 struct account *c_acc;
371
372 MAKE(c_acc, struct account);
373 if (!c_acc) return(NULL);
374
375 path_build(buf, 1024, ANGBAND_DIR_SAVE, "tomenet.acc");
376 fp = fopen(buf, "rb+");
377 if (!fp) {
378 if (errno == ENOENT) { /* ONLY if non-existent */
379 fp = fopen(buf, "wb+");
380 if (!fp) {
381 KILL(c_acc, struct account);
382 return(NULL);
383 }
384 s_printf("Generated new account file\n");
385 }
386 else {
387 KILL(c_acc, struct account);
388 return(NULL); /* failed */
389 }
390 }
391 while (fread(c_acc, sizeof(struct account), 1, fp)) {
392 if (c_acc->flags & ACC_DELD) continue;
393 if (!strcmp(c_acc->name, name)) {
394 int val;
395 if (pass == NULL) { /* direct name lookup */
396 val = 0;
397 } else {
398 val = strcmp(c_acc->pass, t_crypt(pass, name));
399
400 /* Update the timestamp if the password is successfully verified - mikaelh */
401 if (val == 0) {
402 c_acc->acc_laston_real = c_acc->acc_laston = time(NULL);
403 fseek(fp, -sizeof(struct account), SEEK_CUR);
404 if (fwrite(c_acc, sizeof(struct account), 1, fp) < 1) {
405 s_printf("Writing to account file failed: %s\n", feof(fp) ? "EOF" : strerror(ferror(fp)));
406 }
407 }
408 }
409 if (!leavepass || pass != NULL) {
410 memset(c_acc->pass, 0, sizeof(c_acc->pass));
411 }
412 if (val != 0) {
413 fclose(fp);
414 KILL(c_acc, struct account);
415 return(NULL);
416 } else {
417 fclose(fp);
418 return(c_acc);
419 }
420 }
421 }
422 /* New accounts always have pass */
423 if (!pass) {
424 KILL(c_acc, struct account);
425 fclose(fp);
426 return(NULL);
427 }
428
429 /* No account found. Create trial account */
430 WIPE(c_acc, struct account);
431 c_acc->id = new_accid();
432 if (c_acc->id != 0L) {
433 if (c_acc->id == 1)
434 c_acc->flags = (ACC_ADMIN | ACC_NOSCORE);
435 else
436 c_acc->flags = (ACC_TRIAL | ACC_NOSCORE);
437
438 strncpy(c_acc->name, name, 29);
439 c_acc->name[29] = '\0';
440
441 condense_name(buf, c_acc->name);
442 strncpy(c_acc->name_normalised, buf, 29);
443 c_acc->name_normalised[29] = '\0';
444
445 strcpy(c_acc->pass, t_crypt(pass, name));
446 if (!(WriteAccount(c_acc, TRUE))) {
447 KILL(c_acc, struct account);
448 fclose(fp);
449 return(NULL);
450 }
451 }
452 memset(c_acc->pass, 0, sizeof(c_acc->pass));
453 fclose(fp);
454 if(c_acc->id) {
455 return(c_acc);
456 }
457 KILL(c_acc, struct account);
458 return(NULL);
459 }
460
461 /* Return account structure of a specified account name */
Admin_GetAccount(cptr name)462 struct account *Admin_GetAccount(cptr name) {
463 FILE *fp;
464 char buf[1024];
465 struct account *c_acc;
466
467 MAKE(c_acc, struct account);
468 if (!c_acc) return(NULL);
469
470 path_build(buf, 1024, ANGBAND_DIR_SAVE, "tomenet.acc");
471 fp = fopen(buf, "rb");
472 if (!fp) {
473 KILL(c_acc, struct account);
474 return(NULL); /* cannot access account file */
475 }
476 while (fread(c_acc, sizeof(struct account), 1, fp)) {
477 if (c_acc->flags & ACC_DELD) continue;
478 if (!strcmp(c_acc->name, name)) {
479 fclose(fp);
480 return(c_acc);
481 }
482 }
483 fclose(fp);
484 KILL(c_acc, struct account);
485 return(NULL);
486 }
487
488 /* Check for an account of similar name to 'name'. If one is found, the name
489 will be forbiden to be used, except if 'accname' is identical to the found
490 account's name:
491 If checking for an account name, accname must be NULL.
492 If checking for a character name, accname must be set to its account holder.
493 - C. Blue */
494 /* Super-strict mode? Disallow (non-trivial) char/acc names that only have 1+
495 _letter_ inserted somewhere compared to existing account names */
496 #define STRICT_SIMILAR_NAMES
497 /* Only apply super-strict check above to account names being created,
498 let character names be created without this extra check. */
499 //#define SIMILAR_CHARNAMES_OK
lookup_similar_account(cptr name,cptr accname)500 bool lookup_similar_account(cptr name, cptr accname) {
501 FILE *fp;
502 char buf[1024], tmpname[ACCOUNTNAME_LEN > CHARACTERNAME_LEN ? ACCOUNTNAME_LEN : CHARACTERNAME_LEN];
503 const char *ptr, *ptr2;
504 struct account *c_acc;
505 int diff, min;
506
507 MAKE(c_acc, struct account);
508 /* ew, cannot reserve memory! we abuse the return value to cause an
509 'invalid account name' style error on purpose in this case, for paranoia */
510 if (!c_acc) {
511 s_printf("ERROR: COULDN'T ALLOCATE MEMORY IN lookup_similar_account()!\n");
512 return TRUE;
513 }
514
515 condense_name(tmpname, name);
516
517 path_build(buf, 1024, ANGBAND_DIR_SAVE, "tomenet.acc");
518 fp = fopen(buf, "rb");
519 if (!fp) {
520 KILL(c_acc, struct account);
521 s_printf("ERROR: COULDN'T ACCESS ACCOUNT FILE IN lookup_similar_account()!\n");
522 return FALSE; /* cannot access account file */
523 }
524 while (fread(c_acc, sizeof(struct account), 1, fp)) {
525 if (c_acc->flags & ACC_DELD) continue;
526
527 /* We may create character names similar to our own account name as we like */
528 if (accname && !strcmp(c_acc->name, accname)) {
529 continue;
530 }
531
532 #ifdef STRICT_SIMILAR_NAMES
533 /* Super-strict mode? Disallow (non-trivial) names that only have 1+ letter inserted somewhere */
534 if (
535 #ifdef SIMILAR_CHARNAMES_OK
536 /*only apply this check to account names being created, be lenient for character names */
537 !accname &&
538 #endif
539 strlen(name) >= 5 && strlen(c_acc->name) >= 5) { //non-trivial length
540 /* don't exaggerate */
541 if (strlen(name) > strlen(c_acc->name)) min = strlen(c_acc->name);
542 else min = strlen(name);
543
544 /* '->' */
545 diff = 0;
546 ptr2 = name;
547 for (ptr = c_acc->name; *ptr && *ptr2; ) {
548 if (tolower(*ptr) != tolower(*ptr2)) diff++;
549 else ptr++;
550 ptr2++;
551 }
552 //all remaining characters that couldn't be matched are also "different"
553 while (*ptr++) diff++;
554 while (*ptr2++) diff++;
555 //too little difference between account name and this character name? forbidden!
556 if (diff <= (min - 5) / 2 + 1) {
557 s_printf("lookup_similar_account (1): name '%s', aname '%s' (tmp '%s')\n", name, c_acc->name, tmpname);
558 KILL(c_acc, struct account);
559 return TRUE;
560 }
561
562 /* '<-' */
563 diff = 0;
564 ptr2 = c_acc->name;
565 for (ptr = name; *ptr && *ptr2; ) {
566 if (tolower(*ptr) != tolower(*ptr2)) diff++;
567 else ptr++;
568 ptr2++;
569 }
570 //all remaining characters that couldn't be matched are also "different"
571 while (*ptr++) diff++;
572 while (*ptr2++) diff++;
573 //too little difference between account name and this character name? forbidden!
574 if (diff <= (min - 5) / 2 + 1) {
575 s_printf("lookup_similar_account (2): name '%s', aname '%s' (tmp '%s')\n", name, c_acc->name, tmpname);
576 KILL(c_acc, struct account);
577 return TRUE;
578 }
579
580 /* '=' -- note: weakness here is, the first 2 methods don't combine with this one ;).
581 So the checks could be tricked by combining one 'replaced char' with one 'too many char'
582 to circumvent the limitations for longer names that usually would require a 3+ difference.. =P */
583 diff = 0;
584 ptr2 = c_acc->name;
585 for (ptr = name; *ptr && *ptr2; ) {
586 if (tolower(*ptr) != tolower(*ptr2)) diff++;
587 ptr++;
588 ptr2++;
589 }
590 //all remaining characters that couldn't be matched are also "different"
591 while (*ptr++) diff++;
592 while (*ptr2++) diff++;
593 //too little difference between account name and this character name? forbidden!
594 if (diff <= (min - 5) / 2 + 1) {
595 s_printf("lookup_similar_account (3): name '%s', aname '%s' (tmp '%s')\n", name, c_acc->name, tmpname);
596 KILL(c_acc, struct account);
597 return TRUE;
598 }
599 }
600 #endif
601
602 /* Differring normalised names? Skip. */
603 if (strcmp(c_acc->name_normalised, tmpname)) continue;
604
605 /* We found same normalised names. Check! */
606 fclose(fp);
607
608 /* Identical name (account vs character)? that's fine. */
609 if (!strcmp(c_acc->name, name)) {
610 KILL(c_acc, struct account);
611 return FALSE;
612 }
613
614 /* not identical but just too similar? forbidden! */
615 s_printf("lookup_similar_account (4): name '%s', aname '%s' (tmp '%s')\n", name, c_acc->name, tmpname);
616 KILL(c_acc, struct account);
617 return TRUE;
618 }
619 fclose(fp);
620
621 /* no identical/similar account found, all green! */
622 KILL(c_acc, struct account);
623 return FALSE;
624 }
625
626 /* Return account name of a specified PLAYER id */
lookup_accountname(int p_id)627 cptr lookup_accountname(int p_id) {
628 FILE *fp;
629 char buf[1024];
630 static struct account c_acc;
631 u32b acc_id = lookup_player_account(p_id);
632
633 path_build(buf, 1024, ANGBAND_DIR_SAVE, "tomenet.acc");
634 fp = fopen(buf, "rb");
635 if (!fp) return(NULL); /* cannot access account file */
636 while (fread(&c_acc, sizeof(struct account), 1, fp)) {
637 if (c_acc.flags & ACC_DELD) continue;
638 if (c_acc.id == acc_id) {
639 fclose(fp);
640 memset(c_acc.pass, 0, sizeof(c_acc.pass));
641 return(c_acc.name);
642 }
643 }
644 memset(c_acc.pass, 0, sizeof(c_acc.pass));
645 fclose(fp);
646 return(NULL);
647 }
648
649 /* Return account name of a specified account id.
650 Does not return NULL but "" if account doesn't exist! */
lookup_accountname2(u32b acc_id)651 cptr lookup_accountname2(u32b acc_id) {
652 FILE *fp;
653 char buf[1024];
654 static struct account c_acc;
655
656 path_build(buf, 1024, ANGBAND_DIR_SAVE, "tomenet.acc");
657 fp = fopen(buf, "rb");
658 if (!fp) return(""); /* cannot access account file */
659 while (fread(&c_acc, sizeof(struct account), 1, fp)) {
660 if (c_acc.flags & ACC_DELD) continue;
661 if (c_acc.id == acc_id) {
662 fclose(fp);
663 memset(c_acc.pass, 0, sizeof(c_acc.pass));
664 return(c_acc.name);
665 }
666 }
667 memset(c_acc.pass, 0, sizeof(c_acc.pass));
668 fclose(fp);
669 return("");
670 }
671
672 /* our password encryptor */
t_crypt(char * inbuf,cptr salt)673 static char *t_crypt(char *inbuf, cptr salt) {
674 #ifdef HAVE_CRYPT
675 static char out[64];
676 #if 1 /* fix for len-1 names */
677 char setting[3];
678 /* only 1 character long salt? expand to 2 chars length */
679 if (!salt[1]) {
680 setting[0] = '.';
681 setting[1] = salt[0];
682 setting[2] = 0;
683 strcpy(out, (char*)crypt(inbuf, setting));
684 } else
685 #endif
686 #if 1 /* SPACE _ ! - ' , and probably more as _2nd character_ cause crypt() to return a null pointer ('.' is ok) */
687 if (!((salt[1] >= 'A' && salt[1] <= 'Z') ||
688 (salt[1] >= 'a' && salt[1] <= 'z') ||
689 (salt[1] >= '0' && salt[1] <= '9') ||
690 salt[1] == '.')) {
691 char fixed_name[ACCOUNTNAME_LEN];
692 strcpy(fixed_name, salt);
693 fixed_name[1] = '.';
694 strcpy(out, (char*)crypt(inbuf, fixed_name));
695 } else
696 #endif
697 strcpy(out, (char*)crypt(inbuf, salt));
698 return(out);
699 #else
700 return(inbuf);
701 #endif
702 }
703
check_account(char * accname,char * c_name)704 int check_account(char *accname, char *c_name) {
705 struct account *l_acc;
706 u32b id, a_id;
707 u32b flags;
708 hash_entry *ptr;
709 int i, success = 1;
710 #ifndef RPG_SERVER
711 int ded_iddc, ded_pvp;
712 #endif
713
714 /* Make sure noone creates a character of the same name as another player's accountname!
715 This is important for new feat of messaging to an account instead of character name. - C. Blue */
716 struct account *l2_acc;
717 char c2_name[MAX_CHARS];
718 strcpy(c2_name, c_name);
719 // c2_name[0] = toupper(c2_name[0]);
720 l_acc = GetAccount(accname, NULL, FALSE);
721 l2_acc = GetAccount(c2_name, NULL, FALSE);
722 if (l_acc && l2_acc && l_acc->id != l2_acc->id) {
723 /* However, since ppl might have already created such characters, only apply this
724 rule for newly created characters, to avoid someone being unable to login on
725 an already existing character that unfortunately violates this rule :/ */
726 int *id_list, chars;
727 chars = player_id_list(&id_list, l_acc->id);
728 for (i = 0; i < chars; i++)
729 if (!strcmp(c_name, lookup_player_name(id_list[i]))) break;
730 if (chars) C_KILL(id_list, chars, int);
731 if (i == chars) {
732 if (l_acc) KILL(l_acc, struct account);
733 if (l2_acc) KILL(l2_acc, struct account);
734 return 0; /* 'name already in use' */
735 }
736 } else if (!l_acc && l2_acc) {
737 KILL(l2_acc, struct account);
738 return 0; /* we don't even have an account yet? 'name already in use' for sure */
739 }
740 if (l_acc) KILL(l_acc, struct account);
741 if (l2_acc) KILL(l2_acc, struct account);
742
743 if ((l_acc = GetAccount(accname, NULL, FALSE))) {
744 int *id_list, chars;
745 #ifndef RPG_SERVER
746 int max_cpa = MAX_CHARS_PER_ACCOUNT, max_cpa_plus = 0, plus_free = 0;
747 #endif
748 chars = player_id_list(&id_list, l_acc->id);
749 #ifdef RPG_SERVER /* Allow only up to 1 character per account! */
750 /* If this account DOES have characters, but the chosen character name is
751 NOT equal to the first character of this account, don't allow it!
752 (To restrict players to only 1 character per account! - C. Blue) */
753 /* allow multiple chars for admins, even on RPG server */
754 if ((chars > 0) && strcmp(c_name, lookup_player_name(id_list[0])) && !(l_acc->flags & ACC_ADMIN)) {
755 C_KILL(id_list, chars, int);
756 KILL(l_acc, struct account);
757 return(-1);
758 }
759 #else
760 #ifdef ALLOW_DED_IDDC_MODE
761 max_cpa_plus += MAX_DED_IDDC_CHARS;
762 plus_free += MAX_DED_IDDC_CHARS;
763 #endif
764 #ifdef ALLOW_DED_PVP_MODE
765 max_cpa_plus += MAX_DED_PVP_CHARS;
766 plus_free += MAX_DED_PVP_CHARS;
767 #endif
768
769 ded_iddc = 0;
770 ded_pvp = 0;
771 for (i = 0; i < chars; i++) {
772 int m = lookup_player_mode(id_list[i]);
773 if ((m & MODE_DED_IDDC) && ded_iddc < MAX_DED_IDDC_CHARS) {
774 ded_iddc++;
775 plus_free--;
776 }
777 if ((m & MODE_DED_PVP) && ded_pvp < MAX_DED_PVP_CHARS) {
778 ded_pvp++;
779 plus_free--;
780 }
781 /* paranoia (server-client version/type mismatch might cause this in the future) */
782 if (plus_free < 0) {
783 s_printf("debug error: plus_free is %d\n", plus_free);
784 plus_free = 0;
785 }
786 }
787 //s_printf("plus_free=%d, ded_iddc=%d, ded_pvp=%d\n", plus_free, ded_iddc, ded_pvp);
788 //s_printf("chars=%d, max_cpa=%d, max_cpa_plus=%d\n", chars, max_cpa, max_cpa_plus);
789
790 /* no more free chars */
791 if (chars >= max_cpa + max_cpa_plus) {
792 for (i = 0; i < chars; i++)
793 if (!strcmp(c_name, lookup_player_name(id_list[i]))) break;
794 /* We're out of free character slots: Char creation failed! */
795 if (i == chars) {
796 if (chars) C_KILL(id_list, chars, int);
797 KILL(l_acc, struct account);
798 return(-3);
799 }
800 /* only exclusive char slots left */
801 } else if (chars >= max_cpa + max_cpa_plus - plus_free) {
802 /* paranoia (maybe if slot # gets changed again in the future) */
803 if (ded_iddc == MAX_DED_IDDC_CHARS && ded_pvp == MAX_DED_PVP_CHARS) {
804 /* out of character slots */
805 if (chars) C_KILL(id_list, chars, int);
806 KILL(l_acc, struct account);
807 return(-3);
808 }
809 if (ded_iddc == MAX_DED_IDDC_CHARS) success = -4; /* set char mode to MODE_DED_PVP */
810 else if (ded_pvp == MAX_DED_PVP_CHARS) success = -5; /* set char mode to MODE_DED_IDDC */
811 else success = -6; /* char mode can be either, make it depend on user choice */
812 }
813 /* at least one non-exclusive slot free */
814 else {
815 #if 1 /* new: allow any dedicated mode character to be created in 'normal' slots! */
816 success = -7;
817 #else
818 if (ded_iddc < MAX_DED_IDDC_CHARS) {
819 if (ded_pvp < MAX_DED_PVP_CHARS) success = -7; /* allow willing creation of any exlusive slot */
820 else success = -8; /* allow willing creating of iddc-exclusive slot */
821 } else if (ded_pvp < MAX_DED_PVP_CHARS) success = -9; /* allow willing creating of pvp-exclusive slot */
822 #endif
823 }
824 #endif
825 a_id = l_acc->id;
826 flags = l_acc->flags;
827
828 if (chars) C_KILL(id_list, chars, int);
829 KILL(l_acc, struct account);
830
831 id = lookup_player_id(c_name);
832 ptr = lookup_player(id);
833 /* Is the character either new, or is its ID belonging
834 to the same account as our account (ie it's us)? */
835 if (!ptr || ptr->account == a_id) {
836 /* Cannot create another character while being logged on, if not ACC_MULTI.
837 Allow to login with the same name to 'resume connection' though. */
838 if (!(flags & ACC_MULTI)) {
839 /* check for login-timing exploit */
840 if (check_multi_exploit(accname, c_name)) return -2;
841 /* check for normal ineligible multi-login attempts */
842 for (i = 1; i <= NumPlayers; i++) {
843 if (Players[i]->account == a_id && !(flags & ACC_MULTI) && strcmp(c_name, Players[i]->name))
844 return(-2);
845 }
846 }
847
848 /* all green */
849 return(success);
850 }
851 }
852 /* "Name already in use by another player" (coming from 'else' branch above),
853 ie character isn't new and it belongs to a different account than ours. */
854 return(0);
855 }
856
GetAccountID(u32b id,bool leavepass)857 struct account *GetAccountID(u32b id, bool leavepass){
858 FILE *fp;
859 char buf[1024];
860 struct account *c_acc;
861
862 /* we may want to store a local index for fast
863 id/name/filepos lookups in the future */
864 MAKE(c_acc, struct account);
865 if (!c_acc) return(NULL);
866
867 path_build(buf, 1024, ANGBAND_DIR_SAVE, "tomenet.acc");
868 fp = fopen(buf, "rb");
869 if (!fp) return(NULL); /* failed */
870 while (fread(c_acc, sizeof(struct account), 1, fp)) {
871 if(id == c_acc->id && !(c_acc->flags & ACC_DELD)){
872 if (!leavepass) memset(c_acc->pass, 0, sizeof(c_acc->pass));
873 fclose(fp);
874 return(c_acc);
875 }
876 }
877 fclose(fp);
878 KILL(c_acc, struct account);
879 return(NULL);
880 }
881
new_accid()882 static u32b new_accid() {
883 u32b id;
884 FILE *fp;
885 char *t_map;
886 char buf[1024];
887 struct account t_acc;
888 int num_entries = 0;
889 id = account_id;
890
891 path_build(buf, 1024, ANGBAND_DIR_SAVE, "tomenet.acc");
892 fp = fopen(buf, "rb");
893 if (!fp) return(0L);
894
895 C_MAKE(t_map, MAX_ACCOUNTS / 8, char);
896 while (fread(&t_acc, sizeof(struct account), 1, fp)) {
897 if (t_acc.flags & ACC_DELD) continue;
898 t_map[t_acc.id / 8] |= (1 << (t_acc.id % 8));
899 num_entries++;
900 }
901
902 fclose(fp);
903
904 /* HACK - Make account id 1 unavailable if the file is not empty.
905 * This prevents the next new player from becoming an admin if the
906 * first account is ever deleted.
907 * - mikaelh
908 */
909 if (num_entries) {
910 t_map[0] |= (1 << 1);
911 }
912
913 /* Make account id 0 unavailable just to be safe */
914 t_map[0] |= (1 << 0);
915
916 /* Find the next free account ID */
917 for (id = account_id; id < MAX_ACCOUNTS; id++){
918 if(!(t_map[id / 8] & (1 << (id % 8)))) break;
919 }
920
921 if (id == MAX_ACCOUNTS) {
922 /* Wrap around */
923 for (id = 1; id < account_id; id++) {
924 if (!(t_map[id / 8] & (1 << (id % 8)))) break;
925 }
926
927 /* Oops, no free account IDs */
928 if (id == account_id) {
929 s_printf("WARNING: No account ID numbers available!\n");
930 id = 0;
931 }
932 }
933
934 C_KILL(t_map, MAX_ACCOUNTS / 8, char);
935 account_id = id + 1;
936
937 return(id); /* temporary */
938 }
939
940 /*
941 * Lookup a guild number by name.
942 */
guild_lookup(cptr name)943 int guild_lookup(cptr name) {
944 int i;
945
946 /* Check each guild */
947 for (i = 0; i < MAX_GUILDS; i++) { /* start from 0 or from 1? */
948 /* Check name */
949 if (streq(guilds[i].name, name)){
950 return i;
951 }
952 }
953
954 /* No match */
955 return -1;
956 }
957
958 /*
959 * Lookup a party number by name.
960 */
party_lookup(cptr name)961 int party_lookup(cptr name) {
962 int i;
963
964 /* Check each party */
965 for (i = 1; i < MAX_PARTIES; i++) { /* was i = 0 but real parties start from i = 1 - mikaelh */
966 /* Check name */
967 if (streq(parties[i].name, name))
968 return i;
969 }
970
971 /* No match */
972 return -1;
973 }
974
975
976 /*
977 * Check for the existence of a player in a party.
978 */
player_in_party(int party_id,int Ind)979 bool player_in_party(int party_id, int Ind) {
980 player_type *p_ptr = Players[Ind];
981
982 /* Check - Fail on non party */
983 if (party_id && p_ptr->party == party_id)
984 return TRUE;
985
986 /* Not in the party */
987 return FALSE;
988 }
989
group_name_legal_characters(cptr name)990 static bool group_name_legal_characters(cptr name) {
991 const char *ptr;
992
993 /* remove special chars that are used for parsing purpose */
994 for (ptr = &name[strlen(name)]; ptr-- > name; )
995 if (!((*ptr >= 'A' && *ptr <= 'Z') ||
996 (*ptr >= 'a' && *ptr <= 'z') ||
997 (*ptr >= '0' && *ptr <= '9') ||
998 strchr(" .,-'&_$%~#<>|", *ptr))) /* chars allowed for character name */
999 return FALSE;
1000 return TRUE;
1001 }
1002
guild_name_legal(int Ind,char * name)1003 static bool guild_name_legal(int Ind, char *name) {
1004 int index;
1005 char *ptr, buf[NAME_LEN];
1006 player_type *p_ptr = Players[Ind];
1007 object_type forge, *o_ptr = &forge;
1008
1009 char buf2[NAME_LEN], *ptr2;
1010 int diff, min;
1011
1012
1013 if (strlen(name) >= NAME_LEN) {
1014 msg_format(Ind, "\377yGuild name must not exceed %d characters!", NAME_LEN - 1);
1015 return FALSE;
1016 }
1017
1018 strncpy(buf, name, NAME_LEN);
1019 buf[NAME_LEN - 1] = '\0';
1020 /* remove spaces at the beginning */
1021 for (ptr = buf; ptr < buf + strlen(buf); ) {
1022 if (isspace(*ptr)) ptr++;
1023 else break;
1024 }
1025 strcpy(buf, ptr);
1026 /* remove spaces at the end */
1027 for (ptr = buf + strlen(buf); ptr-- > buf; ) {
1028 if (isspace(*ptr)) *ptr = '\0';
1029 else break;
1030 }
1031 /* name consisted only of spaces? */
1032 if (!buf[0]) {
1033 msg_print(Ind, "\377ySorry, names must not just consist of spaces.");
1034 return FALSE;
1035 }
1036 strcpy(name, buf);
1037
1038 /* Check for weird characters */
1039 if (!group_name_legal_characters(name)) {
1040 msg_print(Ind, "\377ySorry, that name contains illegal characters or symbols.");
1041 return FALSE;
1042 }
1043
1044 /* Prevent abuse */
1045 if (ILLEGAL_GROUP_NAME(name)) {
1046 msg_print(Ind, "\377yThat's not a legal guild name, please try again.");
1047 return FALSE;
1048 }
1049
1050 /* Check for already existing party by that name */
1051 if (party_lookup(name) != -1) {
1052 msg_print(Ind, "\377yThere's already a party using that name.");
1053 return FALSE;
1054 }
1055
1056 /* Check for already existing guild by that name */
1057 if ((index = guild_lookup(name) != -1)) {
1058 /* Admin can actually create a duplicate 'spare' key this way */
1059 if (p_ptr->admin_dm) {
1060 /* make the guild key */
1061 invcopy(o_ptr, lookup_kind(TV_KEY, SV_GUILD_KEY));
1062 o_ptr->number = 1;
1063 o_ptr->pval = index;
1064 o_ptr->level = 1;
1065 o_ptr->owner = p_ptr->id;
1066 o_ptr->mode = p_ptr->mode;
1067 object_known(o_ptr);
1068 object_aware(Ind, o_ptr);
1069 (void)inven_carry(Ind, o_ptr);
1070 msg_print(Ind, "Spare key created.");
1071 return FALSE;
1072 }
1073 msg_print(Ind, "\377yA guild by that name already exists.");
1074 return FALSE;
1075 }
1076
1077 /* Check for already existing guild with too similar name */
1078 condense_name(buf, name);
1079 /* Check each guild */
1080 for (index = 0; index < MAX_GUILDS; index++) {
1081 /* for renaming a guild: skip similarity-check if it's our own guild! */
1082 if (guilds[index].master == p_ptr->id) continue;
1083
1084 /* compare condensed names */
1085 condense_name(buf2, guilds[index].name);
1086 if (!strcmp(buf, buf2)) {
1087 msg_print(Ind, "\377yA guild by a too similar name already exists.");
1088 return FALSE;
1089 }
1090
1091 //#ifdef STRICT_SIMILAR_NAMES
1092 #if 1
1093 /* Super-strict mode? Disallow (non-trivial) names that only have 1+ letter inserted somewhere */
1094 if (
1095 #if 1
1096 TRUE &&
1097 #else
1098 //#ifdef SIMILAR_CHARNAMES_OK
1099 FALSE &&
1100 #endif
1101 strlen(buf) >= 5 && strlen(buf2) >= 5) { //non-trivial length
1102 /* don't exaggerate */
1103 if (strlen(buf) > strlen(buf2)) min = strlen(buf2);
1104 else min = strlen(buf);
1105
1106 /* '->' */
1107 diff = 0;
1108 ptr2 = buf;
1109 for (ptr = buf2; *ptr && *ptr2; ) {
1110 if (tolower(*ptr) != tolower(*ptr2)) diff++;
1111 else ptr++;
1112 ptr2++;
1113 }
1114 //all remaining characters that couldn't be matched are also "different"
1115 while (*ptr++) diff++;
1116 while (*ptr2++) diff++;
1117 //too little difference between account name and this character name? forbidden!
1118 if (diff <= (min - 5) / 2 + 1) {
1119 s_printf("similar guild? (1): ngname '%s', ogname '%s'\n", buf, buf2);
1120 msg_print(Ind, "\377yA guild by a too similar name already exists.");
1121 return FALSE;
1122 }
1123
1124 /* '<-' */
1125 diff = 0;
1126 ptr2 = buf2;
1127 for (ptr = buf; *ptr && *ptr2; ) {
1128 if (tolower(*ptr) != tolower(*ptr2)) diff++;
1129 else ptr++;
1130 ptr2++;
1131 }
1132 //all remaining characters that couldn't be matched are also "different"
1133 while (*ptr++) diff++;
1134 while (*ptr2++) diff++;
1135 //too little difference between account name and this character name? forbidden!
1136 if (diff <= (min - 5) / 2 + 1) {
1137 s_printf("similar guild? (2): ngname '%s', ogname '%s'\n", buf, buf2);
1138 msg_print(Ind, "\377yA guild by a too similar name already exists.");
1139 return FALSE;
1140 }
1141
1142 /* '=' -- note: weakness here is, the first 2 methods don't combine with this one ;).
1143 So the checks could be tricked by combining one 'replaced char' with one 'too many char'
1144 to circumvent the limitations for longer names that usually would require a 3+ difference.. =P */
1145 diff = 0;
1146 ptr2 = buf2;
1147 for (ptr = buf; *ptr && *ptr2; ) {
1148 if (tolower(*ptr) != tolower(*ptr2)) diff++;
1149 ptr++;
1150 ptr2++;
1151 }
1152 //all remaining characters that couldn't be matched are also "different"
1153 while (*ptr++) diff++;
1154 while (*ptr2++) diff++;
1155 //too little difference between account name and this character name? forbidden!
1156 if (diff <= (min - 5) / 2 + 1) {
1157 s_printf("similar guild? (3): ngname '%s', ogname '%s'\n", buf, buf2);
1158 msg_print(Ind, "\377yA guild by a too similar name already exists.");
1159 return FALSE;
1160 }
1161 }
1162 #endif
1163 }
1164
1165 return TRUE;
1166 }
1167
1168 /*
1169 * Create a new guild.
1170 */
guild_create(int Ind,cptr name)1171 int guild_create(int Ind, cptr name) {
1172 player_type *p_ptr = Players[Ind];
1173 int index = 0, i, j;
1174 object_type forge, *o_ptr = &forge;
1175 char temp[160];
1176 struct account *acc;
1177 int *id_list, ids;
1178
1179 strcpy(temp, name);
1180 if (!guild_name_legal(Ind, temp)) return FALSE;
1181
1182 /* zonk */
1183 if ((p_ptr->mode & MODE_PVP)) {
1184 msg_print(Ind, "\377yPvP characters may not create a guild.");
1185 return FALSE;
1186 }
1187
1188 /* anti-cheeze: People could get an extra house on each character.
1189 So we allow only one guild master per player account to at least
1190 reduce the nonsense to 1 extra house per Account.. */
1191 acc = GetAccount(p_ptr->accountname, NULL, FALSE);
1192 /* paranoia */
1193 if (!acc) {
1194 /* uhh.. */
1195 msg_print(Ind, "Sorry, guild creation has failed.");
1196 return FALSE;
1197 }
1198 ids = player_id_list(&id_list, acc->id);
1199 for (i = 0; i < ids; i++) {
1200 if ((j = lookup_player_guild(id_list[i])) && /* one of his characters is in a guild.. */
1201 guilds[j].master == id_list[i]) { /* ..and he is actually the master of that guild? */
1202 msg_print(Ind, "\377yOnly one character per account is allowed to be a guild master.");
1203 return FALSE;
1204 }
1205 }
1206 if (ids) C_KILL(id_list, ids, int);
1207 KILL(acc, struct account);
1208
1209 /* Make sure this guy isn't in some other guild already */
1210 if (p_ptr->guild != 0) {
1211 msg_print(Ind, "\377yYou already belong to a guild!");
1212 return FALSE;
1213 }
1214 if (p_ptr->lev < 30) {
1215 msg_print(Ind, "\377yYou are not high enough level to start a guild.");
1216 return FALSE;
1217 }
1218 /* This could probably be improved. */
1219 if (p_ptr->au < GUILD_PRICE) {
1220 if (GUILD_PRICE >= 1000000)
1221 msg_format(Ind, "\377yYou need %d,000,000 gold pieces to start a guild.", GUILD_PRICE / 1000000);
1222 else if (GUILD_PRICE >= 1000)
1223 msg_format(Ind, "\377yYou need %d,000 gold pieces to start a guild.", GUILD_PRICE / 1000);
1224 else
1225 msg_format(Ind, "\377yYou need %d gold pieces to start a guild.", GUILD_PRICE);
1226 return FALSE;
1227 }
1228
1229 /* Find the "best" party index */
1230 for (i = 1; i < MAX_GUILDS; i++) {
1231 if (guilds[i].members == 0) {
1232 index = i;
1233 break;
1234 }
1235 }
1236 /* Make sure we found an empty slot */
1237 if (index == 0) {
1238 /* Error */
1239 msg_print(Ind, "\377yThere aren't enough guild slots!");
1240 return FALSE;
1241 }
1242
1243
1244 /* Set guild identity */
1245 guilds[index].dna = rand_int(0xFFFF) << 16;
1246 guilds[index].dna += rand_int(0xFFFF);
1247
1248 /* Set guild name */
1249 strcpy(guilds[index].name, temp);
1250
1251 /* Set guildmaster */
1252 guilds[index].master = p_ptr->id;
1253 guilds[index].cmode = p_ptr->mode;
1254
1255 /* Init guild hall, flags & level */
1256 guilds[index].h_idx = 0;
1257 guilds[index].flags = 0;
1258 guilds[index].minlev = 0;
1259
1260 /* Add the owner as a member */
1261 p_ptr->guild = index;
1262 p_ptr->guild_dna = guilds[index].dna;
1263 clockin(Ind, 3);
1264 guilds[index].members = 1;
1265
1266 /* Set guild mode */
1267 if ((p_ptr->mode & MODE_EVERLASTING))
1268 guilds[index].flags |= GFLG_EVERLASTING;
1269
1270 Send_guild(Ind, FALSE, FALSE);
1271 Send_guild_config(index);
1272
1273 /* broadcast the news */
1274 snprintf(temp, 160, "\374\377yA new guild '\377%c%s\377y' has been created.", COLOUR_CHAT_GUILD, guilds[index].name);
1275 msg_broadcast(0, temp);
1276 // msg_print(Ind, "\374\377Gou can adjust guild options with the '/guild_cfg' command.");
1277 s_printf("GUILD_CREATE: (by %s) '%s'\n", p_ptr->name, guilds[index].name);
1278 l_printf("%s \\{yA new guild '%s' has been created\n", showdate(), guilds[index].name);
1279
1280 p_ptr->au -= GUILD_PRICE;
1281 p_ptr->redraw |= PR_GOLD;
1282
1283 /* make the guild key */
1284 invcopy(o_ptr, lookup_kind(TV_KEY, SV_GUILD_KEY));
1285 o_ptr->number = 1;
1286 o_ptr->pval = index;
1287 o_ptr->level = 1;
1288 o_ptr->owner = p_ptr->id;
1289 o_ptr->mode = p_ptr->mode;
1290 object_known(o_ptr);
1291 object_aware(Ind, o_ptr);
1292 (void)inven_carry(Ind, o_ptr);
1293
1294 /* Give the guildmaster some scrolls for a hall */
1295 #if 0 //scrolls are broken
1296 invcopy(o_ptr, lookup_kind(TV_SCROLL, SV_SCROLL_HOUSE));
1297 o_ptr->number = 6;
1298 o_ptr->level = p_ptr->lev;
1299 o_ptr->owner = p_ptr->id;
1300 o_ptr->mode = p_ptr->mode;
1301 o_ptr->discount = 50;
1302 object_known(o_ptr);
1303 object_aware(Ind, o_ptr);
1304 (void)inven_carry(Ind, o_ptr);
1305 #endif
1306
1307 return(TRUE);
1308 }
1309
1310 /*
1311 * New party check function - to be timed
1312 *
1313 */
party_check(int Ind)1314 void party_check(int Ind) {
1315 int i, id;
1316 for (i = 1; i < MAX_PARTIES; i++) {
1317 if (parties[i].members != 0){
1318 if (!(id = lookup_player_id(parties[i].owner))) {
1319 msg_format(Ind, "Lost party %s (%s)", parties[i].name, parties[i].owner);
1320 del_party(i);
1321 } else {
1322 if ((lookup_player_party(id) != i)) {
1323 msg_format(Ind, "Disowned party %s (%s)", parties[i].name, parties[i].owner);
1324 del_party(i);
1325 }
1326 }
1327 }
1328 }
1329 }
1330
1331 /*
1332 * as with party checker, scan ALL player entries
1333 * if they are not linked to an existing account,
1334 * delete them.
1335 */
account_check(int Ind)1336 void account_check(int Ind) { /* Temporary Ind */
1337 hash_entry *ptr;
1338 int i, del;
1339 struct account *c_acc;
1340 // player_type *p_ptr = Players[Ind];
1341
1342 /* Search in each array slot */
1343 for (i = 0; i < NUM_HASH_ENTRIES; i++) {
1344 /* Acquire pointer to this chain */
1345 ptr = hash_table[i];
1346
1347 /* Check all entries in this chain */
1348 while (ptr) {
1349 /* Check this name */
1350 if (!(c_acc = GetAccountID(ptr->account, FALSE))) {
1351 s_printf("Lost player: %s\n", ptr->name);
1352 msg_format(Ind, "Lost player: %s", ptr->name);
1353 #if 0 /* del might not always be initialized! */
1354 del = ptr->id;
1355 } else KILL(c_acc, struct account);
1356
1357 /* Next entry in chain */
1358 ptr = ptr->next;
1359 delete_player_id(del);
1360 #else /* isn't it supposed to be this way instead?: */
1361 del = ptr->id;
1362 delete_player_id(del);
1363 } else KILL(c_acc, struct account);
1364
1365 /* Next entry in chain */
1366 ptr = ptr->next;
1367 #endif
1368 }
1369 }
1370 }
1371
1372 /*
1373 * Create a new party, owned by "Ind", and called
1374 * "name".
1375 */
party_create(int Ind,cptr name)1376 int party_create(int Ind, cptr name) {
1377 player_type *p_ptr = Players[Ind];
1378 int index = 0, i, oldest = turn;
1379
1380 char *ptr, buf[NAME_LEN];
1381 if (strlen(name) >= NAME_LEN) {
1382 msg_format(Ind, "\377yParty name must not exceed %d characters!", NAME_LEN - 1);
1383 return FALSE;
1384 }
1385 strncpy(buf, name, NAME_LEN);
1386 buf[NAME_LEN - 1] = '\0';
1387 /* remove spaces at the beginning */
1388 for (ptr = buf; ptr < buf + strlen(buf); ) {
1389 if (isspace(*ptr)) ptr++;
1390 else break;
1391 }
1392 strcpy(buf, ptr);
1393 /* remove spaces at the end */
1394 for (ptr = buf + strlen(buf); ptr-- > buf; ) {
1395 if (isspace(*ptr)) *ptr = '\0';
1396 else break;
1397 }
1398 name = buf;
1399
1400 /* Check for weird characters */
1401 if (!group_name_legal_characters(name)) {
1402 msg_print(Ind, "\377ySorry, that name contains illegal characters or symbols.");
1403 return FALSE;
1404 }
1405 /* Prevent abuse */
1406 if (ILLEGAL_GROUP_NAME(name)) {
1407 msg_print(Ind, "\377yThat's not a legal party name, please try again.");
1408 return FALSE;
1409 }
1410
1411 /* Check for already existing party by that name */
1412 if (party_lookup(name) != -1) {
1413 msg_print(Ind, "\377yA party by that name already exists.");
1414 return FALSE;
1415 }
1416 /* Check for already existing guild by that name */
1417 if (guild_lookup(name) != -1) {
1418 msg_print(Ind, "\377yThere's already a guild using that name.");
1419 return FALSE;
1420 }
1421
1422 /* If he's party owner, it's name change */
1423 if (streq(parties[p_ptr->party].owner, p_ptr->name)) {
1424 if (parties[p_ptr->party].mode != PA_NORMAL) {
1425 msg_print(Ind, "\377yYour party is an Iron Team. Choose '2) Create an Iron Team' instead.");
1426 return FALSE;
1427 }
1428
1429 strcpy(parties[p_ptr->party].name, name);
1430
1431 /* Tell the party about its new name */
1432 party_msg_format(p_ptr->party, "\377%cYour party is now called '%s'.", COLOUR_CHAT_GUILD, name);
1433
1434 Send_party(Ind, FALSE, FALSE);
1435 return TRUE;
1436 }
1437
1438 /* Make sure this guy isn't in some other party already */
1439 if (p_ptr->party != 0) {
1440 msg_print(Ind, "\377yYou already belong to a party!");
1441 return FALSE;
1442 }
1443
1444 /* Find the "best" party index */
1445 for (i = 1; i < MAX_PARTIES; i++) {
1446 /* Check deletion time of disbanded parties */
1447 if (parties[i].members == 0 && parties[i].created < oldest) {
1448 /* Track oldest */
1449 oldest = parties[i].created;
1450 index = i;
1451 }
1452 }
1453
1454 /* Make sure we found an empty slot */
1455 if (index == 0 || oldest == turn) {
1456 /* Error */
1457 msg_print(Ind, "\377yThere aren't enough party slots!");
1458 return FALSE;
1459 }
1460
1461 /* Set party name */
1462 strcpy(parties[index].name, name);
1463
1464 /* Set owner name */
1465 strcpy(parties[index].owner, p_ptr->name);
1466
1467 /* Set mode to normal */
1468 parties[index].mode = PA_NORMAL;
1469 parties[index].cmode = p_ptr->mode;
1470
1471 /* Add the owner as a member */
1472 p_ptr->party = index;
1473 parties[index].members = 1;
1474 clockin(Ind, 2);
1475
1476 /* Set the "creation time" */
1477 parties[index].created = turn;
1478
1479 /* Resend party info */
1480 Send_party(Ind, FALSE, FALSE);
1481
1482 /* Success */
1483 return TRUE;
1484 }
1485
party_create_ironteam(int Ind,cptr name)1486 int party_create_ironteam(int Ind, cptr name) {
1487 player_type *p_ptr = Players[Ind];
1488 int index = 0, i, oldest = turn;
1489
1490 char *ptr, buf[NAME_LEN];
1491 strcpy(buf, name);
1492 /* remove spaces at the beginning */
1493 for (ptr = buf; ptr < buf + strlen(buf); ) {
1494 if (isspace(*ptr)) ptr++;
1495 else break;
1496 }
1497 strcpy(buf, ptr);
1498 /* remove spaces at the end */
1499 for (ptr = buf + strlen(buf); ptr-- > buf; ) {
1500 if (isspace(*ptr)) *ptr = '\0';
1501 else break;
1502 }
1503 name = buf;
1504
1505 /* Only newly created characters can create an iron team */
1506 if (p_ptr->max_exp > 0 || p_ptr->max_plv > 1) {
1507 msg_print(Ind, "\377yOnly newly created characters without experience can create an iron team.");
1508 return FALSE;
1509 }
1510
1511 /* Check for weird characters */
1512 if (!group_name_legal_characters(name)) {
1513 msg_print(Ind, "\377ySorry, that name contains illegal characters or symbols.");
1514 return FALSE;
1515 }
1516 /* Prevent abuse */
1517 if (ILLEGAL_GROUP_NAME(name)) {
1518 msg_print(Ind, "\377yThat's not a legal party name, please try again.");
1519 return FALSE;
1520 }
1521
1522 /* Check for already existing party by that name */
1523 if (party_lookup(name) != -1) {
1524 msg_print(Ind, "\377yA party by that name already exists.");
1525 return FALSE;
1526 }
1527
1528 /* If he's party owner, it's name change */
1529 if (streq(parties[p_ptr->party].owner, p_ptr->name)) {
1530 if (parties[p_ptr->party].mode != PA_IRONTEAM) {
1531 msg_print(Ind, "\377yYour party isn't an Iron Team. Choose '1) Create a party' instead.");
1532 return FALSE;
1533 }
1534
1535 strcpy(parties[p_ptr->party].name, name);
1536
1537 /* Tell the party about its new name */
1538 party_msg_format(p_ptr->party, "\377%cYour iron team is now called '%s'.", COLOUR_CHAT_GUILD, name);
1539
1540 Send_party(Ind, FALSE, FALSE);
1541 return TRUE;
1542 }
1543
1544 /* Make sure this guy isn't in some other party already */
1545 if (p_ptr->party != 0) {
1546 msg_print(Ind, "\377yYou already belong to a party!");
1547 return FALSE;
1548 }
1549
1550 /* Find the "best" party index */
1551 for (i = 1; i < MAX_PARTIES; i++) {
1552 /* Check deletion time of disbanded parties */
1553 if (parties[i].members == 0 && parties[i].created < oldest) {
1554 /* Track oldest */
1555 oldest = parties[i].created;
1556 index = i;
1557 }
1558 }
1559
1560 /* Make sure we found an empty slot */
1561 if (index == 0 || oldest == turn) {
1562 /* Error */
1563 msg_print(Ind, "\377yThere aren't enough party slots!");
1564 return FALSE;
1565 }
1566
1567 /* Set party name */
1568 strcpy(parties[index].name, name);
1569
1570 /* Set owner name */
1571 strcpy(parties[index].owner, p_ptr->name);
1572
1573 /* Set mode to iron team */
1574 parties[index].mode = PA_IRONTEAM;
1575 parties[index].cmode = p_ptr->mode;
1576
1577 /* Initialize max exp */
1578 parties[index].experience = 0;
1579
1580 /* Add the owner as a member */
1581 p_ptr->party = index;
1582 parties[index].members = 1;
1583 clockin(Ind, 2);
1584
1585 /* Set the "creation time" */
1586 parties[index].created = turn;
1587
1588 /* Resend party info */
1589 Send_party(Ind, FALSE, FALSE);
1590
1591 /* Success */
1592 return TRUE;
1593 }
1594
1595 /*
1596 * Add player to a guild
1597 */
1598 /* Allow the guild master or an added to use any other character on their
1599 account too, as long as they are guild members, to add other players? */
1600 #define GUILD_ELIGIBLE_ACCOUNT_CAN_ADD
guild_add(int adder,cptr name)1601 int guild_add(int adder, cptr name) {
1602 player_type *p_ptr;
1603 player_type *q_ptr = Players[adder];
1604 int guild_id = q_ptr->guild, Ind = 0, i;
1605 #ifdef GUILD_ELIGIBLE_ACCOUNT_CAN_ADD
1606 int *id_list, ids;
1607 struct account *acc;
1608 bool far_success = FALSE;
1609 #endif
1610
1611 if (!guild_id) {
1612 msg_print(adder, "\377yYou are not in a guild");
1613 return(FALSE);
1614 }
1615
1616 Ind = name_lookup_loose(adder, name, FALSE, TRUE);
1617 if (Ind <= 0) return FALSE;
1618 p_ptr = Players[Ind];
1619
1620 /* Leaderless guilds do not allow addition of new members */
1621 if (!lookup_player_name(guilds[guild_id].master)) {
1622 msg_print(adder, "\377yNo new members can be added while the guild is leaderless.");
1623 return FALSE;
1624 }
1625
1626 /* Everlasting and other chars cannot be in the same guild */
1627 if (compat_pmode(adder, Ind, TRUE)) {
1628 msg_format(adder, "\377yYou cannot add %s characters to this guild.", compat_pmode(adder, Ind, TRUE));
1629 return FALSE;
1630 }
1631
1632 /* Make sure this added person is neutral */
1633 if (p_ptr->guild != 0) {
1634 /* Message */
1635 if (p_ptr->guild != guild_id) msg_print(adder, "\377yThat player is already in a guild.");
1636 else msg_print(adder, "\377yThat player is already in your guild.");
1637
1638 /* Abort */
1639 return FALSE;
1640 }
1641
1642 if (p_ptr->lev < guilds[guild_id].minlev) {
1643 msg_format(adder, "\377yThat player does not meet the minimum level requirements, %d, of the guild.", guilds[guild_id].minlev);
1644 return FALSE;
1645 }
1646
1647 #ifdef GUILD_ELIGIBLE_ACCOUNT_CAN_ADD
1648 /* check if he has a character in there already, to be eligible to self-add */
1649 acc = GetAccount(q_ptr->accountname, NULL, FALSE);
1650 /* paranoia */
1651 if (!acc) {
1652 /* uhh.. */
1653 msg_print(Ind, "Sorry, self-adding has failed.");
1654 return FALSE;
1655 }
1656 ids = player_id_list(&id_list, acc->id);
1657 for (i = 0; i < ids; i++) {
1658 if (lookup_player_guild(id_list[i]) != guild_id) continue;
1659
1660 if (id_list[i] != guilds[guild_id].master &&
1661 (!(guilds[guild_id].flags & GFLG_ALLOW_ADDERS) ||
1662 !(lookup_player_guildflags(id_list[i]) & PGF_ADDER)))
1663 continue;
1664
1665 /* Log - security */
1666 s_printf("GUILD_ADD_FAR: %s has been added to %s by %s.\n", p_ptr->name, guilds[guild_id].name, q_ptr->name);
1667 far_success = TRUE;
1668 break;
1669 }
1670 if (ids) C_KILL(id_list, ids, int);
1671 KILL(acc, struct account);
1672 /* failure? */
1673 if (!far_success)
1674 #endif
1675
1676 /* Make sure this isn't an impostor */
1677 if (!((guilds[guild_id].flags & GFLG_ALLOW_ADDERS) && (q_ptr->guild_flags & PGF_ADDER))
1678 && guilds[guild_id].master != q_ptr->id
1679 && !is_admin(q_ptr)) {
1680 msg_print(adder, "\377yYou cannot add new members.");
1681 return FALSE;
1682 }
1683
1684 /* Ignoring a player will prevent getting added to a party by him */
1685 if (check_ignore(Ind, adder)) return FALSE;
1686
1687 /* Log - security */
1688 if (!far_success) s_printf("GUILD_ADD: %s has been added to %s by %s.\n", p_ptr->name, guilds[guild_id].name, q_ptr->name);
1689
1690 /* Tell the guild about its new member */
1691 guild_msg_format(guild_id, "\374\377y%s has been added to %s by %s.", p_ptr->name, guilds[guild_id].name, q_ptr->name);
1692
1693 /* One more player in this guild */
1694 guilds[guild_id].members++;
1695
1696 /* Tell him about it */
1697 msg_format(Ind, "\374\377yYou've been added to '%s' by %s.", guilds[guild_id].name, q_ptr->name);
1698
1699 /* Set his guild number */
1700 p_ptr->guild = guild_id;
1701 p_ptr->guild_dna = guilds[guild_id].dna;
1702 clockin(Ind, 3);
1703
1704 /* Resend info */
1705 Send_guild(Ind, FALSE, FALSE);
1706
1707 /* Display the guild note to him */
1708 for (i = 0; i < MAX_GUILDNOTES; i++) {
1709 if (!strcmp(guild_note_target[i], guilds[p_ptr->guild].name)) {
1710 if (strcmp(guild_note[i], ""))
1711 msg_format(Ind, "\374\377bGuild Note: %s", guild_note[i]);
1712 break;
1713 }
1714 }
1715
1716 /* Re-check house permissions, to display doors in correct colour */
1717 if (!p_ptr->wpos.wz) p_ptr->redraw |= PR_MAP;
1718
1719 /* Success */
1720 return TRUE;
1721 }
guild_add_self(int Ind,cptr guild)1722 int guild_add_self(int Ind, cptr guild) {
1723 player_type *p_ptr = Players[Ind];
1724 int guild_id = guild_lookup(guild), i, *id_list, ids;
1725 struct account *acc;
1726 bool member = FALSE;
1727
1728 /* no guild name specified? */
1729 if (!guild[0]) return FALSE;
1730
1731 if (guild_id == -1) {
1732 msg_print(Ind, "That guild does not exist.");
1733 return FALSE;
1734 }
1735
1736 if (p_ptr->lev < guilds[guild_id].minlev) {
1737 msg_format(Ind, "\377yYou do not meet the minimum level requirements, %d, of the guild.", guilds[guild_id].minlev);
1738 return FALSE;
1739 }
1740
1741 /* check if he has a character in there already, to be eligible to self-add */
1742 acc = GetAccount(p_ptr->accountname, NULL, FALSE);
1743 /* paranoia */
1744 if (!acc) {
1745 /* uhh.. */
1746 msg_print(Ind, "Sorry, self-adding has failed.");
1747 return FALSE;
1748 }
1749 ids = player_id_list(&id_list, acc->id);
1750 for (i = 0; i < ids; i++) {
1751 if (lookup_player_guild(id_list[i]) == guild_id) {
1752 member = TRUE;
1753
1754 /* Everlasting and other chars cannot be in the same guild */
1755 if (compat_mode(p_ptr->mode, lookup_player_mode(id_list[i]))) {
1756 msg_format(Ind, "\377yYou cannot join %s guilds.", compat_mode(p_ptr->mode, lookup_player_mode(id_list[i])));
1757 KILL(acc, struct account);
1758 return FALSE;
1759 }
1760
1761 /* player is guild master? -> ok */
1762 if (id_list[i] == guilds[guild_id].master) {
1763 /* Log - security */
1764 s_printf("GUILD_ADD_SELF: (master) %s has been added to %s.\n", p_ptr->name, guilds[guild_id].name);
1765
1766 /* success */
1767 break;
1768 }
1769 /* Make sure this isn't an impostor */
1770 if (!(guilds[guild_id].flags & GFLG_ALLOW_ADDERS)) continue;
1771 if (!(lookup_player_guildflags(id_list[i]) & PGF_ADDER)) continue;
1772
1773 /* Log - security */
1774 s_printf("GUILD_ADD_SELF: (adder) %s has been added to %s.\n", p_ptr->name, guilds[guild_id].name);
1775
1776 /* success */
1777 break;
1778 }
1779 }
1780 if (ids) C_KILL(id_list, ids, int);
1781 KILL(acc, struct account);
1782 /* failure? */
1783 if (i == ids) {
1784 if (!member) msg_print(Ind, "You do not have any character that is member of that guild.");
1785 else msg_print(Ind, "You have no character in that guild that is allowed to add others.");
1786 return FALSE;
1787 }
1788
1789 /* Tell the guild about its new member */
1790 guild_msg_format(guild_id, "\374\377y%s has been added to %s.", p_ptr->name, guilds[guild_id].name);
1791
1792 /* One more player in this guild */
1793 guilds[guild_id].members++;
1794
1795 /* Tell him about it */
1796 msg_format(Ind, "\374\377yYou've been added to '%s'.", guilds[guild_id].name);
1797
1798 /* Set his guild number */
1799 p_ptr->guild = guild_id;
1800 p_ptr->guild_dna = guilds[guild_id].dna;
1801 clockin(Ind, 3);
1802
1803 /* Resend info */
1804 Send_guild(Ind, FALSE, FALSE);
1805
1806 /* Display the guild note to him */
1807 for (i = 0; i < MAX_GUILDNOTES; i++) {
1808 if (!strcmp(guild_note_target[i], guilds[p_ptr->guild].name)) {
1809 if (strcmp(guild_note[i], ""))
1810 msg_format(Ind, "\374\377bGuild Note: %s", guild_note[i]);
1811 break;
1812 }
1813 }
1814
1815 /* Re-check house permissions, to display doors in correct colour */
1816 if (!p_ptr->wpos.wz) p_ptr->redraw |= PR_MAP;
1817
1818 /* Success */
1819 return TRUE;
1820 }
1821
guild_auto_add(int Ind,int guild_id,char * message)1822 int guild_auto_add(int Ind, int guild_id, char *message) {
1823 player_type *p_ptr = Players[Ind];
1824 int i;
1825
1826 /* paranoia */
1827 if (!guild_id) return FALSE;
1828 if (p_ptr->guild) return FALSE;
1829
1830 if (!(guilds[guild_id].flags & GFLG_AUTO_READD)) return FALSE;
1831
1832 /* currently not eligible */
1833 if (p_ptr->mode & MODE_PVP) return FALSE;
1834
1835 /* Everlasting and other chars cannot be in the same guild */
1836 if (guilds[guild_id].flags & GFLG_EVERLASTING) {
1837 if (!(p_ptr->mode & MODE_EVERLASTING)) return FALSE;
1838 } else
1839 if ((p_ptr->mode & MODE_EVERLASTING)) return FALSE;
1840
1841 /* Log - security */
1842 s_printf("GUILD_ADD_AUTO: %s has been added to %s.\n", p_ptr->name, guilds[guild_id].name);
1843
1844 /* Tell the guild about its new member */
1845 //sprintf(message, "\374\377y%s has been auto-added to %s.", p_ptr->name, guilds[guild_id].name);
1846 //guild_msg_format(guild_id, "\374\377y%s has been auto-added to %s.", p_ptr->name, guilds[guild_id].name);
1847 guild_msg_format(guild_id, "\374\377y%s has been added to %s.", p_ptr->name, guilds[guild_id].name);
1848
1849 /* One more player in this guild */
1850 guilds[guild_id].members++;
1851
1852 /* Tell him about it */
1853 msg_format(Ind, "\374\377yYou've been added to '%s'.", guilds[guild_id].name);
1854
1855 /* Set his guild number */
1856 p_ptr->guild = guild_id;
1857 p_ptr->guild_dna = guilds[guild_id].dna;
1858 clockin(Ind, 3);
1859
1860 /* Resend info */
1861 Send_guild(Ind, FALSE, FALSE);
1862
1863 /* Display the guild note to him */
1864 for (i = 0; i < MAX_GUILDNOTES; i++) {
1865 if (!strcmp(guild_note_target[i], guilds[p_ptr->guild].name)) {
1866 if (strcmp(guild_note[i], ""))
1867 msg_format(Ind, "\374\377bGuild Note: %s", guild_note[i]);
1868 break;
1869 }
1870 }
1871
1872 /* Success */
1873 return TRUE;
1874 }
1875
1876 /*
1877 * Add a player to a party.
1878 */
party_add(int adder,cptr name)1879 int party_add(int adder, cptr name) {
1880 player_type *p_ptr;
1881 player_type *q_ptr = Players[adder];
1882 int party_id = q_ptr->party, Ind = 0, i;
1883
1884 if (q_ptr->party == 0) {
1885 msg_print(adder, "\377yYou don't belong to a party.");
1886 return FALSE;
1887 }
1888
1889 Ind = name_lookup_loose(adder, name, FALSE, TRUE);
1890 if (Ind <= 0) return FALSE;
1891
1892 if (adder == Ind) {
1893 msg_print(adder, "\377yYou cannot add yourself, you are already in the party.");
1894 return FALSE;
1895 }
1896
1897 /* Set pointer */
1898 p_ptr = Players[Ind];
1899
1900 #if 0 // It's really a prob that the owner can't add his own chars..so if0
1901 /* Make sure this isn't an impostor */
1902 if (!streq(parties[party_id].owner, q_ptr->name)) {
1903 /* Message */
1904 msg_print(adder, "\377yYou must be the owner to add someone.");
1905
1906 /* Abort */
1907 return FALSE;
1908 }
1909 #endif
1910 /* Make sure this added person is neutral */
1911 if (p_ptr->party != 0) {
1912 /* Message */
1913 if (p_ptr->party != party_id) msg_print(adder, "\377yThat player is already in a party.");
1914 else msg_print(adder, "\377yThat player is already in your party.");
1915
1916 /* Abort */
1917 return FALSE;
1918 }
1919
1920 if (
1921 #ifdef ALLOW_NR_CROSS_PARTIES
1922 (!q_ptr->total_winner || !p_ptr->total_winner ||
1923 !at_netherrealm(&q_ptr->wpos) || !at_netherrealm(&p_ptr->wpos)) &&
1924 #endif
1925 #ifdef IRONDEEPDIVE_ALLOW_INCOMPAT
1926 (!in_irondeepdive(&q_ptr->wpos) || !in_irondeepdive(&p_ptr->wpos)) &&
1927 #endif
1928 /* Everlasting and other chars cannot be in the same party */
1929 (compat_mode(parties[party_id].cmode, p_ptr->mode))) {
1930 msg_format(adder, "\377yYou cannot form a party with %s characters.", compat_mode(parties[party_id].cmode, p_ptr->mode));
1931 return FALSE;
1932 }
1933
1934 /* Only newly created characters can join an iron team */
1935 if ((parties[party_id].mode & PA_IRONTEAM) && (p_ptr->max_exp > 0 || p_ptr->max_plv > 1)) {
1936 msg_print(adder, "\377yOnly newly created characters without experience can join an iron team.");
1937 return FALSE;
1938 }
1939
1940 /* Ignoring a player will prevent getting added to a party by him */
1941 if (check_ignore(Ind, adder)) return FALSE;
1942
1943 /* Log - security */
1944 s_printf("PARTY_ADD: %s has been added to %s by %s.\n", p_ptr->name, parties[party_id].name, q_ptr->name);
1945
1946 /* Tell the party about its new member */
1947 party_msg_format(party_id, "\374\377y%s has been added to party %s by %s.", p_ptr->name, parties[party_id].name, q_ptr->name);
1948
1949 /* One more player in this party */
1950 parties[party_id].members++;
1951
1952 /* Tell him about it */
1953 if (parties[party_id].mode == PA_IRONTEAM)
1954 msg_format(Ind, "\374\377yYou've been added to iron team '%s' by %s.", parties[party_id].name, q_ptr->name);
1955 else
1956 msg_format(Ind, "\374\377yYou've been added to party '%s' by %s.", parties[party_id].name, q_ptr->name);
1957
1958 /* Set his party number */
1959 p_ptr->party = party_id;
1960 clockin(Ind, 2);
1961
1962 /* Resend info */
1963 Send_party(Ind, FALSE, FALSE);
1964
1965 /* Display the party note to him */
1966 for (i = 0; i < MAX_PARTYNOTES; i++) {
1967 if (!strcmp(party_note_target[i], parties[p_ptr->party].name)) {
1968 if (strcmp(party_note[i], ""))
1969 msg_format(Ind, "\374\377bParty Note: %s", party_note[i]);
1970 break;
1971 }
1972 }
1973
1974 /* Re-check house permissions, to display doors in correct colour */
1975 if (!p_ptr->wpos.wz) p_ptr->redraw |= PR_MAP;
1976
1977 /* Success */
1978 return TRUE;
1979 }
party_add_self(int Ind,cptr party)1980 int party_add_self(int Ind, cptr party) {
1981 player_type *p_ptr = Players[Ind];
1982 int party_id = party_lookup(party), i, *id_list, ids;
1983 struct account *acc;
1984
1985 if (party_id == -1) {
1986 msg_print(Ind, "That party does not exist.");
1987 return FALSE;
1988 }
1989
1990 /* Everlasting and other chars cannot be in the same party */
1991 if (compat_mode(p_ptr->mode, parties[party_id].cmode)) {
1992 msg_format(Ind, "\377yYou cannot join %s parties.", compat_mode(p_ptr->mode, parties[party_id].cmode));
1993 return FALSE;
1994 }
1995
1996 /* Only newly created characters can join an iron team */
1997 if ((parties[party_id].mode & PA_IRONTEAM) && (p_ptr->max_exp > 0 || p_ptr->max_plv > 1)) {
1998 msg_print(Ind, "\377yOnly newly created characters without experience can join an iron team.");
1999 return FALSE;
2000 }
2001
2002 /* check if he has a character in there already, to be eligible to self-add */
2003 acc = GetAccount(p_ptr->accountname, NULL, FALSE);
2004 /* paranoia */
2005 if (!acc) {
2006 /* uhh.. */
2007 msg_print(Ind, "Sorry, self-adding has failed.");
2008 return FALSE;
2009 }
2010 ids = player_id_list(&id_list, acc->id);
2011 for (i = 0; i < ids; i++) {
2012 if (lookup_player_party(id_list[i]) == party_id) {
2013 /* success */
2014 break;
2015 }
2016 }
2017 if (ids) C_KILL(id_list, ids, int);
2018 KILL(acc, struct account);
2019 /* failure? */
2020 if (i == ids) {
2021 msg_print(Ind, "You do not have any character that is member of that party.");
2022 return FALSE;
2023 }
2024
2025 /* Log - security */
2026 s_printf("PARTY_ADD_SELF: %s has been added to %s.\n", p_ptr->name, parties[party_id].name);
2027
2028 /* Tell the party about its new member */
2029 party_msg_format(party_id, "\374\377y%s has been added to party %s.", p_ptr->name, parties[party_id].name);
2030
2031 /* One more player in this party */
2032 parties[party_id].members++;
2033
2034 /* Tell him about it */
2035 if (parties[party_id].mode == PA_IRONTEAM)
2036 msg_format(Ind, "\374\377yYou've been added to iron team '%s'.", parties[party_id].name);
2037 else
2038 msg_format(Ind, "\374\377yYou've been added to party '%s'.", parties[party_id].name);
2039
2040 /* Set his party number */
2041 p_ptr->party = party_id;
2042 clockin(Ind, 2);
2043
2044 /* Resend info */
2045 Send_party(Ind, FALSE, FALSE);
2046
2047 /* Display the party note to him */
2048 for (i = 0; i < MAX_PARTYNOTES; i++) {
2049 if (!strcmp(party_note_target[i], parties[p_ptr->party].name)) {
2050 if (strcmp(party_note[i], ""))
2051 msg_format(Ind, "\374\377bParty Note: %s", party_note[i]);
2052 break;
2053 }
2054 }
2055
2056 /* Re-check house permissions, to display doors in correct colour */
2057 if (!p_ptr->wpos.wz) p_ptr->redraw |= PR_MAP;
2058
2059 /* Success */
2060 return TRUE;
2061 }
2062
erase_guild_key(int id)2063 static void erase_guild_key(int id) {
2064 int i, this_o_idx, next_o_idx;
2065 monster_type *m_ptr;
2066 object_type *o_ptr, *q_ptr;
2067 char m_name[MNAME_LEN];
2068
2069 #if 0 /* account-based */
2070 int j;
2071 FILE *fp;
2072 char buf[1024];
2073 cptr cname;
2074 struct account c_acc;
2075 #else /* just hash-table (character) based */
2076 int slot;
2077 hash_entry *ptr;
2078 player_type *p_ptr;
2079 #endif
2080
2081
2082 /* objects on the floor/in monster inventories */
2083 for (i = 0; i < o_max; i++) {
2084 o_ptr = &o_list[i];
2085 /* Skip dead objects */
2086 if (!o_ptr->k_idx) continue;
2087 /* skip non-guild keys */
2088 if (o_ptr->tval != TV_KEY || o_ptr->sval != SV_GUILD_KEY) continue;
2089 /* Skip wrong guild keys */
2090 if (o_ptr->pval != id) continue;
2091
2092 /* in monster inventory */
2093 if (o_ptr->held_m_idx) {
2094 m_ptr = &m_list[o_ptr->held_m_idx];
2095 /* 1st object held is the key? */
2096 if (m_ptr->hold_o_idx == i) {
2097 m_ptr->hold_o_idx = o_ptr->next_o_idx;
2098 monster_desc(0, m_name, o_ptr->held_m_idx, 0);
2099 s_printf("GUILD_KEY_ERASE: monster inventory (%d, '%s', #1)\n", o_ptr->held_m_idx, m_name);
2100 delete_object_idx(i, TRUE);
2101 return;
2102 } else {
2103 i = 1;
2104 q_ptr = &o_list[m_ptr->hold_o_idx];//compiler warning
2105 for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) {
2106 if (this_o_idx == i) {
2107 q_ptr->next_o_idx = o_list[this_o_idx].next_o_idx;
2108 monster_desc(0, m_name, o_ptr->held_m_idx, 0);
2109 s_printf("GUILD_KEY_ERASE: monster inventory (%d, '%s', #%d)\n", o_ptr->held_m_idx, m_name, i);
2110 delete_object_idx(this_o_idx, TRUE);
2111 return;
2112 }
2113 q_ptr = &o_list[this_o_idx];
2114 next_o_idx = q_ptr->next_o_idx;
2115 i++;
2116 }
2117 }
2118 }
2119
2120 s_printf("GUILD_KEY_ERASE: floor\n");
2121 delete_object_idx(i, TRUE);
2122 return;
2123 }
2124
2125 /* Players online */
2126 for (this_o_idx = 1; this_o_idx <= NumPlayers; this_o_idx++) {
2127 p_ptr = Players[this_o_idx];
2128 /* scan his inventory */
2129 for (i = 0; i < INVEN_TOTAL; i++) {
2130 o_ptr = &p_ptr->inventory[i];
2131 if (!o_ptr->k_idx) continue;
2132
2133 if (o_ptr->tval == TV_KEY && o_ptr->sval == SV_GUILD_KEY && o_ptr->pval == id) {
2134 s_printf("GUILD_KEY_ERASE: player '%s'\n", p_ptr->name);
2135 inven_item_increase(this_o_idx, i, -1);
2136 inven_item_describe(this_o_idx, i);
2137 inven_item_optimize(this_o_idx, i);
2138 return;
2139 }
2140 }
2141 }
2142
2143 #if 0 /* account-based */
2144 /* objects in player inventories */
2145 path_build(buf, 1024, ANGBAND_DIR_SAVE, "tomenet.acc");
2146 fp = fopen(buf, "rb+");
2147 if (!fp) {
2148 s_printf("GUILD_KEY_ERASE: failed to open tomenet.acc\n");
2149 return;
2150 }
2151 while (fread(&c_acc, sizeof(struct account), 1, fp)) {
2152 int *id_list, chars;
2153 chars = player_id_list(&id_list, c_acc->id);
2154 for (i = 0; i < chars; i++) {
2155 cname = lookup_player_name(id_list[i]);
2156 ...//not implemented
2157 }
2158 if (chars) C_KILL(id_list, chars, int);
2159 }
2160 #else /* just hash-table (character) based */
2161 /* hack */
2162 NumPlayers++;
2163 MAKE(Players[NumPlayers], player_type);
2164 p_ptr = Players[NumPlayers];
2165 p_ptr->inventory = C_NEW(INVEN_TOTAL, object_type);
2166 for (slot = 0; slot < NUM_HASH_ENTRIES; slot++) {
2167 ptr = hash_table[slot];
2168 while (ptr) {
2169 /* clear his data (especially inventory) */
2170 o_ptr = p_ptr->inventory;
2171 WIPE(p_ptr, player_type);
2172 p_ptr->inventory = o_ptr;
2173 p_ptr->Ind = NumPlayers;
2174 C_WIPE(p_ptr->inventory, INVEN_TOTAL, object_type);
2175 /* set his supposed name */
2176 strcpy(p_ptr->name, ptr->name);
2177 /* generate savefile name */
2178 process_player_name(NumPlayers, TRUE);
2179 /* try to load him! */
2180 if (!load_player(NumPlayers)) {
2181 /* bad fail */
2182 s_printf("GUILD_KEY_ERASE: load_player '%s' failed\n", p_ptr->name);
2183 /* unhack */
2184 C_FREE(p_ptr->inventory, INVEN_TOTAL, object_type);
2185 KILL(p_ptr, player_type);
2186 NumPlayers--;
2187 return;
2188 }
2189 /* scan his inventory */
2190 for (i = 0; i < INVEN_TOTAL; i++) {
2191 o_ptr = &p_ptr->inventory[i];
2192 if (!o_ptr->k_idx) continue;
2193
2194 if (o_ptr->tval == TV_KEY && o_ptr->sval == SV_GUILD_KEY && o_ptr->pval == id) {
2195 s_printf("GUILD_KEY_ERASE: savegame '%s'\n", p_ptr->name);
2196 o_ptr->tval = o_ptr->sval = o_ptr->k_idx = 0;
2197 /* write savegame back */
2198 save_player(NumPlayers);
2199 /* unhack */
2200 C_FREE(p_ptr->inventory, INVEN_TOTAL, object_type);
2201 KILL(p_ptr, player_type);
2202 NumPlayers--;
2203 return;
2204 }
2205 }
2206 /* advance to next character */
2207 ptr = ptr->next;
2208 }
2209 }
2210 /* unhack */
2211 C_FREE(p_ptr->inventory, INVEN_TOTAL, object_type);
2212 KILL(p_ptr, player_type);
2213 NumPlayers--;
2214 #endif
2215
2216 /* hm, failed to locate the guild key. Maybe someone actually lost it. */
2217 s_printf("GUILD_KEY_ERASE: not found\n");
2218 }
2219
2220 /*
2221 * Remove a guild. What a sad day.
2222 *
2223 * In style of del_party.
2224 */
del_guild(int id)2225 void del_guild(int id) {
2226 char temp[160];
2227 int i;
2228
2229 /* Clear the guild hall */
2230 kill_houses(id, OT_GUILD);
2231
2232 /* delete pending guild notes */
2233 for (i = 0; i < MAX_GUILDNOTES; i++) {
2234 if (!strcmp(guild_note_target[i], guilds[id].name)) {
2235 strcpy(guild_note_target[i], "");
2236 strcpy(guild_note[i], "");
2237 break;
2238 }
2239 }
2240
2241 /* erase guild bbs */
2242 for (i = 0; i < BBS_LINES; i++) strcpy(gbbs_line[id][i], "");
2243
2244 /* log! (to track auto-disbanding of leaderless guilds) */
2245 s_printf("DEL_GUILD: '%s' (%d)\n", guilds[id].name, id);
2246
2247 /* Tell everyone */
2248 snprintf(temp, 160, "\374\377yThe guild '\377%c%s\377y' no longer exists.", COLOUR_CHAT_GUILD, guilds[id].name);
2249 msg_broadcast(0, temp);
2250
2251 /* Clear the basic info */
2252 guilds[id].members = 0; /* it should be zero anyway */
2253 strcpy(guilds[id].name,"");
2254 for (i = 0; i < 5; i++) guilds[id].adder[i][0] = '\0'; /* they should be cleared anyway */
2255 guilds[id].flags = GFLG_NONE;
2256 guilds[id].minlev = 0;
2257
2258 /* erase guild hall */
2259 #if 0 /* not needed, since there can only be one guild hall */
2260 kill_houses(id, OT_GUILD);
2261 #else
2262 if (guilds[id].h_idx) {
2263 struct dna_type *dna = houses[guilds[id].h_idx - 1].dna;
2264 dna->owner = 0L;
2265 dna->creator = 0L;
2266 dna->a_flags = ACF_NONE;
2267 kill_house_contents(&houses[guilds[id].h_idx - 1]);
2268
2269 /* and in case it was suspended due to leaderlessness,
2270 so the next guild buying this house won't get a surprise.. */
2271 houses[guilds[id].h_idx - 1].flags &= ~HF_GUILD_SUS;
2272 fill_house(&houses[guilds[id].h_idx - 1], FILL_GUILD_SUS_UNDO, NULL);
2273
2274 guilds[id].h_idx = 0;//obsolete?
2275 }
2276 #endif
2277
2278 /* Find and destroy the guild key! */
2279 erase_guild_key(id);
2280 }
guild_timeout(int id)2281 void guild_timeout(int id) {
2282 int i;
2283 player_type *p_ptr;
2284
2285 for (i = 1; i <= NumPlayers; i++) {
2286 p_ptr = Players[i];
2287 if (p_ptr->guild && p_ptr->guild != id) continue;
2288
2289 p_ptr->guild = 0;
2290 clockin(i, 3);
2291
2292 if (p_ptr->conn == NOT_CONNECTED) continue;
2293
2294 /* Re-check house permissions, to display doors in correct colour */
2295 if (!p_ptr->wpos.wz) p_ptr->redraw |= PR_MAP;
2296
2297 msg_print(i, "\377oYour guild has been deleted from being leaderless for too long.");
2298 Send_guild(i, TRUE, FALSE);
2299 }
2300
2301 s_printf("GUILD_TIMEOUT: '%s' (%d)\n", guilds[id].name, id);
2302 guilds[id].members = 0;
2303 del_guild(id);
2304 }
2305
2306 /*
2307 * Delete a party. Was in party remove.
2308 *
2309 * Design improvement
2310 */
del_party(int id)2311 static void del_party(int id){
2312 int i;
2313 bool sent = FALSE;
2314 /* Remove the party altogether */
2315 kill_houses(id, OT_PARTY);
2316
2317 /* delete pending party notes */
2318 for (i = 0; i < MAX_PARTYNOTES; i++) {
2319 if (!strcmp(party_note_target[i], parties[id].name)) {
2320 strcpy(party_note_target[i], "");
2321 strcpy(party_note[i], "");
2322 break;
2323 }
2324 }
2325
2326 /* erase party bbs */
2327 for (i = 0; i < BBS_LINES; i++) strcpy(pbbs_line[id][i], "");
2328
2329 /* Set the number of people in this party to zero */
2330 parties[id].members = 0;
2331
2332 /* Remove everyone else */
2333 for (i = 1; i <= NumPlayers; i++) {
2334 /* Check if they are in here */
2335 if (player_in_party(id, i)) {
2336 if (!sent) { /* no need to spam this more than once */
2337 Send_party(i, FALSE, TRUE);
2338 sent = TRUE;
2339 }
2340 Players[i]->party = 0;
2341 clockin(i, 2);
2342
2343 /* Re-check house permissions, to display doors in correct colour */
2344 if (!Players[i]->wpos.wz) Players[i]->redraw |= PR_MAP;
2345
2346 if (parties[id].mode == PA_IRONTEAM)
2347 msg_print(i, "\374\377yYour iron team has been disbanded.");
2348 else
2349 msg_print(i, "\374\377yYour party has been disbanded.");
2350 }
2351 }
2352
2353 /* Set the creation time to "disbanded time" */
2354 parties[id].created = turn;
2355
2356 /* Empty the name */
2357 strcpy(parties[id].name, "");
2358 }
2359
2360 /*
2361 * Remove player from a guild
2362 */
guild_remove(int remover,cptr name)2363 int guild_remove(int remover, cptr name) {
2364 player_type *p_ptr;
2365 player_type *q_ptr = Players[remover];
2366 int guild_id = q_ptr->guild, Ind = 0;
2367
2368 if (!guild_id) {
2369 if (!is_admin(q_ptr)) {
2370 msg_print(remover, "\377yYou are not in a guild");
2371 return FALSE;
2372 }
2373 }
2374
2375 /* Make sure this is the owner */
2376 if (guilds[guild_id].master != q_ptr->id && !is_admin(q_ptr)) {
2377 /* Message */
2378 msg_print(remover, "\377yYou must be the owner to delete someone.");
2379
2380 /* Abort */
2381 return FALSE;
2382 }
2383
2384 Ind = name_lookup_loose(remover, name, FALSE, TRUE);
2385
2386 if (Ind <= 0) return FALSE;
2387
2388 if (Ind == remover) { /* remove oneself from guild - leave */
2389 guild_leave(remover, TRUE);
2390 return TRUE;
2391 }
2392
2393 p_ptr = Players[Ind];
2394
2395 if (!guild_id && is_admin(q_ptr)) guild_id = p_ptr->guild;
2396
2397 /* Make sure they were in the guild to begin with */
2398 if (guild_id != p_ptr->guild) {
2399 /* Message */
2400 msg_print(remover, "\377yYou can only delete guild members.");
2401
2402 /* Abort */
2403 return FALSE;
2404 }
2405
2406 /* Keep the guild, just lose a member */
2407 else {
2408 /* Lose a member */
2409 guilds[guild_id].members--;
2410
2411 /* Messages */
2412 msg_print(Ind, "\374\377yYou have been removed from the guild.");
2413 if (!is_admin(p_ptr)) guild_msg_format(guild_id, "\374\377y%s has been removed from the guild.", p_ptr->name);
2414
2415 /* Last member deleted? */
2416 if (guilds[guild_id].members == 0) {
2417 Send_guild(Ind, TRUE, TRUE);
2418 p_ptr->guild = 0;
2419 clockin(Ind, 3);
2420 del_guild(guild_id);
2421
2422 /* Re-check house permissions, to display doors in correct colour */
2423 if (!p_ptr->wpos.wz) p_ptr->redraw |= PR_MAP;
2424 } else {
2425 Send_guild(Ind, TRUE, FALSE);
2426 p_ptr->guild = 0;
2427 clockin(Ind, 3);
2428
2429 /* Re-check house permissions, to display doors in correct colour */
2430 if (!p_ptr->wpos.wz) p_ptr->redraw |= PR_MAP;
2431 }
2432 }
2433
2434 return TRUE;
2435 }
2436
2437 /*
2438 * Remove a person from a party.
2439 *
2440 * Removing the party owner destroys the party. - Not always anymore:
2441 * On RPG server, removing the owner will just promote someone else to owner,
2442 * provided (s)he's logged on. Better for RPG diving partys - C. Blue
2443 */
party_remove(int remover,cptr name)2444 int party_remove(int remover, cptr name) {
2445 player_type *p_ptr;
2446 player_type *q_ptr = Players[remover];
2447 int party_id = q_ptr->party, Ind = 0;
2448 int i, j;
2449
2450 /* Make sure this is the owner */
2451 if (!streq(parties[party_id].owner, q_ptr->name) && !is_admin(q_ptr)) {
2452 msg_print(remover, "\377yYou must be the owner to delete someone.");
2453
2454 /* Abort */
2455 return FALSE;
2456 }
2457
2458 Ind = name_lookup_loose(remover, name, FALSE, TRUE);
2459 if (Ind <= 0) return FALSE;
2460 p_ptr = Players[Ind];
2461
2462 if((!party_id || !streq(parties[party_id].owner, q_ptr->name)) && is_admin(q_ptr))
2463 party_id = p_ptr->party;
2464
2465 /* Make sure they were in the party to begin with */
2466 if (!player_in_party(party_id, Ind)) {
2467 msg_print(remover, "\377yYou can only delete party members.");
2468
2469 /* Abort */
2470 return FALSE;
2471 }
2472
2473 /* See if this is the owner we're deleting */
2474 if (remover == Ind
2475 #ifndef RPG_SERVER
2476 && in_irondeepdive(&p_ptr->wpos)
2477 #endif
2478 ) {
2479 /* Keep the party, just lose a member */
2480 for (i = 1; i <= NumPlayers; i++) {
2481 if (is_admin(Players[i])) continue;
2482 if (Players[i]->party == q_ptr->party && i != Ind) {
2483 strcpy(parties[party_id].owner, Players[i]->name);
2484 msg_print(i, "\374\377yYou are now the party owner!");
2485
2486 for (j = 1; j <= NumPlayers; j++)
2487 if (Players[j]->party == Players[i]->party && j != i)
2488 msg_format(j, "\374\377y%s is now the party owner.", Players[i]->name);
2489 break;
2490 }
2491 }
2492 /* no other player online who is in the same party and could overtake leadership? Then erase party! */
2493 if (i > NumPlayers) {
2494 del_party(party_id);
2495 return TRUE;
2496 }
2497 } else if (remover == Ind) {
2498 del_party(party_id);
2499 return TRUE;
2500 }
2501
2502 /* Lose a member */
2503 parties[party_id].members--;
2504
2505 /* Resend info */
2506 Send_party(Ind, TRUE, FALSE);
2507
2508 /* Set his party number back to "neutral" */
2509 p_ptr->party = 0;
2510 clockin(Ind, 2);
2511
2512 /* Re-check house permissions, to display doors in correct colour */
2513 if (!p_ptr->wpos.wz) p_ptr->redraw |= PR_MAP;
2514
2515 /* Messages */
2516 if (parties[party_id].mode == PA_IRONTEAM) {
2517 msg_print(Ind, "\374\377yYou have been removed from your iron team.");
2518 if (!is_admin(p_ptr)) party_msg_format(party_id, "\374\377y%s has been removed from the iron team.", p_ptr->name);
2519 } else {
2520 msg_print(Ind, "\374\377yYou have been removed from your party.");
2521 if (!is_admin(p_ptr)) party_msg_format(party_id, "\374\377y%s has been removed from the party.", p_ptr->name);
2522 }
2523
2524 return TRUE;
2525 }
2526
guild_leave(int Ind,bool voluntarily)2527 void guild_leave(int Ind, bool voluntarily) {
2528 player_type *p_ptr = Players[Ind];
2529 int guild_id = p_ptr->guild, i;
2530
2531 /* Make sure he belongs to a guild */
2532 if (!guild_id) {
2533 msg_print(Ind, "\377yYou don't belong to a guild.");
2534 return;
2535 }
2536
2537 #ifdef GUILD_ADDERS_LIST
2538 if ((p_ptr->guild_flags & PGF_ADDER))
2539 for (i = 0; i < 5; i++) if (streq(guilds[p_ptr->guild].adder[i], p_ptr->name)) {
2540 guilds[p_ptr->guild].adder[i][0] = '\0';
2541 break;
2542 }
2543 #endif
2544
2545 /* Lose a member */
2546 guilds[guild_id].members--;
2547
2548 /* Inform people */
2549 if (voluntarily) {
2550 msg_print(Ind, "\374\377yYou have left your guild.");
2551 if (!is_admin(p_ptr)) guild_msg_format(guild_id, "\374\377y%s has left the guild.", p_ptr->name);
2552 } else {
2553 msg_print(Ind, "\374\377yYou have been removed from your guild.");
2554 if (!is_admin(p_ptr)) guild_msg_format(guild_id, "\374\377y%s has been removed from the guild.", p_ptr->name);
2555 }
2556
2557 /* If he's the guildmaster, set master to zero */
2558 if (p_ptr->id == guilds[guild_id].master) {
2559 guild_msg(guild_id, "\374\377yThe guild is now leaderless!");
2560 guilds[guild_id].master = 0;
2561
2562 /* set guild hall to 'suspended' */
2563 if ((i = guilds[guild_id].h_idx)) {
2564 houses[i - 1].flags |= HF_GUILD_SUS;
2565 fill_house(&houses[i - 1], FILL_GUILD_SUS, NULL);
2566 }
2567 }
2568
2569 /* Resend info */
2570 Send_guild(Ind, TRUE, FALSE);
2571
2572 /* Set him back to "neutral" */
2573 p_ptr->guild = 0;
2574 clockin(Ind, 3);
2575
2576 /* Re-check house permissions, to display doors in correct colour */
2577 if (!p_ptr->wpos.wz) p_ptr->redraw |= PR_MAP;
2578
2579 /* Last member deleted? */
2580 if (guilds[guild_id].members == 0)
2581 del_guild(guild_id);
2582 }
2583
2584 /*
2585 * A player wants to leave a party.
2586 */
party_leave(int Ind,bool voluntarily)2587 void party_leave(int Ind, bool voluntarily) {
2588 player_type *p_ptr = Players[Ind];
2589 int party_id = p_ptr->party;
2590
2591 /* Make sure he belongs to a party */
2592 if (!party_id) {
2593 msg_print(Ind, "\377yYou don't belong to a party.");
2594 return;
2595 }
2596
2597 /* If he's the owner, use the other function */
2598 if (streq(p_ptr->name, parties[party_id].owner)) {
2599 /* Call party_remove */
2600 party_remove(Ind, p_ptr->name);
2601 return;
2602 }
2603
2604 /* Lose a member */
2605 parties[party_id].members--;
2606
2607 /* Resend info */
2608 Send_party(Ind, TRUE, FALSE);
2609
2610 /* Set him back to "neutral" */
2611 p_ptr->party = 0;
2612 clockin(Ind, 2);
2613
2614 /* Re-check house permissions, to display doors in correct colour */
2615 if (!p_ptr->wpos.wz) p_ptr->redraw |= PR_MAP;
2616
2617 /* Inform people */
2618 if (voluntarily) {
2619 if (parties[party_id].mode == PA_IRONTEAM) {
2620 msg_print(Ind, "\374\377yYou have left your iron team.");
2621 if (!is_admin(p_ptr)) party_msg_format(party_id, "\374\377y%s has left the iron team.", p_ptr->name);
2622 } else {
2623 msg_print(Ind, "\374\377yYou have left your party.");
2624 if (!is_admin(p_ptr)) party_msg_format(party_id, "\374\377y%s has left the party.", p_ptr->name);
2625 }
2626 } else {
2627 if (parties[party_id].mode == PA_IRONTEAM) {
2628 msg_print(Ind, "\374\377yYou have been removed from your iron team.");
2629 if (!is_admin(p_ptr)) party_msg_format(party_id, "\374\377y%s has been removed from the iron team.", p_ptr->name);
2630 } else {
2631 msg_print(Ind, "\374\377yYou have been removed from your party.");
2632 if (!is_admin(p_ptr)) party_msg_format(party_id, "\374\377y%s has been removed from the party.", p_ptr->name);
2633 }
2634 }
2635 }
2636
guild_rename(int Ind,char * new_name)2637 bool guild_rename(int Ind, char *new_name) {
2638 player_type *p_ptr = Players[Ind];
2639 int i, gid = p_ptr->guild;
2640
2641 if (!gid) {
2642 msg_print(Ind, "\377yYou are not in a guild");
2643 return FALSE;
2644 }
2645
2646 /* Leaderless guilds do not allow addition of new members */
2647 if (p_ptr->id != guilds[gid].master) {
2648 msg_print(Ind, "\377yOnly the guild master can rename a guild.");
2649 return FALSE;
2650 }
2651
2652 if (!strcmp(guilds[gid].name, new_name)) {
2653 msg_print(Ind, "\377yNew guild name must be different from its current name.");
2654 return FALSE;
2655 }
2656
2657 if (!guild_name_legal(Ind, new_name)) return FALSE;
2658
2659 /* This could probably be improved. */
2660 if (p_ptr->au < GUILD_PRICE) {
2661 if (GUILD_PRICE >= 1000000 && ((int)(GUILD_PRICE / 1000000)) * 1000000 == GUILD_PRICE)
2662 msg_format(Ind, "\377yYou need %d,000,000 gold pieces to rename a guild.", GUILD_PRICE / 1000000);
2663 else if (GUILD_PRICE >= 1000 && ((int)(GUILD_PRICE / 1000)) * 1000 == GUILD_PRICE)
2664 msg_format(Ind, "\377yYou need %d,000 gold pieces to rename a guild.", GUILD_PRICE / 1000);
2665 else
2666 msg_format(Ind, "\377yYou need %d gold pieces to rename a guild.", GUILD_PRICE);
2667 return FALSE;
2668 }
2669
2670
2671 p_ptr->au -= GUILD_PRICE;
2672 p_ptr->redraw |= PR_GOLD;
2673
2674 for (i = 0; i < MAX_GUILDNOTES; i++)
2675 if (!strcmp(guild_note_target[i], guilds[gid].name))
2676 strcpy(guild_note_target[i], new_name);
2677
2678 msg_broadcast_format(0, "\374\377yThe guild '\377%c%s\377y' changed name to '\377%c%s\377y'.",
2679 COLOUR_CHAT_GUILD, guilds[gid].name, COLOUR_CHAT_GUILD, new_name);
2680
2681 l_printf("%s \\{yThe guild '%s' changed name to '%s'\n",
2682 showdate(), guilds[gid].name, new_name);
2683
2684 strcpy(guilds[gid].name, new_name);
2685 return TRUE;
2686 }
2687
2688 /*
2689 * Send a message to everyone in a party.
2690 */
guild_msg(int guild_id,cptr msg)2691 void guild_msg(int guild_id, cptr msg) {
2692 int i;
2693
2694 /* Check for this guy */
2695 for (i = 1; i <= NumPlayers; i++) {
2696 if (Players[i]->conn == NOT_CONNECTED)
2697 continue;
2698
2699 /* Check this guy */
2700 if (guild_id == Players[i]->guild)
2701 msg_print(i, msg);
2702 }
2703 }
2704
2705
2706 /*
2707 * Send a formatted message to a guild.
2708 */
guild_msg_format(int guild_id,cptr fmt,...)2709 void guild_msg_format(int guild_id, cptr fmt, ...) {
2710 va_list vp;
2711 char buf[1024];
2712
2713 /* Begin the Varargs Stuff */
2714 va_start(vp, fmt);
2715
2716 /* Format the args, save the length */
2717 (void)vstrnfmt(buf, 1024, fmt, vp);
2718
2719 /* End the Varargs Stuff */
2720 va_end(vp);
2721
2722 /* Display */
2723 guild_msg(guild_id, buf);
2724 }
2725
2726 /*
2727 * Send a message to everyone in a party.
2728 */
party_msg(int party_id,cptr msg)2729 void party_msg(int party_id, cptr msg) {
2730 int i;
2731
2732 /* Check for this guy */
2733 for (i = 1; i <= NumPlayers; i++) {
2734 if (Players[i]->conn == NOT_CONNECTED)
2735 continue;
2736
2737 /* Check this guy */
2738 if (player_in_party(party_id, i))
2739 msg_print(i, msg);
2740 }
2741 }
2742
2743 /*
2744 * Send a formatted message to a party.
2745 */
party_msg_format(int party_id,cptr fmt,...)2746 void party_msg_format(int party_id, cptr fmt, ...) {
2747 va_list vp;
2748 char buf[1024];
2749
2750 /* Begin the Varargs Stuff */
2751 va_start(vp, fmt);
2752
2753 /* Format the args, save the length */
2754 (void)vstrnfmt(buf, 1024, fmt, vp);
2755
2756 /* End the Varargs Stuff */
2757 va_end(vp);
2758
2759 /* Display */
2760 party_msg(party_id, buf);
2761 }
2762
2763 /*
2764 * Send a message to everyone in a party, considering ignorance.
2765 */
party_msg_ignoring(int sender,int party_id,cptr msg)2766 static void party_msg_ignoring(int sender, int party_id, cptr msg) {
2767 int i;
2768
2769 /* Check for this guy */
2770 for (i = 1; i <= NumPlayers; i++) {
2771 if (Players[i]->conn == NOT_CONNECTED)
2772 continue;
2773
2774 if (check_ignore(i, sender))
2775 continue;
2776
2777 /* Check this guy */
2778 if (player_in_party(party_id, i))
2779 msg_print(i, msg);
2780 }
2781 }
2782
2783 /*
2784 * Send a formatted message to a party.
2785 */
party_msg_format_ignoring(int sender,int party_id,cptr fmt,...)2786 void party_msg_format_ignoring(int sender, int party_id, cptr fmt, ...) {
2787 va_list vp;
2788 char buf[1024];
2789
2790 /* Begin the Varargs Stuff */
2791 va_start(vp, fmt);
2792
2793 /* Format the args, save the length */
2794 (void)vstrnfmt(buf, 1024, fmt, vp);
2795
2796 /* End the Varargs Stuff */
2797 va_end(vp);
2798
2799 /* Display */
2800 party_msg_ignoring(sender, party_id, buf);
2801 }
2802 /*
2803 * Split some experience among party members.
2804 *
2805 * This should ONLY be used while killing monsters. The amount
2806 * should be the monster base experience times the monster level.
2807 *
2808 * This algorithm may need some work.... However, it does have some nifty
2809 * features, such as:
2810 *
2811 * 1) A party with just one member functions identically as before.
2812 *
2813 * 2) A party with two equally-levelled members functions such that each
2814 * member gets half as much experience as he would have by killing the monster
2815 * by himself.
2816 *
2817 * 3) Higher-leveled members of a party get higher percentages of the
2818 * experience.
2819 */
2820
2821 /* The XP distribution was too unfair for low level characters,
2822 it made partying a real pain. I am changing it so that if the players
2823 have a difference in level of less than 5 than there is no difference
2824 in XP distribution.
2825
2826 I am also changing it so it divides by each players level, AFTER
2827 it has been given to them.
2828
2829 UPDATE: it appears that it may be giving too much XP to the low lvl chars,
2830 but I have been too lazy to change it... however, this doesnt appear to be being
2831 abused much, and the new system is regardless much nicer than the old one.
2832
2833 -APD-
2834 */
2835
2836 /* This helper function checks if two players are allowed to share experience,
2837 based on a) level difference and b) royal status.
2838 Note a quirk here:
2839 Winners and non-winners don't share exp, but winners and once-winners do. */
players_in_level(int Ind,int Ind2)2840 static bool players_in_level(int Ind, int Ind2) {
2841 player_type *p_ptr = Players[Ind], *p2_ptr = Players[Ind2];
2842
2843 /* Check for max allowed level difference.
2844 (p_ptr->lev would be more logical but harsh) */
2845 if (p_ptr->total_winner && p2_ptr->total_winner) {
2846 if (ABS(p_ptr->max_lev - p2_ptr->max_lev) > MAX_KING_PARTY_LEVEL_DIFF) return FALSE;
2847 return TRUE;
2848 }
2849 if (ABS(p_ptr->max_lev - p2_ptr->max_lev) > MAX_PARTY_LEVEL_DIFF) return FALSE;
2850
2851 /* Winners and non-winners don't share experience!
2852 This is actually important because winners get special features
2853 such as super heavy armour and royal stances which are aimed at
2854 nether realm, and are not meant to influence pre-king gameplay. */
2855 if ((p_ptr->total_winner && !(p2_ptr->total_winner || p2_ptr->once_winner)) ||
2856 (p2_ptr->total_winner && !(p_ptr->total_winner || p_ptr->once_winner)))
2857 return FALSE;
2858
2859 return TRUE;
2860 }
2861
2862 /* Note: 'amount' is actually multiplied by 100 for extra decimal digits if we're very low level (Training Tower exp'ing).
2863 The same is done to 'base_amount' so it's *100 too. */
2864 #define PERFORM_IRON_TEAM_CHECKS
party_gain_exp(int Ind,int party_id,s64b amount,s64b base_amount,int henc,int henc_top)2865 void party_gain_exp(int Ind, int party_id, s64b amount, s64b base_amount, int henc, int henc_top) {
2866 player_type *p_ptr = Players[Ind], *q_ptr;
2867 int PInd[NumPlayers], pi = 0, Ind2; /* local working copy of player indices, for efficiency hopefully */
2868 int i, eff_henc, hlev = 0, htop = 0;
2869 #ifdef ANTI_MAXPLV_EXPLOIT
2870 #ifdef ANTI_MAXPLV_EXPLOIT_SOFTEXP
2871 int diff;
2872 #endif
2873 #endif
2874 struct worldpos *wpos = &p_ptr->wpos;
2875 s64b new_exp, new_exp_frac, average_lev = 0, num_members = 0, new_amount, new_boosted_amount;
2876 s64b modified_level;
2877 int dlev = getlevel(wpos);
2878 bool not_in_iddc = !in_irondeepdive(wpos);
2879 #ifdef PERFORM_IRON_TEAM_CHECKS
2880 bool iron = (parties[party_id].mode == PA_IRONTEAM);
2881 int iron_team_members_here = 0;//temporarily here
2882 #endif
2883
2884 #ifdef ALLOW_NR_CROSS_PARTIES
2885 /* quick and dirty anti-cheeze (for if NR surface already allows partying up) */
2886 if (at_netherrealm(wpos) && !wpos->wz) return;
2887 #endif
2888
2889 /* Calculate the average level and iron team presence */
2890 for (i = 1; i <= NumPlayers; i++) {
2891 q_ptr = Players[i];
2892
2893 if (q_ptr->conn == NOT_CONNECTED) continue;
2894
2895 /* Check for his existence in the party */
2896 if (!(player_in_party(party_id, i) && (inarea(&q_ptr->wpos, wpos)))) continue;
2897
2898 /* Create map for efficiency */
2899 PInd[pi] = i;
2900 pi++;
2901
2902 #ifdef PERFORM_IRON_TEAM_CHECKS
2903 /* will be moved to gain_exp() if decrease of party.experience is implemented, nasty though.
2904 until then, iron teams will just have to be re-formed as normal parties if a member falls
2905 behind too much in terms of exp and hence blocks the whole team from gaining exp. */
2906
2907 /* Iron Teams only get exp if the whole team is on the same floor! */
2908 if (iron &&
2909 /* note: this line means that iron teams must not add
2910 admins, or the members won't gain exp anymore */
2911 !is_admin(q_ptr))
2912 iron_team_members_here++;
2913 #endif
2914
2915 /* For exp-splitting: Increase the "divisor" */
2916 average_lev += q_ptr->lev;
2917 num_members++;
2918
2919 /* Floor depth vs player level for efficient exp'ing always uses the highest-level team mate. */
2920 if (q_ptr->lev > hlev) hlev = q_ptr->lev;
2921
2922 /* For keeping track of max-plv-exploits in case henc_strictness is actually disabled. */
2923 if ((q_ptr->max_lev + q_ptr->max_plv) / 2 > htop) htop = (q_ptr->max_lev + q_ptr->max_plv) / 2;
2924 }
2925
2926 #ifdef PERFORM_IRON_TEAM_CHECKS
2927 /* Iron team calc: nobody in an iron team can gain exp if not all members are present and in the same place! */
2928 if (iron && iron_team_members_here != parties[party_id].members) return;
2929 #endif
2930
2931 /* Exp share calc: */
2932 average_lev /= num_members;
2933
2934 /* Replace top max_plv value taken from any party member on the floor
2935 simply by the henc_top value, if we use the 'henc' feature: */
2936 if (cfg.henc_strictness) htop = henc_top;
2937 /* paranoia (m_ptr->henc shouldn't actually get set if
2938 henc_strictness is disabled, but better safe than sorry: */
2939 else henc = 0;
2940
2941 /* Now, distribute the experience */
2942 for (i = 0; i < pi; i++) {
2943 Ind2 = PInd[i];
2944 q_ptr = Players[Ind2];
2945
2946 /* players who exceed the restrictions to share exp with us don't get any,
2947 but still take share toll by increasing the divisor in the loop above. */
2948 if (q_ptr->ghost || !players_in_level(Ind, Ind2)) continue;
2949
2950 /* HEnc-checks -
2951 in case henc_strictness is actually disabled, we abuse 'henc' here
2952 by calculating with max_plv values instead, using them as pseudo-henc values. */
2953 if (!q_ptr->total_winner) {
2954 eff_henc = henc; //(0 if henc_strictness is disabled)
2955
2956 #ifdef ANTI_MAXPLV_EXPLOIT
2957 /* Don't allow cheap support from super-high level characters whose max_plv
2958 is considerably higher than their normal level, resulting in them being
2959 way more powerful than usual (because they have more skill points allocated). */
2960
2961 /* Two cases:
2962 1) We're not the killer and the killer has too high max_plv/supp_top (= henc_top),
2963 2) We're the killer but we have too high supp_top
2964 (and optionally the monster maybe also has too high henc_top from the killer,
2965 but this can't be implemented easily because the henc_top might be ourself! oops!).
2966 However, the problem is that we might actually be a 10(40) char and someone else too;
2967 now we cannot distinguish anymore, and we should definitely get full exp when this
2968 team of two kills a monster, right?
2969 So there is probably no easy way except for disabling ANTI_MAXPLV_EXPLOIT completely :/. */
2970
2971 /* ..instead of the above stuff, here a new, simpler attempt:
2972 If there's anyone whose max_plv is > ours and exceeds the allowed level diff,
2973 henc penalty (AMES-LEV) or exp penalty (AMES-EXP) applies to us. */
2974 #ifdef ANTI_MAXPLV_EXPLOIT_SOFTLEV
2975 /* avg lev compariso, weighing a bit less than max_lev comparison */
2976 if (htop - (q_ptr->max_lev + q_ptr->max_plv) / 2 > ((MAX_PARTY_LEVEL_DIFF + 1) * 3) / 2) continue; /* zonk, bam */
2977 #else
2978 #ifdef ANTI_MAXPLV_EXPLOIT_SOFTEXP
2979 diff = htop - (q_ptr->max_lev + q_ptr->max_plv) / 2 - (MAX_PARTY_LEVEL_DIFF + 1);
2980 if (diff > 0) new_amount = (new_amount * 4) / (4 + diff);
2981 #else
2982 /* avg lev comparison in the same way as max_lev comparison */
2983 if (htop - (q_ptr->max_lev + q_ptr->max_plv) / 2 > MAX_PARTY_LEVEL_DIFF + 1) continue; /* zonk, bam */
2984 #endif
2985 #endif
2986 #endif
2987
2988 /* Don't allow cheap support from super-high level characters */
2989 if (eff_henc - q_ptr->max_lev > MAX_PARTY_LEVEL_DIFF + 1) continue; /* zonk */
2990 }
2991
2992 /* Calculate this guy's experience gain */
2993 new_amount = amount;
2994
2995 /* dungeon floor specific reduction if player dives too shallow */
2996 if (not_in_iddc) new_amount = det_exp_level(new_amount, hlev, dlev);
2997
2998 /* Never get too much exp off a monster
2999 due to high level difference,
3000 make exception for low exp boosts like "holy jackal" */
3001 if ((new_amount > base_amount * 4 * q_ptr->lev) && (new_amount > 200 * q_ptr->lev))
3002 new_amount = base_amount * 4 * q_ptr->lev; /* new_amount gets divided by q_ptr->lev later - mikaelh */
3003
3004 /* Especially high party xp boost in IDDC, or people might mostly prefer to solo to 2k */
3005 if (!not_in_iddc)
3006 new_boosted_amount = (new_amount * (IDDC_PARTY_XP_BOOST + 1)) / (num_members + IDDC_PARTY_XP_BOOST);
3007 /* Some bonus is applied to encourage partying - Jir - */
3008 else
3009 new_boosted_amount = (new_amount * (PARTY_XP_BOOST + 1)) / (num_members + PARTY_XP_BOOST);
3010
3011 /* get less or more exp depending on if we're above or below the party average */
3012 if (q_ptr->lev < average_lev) { // below average
3013 if ((average_lev - q_ptr->lev) > 2)
3014 modified_level = q_ptr->lev + 2;
3015 else modified_level = average_lev;
3016 } else {
3017 if ((q_ptr->lev - average_lev) > 2)
3018 modified_level = q_ptr->lev - 2;
3019 else modified_level = average_lev;
3020 }
3021 new_amount = (new_boosted_amount * modified_level) / average_lev;
3022
3023 /* Calculate integer and fractional exp gain numbers */
3024 new_exp = new_amount / q_ptr->lev / 100;
3025 new_exp_frac = ((new_amount - new_exp * q_ptr->lev * 100)
3026 * 100L) / q_ptr->lev + q_ptr->exp_frac;
3027
3028 /* Keep track of experience */
3029 if (new_exp_frac >= 10000L) {
3030 new_exp++;
3031 q_ptr->exp_frac = new_exp_frac - 10000L;
3032 } else {
3033 q_ptr->exp_frac = new_exp_frac;
3034 q_ptr->redraw |= PR_EXP; //EXP_BAR_FINESCALE
3035 }
3036
3037 /* Gain experience */
3038 if (new_exp) gain_exp(Ind2, new_exp);
3039 else if (!q_ptr->warning_fracexp && base_amount) {
3040 msg_print(Ind2, "\374\377ySome monsters give less than 1 experience point, but you still gain a bit!");
3041 s_printf("warning_fracexp: %s\n", q_ptr->name);
3042 q_ptr->warning_fracexp = 1;
3043 }
3044 }
3045 }
3046
3047 /*
3048 * Add a player to another player's list of hostilities.
3049 */
add_hostility(int Ind,cptr name,bool initiator)3050 bool add_hostility(int Ind, cptr name, bool initiator) {
3051 player_type *p_ptr = Players[Ind], *q_ptr;
3052 hostile_type *h_ptr;
3053 int i;
3054 bool bb = FALSE;
3055
3056 #if 0 /* too risky? (exploitable) */
3057 i = name_lookup_loose(Ind, name, TRUE, TRUE);
3058 #else
3059 i = name_lookup(Ind, name, TRUE, TRUE);
3060 #endif
3061
3062 if (!i) {
3063 s_printf("ADD_HOSTILITY: not found.\n");
3064 return FALSE;
3065 }
3066
3067 /* Check for sillyness */
3068 if (i == Ind) {
3069 /* Message */
3070 msg_print(Ind, "\377yYou cannot be hostile toward yourself.");
3071 return FALSE;
3072 }
3073
3074 /* If it's a blood bond, players may fight in safe zones and with party members np */
3075 if (i > 0) bb = check_blood_bond(Ind, i);
3076 #ifdef NO_PK
3077 if (i < 0 || !bb) return FALSE;
3078 #endif
3079
3080 /* log any attempts */
3081 if (initiator) {
3082 /* paranoia? shouldn't i always be > 0 here? */
3083 if (i > 0) s_printf("HOSTILITY: %s attempts to declare war on %s.\n", p_ptr->name, Players[i]->name);
3084 else s_printf("HOSTILITY: %s attempts to declare war (%d).\n", p_ptr->name, i);
3085 }
3086 /* prevent log spam from stormbringer */
3087 else if (!istown(&p_ptr->wpos)) {
3088 /* paranoia? shouldn't i always be > 0 here? */
3089 if (i > 0) s_printf("HOSTILITY: %s attempts to declare war in return on %s.\n", p_ptr->name, Players[i]->name);
3090 else s_printf("HOSTILITY: %s attempts to declare war in return (%d).\n", p_ptr->name, i);
3091 }
3092
3093 #ifndef KURZEL_PK
3094 if (cfg.use_pk_rules == PK_RULES_DECLARE) {
3095 if(!(p_ptr->pkill & PKILL_KILLER) && (p_ptr->pvpexception != 1)){
3096 msg_print(Ind, "\377yYou may not be hostile to other players.");
3097 return FALSE;
3098 }
3099 }
3100 #endif
3101
3102 if (cfg.use_pk_rules == PK_RULES_NEVER && (p_ptr->pvpexception != 1)) {
3103 msg_print(Ind, "\377yYou may not be hostile to other players.");
3104 return FALSE;
3105 }
3106
3107 /* Non-validated players may not pkill */
3108 if (p_ptr->inval) {
3109 msg_print(Ind, "Your account needs to be validated in order to fight other players.");
3110 return FALSE;
3111 }
3112
3113 #if 1
3114 if (!bb && initiator && !istown(&p_ptr->wpos)) {
3115 msg_print(Ind, "\377yYou need to be in town to declare war.");
3116 return FALSE;
3117 }
3118 #endif
3119
3120 if (p_ptr->pvpexception == 2) return FALSE;
3121 if (p_ptr->pvpexception == 3) {
3122 p_ptr->chp = -3;
3123 strcpy(p_ptr->died_from, "adrenaline poisoning");
3124 p_ptr->deathblow = 0;
3125 p_ptr->energy = -666;
3126 // p_ptr->death = TRUE;
3127 player_death(Ind);
3128 return FALSE;
3129 }
3130
3131 if (i > 0) {
3132 q_ptr = Players[i];
3133
3134 /* Make sure players aren't in the same party */
3135 if (!bb && p_ptr->party && player_in_party(p_ptr->party, i)) {
3136 msg_format(Ind, "\377y%^s is in your party!", q_ptr->name);
3137 return FALSE;
3138 }
3139
3140 /* Ensure we don't add the same player twice */
3141 for (h_ptr = p_ptr->hostile; h_ptr; h_ptr = h_ptr->next) {
3142 /* Check this ID */
3143 if (h_ptr->id == q_ptr->id) {
3144 msg_format(Ind, "\377yYou are already hostile toward %s.", q_ptr->name);
3145 return FALSE;
3146 }
3147 }
3148
3149 /* Create a new hostility node */
3150 MAKE(h_ptr, hostile_type);
3151
3152 /* Set ID in node */
3153 h_ptr->id = q_ptr->id;
3154
3155 /* Put this node at the beginning of the list */
3156 h_ptr->next = p_ptr->hostile;
3157 p_ptr->hostile = h_ptr;
3158
3159 #if 0
3160 /* prevent anti-tele amulet instant recall into barrow-downs and crap */
3161 if (p_ptr->word_recall) //&& istown(&p_ptr->wpos)
3162 set_recall_timer(Ind, 0);
3163 #endif
3164
3165 /* Message */
3166 if (bb) {
3167 msg_format(Ind, "\377yYou are now hostile toward %s.", q_ptr->name);
3168 msg_format(i, "\377y* Player %s declared war on you! *", p_ptr->name);
3169 } else {
3170 msg_format(Ind, "\374\377RYou are now hostile toward %s.", q_ptr->name);
3171 msg_format(i, "\374\377R* Player %s declared war on you! *", p_ptr->name);
3172 }
3173
3174 /* Success */
3175 return TRUE;
3176 } else {
3177 /* Tweak - inverse i once more */
3178 i = 0 - i;
3179
3180 /* Ensure we don't add the same party twice */
3181 for (h_ptr = p_ptr->hostile; h_ptr; h_ptr = h_ptr->next) {
3182 /* Check this ID */
3183 if (h_ptr->id == 0 - i) {
3184 msg_format(Ind, "\377yYou are already hostile toward party '%s'.", parties[i].name);
3185 return FALSE;
3186 }
3187 }
3188
3189 /* Create a new hostility node */
3190 MAKE(h_ptr, hostile_type);
3191
3192 /* Set ID in node */
3193 h_ptr->id = 0 - i;
3194
3195 /* Put this node at the beginning of the list */
3196 h_ptr->next = p_ptr->hostile;
3197 p_ptr->hostile = h_ptr;
3198
3199 #if 0
3200 /* prevent anti-tele amulet instant recall into barrow-downs and crap */
3201 if (p_ptr->word_recall) //&& istown(&p_ptr->wpos)
3202 set_recall_timer(Ind, 0);
3203 #endif
3204
3205 /* Message */
3206 msg_format(Ind, "\377RYou are now hostile toward party '%s'.", parties[i].name);
3207 msg_broadcast_format(Ind, "\374\377R* %s declares war on party '%s'. *", p_ptr->name, parties[i].name);
3208
3209 /* Success */
3210 return TRUE;
3211 }
3212 }
3213
3214 /*
3215 * Remove an entry from a player's list of hostilities
3216 */
remove_hostility(int Ind,cptr name)3217 bool remove_hostility(int Ind, cptr name) {
3218 player_type *p_ptr = Players[Ind];
3219 hostile_type *h_ptr, *i_ptr;
3220 cptr p, q = NULL;
3221 int i = name_lookup_loose(Ind, name, TRUE, TRUE);
3222
3223 if (!i) return FALSE;
3224
3225 /* Check for another silliness */
3226 if (i == Ind) {
3227 /* Message */
3228 msg_print(Ind, "\377GYou are not hostile toward yourself.");
3229
3230 return FALSE;
3231 }
3232
3233 #ifdef NO_PK
3234 /* If it's a blood bond, players may fight in safe zones and with party members np */
3235 if (i < 0) return FALSE;
3236 else if (!check_blood_bond(Ind, i)) return FALSE;
3237 #endif
3238
3239 /* Forge name */
3240 if (i > 0) q = Players[i]->name;
3241
3242 /* Initialize lock-step */
3243 i_ptr = NULL;
3244
3245 /* Search entries */
3246 for (h_ptr = p_ptr->hostile; h_ptr; i_ptr = h_ptr, h_ptr = h_ptr->next) {
3247 /* Lookup name of this entry */
3248 if (h_ptr->id > 0) {
3249 /* Efficiency */
3250 if (i < 0) continue;
3251
3252 /* Look up name */
3253 p = lookup_player_name(h_ptr->id);
3254
3255 /* Check player name */
3256 // if (p && (streq(p, q) || streq(p, name)))
3257 if (p && streq(p, q)) {
3258 /* Delete this entry */
3259 if (i_ptr) {
3260 /* Skip over */
3261 i_ptr->next = h_ptr->next;
3262 } else {
3263 /* Adjust beginning of list */
3264 p_ptr->hostile = h_ptr->next;
3265 }
3266
3267 /* Message */
3268 msg_format(Ind, "\377GNo longer hostile toward %s.", p);
3269 msg_format(i, "\374\377G%s is no longer hostile toward you.", p_ptr->name);
3270
3271 /* Delete node */
3272 KILL(h_ptr, hostile_type);
3273
3274 /* Success */
3275 return TRUE;
3276 }
3277 } else {
3278 /* Efficiency */
3279 if (i >= 0) continue;
3280
3281 /* Assume this is a party */
3282 // if (streq(parties[0 - h_ptr->id].name, q))
3283 if (i == h_ptr->id) {
3284 /* Delete this entry */
3285 if (i_ptr) {
3286 /* Skip over */
3287 i_ptr->next = h_ptr->next;
3288 } else {
3289 /* Adjust beginning of list */
3290 p_ptr->hostile = h_ptr->next;
3291 }
3292
3293 /* Message */
3294 msg_format(Ind, "\377GNo longer hostile toward party '%s'.", parties[0 - i].name);
3295 msg_broadcast_format(Ind, "\374\377G%s is no longer hostile toward party '%s'.", p_ptr->name, parties[0 - i].name);
3296
3297 /* Delete node */
3298 KILL(h_ptr, hostile_type);
3299
3300 /* Success */
3301 return TRUE;
3302 }
3303 }
3304 }
3305 return(FALSE);
3306 }
3307
3308 /*
3309 * Check if one player is hostile toward the other
3310 */
check_hostile(int attacker,int target)3311 bool check_hostile(int attacker, int target) {
3312 player_type *p_ptr = Players[attacker], *q_ptr = Players[target];
3313 hostile_type *h_ptr;
3314
3315 /* Highlander Tournament is a fight to death */
3316 if ((p_ptr->global_event_temp & PEVF_AUTOPVP_00) &&
3317 (q_ptr->global_event_temp & PEVF_AUTOPVP_00)) {
3318 return(TRUE);
3319 }
3320
3321 /* towns are safe-zones for ALL players - except for blood bond */
3322 if ((istown(&q_ptr->wpos) || istown(&p_ptr->wpos)) &&
3323 (!check_blood_bond(attacker, target) ||
3324 p_ptr->afk || q_ptr->afk))
3325 return(FALSE);
3326 /* outside of towns, PvP-mode means auto-hostility! */
3327 else if (((q_ptr->mode & MODE_PVP) && (p_ptr->mode & MODE_PVP))
3328 #ifdef KURZEL_PK
3329 || (((q_ptr->pkill & PKILL_SET) && (p_ptr->pkill & PKILL_SET)) && ((!p_ptr->party && !q_ptr->party) || !(q_ptr->party == p_ptr->party)))
3330 #endif
3331 )
3332 return(TRUE);
3333
3334 /* Scan list */
3335 for (h_ptr = p_ptr->hostile; h_ptr; h_ptr = h_ptr->next) {
3336 /* Check ID */
3337 if (h_ptr->id > 0) {
3338 /* Identical ID's yield hostility */
3339 if (h_ptr->id == q_ptr->id)
3340 return TRUE;
3341 } else {
3342 /* Check if target belongs to hostile party */
3343 if (q_ptr->party == 0 - h_ptr->id)
3344 return TRUE;
3345 }
3346 }
3347
3348 /* Not hostile */
3349 return FALSE;
3350 }
3351
3352
3353 /*
3354 * Add/remove a player to/from another player's list of ignorance.
3355 * These functions should be common with hostilityes in the future. -Jir-
3356 */
add_ignore(int Ind,cptr name)3357 bool add_ignore(int Ind, cptr name) {
3358 player_type *p_ptr = Players[Ind], *q_ptr;
3359 hostile_type *h_ptr, *i_ptr;
3360 int i;
3361 int snum = 0;
3362 cptr p, q = NULL;
3363 char search[80], *pname;
3364
3365 /* Check for silliness */
3366 if (!name) {
3367 msg_print(Ind, "Usage: /ignore foobar");
3368
3369 return FALSE;
3370 }
3371
3372 #ifdef TOMENET_WORLDS
3373 if ((pname = strchr(name, '@'))){
3374 struct remote_ignore *curr, *prev = NULL;
3375 struct rplist *w_player;
3376 strncpy(search, name, pname - name);
3377 search[pname - name] = '\0';
3378 snum = atoi(pname + 1);
3379 w_player = world_find_player(search, snum);
3380 if (!w_player) {
3381 msg_format(Ind, "Could not find %s in the world", search);
3382 return(FALSE);
3383 }
3384 curr = p_ptr->w_ignore;
3385 while (curr) {
3386 if (curr->serverid == w_player->server && curr->id == w_player->id) break;
3387 prev = curr;
3388 curr = curr->next;
3389 }
3390 if (!curr) {
3391 msg_format(Ind, "Ignoring %s across the world", w_player->name);
3392 curr = NEW(struct remote_ignore);
3393 curr->serverid = w_player->server;
3394 curr->id = w_player->id;
3395 curr->next = NULL;
3396 if (prev)
3397 prev->next = curr;
3398 else
3399 p_ptr->w_ignore = curr;
3400 }
3401 else {
3402 msg_format(Ind, "Hearing %s from across the world", search);
3403 if (!prev)
3404 p_ptr->w_ignore = curr->next;
3405 else
3406 prev->next = curr->next;
3407 FREE(curr, struct remote_ignore);
3408 }
3409 return(TRUE);
3410 }
3411 #endif
3412
3413 i = name_lookup_loose(Ind, name, TRUE, TRUE);
3414 if (!i) return FALSE;
3415
3416 /* Check for another silliness */
3417 if (i == Ind) {
3418 /* Message */
3419 msg_print(Ind, "You cannot ignore yourself.");
3420
3421 return FALSE;
3422 }
3423
3424 /* Forge name */
3425 if (i > 0) {
3426 q = Players[i]->name;
3427 }
3428
3429 /* Initialize lock-step */
3430 i_ptr = NULL;
3431
3432 /* Toggle ignorance if already on the list */
3433 for (h_ptr = p_ptr->ignore; h_ptr; i_ptr = h_ptr, h_ptr = h_ptr->next) {
3434 /* Lookup name of this entry */
3435 if (h_ptr->id > 0) {
3436 /* Efficiency */
3437 if (i < 0) continue;
3438
3439 /* Look up name */
3440 p = lookup_player_name(h_ptr->id);
3441
3442 /* Check player name */
3443 if (p && streq(p, q)) {
3444 /* Delete this entry */
3445 if (i_ptr) {
3446 /* Skip over */
3447 i_ptr->next = h_ptr->next;
3448 } else {
3449 /* Adjust beginning of list */
3450 p_ptr->ignore = h_ptr->next;
3451 }
3452
3453 /* Message */
3454 msg_format(Ind, "Now listening to %s again.", p);
3455
3456 /* Delete node */
3457 KILL(h_ptr, hostile_type);
3458
3459 /* Success */
3460 return TRUE;
3461 }
3462 } else {
3463 /* Efficiency */
3464 if (i > 0) continue;
3465
3466 /* Assume this is a party */
3467 // if (streq(parties[0 - h_ptr->id].name, q))
3468 if (i == h_ptr->id) {
3469 /* Delete this entry */
3470 if (i_ptr) {
3471 /* Skip over */
3472 i_ptr->next = h_ptr->next;
3473 } else {
3474 /* Adjust beginning of list */
3475 p_ptr->ignore = h_ptr->next;
3476 }
3477
3478 /* Message */
3479 msg_format(Ind, "Now listening to party '%s' again.", parties[0 - i].name);
3480
3481 /* Delete node */
3482 KILL(h_ptr, hostile_type);
3483
3484 /* Success */
3485 return TRUE;
3486 }
3487 }
3488 }
3489
3490 if (i > 0) {
3491 q_ptr = Players[i];
3492
3493 /* Create a new hostility node */
3494 MAKE(h_ptr, hostile_type);
3495
3496 /* Set ID in node */
3497 h_ptr->id = q_ptr->id;
3498
3499 /* Put this node at the beginning of the list */
3500 h_ptr->next = p_ptr->ignore;
3501 p_ptr->ignore = h_ptr;
3502
3503 /* Message */
3504 msg_format(Ind, "You aren't hearing %s any more.", q_ptr->name);
3505
3506 /* Success */
3507 return TRUE;
3508 } else {
3509 /* Tweak - inverse i once more */
3510 i = 0 - i;
3511
3512 /* Create a new hostility node */
3513 MAKE(h_ptr, hostile_type);
3514
3515 /* Set ID in node */
3516 h_ptr->id = 0 - i;
3517
3518 /* Put this node at the beginning of the list */
3519 h_ptr->next = p_ptr->ignore;
3520 p_ptr->ignore = h_ptr;
3521
3522 /* Message */
3523 msg_format(Ind, "You aren't hearing party '%s' any more.", parties[i].name);
3524
3525 /* Success */
3526 return TRUE;
3527 }
3528 }
3529
3530 /*
3531 * Check if one player is ignoring the other
3532 */
check_ignore(int attacker,int target)3533 bool check_ignore(int attacker, int target) {
3534 player_type *p_ptr = Players[attacker];
3535 hostile_type *h_ptr;
3536
3537 /* Scan list */
3538 for (h_ptr = p_ptr->ignore; h_ptr; h_ptr = h_ptr->next) {
3539 /* Check ID */
3540 if (h_ptr->id > 0) {
3541 /* Identical ID's yield hostility */
3542 if (h_ptr->id == Players[target]->id)
3543 return TRUE;
3544 } else {
3545 /* Check if target belongs to hostile party */
3546 if (Players[target]->party == 0 - h_ptr->id)
3547 return TRUE;
3548 }
3549 }
3550
3551 /* Not hostile */
3552 return FALSE;
3553 }
3554
3555 /*
3556 * The following is a simple hash table, which is used for mapping a player's
3557 * ID number to his name. Only players that are still alive are stored in
3558 * the table, thus the mapping from a 32-bit integer is very sparse. Also,
3559 * duplicate ID numbers are prohibitied.
3560 *
3561 * The hash function is going to be h(x) = x % n, where n is the length of
3562 * the table. For efficiency reasons, n will be a power of 2, thus the
3563 * hash function can be a bitwise "and" and get the relevant bits off the end.
3564 *
3565 * No "probing" is done; if any two ID's map to the same hash slot, they will
3566 * be chained in a linked list. This will most likely be a very rare thing,
3567 * however.
3568 */
3569
3570
3571 /*
3572 * Return the slot in which an ID should be stored.
3573 */
hash_slot(int id)3574 static int hash_slot(int id) {
3575 /* Be very efficient */
3576 return (id & (NUM_HASH_ENTRIES - 1));
3577 }
3578
3579
3580 /*
3581 * Lookup a player record ID. Will return NULL on failure.
3582 */
lookup_player(int id)3583 hash_entry *lookup_player(int id) {
3584 int slot;
3585 hash_entry *ptr;
3586
3587 /* Get the slot */
3588 slot = hash_slot(id);
3589
3590 /* Acquire the pointer to the first element in the chain */
3591 ptr = hash_table[slot];
3592
3593 /* Search the chain, looking for the correct ID */
3594 while (ptr) {
3595 /* Check this entry */
3596 if (ptr->id == id)
3597 return ptr;
3598
3599 /* Next entry in chain */
3600 ptr = ptr->next;
3601 }
3602
3603 /* Not found */
3604 return NULL;
3605 }
3606
3607
3608 /*
3609 * Get the player's highest level.
3610 */
lookup_player_level(int id)3611 byte lookup_player_level(int id) {
3612 hash_entry *ptr;
3613
3614 if ((ptr = lookup_player(id)))
3615 return ptr->level;
3616
3617 /* Not found */
3618 return -1L;
3619 }
3620
lookup_player_admin(int id)3621 byte lookup_player_admin(int id) {
3622 hash_entry *ptr;
3623
3624 if ((ptr = lookup_player(id)))
3625 return (ptr->admin ? 1 : 0);
3626
3627 /* Not found */
3628 return -1L;
3629 }
3630
lookup_player_type(int id)3631 u16b lookup_player_type(int id) {
3632 hash_entry *ptr;
3633
3634 if ((ptr = lookup_player(id)))
3635 return(ptr->race | (ptr->class <<8 ));
3636
3637 /* Not found */
3638 return -1L;
3639 }
3640
3641 /*
3642 * Get the player's current party.
3643 */
lookup_player_party(int id)3644 s32b lookup_player_party(int id) {
3645 hash_entry *ptr;
3646
3647 if ((ptr = lookup_player(id)))
3648 return ptr->party;
3649
3650 /* Not found */
3651 return -1L;
3652 }
3653
lookup_player_guild(int id)3654 s32b lookup_player_guild(int id) {
3655 hash_entry *ptr;
3656
3657 if ((ptr = lookup_player(id)))
3658 return ptr->guild;
3659
3660 /* Not found */
3661 return -1L;
3662 }
3663
lookup_player_guildflags(int id)3664 u32b lookup_player_guildflags(int id) {
3665 hash_entry *ptr;
3666
3667 if ((ptr = lookup_player(id)))
3668 return ptr->guild_flags;
3669
3670 /* Not found */
3671 return -1L;
3672 }
3673
3674 /*
3675 * Get the timestamp for the last time player was on.
3676 */
lookup_player_laston(int id)3677 time_t lookup_player_laston(int id) {
3678 hash_entry *ptr;
3679
3680 if ((ptr = lookup_player(id)))
3681 return ptr->laston;
3682
3683 /* Not found */
3684 return -1L;
3685 }
3686
3687 /*
3688 * Lookup a player name by ID. Will return NULL if the name doesn't exist.
3689 */
lookup_player_name(int id)3690 cptr lookup_player_name(int id) {
3691 hash_entry *ptr;
3692
3693 if ((ptr = lookup_player(id)))
3694 return ptr->name;
3695
3696 /* Not found */
3697 return NULL;
3698 }
3699
3700 /*
3701 * Get the player's character mode (needed for account overview screen only).
3702 */
lookup_player_mode(int id)3703 byte lookup_player_mode(int id) {
3704 hash_entry *ptr;
3705
3706 if ((ptr = lookup_player(id)))
3707 return ptr->mode;
3708
3709 /* Not found */
3710 return -1L;
3711 }
3712
3713 /*
3714 * Get the player's current location in the world.
3715 */
lookup_player_wpos(int id)3716 struct worldpos lookup_player_wpos(int id) {
3717 hash_entry *ptr;
3718 struct worldpos wpos = {-1, -1, 0};
3719
3720 if ((ptr = lookup_player(id)))
3721 return ptr->wpos;
3722
3723 /* Not found */
3724 return wpos;
3725 }
3726
3727 #ifdef AUCTION_SYSTEM
3728 /*
3729 * Get the amount of gold the player has.
3730 */
lookup_player_au(int id)3731 s32b lookup_player_au(int id) {
3732 hash_entry *ptr;
3733
3734 if ((ptr = lookup_player(id)))
3735 return ptr->au;
3736
3737 /* Not found */
3738 return -1L;
3739 }
3740
3741 /*
3742 * Get the player's bank balance.
3743 */
lookup_player_balance(int id)3744 s32b lookup_player_balance(int id) {
3745 hash_entry *ptr;
3746
3747 if ((ptr = lookup_player(id)))
3748 return ptr->balance;
3749
3750 /* Not found */
3751 return -1L;
3752 }
3753 #endif
3754
3755 /*
3756 * Lookup a player's ID by name. Return 0 if not found.
3757 */
lookup_player_id(cptr name)3758 int lookup_player_id(cptr name)
3759 {
3760 hash_entry *ptr;
3761 int i;
3762
3763 /* Search in each array slot */
3764 for (i = 0; i < NUM_HASH_ENTRIES; i++)
3765 {
3766 /* Acquire pointer to this chain */
3767 ptr = hash_table[i];
3768
3769 /* Check all entries in this chain */
3770 while (ptr)
3771 {
3772 /* Check this name */
3773 if (!strcmp(ptr->name, name))
3774 return ptr->id;
3775
3776 /* Next entry in chain */
3777 ptr = ptr->next;
3778 }
3779 }
3780
3781 /* Not found */
3782 return 0;
3783 }
3784
fix_player_case(char * name)3785 bool fix_player_case(char *name) {
3786 hash_entry *ptr;
3787 int i;
3788
3789 /* Search in each array slot */
3790 for (i = 0; i < NUM_HASH_ENTRIES; i++) {
3791 /* Acquire pointer to this chain */
3792 ptr = hash_table[i];
3793
3794 /* Check all entries in this chain */
3795 while (ptr) {
3796 /* Check this name */
3797 if (!strcasecmp(ptr->name, name)) {
3798 /* Overwrite it with currently used capitalization */
3799 if (strcmp(ptr->name, name)) {
3800 strcpy(name, ptr->name);
3801 return TRUE;
3802 }
3803 return FALSE;
3804 }
3805
3806 /* Next entry in chain */
3807 ptr = ptr->next;
3808 }
3809 }
3810
3811 /* Not found */
3812 return FALSE;
3813 }
3814
3815 /* Messed up version of lookup_player_id for house ownership changes
3816 that involve messed up characters (in terms of their ID, for example
3817 if they were copied / restored instead of cleanly created..) mea culpa! */
lookup_player_id_messy(cptr name)3818 int lookup_player_id_messy(cptr name)
3819 {
3820 hash_entry *ptr;
3821 int i, tmp_id = 0;
3822
3823 /* Search in each array slot */
3824 for (i = 0; i < NUM_HASH_ENTRIES; i++)
3825 {
3826 /* Acquire pointer to this chain */
3827 ptr = hash_table[i];
3828
3829 /* Check all entries in this chain */
3830 while (ptr)
3831 {
3832 /* Check this name
3833 The ' && (ptr->id > 0)' part is only needed for the hack below this while loop - C. Blue */
3834 if (!strcmp(ptr->name, name) && (ptr->id > 0)) {
3835 tmp_id = ptr->id;
3836 break;
3837 }
3838
3839 /* Next entry in chain */
3840 ptr = ptr->next;
3841 }
3842 if (tmp_id) break;
3843 }
3844
3845 /* In case of messed up IDs (due to restoring erased chars, etc)
3846 perform a double check on all players who are currently on: - C. Blue
3847 (on a clean server *cough* this shouldn't be needed I think) */
3848 for (i = 1; i <= NumPlayers; i++)
3849 if (!strcmp(Players[i]->name, name)) {
3850 if (tmp_id && (tmp_id != Players[i]->id)) /* mess-up detected */
3851 s_printf("$ID-WARNING$ Player %s has hash id %d but character id %d.\n", Players[i]->name, tmp_id, Players[i]->id);
3852 /* have character-id override the hash id.. sigh */
3853 if (Players[i]->id > 0) return (Players[i]->id);
3854 /* the > 0 check is paranoia.. */
3855 break;
3856 }
3857
3858 if (tmp_id) return (tmp_id);
3859
3860 /* Not found */
3861 return 0;
3862 }
3863
lookup_player_account(int id)3864 u32b lookup_player_account(int id) {
3865 hash_entry *ptr;
3866 if ((ptr = lookup_player(id)))
3867 return ptr->account;
3868
3869 /* Not found */
3870 return -1L;
3871 }
3872
stat_player(char * name,bool on)3873 void stat_player(char *name, bool on) {
3874 int id;
3875 int slot;
3876 hash_entry *ptr;
3877
3878 id = lookup_player_id(name);
3879 if (id) {
3880 slot = hash_slot(id);
3881 ptr = hash_table[slot];
3882 while (ptr) {
3883 if (ptr->id == id) {
3884 ptr->laston = on ? 0L : time(&ptr->laston);
3885 }
3886 ptr = ptr->next;
3887 }
3888 }
3889 }
3890
3891 /* Timestamp an existing player */
clockin(int Ind,int type)3892 void clockin(int Ind, int type) {
3893 int slot;
3894 hash_entry *ptr;
3895 player_type *p_ptr = Players[Ind];
3896 slot = hash_slot(p_ptr->id);
3897 ptr = hash_table[slot];
3898 while (ptr) {
3899 if (ptr->id == p_ptr->id) {
3900 switch (type) {
3901 case 0:
3902 if (ptr->laston) ptr->laston = time(&ptr->laston);
3903 break;
3904 case 1:
3905 /* if(p_ptr->lev>ptr->level) -- changed it to != -- C. Blue */
3906 if (p_ptr->lev != ptr->level)
3907 ptr->level = p_ptr->lev;
3908 break;
3909 case 2:
3910 ptr->party = p_ptr->party;
3911 break;
3912 case 3:
3913 ptr->guild = p_ptr->guild;
3914 ptr->guild_flags = p_ptr->guild_flags;
3915 break;
3916 case 4:
3917 ptr->xorder = p_ptr->xorder_id;
3918 break;
3919 #ifdef AUCTION_SYSTEM
3920 case 5:
3921 ptr->au = p_ptr->au;
3922 ptr->balance = p_ptr->balance;
3923 break;
3924 #endif
3925 case 6:
3926 ptr->admin = p_ptr->admin_dm ? 1 : (p_ptr->admin_wiz ? 2 : 0);
3927 break;
3928 case 7:
3929 ptr->wpos = p_ptr->wpos;
3930 break;
3931 }
3932 break;
3933 }
3934 ptr = ptr->next;
3935 }
3936 }
3937
3938 /* dish out a valid new player ID */
newid()3939 int newid() {
3940 int id;
3941 int slot;
3942 hash_entry *ptr;
3943
3944 /* there should be no need to do player_id > MAX_ID check
3945 as it should cycle just fine */
3946
3947 for (id = player_id; id <= MAX_ID; id++) {
3948 slot = hash_slot(id);
3949 ptr = hash_table[slot];
3950
3951 while (ptr) {
3952 if (ptr->id == id) break;
3953 ptr = ptr->next;
3954 }
3955 if (ptr) continue; /* its on a valid one */
3956 player_id = id + 1; /* new cycle counter */
3957 return(id);
3958 }
3959 for (id = 1; id < player_id; id++) {
3960 slot = hash_slot(id);
3961 ptr = hash_table[slot];
3962
3963 while (ptr) {
3964 if (ptr->id == id) break;
3965 ptr = ptr->next;
3966 }
3967 if(ptr) continue; /* its on a valid one */
3968 player_id = id + 1; /* new cycle counter */
3969 return(id);
3970 }
3971 return(0); /* no user IDs available - not likely */
3972 }
3973
sf_delete(const char * name)3974 void sf_delete(const char *name) {
3975 int i, k = 0;
3976 char temp[128],fname[MAX_PATH_LENGTH];
3977 /* Extract "useful" letters */
3978 for (i = 0; name[i]; i++) {
3979 char c = name[i];
3980
3981 /* Accept some letters */
3982 if (isalpha(c) || isdigit(c)) temp[k++] = c;
3983
3984 /* Convert space, dot, and underscore to underscore */
3985 else if (strchr(SF_BAD_CHARS, c)) temp[k++] = '_';
3986 }
3987 temp[k] = '\0';
3988 path_build(fname, MAX_PATH_LENGTH, ANGBAND_DIR_SAVE, temp);
3989 unlink(fname);
3990 }
3991
3992 /* For marking accounts active */
3993 static bool *account_active = NULL;
3994
3995 /*
3996 * Called once every 24 hours. Deletes unused IDs.
3997 */
scan_players()3998 void scan_players() {
3999 int slot, amt = 0;
4000 int i, j;
4001 hash_entry *ptr, *pptr = NULL;
4002 time_t now;
4003 object_type *o_ptr;
4004
4005 #if 0 /* Low-performance version */
4006 struct account *c_acc = NULL;
4007 #endif
4008
4009 #ifdef PLAYERS_NEVER_EXPIRE
4010 s_printf("(scan_players() disabled due to PLAYERS_NEVER_EXPIRE (DEF).)\n");
4011 return;
4012 #else
4013 if (cfg.players_never_expire) s_printf("(scan_players() disabled due to PLAYERS_NEVER_EXPIRE (cfg).)\n");
4014 return;
4015 #endif
4016
4017 /* Allocate an array for marking accounts as active */
4018 C_MAKE(account_active, MAX_ACCOUNTS / 8, bool);
4019
4020 now = time(&now);
4021
4022 s_printf("Starting player inactivity check..\n");
4023 for (slot = 0; slot < NUM_HASH_ENTRIES; slot++) {
4024 pptr = NULL;
4025 ptr = hash_table[slot];
4026 while (ptr) {
4027 if (ptr->laston && (now - ptr->laston > 3600 * 24 * CHARACTER_EXPIRY_DAYS)) {/*15552000; 7776000 = 90 days at 60fps*/
4028 hash_entry *dptr;
4029 cptr acc;
4030
4031 acc = lookup_accountname(ptr->id);
4032 if (!acc) acc = "(no account)";
4033 s_printf(" Removing player: %s (%s)\n", ptr->name, acc);
4034
4035 if (ptr->level >= 50 && ptr->admin == 0) l_printf("%s \\{D%s, level %d, was erased by timeout\n", showdate(), ptr->name, ptr->level);
4036
4037 for (i = 1; i < MAX_PARTIES; i++) { /* was i = 0 but real parties start from i = 1 - mikaelh */
4038 if (streq(parties[i].owner, ptr->name)) {
4039 s_printf(" Disbanding party: %s\n",parties[i].name);
4040 del_party(i);
4041 /* remove pending notes to his party -C. Blue */
4042 for (j = 0; j < MAX_PARTYNOTES; j++) {
4043 if (!strcmp(party_note_target[j], parties[i].name)) {
4044 strcpy(party_note_target[j], "");
4045 strcpy(party_note[j], "");
4046 }
4047 }
4048 break;
4049 }
4050 }
4051 kill_houses(ptr->id, OT_PLAYER);
4052 rem_xorder(ptr->xorder);
4053 /* Added this one.. should work well? */
4054 kill_objs(ptr->id);
4055
4056 #ifdef AUCTION_SYSTEM
4057 auction_player_death(ptr->id);
4058 #endif
4059
4060 /* Wipe Artifacts (s)he had -C. Blue */
4061 for (i = 0; i < o_max; i++) {
4062 o_ptr = &o_list[i];
4063 if (true_artifact_p(o_ptr) && (o_ptr->owner == ptr->id))
4064 delete_object_idx(i, TRUE);
4065 }
4066
4067 amt++;
4068 sf_delete(ptr->name); /* a sad day ;( */
4069 if (!pptr) hash_table[slot] = ptr->next;
4070 else pptr->next = ptr->next;
4071 /* Free the memory in the player name */
4072 free((char *)(ptr->name));
4073
4074 dptr = ptr; /* safe storage */
4075 ptr = ptr->next; /* advance */
4076
4077 /* Free the memory for this struct */
4078 KILL(dptr, hash_entry);
4079 continue;
4080 } else {
4081 #if 0 /* Low-performance version */
4082 /* if a character didn't timeout, timestamp his
4083 account here, to help the account-expiry routines,
4084 keeping the account 'active' - see scan_accounts() - C. Blue */
4085 c_acc = GetAccountID(ptr->account, TRUE);
4086 /* avoid tagging multi-char accounts again for each char - once is sufficient */
4087 if (c_acc) {
4088 if (c_acc->acc_laston != now) {
4089 c_acc->acc_laston = now;
4090 WriteAccount(c_acc, FALSE);
4091 }
4092 KILL(c_acc, struct account);
4093 }
4094 #else
4095 /* If a character didn't timeout, mark his
4096 account as active here */
4097 account_active[ptr->account / 8] |= 1 << (ptr->account % 8);
4098 #endif
4099 }
4100
4101 /* proceed to next entry of this hash slot */
4102 pptr = ptr;
4103 ptr = ptr->next;
4104 }
4105 }
4106
4107 s_printf(" %d players expired.\n", amt);
4108 s_printf("Finished player inactivity check.\n");
4109 }
4110 /*
4111 * Called once every 24 hours. Deletes unused Account IDs.
4112 * It's called straight after scan_players, usually.
4113 * Unused means that there aren't any characters on it,
4114 * and it's not been used to log in with for a certain amount of time. - C. Blue
4115 */
scan_accounts()4116 void scan_accounts() {
4117 int total = 0, nondel = 0, active = 0, expired = 0, fixed = 0;
4118 bool modified;
4119 FILE *fp;
4120 char buf[1024];
4121 struct account c_acc;
4122 time_t now;
4123
4124 #ifdef PLAYERS_NEVER_EXPIRE
4125 s_printf("(scan_accounts() disabled due to PLAYERS_NEVER_EXPIRE.)\n");
4126 return;
4127 #else
4128 if (cfg.players_never_expire) s_printf("(scan_accounts() disabled due to PLAYERS_NEVER_EXPIRE (cfg).)\n");
4129 return;
4130 #endif
4131
4132 now = time(NULL);
4133
4134 if (!account_active) {
4135 s_printf("scan_players() must be called before scan_accounts() is called!\n");
4136 return;
4137 }
4138
4139 s_printf("Starting account inactivity check..\n");
4140
4141 path_build(buf, 1024, ANGBAND_DIR_SAVE, "tomenet.acc");
4142 fp = fopen(buf, "rb+");
4143 if (!fp) return;
4144
4145 while (fread(&c_acc, sizeof(struct account), 1, fp)) {
4146 modified = FALSE;
4147
4148 /* Count all accounts in the file */
4149 total++;
4150
4151 if (c_acc.flags & ACC_DELD) continue;
4152
4153 /* Count non-deleted accounts */
4154 nondel++;
4155
4156 if (c_acc.flags & ACC_ADMIN) {
4157 /* Admin accounts always count as active */
4158 active++;
4159 continue;
4160 }
4161
4162 // if (!c_acc.acc_laston) continue; /* not using this 'hack' for staticing here */
4163
4164 /* fix old accounts that don't have a timestamp yet */
4165 if (!c_acc.acc_laston) {
4166 c_acc.acc_laston = c_acc.acc_laston_real = now; /* set new timestamp */
4167 fixed++;
4168 modified = TRUE;
4169 }
4170
4171 /* Check for bad account id */
4172 else if (c_acc.id >= MAX_ACCOUNTS) {
4173 /* Log it */
4174 s_printf(" Bad account ID: %d\n", c_acc.id);
4175
4176 /* Fix the id */
4177 c_acc.id = MAX_ACCOUNTS - 1;
4178
4179 /* Mark as deleted */
4180 c_acc.flags |= ACC_DELD;
4181
4182 modified = TRUE;
4183 }
4184
4185 /* Was the account marked as active? */
4186 else if (account_active[c_acc.id / 8] & (1 << (c_acc.id % 8))) {
4187 c_acc.acc_laston = now;
4188
4189 /* Count active accounts */
4190 active++;
4191
4192 modified = TRUE;
4193 }
4194
4195 #if 1
4196 /* test for expiry -> delete */
4197 else if (now - c_acc.acc_laston >= 3600 * 24 * ACCOUNT_EXPIRY_DAYS) {
4198 c_acc.flags |= ACC_DELD;
4199
4200 /* Count expired accounts */
4201 expired++;
4202
4203 s_printf(" Account '%s' expired.\n", c_acc.name);
4204 modified = TRUE;
4205 }
4206 #endif
4207
4208 // if (modified) WriteAccount(&c_acc, FALSE);
4209 if (modified) {
4210 fseek(fp, -sizeof(struct account), SEEK_CUR);
4211 if (fwrite(&c_acc, sizeof(struct account), 1, fp) < 1) {
4212 s_printf("Writing to account file failed: %s\n", feof(fp) ? "EOF" : strerror(ferror(fp)));
4213 }
4214
4215 /* HACK - Prevent reading past the end of the file on Windows - mikaelh */
4216 fseek(fp, 0, SEEK_CUR);
4217 }
4218 }
4219
4220 if (fixed) s_printf(" %d accounts have been fixed.\n", fixed);
4221 s_printf(" %d accounts in total, %d non-deleted, %d active.\n", total, nondel, active);
4222 s_printf(" %d accounts have expired.\n", expired);
4223
4224 memset(c_acc.pass, 0, sizeof(c_acc.pass));
4225 fclose(fp);
4226 s_printf("Finished account inactivity check.\n");
4227
4228 C_KILL(account_active, MAX_ACCOUNTS / 8, bool);
4229 }
4230
4231 /* Rename a player's char savegame as well as the name inside.
4232 Not sure if this function is 100% ok to use, regarding treatment of hash table. */
rename_character(char * pnames)4233 void rename_character(char *pnames){
4234 int slot, pos, i, Ind;
4235 hash_entry *ptr;
4236 char pname[40], nname[40];
4237 player_type *p_ptr;
4238
4239 Ind = ++NumPlayers;
4240
4241 /* Allocate memory for him */
4242 MAKE(Players[Ind], player_type);
4243 C_MAKE(Players[Ind]->inventory, INVEN_TOTAL, object_type);
4244 p_ptr = Players[Ind];
4245
4246 /* extract old+new name from 'pnames' */
4247 if (!(strchr(pnames, ':'))) return;
4248 pos = strchr(pnames, ':') - pnames;
4249 strncpy(pname, pnames, pos);
4250 pname[pos] = 0;
4251 strcpy(nname, pnames + pos + 1);
4252 s_printf("Renaming player: %s to %s\n", pname, nname);
4253
4254 /* scan hash entries */
4255 for (slot = 0; slot < NUM_HASH_ENTRIES; slot++) {
4256 ptr = hash_table[slot];
4257 while (ptr) {
4258 if (strcmp(ptr->name, pname)) {
4259 ptr = ptr->next;
4260 continue;
4261 }
4262
4263 /* load him */
4264 strcpy(p_ptr->name, pname);
4265 process_player_name(Ind, TRUE);
4266 if (!load_player(Ind)) {
4267 /* bad fail */
4268 s_printf("rename_character: load_player '%s' failed\n", pname);
4269 C_KILL(Players[Ind]->inventory, INVEN_TOTAL, object_type);
4270 KILL(Players[Ind], player_type);
4271 NumPlayers--;
4272 return;
4273 }
4274
4275 /* change his name */
4276 strcpy(p_ptr->name, nname);
4277 if (!process_player_name(Ind, TRUE)) {
4278 /* done (failure) */
4279 s_printf("rename_character: bad new name '%s'\n", nname);
4280 C_KILL(Players[Ind]->inventory, INVEN_TOTAL, object_type);
4281 KILL(Players[Ind], player_type);
4282 NumPlayers--;
4283 return;
4284 }
4285 /* change his name in hash table */
4286 strcpy((char *) ptr->name, p_ptr->name);
4287 /* save him */
4288 save_player(Ind);
4289 /* delete old savefile) */
4290 sf_delete(pname);
4291
4292 /* change his name as party owner */
4293 for (i = 1; i < MAX_PARTIES; i++) { /* was i = 0 but real parties start from i = 1 - mikaelh */
4294 if (streq(parties[i].owner, ptr->name)) {
4295 s_printf("Renaming owner of party: %s\n",parties[i].name);
4296 strcpy(parties[i].owner, nname);
4297 break;
4298 }
4299 }
4300
4301 /* (guilds only use numerical ID, not names) */
4302
4303 /* done (success) */
4304 s_printf("rename_character: success '%s' -> '%s'\n", pname, nname);
4305 C_KILL(Players[Ind]->inventory, INVEN_TOTAL, object_type);
4306 KILL(Players[Ind], player_type);
4307 NumPlayers--;
4308
4309 /* update live player */
4310 for (i = 1; i <= NumPlayers; i++) {
4311 p_ptr = Players[i];
4312 if (strcmp(p_ptr->name, pname)) continue;
4313
4314 strcpy(p_ptr->name, nname);
4315 if (!process_player_name(i, TRUE)) {
4316 /* done (failure) -- paranoia, shouldn't happen (caught above already!) */
4317 s_printf("rename_character: *LIVE* bad new name '%s'\n", nname);
4318 Destroy_connection(i, "Name changed. Please log in again.");
4319 /* delete old savefile) */
4320 sf_delete(pname);
4321 return;
4322 }
4323 Send_char_info(i, p_ptr->prace, p_ptr->pclass, p_ptr->ptrait, p_ptr->male, p_ptr->mode, p_ptr->name);
4324 return;
4325 }
4326 return;
4327 }
4328 }
4329
4330 /* free temporary player */
4331 s_printf("rename_character: name not found\n");
4332 C_KILL(Players[Ind]->inventory, INVEN_TOTAL, object_type);
4333 KILL(Players[Ind], player_type);
4334 NumPlayers--;
4335 }
4336
4337 /*
4338 * Erase a player by charfile-name - C. Blue
4339 */
erase_player_name(char * pname)4340 void erase_player_name(char *pname){
4341 int slot;
4342 hash_entry *ptr, *pptr = NULL;
4343 object_type *o_ptr;
4344
4345 for (slot = 0; slot < NUM_HASH_ENTRIES; slot++) {
4346 pptr = NULL;
4347 ptr = hash_table[slot];
4348 while (ptr) {
4349 if (!strcmp(ptr->name, pname)) {
4350 int i,j;
4351 hash_entry *dptr;
4352 cptr acc;
4353
4354 acc = lookup_accountname(ptr->id);
4355 if (!acc) acc = "(no account)";
4356 s_printf("Removing player: %s (%s)\n", ptr->name, acc);
4357
4358 for (i = 1; i < MAX_PARTIES; i++) { /* was i = 0 but real parties start from i = 1 - mikaelh */
4359 if (streq(parties[i].owner, ptr->name)) {
4360 s_printf("Disbanding party: %s\n",parties[i].name);
4361 del_party(i);
4362 /* remove pending notes to his party -C. Blue */
4363 for (j = 0; j < MAX_PARTYNOTES; j++) {
4364 if (!strcmp(party_note_target[j], parties[i].name)) {
4365 strcpy(party_note_target[j], "");
4366 strcpy(party_note[j], "");
4367 }
4368 }
4369 break;
4370 }
4371 }
4372 kill_houses(ptr->id, OT_PLAYER);
4373 rem_xorder(ptr->xorder);
4374 /* Added this one.. should work well? */
4375 kill_objs(ptr->id);
4376
4377 #ifdef AUCTION_SYSTEM
4378 auction_player_death(ptr->id);
4379 #endif
4380
4381 /* Wipe Artifacts (s)he had -C. Blue */
4382 for (i = 0; i < o_max; i++) {
4383 o_ptr = &o_list[i];
4384 if (true_artifact_p(o_ptr) && (o_ptr->owner == ptr->id))
4385 delete_object_idx(i, TRUE);
4386 }
4387
4388 sf_delete(ptr->name); /* a sad day ;( */
4389 if (!pptr)
4390 hash_table[slot] = ptr->next;
4391 else
4392 pptr->next = ptr->next;
4393 /* Free the memory in the player name */
4394 free((char *)(ptr->name));
4395
4396 dptr = ptr; /* safe storage */
4397 ptr = ptr->next; /* advance */
4398
4399 /* Free the memory for this struct */
4400 KILL(dptr, hash_entry);
4401
4402 continue;
4403 }
4404 pptr = ptr;
4405 ptr = ptr->next;
4406 }
4407 }
4408 }
4409
4410 /*
4411 * List of players about to expire - mikaelh
4412 */
checkexpiry(int Ind,int days)4413 void checkexpiry(int Ind, int days)
4414 {
4415 int slot, expire;
4416 hash_entry *ptr;
4417 time_t now;
4418 now = time(NULL);
4419 msg_format(Ind, "\377oPlayers that are about to expire in %d days:", days);
4420 for (slot = 0; slot < NUM_HASH_ENTRIES; slot++) {
4421 ptr = hash_table[slot];
4422 while (ptr) {
4423 expire = CHARACTER_EXPIRY_DAYS * 86400 - now + ptr->laston;
4424 if (ptr->laston && expire < days * 86400) {
4425 if (expire < 86400) {
4426 msg_format(Ind, "\377rPlayer %s (accid %d) will expire in less than a day!", ptr->name, ptr->account);
4427 }
4428 else if (expire < 7 * 86400) {
4429 msg_format(Ind, "\377yPlayer %s (accid %d) will expire in %d days!", ptr->name, ptr->account, (expire / 86400));
4430 }
4431 else {
4432 msg_format(Ind, "\377gPlayer %s (accid %d) will expire in %d days.", ptr->name, ptr->account, (expire / 86400));
4433 }
4434 }
4435 ptr = ptr->next;
4436 }
4437 }
4438 }
4439
4440 /*
4441 * Warn the player if other characters on his/her account will expire soon
4442 * - mikaelh
4443 */
account_checkexpiry(int Ind)4444 void account_checkexpiry(int Ind) {
4445 player_type *p_ptr = Players[Ind];
4446 int slot, expire;
4447 hash_entry *ptr;
4448 time_t now;
4449
4450 #ifdef PLAYERS_NEVER_EXPIRE
4451 return;
4452 #else
4453 if (cfg.players_never_expire) return;
4454 #endif
4455 now = time(NULL);
4456
4457 for (slot = 0; slot < NUM_HASH_ENTRIES; slot++) {
4458 ptr = hash_table[slot];
4459 while (ptr) {
4460 if (p_ptr->id != ptr->id && p_ptr->account == ptr->account && ptr->laston) {
4461 expire = CHARACTER_EXPIRY_DAYS * 86400 - now + ptr->laston;
4462
4463 if (expire < 86400) {
4464 msg_format(Ind, "\374\377yYour character %s will be removed \377rvery soon\377y!", ptr->name, expire / 86400);
4465 } else if (expire < 60 * 86400) {
4466 msg_format(Ind, "\374\377yYour character %s will be removed in %d days.", ptr->name, expire / 86400);
4467 }
4468 }
4469 ptr = ptr->next;
4470 }
4471 }
4472 }
4473
4474 /*
4475 * Add a name to the hash table.
4476 */
add_player_name(cptr name,int id,u32b account,byte race,byte class,byte mode,byte level,u16b party,byte guild,u32b guild_flags,u16b xorder,time_t laston,byte admin,struct worldpos wpos)4477 void add_player_name(cptr name, int id, u32b account, byte race, byte class, byte mode, byte level, u16b party, byte guild, u32b guild_flags, u16b xorder, time_t laston, byte admin, struct worldpos wpos) {
4478 int slot;
4479 hash_entry *ptr;
4480
4481 /* Set the entry's id */
4482
4483 /* Get the destination slot */
4484 slot = hash_slot(id);
4485
4486 /* Create a new hash entry struct */
4487 MAKE(ptr, hash_entry);
4488
4489 /* Make a copy of the player name in the entry */
4490 ptr->name = strdup(name);
4491 ptr->accountname = strdup(lookup_accountname2(account));
4492 ptr->laston = laston;
4493 ptr->id = id;
4494 ptr->account = account;
4495 ptr->level = level;
4496 ptr->party = party;
4497 ptr->guild = guild;
4498 ptr->guild_flags = guild_flags;
4499 ptr->xorder = xorder;
4500 ptr->race = race;
4501 ptr->class = class;
4502 ptr->mode = mode;
4503 ptr->admin = admin;
4504 ptr->wpos.wx = wpos.wx;
4505 ptr->wpos.wy = wpos.wy;
4506 ptr->wpos.wz = wpos.wz;
4507
4508 /* Add the rest of the chain to this entry */
4509 ptr->next = hash_table[slot];
4510
4511 /* Put this entry in the table */
4512 hash_table[slot] = ptr;
4513 }
4514
4515 /*
4516 * Verify a player's data against the hash table. - C. Blue
4517 */
verify_player(cptr name,int id,u32b account,byte race,byte class,byte mode,byte level,u16b party,byte guild,u32b guild_flags,u16b quest,time_t laston,byte admin,struct worldpos wpos)4518 void verify_player(cptr name, int id, u32b account, byte race, byte class, byte mode, byte level, u16b party, byte guild, u32b guild_flags, u16b quest, time_t laston, byte admin, struct worldpos wpos) {
4519 hash_entry *ptr = lookup_player(id);
4520
4521 /* For savegame conversion 4.2.0 -> 4.2.2: */
4522 if (ptr->mode != mode) {
4523 s_printf("hash_entry: fixing mode of %s.\n", ptr->name);
4524 ptr->mode = mode;
4525 }
4526 /* For savegame conversion 4.3.4 -> 4.3.5: */
4527 if (ptr->class != class) {
4528 s_printf("hash_entry: fixing class of %s.\n", ptr->name);
4529 ptr->class = class;
4530 }
4531 if (ptr->guild != guild) {
4532 s_printf("hash_entry: fixing guild of %s.\n", ptr->name);
4533 ptr->guild = guild;
4534 }
4535 if (ptr->guild_flags != guild_flags) {
4536 s_printf("hash_entry: fixing guild_flags of %s.\n", ptr->name);
4537 ptr->guild_flags = guild_flags;
4538 }
4539 if (ptr->admin != admin) {
4540 s_printf("hash_entry: fixing admin state of %s.\n", ptr->name);
4541 ptr->admin = admin;
4542 }
4543 /* added in 4.5.7.1 */
4544 if (ptr->wpos.wx != wpos.wx || ptr->wpos.wy != wpos.wy || ptr->wpos.wz != wpos.wz) {
4545 s_printf("hash_entry: fixing wpos of %s.\n", ptr->name);
4546 ptr->wpos.wx = wpos.wx;
4547 ptr->wpos.wy = wpos.wy;
4548 ptr->wpos.wz = wpos.wz;
4549 }
4550 /* --actually not needed-- */
4551 if (!ptr->accountname) {
4552 s_printf("hash_entry: fixing accountname of %s.\n", ptr->name);
4553 ptr->accountname = strdup(lookup_accountname2(account));
4554 }
4555 }
4556
4557 /*
4558 * Delete an entry from the table, by ID.
4559 */
delete_player_id(int id)4560 void delete_player_id(int id) {
4561 int slot;
4562 hash_entry *ptr, *old_ptr;
4563
4564 /* Get the destination slot */
4565 slot = hash_slot(id);
4566
4567 /* Acquire the pointer to the entry chain */
4568 ptr = hash_table[slot];
4569
4570 /* Keep a pointer one step behind this one */
4571 old_ptr = NULL;
4572
4573 /* Attempt to find the ID to delete */
4574 while (ptr) {
4575 /* Check this one */
4576 if (ptr->id == id) {
4577 /* Delete this one from the table */
4578 if (old_ptr == NULL)
4579 hash_table[slot] = ptr->next;
4580 else old_ptr->next = ptr->next;
4581
4582 /* Free the memory in the player name */
4583 free((char *)(ptr->name));
4584
4585 /* Free the memory for this struct */
4586 KILL(ptr, hash_entry);
4587
4588 /* Done */
4589 return;
4590 }
4591
4592 /* Remember this entry */
4593 old_ptr = ptr;
4594
4595 /* Advance to next entry in the chain */
4596 ptr = ptr->next;
4597 }
4598
4599 /* Not found */
4600 return;
4601 }
4602
4603 /*
4604 * Delete a player by name.
4605 *
4606 * This is useful for fault tolerance, as it is possible to have
4607 * two entries for one player name, if the server crashes hideously
4608 * or the machine has a power outage or something.
4609 */
delete_player_name(cptr name)4610 void delete_player_name(cptr name) {
4611 int id;
4612
4613 /* Delete every occurence of this name */
4614 while ((id = lookup_player_id(name))) {
4615 /* Delete this one */
4616 delete_player_id(id);
4617 }
4618 }
4619
4620 /*
4621 * Return a list of the player ID's stored in the table.
4622 */
player_id_list(int ** list,u32b account)4623 int player_id_list(int **list, u32b account) {
4624 int i, j, len = 0, k = 0, tmp;
4625 hash_entry *ptr;
4626 int *llist;
4627 int max_cpa = MAX_CHARS_PER_ACCOUNT;
4628 #ifdef ALLOW_DED_IDDC_MODE
4629 max_cpa += MAX_DED_IDDC_CHARS;
4630 #endif
4631 #ifdef ALLOW_DED_PVP_MODE
4632 max_cpa += MAX_DED_PVP_CHARS;
4633 #endif
4634
4635 /* Count up the number of valid entries */
4636 for (i = 0; i < NUM_HASH_ENTRIES; i++) {
4637 /* Acquire this chain */
4638 ptr = hash_table[i];
4639
4640 /* Check this chain */
4641 while (ptr) {
4642 /* One more entry */
4643 if(!account || ptr->account == account)
4644 len++;
4645
4646 /* Next entry in chain */
4647 ptr = ptr->next;
4648 }
4649 }
4650 if (!len) return(0);
4651
4652 /* Allocate memory for the list */
4653 C_MAKE((*list), len, int);
4654
4655 /* Direct pointer to the list */
4656 llist = *list;
4657
4658 /* Look again, this time storing ID's */
4659 for (i = 0; i < NUM_HASH_ENTRIES; i++) {
4660 /* Acquire this chain */
4661 ptr = hash_table[i];
4662
4663 /* Check this chain */
4664 while (ptr) {
4665 /* Store this ID */
4666 if (!account || ptr->account == account) {
4667 llist[k++] = ptr->id;
4668
4669 /* Insertion sort for account specific lists */
4670 if (account) {
4671 j = k - 1;
4672 while (j > 0 && llist[j - 1] > llist[j]) {
4673 tmp = llist[j - 1];
4674 llist[j - 1] = llist[j];
4675 llist[j] = tmp;
4676 j--;
4677 }
4678 }
4679 }
4680
4681 /* Next entry in chain */
4682 ptr = ptr->next;
4683 }
4684 }
4685
4686 /* Limit number of characters per account - C. Blue */
4687 /* This screwed up saving players in save.c, check that account is not 0 - mikaelh */
4688 if (len > max_cpa && account) len = max_cpa;
4689
4690 return len;
4691 }
4692
4693 /*
4694 * Set/reset 'pk' mode, which allows a player to kill the others
4695 *
4696 * These functions should be common with hostilityes in the future. -Jir-
4697 */
set_pkill(int Ind,int delay)4698 void set_pkill(int Ind, int delay) {
4699 player_type *p_ptr = Players[Ind];
4700 //bool admin = is_admin(p_ptr);
4701
4702 if (cfg.use_pk_rules != PK_RULES_DECLARE) {
4703 msg_print(Ind, "\377o/pkill is not available on this server. Be pacifist.");
4704 p_ptr->tim_pkill = 0;
4705 p_ptr->pkill = 0;
4706 return;
4707 }
4708 #ifdef KURZEL_PK
4709 else {
4710 if (is_admin(p_ptr)) {
4711 p_ptr->pkill ^= PKILL_SET; //Flag TOGGLE
4712 if (p_ptr->pkill & PKILL_SET) msg_print(Ind, "\377RYou may now kill (and be killed by) other PK enabled players.");
4713 else msg_print(Ind, "\377RPK Mode toggled OFF. (Admin ONLY)");
4714 } else {
4715 if (!(p_ptr->pkill & PKILL_SET)) {
4716 p_ptr->pkill |= PKILL_SET; //Flag ON
4717 msg_print(Ind, "\377RYou may now kill (and be killed by) other PK enabled players.");
4718 } else {
4719 msg_print(Ind, "\377RThere is no going back! (Party members are pacifist, however.)");
4720 }
4721 }
4722 return;
4723 }
4724 #endif
4725
4726 // p_ptr->tim_pkill= admin ? 10 : 200; /* so many turns */
4727 p_ptr->tim_pkill= delay;
4728 p_ptr->pkill ^= PKILL_SET; /* Toggle value */
4729 if (p_ptr->pkill & PKILL_SET) {
4730 msg_print(Ind, "\377rYou wish to kill other players");
4731 p_ptr->pkill |= PKILL_KILLABLE;
4732 } else {
4733 hostile_type *t_host;
4734 msg_print(Ind, "\377gYou do not wish to kill other players");
4735 p_ptr->pkill &= ~PKILL_KILLER;
4736 /* Remove all hostilities */
4737 while (p_ptr->hostile) {
4738 t_host = p_ptr->hostile;
4739 p_ptr->hostile = t_host->next;
4740 KILL(t_host, hostile_type);
4741 }
4742 }
4743 }
4744
4745 #if 0 // under construction
4746 /*
4747 * Set/reset 'pilot' mode, which allows a player to follow another player
4748 * for comfort in party diving.
4749 *
4750 * These functions should be common with hostilityes in the future. -Jir-
4751 */
4752 bool pilot_set(int Ind, cptr name)
4753 {
4754 player_type *p_ptr = Players[Ind], *q_ptr;
4755 hostile_type *h_ptr, *i_ptr;
4756 int i;
4757 cptr p,q;
4758
4759 /* Check for silliness */
4760 if (!name) {
4761 msg_print(Ind, "Usage: /pilot foobar");
4762 return FALSE;
4763 }
4764
4765 i = name_lookup_loose(Ind, name, TRUE, TRUE);
4766
4767 if (!i) {
4768 return FALSE;
4769 }
4770
4771 /* Check for another silliness */
4772 if (i == Ind) {
4773 /* Message */
4774 msg_print(Ind, "You cannot follow yourself.");
4775
4776 return FALSE;
4777 }
4778
4779 /* Forge name */
4780 if (i > 0) {
4781 q = Players[i]->name;
4782 }
4783
4784
4785 if (i > 0) {
4786 q_ptr = Players[i];
4787
4788 /* Create a new hostility node */
4789 MAKE(h_ptr, hostile_type);
4790
4791 /* Set ID in node */
4792 h_ptr->id = q_ptr->id;
4793
4794 /* Put this node at the beginning of the list */
4795 h_ptr->next = p_ptr->ignore;
4796 p_ptr->ignore = h_ptr;
4797
4798 /* Message */
4799 msg_format(Ind, "You aren't hearing %s any more.", q_ptr->name);
4800
4801 /* Success */
4802 return TRUE;
4803 } else {
4804 /* Tweak - inverse i once more */
4805 i = 0 - i;
4806
4807 /* Create a new hostility node */
4808 MAKE(h_ptr, hostile_type);
4809
4810 /* Set ID in node */
4811 h_ptr->id = 0 - i;
4812
4813 /* Put this node at the beginning of the list */
4814 h_ptr->next = p_ptr->ignore;
4815 p_ptr->ignore = h_ptr;
4816
4817 /* Message */
4818 msg_format(Ind, "You aren't hearing party '%s' any more.", parties[i].name);
4819
4820 /* Success */
4821 return TRUE;
4822 }
4823 }
4824 #endif // 0
4825
strip_true_arts_from_hashed_players()4826 void strip_true_arts_from_hashed_players(){
4827 int slot, i, j = 0;
4828 hash_entry *ptr;
4829 object_type *o_ptr;
4830
4831 s_printf("Starting player true artifact stripping\n");
4832 for (slot = 0; slot < NUM_HASH_ENTRIES; slot++) {
4833 j++;
4834 // if (j > 5) break;/*only check for 5 players right now */
4835
4836 ptr = hash_table[slot];
4837 if (!ptr) continue;
4838 s_printf("Stripping player: %s\n", ptr->name);
4839 /* Wipe Artifacts (s)he had -C. Blue */
4840 for (i = 0; i < o_max; i++) {
4841 o_ptr = &o_list[i];
4842 if (true_artifact_p(o_ptr) &&
4843 (o_ptr->owner == ptr->id) && // <- why?
4844 !winner_artifact_p(o_ptr))
4845 #if 1 /* set 0 to not change cur_num */
4846 delete_object_idx(i, TRUE);
4847 #else
4848 excise_object_idx(o_idx);
4849 WIPE(o_ptr, object_type);
4850 #endif
4851 }
4852 }
4853 s_printf("Finished true artifact stripping for %d players.\n", j);
4854 }
4855
account_change_password(int Ind,char * old_pass,char * new_pass)4856 void account_change_password(int Ind, char *old_pass, char *new_pass) {
4857 player_type *p_ptr = Players[Ind];
4858 struct account *c_acc;
4859
4860 c_acc = GetAccount(p_ptr->accountname, old_pass, FALSE);
4861
4862 if (!c_acc) {
4863 msg_print(Ind, "Wrong password!");
4864 return;
4865 }
4866
4867 /* Change password */
4868 strcpy(c_acc->pass, t_crypt(new_pass, c_acc->name));
4869
4870 if (!(WriteAccount(c_acc, FALSE))) {
4871 KILL(c_acc, struct account);
4872 msg_print(Ind, "Failed to write to account file!");
4873 return;
4874 }
4875
4876 s_printf("Changed password for account %s.\n", c_acc->name);
4877 msg_print(Ind, "Password changed.");
4878
4879 KILL(c_acc, struct account);
4880 }
4881
lookup_player_ind(u32b id)4882 int lookup_player_ind(u32b id) {
4883 int n;
4884 for (n = 1; n <= NumPlayers; n++)
4885 if (Players[n]->id == id) return n;
4886 return 0;
4887 }
4888
backup_acclists(void)4889 void backup_acclists(void) {
4890 FILE *fp;
4891 char buf[MAX_PATH_LENGTH], buf2[MAX_PATH_LENGTH];
4892 hash_entry *ptr;
4893 int del, i;
4894 struct account *c_acc;
4895
4896 s_printf("Backing up all accounts...\n");
4897 /* create folder lib/save/estate if not existing */
4898 path_build(buf2, MAX_PATH_LENGTH, ANGBAND_DIR_SAVE, "estate");
4899 path_build(buf, MAX_PATH_LENGTH, buf2, "_accounts_");
4900 if ((fp = fopen(buf, "wb")) == NULL) {
4901 s_printf(" error: cannot open file '%s'.\nfailed.\n", buf);
4902 return;
4903 }
4904 /* begin with a version tag */
4905 fprintf(fp, "%s\n", "v1");
4906
4907 /* Search in each array slot */
4908 for (i = 0; i < NUM_HASH_ENTRIES; i++) {
4909 /* Acquire pointer to this chain */
4910 ptr = hash_table[i];
4911
4912 /* Check all entries in this chain */
4913 while (ptr) {
4914 /* Check this name */
4915 if ((c_acc = GetAccountID(ptr->account, FALSE))) {
4916 /* back him up */
4917 fprintf(fp, "%d\n", (int)strlen(ptr->name));
4918 (void)fwrite(ptr->name, sizeof(char), strlen(ptr->name), fp);
4919 #if 0
4920 (void)fprintf(fp, "%lu%d%u%c%hu%c%hd%c%c%c",
4921 ptr->laston, ptr->id, ptr->account,
4922 ptr->level, ptr->party, ptr->guild,
4923 ptr->xorder, ptr->race, ptr->class, ptr->mode);
4924 #ifdef AUCTION_SYSTEM
4925 fprintf(fp, "%d%d", ptr->au, ptr->balance);
4926 #endif
4927 #else
4928 (void)fwrite(ptr, sizeof(hash_entry), 1, fp);
4929 #endif
4930
4931 /* cleanup (?) */
4932 KILL(c_acc, struct account);
4933 } else {
4934 s_printf("Lost player: %s\n", ptr->name);
4935 #if 0 /* del might not always be initialized! */
4936 del = ptr->id;
4937 }
4938
4939 /* Next entry in chain */
4940 ptr = ptr->next;
4941 delete_player_id(del);
4942 #else /* isn't it supposed to be this way instead?: */
4943 del = ptr->id;
4944 delete_player_id(del);
4945 }
4946
4947 /* Next entry in chain */
4948 ptr = ptr->next;
4949 #endif
4950 }
4951 }
4952
4953 s_printf("done.\n");
4954 fclose(fp);
4955 }
4956
restore_acclists(void)4957 void restore_acclists(void) {
4958 FILE *fp;
4959 char buf[MAX_PATH_LENGTH], buf2[MAX_PATH_LENGTH], tmp[MAX_CHARS];
4960 char name_forge[MAX_CHARS];
4961 hash_entry forge, *ptr = &forge;
4962 forge.name = name_forge;
4963 int name_len, r;
4964
4965 s_printf("Restoring accounts...\n");
4966
4967 path_build(buf2, MAX_PATH_LENGTH, ANGBAND_DIR_SAVE, "estate");
4968 path_build(buf, MAX_PATH_LENGTH, buf2, "_accounts_");
4969 if ((fp = fopen(buf, "rb")) == NULL) {
4970 s_printf(" error: cannot open file '%s'.\nfailed.\n", buf);
4971 return;
4972 }
4973 /* begin with a version tag */
4974 r = fscanf(fp, "%s\n", tmp);
4975 if (r == EOF || r == 0) {
4976 s_printf(" error: failed to read version tag.\n");
4977 fclose(fp);
4978 return;
4979 }
4980
4981 while (!feof(fp)) {
4982 r = fscanf(fp, "%d\n", &name_len);
4983 if (r == EOF || r == 0) break;
4984 r = fread(name_forge, sizeof(char), name_len, fp);
4985 if (r == 0) break;
4986 name_forge[name_len] = '\0';
4987 #if 0
4988 r = fscanf(fp, "%lu%d%u%c%hu%c%hd%c%c%c",
4989 &ptr->laston, &ptr->id, &ptr->account,
4990 &ptr->level, &ptr->party, &ptr->guild,
4991 &ptr->xorder, &ptr->race, &ptr->class, &ptr->mode);
4992 if (r == EOF || r == 0) break;
4993 #ifdef AUCTION_SYSTEM
4994 r = fscanf(fp, "%d%d", &ptr->au, &ptr->balance);
4995 if (r == EOF || r == 0) break;
4996 #endif
4997 #else
4998 r = fread(ptr, sizeof(hash_entry), 1, fp);
4999 if (r == 0) break;
5000 ptr->name = NULL;//just to be clean
5001 #endif
5002
5003 //s_printf(" '%s', id %d, acc %d, lev %d, race %d, class %d, mode %d.\n", ptr->name, ptr->id, ptr->account, ptr->level, ptr->race, ptr->class, ptr->mode);
5004
5005 if (!lookup_player_name(ptr->id)) { /* paranoia: if the 'server' file was just deleted then there can be no names */
5006 time_t ttime;
5007 //s_printf(" adding: '%s' (id %d, acc %d)\n", ptr->name, ptr->id, ptr->account);
5008 /* Add backed-up entry again */
5009 add_player_name(name_forge, ptr->id, ptr->account, ptr->race, ptr->class, ptr->mode, 1, 0, 0, 0, 0, time(&ttime), ptr->admin, ptr->wpos);
5010 } else s_printf(" already exists: '%s' (id %d, acc %d)\n", name_forge, ptr->id, ptr->account);
5011 }
5012
5013 s_printf("done.\n");
5014 fclose(fp);
5015 }
5016
5017 /* search for any members of a guild, and set guild's mode to that first member found's mode */
fix_lost_guild_mode(int g_id)5018 void fix_lost_guild_mode(int g_id) {
5019 int slot;
5020 hash_entry *ptr;
5021 for (slot = 0; slot < NUM_HASH_ENTRIES; slot++) {
5022 ptr = hash_table[slot];
5023 while (ptr) {
5024 if (ptr->guild && ptr->guild == g_id) {
5025 guilds[g_id].cmode = ptr->mode;
5026 s_printf("Guild '%s' (%d): Mode has been fixed to %d.\n", guilds[g_id].name, g_id, guilds[g_id].cmode);
5027 return;
5028 }
5029 ptr = ptr->next;
5030 }
5031
5032 }
5033 /* paranoia: something went really wrong, such as savefile rollback or divine intervention */
5034 if (slot == NUM_HASH_ENTRIES) {
5035 guilds[g_id].members = 0;
5036 s_printf("Guild '%s' (%d): Has been erased!\n", guilds[g_id].name, g_id);
5037 }
5038 }
5039