/
Crimson2/alias/
Crimson2/area.tmp/
Crimson2/area.tmp/AnomalySpaceDock/
Crimson2/area.tmp/AnomalyStation/
Crimson2/area.tmp/AntHill/
Crimson2/area.tmp/ArcticTerrarium/
Crimson2/area.tmp/BuilderCity/
Crimson2/area.tmp/Dungeon/
Crimson2/area.tmp/MiningDock/
Crimson2/area.tmp/PipeSystem/
Crimson2/area.tmp/RattArea/
Crimson2/area.tmp/RobotFactory/
Crimson2/area.tmp/SilverDale/
Crimson2/area.tmp/StarshipFearless/
Crimson2/area.tmp/StationConduits/
Crimson2/area.tmp/TerrariumAlpha/
Crimson2/area.tmp/TerrariumBeta/
Crimson2/area.tmp/TestArea/
Crimson2/area.tmp/Void/
Crimson2/area/
Crimson2/area/AnomalySpaceDock/
Crimson2/area/AnomalyStation/
Crimson2/area/MiningDock/
Crimson2/area/PipeSystem/
Crimson2/area/SilverDale/
Crimson2/area/StationConduits/
Crimson2/area/Void/
Crimson2/board/
Crimson2/clone/
Crimson2/lib/
Crimson2/mole/
Crimson2/mole/mole_src/HELP/
Crimson2/player/
Crimson2/util/
Crimson2/wldedit/
Crimson2/wldedit/res/
/* Crimson2 Mud Server
 * All source written/copyright Ryan Haksi 1995 *
 * This source code is proprietary. Use in whole or in part without
 * explicity permission by the author is strictly prohibited
 *
 * Current email address(es): cryogen@infoserve.net
 * Phone number: (604) 591-5295
 *
 * C4 Script Language written/copyright Cam Lesiuk 1995
 * Email: clesiuk@engr.uvic.ca
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#ifndef WIN32
  #include <arpa/telnet.h>
#else
  #define IAC         '\0'
  #define GA          '\0'
  #define WILL        '\0'
  #define WONT        '\0'
  #define TELOPT_ECHO '\0'
#endif

#include "crimson2.h"
#include "macro.h"
#include "queue.h"
#include "str.h"
#include "extra.h"
#include "thing.h"
#include "index.h"
#include "edit.h"
#include "history.h"
#include "socket.h"
#include "exit.h"
#include "parse.h"
#include "area.h"
#include "world.h"
#include "base.h"
#include "object.h"
#include "char.h"
#include "group.h"
#include "affect.h"
#include "mobile.h"
#include "player.h"
#include "fight.h"
#include "skill.h"
#include "file.h"
#include "send.h"

#define SEND_HISTORY_MAX 100

BYTE TELNET_GA[]       = {IAC, GA, '\0'};
BYTE TELNET_ECHO_ON[]  = {IAC, WILL, TELOPT_ECHO, '\0'};
BYTE TELNET_ECHO_OFF[] = {IAC, WONT, TELOPT_ECHO, '\0'};

BYTE *sendFlagList[] = {
  "SEND_ROOM",
  "SEND_SRC",
  "SEND_DST",
  "SEND_VISIBLE",
  "SEND_AUDIBLE",
  "SEND_CAPFIRST",
  "SEND_GROUP",
  ""
};

/* started out as a macro */
void SendCheckPrompt(SOCK *sock) {
  if (BIT(sock->sFlag, SF_ATPROMPT)) { 
    SEND("\n", sock); 
    BITCLR(sock->sFlag, SF_ATPROMPT); 
  }
}

/* if we are sitting at a prompt prefix a EOLN sequence */
#define SENDCHECKPROMPT(sock) SendCheckPrompt(sock)

void SendEchoOn(SOCK *sock) {
  QAppend(sock->sOut, TELNET_ECHO_OFF, Q_DONTCHECKSTR);
}

void SendEchoOff(SOCK *sock) {
  QAppend(sock->sOut, TELNET_ECHO_ON, Q_DONTCHECKSTR);
}

/* this routine is only intended to clean up menu text in the editor
   when color codes are being shown, so it handles only up to 512 char
   strings, any longer will result in a crash! (since the edit menu is
   a fixed length this shouldnt be a problem)

   the idea is that color codes can be stripped from menu text but shown
   for the text being edited....
 */
void SendSmartColor(BYTE *str, SOCK *sock) {
  BYTE buf[512];

  LWORD src = 0;
  LWORD dst = 0;

  if ( !BIT(sock->sFlag, SF_XLATE) ) { /* if color codes are being shown */
    while (str[src] != '\0') {
      if (str[src] != '^') { /* copy the character, its not a color code */
        buf[dst] = str[src];
        src++;
        dst++;
      } else {
        src+=2; /* skip over color code */
      }
    }
    buf[dst] = '\0';
    SEND(buf, sock);
  } else {
    SEND(str, sock);
  }
}

