pdirt/data/
pdirt/data/HELP/
pdirt/data/HELP/0/
pdirt/data/HELP/F/
pdirt/data/HELP/G/
pdirt/data/HELP/H/
pdirt/data/HELP/J/
pdirt/data/HELP/K/
pdirt/data/HELP/O/
pdirt/data/HELP/Q/
pdirt/data/HELP/R/
pdirt/data/HELP/U/
pdirt/data/HELP/V/
pdirt/data/HELP/Y/
pdirt/data/HELP/Z/
pdirt/data/MESSAGES/
pdirt/data/POWERINFO/
pdirt/data/WIZ_ZONES/
pdirt/drv/
pdirt/drv/bin/
pdirt/drv/compiler/converter/
pdirt/drv/compiler/libs/
pdirt/drv/compiler/scripts/
pdirt/drv/include/AberChat/
pdirt/drv/include/InterMud/
pdirt/drv/include/machine/
pdirt/drv/src/InterMud/
pdirt/drv/src/Players/
pdirt/drv/utils/UAFPort/
pdirt/drv/utils/dnsresolv/
pdirt/drv/utils/gdbm/
/****************************************************************************
 ** Project    : pDirt (Aber IV MUD Daemon)
 ** Module     : Ferries.c
 ** Description: A system to allow passage between various ports/jetty's
 **              across the whole mud.
 **              It originally was meant to work ferries, but has been 
 **              expanded so it can be used for more than just ferries.
 ** Author     : Peter Eussen (Marty) 
 ** Copyright  : This system is part of the Pdirt code.
 ** Date       : 30 June 1996
 ** Version    : 0.4
 ****************************************************************************/
 
/** DOCUMENTATION

 Lets first define Automated Transport. Automated Transport is a loaction in
 the game, that moves between two or more other locations, and need a 
 specified time to do this. Examples of Automated Transports (AT's) are:
 ferries, elevators, public transport etc.
 
 This is a system that is designed to handle multiple Automated Transports
 that move between a given number of ports/jetty's. The amount of At's you
 can handle is determined by the amount of memory you have and by the 
 machine speed.
 
 It uses a linked list to administrate all the data needed to work the 
 AT's. All data is contained in a single struct which can be found
 by using the function find_AT().
 
 How to make a Automated Transport:
 1) Make a location which is the interior of the AT.
 2) Make 2 or more locations which will serve as 'ports'.
 3) Give the AT a function which is called at E_ONINIT. Use this
    function to registrate the AT to the AT-system, by calling
    register_AT (only at startup)
 4) Remove the ferry by using the remove_AT() function.

 REQUIREMENTS:
 
 I'm not a perfect coder, so there are some requirements.
 - The size of the ports array should equal the size of the portnames
   array, and should not be 0. 
 - The last location in the ports should be -1 (terminator)
 - The start location should be in the ports array
 - The size of the ports array should not be 0
 - The AT name should not be 0
 - the traveltime should be in 2 second ticks.

 Additions: 
 Marty: Made it possible to activate/stop an AT . 
        You can now specify the start state (Moving or Stranded).
        You can change the ports an AT has to go to.
        The AT will stop automatically if the next port is the same as 
         the current port.
        Added external messages, to make it more flexible.
        find_AT_loc_by_name routine.
        
 END DOCUMENTATION **/

/****************************************************************************
 ** START CODE
 ****************************************************************************/ 
#define ATSYS_C
#include "kernel.h"
#include <stdlib.h>
#include "log.h"
#include "bprintf.h"
#include "sendsys.h"
#include "global.h"
#include "atsys.h"
#include "parse.h"
#include "rooms.h"

/* Find the index to the ports array */
PUBLIC int find_port_index(int *ports, int port)
{  int i;

   for (i = 0; i < sizeof(ports); i++) 
   {  if (ports[i] == port)             /* Found it! */
          return i;
      else if (ports[i] == -1)          /* Reached end of table */
          return -1;
   }
   /* No ports in array so search failed */
   return -1;
}

PUBLIC int find_AT_loc_by_name(char *name)
{  AT_REC *p = at_list;
   int i;

   while (p != NULL)
   {   if (strncasecmp(p->ATname,name,strlen(name)) == 0) /* Find AT with same name */
       { 
          for (i=0; i < NEXITS; i++)          /* Check if player can see the loc */
          {  
             if (lexit(ploc(mynum),i) == p->loc)
                return p->loc;
          }
       }
       p = p->next;
   }
   return 0;
}

/* Find pointer to AT_REC struct by using the location */
PUBLIC AT_REC *find_AT(int loc)
{  AT_REC *p;

   p = at_list;
   while (p != NULL)
   {  if (p->loc == loc)
        break;
      p = p->next;
   }
   return p;
}

