//////////////////////////////////////////////////////////////////////////////////////
// RAID system.
// Overall goal: allow raids every few hours, if there are enough people online from
// a sect, they will be teleported to an enemy city, with mobs (soldiers) to aid
// in the raid, goal, kill as many people/mobs as possible
// at the end of the raid, everyone is rewarded based on the kills they made.
// and deaths lower points.
//
// killing certain mobs during a raid causes bonus points to be added for the raider group
// those bonus points at the end of the raid, earn you bonus exp/money.
//
// killing enemy players during raid cause double xp.
// 
// Things to add when home:
// While raiding, no recalling, transfering, teleporting out of waiting room.
// No leaving the zone your raiding!
// On death you restart back in the Raid start-room apposed to your guild-hall/sect town
//
// Add level based stat controller for soldier vnum
// Add act/plr flags to appropriate tables, non-settable.
// Create holder-rooms, flagged to hell to prevent leaving/entering unless transfered in by
// the system.
// at the end of boot_db add in rebel_raid = new raid_data(); and ally_raid = new raid_data();
// on shutdown add delete rebel_raid; delete ally_raid; 
// on shutdown, transfer all RAIDERS to their hometowns to prevent glitchs, strip raider flag.
// add random object rewards for competing in raids
// add double-raid-bonus, to daily bonus's.
// add aggress change that makes ALL mobs aggress against raiders that are not the same sect!
//			-- this will mean that any-mob in midgaard will aggress against rebel
//			-- if they have the RAIDER flag on.
// raiders do not group-loot, mobs/players do not drop corpses when killed by raiders.
//			-- fair playing technique: forces safety of players, preventing them from being useless.
// blocking of nukes of a area while raids are going on (you'd kill all your own guys aswell as the enemies, unfair advantage)
//
// checks to raw_kill about raiding, no dishonorable pvp points will be awarded unless you kill your own sect
// removing the level checks for it.
//
// kill/death counters into raw_kill.
//
//

#define MOB_VNUM_SOLDIER	 298
#define ROOM_VNUM_REBEL_HOLDER	 201 // room that holds rebels while waiting raid
#define ROOM_VNUM_ALLY_HOLDER	 202 // room that holds ally's while waiting raid

#define RAID_SIZE		  25 // how many people are required for a raid!
#define ROOM_VNUM_REBEL_RAIDED  0000 // entrance 1
#define ROOM_VNUM_ALLY_RAIDED	3000 // entrance 1 
#define PLR_RAIDER              (ff) // are we a raider!
#define ACT_RAIDING		(ff) // for mobile raiders!

//////////////////////////////////////////////////////////////////////////////////////
class raid_data;

raid_data *rebel_raid;						// global rebel raid data
raid_data *ally_raid;						// global ally raid data