void SendDefaultPrompt(SOCK *sock) {
  BYTE  buf[256];
  BYTE  eName[256];
  LWORD bLen;

  /* show exits etc on prompt if flags are so set */
  /* right now this is a little inefficient what with the
     repeated strlen's etc */
  buf[0] = '\0';
  /* hit points */
  if (BIT(sock->sPref, SP_PROMPTHP)) {
    sprintf(buf, "^bHP^p%ld", Character(sock->sControlThing)->cHitP);
  }
  /* move points */
  if (BIT(sock->sPref, SP_PROMPTMV)) {
    bLen = strlen(buf);
    if (bLen) { strcat(buf, " "); bLen++; }
    sprintf(buf+bLen, "^bMV^p%ld", Character(sock->sControlThing)->cMoveP);
  }
  /* Power points */
  if (BIT(sock->sPref, SP_PROMPTPP)) {
    bLen = strlen(buf);
    if (bLen) { strcat(buf, " "); bLen++; }
    sprintf(buf+bLen, "^bPP^p%ld", Character(sock->sControlThing)->cPowerP);
  }
  /* room #'s */
  if (BIT(sock->sPref, SP_PROMPTRM)) {
    bLen = strlen(buf);
    if (bLen) { strcat(buf, " "); bLen++; }
    if (Base(sock->sControlThing)->bInside->tType == TTYPE_WLD)
      sprintf(buf+bLen, "^b#^p%ld", Wld(Base(sock->sControlThing)->bInside)->wVirtual);
    else 
      sprintf(buf+bLen, "^b#^p<?>");
  }
  /* exits */
  if (BIT(sock->sPref, SP_PROMPTEX)) {
    EXIT *exit;
    WORD eOrder;

    bLen = strlen(buf);
    if (bLen) { strcat(buf, " "); bLen++; }
    sprintf(buf+bLen, "^b[^p"); bLen+=5;
    if (Base(sock->sControlThing)->bInside->tType == TTYPE_WLD) {
      for (eOrder=0;eOrderList[eOrder]<EDIR_MAX;eOrder++) {
        for (exit = Wld(Base(sock->sControlThing)->bInside)->wExit; exit; exit=exit->eNext) {
          if (exit->eWorld && !BIT(exit->eFlag, EF_HIDDEN)&&
	    (exit->eDir==eOrderList[eOrder])) {
            ExitGetName(exit, eName);
            if (BIT(exit->eFlag, EF_CLOSED)) {
              buf[bLen]=*eName; bLen++;
            } else {
              buf[bLen]=toupper(*eName); bLen++;
            }
            buf[bLen]=' '; bLen++;
          }
        }
      }
    } else {
      sprintf(buf+bLen, "<?>"); bLen+=3;
    }
    if (buf[bLen-1]==' ') bLen--;
    sprintf(buf+bLen, "^b]"); bLen+=3;
  }

  /* show prompt if prompt is on */
  bLen = strlen(buf);
  if (bLen) { strcat(buf, " "); bLen++; }
  sprintf(buf+bLen, "^b-+^w>^V");
  SEND(buf, sock);
}

void SendCustomPrompt(SOCK *sock) {
  BYTE    buf[512];
  BYTE   *src;
  LWORD   bLen;
  LWORD   srcOS;
  THING  *thing;

  bLen = 0;
  buf[0] = 0;
  thing = sock->sControlThing;
  src = Plr(sock->sHomeThing)->pPrompt->sText;
  srcOS = 0;

  while (src[srcOS]) {
    if (src[srcOS]!='%') {
      buf[bLen] = src[srcOS];
      srcOS++;
      bLen++;
      continue;
    }
    srcOS++;
    switch(src[srcOS]) {
    case '%':
      srcOS++;
      buf[bLen]='%';
      bLen++;
      break;

    case 'h':
      srcOS++;
      sprintf(buf+bLen, "%ld", Character(thing)->cHitP);
      bLen = strlen(buf);
      break;

    case 'm':
      srcOS++;
      sprintf(buf+bLen, "%ld", Character(thing)->cMoveP);
      bLen = strlen(buf);
      break;

    case 'p':
      srcOS++;
      sprintf(buf+bLen, "%ld", Character(thing)->cPowerP);
      bLen = strlen(buf);
      break;

    case 'H':
      srcOS++;
      sprintf(buf+bLen, "%ld", CharGetHitPMax(thing));
      bLen = strlen(buf);
      break;

    case 'M':
      srcOS++;
      sprintf(buf+bLen, "%ld", CharGetMovePMax(thing));
      bLen = strlen(buf);
      break;

    case 'P':
      srcOS++;
      sprintf(buf+bLen, "%ld", CharGetPowerPMax(thing));
      bLen = strlen(buf);
      break;

    case 't':
      srcOS++;
      if (thing->tType==TTYPE_PLR)
        sprintf(buf+bLen, "%hd", Plr(thing)->pThirst);
      else
        sprintf(buf+bLen, "-");
      bLen = strlen(buf);
      break;

    case 'n':
      srcOS++;
      if (thing->tType==TTYPE_PLR)
        sprintf(buf+bLen, "%hd", Plr(thing)->pHunger);
      else
        sprintf(buf+bLen, "-");
      bLen = strlen(buf);
      break;

    case 'u':
      srcOS++;
      sprintf(buf+bLen, "%hd", Character(thing)->cAura);
      bLen = strlen(buf);
      break;

    case 'c':
      srcOS++;
      sprintf(buf+bLen, "%ld", Character(thing)->cMoney);
      bLen = strlen(buf);
      break;

    case 'i':
      srcOS++;
      if (thing->tType==TTYPE_PLR)
        sprintf(buf+bLen, "%hd", Plr(thing)->pIntox);
      else
        sprintf(buf+bLen, "-");
      bLen = strlen(buf);
      break;

    case 'e': {
      EXIT *exit;
      BYTE  eName[256];
      srcOS++;

      if (Base(thing)->bInside->tType == TTYPE_WLD) {
        for (exit = Wld(Base(thing)->bInside)->wExit; exit; exit=exit->eNext) {
          if (exit->eWorld && !BIT(exit->eFlag, EF_HIDDEN)) {
            ExitGetName(exit, eName);
            if (BIT(exit->eFlag, EF_CLOSED)) {
              buf[bLen]=*eName; bLen++;
            } else {
              buf[bLen]=toupper(*eName); bLen++;
            }
            buf[bLen]=' '; bLen++;
          }
        }
      } else {
        sprintf(buf+bLen, "<?>"); bLen+=3;
      }
      if (buf[bLen-1]==' ') bLen--;
    } break;

    case 'b':
      srcOS++;
      sprintf(buf+bLen, "%ld", CharGetHitBonus(thing, Character(thing)->cWeapon));
      bLen = strlen(buf);
      break;

    case 'B':
      srcOS++;
      sprintf(buf+bLen, "%ld", CharGetDamBonus(thing, Character(thing)->cWeapon));
      bLen = strlen(buf);
      break;

    case 'N':
      srcOS++;
      sprintf(buf+bLen, "%s", thing->tSDesc->sText);
      bLen = strlen(buf);
      break;

    case 'w':
      srcOS++;
      if (Character(thing)->cWeapon)
        sprintf(buf+bLen, "%s", Character(thing)->cWeapon->tSDesc->sText);
      else
        sprintf(buf+bLen, "-");
      bLen = strlen(buf);
      break;

    case 'l':
      srcOS++;
      if (Character(thing)->cLead)
        sprintf(buf+bLen, "%s", Character(thing)->cLead->tSDesc->sText);
      else
        sprintf(buf+bLen, "-");
      bLen = strlen(buf);
      break;

    case 'f':
      srcOS++;
      if (Character(thing)->cFight)
        sprintf(buf+bLen, "%s", Character(thing)->cFight->tSDesc->sText);
      else
        sprintf(buf+bLen, "-");
      bLen = strlen(buf);
      break;

    case 'r':
      srcOS++;
      sprintf(buf+bLen, "%hd", Character(thing)->cArmor);
      bLen = strlen(buf);
      break;

    case 'E':
      srcOS++;
      sprintf(buf+bLen, "%ld", Character(thing)->cExp);
      bLen = strlen(buf);
      break;

    case 'a':
      srcOS++;
      if (Base(thing)->bInside->tType==TTYPE_WLD)
        sprintf(buf+bLen, "%s", areaList[Wld(Base(thing)->bInside)->wArea].aFileName->sText);
      else
        sprintf(buf+bLen, "-");
      break;

    case 'R':
      srcOS++;
      if (Base(thing)->bInside->tType==TTYPE_WLD) {
        if (!ParseCommandCheck(TYPEFIND("wdesc", commandList), sock, "")) {
          sprintf(buf+bLen, "-");
        } else {
          sprintf(buf+bLen, "%ld", Wld(Base(thing)->bInside)->wVirtual);
        }
      } else {
        sprintf(buf+bLen, "-");
      }
      break;

/*
 * r Room #  - make sure they are a god before you show it
 */

    case 'L':
      srcOS++;
      sprintf(buf+bLen, "%ld", PlayerExpUntilLevel(thing));
      bLen = strlen(buf);
      break;

    default:
      srcOS++;
      sprintf(buf+bLen, "<???>");
      bLen = strlen(buf);
      break;
    }
  }
  buf[bLen] = '\0';
  SEND(buf, sock);
}