PUBLIC int replace_ports(int loc, int *ports, char **portnames)
{  AT_REC *t;

   t = find_AT(loc);
   
   if (t == NULL)     /* AT not found */
     return -1;
     
   t->ports = ports;
   t->portnames = portnames;
   t->port = 0;
   t->next_port = ports[1] == -1 ? 0 : 1;
   return 1;
}

PUBLIC int activate_AT(int loc)
{  AT_REC *t;

   t = find_AT(loc);
   
   if (t == NULL)
     return -1;
     
   t->ticks = 0;
   t->state = Moving;

   setexit(t->ports[t->port],t->exit_on_at,0);
   setexit(loc,t->exit_of_at,0);

   if (!t->use_external)
   {  sendf(t->loc,"The %s starts its journey towards %s.\n",t->ATname,
            t->portnames[t->next_port]);
      sendf(t->ports[t->port],"The %s has started its journey towards %s.\n",
            t->ATname,t->portnames[t->next_port]);
   } 
   else
   {  if (t->msgs->departmessage_in != NULL)
          sendf(t->loc,t->msgs->departmessage_in,t->ATname,t->portnames[t->next_port]);
      if (t->msgs->departmessage_out != NULL)
          sendf(t->ports[t->port],t->msgs->departmessage_out,t->ATname,t->portnames[t->next_port]);
   }

   return 1;
}

PUBLIC int stop_AT(int loc)
{  AT_REC *t;

   t = find_AT(loc);
   
   if (t == NULL)
      return -1;
   
   t->state = Stranded;
   return 1;
}

PUBLIC int remove_AT(AT_REC *p)
{  AT_REC *t = at_list;

   if (t == NULL)                   /* No AT's in list, so nothing to do */
      return -1;
 
   if (t->msgs != NULL)             /* Free external messages */
     free (t->msgs);
          
   if (t == p)                      /* p is at the head of the list, so a */
   {  at_list = t->next;            /* Simple update of AT is sufficient */
      free(t);
   } else
   {   /* Otherwise search through list until we have found the end or the
        * AT needed 
        */
       while (t->next != NULL && t->next != p)
          t = t->next;
          
       if (t->next == p)            /* If we found the AT */
       {  t->next = t->next->next;  /* Update list */
          free (p);                 /* Free allocated space */
          return 1;                 /* Return a succesful remove */
       }
   }
   return 1;                        /* Nothing to do so succesful */
}
   
/***************************************************************************
 ** REGISTER_AT - Add an AT to the list of ferries.
 ** This function reserves memory for a AT_REC struct, fills in all fills and
 ** adds the struct to the list of ferries.
 ** Parameters: loc     = Location number of the AT, ie. LOC_ZONES_ROOM
 **             *ports  = List of all ports the AT has to service.
 **             exit_off= Direction off the AT when at an harbor/port
 **             exit_on = Direction from harbor onto the AT
 **             start   = Location where the AT should start.
 **             ttime   = Travel time in signal ticks (every 2 seconds)
 **             **portnames = Names of all harbors allong the route.
 *************************************************************************/
PUBLIC int register_AT(int loc, 
                       int *ports, 
                       int exit_off, 
                       int exit_on,
                       int start,
                       int ttime,
                       char *ATname,
                       char **portnames,
                       at_state beginstate
                      )
{  AT_REC  *p = NULL;
   int    r;

   /* Check if the AT is already defined somewhere */
   if (find_AT(loc) != NULL)
   {  /* mudlog("ERROR: Multiple registration of AT %s",ATname);*/
      return -1;
   }
   
   /* Allocate some memory for the data */
   p = (AT_REC *)malloc(sizeof(AT_REC));
   
   if (p == NULL)  /* Malloc failed */
   {  mudlog("MALLOC: Unable to initialise AT %s",ATname);
      return -1;
   }

   /* Assign Public data */   
   p->loc        = loc;
   p->ports      = ports;
   p->exit_of_at = exit_off;
   p->exit_on_at = exit_on;
   /* Find index to array, if start not in list then default to the first one
    * in the list.
    */
   p->port          = ((r = find_port_index(ports,start)) == -1) ? 0 : r;
   p->traveltime    = ttime;
   p->ATname        = ATname;
   p->portnames     = portnames;
   p->use_external  = False;
   p->msgs	    = NULL;
   
   p->next  = at_list;        /* Add to linked list of all ferries */
   at_list  = p;              /* Update linked list */
   p->ticks = 0;
   if (sizeof(ports) == 0)    /* Check to see we dont work for nothing */
     p->state = Stranded;
   else 
     p->state = (beginstate == Boarding) ? Moving : beginstate;
   
   p->next_port = p->port + 1;      /* Find next port */
   if (ports[p->next_port] == -1)
      p->next_port = 0;
      
   return 1;
}

