void bot_clearqueue(entity bot)
{
if(!bot.bot_cmdqueuebuf_allocated)
- error("clearqueue but no queue allocated");
+ return;
buf_del(bot.bot_cmdqueuebuf);
bot.bot_cmdqueuebuf_allocated = FALSE;
dprint("bot ", bot.netname, " queue cleared\n");
{
bot.bot_cmdqueuebuf = buf_create();
bot.bot_cmdqueuebuf_allocated = TRUE;
+ bot.bot_cmdqueuebuf_start = 0;
+ bot.bot_cmdqueuebuf_end = 0;
}
bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
+
+ // if the command was a "sound" command, precache the sound NOW
+ // this prevents lagging!
+ {
+ float sp;
+ string parm;
+ string cmdstr;
+
+ sp = strstrofs(cmdstring, " ", 0);
+ if(sp >= 0)
+ {
+ parm = substring(cmdstring, sp + 1, -1);
+ cmdstr = substring(cmdstring, 0, sp);
+ if(cmdstr == "sound")
+ {
+ // find the LAST word
+ for(;;)
+ {
+ sp = strstrofs(parm, " ", 0);
+ if(sp < 0)
+ break;
+ parm = substring(parm, sp + 1, -1);
+ }
+ precache_sound(parm);
+ }
+ }
+ }
+
bot.bot_cmdqueuebuf_end += 1;
}
#define MAX_BOT_PLACES 4
.float bot_places_count;
-.entity bot_places[MAX_BOT_PLACES]; FTEQCC_YOU_SUCK_THIS_IS_NOT_UNREFERENCED(bot_places);
-.string bot_placenames[MAX_BOT_PLACES]; FTEQCC_YOU_SUCK_THIS_IS_NOT_UNREFERENCED(bot_placenames);
+.entity bot_places[MAX_BOT_PLACES];
+.string bot_placenames[MAX_BOT_PLACES];
entity bot_getplace(string placename)
{
entity e;
#define BOT_CMD_BARRIER 20
#define BOT_CMD_CONSOLE 21
#define BOT_CMD_SOUND 22
-#define BOT_CMD_WHILE 23 // TODO: Not implemented yet
-#define BOT_CMD_WEND 24 // TODO: Not implemented yet
-#define BOT_CMD_CHASE 25 // TODO: Not implemented yet
+#define BOT_CMD_DEBUG_ASSERT_CANFIRE 23
+#define BOT_CMD_WHILE 24 // TODO: Not implemented yet
+#define BOT_CMD_WEND 25 // TODO: Not implemented yet
+#define BOT_CMD_CHASE 26 // TODO: Not implemented yet
-#define BOT_CMD_COUNTER 23 // Update this value if you add/remove a command
+#define BOT_CMD_COUNTER 24 // Update this value if you add/remove a command
// NOTE: Following commands should be implemented on the bot ai
// If a new command should be handled by the target ai(s) please declare it here
bot_cmd_string[BOT_CMD_SOUND] = "sound";
bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING;
+ bot_cmd_string[BOT_CMD_DEBUG_ASSERT_CANFIRE] = "debug_assert_canfire";
+ bot_cmd_parm_type[BOT_CMD_DEBUG_ASSERT_CANFIRE] = BOT_CMD_PARAMETER_FLOAT;
+
bot_cmds_initialized = TRUE;
}
// Returns first bot with matching name
entity find_bot_by_name(string name)
{
- local entity bot;
+ entity bot;
bot = findchainflags(flags, FL_CLIENT);
while (bot)
{
- if(clienttype(bot) == CLIENTTYPE_BOT)
+ if(IS_BOT_CLIENT(bot))
if(bot.netname==name)
return bot;
// Returns a bot by number on list
entity find_bot_by_number(float number)
{
- local entity bot;
- local float c;
+ entity bot;
+ float c = 0;
if(!number)
return world;
bot = findchainflags(flags, FL_CLIENT);
while (bot)
{
- if(clienttype(bot) == CLIENTTYPE_BOT)
+ if(IS_BOT_CLIENT(bot))
{
if(++c==number)
return bot;
float bot_decodecommand(string cmdstring)
{
- local float cmd_parm_type, i;
+ float cmd_parm_type, i;
float sp;
string parm;
void bot_cmdhelp(string scmd)
{
- local float i, ntype;
- local string stype;
+ float i, ntype;
+ string stype;
if(!bot_cmds_initialized)
bot_commands_init();
case BOT_CMD_SOUND:
print("play sound file at bot location");
break;
+ case BOT_CMD_DEBUG_ASSERT_CANFIRE:
+ print("verify the state of the weapon entity");
+ break;
default:
print("This command has no description yet.");
break;
void bot_list_commands()
{
- local float i;
- local string ptype;
+ float i;
+ string ptype;
if(!bot_cmds_initialized)
bot_commands_init();
print("List of all available commands:\n");
- print(" Command\t\t\t\tParameter Type\n");
+ print(" Command - Parameter Type\n");
for(i=1;i<BOT_CMD_COUNTER;++i)
{
ptype = "none";
break;
}
- print(strcat(" ",bot_cmd_string[i],"\t\t\t\t<",ptype,"> \n"));
+ print(strcat(" ",bot_cmd_string[i]," - <",ptype,"> \n"));
}
}
float bot_cmd_continue()
{
- self.bot_exec_status &~= BOT_EXEC_STATUS_PAUSED;
+ self.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED;
return CMD_STATUS_FINISHED;
}
{
if(time>=self.bot_cmd_wait_time)
{
- self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
+ self.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
return CMD_STATUS_FINISHED;
}
else
self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
return CMD_STATUS_EXECUTING;
}
- self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
+ self.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
return CMD_STATUS_FINISHED;
}
float bot_cmd_select_weapon()
{
- local float id;
+ float id;
id = bot_cmd.bot_cmd_parm_float;
float bot_cmd_if()
{
- local string expr, val_a, val_b;
- local float cmpofs;
+ string expr, val_a, val_b;
+ float cmpofs;
if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
{
float bot_cmd_else()
{
- self.bot_cmd_condition_status &~= CMD_CONDITION_TRUE_BLOCK;
+ self.bot_cmd_condition_status &= ~CMD_CONDITION_TRUE_BLOCK;
self.bot_cmd_condition_status |= CMD_CONDITION_FALSE_BLOCK;
return CMD_STATUS_FINISHED;
}
// Current direction
if(self.bot_cmd_aim_endtime)
{
- local float progress;
+ float progress;
progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
}
// New aiming direction
- local string parms;
- local float tokens, step;
+ string parms;
+ float tokens, step;
parms = bot_cmd.bot_cmd_parm_string;
tokens = tokenizebyseparator(parms, " ");
- if(tokens==2)
+ if(tokens<2||tokens>3)
+ return CMD_STATUS_ERROR;
+
+ step = (tokens == 3) ? stof(argv(2)) : 0;
+
+ if(step == 0)
{
self.v_angle_x -= stof(argv(1));
self.v_angle_y += stof(argv(0));
return CMD_STATUS_FINISHED;
}
- if(tokens<2||tokens>3)
- return CMD_STATUS_ERROR;
-
- step = stof(argv(2));
-
self.bot_cmd_aim_begin = self.v_angle;
self.bot_cmd_aim_end_x = self.v_angle_x - stof(argv(1));
return bot_cmd_aim();
}
- local entity e;
- local string parms;
- local vector v;
- local float tokens, step;
+ entity e;
+ string parms;
+ vector v;
+ float tokens, step;
parms = bot_cmd.bot_cmd_parm_string;
if(enabled)
{
self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
- self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
}
else
- self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
break;
case "backward":
if(enabled)
{
self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
- self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
}
else
- self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
break;
case "left":
if(enabled)
{
self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
- self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
}
else
- self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
break;
case "right":
if(enabled)
{
self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
- self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
}
else
- self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
break;
case "jump":
if(enabled)
self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
else
- self.bot_cmd_keys &~= BOT_CMD_KEY_JUMP;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_JUMP;
break;
case "crouch":
if(enabled)
self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
else
- self.bot_cmd_keys &~= BOT_CMD_KEY_CROUCH;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_CROUCH;
break;
case "attack1":
if(enabled)
self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
else
- self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK1;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK1;
break;
case "attack2":
if(enabled)
self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
else
- self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK2;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK2;
break;
case "use":
if(enabled)
self.bot_cmd_keys |= BOT_CMD_KEY_USE;
else
- self.bot_cmd_keys &~= BOT_CMD_KEY_USE;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_USE;
break;
case "hook":
if(enabled)
self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
else
- self.bot_cmd_keys &~= BOT_CMD_KEY_HOOK;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_HOOK;
break;
case "chat":
if(enabled)
self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
else
- self.bot_cmd_keys &~= BOT_CMD_KEY_CHAT;
+ self.bot_cmd_keys &= ~BOT_CMD_KEY_CHAT;
break;
default:
break;
float bot_cmd_presskey()
{
- local string key;
+ string key;
key = bot_cmd.bot_cmd_parm_string;
float bot_cmd_releasekey()
{
- local string key;
+ string key;
key = bot_cmd.bot_cmd_parm_string;
string f;
f = bot_cmd.bot_cmd_parm_string;
+ float n = tokenizebyseparator(f, " ");
+
+ string sample = f;
+ float chan = CH_WEAPON_B;
+ float vol = VOL_BASE;
+ float atten = ATTEN_MIN;
+
+ if(n >= 1)
+ sample = argv(n - 1);
+ if(n >= 2)
+ chan = stof(argv(0));
+ if(n >= 3)
+ vol = stof(argv(1));
+ if(n >= 4)
+ atten = stof(argv(2));
+
precache_sound(f);
- sound(self, CH_WEAPON_B, f, VOL_BASE, ATTN_MIN);
+ sound(self, chan, sample, vol, atten);
+
+ return CMD_STATUS_FINISHED;
+}
+
+.entity tuba_note;
+float bot_cmd_debug_assert_canfire()
+{
+ float f;
+ f = bot_cmd.bot_cmd_parm_float;
+
+ if(self.weaponentity.state != WS_READY)
+ {
+ if(f)
+ {
+ self.colormod = '0 8 8';
+ print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by weaponentity state\n");
+ }
+ }
+ else if(ATTACK_FINISHED(self) > time)
+ {
+ if(f)
+ {
+ self.colormod = '8 0 8';
+ print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(self) - time), " seconds left)\n");
+ }
+ }
+ else if(self.tuba_note)
+ {
+ if(f)
+ {
+ self.colormod = '8 0 0';
+ print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, bot still has an active tuba note\n");
+ }
+ }
+ else
+ {
+ if(!f)
+ {
+ self.colormod = '8 8 0';
+ print("Bot ", self.netname, " using ", self.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(self) - time), " seconds left\n");
+ }
+ }
return CMD_STATUS_FINISHED;
}
FOR_EACH_CLIENT(cl) if(cl.isbot)
{
- if(cl.bot_cmdqueuebuf_allocated)
- bot_clearqueue(cl);
+ cl.bot_cmd_execution_index = 0;
+ bot_clearqueue(cl);
// also, cancel all barriers
cl.bot_barrier = 0;
for(i = 0; i < cl.bot_places_count; ++i)
// NOTE: Of course you need to include your commands here too :)
float bot_execute_commands_once()
{
- local float status, ispressingkey;
-
- if(self.deadflag!=DEAD_NO)
- return 0;
+ float status, ispressingkey;
// Find command
bot_setcurrentcommand();
+ // if we have no bot command, better return
+ // old logic kept pressing previously pressed keys, but that has problems
+ // (namely, it means you cannot make a bot "normal" ever again)
+ // to keep a bot walking for a while, use the "wait" bot command
+ if(bot_cmd == world)
+ return FALSE;
+
// Ignore all commands except continue when the bot is paused
if(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
// Keep pressing keys raised by the "presskey" command
ispressingkey = !!bot_presskeys();
- if(bot_cmd==world)
- return ispressingkey;
-
// Handle conditions
- if not(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE)
+ if (!(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE))
if(self.bot_cmd_condition_status & CMD_CONDITION_TRUE && self.bot_cmd_condition_status & CMD_CONDITION_FALSE_BLOCK)
{
bot_command_executed(TRUE);
case BOT_CMD_SOUND:
status = bot_cmd_sound();
break;
+ case BOT_CMD_DEBUG_ASSERT_CANFIRE:
+ status = bot_cmd_debug_assert_canfire();
+ break;
default:
print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
return 0;
{
if(autocvar_g_debug_bot_commands)
{
- local string parms;
+ string parms;
switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
{