void SendPrompt(SOCK *sock) {
  if (sock->sEdit.eStr) {
    EditSendPrompt(sock); /* to keep all edit stuff together */
  } else if (sock->sSubMode != SUBM_NONE) {
    SEND("^g>^V", sock);
  } else {
    switch(sock->sMode) {
    case MODE_PLAY:
      if (!BIT(sock->sPref, SP_COMPACT))
        SEND("^V\n", sock);

      if (Plr(sock->sHomeThing)->pPrompt->sLen>0) {
        SendCustomPrompt(sock);
        SEND("^V", sock);
      } else {
        SendDefaultPrompt(sock);
      }
      break;
    case MODE_MAINMENU:
      SEND("\n^G[^gMain Menu^G] ^c-+>^V", sock);
      break;
    case MODE_LOGIN:
    case MODE_PASSWORD:
    case MODE_CONFIRMNAME:
      SEND("^g>^V", sock);
      break;
    default:
      SEND("^B[^w? <topic>^b for help^B] ^G-+^g>^V", sock);
      break;
    }
  }
  /* append telnet GA string if flag is so set */
  if (BIT(sock->sPref, SP_TELNETGA))
    SEND(TELNET_GA, sock);
  sock->sCurrentLines=0;
  BITSET(sock->sFlag, SF_ATPROMPT);
}

/* going to move this inside help system, as default soon... */
void SendMainMenu(SOCK *sock) {
  BYTE buf[256];

  SEND("\n", sock);
  SEND("^cChoose an option:\n", sock);
  SEND("^gP^GLAY    ^bStart playing CrimsonMUD II\n", sock);
  if (BIT(Plr(sock->sHomeThing)->pAuto, PA_EXPERT)) {
    SEND("^gD^GESC    ^bEdit Character description\n", sock);
    SEND("^gEM^GAIL   ^bEdit Email address\n", sock);
    SEND("^gPL^GAN    ^bEdit Plan message\n", sock);
    SEND("^gN^GEWS    ^bRead News - last updated ", sock);
    SEND(ctime(&fileList[FILE_NEWS].fileDate), sock);
    SEND("^gC^GREDITS ^bRead who did what\n", sock);
  }
  SEND("^gPA^GSS    ^bChange Password\n", sock);
  if (BIT(Plr(sock->sHomeThing)->pAuto, PA_EXPERT)) {
    /*SEND("^gL^GOGON   ^bLogon as another Character\n", sock);*/
    SEND("^gS^GTATS   ^bShow Class/Race stats for this character\n", sock);
  }
  SEND("^gQ^GUIT    ^bLeave the Game\n", sock);
  SEND("\n", sock);
  if (BIT(Plr(sock->sHomeThing)->pAuto, PA_EXPERT)) {
    SEND("^cThe Following Operations have dire consequences:\n", sock);
    SEND("^gDELETE  ^bthis character\n", sock);
    /*SEND("^gRENAME  ^bthis character (costs you half your level)\n", sock);*/
    if (Character(sock->sHomeThing)->cLevel >= LEVEL_CODER) {
      /* SEND("^gDOWN    ^bturf the server\n", sock); */
      SEND("^gREBOOT  ^breboot the server\n", sock);
    }
    SEND("\n^cUptime: ^g", sock);
    TimeSprintf(buf, time(0)-startTime);
    SEND(buf, sock);
    SEND(".\n", sock);
  }
}

void SendChooseAnsi(SOCK *sock) {
  SEND("\n", sock);
  SEND("Does your terminal support ANSI graphics?\n", sock);
  SEND("YES or NO\n", sock);
  SEND("\nThe Following Operations have dire consequences:\n", sock);
  SEND("CANCEL  Dont create this character\n", sock);
  SEND("\n", sock);
}

