|
Atrinik Server 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00034 #include <global.h> 00035 #include <book.h> 00036 00037 /* This flag is useful for debugging archiving action */ 00038 /* #define ARCHIVE_DEBUG */ 00039 00041 typedef struct namebytype 00042 { 00044 char *name; 00045 00047 int type; 00048 } arttypename; 00049 00052 static object **monsters = NULL; 00055 static size_t num_monsters = 0; 00056 00059 static char **msgs = NULL; 00062 static size_t num_msgs = 0; 00063 00065 static uint32 spellpathdef[NRSPELLPATHS] = 00066 { 00067 PATH_PROT, 00068 PATH_FIRE, 00069 PATH_FROST, 00070 PATH_ELEC, 00071 PATH_MISSILE, 00072 PATH_SELF, 00073 PATH_SUMMON, 00074 PATH_ABJURE, 00075 PATH_RESTORE, 00076 PATH_DETONATE, 00077 PATH_MIND, 00078 PATH_CREATE, 00079 PATH_TELE, 00080 PATH_INFO, 00081 PATH_TRANSMUTE, 00082 PATH_TRANSFER, 00083 PATH_TURNING, 00084 PATH_WOUNDING, 00085 PATH_DEATH, 00086 PATH_LIGHT 00087 }; 00088 00090 static char *path_book_name[] = 00091 { 00092 "codex", 00093 "compendium", 00094 "exposition", 00095 "tables", 00096 "treatise", 00097 "devotional", 00098 "devout notes", 00099 "divine text", 00100 "divine work", 00101 "holy book", 00102 "holy record", 00103 "moral text", 00104 "sacred guide", 00105 "testament", 00106 "transcript" 00107 }; 00108 00110 static char *path_author[] = 00111 { 00112 "aether", 00113 "astral byways", 00114 "connections", 00115 "the Grey Council", 00116 "deep pathways", 00117 "knowledge", 00118 "magic", 00119 "mystic ways", 00120 "pathways", 00121 "power", 00122 "spells", 00123 "transforms", 00124 "the mystic veil", 00125 "unknown spells", 00126 "cults", 00127 "joy", 00128 "lasting curse", 00129 "madness", 00130 "religions", 00131 "the dead", 00132 "the gods", 00133 "the heirophant", 00134 "the poor priest", 00135 "the priestess", 00136 "pain", 00137 "white" 00138 }; 00139 00145 static arttypename art_name_array[] = 00146 { 00147 {"Helmet", HELMET}, 00148 {"Amulet", AMULET}, 00149 {"Shield", SHIELD}, 00150 {"Bracers", BRACERS}, 00151 {"Boots", BOOTS}, 00152 {"Cloak", CLOAK}, 00153 {"Gloves", GLOVES}, 00154 {"Girdle", GIRDLE}, 00155 {"Ring", RING}, 00156 {"Horn", HORN}, 00157 {"Missile Weapon", BOW}, 00158 {"Missile", ARROW}, 00159 {"Hand Weapon", WEAPON}, 00160 {"Artifact", SKILL}, 00161 {"Food", FOOD}, 00162 {"Body Armour", ARMOUR} 00163 }; 00164 00166 static char *art_book_name[] = 00167 { 00168 "collection", 00169 "file", 00170 "files", 00171 "guide", 00172 "handbook", 00173 "index", 00174 "inventory", 00175 "list", 00176 "listing", 00177 "record", 00178 "record book" 00179 }; 00180 00182 static char *art_author[] = 00183 { 00184 "ancient things", 00185 "artifacts", 00186 "Havlor", 00187 "items", 00188 "lost artifacts", 00189 "the ancients", 00190 "useful things" 00191 }; 00192 00194 static char *mon_book_name[] = 00195 { 00196 "bestiary", 00197 "catalog", 00198 "compilation", 00199 "collection", 00200 "encyclopedia", 00201 "guide", 00202 "handbook", 00203 "list", 00204 "manual", 00205 "notes", 00206 "record", 00207 "register", 00208 "volume" 00209 }; 00210 00212 static char *mon_author[] = 00213 { 00214 "beasts", 00215 "creatures", 00216 "dezidens", 00217 "dwellers", 00218 "evil nature", 00219 "life", 00220 "monsters", 00221 "nature", 00222 "new life", 00223 "residents", 00224 "the spawn", 00225 "the living", 00226 "things" 00227 }; 00228 00231 static char *book_name[] = 00232 { 00233 "calendar", 00234 "datebook", 00235 "diary", 00236 "guidebook", 00237 "handbook", 00238 "ledger", 00239 "notes", 00240 "notebook", 00241 "octavo", 00242 "pamphlet", 00243 "practicum", 00244 "script", 00245 "transcript", 00246 "catalog", 00247 "compendium", 00248 "guide", 00249 "manual", 00250 "opus", 00251 "tome", 00252 "treatise", 00253 "volume", 00254 "work" 00255 }; 00256 00258 static char *book_author[] = 00259 { 00260 "Abdulah", 00261 "Al'hezred", 00262 "Alywn", 00263 "Arundel", 00264 "Arvind", 00265 "Aerlingas", 00266 "Bacon", 00267 "Baliqendii", 00268 "Bosworth", 00269 "Beathis", 00270 "Bertil", 00271 "Cauchy", 00272 "Chakrabarti", 00273 "der Waalis", 00274 "Dirk", 00275 "Djwimii", 00276 "Eisenstaadt", 00277 "Fendris", 00278 "Frank", 00279 "Habbi", 00280 "Harlod", 00281 "Ichibod", 00282 "Janus", 00283 "June", 00284 "Magnuson", 00285 "Nandii", 00286 "Nitfeder", 00287 "Norris", 00288 "Parael", 00289 "Penhew", 00290 "Sophia", 00291 "Skilly", 00292 "Tahir", 00293 "Thockmorton", 00294 "Thomas", 00295 "van Helsing", 00296 "van Pelt", 00297 "Voormis", 00298 "Xavier", 00299 "Xeno", 00300 "Zardoz", 00301 "Zagy", 00302 "Albertus Magnus", 00303 }; 00304 00312 int book_overflow(const char *buf1, const char *buf2, size_t booksize) 00313 { 00314 /* 2 less so always room for trailing \n */ 00315 if (buf_overflow(buf1, buf2, BOOK_BUF - 2) || buf_overflow(buf1, buf2, booksize)) 00316 { 00317 return 1; 00318 } 00319 00320 return 0; 00321 } 00322 00325 static void init_msgfile() 00326 { 00327 FILE *fp; 00328 char buf[MAX_BUF], fname[MAX_BUF], *cp; 00329 int comp; 00330 00331 snprintf(fname, sizeof(fname), "%s/messages", settings.datadir); 00332 LOG(llevDebug, "Reading messages from %s...\n", fname); 00333 00334 fp = open_and_uncompress(fname, 0, &comp); 00335 00336 if (fp) 00337 { 00338 int lineno, error_lineno = 0, in_msg = 0; 00339 char msgbuf[HUGE_BUF]; 00340 00341 for (lineno = 1; fgets(buf, sizeof(buf), fp); lineno++) 00342 { 00343 if (*buf == '#' || (*buf == '\n' && !in_msg)) 00344 { 00345 continue; 00346 } 00347 00348 cp = strchr(buf, '\n'); 00349 00350 if (cp) 00351 { 00352 while (cp > buf && (cp[-1] == ' ' || cp[-1] == '\t')) 00353 { 00354 cp--; 00355 } 00356 00357 *cp = '\0'; 00358 } 00359 00360 if (in_msg) 00361 { 00362 if (!strcmp(buf, "ENDMSG")) 00363 { 00364 if (strlen(msgbuf) > BOOK_BUF) 00365 { 00366 LOG(llevBug, "This string exceeded max book buf size:\n"); 00367 LOG(llevBug, " %s\n", msgbuf); 00368 } 00369 00370 num_msgs++; 00371 msgs = realloc(msgs, sizeof(char *) * num_msgs); 00372 msgs[num_msgs - 1] = strdup_local(msgbuf); 00373 in_msg = 0; 00374 } 00375 else if (!buf_overflow(msgbuf, buf, sizeof(msgbuf) - 1)) 00376 { 00377 strcat(msgbuf, buf); 00378 strcat(msgbuf, "\n"); 00379 } 00380 else if (error_lineno != 0) 00381 { 00382 LOG(llevBug, "Truncating book at %s, line %d\n", fname, error_lineno); 00383 error_lineno = 0; 00384 } 00385 } 00386 else if (!strcmp(buf, "MSG")) 00387 { 00388 error_lineno = lineno; 00389 msgbuf[0] = '\0'; 00390 in_msg = 1; 00391 } 00392 else 00393 { 00394 LOG(llevBug, "Syntax error at %s, line %d\n", fname, lineno); 00395 } 00396 } 00397 00398 close_and_delete(fp, comp); 00399 } 00400 00401 LOG(llevDebug, " Done, got %"FMT64U" messages.\n", (uint64) num_msgs); 00402 } 00403 00406 static void init_mon_info() 00407 { 00408 archetype *at; 00409 00410 for (at = first_archetype; at; at = at->next) 00411 { 00412 if (QUERY_FLAG(&at->clone, FLAG_MONSTER)) 00413 { 00414 num_monsters++; 00415 monsters = realloc(monsters, sizeof(object *) * num_monsters); 00416 monsters[num_monsters - 1] = &at->clone; 00417 } 00418 } 00419 00420 LOG(llevDebug, "init_mon_info() got %"FMT64U" monsters...", (uint64) num_monsters); 00421 } 00422 00429 void init_readable() 00430 { 00431 LOG(llevDebug, "Initializing reading data... "); 00432 init_msgfile(); 00433 init_mon_info(); 00434 LOG(llevDebug, " done.\n"); 00435 } 00436 00446 static void new_text_name(object *book, int msgtype) 00447 { 00448 const char *name; 00449 00450 if (book->type != BOOK) 00451 { 00452 return; 00453 } 00454 00455 switch (msgtype) 00456 { 00457 case MSGTYPE_MONSTER: 00458 name = mon_book_name[rndm(1, arraysize(mon_book_name)) - 1]; 00459 break; 00460 00461 case MSGTYPE_ARTIFACT: 00462 name = art_book_name[rndm(1, arraysize(art_book_name)) - 1]; 00463 break; 00464 00465 case MSGTYPE_SPELLPATH: 00466 name = path_book_name[rndm(1, arraysize(path_book_name)) - 1]; 00467 break; 00468 00469 case MSGTYPE_MSGFILE: 00470 default: 00471 name = book_name[rndm(1, arraysize(book_name)) - 1]; 00472 break; 00473 } 00474 00475 FREE_AND_COPY_HASH(book->name, name); 00476 } 00477 00483 static void add_author(object *op, int msgtype) 00484 { 00485 char title[MAX_BUF]; 00486 const char *name; 00487 00488 if (msgtype < 0 || strlen(op->msg) < 5) 00489 { 00490 return; 00491 } 00492 00493 switch (msgtype) 00494 { 00495 case MSGTYPE_MONSTER: 00496 name = mon_author[rndm(1, arraysize(mon_author)) - 1]; 00497 break; 00498 00499 case MSGTYPE_ARTIFACT: 00500 name = art_author[rndm(1, arraysize(art_author)) - 1]; 00501 break; 00502 00503 case MSGTYPE_SPELLPATH: 00504 name = path_author[rndm(1, arraysize(path_author)) - 1]; 00505 break; 00506 00507 case MSGTYPE_MSGFILE: 00508 default: 00509 name = book_author[rndm(1, arraysize(book_author)) - 1]; 00510 } 00511 00512 snprintf(title, sizeof(title), "of %s", name); 00513 FREE_AND_COPY_HASH(op->title, title); 00514 } 00515 00525 static void change_book(object *book, int msgtype) 00526 { 00527 if (book->type != BOOK || book->title) 00528 { 00529 return; 00530 } 00531 00532 /* Random book name */ 00533 new_text_name(book, msgtype); 00534 /* Random author */ 00535 add_author(book, msgtype); 00536 } 00537 00541 object *get_random_mon() 00542 { 00543 /* Safety. */ 00544 if (!monsters || !num_monsters) 00545 { 00546 return NULL; 00547 } 00548 00549 return monsters[rndm(1, num_monsters) - 1]; 00550 } 00551 00558 static char *mon_desc(object *mon, char *buf, size_t size) 00559 { 00560 snprintf(buf, size, "<title>%s</title>\n%s", mon->name, describe_item(mon)); 00561 return buf; 00562 } 00563 00570 static char *mon_info_msg(char *buf, size_t booksize) 00571 { 00572 char tmpbuf[HUGE_BUF], desc[MAX_BUF]; 00573 object *tmp; 00574 00575 /* Preamble */ 00576 strncpy(buf, "<title>Bestiary</title>\nHerein are detailed creatures found in the world around.\n", booksize - 1); 00577 00578 /* Lets print info on as many monsters as will fit in our 00579 * document. */ 00580 while ((tmp = get_random_mon())) 00581 { 00582 snprintf(tmpbuf, sizeof(tmpbuf), "\n%s", mon_desc(tmp, desc, sizeof(desc))); 00583 00584 if (!rndm(0, 6) || book_overflow(buf, tmpbuf, booksize)) 00585 { 00586 break; 00587 } 00588 00589 snprintf(buf + strlen(buf), booksize - strlen(buf), "%s", tmpbuf); 00590 } 00591 00592 return buf; 00593 } 00594 00602 static char *artifact_msg(int level, char *buf, size_t booksize) 00603 { 00604 artifactlist *al; 00605 artifact *art; 00606 int chance, i, type, idx; 00607 int book_entries = level > 5 ? RANDOM () % 3 + RANDOM () % 3 + 2 : RANDOM () % level + 1; 00608 char *final, *ch; 00609 object *tmp = NULL; 00610 StringBuffer *desc; 00611 00612 /* Values greater than 5 create msg buffers that are too big! */ 00613 if (book_entries > 5) 00614 { 00615 book_entries = 5; 00616 } 00617 00618 /* Let's determine what kind of artifact type randomly. 00619 * Right now legal artifacts only come from those listed 00620 * in art_name_array. Also, we check to be sure an artifactlist 00621 * for that type exists! */ 00622 i = 0; 00623 00624 do 00625 { 00626 idx = rndm(1, arraysize(art_name_array)) - 1; 00627 type = art_name_array[idx].type; 00628 al = find_artifactlist(type); 00629 i++; 00630 } 00631 while (al == NULL && i < 10); 00632 00633 /* Unable to find a message */ 00634 if (i == 10) 00635 { 00636 snprintf(buf, booksize, "None"); 00637 return buf; 00638 } 00639 00640 /* There is no reason to start on the artifact list at the beginning. Lets 00641 * take our starting position randomly... */ 00642 art = al->items; 00643 00644 for (i = rndm(1, level) + rndm(0, 1); i > 0; i--) 00645 { 00646 /* Out of stuff, loop back around */ 00647 if (art == NULL) 00648 { 00649 art = al->items; 00650 } 00651 00652 art = art->next; 00653 } 00654 00655 /* Ok, let's print out the contents */ 00656 snprintf(buf, booksize, "<title>Magical %s</title>\nHerein %s detailed %s...\n", art_name_array[idx].name, book_entries > 1 ? "are" : "is", book_entries > 1 ? "some artifacts" : "an artifact"); 00657 00658 /* Artifact msg attributes loop. Let's keep adding entries to the 'book' 00659 * as long as we have space up to the allowed max # (book_entries) */ 00660 while (book_entries > 0) 00661 { 00662 if (art == NULL) 00663 { 00664 art = al->items; 00665 } 00666 00667 desc = stringbuffer_new(); 00668 tmp = get_archetype(art->def_at_name); 00669 give_artifact_abilities(tmp, art); 00670 SET_FLAG(tmp, FLAG_IDENTIFIED); 00671 00672 stringbuffer_append_printf(desc, "\n<title>%s</title>\nIt is ", query_material_name(tmp)); 00673 00674 /* Chance of finding. */ 00675 chance = 100 * ((float) art->chance / al->total_chance); 00676 00677 if (chance >= 20) 00678 { 00679 stringbuffer_append_string(desc, "an uncommon"); 00680 } 00681 else if (chance >= 10) 00682 { 00683 stringbuffer_append_string(desc, "an unusual"); 00684 } 00685 else if (chance >= 5) 00686 { 00687 stringbuffer_append_string(desc, "a rare"); 00688 } 00689 else 00690 { 00691 stringbuffer_append_string(desc, "a very rare"); 00692 } 00693 00694 /* Value of artifact. */ 00695 stringbuffer_append_printf(desc, " item with a value of %s.", cost_string_from_value(tmp->value)); 00696 00697 if ((ch = describe_item(tmp)) && strlen(ch) > 1) 00698 { 00699 stringbuffer_append_printf(desc, "\nProperties of this artifact include:\n %s", ch); 00700 } 00701 00702 final = stringbuffer_finish(desc); 00703 00704 /* Add the buf if it will fit. */ 00705 if (book_overflow(buf, final, booksize)) 00706 { 00707 free(final); 00708 break; 00709 } 00710 00711 snprintf(buf + strlen(buf), booksize - strlen(buf), "%s", final); 00712 free(final); 00713 00714 art = art->next; 00715 book_entries--; 00716 } 00717 00718 return buf; 00719 } 00720 00728 static char *spellpath_msg(int level, char *buf, size_t booksize) 00729 { 00730 int path = rndm(1, NRSPELLPATHS) - 1, prayers = rndm(0, 1); 00731 int i, did_first_sp = 0; 00732 uint32 pnum = spellpathdef[path]; 00733 StringBuffer *desc; 00734 char *final; 00735 00736 desc = stringbuffer_new(); 00737 buf[0] = '\0'; 00738 00739 /* Preamble */ 00740 stringbuffer_append_printf(desc, "<title>Path of %s</title>\nHerein are detailed the names of %s belonging to the path of %s:\n\n", spellpathnames[path], prayers ? "prayers" : "incantations", spellpathnames[path]); 00741 00742 /* Now go through the entire list of spells. Add appropriate spells 00743 * in our message buffer */ 00744 for (i = 0; i < NROFREALSPELLS; i++) 00745 { 00746 if ((prayers && spells[i].type != SPELL_TYPE_PRIEST) || !(pnum & spells[i].path)) 00747 { 00748 continue; 00749 } 00750 00751 /* Book level determines max spell level to show 00752 * thus higher level books are more comprehensive */ 00753 if (spells[i].level > (level * 8)) 00754 { 00755 continue; 00756 } 00757 00758 if (strlen(spells[i].name) + stringbuffer_length(desc) >= booksize) 00759 { 00760 break; 00761 } 00762 00763 if (did_first_sp) 00764 { 00765 stringbuffer_append_string(desc, ",\n"); 00766 } 00767 00768 did_first_sp = 1; 00769 stringbuffer_append_string(desc, spells[i].name); 00770 } 00771 00772 final = stringbuffer_finish(desc); 00773 00774 /* Geez, no spells were generated. */ 00775 if (!did_first_sp) 00776 { 00777 snprintf(buf + strlen(buf), booksize - strlen(buf), "%s\n - no known spells exist -\n", final); 00778 } 00779 else 00780 { 00781 snprintf(buf + strlen(buf), booksize - strlen(buf), "%s\n", final); 00782 } 00783 00784 free(final); 00785 return buf; 00786 } 00787 00792 static char *msgfile_msg(size_t booksize) 00793 { 00794 static char buf[BOOK_BUF]; 00795 char *msg = NULL; 00796 00797 /* Get a random message. */ 00798 if (msgs && num_msgs) 00799 { 00800 msg = msgs[rndm(1, num_msgs) - 1]; 00801 } 00802 00803 if (msg && !book_overflow(buf, msg, booksize)) 00804 { 00805 strncpy(buf, msg, sizeof(buf) - 1); 00806 } 00807 else 00808 { 00809 strncpy(buf, "*undecipherable text*", sizeof(buf) - 1); 00810 } 00811 00812 return buf; 00813 } 00814 00830 void tailor_readable_ob(object *book, int msg_type) 00831 { 00832 char msgbuf[BOOK_BUF]; 00833 int level = book->level ? (RANDOM () % book->level) + 1 : 1; 00834 00835 /* Safety. */ 00836 if (book->type != BOOK) 00837 { 00838 return; 00839 } 00840 00841 /* If no level no point in doing any more... */ 00842 if (level <= 0) 00843 { 00844 return; 00845 } 00846 00847 msg_type = msg_type > 0 ? msg_type : rndm(0, MSGTYPE_NUM); 00848 00849 switch (msg_type) 00850 { 00851 case MSGTYPE_MONSTER: 00852 mon_info_msg(msgbuf, BOOK_BUF); 00853 break; 00854 00855 case MSGTYPE_ARTIFACT: 00856 artifact_msg(level, msgbuf, BOOK_BUF); 00857 break; 00858 00859 case MSGTYPE_SPELLPATH: 00860 spellpath_msg(level, msgbuf, BOOK_BUF); 00861 break; 00862 00863 case MSGTYPE_MSGFILE: 00864 default: 00865 strcpy(msgbuf, msgfile_msg( BOOK_BUF)); 00866 break; 00867 } 00868 00869 /* Safety -- we get ugly map saves/crashes without this */ 00870 strcat(msgbuf, "\n"); 00871 00872 if (strlen(msgbuf) > 1) 00873 { 00874 FREE_AND_COPY_HASH(book->msg, msgbuf); 00875 /* Let's give the "book" a new name, which may be a compound word */ 00876 change_book(book, msg_type); 00877 } 00878 } 00879 00882 void free_all_readable() 00883 { 00884 size_t i; 00885 00886 for (i = 0; i < num_msgs; i++) 00887 { 00888 free(msgs[i]); 00889 } 00890 00891 free(msgs); 00892 LOG(llevDebug, "Freed %"FMT64U" book messages.\n", (uint64) num_msgs); 00893 00894 free(monsters); 00895 LOG(llevDebug, "Freed %"FMT64U" book monsters.\n", (uint64) num_monsters); 00896 }
1.7.4