//////////////////////////////////////////////////////////////////////////////////////
class raid_data {
	public:
		raid_data() { kills = 0; deaths = 0; bonus = 0; start_time = 0; end_time = 0; next_raid = current_time+number_range(2600,4800); raid_list.clear(); }
		~raid_data() { /* Oddly nothing happens :P */ raid_list.clear(); };
	private:
		int kills;					// how many people your team has killed in the raid
		int deaths;					// how many deaths your team has had
		int bonus;					// Bonus points earned
		time_t start_time;				// start time of the raid
		time_t end_time;				// end time of the raid.
		time_t next_raid;				// how long before you can raid again!
		Lexi::List<CHAR_DATA *>raid_list;		// who is raiding!
	public:
		void add_list(CHAR_DATA *ch) { if(!InList(raid_list, ch)) raid_list.push_back(ch); }
		void remove_list(CHAR_DATA*ch) { if(InList(raid_list, ch)) raid_list.remove_one(ch); }
		void in_raid(CHAR_DATA *ch) {if(InList(raid_list, ch)) return true;   return false; }
		void send_to_raid(CHAR_DATA *ch, const char * argument) {
			FOREACH(Lexi::List<CHAR_DATA *>, raid_list, raid_iter) {
				CHAR_DATA *r = (*raid_iter);
				if(r == ch) {
					r->Sendf("[RAID] %s\n\r", argument);
				} else {
					r->Sendf("[RAID] %s: %s\n\r", ch->name, argument);
				}
			}
		}
		CHAR_DATA *first_raider() { return (*raid_list.begin()); }
		size_t raid_size() { return raid_list.size; }
		time_t get_start() { return start_time; }
		time_t get_end() { return end_time; }
		time_t get_next() { return next_raid; }
		void add_bonus(int bonbon) { bonus +=bonbon; }	// bonbon means candy ;)
		int get_bonus() { return bonus; }
		void add_kill() { kills++; }
		int get_kills() {return kills; }
		void add_death() { deaths++; }
		int get_deaths() { return deaths; }
		void set_next() { next_raid = current_time+(3600*3); start_time = 0; end_time = 0; kills = 0; deaths = 0; bonus = 0;}
		void set_start() { start_time = current_time; }
		void set_end() { end_time = start_time+400; }	// 3600 = 1 hour, 400 should be approx 15 minutes (give or take, we hope)
		void reward() {
			// lets reward and cleanup!
			FOREACH(Lexi::List<CHAR_DATA *>, raid_list, raid_iter) {
				CHAR_DATA *raider = (*raid_iter);
				int bon = (kills-deaths)*bonus; // this can work out to be a negative effect
				char_from_combat(ch);		// stop fighting if engaged!
				if(!IS_NPC(raider)) {
					gain_exp(raider, bon);	// LOTS of experience in raiding!
					raider->pcdata->questpoints +=bonus;	// lots of quest-points in raiding.
										// add in RP-points for raids!
					raid_list.remove_one(raider);
					extract_char(raider, false);
					ch->Sendf("The raid is over!\n\r");
				} else 
					raid_list.remove_one(raider);
					extract_char(raider, true);
				}
			}
			// make-sure the raid-list is cleared out.
			raid_list.clear();
			kills = 0;				// reset our kill data
			deaths = 0;				// reset our death data
			bonus = 0;				// reset our bonus data.
		}
		void raid_start(long vnum) {
			ROOM_INDEX_DATA *rm = get_room_index(vnum);
			if(!rm) {
				log_string("[RAID]: failed to find raid start vnum!");
				set_next();
				FOREACH(Lexi::List<CHAR_DATA *>, raid_list, raid_iter) {
					CHAR_DATA *raider = (*raid_iter);
					char_from_combat(ch);		// stop fighting if engaged!
					if(!IS_NPC(raider)) {
						raid_list.remove_one(raider);
						extract_char(raider, false);
						ch->Sendf("The raid failed before it could begin!!\n\r");
					} else 
						raid_list.remove_one(raider);
						extract_char(raider, true);
					}
				}
				return;
			}

			// make-sure there are enemies to kill during the raid!
			log_string("[RAID]: %s has been reset by the raid system for %s's raid!", rm->area->name, first_raider()->name);
			reset_area( rm->area );

			// log it for our records!  (this is going to cause some SERIOUS fun!
			log_string("[RAID]: Raid started at %s, by %s, with %d player raiders!", grab_time_log(start_time), first_raider()->name, raid_list.size());
			FOREACH(Lexi::List<CHAR_DATA *>, raid_list, raid_iter) {
				CHAR_DATA *raider = (*raid_iter);
				char_from_room(raider);
				char_to_room(raider, rm);
			}

			// add our soldiers into the raid!
			MOB_INDEX_DATA *pM = get_mob_index(MOB_VNUM_SOLDIER);
			int bm = 0;
			if(pM) {
				int x = 0, y = number_range(25,100)-raid_list.size();		// larger the group, less soldiers added.
				if(y < 0) y = 10;						// min of 10 get added!
				for(x = 0; x < y; x++) {
					pM->level = number_range(1, 45);			// yeah, we adjust the index'level, because this
												// is a dummy mob, and on create_mobile, it sets
												// information based on the level (important)

					if(number_percent() > number_range(95,100)) {		// chance we cause the mob to be a boss mob
						SET_BIT(pM->act, ACT_BOSS);			// which increases our offensive greatly
						bm++;						// this is a low chance, but if it happens
					}							// the raid will be allot stronger for it.
	
					pM->iclass = number_range(0,7);				// change our class, this will affect our
												// overall AI, aswell as what gear will get
												// loaded onto the mob

					CHAR_DATA *mob = create_mobile(pM);
												// add raid-gear loading onto the mobile
												// so we don't send naked mobs to the fight!
					char_to_room(mob, rm);
					mob->rebel = first_raider()->rebel;			// make us the same team!
					raid_list.push_back(mob);
					if(!IS_SET(mob->act, ACT_AGGRESSIVE))
						SET_BIT(mob->act, ACT_AGGRESSIVE);		// just incase we aren't set to aggress.
												// mobs aggress against all *NON* sect
												// including npc's, thus making the soldiers
												// attack everything
					REMOVE_BIT(pM->act, ACT_BOSS);				// we dont' want all our soldiers working
												// like a boss mob
					SET_BIT(mob->act, ACT_STAY_AREA);			// force them to stay in the area, and
												// not leave into connected area's.
												// which would be annoying.

					SET_BIT(mob->act, ACT_RAIDING);				// set the mob as a raider, so that when
												// victims are killed, it automatically
												// checks and updates the raid information.
				}
				// we want to keep track of this information for debugging purposes, aswell as being nosey
				// frankly this will help keep plenty of information incase something should happen and
				// we need to diagnose an issue pertaining to the raid core code.
				log_string("[RAID]: Raid added %d soldiers to %s's team, with %d boss mobs!!", y, first_raider()->name, bm);
			} else {
				log_string("[RAID]: Raid could not load any soldiers.  This could be a tough one for %s's raid!", first_raider()->name);
			}

			// may need to make this formatted if it isn't :P  but it is! we know this because we format { }
			sitrep(NULL, 0, "The %s are raiding %s: {%d}!", first_raider->rebel ? "Rebellion!" : "Alliance", first_raider->rebel ? "Midgaard" : "New Thalos", raid_list.size());
		}
};