void SendChooseExpert(SOCK *sock) {
  SEND("\n", sock);
  SEND("^bIf you answer ^gYES^b to this question you might find that\n", sock);
  SEND("^bthe amount of decisions to make can be quite overwhelming!\n", sock);
  SEND("^bFor fast easy character creation please type ^gNO!\n", sock);
  SEND("\n", sock);
  SEND("^cAre you an expert mud player?\n", sock);
  SEND("^gYES ^bor ^gNO\n", sock);
  SEND("\n^cThe Following Operations have dire consequences:\n", sock);
  SEND("^gCANCEL  ^bDont create this character\n", sock);
  SEND("\n", sock);
}

void SendChooseSex(SOCK *sock) {
  if (!BIT(Plr(sock->sHomeThing)->pAuto, PA_EXPERT)) {
    SEND("\n", sock);
    SEND("^bIn this sort of game you create a character that you pretend\n", sock);
    SEND("^bto be. Everyone else in the game sees only what you tell them\n", sock);
    SEND("^babout your character. If you make your character a hulking female\n", sock);
    SEND("^bSiliconoid Soldier over 7 feet tall, then as far as everyone else\n", sock);
    SEND("^bplaying the game is concerned that is who you are!\n", sock);
    SEND("\n", sock);
  }
  SEND("\n", sock);
  SEND("^cChoose a sex for your character:\n^g", sock);
  SENDARRAY(sexList, 1, sock->sHomeThing);
  SEND("\n^cThe Following Operations have dire consequences:\n", sock);
  SEND("^gCANCEL  ^bDont create this character\n", sock);
  SEND("\n", sock);
}

void SendChooseRace(SOCK *sock) {
  SEND("\n", sock);
  SEND("^cChoose a Race for your character:\n^g", sock);
  SENDARRAY(raceList, 1, sock->sHomeThing);
  SEND("\n^cThe Following Operations have dire consequences:\n", sock);
  SEND("^gBACK    ^bBack up a step, I changed my mind about something\n", sock);
  SEND("^gCANCEL  ^bDont create this character\n", sock);
  if (!BIT(Plr(sock->sHomeThing)->pAuto, PA_EXPERT)) {
    SEND("\n", sock);
    SEND("^;HINT: The SILICONOID is probably the easiest race to get started with!\n", sock);
  }
  SEND("\n", sock);
}

void SendChooseClass(SOCK *sock) {
  SEND("\n", sock);
  SEND("^cChoose a Class for your character:\n^g", sock);
  SENDARRAY(classList, 1, sock->sHomeThing);
  SEND("\n^cThe Following Operations have dire consequences:\n", sock);
  SEND("^gBACK    ^bBack up a step, I changed my mind about something\n", sock);
  SEND("^gCANCEL  ^bDont create this character\n", sock);
  if (!BIT(Plr(sock->sHomeThing)->pAuto, PA_EXPERT)) {
    SEND("\n", sock);
    SEND("^;HINT: The SOLDIER is probably the easiest class to get started with!\n", sock);
  }
  SEND("\n", sock);
}

void SendShowCombo(SOCK *sock) {
  THING       *player;
  BYTE         buf[256];
  LWORD        i;

  player = sock->sHomeThing;
  SEND("\n", sock);
  /* Class Stuff */
  SEND("^c", sock);
  SEND(classList[Plr(player)->pClass].cName, sock);
  SEND("^C skill category modifiers are:\n^w", sock);
  FlagSprintf(buf, classList[Plr(player)->pClass].cSkillDefault, skillFlagList, ' ', sizeof(buf));
  SEND(buf, sock);
  SEND("\n", sock);
  SEND("^bGood: ^p", sock);
  FlagSprintf(buf, classList[Plr(player)->pClass].cSkillGood, skillFlagList, ' ', sizeof(buf));
  SEND(buf, sock);
  SEND("\n", sock);
  SEND("^bLame: ^p", sock);
  FlagSprintf(buf, classList[Plr(player)->pClass].cSkillBad, skillFlagList, ' ', sizeof(buf));
  SEND(buf, sock);
  SEND("\n", sock);
  SEND("^bCant: ^p", sock);
  FlagSprintf(buf, classList[Plr(player)->pClass].cSkillCant, skillFlagList, ' ', sizeof(buf));
  SEND(buf, sock);
  SEND("\n", sock);
  SEND("\n", sock);

  /* Racial Stuff */
  SEND("^c", sock);
  SEND(raceList[Plr(player)->pRace].rName, sock);
  SEND("^C skill category modifiers are:\n", sock);
  SEND("^bGood: ^p", sock);
  FlagSprintf(buf, raceList[Plr(player)->pRace].rSkillGood, skillFlagList, ' ', sizeof(buf));
  SEND(buf, sock);
  SEND("\n", sock);
  SEND("^bLame: ^p", sock);
  FlagSprintf(buf, raceList[Plr(player)->pRace].rSkillBad, skillFlagList, ' ', sizeof(buf));
  SEND(buf, sock);
  SEND("\n", sock);
  SEND("^bCant: ^p", sock);
  FlagSprintf(buf, raceList[Plr(player)->pRace].rSkillCant, skillFlagList, ' ', sizeof(buf));
  SEND(buf, sock);
  SEND("\n", sock);
  SEND("\n", sock);

  /* Show skill modifiers here */
  SEND("^bRacial Specific Skill modifiers are:\n^p", sock);
  #define SDETAIL raceList[Plr(player)->pRace].rSkill
  for (i=0;  SDETAIL[i].sNum; i++) {
    if (SDETAIL[i].sGetSkill == SD_GETSKILL)
      sprintf(buf, 
              "%-15s %+hd ", 
              skillList[ *(SDETAIL[i].sNum) ].sName, 
              (WORD)SDETAIL[i].sModifier);
    else
      sprintf(buf, 
              "%-15s(%+hd)", 
              skillList[ *(SDETAIL[i].sNum) ].sName, 
              (WORD)SDETAIL[i].sModifier);
    if ((i+1)%3)
      strcat(buf, "      ");
    else
      strcat(buf, "\n");
    SEND(buf, sock);
  }

  #undef SDETAIL
  if (i%3)
    SEND("\n", sock);
  /* Show skill modifiers here */
  SEND("\n^bClass Specific Skill modifiers are:\n^p", sock);
  #define SDETAIL classList[Plr(player)->pClass].cSkill
  for (i=0;  SDETAIL[i].sNum; i++) {
    if (SDETAIL[i].sGetSkill == SD_GETSKILL)
      sprintf(buf, 
              "%-15s %+hd ", 
              skillList[ *(SDETAIL[i].sNum) ].sName, 
              (WORD)SDETAIL[i].sModifier);
    else
      sprintf(buf, 
              "%-15s(%+hd)", 
              skillList[ *(SDETAIL[i].sNum) ].sName, 
              (WORD)SDETAIL[i].sModifier);
    if ((i+1)%3)
      strcat(buf, "      ");
    else
      strcat(buf, "\n");
    SEND(buf, sock);
  }
  #undef SDETAIL
  if (i%3)
    SEND("\n", sock);

  SEND("\n", sock);
  if (sock->sMode == MODE_SHOWCOMBO) {
    SEND("\n^cThe Following Operations have dire consequences:\n", sock);
    SEND("^gBACK    ^bBack up a step, I changed my mind about something\n", sock);
    SEND("^gCANCEL  ^bDont create this character\n", sock);
    SEND("^gACCEPT  ^bLooks good, I wanna keep 'em\n", sock);
    SEND("\n", sock);
  }
}

