/
ScryMUD/mud/
ScryMUD/mud/grrmud/Boards/
ScryMUD/mud/grrmud/Help/
ScryMUD/mud/grrmud/Pfiles/
ScryMUD/mud/grrmud/PlayerSacks/
ScryMUD/mud/grrmud/PlayerShops/
ScryMUD/mud/grrmud/help_filter/
ScryMUD/mud/hegemon/
ScryMUD/mud/hegemon/data/
ScryMUD/mud/hegemon/data/help/battle/
ScryMUD/mud/hegemon/data/help/client/
ScryMUD/mud/hegemon/data/help/communications/
ScryMUD/mud/hegemon/data/help/skills/
ScryMUD/mud/hegemon/data/help/spells/
ScryMUD/mud/include/
ScryMUD/mud/lib/
ScryMUD/mud/lib/bitfield/
ScryMUD/mud/lib/log/
ScryMUD/mud/lib/string2/
// $Id: vehicle.cc,v 1.4.2.4 2000/05/03 02:25:14 justin Exp $
// $Revision: 1.4.2.4 $  $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
//

///*************************  Vehicle  *****************************///

#include "vehicle.h"
#include "const.h"
#include <PtrArray.h>
#include "room.h"
#include "misc.h"
#include "misc2.h"
#include "commands.h"

vehicle::vehicle() {
   path_cells.head(cll);
   cll.next(); //move to first one.

   ticks_till_next_stop = in_room = cur_fuel = max_fuel = 0;
   ticks_between_stops = 0;
}

vehicle::vehicle(int num) : room(num) {
   path_cells.head(cll);
   cll.next(); //move to first one.

   ticks_till_next_stop = in_room = cur_fuel = max_fuel = 0;
   ticks_between_stops = 0;
}


vehicle::~vehicle() {
   clear_ptr_list(path_cells);
}

int vehicle::canEnter(const room* dest_room, int do_msg) const {
   int retval = FALSE;
   String buf(100);

   if (!(hasUnlimitedFuel() || (cur_fuel > 0))) {
      if (do_msg) {
         Sprintf(buf, "%S is out of fuel.\n", &(short_desc));
         showAllCept(buf);
      }//if
   }//if
   else if (!dest_room->isInUse()) { //if not used
      if (do_msg)
         showAllCept("That room doesn't really exist!!\n");
   }//if
   else if (dest_room->isNoVehicle()) { //!vehicle
      if (do_msg)
         showAllCept("Vehicles cannot travel there.\n");
   }//if
   else if (dest_room->needsBoat() && !(canFloat() || canFly())) {
      if (do_msg)
         showAllCept("This vehicle can't travel on water.\n");
   }//if
   else if ((dest_room->needsClimb()) && (!(canClimb() || canFly()))) {
      if (do_msg)
         showAllCept("This vehicle can't climb.\n");
   }//if
   else if ((dest_room->needsDive()) && (!canDive())) {
      if (do_msg)
         showAllCept("This vehicle can't dive.\n");
   }//if
   else if ((dest_room->needsFly()) && (!canFly())) {
      if (do_msg)
         showAllCept("This vehicle can't fly.\n");
   }//if
   else if (dest_room->isZlocked()) { //is zlocked
      if (do_msg)
         showAllCept("That direction has been locked to vehicles.\n");
   }//if
   else
      retval = TRUE;
   return retval;
}//canEnter

void vehicle::setInRoom(int i) {
   if (i < 0)
      i = 0;
   if (i > NUMBER_OF_ROOMS)
      i = 0;

   in_room = i;
}


int vehicle::move() {
   return move(~0, getExitNum(), getExitDirection());
}

void vehicle::stat(critter& pc) {
   room::stat(pc);
   show("\n\t\t\tIS A VEHICLE\nVehicle flags:\n", pc);
   out_field(vehicle_flags, pc, VEHICLE_FLAGS_NAMES);

   String buf(200);

   Sprintf(buf, "ticks_till_next_stop:  %i, in_room:  %i, cur_fuel:  %i\n",
           ticks_till_next_stop, in_room, cur_fuel);
   show(buf, pc);

   Sprintf(buf, "Max fuel:  %i, ticks_between_stops:  %i\n",
           max_fuel, ticks_between_stops);
   show(buf, pc);
}//stat

