FlCodebase3.1/
FlCodebase3.1/bounty/
FlCodebase3.1/challenge/
FlCodebase3.1/clans/
FlCodebase3.1/gods/
FlCodebase3.1/mobprogs/
FlCodebase3.1/player/
FlCodebase3.1/savemud/
/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Envy Diku Mud, you must comply with   *
 *  the original Diku license in 'license.doc', the Merc license in        *
 *  'license.txt', as well as the Envy license in 'license.nvy'.           *
 *  In particular, you may not remove either of these copyright notices.   *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  * 
 *                                                                         *
 *      ROM 2.4 is copyright 1993-1998 Russ Taylor                         *
 *      ROM has been brought to you by the ROM consortium                  *
 *          Russ Taylor (rtaylor@hypercube.org)                            *
 *          Gabrielle Taylor (gtaylor@hypercube.org)                       *
 *          Brian Moore (zump@rom.org)                                     *
 *      By using this code, you have agreed to follow the terms of the     *
 *      ROM license, in the file Rom24/doc/rom.license                     *
 *                                                                         *
 * Code Adapted and Improved by Abandoned Realms Mud                       *
 * and Aabahran: The Forsaken Lands Mud by Virigoth                        *
 *                                                                         *
 * Continued Production of this code is available at www.flcodebase.com    *
 ***************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "merc.h"
#include "raid.h"
#include "cabal.h"

/***************************************************************/
/*Following are city raid orientanted routines for Forsaken Lands  */
/*mud created by Virigoth circa Feb 2004.  Copyrighted for Forsaken*/
/*Lands mud Feb 4, 2004.  Do not use or copy without explicit */
/*permission of author. (Voytek Plawny aka Virigoth)	       */
/***************************************************************/

//table which holds special raider strings etc.
const struct raid_strings raid_table [RAID_MAX] = {
  { ACT_UNDEAD, 
    "undead",
    "bite",
    "rotten zombie",
    "a rotten zombie",
    "Covered in clotted veins and wrapped in stench a zombie stands here.\n\r"
  },
  { ACT_UNDEAD, 
    "undead",
    "frbite",
    "wraith",
    "a wraith",
    "Its eyes blazing with hate a wraith floats before you.\n\r"
  },
  { ACT_UNDEAD, 
    "undead",
    "tainted",
    "fallen ruler",
    "a fallen ruler",
    "Dressed in rotten regal clothing a fallen ruler awaits its victim.\n\r"
  },
  { ACT_UNDEAD, 
    "undead",
    "skdrain",
    "vampiric lord",
    "Vampiric blood lord",
    "Draped in fresh skin and blood a vampiric blood lord floats nearby.\n\r"
  },

  { 0,
    "demon",
    "bite",
    "gremlin",
    "a gremlin",
    "Scrambing wildly on all fours a gremlin circles the area.\n\r"
  },
  { ACT_MAGE,
    "demon",
    "magic",
    "demonic cultist",
    "a demonic cultist",
    "With a bloody dagger impaled upon his arm a demonic cultist lurks.\n\r"
  },
  { 0,
    "demon",
    "tainted",
    "demon",
    "a demon",
    "Covered in drool a mutated demon searches for a fresh victim.\n\r"
  },
  { 0,
    "demon",
    "blight",
    "tryst lord",
    "tryst Lord",
    "A mutilated torso in its claws a Tryst Lord feeds before you.\n\r"
  },

  { 0,
    "treant",
    "crush",
    "wild treant",
    "a wild treant",
    "A wild treant moans with ancient anguish.\n\r"
  },
  { 0,
    "lizard",
    "slash",
    "slith warrior",
    "a slith warrior",
    "A slith warrior stands before you, scales glistening with blood and muck.\n\r"
  },
  { ACT_MAGE,
    "faerie",
    "magic",
    "dryad",
    "a dryad",
    "A scantly clad dryad lingers here, muttering prayers to Gaia.\n\r"
  },
  { 0,
    "unique",
    "blast",
    "elemental",
    "an elemental",
    "A spirit of earthly power emits an aura of rage and anger. \n\r"
  },
};