void SendRollAbilities(SOCK *sock) {
  THING *player;
  BYTE   buf[256];

  player = sock->sHomeThing;
  SEND("\n", sock);
  SEND("^cYour Abilities are currently:\n^g", sock);
  sprintf(buf, "^gStr: ^G[^c%3hd^G] ^gCarry: ^c%3ld^glbs        ^gHP:^G[^c%3ld^G]\n", 
               Plr(player)->pStr, 
               CharGetCarryMax(player),
               CharGetHitPMax(player));
  SEND(buf, sock);
  sprintf(buf, "^gDex: ^G[^c%3hd^G] ^g+^c%d ^gMP/level          ^gMP:^G[^c%3ld^G]\n", 
               Plr(player)->pDex, 
               Plr(player)->pDex/20, 
               CharGetMovePMax(player));
  SEND(buf, sock);
  sprintf(buf, "^gCon: ^G[^c%3hd^G] ^g+^c%d ^gHP/level          ^gPP:^G[^c%3ld^G]\n", 
               Plr(player)->pCon, 
               Plr(player)->pCon/20, 
               CharGetPowerPMax(player));
  SEND(buf, sock);
  sprintf(buf, "^gWis: ^G[^c%3hd^G] ^g+^c%3.1f ^gPP/level\n", 
               Plr(player)->pWis,
               ((float)(Plr(player)->pWis/20))/2);
  SEND(buf, sock);
  sprintf(buf, "^gInt: ^G[^c%3hd^G] ^g+^c%3.1f ^gPP/level\n", 
               Plr(player)->pInt,
               ((float)(Plr(player)->pInt/20))/2);
  SEND(buf, sock);
  SEND("\nYour Resistances are:\n", sock);
  sprintf(buf, 
          "^gPuncture  [^c%3hd^G]",
          CharGetResist(player, FD_PUNCTURE));
  SEND(buf, sock);
  sprintf(buf, 
          "  ^gSlash     [^c%3hd^G]",
            CharGetResist(player, FD_SLASH));
  SEND(buf, sock);
  sprintf(buf, 
          "  ^gConcussive[^c%3hd^G]\n",
            CharGetResist(player, FD_CONCUSSIVE));
  SEND(buf, sock);
  sprintf(buf, 
          "^gHeat      [^c%3hd^G]",
            CharGetResist(player, FD_HEAT));
  SEND(buf, sock);
  sprintf(buf, 
          "  ^gEMR       [^c%3hd^G]",
            CharGetResist(player, FD_EMR));
  SEND(buf, sock);
  sprintf(buf, 
          "  ^gLaser     [^c%3hd^G]\n",
            CharGetResist(player, FD_LASER));
  SEND(buf, sock);
  sprintf(buf, 
          "^gPsychic   [^c%3hd^G]",
            CharGetResist(player, FD_PSYCHIC));
  SEND(buf, sock);
  sprintf(buf, 
          "  ^gAcid      [^c%3hd^G]",
            CharGetResist(player, FD_ACID));
  SEND(buf, sock);
  sprintf(buf, 
          "  ^gPoison    [^c%3hd^G]\n",
            CharGetResist(player, FD_POISON));
  SEND(buf, sock);
  SEND("\n^cYou may:\n", sock);
  SEND("^gA^GCCEPT  ^bGreat abilities, I like 'em\n", sock);
  SEND("^gR^GEROLL  ^bEww, Ick, cant we do better than that?\n", sock);
  SEND("\n^cThe Following Operations have dire consequences:\n", sock);
  SEND("^gBACK    ^bBack up a step, I changed my mind about something\n", sock);
  SEND("^gCANCEL  ^bDont create this character\n", sock);
  SEND("\n", sock);
}

/* Telepathy links and Control links are handled here,
   Hear and See links are handled in SendActionStr */
BYTE SendThing(BYTE *str, THING *thing) {
  BASELINK *i;
  SOCK     *sock;
  BYTE      sentTo = 0;
  THING    *room;

  if (!thing)
    return 0;
  if (thing->tType < TTYPE_BASE) return 0;
  room=Base(thing)->bInside;
  for (i=Base(thing)->bLink; i; i=i->lNext) {
    if (i->lType == BL_CONTROL) {
      SENDCHECKPROMPT(i->lDetail.lSock);
      SEND(str, i->lDetail.lSock);
      sentTo++;
    } else if (i->lType == BL_TELEPATHY_SND) {
      if (room->tType==TTYPE_WLD) {
	if (BIT(Wld(room)->wFlag,WF_PRIVATE)) {
	  continue;
	}
      } /* else no private! <grin> */
      sock = BaseControlFind(i->lDetail.lThing);
      SENDCHECKPROMPT(sock);
      SEND("^w%", sock);
      SEND(str, sock);
      sentTo++;
    }
  }
  return sentTo;
}