int vehicle::move(int see_bit, int i_th, const String& exit_direction) {
   int dest;
   String buf(200);

   if (mudlog.ofLevel(DBG)) {
      mudlog << "In vehicle::move, in_room:  " << in_room 
             << "  see_bit:  " << see_bit << "  i_th:  " << i_th
             << " exit dir -:" << exit_direction << ":-" << endl;
   }

   ticks_till_next_stop = ticks_between_stops;

   door* exit_dr_ptr = door::findDoor(room_list[in_room].DOORS, i_th,
                                      &exit_direction, see_bit,
                                      room_list[in_room].getVisBit());

   //log("Got exit_dr_ptr.\n");

   if (!exit_dr_ptr) {
      //mudlog.log(WRN, "WARNING:  destination dr_ptr is NULL in move_room.\n");
      Sprintf(buf, "Your vehicle cannot go %S.\n", &exit_direction);
      showAllCept(buf);
      return FALSE;
   }//if

   dest = abs(exit_dr_ptr->destination);

   if (mudlog.ofLevel(DBG)) {
      mudlog << "got dest:  " << dest << endl;
   }

   if (!canEnter(&room_list[dest], TRUE)) {
      mudlog.log(WRN, 
                 "WARNING:  vehicle::can_enter failed in vehicle::move\n");
      return FALSE;
   }//if

   ticks_till_next_stop += exit_dr_ptr->distance;

   if (mudlog.ofLevel(DBG)) {
      mudlog << "Passed tests, looking for doors to vehicle, size list:  "
             << room_list[in_room].DOORS.size() << endl;
   }

   /* passed all tests, lets move it!! */

   /* delete door(s) TO the car */
   Cell<door*> dcll(room_list[in_room].DOORS);
   door* tmp_ptr = NULL;

   door* dr_ptr = dcll.next();
   while (dr_ptr) {
      if (mudlog.ofLevel(DBG)) {
         mudlog << "Comparing dest:  " << abs(dr_ptr->destination)
                << "  and this room num:  " << getRoomNum() << endl;
      }
      if (abs(dr_ptr->destination) == getRoomNum()) {
         //mudlog.log(DBG, "They were equal.\n");
         tmp_ptr = dr_ptr;
         dr_ptr = room_list[in_room].DOORS.lose(dcll);
      }//if
      else {
         //mudlog.log(DBG, "They were not equal.");
         dr_ptr = dcll.next();
      }
   }//if

   /* save tmp_ptr, it can be reused in next room */
   mudlog.log(DBG, "Found tmp_ptr.\n");

   if (!tmp_ptr) {  //the train door, the exterior one
      mudlog.log(ERROR, "ERROR:  tmp_ptr is NULL in vehicle::move()\n");
      return FALSE;
   }//if

   if (!tmp_ptr->dr_data) {
      mudlog.log(ERROR, "ERROR:  dr_data is null.\n");
      return FALSE;
   }//if

   mudlog.log(DBG, "About to do messages.\n");

   dr_ptr = NULL;
   // lets find the door from the train to the outside.
   DOORS.head(dcll); //head of vehicle doors
   while ((dr_ptr = dcll.next())) {
      if (dr_ptr->dr_data == tmp_ptr->dr_data) {
         break;
      }
   }//while
      

   /* show messages to room, the one BEFORE the move */
   if (!isStealthy()) { //if not stealth
      if (isAtDestination() && 
          tmp_ptr->dr_data->isOpen() &&
          tmp_ptr->dr_data->canClose()) {
         if (dr_ptr) {
            Sprintf(buf, "The %S closes.\n", name_of_door(*dr_ptr, ~0));
            showAllCept(buf);
         }
         else {
            mudlog.log(ERROR, "ERROR:  dr_ptr is NULL...\n");
         }
         
         Sprintf(buf, "The %S closes.\n", name_of_door(*tmp_ptr, ~0));
         room_list[in_room].showAllCept(buf);

         tmp_ptr->dr_data->close(); //make it closed
      }//if

      Sprintf(buf, "%S leaves towards the %S.\n", &(short_desc),
              &exit_direction);
      room_list[in_room].showAllCept(buf);
   }//if not stealth

   /*  make vehicle door exit to new room */

   mudlog.log(DBG, "About to update vehicle door.\n");
   if (!hasUnlimitedFuel()) {
      cur_fuel -= room_list[in_room].getMovCost();
   }

   DOORS.head(dcll); //list of doors to/from vehicle
   while ((dr_ptr = dcll.next())) {
      if (abs(dr_ptr->destination) == abs(in_room)) { //if is_vehicle_exit
         if (dr_ptr->destination < 0)
            dr_ptr->destination = -(abs(dest));
         else
            dr_ptr->destination = abs(dest); 
         break; 
      }//if
   }//while
   /* vehicle door now points to next room */

   in_room = dest;  //guaranteed to be positive


   mudlog.log(ERROR, "Vehicle door updated.\n");

             /* add door to destination room */
   room_list[in_room].DOORS.prepend(tmp_ptr);

   advancePathIterator(); /* advance the internal state, let this object
                             know that it's actually been moved. */

   mudlog.log(ERROR, "In new room..doing messages.\n");

   /* now in new room, do some messages if needed */
   if (!isStealthy()) { //if not stealty
      Sprintf(buf, "%S has arrived.\n", &(short_desc));
      room_list[in_room].showAllCept(buf);
   }//if not stealth


   dr_ptr = NULL;
   // lets find the door from the train to the outside.
   DOORS.head(dcll); //head of vehicle doors
   while ((dr_ptr = dcll.next())) {
      if (dr_ptr->dr_data == tmp_ptr->dr_data) {
         break;
      }
   }//while

   if (isAtDestination()) { //in other words, the one its in NOW
      if (!isStealthy() && tmp_ptr->dr_data->isClosed()) {
         if (dr_ptr) {
            Sprintf(buf, "The %S opens.\n", name_of_door(*dr_ptr, ~0));
            showAllCept(buf);
         }
         else {
            mudlog.log(ERROR, "ERROR:  dr_ptr is NULL, end of move.\n");
         }

         if (tmp_ptr) {
            Sprintf(buf, "The %S opens.\n", name_of_door(*tmp_ptr, ~0));
            room_list[in_room].showAllCept(buf);
         }
         else {
            mudlog.log(ERROR, "ERROR:  tmp_ptr is NULL, end of move.\n");
         }

      }//if
      tmp_ptr->dr_data->open(); //make it open
   }//if

   buf = getPassengerMessage();
   if (buf.Strlen() > 0) {
      showAllCept(buf);
   }//if

   if (vehicle_flags.get(7)) { //if can see out
      critter* cptr;
      Cell<critter*> ccll(CRITTERS);

      while ((cptr = ccll.next())) {
         do_look(1, &NULL_STRING, *cptr, room_list[in_room],
                 TRUE); /* ignore brief */
      }//while
   }//if can see out

   return TRUE;

}//move