//////////////////////////////////////////////////////////////////////////////////////
// Simple updater to control raid specifics.
// Raid updates every 15 minutes, a raid shouldn't last longer then that, and should be
// roughly 4 hours between raids
Updater(raid_update) {
	if(ally_raid->get_start() != 0) {
		// raid ended!
		if(ally_raid->get_end() < current_time) {
			ally_raid->reward();
			sitrep(NULL, 0, "The alliance raid is over, casualties a plenty.");
			ally_raid->set_next();
		}
	} else {
		if(ally_raid->get_next() < current_time) {
			FOREACH(Lexi::List<CHAR_DATA *>, player_list, player_iter) {
				CHAR_DATA *ch = (*player_iter);
				if(ch->rebel != SECT_ALLIANCE)
					continue;
				ch->Sendf("[RAID]     It is possible to raid the rebellion hometown now!      [RAID]\n\r");
			}
		}
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Check the rebellion now
	///////////////////////////////////////////////////////////////////////////////////////////////////////////

	if(rebel_raid->get_start() != 0) {
		// raid ended!
		if(rebel_raid->get_end() < current_time) {
			rebel_raid->reward();
			sitrep(NULL, 0, "The rebellion's raid is over, casualties a plenty.");
			rebel_raid->set_next();
		}
	}
	else {
		if(rebel_raid->get_next() < current_time) {
			FOREACH(Lexi::List<CHAR_DATA *>, player_list, player_iter) {
				CHAR_DATA *ch = (*player_iter);
				if(ch->rebel != SECT_REBELLION)
					continue;
				ch->Sendf("[RAID]     It is possible to raid the alliance hometown now!      [RAID]\n\r");
			}
		}
	}
	return;
}

//////////////////////////////////////////////////////////////////////////////////////
// Here we go with the main data
COMMAND(cmd_raid) {
	raid_data *rd;
	char arg[MIL];
	if(NullString(argument)) {
		ch->Sendf("Syntax: raid join quit stats talk start\n\r");
		return;
	}

	if(IS_NPC(ch)) {
		ch->Sendf("Mobiles have no control over raiding!\n\r");
		return;
	}

	argument = one_argument(argument, arg);

	switch(ch->rebel) {
		default: return;
		case SECT_ALLIANCE: rd = ally_raid; break;
		case SECT_REBELLION: rd = rebel_raid; break;
	}

	if(!str_cmp(arg, "talk")) {
		if(rd->get_start() == 0){
			ch->Sendf("There is no raid started yet!\n\r");
			return;
		}

		if(!rd->in_raid(ch)) {
			ch->Sendf("Your not in a raid!\n\r");
			return;
		}

		if(NullString(argument)) {
			ch->Sendf("Syntax: raid talk <message>\n\r");
			return;
		}

		send_to_raid(ch, argument);
		return;
	}

	if(!str_cmp(arg, "stats") || !str_cmp(arg, "info")) {
		if(!rd->in_raid(ch)) {
			ch->Sendf("Your not in a raid!\n\r");
			return;
		}

		ch->Sendf("[----------------------------- RAID -----------------------------]\n\r");
		ch->Sendf(" Kills:                    %3d      Deaths:                   %3d\n\r", rd->get_kills(); rd->get_deaths());
		ch->Sendf("            Bonus points earned:  %d\n\r", rd->get_bonus());
		ch->Sendf("    There are currently %d PCs and NPC's within the raid group!\n\r", rd->raid_size());

		// what time the raid started at, and what time it will ultimatly end at!
		if(rd->get_start() != 0) {
			ch->Sendf(" Started at:       %s\n\r", grab_time_log(rd->get_start());
			ch->Sendf(" Will end At:      %s\n\r", grab_time_log(rd->get_end());
		}
		ch->Sendf("[----------------------------- RAID -----------------------------]\n\r");
		return;
	}

	if(!str_cmp(arg, "start")) {
		time_t tt, tn;
		switch(ch->rebel) {
			default: break;
			case SECT_ALLIANCE:  rd = ally_raid; tt = ally_raid->get_start(); tn = ally_raid->get_next();	break;
			case SECT_REBELLION: rd = rebel_raid; tt = rebel_raid->get_start(); tn = rebel_raid->get_next(); break;
		}

		if(rd->first_raider() != ch) {
			ch->Sendf("%s is in-charge of the raid!  You cannot start it!\n\r", rd->first_raider());
			return;
		}

		if(rd->raid_size() <=RAID_SIZE-1) {
			ch->Sendf("You need %d people to start a raid!", RAID_SIZE);
			return;
		}

		if(rd->get_next() > current_time) {
			ch->Sendf("You cannot start the raid yet!\n\r");
			return;
		}

		if(rd->get_start() != 0) {
			ch->Sendf("The raid is already underway!  KILL SOMETHING!\n\r");
			return;
		}

		rd->set_start();
		rd->set_end();

		// send everyone in the raid-group to the area's start room!
		rd->start_raid(ch->rebel ? 3255 : 9511 );
		return;
	}

	// add me to the group!
	if(!str_cmp(arg, "join")) {
		if(rd->get_start() != 0) {
			ch->Sendf("You cannot join a raid in progress!\n\r");
			return;
		}
		if(rd->in_raid(ch)) {
			ch->Sendf("You are already in the raid!\n\r");
			return;
		}
		ch->Sendf("You have been put into the raid list!\n\r");
		rd->add_list(ch);

		if(!IS_SET(ch->act, PLR_RAIDER))
			SET_BIT(ch->act, PLR_RAIDER);
		return;
	}

	// remove me from the group!
	if(!str_cmp(arg, "quit")) {
		if(!rd->in_raid(ch)) {
			ch->Sendf("You are not in a raid!\n\r");
			return;
		}
		if(is_fighting(ch)) {
			ch->Sendf("You cannot quit a raid while fighting in one!\n\r");
			return;
		}

		ch->Sendf("You have quit the raid group!\n\r");
		rd->remove_list(ch);
		rd->send_to_raid(ch, "I have quit raiding like a coward!");
		if(IS_SET(ch->act, PLR_RAIDER))
			REMOVE_BIT(ch->act, PLR_RAIDER);
		// send to their hometown!
		extract_char(ch, false);
		return;
	}

}