X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fbot%2Fdefault%2Fhavocbot%2Fhavocbot.qc;h=306f3a05e835697394474c16febcc0f9dcb01164;hb=fb5876e8defeb6af31e62002be26bdfa2beb56a7;hp=c533071b029871e9b4c1436b6a0f406061cd005e;hpb=c879eb771ea22f97a56cc1c1aaf5487c40cf9e3d;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc index c533071b0..306f3a05e 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qc +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -1,5 +1,7 @@ #include "havocbot.qh" +#include +#include #include "../cvars.qh" #include "../aim.qh" @@ -44,7 +46,6 @@ void havocbot_ai(entity this) this.havocbot_role(this); // little too far down the rabbit hole } - // TODO: tracewalk() should take care of this job (better path finding under water) // if we don't have a goal and we're under water look for a waypoint near the "shore" and push it if(!(IS_DEAD(this) || STAT(FROZEN, this))) if(!this.goalcurrent) @@ -137,11 +138,23 @@ void havocbot_ai(entity this) this.aistatus |= AI_STATUS_ROAMING; this.aistatus &= ~AI_STATUS_ATTACKING; - vector now,v,next;//,heading; + vector v, now, next; float aimdistance,skillblend,distanceblend,blend; - next = now = ( (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5) - (this.origin + this.view_ofs); + + SET_DESTCOORDS(this.goalcurrent, this.origin, v); + if(this.goalcurrent.wpisbox) + { + // avoid a glitch when bot is teleported but teleport waypoint isn't removed yet + if(this.goalstack02 && this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT + && this.lastteleporttime > 0 && time - this.lastteleporttime < 0.15) + v = (this.goalstack02.absmin + this.goalstack02.absmax) * 0.5; + // aim to teleport origin if bot is inside teleport waypoint but hasn't touched the real teleport yet + else if(boxesoverlap(this.goalcurrent.absmin, this.goalcurrent.absmax, this.origin, this.origin)) + v = this.goalcurrent.origin; + } + next = now = v - (this.origin + this.view_ofs); aimdistance = vlen(now); - //heading = this.velocity; + //dprint(this.goalstack01.classname,etos(this.goalstack01),"\n"); if( this.goalstack01 != this && this.goalstack01 && !wasfreed(this.goalstack01) && ((this.aistatus & AI_STATUS_RUNNING) == 0) && @@ -180,20 +193,20 @@ void havocbot_ai(entity this) // we are currently holding a weapon that's not fully loaded, reload it if(skill >= 2) // bots can only reload the held weapon on purpose past this skill if(this.(weaponentity).clip_load < this.(weaponentity).clip_size) - this.impulse = IMP_weapon_reload.impulse; // not sure if this is done right + CS(this).impulse = IMP_weapon_reload.impulse; // not sure if this is done right // if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next // the code above executes next frame, starting the reloading then if(skill >= 5) // bots can only look for unloaded weapons past this skill if(this.(weaponentity).clip_load >= 0) // only if we're not reloading a weapon already { - FOREACH(Weapons, it != WEP_Null, LAMBDA( + FOREACH(Weapons, it != WEP_Null, { if((this.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.(weaponentity).weapon_load[it.m_id] < it.reloading_ammo)) { this.(weaponentity).m_switchweapon = it; break; } - )); + }); } } } @@ -212,7 +225,7 @@ void havocbot_keyboard_movement(entity this, vector destorg) + 0.05 / max(1, sk + this.havocbot_keyboardskill) + random() * 0.025 / max(0.00025, skill + this.havocbot_keyboardskill) , time); - keyboard = this.movement / autocvar_sv_maxspeed; + keyboard = CS(this).movement / autocvar_sv_maxspeed; float trigger = autocvar_bot_ai_keyboard_threshold; float trigger1 = -trigger; @@ -264,8 +277,8 @@ void havocbot_keyboard_movement(entity this, vector destorg) keyboard = this.havocbot_keyboard; float blend = bound(0, vlen(destorg - this.origin) / autocvar_bot_ai_keyboard_distance, 1); // When getting close move with 360 degree - //dprint("movement ", vtos(this.movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n"); - this.movement = this.movement + (keyboard - this.movement) * blend; + //dprint("movement ", vtos(CS(this).movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n"); + CS(this).movement = CS(this).movement + (keyboard - CS(this).movement) * blend; } void havocbot_bunnyhop(entity this, vector dir) @@ -306,12 +319,12 @@ void havocbot_bunnyhop(entity this, vector dir) this.bot_timelastseengoal = 0; } - gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5; + SET_DESTCOORDS(this.goalcurrent, this.origin, gco); bunnyhopdistance = vlen(this.origin - gco); // Run only to visible goals if(IS_ONGROUND(this)) - if(vlen(this.velocity - eZ * this.velocity.z) >= autocvar_sv_maxspeed) // if -really- running + if(vdist(vec2(this.velocity), >=, autocvar_sv_maxspeed)) // if -really- running if(checkpvs(this.origin + this.view_ofs, this.goalcurrent)) { this.bot_lastseengoal = this.goalcurrent; @@ -363,7 +376,7 @@ void havocbot_bunnyhop(entity this, vector dir) if(checkdistance) { this.aistatus &= ~AI_STATUS_RUNNING; - // increase stop distance in case the goal is on a slope or a lower platform + // increase stop distance in case the goal is on a slope or a lower platform if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance + (this.origin.z - gco.z)) PHYS_INPUT_BUTTON_JUMP(this) = true; } @@ -401,12 +414,12 @@ void havocbot_bunnyhop(entity this, vector dir) while (deviation.y > 180) deviation.y = deviation.y - 360; if(fabs(deviation.y)>10) - this.movement_x = 0; + CS(this).movement_x = 0; if(deviation.y>10) - this.movement_y = maxspeed * -1; + CS(this).movement_y = maxspeed * -1; else if(deviation.y<10) - this.movement_y = maxspeed; + CS(this).movement_y = maxspeed; } } @@ -439,8 +452,6 @@ void havocbot_movetogoal(entity this) vector diff; vector dir; vector flatdir; - vector m1; - vector m2; vector evadeobstacle; vector evadelava; float maxspeed; @@ -449,7 +460,7 @@ void havocbot_movetogoal(entity this) vector dodge; //if (this.goalentity) // te_lightning2(this, this.origin, (this.goalentity.absmin + this.goalentity.absmax) * 0.5); - this.movement = '0 0 0'; + CS(this).movement = '0 0 0'; maxspeed = autocvar_sv_maxspeed; // Jetpack navigation @@ -493,7 +504,7 @@ void havocbot_movetogoal(entity this) // Brake if(fabs(this.velocity.x)>maxspeed*0.3) { - this.movement_x = dir * v_forward * -maxspeed; + CS(this).movement_x = dir * v_forward * -maxspeed; return; } // Switch to normal mode @@ -515,8 +526,8 @@ void havocbot_movetogoal(entity this) PHYS_INPUT_BUTTON_HOOK(this) = true; if(this.navigation_jetpack_point.z - STAT(PL_MAX, this).z + STAT(PL_MIN, this).z < this.origin.z) { - this.movement_x = dir * v_forward * maxspeed; - this.movement_y = dir * v_right * maxspeed; + CS(this).movement_x = dir * v_forward * maxspeed; + CS(this).movement_y = dir * v_right * maxspeed; } return; } @@ -633,8 +644,8 @@ void havocbot_movetogoal(entity this) tracebox(this.origin, this.mins, this.maxs, this.origin + (dir * maxspeed * 3), MOVE_NOMONSTERS, this); if(trace_fraction==1) { - this.movement_x = dir * v_forward * maxspeed; - this.movement_y = dir * v_right * maxspeed; + CS(this).movement_x = dir * v_forward * maxspeed; + CS(this).movement_y = dir * v_right * maxspeed; if (skill < 10) havocbot_keyboard_movement(this, this.origin + dir * 100); } @@ -657,7 +668,7 @@ void havocbot_movetogoal(entity this) if(client_hasweapon(this, WEP_DEVASTATOR, weaponentity, true, false)) { - this.movement_x = maxspeed; + CS(this).movement_x = maxspeed; if(this.rocketjumptime) { @@ -682,7 +693,7 @@ void havocbot_movetogoal(entity this) { // If there is no goal try to move forward if(this.goalcurrent==NULL) - this.movement_x = maxspeed; + CS(this).movement_x = maxspeed; } } @@ -698,9 +709,9 @@ void havocbot_movetogoal(entity this) else PHYS_INPUT_BUTTON_JUMP(this) = false; makevectors(this.v_angle.y * '0 1 0'); - this.movement_x = dir * v_forward * maxspeed; - this.movement_y = dir * v_right * maxspeed; - this.movement_z = dir * v_up * maxspeed; + CS(this).movement_x = dir * v_forward * maxspeed; + CS(this).movement_y = dir * v_right * maxspeed; + CS(this).movement_z = dir * v_up * maxspeed; } // if there is nowhere to go, exit @@ -752,17 +763,18 @@ void havocbot_movetogoal(entity this) if(autocvar_bot_debug_goalstack) debuggoalstack(this); - m1 = this.goalcurrent.origin + this.goalcurrent.mins; - m2 = this.goalcurrent.origin + this.goalcurrent.maxs; - destorg = this.origin; - destorg.x = bound(m1_x, destorg.x, m2_x); - destorg.y = bound(m1_y, destorg.y, m2_y); - destorg.z = bound(m1_z, destorg.z, m2_z); + bool bunnyhop_forbidden = false;; + SET_DESTCOORDS(this.goalcurrent, this.origin, destorg); // in case bot ends up inside the teleport waypoint without touching // the teleport itself, head to the teleport origin - if(destorg == this.origin) + if(this.goalcurrent.wpisbox && boxesoverlap(this.goalcurrent.absmin, this.goalcurrent.absmax, this.origin + eZ * this.mins.z, this.origin + eZ * this.maxs.z)) + { + bunnyhop_forbidden = true; destorg = this.goalcurrent.origin; + if(destorg.z > this.origin.z) + PHYS_INPUT_BUTTON_JUMP(this) = true; + } diff = destorg - this.origin; //dist = vlen(diff); @@ -773,8 +785,8 @@ void havocbot_movetogoal(entity this) //if (this.bot_dodgevector_time < time) { - // this.bot_dodgevector_time = time + cvar("bot_ai_dodgeupdateinterval"); - // this.bot_dodgevector_jumpbutton = 1; + //this.bot_dodgevector_time = time + cvar("bot_ai_dodgeupdateinterval"); + //this.bot_dodgevector_jumpbutton = 1; evadeobstacle = '0 0 0'; evadelava = '0 0 0'; @@ -784,18 +796,20 @@ void havocbot_movetogoal(entity this) { if(this.waterlevel>WATERLEVEL_SWIMMING) { - // flatdir_z = 1; - this.aistatus |= AI_STATUS_OUT_WATER; + if(!this.goalcurrent) + this.aistatus |= AI_STATUS_OUT_WATER; + else if(gco.z > this.origin.z) + PHYS_INPUT_BUTTON_JUMP(this) = true; } else { + dir = flatdir; if(this.velocity.z >= 0 && !(this.watertype == CONTENT_WATER && gco.z < this.origin.z) && ( !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER) || this.aistatus & AI_STATUS_OUT_WATER)) PHYS_INPUT_BUTTON_JUMP(this) = true; else PHYS_INPUT_BUTTON_JUMP(this) = false; } - dir = normalize(flatdir); } else { @@ -840,7 +854,6 @@ void havocbot_movetogoal(entity this) traceline(this.origin + this.view_ofs, dst_ahead, true, NULL); bool unreachable = false; - bool ignorehazards = false; s = CONTENT_SOLID; if(trace_fraction == 1 && this.jumppadcount == 0 && !this.goalcurrent.wphardwired ) if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || (this.aistatus & AI_STATUS_ROAMING) || PHYS_INPUT_BUTTON_JUMP(this)) @@ -854,16 +867,7 @@ void havocbot_movetogoal(entity this) s = pointcontents(trace_endpos + '0 0 1'); if (s != CONTENT_SOLID) if (s == CONTENT_LAVA || s == CONTENT_SLIME) - { evadelava = normalize(this.velocity) * -1; - if(this.waterlevel >= WATERLEVEL_WETFEET && (this.watertype == CONTENT_LAVA || this.watertype == CONTENT_SLIME)) - ignorehazards = true; - } - else if (s == CONTENT_WATER) - { - if(this.waterlevel >= WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER) - ignorehazards = true; - } else if (s == CONTENT_SKY) evadeobstacle = normalize(this.velocity) * -1; else if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos)) @@ -892,8 +896,7 @@ void havocbot_movetogoal(entity this) if(evadeobstacle || evadelava || (s == CONTENT_WATER)) { - if(!ignorehazards) - this.aistatus |= AI_STATUS_DANGER_AHEAD; + this.aistatus |= AI_STATUS_DANGER_AHEAD; if(IS_PLAYER(this.goalcurrent)) unreachable = true; } @@ -933,17 +936,17 @@ void havocbot_movetogoal(entity this) //dir = this.bot_dodgevector; //if (this.bot_dodgevector_jumpbutton) // PHYS_INPUT_BUTTON_JUMP(this) = true; - this.movement_x = dir * v_forward * maxspeed; - this.movement_y = dir * v_right * maxspeed; - this.movement_z = dir * v_up * maxspeed; + CS(this).movement_x = dir * v_forward * maxspeed; + CS(this).movement_y = dir * v_right * maxspeed; + CS(this).movement_z = dir * v_up * maxspeed; // Emulate keyboard interface if (skill < 10) havocbot_keyboard_movement(this, destorg); // Bunnyhop! -// if(this.aistatus & AI_STATUS_ROAMING) - if(this.goalcurrent) + //if(this.aistatus & AI_STATUS_ROAMING) + if(!bunnyhop_forbidden && this.goalcurrent) if(skill+this.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset) havocbot_bunnyhop(this, dir); @@ -1099,10 +1102,10 @@ float havocbot_chooseweapon_checkreload(entity this, .entity weaponentity, int n // if this weapon is scheduled for reloading, don't switch to it during combat if (this.(weaponentity).weapon_load[new_weapon] < 0) { - FOREACH(Weapons, it != WEP_Null, LAMBDA( + FOREACH(Weapons, it != WEP_Null, { if(it.wr_checkammo1(it, this, weaponentity) + it.wr_checkammo2(it, this, weaponentity)) return true; // other weapon available - )); + }); } return false; @@ -1124,13 +1127,13 @@ void havocbot_chooseweapon(entity this, .entity weaponentity) { // If no weapon was chosen get the first available weapon if(this.(weaponentity).m_weapon==WEP_Null) - FOREACH(Weapons, it != WEP_Null, LAMBDA( + FOREACH(Weapons, it != WEP_Null, { if(client_hasweapon(this, it, weaponentity, true, false)) { this.(weaponentity).m_switchweapon = it; return; } - )); + }); return; } @@ -1223,10 +1226,10 @@ void havocbot_aim(entity this) vector enemyvel = this.enemy.velocity; if (!this.enemy.waterlevel) enemyvel.z = 0; - lag_additem(this, time + this.ping, 0, 0, this.enemy, this.origin, myvel, (this.enemy.absmin + this.enemy.absmax) * 0.5, enemyvel); + lag_additem(this, time + CS(this).ping, 0, 0, this.enemy, this.origin, myvel, (this.enemy.absmin + this.enemy.absmax) * 0.5, enemyvel); } else - lag_additem(this, time + this.ping, 0, 0, NULL, this.origin, myvel, ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5, '0 0 0'); + lag_additem(this, time + CS(this).ping, 0, 0, NULL, this.origin, myvel, ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5, '0 0 0'); } bool havocbot_moveto_refresh_route(entity this) @@ -1284,7 +1287,9 @@ float havocbot_moveto(entity this, vector pos) debuggoalstack(this); // Heading - vector dir = ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - (this.origin + this.view_ofs); + vector dir; + SET_DESTCOORDS(this.goalcurrent, this.origin, dir); + dir = dir - (this.origin + this.view_ofs); dir.z = 0; bot_aimdir(this, dir, -1);