// If cll is currently 'pointing' to a room that
// is a destination, return true
int vehicle::isAtDestination() {
   PathCell* data = cll.item();
   
   return (data && data->isDest());
}

int vehicle::isSelfGuided() const { return vehicle_flags.get(0); }
int vehicle::isStealthy() const { return vehicle_flags.get(1); }
int vehicle::hasUnlimitedFuel() const { return vehicle_flags.get(2); }
int vehicle::canFly() const { return vehicle_flags.get(3); }
int vehicle::canClimb() const { return (vehicle_flags.get(4) || canFly()); }
int vehicle::canFloat() const { return (vehicle_flags.get(5) || canFly()); }
int vehicle::canDive() const { return vehicle_flags.get(6); }
void vehicle::decrementTicksTillNextStop() { ticks_till_next_stop--; }
int vehicle::getTicksTillNextStop() const { return ticks_till_next_stop; }

void vehicle::advancePathIterator() {
   //iterate past end of the list.
   if (!cll.next())
      cll.next();
}


String vehicle::getPassengerMessage() {
   PathCell* ptr = cll.item();
   if (ptr)
      return ptr->getDesc();
   else
      return UNKNOWN_DESC;
}

String vehicle::getExitDirection() {
   PathCell* ptr = cll.item();
   if (ptr)
      return ptr->getDir();
   else
      return UNKNOWN;
}

// get the i_th exit value
int vehicle::getExitNum() {
   PathCell* ptr = cll.item();
   if (ptr)
      return ptr->getDirNum();
   else
      return 1;
}


void vehicle::Clear() {
   room::Clear();
   vehicle_flags.Clear();
   clear_ptr_list(path_cells);
   path_cells.head(cll);
   ticks_till_next_stop = in_room = cur_fuel = max_fuel = 0;
   ticks_between_stops = 0;
}



void vehicle::showPaths(critter& pc) {
   Cell<PathCell*> cell(path_cells);
   PathCell* ptr;
   int i = 0;

   while ((ptr = cell.next())) {
      ptr->stat(getRoomNum(), i, pc);
      pc.show("\n");
      i++;
   }
}//showPaths

void vehicle::insertPathCell(int index, PathCell* pcell) {
   path_cells.insertAt(index, pcell);
}

PathCell* vehicle::hasPathCell(int path_cell_num) {
   return path_cells.elementAt(path_cell_num);
}