/****************************************************************************
 ** SET_AT_MESSAGES 
 ** Declare external messages that overrule the default messages.
 ** The messages may be NULL or a pointer to a string. loc is the location
 ** number of the AT, travelchance is the chance that the travelmessage
 ** will be shown.
 ** To de-activate the external messages, use free_AT_messages()
 ***************************************************************************/
PUBLIC int   set_AT_messages(int loc,
                             char *travelmessage,
                             char *boardmessage,
                             char *arivemessage_in,
                             char *arivemessage_out,
		             char *departmessage_in,
		             char *departmessage_out,
		             char *before_arival_in,
			     char *before_arival_out,
			     int  travelchance
			    )
{  AT_REC *f;

   if ((f = find_AT(loc)) != NULL)
   {  f->msgs = (AT_Messages *)malloc(sizeof(AT_Messages));
   
      if (f->msgs == NULL)
         return -1;
         
      f->use_external = True;
      f->msgs->travelmessage= travelmessage;
      f->msgs->boardmessage = boardmessage;
      f->msgs->arivemessage_in = arivemessage_in;
      f->msgs->arivemessage_out= arivemessage_out;
      f->msgs->departmessage_in= departmessage_in;
      f->msgs->departmessage_out= departmessage_out;
      f->msgs->before_arival_in = before_arival_in;
      f->msgs->before_arival_out= before_arival_out;
      f->msgs->travelchance = travelchance;
      return 1;
   }
   return 1;
}

/*****************************************************************************
 ** FREE_AT_MESSAGES
 ** free the memory used by the message structure and deactivate the use of
 ** the messages.
 *****************************************************************************/
PUBLIC void free_AT_messages(int loc)
{  AT_REC *f = find_AT(loc);

   if (f != NULL)
   {   if (f->msgs != NULL)
           free(f->msgs);
       f->use_external = False;
   }
}

/*****************************************************************************
 ** DO_FERRY
 ** updates the ticks, and takes actions depending on the state of the AT 
 ** and traveltimes.
 *****************************************************************************/
PUBLIC void do_AT(AT_REC *p)
{  
   if (p == NULL)
   {  mudlog("ERROR: do_AT called with NULL pointer");
      return;
   }
   if (p->state != Stranded)
   {  p->ticks++;       /* Update the ticks */
   
      switch (p->state) {
      case Boarding:
           /* Almost ready to depart */
           if (p->ticks == BOARDING_DELAY-1)
           {  
              if (!p->use_external)
              {  sendf(p->ports[p->port],"The Captain shouts 'Last boarding call for %s!'\n",p->ATname);
                 sendf(p->loc,"The Captain shouts '%s is almost ready for departure.'\n",p->ATname);
              }
           } 
           /* Departure time has come (yes this AT leaves on time ;) */
           else if (p->ticks == BOARDING_DELAY)
           {  
              /* If next port is same as this port we stop */
              if (p->ports[p->port] == p->ports[p->next_port])
              {  p->state = Stranded;
                 return;
              } 
              /* Close all exits off the AT */
              setexit(p->ports[p->port],p->exit_on_at,0);
              setexit(p->loc,p->exit_of_at,0);

              /* Reset the ticks */
              p->ticks = 0;
              
              if (!p->use_external)
              {  sendf(p->loc,"The %s starts its journey towards %s.\n",p->ATname,
                   p->portnames[p->next_port]);
                 sendf(p->ports[p->port],"The %s has started its journey towards %s.\n",
                   p->ATname,p->portnames[p->next_port]);
              } 
              else
              {  if (p->msgs->departmessage_in != NULL)
                    sendf(p->loc,p->msgs->departmessage_in,p->ATname,p->portnames[p->next_port]);
                 if (p->msgs->departmessage_out != NULL)
                    sendf(p->ports[p->port],p->msgs->departmessage_out,p->ATname,p->portnames[p->next_port]);
              }
              
              /* Search next port */
              p->port = p->next_port;
              p->next_port++;
              if (p->ports[p->next_port] == -1)
                p->next_port = 0;
                
              p->state = Moving;       /* Go Automated Transport! */
           } 
           /* Send a message notifying players that they can board the AT */
           else
           { if (!p->use_external)
             {
               if (randperc() < 8)  
                  sendf(p->ports[p->port],"The Captain shouts 'All passengers for %s please board now!.\n",
                    p->portnames[p->next_port]);
               if (randperc() < 6)
                  sendf(p->loc,"The Captain shouts 'All passengers for %s can disembark now.'\n",
                    p->portnames[p->port]);
             }
             else
             {  if (randperc() < 8 && p->msgs != NULL)
                  if (p->msgs->boardmessage != NULL)
                   sendf(p->ports[p->port],p->msgs->boardmessage,p->portnames[p->next_port]);
             }
           }
           break;

      case Moving:
           /* We have traveled all the way */
           if (p->ticks == p->traveltime)
           {  
              if (!p->use_external)
              {  sendf(p->loc,"%s has arrived at the %s.\n",p->ATname,
                    p->portnames[p->port]);
                 sendf(p->ports[p->port],"%s has arrived at the %s.\n",
                    p->ATname,p->portnames[p->port]);
              }
              else
              {  if (p->msgs->arivemessage_in != NULL)
                    sendf(p->loc,p->msgs->arivemessage_in, p->ATname,
                          p->portnames[p->port]);
                 if (p->msgs->arivemessage_out != NULL)
                     sendf(p->ports[p->port],p->msgs->arivemessage_out, p->ATname,
                           p->portnames[p->port]);
              }
              /* Fix the exits, so people can get on and off */
              setexit(p->loc,p->exit_of_at,p->ports[p->port]);
              setexit(p->ports[p->port],p->exit_on_at,p->loc);
              p->state = Boarding;     /* Arrived at a port so start boarding */
              p->ticks = 0;
              
           } 
           /* Show we almost made it to the next port */
           else if (p->ticks == (p->traveltime - 2))
           {  if (!p->use_external)
                 sendf(p->loc,"You can see land on the horizon.\n");
           } 
           /* Show that we are entering the port */
           else if (p->ticks == (p->traveltime - 1))
           {  if (!p->use_external)
              {
                 sendf(p->loc,"The sails are lowered as the %s arrives at the %s.\n",
                    p->ATname,p->portnames[p->port]);
                 sendf(p->ports[p->port],"You can see a ship closing in.\n");
              }
           }
           else
           {  if (randperc() < 5 && !p->use_external)
                sendf(p->loc,"The ship moves gently on the rhythm of the waves.\n");
              else if (p->use_external && p->msgs != NULL)
              {  if (randperc() < p->msgs->travelchance)
                    sendf(p->loc,p->msgs->travelmessage);
              }
           }
           break;

      default:
           /* This should never be called, but if it does, then something went
            * very wrong. So log it and stop this ferry from working
            */
           mudlog("ERROR: do_AT, case-slip. Adjusting Data for %s",p->ATname);
           p->state = Stranded;
           p->ticks = 0;
      } 
   }   
}