/* PROTOTYPES */

/* PRIVATE */

RAID_DATA* new_raid(){
  RAID_DATA* pr = alloc_mem( sizeof( *pr ));

  return pr;
}

void free_raid( RAID_DATA* pr ){
  free_mem( pr, sizeof( *pr ));
}

//creates a raid in a given area
//if specRaid is >= 0 data from raid table are used.
bool create_raid( AREA_DATA* pArea, int vnum, int tot, int life, int specRaid, CABAL_DATA* pCab){
  RAID_DATA* pr;
  MOB_INDEX_DATA* pIndex;

  if (pArea == NULL || vnum < 1 || tot < 1 || life < 1)
    return FALSE;
  else if ( (pIndex = get_mob_index( vnum )) == NULL){
    bug("raid.c>create_raid: Could not load mob index %d", vnum );
    return FALSE;
  }
  
  if (specRaid >= 0 && specRaid >= RAID_MAX){
    bug("raid.c>create_raid: specRaid (%d) over max.", specRaid);
    specRaid = -1;
  }

  pr = new_raid();
  memset( pr, 0, sizeof( *pr ));

  pr->pCab = pCab;
  pr->mob_vnum = vnum;
  pr->mob_total = tot;
  pr->lifetime = life;

  pr->mob_spawned = 0;
  pr->duration = 0;

  if (specRaid >= 0){
    pr->race= race_lookup(raid_table[specRaid].race);
    if (pr->race < 0)
      pr->race = race_lookup("human");

    pr->attack= attack_lookup(raid_table[specRaid].attack);
    if (pr->attack < 0)
      pr->attack = attack_lookup("slash");

    pr->act = raid_table[specRaid].act;

    pr->name = raid_table[specRaid].name;
    pr->long_desc = raid_table[specRaid].long_desc;
    pr->short_desc = raid_table[specRaid].short_desc;
  }
  else{
    pr->race = pIndex->race;
    pr->act = 0;
    pr->name = pIndex->player_name;
    pr->long_desc = pIndex->long_descr;
    pr->short_desc = pIndex->short_descr;
  }

  if (pArea->raid != NULL)
    free_raid(pArea->raid);
  pArea->raid = pr;

  //set stamp on cabal for raids
  pCab->raid_stamp = mud_data.current_time;
  //set stamp on area
  pArea->last_raid = mud_data.current_time;
  return TRUE;
}

