/*************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * Much time and thought has gone into this software and you are * * benefitting. We hope that you share your changes too. What goes * * around, comes around. * ***************************************************************************/ /* MurkMUD++ - A Windows compatible, C++ compatible Merc 2.2 Mud. \author Jon A. Lambert \date 01/02/2007 \version 1.5 \remarks This source code copyright (C) 2005, 2006, 2007 by Jon A. Lambert All rights reserved. Use governed by the MurkMUD++ public license found in license.murk++ */ #include "os.hpp" #include "config.hpp" #include "globals.hpp" #include "io.hpp" #include "utils.hpp" #include "database.hpp" #include "shop.hpp" #include "note.hpp" #include "affect.hpp" #include "exit.hpp" #include "reset.hpp" #include "area.hpp" #include "room.hpp" #include "objproto.hpp" #include "object.hpp" #include "mobproto.hpp" #include "extra.hpp" #include "world.hpp" Database* Database::_instance = 0; Database* Database::instance() { if (_instance == 0) _instance = new Database; return _instance; } Database::Database() { database = NULL; fBootDb = false; } Database::~Database() { } bool Database::initialize(std::string name) { if (database != NULL) return true; if(sqlite3_open(name.c_str(), &database)) { fatal_printf("Can't open database: %s.", sqlite3_errmsg(database)); return false; } return true; } void Database::shutdown(void) { if (database != NULL) { sqlite3_close(database); database = NULL; } } void Database::boot(void) { fBootDb = true; /* * Seed random number generator. */ OS_SRAND ((unsigned int)std::time (NULL)); // Set welcome screen load_greeting(); /* * Read in all the area files. */ std::ifstream fpList; std::ifstream fp; fpList.open (AREA_LIST, std::ifstream::in | std::ifstream::binary); if (!fpList.is_open()) { fatal_printf (AREA_LIST); } for (;;) { strArea = fread_word (fpList); if (strArea[0] == '$') break; fp.open (strArea.c_str(), std::ifstream::in | std::ifstream::binary); if (!fp.is_open()) { fatal_printf (strArea.c_str()); } fpArea = &fp; for (;;) { std::string word; if (fread_letter (fp) != '#') { fatal_printf ("Boot_db: # not found."); } word = fread_word (fp); if (word[0] == '$') break; else if (!str_cmp (word, "AREA")) load_area (fp); else if (!str_cmp (word, "MOBILES")) load_mobiles (fp); else if (!str_cmp (word, "MOBPROGS")) load_mobprogs (fp); else if (!str_cmp (word, "OBJECTS")) load_objects (fp); else if (!str_cmp (word, "RESETS")) load_resets (fp); else if (!str_cmp (word, "ROOMS")) load_rooms (fp); else if (!str_cmp (word, "SHOPS")) load_shops (fp); else if (!str_cmp (word, "SPECIALS")) load_specials (fp); else { fatal_printf ("Boot_db: bad section name."); } } fp.close(); fpArea = NULL; } fpList.close(); /* * Fix up exits. * Declare db booting over. * Reset all areas once. * Load up the notes file. * Set the MOBtrigger. */ fix_exits (); fBootDb = false; load_notes (); MOBtrigger = true; return; } void Database::load_area (std::ifstream & fp) { Area *pArea = new Area(); pArea->name = fread_string (fp); g_world->add_area(pArea); area_last = pArea; return; } void Database::load_greeting(void) { sqlite3_stmt *stmt = NULL; Database* db = Database::instance(); char *sql = sqlite3_mprintf("SELECT text FROM helps WHERE keyword = 'GREETING'"); if (sqlite3_prepare(db->database, sql, -1, &stmt, 0) != SQLITE_OK) { bug_printf("Could not prepare statement: '%s' Error: %s", sql, sqlite3_errmsg(db->database)); sqlite3_free(sql); return; } if (sqlite3_step(stmt) == SQLITE_ROW) { help_greeting.assign((const char*)sqlite3_column_text( stmt, 0 )); } sqlite3_finalize(stmt); sqlite3_free(sql); return; } void Database::load_mobiles (std::ifstream & fp) { MobPrototype *pMobIndex; for (;;) { sh_int vnum; char letter; letter = fread_letter (fp); if (letter != '#') { fatal_printf ("Load_mobiles: # not found."); } vnum = fread_number (fp); if (vnum == 0) break; fBootDb = false; if (get_mob_index (vnum) != NULL) { fatal_printf ("Load_mobiles: vnum %d duplicated.", vnum); } fBootDb = true; pMobIndex = new MobPrototype(); pMobIndex->vnum = vnum; pMobIndex->name = fread_string (fp); pMobIndex->short_descr = fread_string (fp); pMobIndex->long_descr = fread_string (fp); pMobIndex->description = fread_string (fp); pMobIndex->long_descr[0] = toupper (pMobIndex->long_descr[0]); pMobIndex->description[0] = toupper (pMobIndex->description[0]); pMobIndex->actflags = fread_number (fp) | ACT_IS_NPC; pMobIndex->affected_by = fread_number (fp); pMobIndex->pShop = NULL; pMobIndex->alignment = fread_number (fp); letter = fread_letter (fp); pMobIndex->level = number_fuzzy (fread_number (fp)); pMobIndex->sex = fread_number (fp); if (letter != 'S') { fatal_printf ("Load_mobiles: vnum %d non-S.", vnum); } letter = fread_letter (fp); if (letter == '>') { fp.unget(); mprog_read_programs (fp, pMobIndex); } else fp.unget(); mob_table.insert (std::map<int, MobPrototype*>::value_type (vnum, pMobIndex)); kill_table[URANGE (0, pMobIndex->level, MAX_LEVEL - 1)].number++; } return; } void Database::load_objects (std::ifstream & fp) { ObjectPrototype *pObjIndex; for (;;) { sh_int vnum; char letter; letter = fread_letter (fp); if (letter != '#') { fatal_printf ("Load_objects: # not found."); } vnum = fread_number (fp); if (vnum == 0) break; fBootDb = false; if (get_obj_index (vnum) != NULL) { fatal_printf ("Load_objects: vnum %d duplicated.", vnum); } fBootDb = true; pObjIndex = new ObjectPrototype(); pObjIndex->vnum = vnum; pObjIndex->name = fread_string (fp); pObjIndex->short_descr = fread_string (fp); pObjIndex->description = fread_string (fp); /* Action description */ fread_string (fp); pObjIndex->short_descr[0] = tolower (pObjIndex->short_descr[0]); pObjIndex->description[0] = toupper (pObjIndex->description[0]); pObjIndex->item_type = fread_number (fp); pObjIndex->extra_flags = fread_number (fp); pObjIndex->wear_flags = fread_number (fp); pObjIndex->value[0] = fread_number (fp); // Translate spells into internal "skill numbers." switch (pObjIndex->item_type) { case ITEM_PILL: case ITEM_POTION: case ITEM_SCROLL: pObjIndex->value[1] = skill_lookup (fread_word (fp)); pObjIndex->value[2] = skill_lookup (fread_word (fp)); pObjIndex->value[3] = skill_lookup (fread_word (fp)); break; case ITEM_STAFF: case ITEM_WAND: pObjIndex->value[1] = fread_number (fp); pObjIndex->value[2] = fread_number (fp); pObjIndex->value[3] = skill_lookup (fread_word (fp)); break; default: pObjIndex->value[1] = fread_number (fp); pObjIndex->value[2] = fread_number (fp); pObjIndex->value[3] = fread_number (fp); } pObjIndex->weight = fread_number (fp); pObjIndex->cost = fread_number (fp); /* Unused */ /* Cost per day */ fread_number (fp); if (pObjIndex->item_type == ITEM_POTION) SET_BIT (pObjIndex->extra_flags, ITEM_NODROP); for (;;) { char letter; letter = fread_letter (fp); if (letter == 'A') { Affect* paf = new Affect(); paf->type = -1; paf->duration = -1; paf->location = fread_number (fp); paf->modifier = fread_number (fp); paf->bitvector = 0; pObjIndex->affected.push_back(paf); } else if (letter == 'E') { ExtraDescription* ed = new ExtraDescription(); ed->keyword = fread_string (fp); ed->description = fread_string (fp); pObjIndex->extra_descr.push_back(ed); } else { fp.unget(); break; } } obj_table.insert (std::map<int, ObjectPrototype*>::value_type (vnum, pObjIndex)); } return; } void Database::load_resets (std::ifstream & fp) { Reset *pReset; if (area_last == NULL) { fatal_printf ("Load_resets: no #AREA seen yet."); } for (;;) { Room *pRoomIndex; Exit *pexit; char letter; if ((letter = fread_letter (fp)) == 'S') break; if (letter == '*') { fread_to_eol (fp); continue; } pReset = new Reset(); pReset->command = letter; /* if_flag */ fread_number (fp); pReset->arg1 = fread_number (fp); pReset->arg2 = fread_number (fp); pReset->arg3 = (letter == 'G' || letter == 'R') ? 0 : fread_number (fp); fread_to_eol (fp); /* * Validate parameters. * We're calling the index functions for the side effect. */ switch (letter) { default: fatal_printf ("Load_resets: bad command '%c'.", letter); break; case 'M': get_mob_index (pReset->arg1); get_room_index (pReset->arg3); break; case 'O': get_obj_index (pReset->arg1); get_room_index (pReset->arg3); break; case 'P': get_obj_index (pReset->arg1); get_obj_index (pReset->arg3); break; case 'G': case 'E': get_obj_index (pReset->arg1); break; case 'D': pRoomIndex = get_room_index (pReset->arg1); if (pReset->arg2 < 0 || pReset->arg2 > 5 || (pexit = pRoomIndex->exit[pReset->arg2]) == NULL || !IS_SET (pexit->exit_info, EX_ISDOOR)) { fatal_printf ("Load_resets: 'D': exit %d not door.", pReset->arg2); } if (pReset->arg3 < 0 || pReset->arg3 > 2) { fatal_printf ("Load_resets: 'D': bad 'locks': %d.", pReset->arg3); } break; case 'R': // pRoomIndex = get_room_index (pReset->arg1); get_room_index (pReset->arg1); if (pReset->arg2 < 0 || pReset->arg2 > 6) { fatal_printf ("Load_resets: 'R': bad exit %d.", pReset->arg2); } break; } area_last->reset_list.push_back(pReset); } return; } void Database::load_rooms (std::ifstream & fp) { Room *pRoomIndex; if (area_last == NULL) { fatal_printf ("Load_resets: no #AREA seen yet."); } for (;;) { sh_int vnum; char letter; int door; letter = fread_letter (fp); if (letter != '#') { fatal_printf ("Load_rooms: # not found."); } vnum = fread_number (fp); if (vnum == 0) break; fBootDb = false; if (get_room_index (vnum) != NULL) { fatal_printf ("Load_rooms: vnum %d duplicated.", vnum); } fBootDb = true; pRoomIndex = new Room(); pRoomIndex->area = area_last; pRoomIndex->vnum = vnum; pRoomIndex->name = fread_string (fp); pRoomIndex->description = fread_string (fp); /* Area number */ fread_number (fp); pRoomIndex->room_flags = fread_number (fp); pRoomIndex->sector_type = fread_number (fp); pRoomIndex->light = 0; for (door = 0; door <= 5; door++) pRoomIndex->exit[door] = NULL; for (;;) { letter = fread_letter (fp); if (letter == 'S') break; if (letter == 'D') { Exit *pexit; int locks; door = fread_number (fp); if (door < 0 || door > 5) { fatal_printf ("Fread_rooms: vnum %d has bad door number.", vnum); } pexit = new Exit(); pexit->description = fread_string (fp); pexit->name = fread_string (fp); pexit->exit_info = 0; locks = fread_number (fp); pexit->key = fread_number (fp); pexit->vnum = fread_number (fp); switch (locks) { case 1: pexit->exit_info = EX_ISDOOR; break; case 2: pexit->exit_info = EX_ISDOOR | EX_PICKPROOF; break; } pRoomIndex->exit[door] = pexit; } else if (letter == 'E') { ExtraDescription *ed; ed = new ExtraDescription(); ed->keyword = fread_string (fp); ed->description = fread_string (fp); pRoomIndex->extra_descr.push_back(ed); } else { fatal_printf ("Load_rooms: vnum %d has flag not 'DES'.", vnum); } } room_table.insert (std::map<int, Room*>::value_type (vnum, pRoomIndex)); } return; } void Database::load_shops (std::ifstream & fp) { Shop *pShop; for (;;) { MobPrototype *pMobIndex; int iTrade; pShop = new Shop(); pShop->keeper = fread_number (fp); if (pShop->keeper == 0) break; for (iTrade = 0; iTrade < MAX_TRADE; iTrade++) pShop->buy_type[iTrade] = fread_number (fp); pShop->profit_buy = fread_number (fp); pShop->profit_sell = fread_number (fp); pShop->open_hour = fread_number (fp); pShop->close_hour = fread_number (fp); fread_to_eol (fp); pMobIndex = get_mob_index (pShop->keeper); pMobIndex->pShop = pShop; shop_list.push_back(pShop); } return; } void Database::load_specials (std::ifstream & fp) { for (;;) { MobPrototype *pMobIndex; char letter; switch (letter = fread_letter (fp)) { default: fatal_printf ("Load_specials: letter '%c' not *MS.", letter); case 'S': return; case '*': break; case 'M': pMobIndex = get_mob_index (fread_number (fp)); pMobIndex->spec_fun = spec_lookup (fread_word (fp)); if (pMobIndex->spec_fun == 0) { fatal_printf ("Load_specials: 'M': vnum %d.", pMobIndex->vnum); } break; } fread_to_eol (fp); } } void Database::load_notes (void) { std::ifstream fp; fp.open (NOTE_FILE, std::ifstream::in | std::ifstream::binary); if (!fp.is_open()) return; for (;;) { Note *pnote; char letter; do { letter = fp.get(); if (fp.eof()) { fp.close(); return; } } while (isspace (letter)); fp.unget(); pnote = new Note(); if (str_cmp (fread_word (fp), "sender")) break; pnote->sender = fread_string (fp); if (str_cmp (fread_word (fp), "date")) break; pnote->date = fread_string (fp); if (str_cmp (fread_word (fp), "stamp")) break; pnote->date_stamp = fread_number (fp); if (str_cmp (fread_word (fp), "to")) break; pnote->to_list = fread_string (fp); if (str_cmp (fread_word (fp), "subject")) break; pnote->subject = fread_string (fp); if (str_cmp (fread_word (fp), "text")) break; pnote->text = fread_string (fp); note_list.push_back(pnote); } strArea = NOTE_FILE; fpArea = &fp; fatal_printf ("Load_notes: bad key word."); return; } /* * Translate all room exits from virtual to real. * Has to be done after all rooms are read in. * Check for bad reverse exits. */ void Database::fix_exits (void) { Room *to_room; Exit *pexit; Exit *pexit_rev; int door; std::map<int,Room*>::iterator proom; for (proom = room_table.begin(); proom != room_table.end(); proom++) { bool fexit; fexit = false; for (door = 0; door <= 5; door++) { if ((pexit = (*proom).second->exit[door]) != NULL) { fexit = true; if (pexit->vnum <= 0) pexit->to_room = NULL; else pexit->to_room = get_room_index (pexit->vnum); } } if (!fexit) SET_BIT ((*proom).second->room_flags, ROOM_NO_MOB); } for (proom = room_table.begin(); proom != room_table.end(); proom++) { for (door = 0; door <= 5; door++) { if ((pexit = (*proom).second->exit[door]) != NULL && (to_room = pexit->to_room) != NULL && (pexit_rev = to_room->exit[rev_dir[door]]) != NULL && pexit_rev->to_room != (*proom).second) { bug_printf ("Fix_exits: %d:%d -> %d:%d -> %d.", (*proom).second->vnum, door, to_room->vnum, rev_dir[door], (pexit_rev->to_room == NULL) ? 0 : pexit_rev->to_room->vnum); } } } return; }