// $Id: zone.cc,v 1.3.2.5 2000/05/03 02:25:14 justin Exp $
// $Revision: 1.3.2.5 $ $Author: justin $ $Date: 2000/05/03 02:25:14 $
//
//ScryMUD Server Code
//Copyright (C) 1998 Ben Greear
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// To contact the Author, Ben Greear: greear@cyberhighway.net, (preferred)
// greearb@agcs.com
//
#ifdef USEMYSQL
#include "mysql/mysql.h"
#endif
#include "zone.h"
#include "const.h"
#include "misc.h"
#include "misc2.h"
#include "command4.h"
#include "command5.h"
int ZoneList::_cnt = 0; //instance count
extern String UNKNOWN;
ZoneList& ZoneList::instance() {
static ZoneList self;
return self;
}
void ZoneList::readSelf() {
#ifdef USEMYSQL
if (config.useMySQL)
dbReadSelf();
else
#endif
fileReadSelf();
}
#ifdef USEMYSQL
void ZoneList::dbReadSelf() {
long i;
MYSQL_RES* result;
MYSQL_ROW row;
String query="select ZONE_NUM from Zones";
if (mysql_real_query(database, query, strlen(query))==0) {
if ((result=mysql_store_result(database))==NULL) {
if (mudlog.ofLevel(WRN)) {
mudlog << "In ZoneList::dbReadSelf():\n";
mudlog << "Eror retrieving query results: " << mysql_error(database)
<< endl;
}
return;
}
while ((row=mysql_fetch_row(result))) {
i=atol(row[0]);
if ((i >= 0) && (i < NUMBER_OF_ZONES)) {
add(i);
}
else {
if (mudlog.ofLevel(WRN)) {
mudlog << "In ZoneList::dbReadSelf():\n";
mudlog << "Bad zone number " << i << " in Zones table. You may\n"
<< "need to increase NUMBER_OF_ZONES in const.h, then\n"
<< "recompile.\n";
return;
}
}
}
mysql_free_result(result);
}
else {
if (mudlog.ofLevel(WRN)) {
mudlog << "In ZoneList::dbReadSelf():\n";
mudlog << "Error executing query: " << mysql_error(database) << endl;
}
}
}
#endif
void ZoneList::fileReadSelf() {
ifstream dafile("./World/ENABLED_ZONES");
int i;
if (dafile) {
dafile >> i;
while (i != -1) {
if ((i >= 0) && (i < NUMBER_OF_ZONES)) {
add(i);
}//if
dafile >> i;
}//while
}//if
}//read()
void ZoneList::writeSelf() {
// In theory, writing the Zones collection will take care of this
// in the MySQL database.
fileWriteSelf();
}
void ZoneList::fileWriteSelf() {
ofstream dafile("./World/ENABLED_ZONES");
Cell<int*> cll(nums);
int* ptr;
while ((ptr = cll.next())) {
dafile << *ptr << endl;
}
dafile << "-1";
}//write
void ZoneList::add(int i) {
if (!contains(i)) {
nums.append(new int(i));
}//if
}//add
void ZoneList::remove(int i) {
Cell<int*> cll(nums);
int* ptr;
while ((ptr = cll.next())) {
if (*ptr == i) {
nums.lose(cll);
return;
}//if
}//while
}//remove
int ZoneList::contains(int i) {
Cell<int*> cll(nums);
int* ptr;
while ((ptr = cll.next())) {
if (*ptr == i) {
return TRUE;
}//if
}//while
return FALSE;
}
String ZoneList::toString() {
String retval(150);
Cell<int*> cll(nums);
int* ptr;
retval = "These zones are currently open to mortals:\n";
while ((ptr = cll.next())) {
retval.Append(*ptr);
retval.Append(" ");
}//while
return retval;
}//toString
void ZoneList::execute() {
Cell<int*> cll(nums);
int* ptr;
while ((ptr = cll.next())) {
ZoneCollection::instance().zunlock(*ptr);
}//while
}//execute
int ZoneCollection::_cnt = 0;
ZoneCollection& ZoneCollection::instance() {
static ZoneCollection self;
return self;
}
zone& ZoneCollection::elementAt(int i) {
zone* foo = zone_list.elementAt(i);
if (!foo)
return dummy;
else
return *foo;
}//elementAt
zone& ZoneCollection::getZoneFor(room& rm) {
int rm_num = rm.getIdNum();
for (int i = 0; i<NUMBER_OF_ZONES; i++) {
if ((zone_list[i].getBeginRoomNum() <= rm_num) &&
(zone_list[i].getEndRoomNum() >= rm_num)) {
return zone_list[i];
}//if
}//for
if (mudlog.ofLevel(WRN)) {
mudlog << "WARNING: getZone(room: " << rm.getIdNum()
<< ") returning dummy" << endl;
}//if
return dummy;
}//getZoneNum
void ZoneCollection::readSelf() {
int k;
char buf[82];
mudlog.log(DB, "In ZoneCollection::readSelf\n");
ifstream zfile("./World/ZONE_FILE");
if (!zfile) {
mudlog.log(DIS, "ERROR: ZONE_FILE not opened correctly, fatal.\n");
do_shutdown = TRUE;
exit(101);
}//if
zfile >> k;
zfile.getline(buf, 80);
while ((k != -1) && zfile) { //then read it in.
if (!check_l_range(k, 0, NUMBER_OF_ZONES, mob_list[0], FALSE)) {
mudlog.log(ERROR, "ERROR: zone number is out of range, fatal\n");
do_shutdown = TRUE;
exit(100);
}//if
zone_list[k].Read(zfile, k);
zfile >> k;
zfile.getline(buf, 80);
}//while
}//readSelf
void ZoneCollection::doRegeneration() { //for all areas that need it.
int i;
for (i = 0; i<NUMBER_OF_ZONES; i++) {
if (zone_list.elementAtNoCreate(i)) {
if (zone_list[i].isInUse()) {
zone_list[i].decrementTicksTillRegen();
if (zone_list[i].getTicksTillRegen() <= 0) {
if (!room_list[zone_list[i].getEndRoomNum()].isZlocked()) {
update_objects(i, FALSE);
// log("Objects updated.\n");
update_critters(i, FALSE);
update_zone(i, FALSE);
}
zone_list[i].resetTicksTillRegen();
}//if
}//if
}//if
}//for
}//do_regeneration_zones
void ZoneCollection::zunlock(int znum) {
int j;
if ((znum >= 0) && (znum < NUMBER_OF_ZONES)) {
for (j = zone_list[znum].getBeginRoomNum();
j <= zone_list[znum].getEndRoomNum();
j++) {
room_list[j].unlock();
}//for
}
}//zunlock
void ZoneCollection::zlock(int znum) {
int j;
if ((znum >= 0) && (znum < NUMBER_OF_ZONES)) {
for (j = zone_list[znum].getBeginRoomNum();
j <= zone_list[znum].getEndRoomNum();
j++) {
room_list[j].lock();
}//for
}
}//zlock
void ZoneCollection::zlist(critter& pc, int start, int end) {
String buf(100);
Cell<String*> cll;
for (int i = start; i<= end; i++) {
if (zone_list[i].isInUse()) {
zone_list[i].stat(pc);
}//if
else {
Sprintf(buf, "[%i]\tTHIS ZONE UNDEFINED.\n", i);
show(buf, pc);
}//else
}//for
}//zlist
void ZoneCollection::writeWorld(critter& pc) {
String buf(100);
for (int i = 0; i<NUMBER_OF_ZONES; i++) {
if (zone_list[i].isInUse()) {
if (zone_list[i].canWriteRooms()) {
do_write_zone(i);
}
else {
Sprintf(buf,
"Can't write rooms in zone: %i, it's being edited.\n",
i);
show(buf, pc);
}
if (zone_list[i].canWriteMobs()) {
do_amsave(i);
}
else {
Sprintf(buf,
"Can't write mobs in zone: %i, mobs are being edited.\n",
i);
show(buf, pc);
}
if (zone_list[i].canWriteObjects()) {
do_aosave(i);
}
else {
Sprintf(buf,
"Can't write objs in zone: %i, they're being edited.\n",
i);
show(buf, pc);
}
if (zone_list[i].canWriteDoors()) {
do_adsave(i);
}
else {
Sprintf(buf,
"Can't write doors in zone: %i, they're being edited.\n",
i);
show(buf, pc);
}
Sprintf(buf, "Zone %i has been written.\n", i);
show(buf, pc);
}//else
else {
Sprintf(buf, "Zone %i has not been defined yet.\n", i);
show(buf, pc);
}//else
}//for
}//writeWorld
/* helper function, not directly called by player */
void ZoneCollection::writeSelf() {
#ifdef USEMYSQL
if (config.useMySQL)
dbWriteSelf();
else
#endif
fileWriteSelf();
}
void ZoneCollection::fileWriteSelf() {
ofstream dafile("./World/ZONE_FILE");
for (int i = 0; i<NUMBER_OF_ZONES; i++) {
if (zone_list[i].isInUse()) {
dafile << i << "\tBegin of zone\n";
zone_list[i].fileWrite(dafile);
}//if
}//for
dafile << "-1 END OF ZONE FILE\n" << flush;
}//write_zone_list
#ifdef USEMYSQL
void ZoneCollection::dbWriteSelf() {
for (int i = 0; i<NUMBER_OF_ZONES; i++) {
if (zone_list[i].isInUse()) {
zone_list[i].dbWrite();
}
}
}
#endif
void ZoneCollection::createNeatoFiles() {
String buf(100);
for (int i = 0; i<NUMBER_OF_ZONES; i++) {
if (zone_list[i].isInUse()) {
Sprintf(buf, "./World/zone_%i.nto", i);
ofstream ofile(buf);
ofile << zone_list[i].createNeatoMapFile();
}//if
}//for
}//createNeatoFiles
void ZoneCollection::createNewZone(critter& pc, int num_ticks, int num_rooms,
const String& name) {
String buf(100);
/* find the starting room */
int start = 0, i = 0;
for (; i<NUMBER_OF_ZONES; i++) {
if (zone_list[i].getEndRoomNum() > start) {
start = zone_list[i].getEndRoomNum();
}//if
}//for
if ((1 + start + num_rooms) > NUMBER_OF_ROOMS) {
show("ERROR: there isn't room in the database.\n", pc);
show("NUMBER_OF_ROOMS needs to be increased in externs.h.\n", pc);
return;
}//if
start += 1;
int free_zone = -1;
for (i = 0; i< NUMBER_OF_ZONES; i++) {
if (!zone_list[i].isInUse()) {
free_zone = i;
break;
}//if
}//for
if (free_zone == -1) {
show("Not enough zones in database.\n", pc);
show("Increase NUMBER_OF_ZONES in extersn.h.\n", pc);
return;
}//if
if (num_ticks < 10)
num_ticks = 10;
/* good to go */
zone_list[free_zone].setName(name);
zone_list[free_zone].setTicksInRegenCycle(num_ticks);
zone_list[free_zone].setTicksTillRegen(num_ticks);
zone_list[free_zone].setBeginRoomNum(start);
zone_list[free_zone].setEndRoomNum(start + num_rooms - 1);
Sprintf(buf, "cp ./World/DEFAULT_DOORS ./World/doors_%i", free_zone);
system(buf);
Sprintf(buf, "cp ./World/DEFAULT_DOORS ./World/objects_%i", free_zone);
system(buf);
Sprintf(buf, "cp ./World/DEFAULT_DOORS ./World/zone_%i", free_zone);
system(buf);
Sprintf(buf, "cp ./World/DEFAULT_DOORS ./World/critters_%i", free_zone);
system(buf);
show("New zone created.\n", pc);
show("Now you should use 'zenable' to specify zone builders.\n", pc);
writeSelf();
}//createNewZone
///*****************************************************************///
///********************** zone class *****************************///
int zone::_cnt = 0;
zone::zone() {
_cnt++;
ticks_in_regen_cycle = ticks_till_regen = 0;
begin_room_num = end_room_num = 0;
zone_num = 0;
}//default constructor
zone::zone(int id_num) {
_cnt++;
ticks_in_regen_cycle = ticks_till_regen = 0;
begin_room_num = end_room_num = 0;
zone_num = id_num;
}//default constructor
zone::zone(const zone& src) { //copy constructor
_cnt++;
ticks_in_regen_cycle = src.ticks_in_regen_cycle;
ticks_till_regen = src.ticks_till_regen;
begin_room_num = src.begin_room_num;
end_room_num = src.end_room_num;
zone_num = src.zone_num;
}//copy constructor
zone::~zone() {
_cnt--;
clear_ptr_list(owners);
}//~zone
/** create some output that the graph-viz program neato can use to
* create a horribly complex looking graph of the zone!
*/
String zone::createNeatoMapFile() {
String retval(10000);
String buf(100);
// This will create lots of duplicate entries, but the
// neato program should deal with it easier than I can!
Cell<door*> cll;
door* ptr;
String cur_room_name(50);
String dest_room_name(50);
Sprintf(retval, "graph zone_%i {\n\tpage=\"40,80\";\n\tsize=\"40,80\";\n",
zone_num);
for (int i = begin_room_num; i <= end_room_num; i++) {
if (room_list.elementAtNoCreate(i)) {
Sprintf(cur_room_name, "[%i] %S", i, &(room_list[i].short_desc));
//spaceToNewlines(cur_room_name);
room_list[i].doors.head(cll);
while ((ptr = cll.next())) {
if (ptr->getDestRoom()) {
Sprintf(dest_room_name, "[%i] %S",
ptr->getDestRoom()->getIdNum(),
&(ptr->getDestRoom()->short_desc));
//spaceToNewlines(dest_room_name);
if (ptr->getDestRoom()->getZoneNum() != zone_num) {
Sprintf(buf, "\t\"%S\" -- \"%S\" [color=blue];\n",
&cur_room_name, &dest_room_name);
}
else {
Sprintf(buf, "\t\"%S\" -- \"%S\";\n", &cur_room_name,
&dest_room_name);
}
retval.Append(buf);
}//if
}//while
}//if
}//for
retval.Append("}\n");
return retval;
}//createNeatoFile
void zone::spaceToNewlines(String& str) {
for (int i = 0; i<str.Strlen(); i++) {
if (isspace(str[i])) {
str.setCharAt(i, '\n');
}//if
}//for
}//spaceToNewlines
void zone::setEndRoomNum(int i) {
if ((i >= 0) && (i < NUMBER_OF_ROOMS))
end_room_num = i;
}
void zone::setBeginRoomNum(int i) {
if ((i >= 0) && (i < NUMBER_OF_ROOMS))
begin_room_num = i;
}
void zone::addOwner(const String& str) {
owners.append(new String(str));
}//addOwner
int zone::removeOwner(const String& char_name) {
Cell<String*> cll(owners);
String* ptr;
while ((ptr = cll.next())) {
if (strcasecmp(*ptr, char_name) == 0) {
String* ptr2 = ptr;
ptr = owners.lose(cll);
delete ptr2;
ptr2 = NULL;
return TRUE;
}//if
}//while
return FALSE;
}//removeOwner
void zone::stat(critter& pc) {
String buf(100);
Cell<String*> cll;
String* ptr;
Sprintf(buf, "[%i] %S %P35 Begin# %i, End# %i, Ticks in Regen: %i\n",
zone_num, &(zone_name), begin_room_num, end_room_num,
ticks_in_regen_cycle);
show(buf, pc);
show(" Owners: ", pc);
owners.head(cll);
buf = "";
while ((ptr = cll.next())) {
buf.Append(*ptr);
buf.Append(" ");
}//while
buf.Append("\n");
pc.show(buf);
}//stat
String& zone::getFirstOwner() { //returns NONE if has none.
if (owners.isEmpty()) {
return UNKNOWN;
}//if
else {
return *(owners.peekFront());
}//else
}//getFirstOwner
int zone::isOwnedBy(critter& pc) {
if (pc.isImmort()) {
Cell<String*> cll(owners);
String* ptr;
while ((ptr = cll.next())) {
if (strcasecmp(*ptr, *(Top(pc.names))) == 0)
return TRUE;
}//wile
}//if
return FALSE;
}//isOwnedBy
/** znum will be the number for this zone. */
int zone::Read(ifstream& dafile, int znum) {
mudlog.log(DBG, "In zone::read.\n");
char buf[81];
Clear();
if ((znum < 0) || (znum > NUMBER_OF_ZONES)) {
mudlog << "ERROR: znum is out of range: " << znum << endl;
zone_num = 0;
}//if
else {
zone_num = znum;
}
if (!dafile) {
if (mudlog.ofLevel(ERROR)) {
mudlog << "ERROR: da_file FALSE in obj read." << endl;
}
return FALSE;
}
/* zone_name */
dafile.getline(buf, 80);
zone_name = buf;
/* numeric data */
dafile >> ticks_in_regen_cycle >> ticks_till_regen >>
begin_room_num >> end_room_num;
if ((ticks_till_regen < 0) || (ticks_till_regen > ticks_in_regen_cycle)) {
ticks_till_regen = ticks_in_regen_cycle;
}
dafile.getline(buf, 80); //clear junk
//mudlog.log(DBG, "About to read zone_flags.\n");
zone_flags.Read(dafile);
//mudlog.log(DBG, "read em.\n");
short test = TRUE;
while (test) {
if (!dafile) {
if (mudlog.ofLevel(ERROR)) {
mudlog << "ERROR: da_file FALSE in zone read." << endl;
}
return FALSE;
}
dafile >> buf;
mudlog.log(DBG, buf);
if (strcmp(buf, "~") == 0) {
test = FALSE;
}//if
else {
owners.append(new String(buf));
}//else
}//while
dafile.getline(buf, 80); //junk stuff at end of names
dafile.getline(buf, 80); //junk extra line
return TRUE;
}//Read
int zone::fileWrite(ofstream& dafile) {
Cell<String*> st_cell(owners);
String* st_ptr;
dafile << zone_name << endl;
dafile << ticks_in_regen_cycle << " " << ticks_till_regen << " "
<< begin_room_num << " " << end_room_num << endl;
int len = 0;
zone_flags.Write(dafile);
while ((st_ptr = st_cell.next())) {
len += st_ptr->Strlen() + 1;
if (len > 79) {
dafile << endl;
len = 0;
}//if
dafile << *st_ptr << " ";
}//while
dafile << "~" << "\towners\n\n";
return true;
}//fileWrite
#ifdef USEMYSQL
int zone::dbWrite() {
Cell<String*> st_cell(owners);
String query;
String* st_ptr;
query = "DELETE FROM Zones WHERE ZONE_NUM = ";
query += zone_num;
if (mysql_real_query(database, query, strlen(query)) !=0) {
if (mudlog.ofLevel(WRN)) {
mudlog << "In zone::dbWrite():\n";
mudlog << "Eror executing query: " << mysql_error(database)
<< endl;
}
return FALSE;
}
query = "DELETE FROM ZoneOwners WHERE ZONE_NUM = ";
query += zone_num;
if (mysql_real_query(database, query, strlen(query)) !=0) {
if (mudlog.ofLevel(WRN)) {
mudlog << "In zone::dbWrite():\n";
mudlog << "Eror executing query: " << mysql_error(database)
<< endl;
}
return FALSE;
}
query = "INSERT INTO Zones (ZONE_NUM, TICKS_IN_REGEN_CYCLE, ";
query += "TICKS_TILL_REGEN, ZONE_NAME, BEG_ROOM_NUM, END_ROOM_NUM) VALUES ";
query += "("; query += zone_num; query += ", ";
query += ticks_in_regen_cycle; query += ", ";
query += ticks_till_regen; query += ", ";
query += zone_name; query += ", ";
query += begin_room_num; query += ", ";
query += end_room_num;
query += ")";
if (mysql_real_query(database, query, strlen(query)) !=0) {
if (mudlog.ofLevel(WRN)) {
mudlog << "In zone::dbWrite():\n";
mudlog << "Eror executing query: " << mysql_error(database)
<< endl;
}
return false;
}
query = "INSERT INTO ZoneOwners (ZONE_NUM, OWNER) VALUES ";
while ((st_ptr = st_cell.next())) {
query += "(";
query += zone_num; query += ", ";
query += *st_ptr; query += "), ";
}//while
query.dropFromEnd(2);
if (mysql_real_query(database, query, strlen(query)) !=0) {
if (mudlog.ofLevel(WRN)) {
mudlog << "In zone::dbWrite():\n";
mudlog << "Eror executing query: " << mysql_error(database)
<< endl;
}
return false;
}
return true;
}
#endif
void zone::Clear() {
zone_name.Clear();
zone_flags.Clear();
clear_ptr_list(owners);
ticks_in_regen_cycle = ticks_till_regen = begin_room_num = end_room_num = 0;
zone_num = 0;
}//clear
int zone::isTotallyLoaded() {
int i;
for (i = begin_room_num; i <= end_room_num; i++) {
if (room_list.elementAtNoCreate(i) && room_list[i].isInUse()) {
if (room_list[i].isTotalLoaded()) { //if total loaded
return TRUE;
}//if
else {
return FALSE;
}//else
}//if
}//for
if (room_list[begin_room_num].isTotalLoaded())
return TRUE;
else
return FALSE;
}//isTotallyLoaded
/** From mortal intervention, mob procs, vehicle movement.. */
int zone::isLocked() {
int i;
for (i = begin_room_num; i <= end_room_num; i++) {
if (room_list.elementAtNoCreate(i) && room_list[i].isInUse()) {
if (room_list[i].isZlocked()) { //if total loaded
return TRUE;
}//if
else {
return FALSE;
}//else
}//if
}//for
if (room_list[begin_room_num].isZlocked())
return TRUE;
else
return FALSE;
}//isLocked
int zone::canWriteRooms() {
int i;
for (i = begin_room_num; i <= end_room_num; i++) {
if (room_list.elementAtNoCreate(i) && room_list[i].isInUse()
&& (room_list[i].isNotComplete())) {
return FALSE;
}//if
}//for
return TRUE;
}//canWriteRooms
/** Check to make sure there are no mobs that are only half-way
* constructed in OLC.
*/
int zone::canWriteMobs() {
int i;
for (i = 0; i < NUMBER_OF_MOBS; i++) {
if (mob_list[i].isInUse() && mob_list[i].isNotComplete()
&& (mob_list[i].getNativeZoneNum() == zone_num)) {
return FALSE;
}//if
}//for
return TRUE;
}//canWriteMobs
/** Check to make sure there are no doors that are only half-way
* constructed in OLC.
*/
int zone::canWriteDoors() {
int i;
for (i = 0; i < NUMBER_OF_DOORS; i++) {
if (door_list[i].isInUse() && door_list[i].isNotComplete()
&& (door_list[i].getZoneNum() == zone_num)) {
return FALSE;
}//if
}//for
return TRUE;
}//canWriteDoors
/** Check to make sure there are no objects that are only half-way
* constructed in OLC.
*/
int zone::canWriteObjects() {
int i;
for (i = 0; i < NUMBER_OF_ITEMS; i++) {
if (obj_list[i].isInUse() && obj_list[i].isNotComplete()
&& (obj_list[i].getZoneNum() == zone_num)) {
return FALSE;
}//if
}//for
return TRUE;
}//canWriteObjects