// spawns a single raider and sets his path towards target area
bool raid_spawn( AREA_DATA* pArea, RAID_DATA* pr ){
  const int MAX_AREAS = 16;
  AREA_DATA* areas[MAX_AREAS];
  AREA_DATA* pTar, *pOri;
  ROOM_INDEX_DATA* pRTar, *pROri;
  CHAR_DATA* pMob;
  EXIT_DATA* pExit;
  PATH_QUEUE* path;

//  int sectors[2] = {SECT_CITY, SECT_INSIDE};
  int max_area = 0;
  int area_pool = 10;
  int dist = 0;

  if (pArea == NULL || pr == NULL)
    return FALSE;
  else
    pTar = pArea;

  
  /* first get the list of areas that connect to target area */
  for (pExit = pTar->exits; pExit; pExit = pExit->next_in_area ){
    //skip on blind exit, or exit to same area
    if (pExit->to_room == NULL || pExit->to_room->area == pTar)
      continue;
    //skip on cabal home areas
    else if (pExit->to_room->area->pCabal 
	     && pExit->to_room->area->pCabal->anchor->area == pExit->to_room->area)
      continue;
    //skip jails, restricted areas etc.
    else if (IS_AREA(pExit->to_room->area, AREA_MUDSCHOOL)
	     || IS_AREA(pExit->to_room->area, AREA_NOPATH)
	     || IS_AREA(pExit->to_room->area, AREA_RESTRICTED))
      continue;
    areas[max_area++] = pExit->to_room->area;
  }
//if no exists, then origin is target
  if (max_area < 1)
    pOri = pTar;
  else
    pOri = areas[number_range(0, max_area - 1)];

  //we now have origin and target area.  Try for random rooms in each.
  pROri =  get_rand_room(pOri->vnum,pOri->vnum,	//area range (0 to ignore)
			 0,0,			//room ramge (0 to ignore)
			 0,0,			//areas to choose from
			 NULL,0,		//areas to exclude
			 NULL,0,		//sectors required
			 NULL,0,		//sectors to exlude
			 NULL,0,		//room1 flags required
			 0,0,			//room1 flags to exclude
			 NULL,0,		//room2 flags required
			 NULL,0,		//room2 flags to exclude
			 area_pool,		//number of seed areas
			 TRUE,			//exit required?
			 FALSE,			//Safe?
			 NULL);			//Character for room checks
  
  if (pROri == NULL)
    return FALSE;

  pRTar =  get_rand_room(pTar->vnum,pTar->vnum,	//area range (0 to ignore)
			 0,0,			//room ramge (0 to ignore)
			 0,0,			//areas to choose from
			 NULL,0,		//areas to exclude
			 NULL,0,		//sectors required
			 NULL,0,		//sectors to exlude
			 NULL,0,		//room1 flags required
			 0,0,			//room1 flags to exclude
			 NULL,0,		//room2 flags required
			 NULL,0,		//room2 flags to exclude
			 area_pool,		//number of seed areas
			 TRUE,			//exit required?
			 FALSE,			//Safe?
			 NULL);			//Character for room checks
  if (pRTar == NULL || pRTar == pROri)
    return FALSE;

  //see if we can get a path between the two rooms
  if ( (path = generate_path( pROri, pRTar, 256, TRUE, &dist, NULL)) == NULL){
    clean_path();
    return FALSE;
  }
  else
    clean_path();
  
  //time to load the mob
  if ( (pMob = create_mobile( get_mob_index( pr->mob_vnum))) == NULL){
    clean_path_queue( path );
    return FALSE;
  }
  else{
    AFFECT_DATA af;

    af.type = gsn_timer;
    af.level= pMob->level;
    af.duration = 3 * pr->lifetime / 2;
    af.where = TO_AFFECTS;
    af.bitvector = 0;
    af.location = APPLY_NONE;
    af.modifier =  0;
    affect_to_char(pMob, &af);
  } 

  //set the path
  pMob->spec_path = path;
  //set cabal
  pMob->pCabal = pr->pCab;
  //set attack
  pMob->dam_type = pr->attack;

  //set the raid string
  pMob->act |= ACT_RAIDER | pr->act;

  //set strings
  free_string( pMob->name );
  pMob->name = str_dup(pr->name );
  free_string( pMob->long_descr);
  pMob->long_descr = str_dup(pr->long_desc );
  free_string( pMob->short_descr );
  pMob->short_descr = str_dup(pr->short_desc );

  //set mob's security to 6969 before putting it in room to prevent firestorm
  pMob->trust = 6969;
  char_to_room( pMob, pROri );
  act("$n enters the area.", pMob, NULL, NULL, TO_ROOM);
  pMob->trust = pMob->level;

  return TRUE;
}