/* Add to their personal history */
void SendHistory(BYTE *str, THING *thing) {
  SOCK     *sock;
  BYTE      buf[512];

  sock = BaseControlFind(thing);
  if (sock) {
    if (sock->sPersonal->qValid >= SEND_HISTORY_MAX)
      QRead(sock->sPersonal, buf, sizeof(buf), Q_COLOR_IGNORE, NULL);
    QAppend(sock->sPersonal, str, Q_DONTCHECKSTR);
  }
}

BYTE SendThingCapFirst(BYTE *str, THING *thing) {
  BYTE  buf[2];
  BYTE  sentTo = 0;

  buf[0] = toupper(*str);
  buf[1] = '\0';

  sentTo = SendThing(buf, thing);
  if (sentTo) /* if theres anybody out there... send the rest too */
    SendThing(str+1, thing);
 
  return sentTo;
}

/* if the channel flag is set, send them the text */
/* Note that snooping will not show channel messages */
/* will not work for clantalk however since that depends on their clan as well as the flag */
void SendChannel(BYTE *str, THING *thing, FLAG chanFlag) {
  SOCK *i;

  for (i = sockList; i; i=i->sNext) {
    if (i->sMode>MODE_PASSWORD 
    && (BIT(i->sPref, chanFlag)) 
    && (!thing || i->sControlThing != thing)) {
      SENDCHECKPROMPT(i);
      SEND(str, i);
    }
  }
}

void SendHint(BYTE *str, THING *thing) {
  SOCK *sock;
  
  sock = BaseControlFind(thing);
  if (!sock) return;
  if (BIT(Plr(sock->sHomeThing)->pAuto, PA_HINT))
    SendThing(str, thing);
}

void SendAll(BYTE *str) {
  SOCK *i;

  for (i = sockList; i; i=i->sNext) {
    SENDCHECKPROMPT(i);
    SEND(str, i);
  }
}

BYTE *him       = "him";
BYTE *her       = "her";
BYTE *it        = "it";
BYTE *he        = "he";
BYTE *she       = "she";
BYTE *yourself  = "yourself";
BYTE *himself   = "himself";
BYTE *herself   = "herself";
BYTE *itself    = "itself";
BYTE *his       = "his";
BYTE *its       = "its";
BYTE *an        = "an";
BYTE *a         = "a";
BYTE *someone   = "someone";
BYTE *something = "something";

BYTE *HimHerIt(THING *thing) {
  switch(thing->tType) {
  case TTYPE_PLR:
  case TTYPE_MOB:
    if (Character(thing)->cSex == SEX_MALE) return him;
    if (Character(thing)->cSex == SEX_FEMALE) return her;
  }
  return it;
}

BYTE *HeSheIt(THING *thing) {
  switch(thing->tType) {
  case TTYPE_PLR:
  case TTYPE_MOB:
    if (Character(thing)->cSex == SEX_MALE) return he;
    if (Character(thing)->cSex == SEX_FEMALE) return she;
  }
  return it;
}

BYTE *HimselfHerselfItself(THING *thing) {
  switch(thing->tType) {
  case TTYPE_PLR:
  case TTYPE_MOB:
    if (Character(thing)->cSex == SEX_MALE) return himself;
    if (Character(thing)->cSex == SEX_FEMALE) return herself;
  }
  return itself;
}

BYTE *HisHerIts(THING *thing) {
  switch(thing->tType) {
  case TTYPE_PLR:
  case TTYPE_MOB:
    if (Character(thing)->cSex == SEX_MALE) return his;
    if (Character(thing)->cSex == SEX_FEMALE) return her;
  }
  return its;
}

BYTE *AnA(THING *thing) {
  switch(*thing->tSDesc->sText) {
  case 'a':
  case 'e':
  case 'i':
  case 'o':
  case 'u':
  case 'A':
  case 'E':
  case 'I':
  case 'O':
  case 'U':
    return an;
  default: /* fall thru to here */
    return a;
  }
}

BYTE *SomeoneSomething(THING *thing) {
  switch(thing->tType) {
  case TTYPE_PLR:
  case TTYPE_MOB:
    return someone;
  }
  return something;
}


/* Get the string substitution for a reserved escape sequence */
BYTE *SendThingStr(BYTE reservedChar, THING *srcThing, THING *dstThing, FLAG actionFlag) {
  if (reservedChar=='$') return "$";

  /* Substitute srcThing escape sequences */
  if (srcThing) {
    switch (reservedChar) {
    case 'n': /* name ie sdesc */
      if (BIT(actionFlag, SEND_PARTIALSRC))
        return SomeoneSomething(srcThing);
      else
        return srcThing->tSDesc->sText;
      break;
    case 'l': /* ldesc */
      if (srcThing->tType!=TTYPE_WLD)
        return Base(srcThing)->bLDesc->sText;
      else
        return strNull;
      break;
    case 'd': 
      return srcThing->tDesc->sText;
      break;
    case 's': 
      if (BIT(actionFlag, SEND_PARTIALSRC))
        return its;
      else
        return HisHerIts(srcThing);
      break;
    case 'e': 
      if (BIT(actionFlag, SEND_PARTIALSRC))
        return it;
      else
        return HeSheIt(srcThing);
      break;
    case 'm':
      if (BIT(actionFlag, SEND_PARTIALSRC))
        return its;
      else
        return HimHerIt(srcThing);
      break;
    case 'a': 
      return AnA(srcThing);
      break;
    }
  }

  /* Substitute dstThing escape sequeces */
  if (dstThing) {
    switch (reservedChar) {
    case 'N':
      /* dstThing==srcThing is a special case, where you are performing
         an action on yourself, so substitute himself, etc for $N
      */
      if (dstThing==srcThing)
        if (BIT(actionFlag, SEND_SRC))
          return yourself;
        else
          if (BIT(actionFlag, SEND_PARTIALDST))
            return itself;
          else
            return HimselfHerselfItself(dstThing);
      else
        if (BIT(actionFlag, SEND_PARTIALDST))
          return SomeoneSomething(dstThing);
        else
          return dstThing->tSDesc->sText;
      break;
    case 'L':
      if (dstThing->tType!=TTYPE_WLD)
        return Base(dstThing)->bLDesc->sText;
      else
        return strNull;
      break;
    case 'D':
      return dstThing->tDesc->sText;
      break;
    case 'S':
      if (BIT(actionFlag, SEND_PARTIALDST))
        return its;
      else
        return HisHerIts(dstThing);
      break;
    case 'E':
      if (BIT(actionFlag, SEND_PARTIALDST))
        return it;
      else
        return HeSheIt(dstThing);
      break;
    case 'M':
      if (BIT(actionFlag, SEND_PARTIALDST))
        return it;
      else
        return HimHerIt(dstThing);
      break;
    case 'A':
      return AnA(dstThing);
      break;
    }
  }

  return strNull; /* global null str pointer */
}

