X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fbot%2Fdefault%2Fhavocbot%2Fhavocbot.qc;h=e98bd117c9a9fa9222f736a47ea9fd8837ac3524;hb=2f3c32bb6372a436aad4a7e23fea91378d2ce740;hp=42f51af8c7f279c3e1e510fd2ff3c1e56dd469d0;hpb=53911d2ce2724f2da23fb6806e491ce795ae4326;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 42f51af8c..e98bd117c 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qc +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -44,7 +44,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 +136,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,7 +191,7 @@ 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 @@ -212,7 +223,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 +275,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 +317,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; @@ -401,29 +412,44 @@ 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; } } #endif } -.entity goalcurrent_prev; -.float goalcurrent_distance; -.float goalcurrent_distance_time; +// return true when bot isn't getting closer to the current goal +bool havocbot_checkgoaldistance(entity this, vector gco) +{ + float curr_dist = vlen(this.origin - gco); + if(curr_dist > this.goalcurrent_distance) + { + if(!this.goalcurrent_distance_time) + this.goalcurrent_distance_time = time; + else if (time - this.goalcurrent_distance_time > 0.5) + return true; + } + else + { + // reduce it a little bit so it works even with very small approaches to the goal + this.goalcurrent_distance = max(20, curr_dist - 15); + this.goalcurrent_distance_time = 0; + } + return false; +} + void havocbot_movetogoal(entity this) { vector destorg; vector diff; vector dir; vector flatdir; - vector m1; - vector m2; vector evadeobstacle; vector evadelava; float maxspeed; @@ -432,7 +458,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 @@ -476,7 +502,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 @@ -498,8 +524,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; } @@ -507,17 +533,25 @@ void havocbot_movetogoal(entity this) // Handling of jump pads if(this.jumppadcount) { - // If got stuck on the jump pad try to reach the farthest visible waypoint - // but with some randomness so it can try out different paths - if(this.aistatus & AI_STATUS_OUT_JUMPPAD) + if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT) { - if(fabs(this.velocity.z)<50) + this.aistatus |= AI_STATUS_OUT_JUMPPAD; + navigation_poptouchedgoals(this); + return; + } + else if(this.aistatus & AI_STATUS_OUT_JUMPPAD) + { + // If got stuck on the jump pad try to reach the farthest visible waypoint + // but with some randomness so it can try out different paths + if(!this.goalcurrent) { entity newgoal = NULL; - if (vdist(this.origin - this.goalcurrent.origin, <, 150)) - this.aistatus &= ~AI_STATUS_OUT_JUMPPAD; - else IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 1000), + IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 1000), { + if(it.wpflags & WAYPOINTFLAG_TELEPORT) + if(it.origin.z < this.origin.z - 100 && vdist(vec2(it.origin - this.origin), <, 100)) + continue; + traceline(this.origin + this.view_ofs, ((it.absmin + it.absmax) * 0.5), true, this); if(trace_fraction < 1) @@ -539,11 +573,22 @@ void havocbot_movetogoal(entity this) } } else - return; + { + gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5; + if (this.origin.z > gco.z && vdist(vec2(this.velocity), <, autocvar_sv_maxspeed)) + this.aistatus &= ~AI_STATUS_OUT_JUMPPAD; + else if(havocbot_checkgoaldistance(this, gco)) + { + navigation_clearroute(this); + this.bot_strategytime = 0; + } + else + return; + } } else { - if(time - this.lastteleporttime > 0.3 && this.velocity.z > 0) + if(time - this.lastteleporttime > 0.2 && this.velocity.z > 0) { vector velxy = this.velocity; velxy_z = 0; if(vdist(velxy, <, autocvar_sv_maxspeed * 0.2)) @@ -597,8 +642,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); } @@ -621,7 +666,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) { @@ -646,7 +691,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; } } @@ -662,9 +707,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 @@ -716,12 +761,19 @@ 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(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); dir = normalize(diff); @@ -731,8 +783,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'; @@ -742,18 +794,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 { @@ -782,33 +836,12 @@ void havocbot_movetogoal(entity this) } // if bot for some reason doesn't get close to the current goal find another one - if(!IS_PLAYER(this.goalcurrent) && !(this.goalcurrent.bot_pickup_respawning && this.goalcurrent_distance < 50)) + if(!this.jumppadcount && !IS_PLAYER(this.goalcurrent) && !(this.goalcurrent.bot_pickup_respawning && this.goalcurrent_distance < 50)) + if(havocbot_checkgoaldistance(this, gco)) { - float curr_dist = vlen(this.origin - this.goalcurrent.origin); - if(this.goalcurrent != this.goalcurrent_prev) - { - this.goalcurrent_prev = this.goalcurrent; - this.goalcurrent_distance = curr_dist; - this.goalcurrent_distance_time = 0; - } - else if(curr_dist > this.goalcurrent_distance) - { - if(!this.goalcurrent_distance_time) - this.goalcurrent_distance_time = time; - else if (time - this.goalcurrent_distance_time > 0.5) - { - this.goalcurrent_prev = NULL; - navigation_clearroute(this); - this.bot_strategytime = 0; - return; - } - } - else - { - // reduce it a little bit so it works even with very small approaches to the goal - this.goalcurrent_distance = max(20, curr_dist - 15); - this.goalcurrent_distance_time = 0; - } + navigation_clearroute(this); + this.bot_strategytime = 0; + return; } // Check for water/slime/lava and dangerous edges @@ -819,7 +852,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)) @@ -833,16 +865,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)) @@ -871,8 +894,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; } @@ -912,17 +934,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); @@ -1202,10 +1224,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) @@ -1263,7 +1285,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);