/* INTERFACE */
//spawns raiders based on raid data
void raid_update( AREA_DATA* pArea, RAID_DATA* pr ){
  int mobs;
  int mobs_expected = 0;
  int count = 0;

  if (pArea == NULL || pr == NULL)
    return;

  if (pr->duration > pr->lifetime){
    char buf[MIL], buf1[MIL];
    CHAR_DATA* ch;
    free_raid( pr );
    REMOVE_BIT(pArea->area_flags, AREA_RAID);

    sprintf( buf, "A messenger yells '`6%s`6 has defeated the %ss!``'\n\r",
	     pArea->name,
	     pArea->raid->name); 
  sprintf(buf1, "A guard yells '`6The %ss are falling back! Chaaarge!!``'\n\r", 
	  pArea->raid->name);
    for (ch = player_list; ch; ch = ch->next_player){
      if (!IS_AWAKE( ch))
	continue;
      else if (ch->in_room->area == pArea)
	send_to_char( buf1, ch );
      else if (IS_AREA(pArea, AREA_CABAL) && is_same_cabal(ch->pCabal, pArea->pCabal))
	send_to_char( buf, ch );
    }

    pArea->raid = NULL;
    return;
  }
  else
    pr->duration += 2;

  //we get mobs expected by following linear relationship
  mobs_expected = pr->mob_total * pr->duration / pr->lifetime;
  //we get mobs to spawn by subtracting mobs spawned so far 
  //from expected
  mobs = mobs_expected - pr->mob_spawned;
  count = 0;
  while (mobs && count++ < 10){
    count++;
    if (raid_spawn( pArea, pr)){
      mobs--;
      pr->mob_spawned++;
    }
  }
}
    
    
//sets raid in an area
//type is one of the RAID_XXXX constants
void set_raid(AREA_DATA* pArea, int type, CABAL_DATA* pCab, int level){
  CHAR_DATA* ch;
  char buf[MIL];
  char buf1[MIL];

  int specRaid = RAID_NORMAL;
  int tot;
  int life;
  int vnum;
  int offset = 0;

  //get vnum
  if (level < 0)
    level = number_range(0, 3);

  switch (level){
  case 0:
    vnum = MOB_VNUM_RAID_WEAK;	break;
    offset = 0;
  case 1:
    offset = 1;
    vnum = MOB_VNUM_RAID_MEDI;	break;
  case 2:
    offset = 2;
    vnum = MOB_VNUM_RAID_NORM;	break;
  default:
    offset = 3;
    vnum = MOB_VNUM_RAID_STRO;	break;
  };


  //get lifetime and number of mobs
  if (IS_AREA(pArea, AREA_CABAL)){
    life = number_range(24, 48);
    tot = number_range(12, 24);
  }
  else{
    life = number_range(12, 24);
    tot = number_range(24, 48);
  }
    
  if (type >= 0 && type < RAID_MAX)
    specRaid = UMIN(type + offset, RAID_MAX);
  
  if (!create_raid( pArea, vnum, tot, life, specRaid, pCab)){
    bug("raid.c>set_raid: Could not create raid.", 0);
    return;
  }

  sprintf( buf, "A messenger yells '`6%s`6 is under attack by %ss!``'\n\r",
	   pArea->name,
	   pArea->raid->name);
  sprintf(buf1, "A guard yells '`6Scouts report %ss marching upon us! To the walls!``'\n\r", pArea->raid->name);

  for (ch = player_list; ch; ch = ch->next_player){
    if (!IS_AWAKE( ch))
      continue;
    else if (ch->in_room->area == pArea)
      send_to_char( buf1, ch );
    else if (IS_AREA(pArea, AREA_CABAL) && is_same_cabal(ch->pCabal, pArea->pCabal))
      send_to_char( buf, ch );
    else
      send_to_char( buf, ch );
  }

}

//Imm command for raids
//creates a raid of given type in area of given room
//raid <room number> [type]
void do_immraid( CHAR_DATA* ch, char* argument ){
  char arg[MIL];
  ROOM_INDEX_DATA* room;
  int vnum;
  int type;

  argument = one_argument(argument, arg );

  if (IS_NULLSTR(arg)){
    send_to_char("raid <room number> [normal/nature/undead/demon]\n\r", ch);
    return;
  }
  else if ( (vnum = atoi( arg)) < 1
	    || (room = get_room_index( vnum)) == NULL){
    send_to_char("Unknown room vnum.\n\r", ch);
    return;
  }

  if (!str_cmp( argument, "nature"))
    type = RAID_NATURE_1;
  else if (!str_cmp( argument, "demon"))
    type = RAID_DEMON_1;
  else if (!str_cmp( argument, "undead"))
    type = RAID_UNDEAD_1;
  else 
    type = RAID_NORMAL;

  set_raid(room->area, type, ch->pCabal, -1);

  if (room->area->raid == NULL){
    send_to_char("Error creating raid.\n\r", ch);
  }
  else
    sendf( ch, "Raid set:\n\r"\
	   "Raiders: %d\n\r"\
	   "Duration: %dh\n\r"\
	   "Vnum: %d (%s)\n\r",
	   room->area->raid->mob_total,
	   room->area->raid->lifetime,
	   room->area->raid->mob_vnum,
	   room->area->raid->short_desc);
}

