This is the tutorial which has been bugging me for I while. I expect several of you have tried something similar to this, and had no luck. The idea seems simple. Search around the area for a secret door, and make it your enemy. Easy, right?
Wrong. The main problem is this. The ID routine visible() won't spot secret doors. At first, I simply couldn't understand why, until I physicaly edited a BSP file, and discovered the horible truth ... secret doors don't have a .origin point set! .origin points are required for both visible() and bot_aim_at_enemy(). After a while, I came up with a solution. Instead of using an origin point, tell it to use a point halfway between the top right and bottom left corners of the door. Easy to say, not so easy to implement.
Ok, lets begin coding on this monster. Open up tutor.qc, and scroll down to bot_aim_at_enemy. It may look like this, but it may not:
// ------------------------------------- vector() bot_aim_at_enemy = // ------------------------------------- { return normalize(self.enemy.origin - self.origin); };Now at the begining of that routine, whatever it now looks like, add this:
if (self.enemy.classname == "door") return normalize((self.enemy.absmin + (self.enemy.absmax - self.enemy.absmin) * 0.5) - self.origin);That is short but sweet. That's my code for making it aim at the centre of an object, not it's origin. Next, go up to bot_walk, and above it paste these two routines:
// ------------------------------------------------ float (entity targ) wall_visible = // ------------------------------------------------ { local vector spot1, spot2; spot1 = self.origin + self.view_ofs; spot2 = targ.absmin + ((targ.absmax - targ.absmin) * 0.5); traceline (spot1, spot2, TRUE, self); // see through other monsters if (trace_inopen && trace_inwater) return FALSE; // sight line crossed contents if ((trace_fraction == 1) || (trace_ent == targ)) return TRUE; return FALSE; }; // ------------------------------------------------ void() bot_find_secret = // ------------------------------------------------ { local entity item; // he gives up on that item and marks it to avoid it for a while if (self.goalentity != world) return; // checks a radius around him for items item = findradius(self.origin, 200); while(item) { if ((item.health) && (item.classname == "door") && (wall_visible(item))) { self.search_time = time + 2; self.goalentity = item; self.enemy = item; self.th_run(); return; } item = item.chain; } return; };The first routine is my version of visible, which uses the centre of the door as the target point instead of the origin point.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ void() bot_walk = // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ { // this is his main AI routine, where he will look for items and enemies if (!(self.flags & FL_ONGROUND)) return; bot_look_for_bots(); bot_look_for_players(); bot_search_for_items();After this stick in a line saying:
bot_find_secret();Onwards, onwards to the final step (I hope). Go down to bot_run and at the very end of it, add the following two lines.
if ((self.enemy.classname == "door") && (wall_visible(self.enemy)) && (time > self.attack_finished)) self.th_missile();Phew! I didn't really explain much of this tutorial, but it should be kinda obvious. Basically, everywhere that visible() is used, I've added a bit saying "if my enemy is a door, use the wall_visible code to determine if I can see it."