/* return whether we sent anything at all - this routine does the checking
   for whether the recipient can hear/see the message, it also checks for
   audio/visual links (control/telepathic links are check by SendThing) */
BYTE SendActionStr(BYTE *str, BYTE escapeCode, THING *srcThing, THING *dstThing, FLAG actionFlag, THING *sendThing) {
  BYTE        partialSrc;
  BYTE        send;
  BYTE        hear;
  BYTE        see;
  BASELINK   *baseLink;

  partialSrc = 0;
  send       = 1;
  hear       = 0;
  see        = 0;
  if (BIT(actionFlag, SEND_AUDIBLE|SEND_VISIBLE)) {
    see = 1;
    hear = 1;
    switch(ThingCanSee(sendThing, srcThing)) {
    case TCS_CANTSEE:    
      if(!ThingCanHear(sendThing, srcThing))
        send=0;
      else
        partialSrc=1;
      break;
    case TCS_SEEPARTIAL:  
      partialSrc=1; 
      break;
    }
  } else if (BIT(actionFlag, SEND_VISIBLE)) {
    see = 1;
    switch(ThingCanSee(sendThing, srcThing)) {
    case TCS_CANTSEE:    send=0;       break;
    case TCS_SEEPARTIAL: partialSrc=1; break;
    }
  } else if (BIT(actionFlag, SEND_AUDIBLE)) {
    hear = 1;
    if (!ThingCanHear(sendThing, srcThing)) send=0;
  }


  if (send) {
    if (*str) {
      SendThing(str, sendThing);
      if (BIT(actionFlag, SEND_HISTORY)) 
        SendHistory(str, sendThing);

      /* send to all the eavesdroppers */
      if (sendThing->tType>=TTYPE_BASE && Base(sendThing)->bLink) {
        if (see) {
          baseLink = BaseLinkFind(Base(sendThing)->bLink, BL_HEAR_SND, NULL);
          while (baseLink) {
            SendThing("^c%", baseLink->lDetail.lThing);
            SendThing(str, baseLink->lDetail.lThing);
            baseLink = BaseLinkFind(baseLink->lNext, BL_HEAR_SND, NULL);
          }
        }
      }
      /* send to all the peeping toms */
      if (sendThing->tType>=TTYPE_BASE && Base(sendThing)->bLink) {
        if (see) {
          baseLink = BaseLinkFind(Base(sendThing)->bLink, BL_SEE_SND, NULL);
          while (baseLink) {
            SendThing("^y%", baseLink->lDetail.lThing);
            SendThing(str, baseLink->lDetail.lThing);
            baseLink = BaseLinkFind(baseLink->lNext, BL_SEE_SND, NULL);
          }
        }
      }
    }    
    
    /* Send the xlated escape code if there is one */
    if (escapeCode) {
      if (partialSrc) BITSET(actionFlag, SEND_PARTIALSRC);
      if (escapeCode == toupper(escapeCode)) {
        /* only do this if its going to a dest */
        if (ThingCanSee(sendThing, dstThing) != TCS_SEENORMAL) 
          BITSET(actionFlag, SEND_PARTIALDST);
      }

      if (BIT(actionFlag, SEND_CAPFIRST)) {
        SendThingCapFirst(SendThingStr(escapeCode, srcThing, dstThing, actionFlag), sendThing);
        if (BIT(actionFlag, SEND_HISTORY))
          SendHistory(SendThingStr(escapeCode, srcThing, dstThing, actionFlag), sendThing);

        /* send to all the eavesdroppers */
        if (sendThing->tType>=TTYPE_BASE && Base(sendThing)->bLink) {
          if (see) {
            baseLink = BaseLinkFind(Base(sendThing)->bLink, BL_HEAR_SND, NULL);
            while (baseLink) {
              SendThing("^c%", baseLink->lDetail.lThing);
              SendThingCapFirst(SendThingStr(escapeCode, srcThing, dstThing, actionFlag), baseLink->lDetail.lThing);
              baseLink = BaseLinkFind(baseLink->lNext, BL_HEAR_SND, NULL);
            }
          }
        }
        /* send to all the peeping toms */
        if (sendThing->tType>=TTYPE_BASE && Base(sendThing)->bLink) {
          if (see) {
            baseLink = BaseLinkFind(Base(sendThing)->bLink, BL_SEE_SND, NULL);
            while (baseLink) {
              SendThing("^y%", baseLink->lDetail.lThing);
              SendThingCapFirst(SendThingStr(escapeCode, srcThing, dstThing, actionFlag), baseLink->lDetail.lThing);
              baseLink = BaseLinkFind(baseLink->lNext, BL_SEE_SND, NULL);
            }
          }
        }        

      } else {
        SendThing(SendThingStr(escapeCode, srcThing, dstThing, actionFlag), sendThing);
        if (BIT(actionFlag, SEND_HISTORY))
          SendHistory(SendThingStr(escapeCode, srcThing, dstThing, actionFlag), sendThing);

        /* send to all the eavesdroppers */
        if (sendThing->tType>=TTYPE_BASE && Base(sendThing)->bLink) {
          if (see) {
            baseLink = BaseLinkFind(Base(sendThing)->bLink, BL_HEAR_SND, NULL);
            while (baseLink) {
              SendThing("^c%", baseLink->lDetail.lThing);
              SendThing(SendThingStr(escapeCode, srcThing, dstThing, actionFlag), baseLink->lDetail.lThing);
              baseLink = BaseLinkFind(baseLink->lNext, BL_HEAR_SND, NULL);
            }
          }
        }
        /* send to all the peeping toms */
        if (sendThing->tType>=TTYPE_BASE && Base(sendThing)->bLink) {
          if (see) {
            baseLink = BaseLinkFind(Base(sendThing)->bLink, BL_SEE_SND, NULL);
            while (baseLink) {
              SendThing("^y%", baseLink->lDetail.lThing);
              SendThing(SendThingStr(escapeCode, srcThing, dstThing, actionFlag), baseLink->lDetail.lThing);
              baseLink = BaseLinkFind(baseLink->lNext, BL_SEE_SND, NULL);
            }
          }
        }
        
      }
    }
  }

  return send;
}
  