void vehicle::remPathCell(int path_cell_num) {//deletes it from memory
   PathCell* ptr = path_cells.removeAt(path_cell_num);
   if (ptr)
      delete ptr;

   //now, reset the cell pointing to the next direction...
   path_cells.head(cll);
   cll.next();
}

void vehicle::setPathPointer(int index) {
   if ((path_cells.size() > index) || (index < 0))
      index = 0;

   PathCell* ptr;
   path_cells.head(cll);
   int i = 0;

   while ((ptr = cll.next())) {
      if (i == index) {
         return;
      }//if
      i++;
   }//while
}//setPathPointer
   
// Must use the number as it's stored in the text files for veh_num
#ifdef USEMYSQL
void vehicle::dbRead(int veh_num, short read_all) {
   MYSQL_RES* result;
   MYSQL_ROW row;
   String query="select * from Vehicles where ROOM_NUM=";
   query+=veh_num;

   Clear();
   room::dbRead(veh_num, read_all);
   cur_stats[2] = cur_stats[2] & ~(0x01000000);

   if (mysql_real_query(database, query, strlen(query))==0) {
      if ((result=mysql_store_result(database))==NULL) {
         if (mudlog.ofLevel(WRN)) {
            mudlog << "In [function]:\n";
            mudlog << "Error retrieving query results: "
                   << mysql_error(database) << endl;
         }
         return;
      }
      row=mysql_fetch_row(result);

      in_room = atoi(row[VEHTBL_IN_ROOM]);
      cur_fuel = atoi(row[VEHTBL_CUR_FUEL]);
      max_fuel = atoi(row[VEHTBL_MAX_FUEL]);
      ticks_between_stops = atoi(row[VEHTBL_TICKS_BETWEEN_STOPS]);
      ticks_till_next_stop = ticks_between_stops;

      vehicle_flags.set(VEHFLAG_SELF_GUIDED, atoi(row[VEHTBL_SELF_GUIDED]));
      vehicle_flags.set(VEHFLAG_STEALTHY, atoi(row[VEHTBL_STEALTHY]));
      vehicle_flags.set(VEHFLAG_UNLIMITED_FUEL, atoi(row[VEHTBL_UNLIMITED_FUEL]));
      vehicle_flags.set(VEHFLAG_FLIES, atoi(row[VEHTBL_FLIES]));
      vehicle_flags.set(VEHFLAG_CLIMBS, atoi(row[VEHTBL_CLIMBS]));
      vehicle_flags.set(VEHFLAG_FLOATS, atoi(row[VEHTBL_FLOATS]));
      vehicle_flags.set(VEHFLAG_DIVES, atoi(row[VEHTBL_DIVES]));
      vehicle_flags.set(VEHFLAG_CAN_SEE_OUT, atoi(row[VEHTBL_CAN_SEE_OUT]));

      mysql_free_result(result);
   }
   else {
      if (mudlog.ofLevel(WRN)) {
         mudlog << "In [function]:\n";
         mudlog << "Error executing query: " << mysql_error(database) << endl;
      }
      return;
   }
}
#endif

void vehicle::fileRead(ifstream& da_file, short read_all) {
   Clear();
   room::fileRead(da_file, read_all);


   char tmp[81];
   //now, read in vehicle specific stuff...

   vehicle_flags.Read(da_file);

   da_file.getline(tmp, 80);

   int sentinel;
   PathCell* ptr;
   da_file >> sentinel;
   da_file.getline(tmp, 80);
   while ((sentinel != -1) && da_file) {
      ptr = new PathCell();
      ptr->Read(da_file);
      Put(ptr, path_cells);

      da_file >> sentinel;
      da_file.getline(tmp, 80);
   }

   path_cells.head(cll);
   cll.next();
   //da_file.getline(tmp, 80);

   da_file >> ticks_till_next_stop >> in_room >> cur_fuel >> max_fuel;
   da_file >> ticks_between_stops;
      
   da_file.getline(tmp, 80);
}//Read


void vehicle::Write(ofstream& da_file) {
   room::Write(da_file);
   vehicle_flags.Write(da_file);

   da_file << endl;

   Cell<PathCell*> pcell(path_cells);
   PathCell* ptr;
   while ((ptr = pcell.next())) {
      da_file << "1  Beginning of PathCell\n";
      ptr->Write(da_file);
   }

   da_file << "-1 End of PathCells..\n";

   da_file << ticks_till_next_stop << " " << in_room << " " << cur_fuel 
           << " " << max_fuel << " " << ticks_between_stops
           << "  End of Vehicle" << endl;
}//write

void vehicle::normalize() {
   room::normalize();
   path_cells.head(cll);
}//normalize_room