AI Tutorial
Bomb Squad

 

      Mad bombers are loose on the city streets. They're setting deadly explosives everywhere. Citizens are getting gibbed left and right. You are the best member of an elite bomb squad. You are the best because you are the last; the rest of the team has been blown up trying to stop the bombers.
      Suffice it to say, this will be a pretty decent tutorial. Maybe it won't be as dramatic as all that, but you'll enjoy it. Bomb Squad is a new deathmatch mode in which the bots drop bombs throughout the map. To defuse one, all you need do is touch it. But you must find it first.
      You see, when a bot throws an explosive, you have but one minute to find it and touch it. If you do so, you will score five frags. If you fail, the bomb will gib you wherever you stand and reset your frags to zero! And each bot will drop several explosives ...
      I like it. Let's rock. We will do the bomb creation subroutine. This needs to go before bot_walk(); you can put it right above it or at the top of TUTOR.QC, depending on your organizational preferences. Here it is:


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void() drop_da_bomb =
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
local entity bomb;

	bomb = spawn();
	bomb.owner = self;
	bomb.classname = "bomb";
	bomb.solid = SOLID_TRIGGER;
	bomb.touch = bomb_touch;
	bomb.think = bomb_explode;
	bomb.nextthink = time + 60;
	setmodel(bomb, "progs/grenade.mdl");
	setorigin(bomb, self.origin);
	bomb.movetype = MOVETYPE_NONE;
	bprint(self.netname);
	bprint(" drops a bomb!\n");
};

      This is a straight-forward entity-spawning routine in which we give it a model, a touch, and a think. You can change the model.
      See where the bomb will think the routine bomb_explode() in time plus 60 seconds? That of course is the explosion. Let's go ahead and create that now. Paste the following above that:

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void() bomb_explode =
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
local entity plr;

	plr = find(world, classname, "player");
	if (plr.health > 0)
		T_Damage(plr, self.owner, self, 1000);
	remove(self);
};

      Obviously, this is rather simple. We find the player and damage the hell out of him. Hee hee. Of course, you can change the lifespan of the bomb. That is, if you make its nextthink more than 60 seconds, the player will have more time to search for it, thus, the game will be easier. Or you can shorten the nextthink, making the game more challenging. Your call.
      Now for the touch routine. Put this above the previous:

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void() bomb_touch =
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{

	if (other.classname != "player")
		return;

	bprint(other.netname);
	bprint(" defuses a bomb!\n");
	other.frags = other.frags + 5;
	remove(self);
};

      Again, we are dealing with easy stuff here. Basically all we do is give him five frags and remove the bomb. That's the deal.
      One more change to TUTOR.QC. We need to tell the bot to set an explosive. We can put this anywhere we want. For simplicity, let's just shove it into bot_walk(). Paste this at the bottom of that routine:

	if (cvar("deathmatch") == 5)
		if (random() <= 0.02)
			if (random() >= 0.75)
				drop_da_bomb();

      To be only occassionally, we check a double random number to decide when to drop an explosive. You change either of these values; you can even delete one if you wish. You'll have to playtest the game for the frequency and challenge you prefer. And of course we want this new game merely to be an option, so you and the bot will only play the game when you set the console variable "deathmatch" to five.
      Speaking of that, here's an optional change for you. If dont't want the bots to fight each other during this game, scroll over to bot_look_for_bots(). After the bracket, add this:

	if (cvar("deathmatch") == 5)
		return FALSE;

      This way, it may seem like all the "mad bombers" are out to get you. But I really don't think it matters if they fight and kill each other. They are mad, after all.
      Save that file and open CLIENT.QC. Scroll down to PlayerPostThink() and add this at the very bottom:

	if (cvar("deathmatch") == 5 && time < 5)
		centerprint(self, "Now playing Bomb Squad\n");

      This is merely a message of the day. If you wish to change or move it, by all means, feel free. Don't let me run your life.
      Last change, Joe Bob. Drop down to ClientObituary(). We have to penalize the player for getting blown up. You'll see this stuff:

/*
===========
ClientObituary

called when a player dies
============
*/
void(entity targ, entity attacker) ClientObituary =
{
	local	float rnum;
	local	string deathstring, deathstring2;
	rnum = random();


      Right after that, add this:

	if (attacker.classname == "bomb" && cvar("deathmatch") == 5)
		{
		targ.frags = 0;
		attacker.owner.frags = attacker.owner.frags + 5;
		bprint(targ.netname);
		bprint(" gets blown up!\n");
		return;
		}

      Wait, what's that sound? Is it a cow? No, it's a fat lady singing! It's all over. Save, compile, enjoy. Remember, you can tweak the rules of this game if it's too hard or too easy. For added atmosphere, play this game on the Fragtown deathmatch map series. Wonderful city ambience.
      If you think that the player does all the work and that the bots have it easy in this game, that's because it's true. I made it that way on purpose. The secret to making any bot game enjoyable is choosing an objective that the bots are good at, and avoiding anything they're bad at. In other words, if the player dropped the bombs and the bots had to find them, the player would never lose. The bots would rarely get to them fast enough, if ever. We have to accept the fact that bots are limited. I try for fun first, and fairness later. If ever.

 


What We Learned

1. You can find any entity in the entity list
2. So we can kill a certain player if need be
3. Bots are good at standing their ground, not running the whole map
4. We must work with a bot's strengths and weaknesses
5. Go for fun first and equality later

 


     Author: Coffee
     Questions: coffee@planetquake.com
     AI chat on ICQ: 2716002