//////////////////////////////////////////////////////////////////////////////////////
// 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;
}
}