Atrinik Server 2.5
server/readable.c
Go to the documentation of this file.
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 }