void SendAction(BYTE *str, THING *srcThing, THING *dstThing, FLAG actionFlag) {
/* color is processed in Q routines, not here so it will work with SendAll etc */
  LWORD i      = 0;
  LWORD count;
  LWORD sendCount = 0;
  BYTE  strEnd = FALSE;
  THING *room;
  THING *group;
  BYTE  buf[512];
  BYTE  escapeCode = 0;

  if (!str) return;
  /* determine if we need to capitalize the first escape sequence */
  for (i=0;str[i]&&str[i]!='$';i++)
    if (str[i]=='^' && str[i+1]) 
      i++; /* Only +1 since the ++ in the for loop will incr. again */
    else 
      BITCLR(actionFlag, SEND_CAPFIRST);

  i=0;
  while(str[i]!='\0') {
    count = 0;
    for(;count < 511 && str[i]!='\0' && str[i]!='$';i++) {
      buf[count] = str[i];
      count++;
    }
    buf[count] = '\0';
    if (str[i] == '\0') {
      strEnd = TRUE;
    } else if (str[i] == '$') {
      i+=2; /* skip over $<c> sequence */
    }
    /* at this point buf contains a copy of everything up to but not including
      the first $ escape sequence,
      
      if we have copied the whole of str then strEnd=TRUE, otherwise 
      str[i-2]=='$' and str[i-1] is equal to the escape code (n,N,s,S etc)
      
      str[i] is the first character after the escape code (possibly even \0)
     */
    /* escapeCode is either 0 or the escape code */
    if (!strEnd && str[i-2]=='$')
      escapeCode = str[i-1];
    else
      escapeCode = 0;

    /* fire off the string */
    if (BIT(actionFlag, SEND_SRC)) {
      /* send all the text up to the escape sequence or end of string */
      /* if there is an escape sequence, xlate it first then send the result */
      sendCount += SendActionStr(buf, escapeCode, srcThing, dstThing, actionFlag, srcThing);
    }
    /* if you are doing an action to yourself (srcThing==dstThing) then
       dont send the SEND_DST message, you got the message allready with SEND_SRC
    */
    if (BIT(actionFlag, SEND_DST) 
    && ( (BIT(actionFlag, SEND_SELF)) || (srcThing!=dstThing) )) {
      sendCount += SendActionStr(buf, escapeCode, srcThing, dstThing, actionFlag, dstThing);
    }
    /* Send the message to the room */
    if (BIT(actionFlag, SEND_ROOM)) {
      /* if the first pointer is a world structure - by Cams request */
      if (srcThing && srcThing->tType == TTYPE_WLD)
        room = srcThing->tContain;
      else if (srcThing && srcThing->tType>=TTYPE_BASE && Base(srcThing)->bInside)
        room = Base(srcThing)->bInside->tContain;
      else
        room = NULL;
      for (; room; room = room->tNext) {
        if (room!=srcThing && room!=dstThing) {
          sendCount += SendActionStr(buf, escapeCode, srcThing, dstThing, actionFlag, room);
        }
      }
    }
    /* Send it to any groups you might be in.... */
    if (BIT(actionFlag, SEND_GROUP) 
        && srcThing->tType >= TTYPE_CHARACTER
        && Character(srcThing)->cLead) {
      for (group = GroupGetHighestLeader(srcThing); group; group = Character(group)->cFollow) {
        if (GroupIsGroupedMember(group,srcThing) 
            && group!=srcThing 
            && group!=dstThing) {
          sendCount += SendActionStr(buf, escapeCode, srcThing, dstThing, actionFlag, group);
        }
      }
    }

    /* loop again or break out of the while loop */
    if (strEnd || sendCount==0) {
      /* break out of the loop if either:
          Nobody can see these messages
       or This is the end of the string silly!
      */
      break;
    } else {
      /* set str to point to the text immediately following the 
         the $ escape sequence
       */
      str += i;
      i = 0;
    }

    /* after first pass, no more capitalization */
    BITCLR(actionFlag, SEND_CAPFIRST);

  }
}

/* can make more efficient by replacing all SendThing's */
void SendArray(ULWORD tList, LWORD tListSize, LWORD column, THING *thing) {
  BYTE   buf[256];
  BYTE   truncateStr[256];
  LWORD  maxLen;
  LWORD  j;
  ULWORD i = 0;

  maxLen = 80/column;

  while(**((BYTE**)(tList+i*tListSize))!='\0') {
    sprintf(buf, "%s", StrTruncate(truncateStr, (*((BYTE**)(tList+i*tListSize))), maxLen-1));
    i++;
    if (i%column==0) {
      strcat(buf,"\n");
    } else {
      for(j = strlen(buf); j<maxLen; j++)
        buf[j]=' '; /* pad out with spaces */
      buf[j]='\0'; /* reterminate */
    }
    SendThing(buf, thing);
  }

  if (i%column!=0)
    SendThing("\n", thing);
}