PUBLIC void do_all_ATs(void)
{  AT_REC *p = at_list;      /* Start of list */
  
   while (p != NULL)        /* Work our way through all AT's */
   {  do_AT(p);             /* Update current AT */
      p = p->next;
   }
}

PUBLIC int countATs(void)
{  AT_REC *p = at_list;
   int ct=0;
   
   for (ct = 0;p != NULL; ct++, p = p->next);
   return ct;
}

PUBLIC time_t time_to_port(int port,int loc)
{  AT_REC *p = find_AT(loc);
   time_t total_time = -1;  /* -1 is never arrives */
   int    ports = 0,pi = 0;

   if (p != NULL)
   {  if (p->state == Stranded)
         total_time = -1;
      else if (p->ports[p->port] == port)
      {  if (p->state == Boarding)
            total_time = -2;
         else if (p->state == Moving)
            total_time = 2 * (p->traveltime - p->ticks);
      }
      else
      {  pi = p->port+1; 
         ports = 1;
         while (p->ports[pi] != port && pi != p->port)
         {  if (p->ports[pi] == -1)
              pi = 0;
            else
            {  pi++;
               ports++;
            }
         }
         if (pi != p->port)
             total_time = 2 * (((ports) * p->traveltime) + 
                               (p->traveltime - p->ticks) +
                               ((ports -1 ) * BOARDING_DELAY));
      }       
   }
   return total_time;
}

PUBLIC A_COMMAND(showATscom)
{  AT_REC *p = at_list;
   char  *s = NULL;
   
   if (plev(mynum) < LVL_ARCHWIZARD)  /* Awiz+ command */
   {  erreval();
      return;
   }

   /* Print header */   
   bprintf("&+wAT-name      Status    Port                Time&*\n");
   bprintf("&+w%s&*\n",MIXLINE);
   
   /* Work through all ferries and show the data of interest */
   while (p != NULL)
   {   bprintf("%-13.13s",p->ATname);
       switch (p->state) {
       case Boarding : s = "Boarding "; break;
       case Moving   : s = "Moving   "; break;
       case Stranded : s = "Stranded "; break;
       default: s = "Unknown  "; break;
       }
       bprintf("%s %-20.20s %d\n",s,p->portnames[p->port],p->ticks);
       p = p->next;
   }
   
   /* Print footer */
   bprintf("&+w%s&*\n",MIXLINE);
}