diff --git a/src/k_bot.c b/src/k_bot.c index 73139e6b6..caf6aa55f 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -710,7 +710,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) if (P_TraceBotTraversal(player->mo, wp->mobj) == false) { // If we can't get a direct path to this waypoint, predict less. - distanceleft -= disttonext; + distanceleft /= 2; radreduce = FRACUNIT >> 1; } @@ -745,6 +745,25 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) predict->y += P_ReturnThrustY(NULL, angletonext, min(disttonext, distanceleft) * FRACUNIT); } + if (player->mo->standingslope != NULL) + { + const pslope_t *slope = player->mo->standingslope; + + if (!(slope->flags & SL_NOPHYSICS) && abs(slope->zdelta) >= FRACUNIT/21) + { + // Displace the prediction to go against the slope physics. + angle_t angle = slope->xydirection; + + if (P_MobjFlip(player->mo) * slope->zdelta < 0) + { + angle ^= ANGLE_180; + } + + predict->x -= P_ReturnThrustX(NULL, angle, startDist * abs(slope->zdelta)); + predict->y -= P_ReturnThrustY(NULL, angle, startDist * abs(slope->zdelta)); + } + } + ps_bots[player - players].prediction += I_GetPreciseTime() - time; return predict; } @@ -875,7 +894,7 @@ static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player) debugMobj->frame |= FF_TRANS20|FF_FULLBRIGHT; debugMobj->color = SKINCOLOR_ORANGE; - debugMobj->scale *= 2; + P_SetScale(debugMobj, debugMobj->destscale * 2); debugMobj->tics = 2; @@ -902,7 +921,7 @@ static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player) radiusMobj->frame |= FF_TRANS20|FF_FULLBRIGHT; radiusMobj->color = SKINCOLOR_YELLOW; - radiusMobj->scale /= 2; + P_SetScale(debugMobj, debugMobj->destscale / 2); radiusMobj->tics = 2; } @@ -1452,7 +1471,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } - if (spindash == 0) + if (spindash == 0 && player->exiting == 0) { // Don't pointlessly try to use rings/sneakers while charging a spindash. // TODO: Allowing projectile items like orbinaut while e-braking would be nice, maybe just pass in the spindash variable? diff --git a/src/k_botitem.c b/src/k_botitem.c index bb90ef736..6c8b9ece4 100644 --- a/src/k_botitem.c +++ b/src/k_botitem.c @@ -486,6 +486,12 @@ static void K_BotItemGenericOrbitShield(player_t *player, ticcmd_t *cmd) --------------------------------------------------*/ static void K_BotItemSneaker(player_t *player, ticcmd_t *cmd) { + if (P_IsObjectOnGround(player->mo) == false) + { + // Don't use while mid-air. + return; + } + if ((player->offroad && K_ApplyOffroad(player)) // Stuck in offroad, use it NOW || K_GetWaypointIsShortcut(player->nextwaypoint) == true // Going toward a shortcut! || player->speed < K_GetKartSpeed(player, false, true) / 2 // Being slowed down too much @@ -518,6 +524,12 @@ static void K_BotItemSneaker(player_t *player, ticcmd_t *cmd) --------------------------------------------------*/ static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd) { + if (P_IsObjectOnGround(player->mo) == false) + { + // Don't use while mid-air. + return; + } + if (player->botvars.itemconfirm > TICRATE) { if (player->sneakertimer == 0 && K_ItemButtonWasDown(player) == false) @@ -892,9 +904,9 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) } /*-------------------------------------------------- - static void K_BotItemDropTarget(player_t *player, ticcmd_t *cmd) + static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) - Item usage for Drop Target throwing. + Item usage for Ballhog throwing. Input Arguments:- player - Bot to do this for. @@ -903,14 +915,15 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) Return:- None --------------------------------------------------*/ -static void K_BotItemDropTarget(player_t *player, ticcmd_t *cmd) +static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) { const fixed_t topspeed = K_GetKartSpeed(player, false, true); - fixed_t radius = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + fixed_t radius = FixedMul(2560 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); SINT8 throwdir = -1; boolean tryLookback = false; UINT8 snipeMul = 2; player_t *target = NULL; + boolean hold = false; if (player->speed > topspeed) { @@ -918,8 +931,6 @@ static void K_BotItemDropTarget(player_t *player, ticcmd_t *cmd) snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!! } - player->botvars.itemconfirm++; - target = K_PlayerInCone(player, radius, 15, false); if (target != NULL) { @@ -943,7 +954,89 @@ static void K_BotItemDropTarget(player_t *player, ticcmd_t *cmd) cmd->buttons |= BT_LOOKBACK; } - if (player->botvars.itemconfirm > 25*TICRATE) + if (target != NULL) + { + // Charge up! + hold = true; + } + else + { + // If we lose sight of the target, then we'll just + // let go and it'll do a partial-blast. + + // If we've been waiting for too long though, then + // we'll go for the full charge :) + player->botvars.itemconfirm++; + hold = (player->botvars.itemconfirm > 10*TICRATE); + } + + if (hold == true) + { + cmd->throwdir = KART_FULLTURN * throwdir; + cmd->buttons |= BT_ATTACK; + } +} + +/*-------------------------------------------------- + static void K_BotItemDropTarget(player_t *player, ticcmd_t *cmd, INT16 turnamt) + + Item usage for Drop Target throwing. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + turnamt - How hard they currently are turning. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemDropTarget(player_t *player, ticcmd_t *cmd, INT16 turnamt) +{ + const fixed_t topspeed = K_GetKartSpeed(player, false, true); + fixed_t radius = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + SINT8 throwdir = -1; + boolean tryLookback = false; + UINT8 snipeMul = 2; + player_t *target = NULL; + + if (player->speed > topspeed) + { + radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!! + } + + player->botvars.itemconfirm++; + + if (abs(turnamt) >= KART_FULLTURN/2) + { + player->botvars.itemconfirm += player->botvars.difficulty / 2; + throwdir = -1; + } + + target = K_PlayerInCone(player, radius, 15, false); + if (target != NULL) + { + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * snipeMul); + throwdir = 1; + } + else + { + target = K_PlayerInCone(player, radius, 15, true); + + if (target != NULL) + { + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); + throwdir = -1; + tryLookback = true; + } + } + + if (tryLookback == true && throwdir == -1) + { + cmd->buttons |= BT_LOOKBACK; + } + + if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) { K_BotGenericPressItem(player, cmd, throwdir); } @@ -1166,6 +1259,92 @@ static void K_BotItemFlame(player_t *player, ticcmd_t *cmd) } } +/*-------------------------------------------------- + static void K_BotItemGardenTopDeploy(player_t *player, ticcmd_t *cmd) + + Item usage for deploying the Garden Top. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemGardenTopDeploy(player_t *player, ticcmd_t *cmd) +{ + //if (player->curshield != KSHIELD_TOP) + if (player->botvars.itemconfirm++ > 2*TICRATE) + { + K_BotGenericPressItem(player, cmd, 0); + } +} + +/*-------------------------------------------------- + static void K_BotItemGardenTop(player_t *player, ticcmd_t *cmd, INT16 turnamt) + + Item usage for Garden Top movement. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + turnamt - How hard they currently are turning. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemGardenTop(player_t *player, ticcmd_t *cmd, INT16 turnamt) +{ + const fixed_t topspeed = K_GetKartSpeed(player, false, true); + fixed_t radius = FixedMul(2560 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + SINT8 throwdir = -1; + UINT8 snipeMul = 1; + player_t *target = NULL; + + if (player->speed > topspeed) + { + radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + snipeMul = 2; // Confirm faster when you'll throw it with a bunch of extra speed!! + } + + player->botvars.itemconfirm++; + + target = K_PlayerInCone(player, radius, 15, false); + if (target != NULL) + { + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * snipeMul); + throwdir = 1; + } + + if (player->topdriftheld > 0) + { + // Grinding in place. + // Wait until we're mostly done turning. + // Cancel early if we hit max thrust speed. + if ((abs(turnamt) >= KART_FULLTURN/8) + && (player->topdriftheld <= GARDENTOP_MAXGRINDTIME)) + { + cmd->buttons |= BT_DRIFT; + } + } + else + { + const angle_t maxDelta = ANGLE_11hh; + angle_t delta = AngleDelta(player->mo->angle, K_MomentumAngle(player->mo)); + + if (delta > maxDelta) + { + // Do we need to turn? Start grinding! + cmd->buttons |= BT_DRIFT; + } + } + + if (player->botvars.itemconfirm > 25*TICRATE) + { + K_BotGenericPressItem(player, cmd, throwdir); + } +} + /*-------------------------------------------------- static void K_BotItemRings(player_t *player, ticcmd_t *cmd) @@ -1182,6 +1361,12 @@ static void K_BotItemRings(player_t *player, ticcmd_t *cmd) { INT32 saferingsval = 16 - K_GetKartRingPower(player, false); + if (P_IsObjectOnGround(player->mo) == false) + { + // Don't use while mid-air. + return; + } + if (player->speed < K_GetKartSpeed(player, false, true) / 2 // Being slowed down too much || player->speedboost > (FRACUNIT/5)) // Have another type of boost (tethering) { @@ -1242,7 +1427,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) { // Use rings! - if (leveltime > starttime && !player->exiting) + if (leveltime > starttime) { K_BotItemRings(player, cmd); } @@ -1293,7 +1478,6 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) case KITEM_SPB: case KITEM_GROW: case KITEM_SHRINK: - case KITEM_HYUDORO: case KITEM_SUPERRING: K_BotItemGenericTap(player, cmd); break; @@ -1325,8 +1509,6 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) K_BotItemGenericOrbitShield(player, cmd); } else if (player->position != 1) // Hold onto orbiting items when in 1st :) - /* FALLTHRU */ - case KITEM_BALLHOG: { K_BotItemOrbinaut(player, cmd); } @@ -1352,8 +1534,12 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) } break; case KITEM_LANDMINE: + case KITEM_HYUDORO: // Function re-use, as they have about the same usage. K_BotItemLandmine(player, cmd, turnamt); break; + case KITEM_BALLHOG: + K_BotItemBallhog(player, cmd); + break; case KITEM_DROPTARGET: if (!(player->pflags & PF_ITEMOUT)) { @@ -1361,7 +1547,17 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) } else { - K_BotItemDropTarget(player, cmd); + K_BotItemDropTarget(player, cmd, turnamt); + } + break; + case KITEM_GARDENTOP: + if (player->curshield != KSHIELD_TOP) + { + K_BotItemGardenTopDeploy(player, cmd); + } + else + { + K_BotItemGardenTop(player, cmd, turnamt); } break; case KITEM_LIGHTNINGSHIELD: diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 2d9c9774d..06934024d 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -149,7 +149,7 @@ static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) return true; } - if (sec->offroad > FRACUNIT) // Only care about strong offroad. + if (sec->offroad > 0) { return !K_BotCanTakeCut(player); } diff --git a/src/k_kart.c b/src/k_kart.c index 9973bc5d5..15cd34b9a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9067,6 +9067,11 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) if (angledelta < nextbestdelta && momdelta < nextbestmomdelta) { + if (waypoint->prevwaypoints[i] == finishline) // NEVER allow finish line. + { + continue; + } + if (P_TraceWaypointTraversal(player->mo, waypoint->prevwaypoints[i]->mobj) == false) { // Save sight checks when all of the other checks pass, so we only do it if we have to diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 837f77102..a36cdfeb7 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -361,7 +361,7 @@ waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj) // remember: huge radius if (closestdist <= rad && checkdist <= rad && finishline != NULL) { - if (!P_TraceBlockingLines(mobj, checkwaypoint->mobj)) // Intentionally not P_TraceWaypointTraversal + if (!P_TraceWaypointTraversal(mobj, checkwaypoint->mobj)) { // Save sight checks when all of the other checks pass, so we only do it if we have to continue; @@ -379,7 +379,7 @@ waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj) } else if (checkdist < closestdist && bestfindist == INT32_MAX) { - if (!P_TraceBlockingLines(mobj, checkwaypoint->mobj)) // Intentionally not P_TraceWaypointTraversal + if (!P_TraceWaypointTraversal(mobj, checkwaypoint->mobj)) { // Save sight checks when all of the other checks pass, so we only do it if we have to continue; diff --git a/src/p_enemy.c b/src/p_enemy.c index 0db891f81..131e92bf7 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -750,9 +750,6 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed if (player->mo->health <= 0) continue; // dead - if (player->bot) - continue; // ignore bots - if (dist > 0 && P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist) continue; // Too far away diff --git a/src/p_mobj.c b/src/p_mobj.c index 814aa6b57..0613d816c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4538,7 +4538,7 @@ boolean P_SupermanLook4Players(mobj_t *actor) { if (playeringame[c] && !players[c].spectator) { - if (!players[c].mo || players[c].bot) + if (!players[c].mo) continue; if (players[c].mo->health <= 0) diff --git a/src/p_sight.c b/src/p_sight.c index e96bf9db8..7ef9b9dd3 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -334,6 +334,12 @@ static boolean P_CanTraceBlockingLine(seg_t *seg, divline_t *divl, register los_ (void)divl; + if (!(line->flags & ML_TWOSIDED)) + { + // stop because it is not two sided anyway + return false; + } + if (P_IsLineBlocking(line, los->compareThing) == true) { // This line will always block us @@ -380,11 +386,16 @@ static boolean P_CanBotTraverse(seg_t *seg, divline_t *divl, register los_t *los if (los->compareThing->player != NULL && los->alreadyHates == false) { // Treat damage sectors like walls, if you're not already in a bad sector. + sector_t *front, *back; vertex_t pos; + P_ClosestPointOnLine(los->compareThing->x, los->compareThing->y, line, &pos); - if (K_BotHatesThisSector(los->compareThing->player, line->frontsector, pos.x, pos.y) - || K_BotHatesThisSector(los->compareThing->player, line->backsector, pos.x, pos.y)) + front = seg->frontsector; + back = seg->backsector; + + if (K_BotHatesThisSector(los->compareThing->player, front, pos.x, pos.y) + || K_BotHatesThisSector(los->compareThing->player, back, pos.x, pos.y)) { // This line does not block us, but we don't want to be in it. return false; diff --git a/src/p_spec.c b/src/p_spec.c index 4abe9f5e1..56eb3d941 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4282,7 +4282,7 @@ boolean P_IsPlayerValid(size_t playernum) boolean P_CanPlayerTrigger(size_t playernum) { - return P_IsPlayerValid(playernum) && !players[playernum].bot; + return P_IsPlayerValid(playernum); } /// \todo check continues for proper splitscreen support? @@ -4310,7 +4310,7 @@ static void P_ProcessEggCapsule(player_t *player, sector_t *sector) mobj_t *mo2; INT32 i; - if (player->bot || sector->ceilingdata || sector->floordata) + if (sector->ceilingdata || sector->floordata) return; // Find the center of the Eggtrap and release all the pretty animals! @@ -4517,9 +4517,6 @@ static void P_EvaluateDamageType(player_t *player, sector_t *sector, boolean isT static void P_EvaluateLinedefExecutorTrigger(player_t *player, sector_t *sector, boolean isTouching) { - if (player->bot) - return; - if (!sector->triggertag) return;