//cabal ability to cause raids
//type of raid and cost decided randomly
//must target a city
void do_raid( CHAR_DATA* ch, char* argument ){
  AREA_DATA* pArea;
  CABAL_DATA* pCab;

  char arg[MIL], buf[MIL];
  int count = 0, city = 0;
  int level, cost, cabal_cost;
  int type;

  if (ch->pCabal == NULL){
    send_to_char("Huh?\n\r", ch);
    return;
  }
  else
    pCab = get_parent(ch->pCabal);

  //switch to imm command
  if (IS_IMMORTAL(ch) && get_trust(ch) >= CREATOR){
    do_immraid( ch, argument );
    return;
  }
  else if (!IS_CABAL(pCab, CABAL_RAID)){
    send_to_char("Huh?\n\r", ch);
    return;
  }    
  else if (is_captured( pCab)){
    send_to_char("Your altar lacks the power of your Standard.\n\r", ch);
    return;
  }

  argument = one_argument( argument, arg );

  if (IS_NULLSTR(arg)){
    send_to_char("Raid <area #> <strength 1-4>\n\rAreas:\n\r", ch);
    buf[0] = 0; count = 0;
    for (pArea = area_first; pArea; pArea = pArea->next){
      if (!IS_AREA(pArea, AREA_CITY)
	  && (!IS_CABAL(pCab, CABAL_ROUGE) || !IS_AREA(pArea, AREA_CABAL) || IS_AREA(pArea, AREA_RESTRICTED))
	  )
	continue;
      sprintf(buf, "%2d. %-20.20s (%s)\n\r", 
	      ++count, 
	      pArea->name, 
	      pArea->pCabal ? pArea->pCabal->name : "none" );
      send_to_char( buf, ch );
    }
    return;
  }
  else if ( (city = atoi(arg)) < 1){
    send_to_char("Raid <area #>\n\r", ch);
    return;
  }
  for (pArea = area_first; pArea; pArea = pArea->next){
    if (!IS_AREA(pArea, AREA_CITY)
	&& (!IS_CABAL(pCab, CABAL_ROUGE) || !IS_AREA(pArea, AREA_CABAL)))
      continue;
    if (++count != city)
      continue;
    else
      break;
  }
  if (pArea == NULL){
    send_to_char("No such area.  Use \"raid\" for list.\n\r", ch);
    return;
  }
  if (pArea->raid){
    send_to_char("That area is already under attack!\n\r", ch);
    return;
  }
  else if (mud_data.current_time - pArea->last_raid < RAID_WAIT){
    int hour = (RAID_WAIT + pArea->last_raid - mud_data.current_time) / 30;
    int days = hour / MAX_HOURS;

    sendf(ch, "The area's defenses will still be altert for %d %s%s.\n\r", 
	  days > 1 ? days : hour,
	  days > 1 ? "day" : "hour",
	  (days  == 1 || hour == 1) ? "" : "s");
    return;
  }
  else if (mud_data.current_time - pCab->raid_stamp < CAB_RAID_WAIT){
    int hour = (CAB_RAID_WAIT + pCab->raid_stamp - mud_data.current_time) / 30;
    int days = hour / MAX_HOURS;

    sendf(ch, "%s will not be ready to raid for another %d %s%s.\n\r", 
	  pCab->name,
	  days > 1 ? days : hour,
	  days > 1 ? "day" : "hour",
	  (days  == 1 || hour == 1) ? "" : "s");
    return;
  }
  else if (pArea->pCabal && is_friendly(pArea->pCabal, pCab) != CABAL_ENEMY){
    send_to_char("You must declare a vendetta first.\n\r", ch);
    return;
  }
  //get level of the mobs to use
  level = URANGE(1, atoi(argument), 4) - 1;
  //get cost
  cost = 50 + (level * 25);
  cabal_cost = UMIN(GET_CAB_CP(pCab), 500 + (level * 200));
  if (cabal_cost < 0)
    cabal_cost = 0;

  if (GET_CP(ch) < cost){
    sendf(ch, "You need at least %d %ss to order a raid.\n\r",
	  cost, ch->pCabal->currency);
    return;
  }
  CP_GAIN(ch, -cost, TRUE );
  CP_CAB_GAIN(pCab, -cabal_cost );

  //select type
  if (!str_cmp(pCab->name, "Watcher"))
    type = RAID_NATURE_1;
  else if (IS_UNDEAD(ch))
    type = RAID_UNDEAD_1;
  else if (IS_DEMON(ch))
    type = RAID_DEMON_1;
  else
    type = RAID_NORMAL;

  set_raid(pArea, type, pCab, level);
  if (pArea->raid == NULL){
    send_to_char("Error.  Contact Staff.\n\r", ch);
    return;
  }
  sprintf(buf, "On %s's orders %ss march on %s.",
	  ch->name, pArea->raid->name, pArea->name);
  cabal_echo( pCab, buf );
  if (cabal_cost){
    sprintf(buf, "%d %ss have been drawn from coffers to pay for the raid.",
	    cabal_cost, pCab->currency);
    cabal_echo( pCab, buf );
  }
}

//checks if a kill on raid mob should increase the killer's cabal sup/cps
//or grant the killer cps
void raidmob_check( CHAR_DATA* vch, CHAR_DATA* victim){
  CHAR_DATA* ch = vch, *wch;
  CABAL_DATA* pCab;
  char buf[MIL];
  int gold;

  if (victim == NULL || vch == NULL || victim->in_room == NULL)
    return;
  else if (!IS_NPC(victim) || !IS_SET(victim->act, ACT_RAIDER))
    return;
  pCab = victim->in_room->area->pCabal;

  //charmie to owner switch
  if (IS_AFFECTED(vch, AFF_CHARM) && IS_NPC(vch) && vch->master)
    ch = vch->master;
  else
    ch = vch;

  if (IS_NPC(ch))
    return;

  //always get some gold unless member of same cabal
  for (wch = ch->in_room->people; wch; wch = wch->next_in_room){
    if (wch->fighting != victim)
      continue;
    else if (wch->pCabal != NULL 
	     && is_same_cabal(wch->pCabal, victim->pCabal))
      continue;
    gold = number_range(300, 1000);
    sprintf( buf, "The bank rewards you with %d gold.\n\r", gold );
    send_to_char( buf, wch );
    wch->in_bank +=  gold;
    if (wch->pCabal && !is_same_cabal(pCab, wch->pCabal)){
      CP_GAIN(wch, number_range(1, 3), TRUE );
    }
  }

  //cabal related rewards
  if (pCab){
    long sup_dam, cps_dam;
    
    sup_dam = number_range(1, 12 * CABAL_FULL_SUPPORT) / 6;
    cps_dam = number_range( CPTS , 3 * CPTS);      
    
    distribute_cps( ch, pCab, cps_dam, TRUE );
    
    //If person member of justice cabal and is in lawful city gain cps
    //Or if person is in lawful city belonging to their cabal
    if (IS_CABAL(pCab, CABAL_JUSTICE) 
	&& IS_AREA(victim->in_room->area, AREA_LAWFUL) ){
      justice_raid_gain( sup_dam, cps_dam );
    }
    //The raided cabal gains back some damage
    if (IS_AREA(victim->in_room->area, AREA_CITY)){
      pCab->raid_cps += cps_dam;
      pCab->raid_sup += sup_dam;
    }
  }
}