diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 233252536..9f63023d6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -432,6 +432,7 @@ consvar_t cv_kartdebugdistribution = CVAR_INIT ("kartdebugdistribution", "Off", consvar_t cv_kartdebughuddrop = CVAR_INIT ("kartdebughuddrop", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL); static CV_PossibleValue_t kartdebugwaypoint_cons_t[] = {{0, "Off"}, {1, "Forwards"}, {2, "Backwards"}, {0, NULL}}; consvar_t cv_kartdebugwaypoints = CVAR_INIT ("kartdebugwaypoints", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, kartdebugwaypoint_cons_t, NULL); +consvar_t cv_kartdebugbotpredict = CVAR_INIT ("kartdebugbotpredict", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL); consvar_t cv_kartdebugcheckpoint = CVAR_INIT ("kartdebugcheckpoint", "Off", CV_NOSHOWHELP, CV_OnOff, NULL); consvar_t cv_kartdebugnodes = CVAR_INIT ("kartdebugnodes", "Off", CV_NOSHOWHELP, CV_OnOff, NULL); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index bd435c49e..288f8eb94 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -93,7 +93,7 @@ extern consvar_t cv_votetime; extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartallowgiveitem, cv_kartdebugshrink, cv_kartdebugdistribution, cv_kartdebughuddrop; extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes, cv_kartdebugcolorize; -extern consvar_t cv_kartdebugwaypoints; +extern consvar_t cv_kartdebugwaypoints, cv_kartdebugbotpredict; extern consvar_t cv_itemfinder; diff --git a/src/k_bot.c b/src/k_bot.c index 126e4cee9..13eb9c1fe 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -572,9 +572,11 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict const fixed_t speed = max(P_AproxDistance(player->mo->momx, player->mo->momy), K_GetKartSpeed(player, false) / 4); - const INT32 distance = (FixedMul(speed, distreduce) / FRACUNIT) * futuresight; - botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); + const INT32 startDist = (768 * mapobjectscale) / FRACUNIT; + const INT32 distance = ((FixedMul(speed, distreduce) / FRACUNIT) * futuresight) + startDist; + + botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); waypoint_t *wp = player->nextwaypoint; INT32 distanceleft = distance; @@ -837,6 +839,70 @@ static INT16 K_FindBotController(mobj_t *mo) return -1; } +/*-------------------------------------------------- + static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player) + + Draws objects to show where the viewpoint bot is trying to go. + + Input Arguments:- + predict - The prediction to visualize. + player - The bot player this prediction is for. + + Return:- + None +--------------------------------------------------*/ +static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player) +{ + mobj_t *debugMobj = NULL; + angle_t sideAngle = ANGLE_MAX; + UINT8 i = UINT8_MAX; + + I_Assert(predict != NULL); + I_Assert(player != NULL); + I_Assert(player->mo != NULL && P_MobjWasRemoved(player->mo) == false); + + sideAngle = player->mo->angle + ANGLE_90; + + debugMobj = P_SpawnMobj(predict->x, predict->y, player->mo->z, MT_SPARK); + P_SetMobjState(debugMobj, S_THOK); + + debugMobj->frame &= ~FF_TRANSMASK; + debugMobj->frame |= FF_TRANS20|FF_FULLBRIGHT; + + debugMobj->color = SKINCOLOR_ORANGE; + debugMobj->scale *= 2; + + debugMobj->tics = 2; + + for (i = 0; i < 2; i++) + { + mobj_t *radiusMobj = NULL; + fixed_t radiusX = predict->x, radiusY = predict->y; + + if (i & 1) + { + radiusX -= FixedMul(predict->radius, FINECOSINE(sideAngle >> ANGLETOFINESHIFT)); + radiusY -= FixedMul(predict->radius, FINESINE(sideAngle >> ANGLETOFINESHIFT)); + } + else + { + radiusX += FixedMul(predict->radius, FINECOSINE(sideAngle >> ANGLETOFINESHIFT)); + radiusY += FixedMul(predict->radius, FINESINE(sideAngle >> ANGLETOFINESHIFT)); + } + + radiusMobj = P_SpawnMobj(radiusX, radiusY, player->mo->z, MT_SPARK); + P_SetMobjState(radiusMobj, S_THOK); + + radiusMobj->frame &= ~FF_TRANSMASK; + radiusMobj->frame |= FF_TRANS20|FF_FULLBRIGHT; + + radiusMobj->color = SKINCOLOR_YELLOW; + radiusMobj->scale /= 2; + + radiusMobj->tics = 2; + } +} + /*-------------------------------------------------- void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) @@ -863,6 +929,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) gamestate != GS_LEVEL || player->mo->scale <= 1 || player->playerstate == PST_DEAD + || leveltime <= introtime ) { // No need to do anything else. @@ -927,7 +994,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) destangle = FixedAngle(sides[controllerLine->sidenum[0]].textureoffset); // Overwritten prediction - predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); + predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); predict->x = player->mo->x + FixedMul(dist, FINECOSINE(destangle >> ANGLETOFINESHIFT)); predict->y = player->mo->y + FixedMul(dist, FINESINE(destangle >> ANGLETOFINESHIFT)); @@ -936,6 +1003,9 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) else { predict = K_CreateBotPrediction(player); + + K_NudgePredictionTowardsObjects(predict, player); + destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); } @@ -1033,11 +1103,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) cmd->forwardmove /= 2; cmd->buttons |= BT_BRAKE; } - else if (dirdist <= realrad) - { - // Steer towards/away from objects! - turnamt += K_BotFindObjects(player, turnamt); - } } } @@ -1053,7 +1118,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) finishBeamLine->v1->x, finishBeamLine->v1->y, finishBeamLine->v2->x, finishBeamLine->v2->y, player->mo->x, player->mo->y - ); + ) - player->speed; // Don't run the spindash code at all until we're in the right place trySpindash = false; @@ -1163,6 +1228,11 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Free the prediction we made earlier if (predict != NULL) { + if (cv_kartdebugbotpredict.value != 0 && player - players == displayplayers[0]) + { + K_DrawPredictionDebug(predict, player); + } + Z_Free(predict); } } diff --git a/src/k_bot.h b/src/k_bot.h index 2cb1ae460..38408c6af 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -198,19 +198,19 @@ fixed_t K_BotReducePrediction(player_t *player); /*-------------------------------------------------- - INT16 K_BotFindObjects(player_t *player, INT16 turn); + void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player); - Generates a sum for objects to steer towards/away from. + Moves the bot's prediction, based on objects around the bot. Input Arguments:- + predict - The bot's prediction to nudge. player - Player to compare. - turn - Turn value before object steering. Return:- - Turn amount sum to add to final product. + None --------------------------------------------------*/ -INT16 K_BotFindObjects(player_t *player, INT16 turn); +void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player); /*-------------------------------------------------- diff --git a/src/k_botitem.c b/src/k_botitem.c index a871c2305..070f927c8 100644 --- a/src/k_botitem.c +++ b/src/k_botitem.c @@ -909,7 +909,7 @@ static void K_BotItemFlame(player_t *player, ticcmd_t *cmd) --------------------------------------------------*/ static void K_BotItemRings(player_t *player, ticcmd_t *cmd) { - INT32 saferingsval = 16 - K_GetKartRingPower(player); + INT32 saferingsval = 16 - K_GetKartRingPower(player, false); if (player->speed < K_GetKartSpeed(player, false)/2 // Being slowed down too much || player->kartstuff[k_speedboost] > (FRACUNIT/5)) // Have another type of boost (tethering) @@ -971,7 +971,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) { // Use rings! - if (!player->exiting) + if (leveltime > starttime && !player->exiting) { K_BotItemRings(player, cmd); } diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 5d6425127..880eb5ef9 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -31,12 +31,16 @@ struct globalsmuggle { mobj_t *botmo; + botprediction_t *predict; fixed_t distancetocheck; - fixed_t closestlinedist; + INT64 gotoAvgX[2], gotoAvgY[2]; + UINT32 gotoObjs[2]; - INT16 curturn; - INT16 steer; + INT64 avoidAvgX[2], avoidAvgY[2]; + UINT32 avoidObjs[2]; + + fixed_t closestlinedist; fixed_t eggboxx, eggboxy; UINT8 randomitems; @@ -383,7 +387,7 @@ fixed_t K_BotReducePrediction(player_t *player) INT32 xl, xh, yl, yh, bx, by; globalsmuggle.botmo = player->mo; - globalsmuggle.distancetocheck = (player->mo->radius * 16); + globalsmuggle.distancetocheck = (player->mo->radius * 32); globalsmuggle.closestlinedist = INT32_MAX; tmx = player->mo->x; @@ -415,87 +419,107 @@ fixed_t K_BotReducePrediction(player_t *player) return FRACUNIT; } - return FixedDiv(globalsmuggle.closestlinedist, globalsmuggle.distancetocheck); + return (FRACUNIT/2) + (FixedDiv(globalsmuggle.closestlinedist, globalsmuggle.distancetocheck) / 2); } /*-------------------------------------------------- - static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount) + static void K_AddAttackObject(mobj_t *thing, UINT8 side, UINT8 weight) - Handles steering away/towards the specified object. + Adds an object to the list that the bot wants to go towards. Input Arguments:- - bot - Bot's mobj. - thing - Mobj to steer towards/away from. - fulldist - Distance away from object. - xdist - Horizontal distance away from object. - towards - If true, steer towards the object. Otherwise, steer away. - amount - How hard to turn. + thing - Object to move towards. + side - Which side -- 0 for left, 1 for right + weight - How important this object is. Return:- None --------------------------------------------------*/ -static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount) +static void K_AddAttackObject(mobj_t *thing, UINT8 side, UINT8 weight) { - angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); - angle_t angle; - SINT8 flip = 1; + UINT8 i; - amount = (amount * FixedDiv(globalsmuggle.distancetocheck - fulldist, globalsmuggle.distancetocheck)) / FRACUNIT; + I_Assert(side <= 1); - if (amount == 0) + if (weight == 0) { - // Shouldn't happen return; } - if (towards) + for (i = 0; i < weight; i++) { - if (xdist < FixedHypot(bot->radius, thing->radius)) - { - // Don't need to turn any harder! - - if (abs(globalsmuggle.steer) <= amount) - { - globalsmuggle.steer = 0; - } - else - { - if (globalsmuggle.steer > 0) - { - globalsmuggle.steer -= amount; - } - else if (globalsmuggle.steer < 0) - { - globalsmuggle.steer += amount; - } - } - - return; - } - - // Still turning towards it, flip. - flip = -flip; + globalsmuggle.gotoAvgX[side] += thing->x / mapobjectscale; + globalsmuggle.gotoAvgY[side] += thing->y / mapobjectscale; + globalsmuggle.gotoObjs[side]++; } - - angle = (bot->angle - destangle); - if (angle < ANGLE_180) - { - flip = -flip; - } - - // If going in the opposite direction of where you wanted to turn, - // then reduce the amount that you can turn in that direction. - if ((flip == 1 && globalsmuggle.curturn < 0) - || (flip == -1 && globalsmuggle.curturn > 0)) - { - amount /= 4; - } - - globalsmuggle.steer += amount * flip; } /*-------------------------------------------------- - static boolean K_BotSteerObjects(mobj_t *thing) + static void K_AddDodgeObject(mobj_t *thing, UINT8 side, UINT8 weight) + + Adds an object to the list that the bot wants to dodge. + + Input Arguments:- + thing - Object to move away from. + side - Which side -- 0 for left, 1 for right + weight - How important this object is. + + Return:- + None +--------------------------------------------------*/ +static void K_AddDodgeObject(mobj_t *thing, UINT8 side, UINT8 weight) +{ + UINT8 i; + + I_Assert(side <= 1); + + if (weight == 0) + { + return; + } + + for (i = 0; i < weight; i++) + { + globalsmuggle.gotoAvgX[side] += thing->x / mapobjectscale; + globalsmuggle.gotoAvgY[side] += thing->y / mapobjectscale; + globalsmuggle.gotoObjs[side]++; + } +} + +/*-------------------------------------------------- + static boolean K_PlayerAttackSteer(mobj_t *thing, UINT8 side, UINT8 weight, boolean attackCond, boolean dodgeCond) + + Checks two conditions to determine if the object should be + attacked or dodged. + + Input Arguments:- + thing - Object to move towards/away from. + side - Which side -- 0 for left, 1 for right + weight - How important this object is. + attackCond - If this is true, and dodgeCond isn't, then we go towards the object. + dodgeCond - If this is true, and attackCond isn't, then we move away from the object. + + Return:- + true if either condition is successful. +--------------------------------------------------*/ +static boolean K_PlayerAttackSteer(mobj_t *thing, UINT8 side, UINT8 weight, boolean attackCond, boolean dodgeCond) +{ + if (attackCond == true && dodgeCond == false) + { + K_AddAttackObject(thing, side, weight); + return true; + } + else if (dodgeCond == true && attackCond == false) + { + K_AddDodgeObject(thing, side, weight); + return true; + } + + return false; +} + +/*-------------------------------------------------- + static boolean K_FindObjectsForNudging(mobj_t *thing) Blockmap search function. Finds objects around the bot to steer towards/away from. @@ -506,20 +530,19 @@ static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixe Return:- true continues searching, false ends the search early. --------------------------------------------------*/ -static boolean K_BotSteerObjects(mobj_t *thing) +static boolean K_FindObjectsForNudging(mobj_t *thing) { INT16 anglediff; - fixed_t xdist, ydist, fulldist; - angle_t destangle, angle; - INT16 attack = ((9 - globalsmuggle.botmo->player->kartspeed) * KART_FULLTURN) / 8; // Acceleration chars are more aggressive - INT16 dodge = ((9 - globalsmuggle.botmo->player->kartweight) * KART_FULLTURN) / 8; // Handling chars dodge better + fixed_t fulldist; + angle_t destangle, angle, predictangle; + UINT8 side = 0; if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player) { return false; } - if (!thing->health) + if (thing->health <= 0) { return true; } @@ -529,32 +552,21 @@ static boolean K_BotSteerObjects(mobj_t *thing) return true; } - xdist = K_DistanceOfLineFromPoint( - globalsmuggle.botmo->x, globalsmuggle.botmo->y, - globalsmuggle.botmo->x + FINECOSINE(globalsmuggle.botmo->angle >> ANGLETOFINESHIFT), globalsmuggle.botmo->y + FINESINE(globalsmuggle.botmo->angle >> ANGLETOFINESHIFT), - thing->x, thing->y - ) / 2; // weight x dist more heavily than y dist - - ydist = K_DistanceOfLineFromPoint( - globalsmuggle.botmo->x, globalsmuggle.botmo->y, - globalsmuggle.botmo->x + FINECOSINE((globalsmuggle.botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT), globalsmuggle.botmo->y + FINESINE((globalsmuggle.botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT), - thing->x, thing->y - ); - - fulldist = FixedHypot(xdist, ydist); + fulldist = R_PointToDist2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, thing->x, thing->y) - thing->radius; if (fulldist > globalsmuggle.distancetocheck) { return true; } - if (!P_CheckSight(globalsmuggle.botmo, thing)) + if (P_CheckSight(globalsmuggle.botmo, thing) == false) { return true; } + predictangle = R_PointToAngle2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, globalsmuggle.predict->x, globalsmuggle.predict->y); destangle = R_PointToAngle2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, thing->x, thing->y); - angle = (globalsmuggle.botmo->angle - destangle); + angle = (predictangle - destangle); if (angle < ANGLE_180) { @@ -563,20 +575,11 @@ static boolean K_BotSteerObjects(mobj_t *thing) else { anglediff = 360-(AngleFixed(angle)>>FRACBITS); + side = 1; } anglediff = abs(anglediff); -#define PlayerAttackSteer(botcond, thingcond) \ - if ((botcond) && !(thingcond)) \ - { \ - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); \ - } \ - else if ((thingcond) && !(botcond)) \ - { \ - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); \ - } - switch (thing->type) { case MT_BANANA: @@ -593,21 +596,21 @@ static boolean K_BotSteerObjects(mobj_t *thing) case MT_BALLHOG: case MT_SPB: case MT_BUBBLESHIELDTRAP: - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); + K_AddDodgeObject(thing, side, 20); break; case MT_RANDOMITEM: - if (anglediff >= 60) + if (anglediff >= 45) { break; } if (P_CanPickupItem(globalsmuggle.botmo->player, 1)) { - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); + K_AddAttackObject(thing, side, 10); } break; case MT_EGGMANITEM: - if (anglediff >= 60) + if (anglediff >= 45) { break; } @@ -619,28 +622,28 @@ static boolean K_BotSteerObjects(mobj_t *thing) if (stealth >= requiredstealth) { - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); + K_AddAttackObject(thing, side, 10); } else { - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); + K_AddDodgeObject(thing, side, 10); } } break; case MT_FLOATINGITEM: - if (anglediff >= 60) + if (anglediff >= 45) { break; } if (P_CanPickupItem(globalsmuggle.botmo->player, 3)) { - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); + K_AddAttackObject(thing, side, 20); } break; case MT_RING: case MT_FLINGRING: - if (anglediff >= 60) + if (anglediff >= 45) { break; } @@ -650,11 +653,7 @@ static boolean K_BotSteerObjects(mobj_t *thing) && !thing->extravalue1 && (globalsmuggle.botmo->player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD)) { - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, - (RINGTOTAL(globalsmuggle.botmo->player) < 3 - ? (4 * (KART_FULLTURN + attack)) - : (KART_FULLTURN + attack)) - ); + K_AddAttackObject(thing, side, (RINGTOTAL(globalsmuggle.botmo->player) < 3) ? 5 : 1); } break; case MT_PLAYER: @@ -664,40 +663,61 @@ static boolean K_BotSteerObjects(mobj_t *thing) { // There REALLY ought to be a better way to handle this logic, right?! // Squishing - PlayerAttackSteer( + if (K_PlayerAttackSteer(thing, side, 20, globalsmuggle.botmo->scale > thing->scale + (mapobjectscale/8), thing->scale > globalsmuggle.botmo->scale + (mapobjectscale/8) - ) + )) + { + break; + } // Invincibility - else PlayerAttackSteer( + else if (K_PlayerAttackSteer(thing, side, 20, globalsmuggle.botmo->player->kartstuff[k_invincibilitytimer], thing->player->kartstuff[k_invincibilitytimer] - ) + )) + { + break; + } // Thunder Shield - else PlayerAttackSteer( + else if (K_PlayerAttackSteer(thing, side, 20, globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD, thing->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD - ) + )) + { + break; + } // Bubble Shield - else PlayerAttackSteer( + else if (K_PlayerAttackSteer(thing, side, 20, globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD, thing->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD - ) + )) + { + break; + } // Flame Shield - else PlayerAttackSteer( + else if (K_PlayerAttackSteer(thing, side, 20, globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD, thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD - ) + )) + { + break; + } // Has held item shield - else PlayerAttackSteer( + else if (K_PlayerAttackSteer(thing, side, 20, (globalsmuggle.botmo->player->kartstuff[k_itemheld] || globalsmuggle.botmo->player->kartstuff[k_eggmanheld]), (thing->player->kartstuff[k_itemheld] || thing->player->kartstuff[k_eggmanheld]) - ) + )) + { + break; + } // Ring Sting - else PlayerAttackSteer( + else if (K_PlayerAttackSteer(thing, side, 20, thing->player->rings <= 0, globalsmuggle.botmo->player->rings <= 0 - ) + )) + { + break; + } else { // After ALL of that, we can do standard bumping @@ -716,33 +736,43 @@ static boolean K_BotSteerObjects(mobj_t *thing) if (weightdiff > mapobjectscale) { - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); + K_AddAttackObject(thing, side, 20); } else { - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, KART_FULLTURN + dodge); + K_AddDodgeObject(thing, side, 20); } } } break; case MT_BOTHINT: - if (anglediff >= 60) + if (anglediff >= 45) { break; } + else + { + UINT8 weight = 20; - if (thing->extravalue1 == 0) - { - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, thing->extravalue2 * (KART_FULLTURN + dodge)); - } - { - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, thing->extravalue2 * (KART_FULLTURN + attack)); + if (thing->extravalue2 > 0) + { + weight = thing->extravalue2 * 5; + } + + if (thing->extravalue1 == 0) + { + K_AddDodgeObject(thing, side, weight); + } + else + { + K_AddAttackObject(thing, side, weight); + } } break; default: if (thing->flags & (MF_SOLID|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE)) { - K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); + K_AddDodgeObject(thing, side, 20); } break; } @@ -751,18 +781,40 @@ static boolean K_BotSteerObjects(mobj_t *thing) } /*-------------------------------------------------- - INT16 K_BotFindObjects(player_t *player, INT16 turn) + void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) See header file for description. --------------------------------------------------*/ -INT16 K_BotFindObjects(player_t *player, INT16 turn) +void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) { INT32 xl, xh, yl, yh, bx, by; - globalsmuggle.steer = 0; + fixed_t distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y); + + fixed_t avgX = 0, avgY = 0; + fixed_t avgDist = 0; + + const fixed_t baseNudge = 128 * mapobjectscale; + fixed_t maxNudge = distToPredict; + fixed_t nudgeDist = 0; + angle_t nudgeDir = 0; + + SINT8 gotoSide = -1; + UINT8 i; + globalsmuggle.botmo = player->mo; - globalsmuggle.curturn = turn; - globalsmuggle.distancetocheck = (player->mo->radius * 32) + (player->speed * 4); + globalsmuggle.predict = predict; + + globalsmuggle.distancetocheck = distToPredict; + + for (i = 0; i < 2; i++) + { + globalsmuggle.gotoAvgX[i] = globalsmuggle.gotoAvgY[i] = 0; + globalsmuggle.gotoObjs[i] = 0; + + globalsmuggle.avoidAvgX[i] = globalsmuggle.avoidAvgY[i] = 0; + globalsmuggle.avoidObjs[i] = 0; + } xl = (unsigned)(globalsmuggle.botmo->x - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; xh = (unsigned)(globalsmuggle.botmo->x + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; @@ -775,9 +827,120 @@ INT16 K_BotFindObjects(player_t *player, INT16 turn) { for (by = yl; by <= yh; by++) { - P_BlockThingsIterator(bx, by, K_BotSteerObjects); + P_BlockThingsIterator(bx, by, K_FindObjectsForNudging); } } - return globalsmuggle.steer; + // Handle dodge characters + if (globalsmuggle.avoidObjs[1] > 0 || globalsmuggle.avoidObjs[0] > 0) + { + if (globalsmuggle.avoidObjs[1] > globalsmuggle.avoidObjs[0]) + { + gotoSide = 1; + } + else + { + gotoSide = 0; + } + + avgX = (globalsmuggle.avoidAvgX[gotoSide] / globalsmuggle.avoidObjs[gotoSide]) * mapobjectscale; + avgY = (globalsmuggle.avoidAvgY[gotoSide] / globalsmuggle.avoidObjs[gotoSide]) * mapobjectscale; + + avgDist = R_PointToDist2( + avgX, avgY, + predict->x, predict->y + ); + + // High handling characters dodge better + nudgeDist = ((9 - globalsmuggle.botmo->player->kartweight) + 1) * baseNudge; + + maxNudge = max(distToPredict - predict->radius, predict->radius); + if (nudgeDist > maxNudge) + { + nudgeDist = maxNudge; + } + + // Point away + nudgeDir = R_PointToAngle2( + avgX, avgY, + predict->x, predict->y + ); + + predict->x += FixedMul(nudgeDist, FINECOSINE(nudgeDir >> ANGLETOFINESHIFT)); + predict->y += FixedMul(nudgeDist, FINESINE(nudgeDir >> ANGLETOFINESHIFT)); + + distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y); + + // Flip side, since we want to check for objects to steer towards on the side we're NOT dodging. + if (gotoSide == 0) + { + gotoSide = 1; + } + else + { + gotoSide = 0; + } + } + + if (gotoSide == -1) + { + // Pick a side here if there were no objects to dodge. + // We don't want to pick contradictory sides, so keep the old side otherwise, + // even if there's more to grab on the other side. + + if (globalsmuggle.gotoObjs[1] > globalsmuggle.gotoObjs[0]) + { + gotoSide = 1; + } + else + { + gotoSide = 0; + } + } + + // Check if our side is invalid, if so, don't do the code below. + if (gotoSide != -1 && globalsmuggle.gotoObjs[gotoSide] == 0) + { + // Do not use a side + gotoSide = -1; + } + + if (gotoSide != -1) + { + avgX = (globalsmuggle.gotoAvgX[gotoSide] / globalsmuggle.gotoObjs[gotoSide]) * mapobjectscale; + avgY = (globalsmuggle.gotoAvgY[gotoSide] / globalsmuggle.gotoObjs[gotoSide]) * mapobjectscale; + + avgDist = R_PointToDist2( + predict->x, predict->y, + avgX, avgY + ); + + // Acceleration characters are more aggressive + nudgeDist = ((9 - globalsmuggle.botmo->player->kartspeed) + 1) * baseNudge; + + maxNudge = max(distToPredict - predict->radius, predict->radius); + if (nudgeDist > maxNudge) + { + nudgeDist = maxNudge; + } + + if (avgDist <= nudgeDist) + { + predict->x = avgX; + predict->y = avgY; + } + else + { + // Point towards + nudgeDir = R_PointToAngle2( + predict->x, predict->y, + avgX, avgY + ); + + predict->x += FixedMul(nudgeDist, FINECOSINE(nudgeDir >> ANGLETOFINESHIFT)); + predict->y += FixedMul(nudgeDist, FINESINE(nudgeDir >> ANGLETOFINESHIFT)); + + //distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y); + } + } } diff --git a/src/k_kart.c b/src/k_kart.c index 6621042d8..91335cdcb 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -252,6 +252,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartdebugdistribution); CV_RegisterVar(&cv_kartdebughuddrop); CV_RegisterVar(&cv_kartdebugwaypoints); + CV_RegisterVar(&cv_kartdebugbotpredict); CV_RegisterVar(&cv_kartdebugcheckpoint); CV_RegisterVar(&cv_kartdebugnodes); @@ -6938,11 +6939,11 @@ void K_UpdateDistanceFromFinishLine(player_t *const player) } } -INT32 K_GetKartRingPower(player_t *player) +INT32 K_GetKartRingPower(player_t *player, boolean boosted) { INT32 ringPower = ((9 - player->kartspeed) + (9 - player->kartweight)) / 2; - if (K_PlayerUsesBotMovement(player)) + if (boosted == true && K_PlayerUsesBotMovement(player)) { // Double for Lv. 9 ringPower += (player->botvars.difficulty * ringPower) / MAXBOTDIFFICULTY; diff --git a/src/k_kart.h b/src/k_kart.h index c91bba806..bde845a22 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -69,7 +69,7 @@ void K_UpdateHnextList(player_t *player, boolean clean); void K_DropHnextList(player_t *player, boolean keepshields); void K_RepairOrbitChain(mobj_t *orbit); player_t *K_FindJawzTarget(mobj_t *actor, player_t *source); -INT32 K_GetKartRingPower(player_t *player); +INT32 K_GetKartRingPower(player_t *player, boolean boosted); void K_UpdateDistanceFromFinishLine(player_t *const player); boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y); INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue); diff --git a/src/k_waypoint.c b/src/k_waypoint.c index ae6841a34..73eb82bfa 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -535,9 +535,9 @@ static void K_DebugWaypointsSpawnLine(waypoint_t *const waypoint1, waypoint_t *c { spawnedmobj = P_SpawnMobj(x, y, z, MT_SPARK); P_SetMobjState(spawnedmobj, S_THOK); - spawnedmobj->state->nextstate = S_NULL; - spawnedmobj->state->tics = 1; - spawnedmobj->frame = spawnedmobj->frame & ~FF_TRANSMASK; + spawnedmobj->tics = 1; + spawnedmobj->frame &= ~FF_TRANSMASK; + spawnedmobj->frame |= FF_FULLBRIGHT; spawnedmobj->color = linkcolour; spawnedmobj->scale = FixedMul(spawnedmobj->scale, FixedMul(FRACUNIT/4, FixedDiv((15 - ((leveltime + n) % 16))*FRACUNIT, 15*FRACUNIT))); } @@ -582,9 +582,9 @@ static void K_DebugWaypointDrawRadius(waypoint_t *const waypoint) radiusOrb = P_SpawnMobj(spawnX, spawnY, spawnZ, MT_SPARK); P_SetMobjState(radiusOrb, S_THOK); - radiusOrb->state->nextstate = S_NULL; - radiusOrb->state->tics = 1; - radiusOrb->frame = radiusOrb->frame & ~FF_TRANSMASK; + radiusOrb->tics = 1; + radiusOrb->frame &= ~FF_TRANSMASK; + radiusOrb->frame |= FF_FULLBRIGHT; radiusOrb->color = SKINCOLOR_PURPLE; radiusOrb->scale = radiusOrb->scale / 4; } @@ -623,7 +623,7 @@ void K_DebugWaypointsVisualise(void) P_SetMobjState(debugmobj, S_THOK); debugmobj->frame &= ~FF_TRANSMASK; - debugmobj->frame |= FF_TRANS20; + debugmobj->frame |= FF_TRANS20|FF_FULLBRIGHT; // There's a waypoint setup for this mobj! So draw that it's a valid waypoint and draw lines to its connections if (waypoint != NULL) @@ -683,8 +683,7 @@ void K_DebugWaypointsVisualise(void) { debugmobj->color = SKINCOLOR_RED; } - debugmobj->state->tics = 1; - debugmobj->state->nextstate = S_NULL; + debugmobj->tics = 1; } } diff --git a/src/p_enemy.c b/src/p_enemy.c index c96bf979f..ce3186442 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -4159,7 +4159,7 @@ void A_AttractChase(mobj_t *actor) angle_t offset = FixedAngle(18<target->player->kartstuff[k_ringboost] += K_GetKartRingPower(actor->target->player)+3; + actor->target->player->kartstuff[k_ringboost] += K_GetKartRingPower(actor->target->player, true) + 3; S_StartSound(actor->target, sfx_s1b5); sparkle = P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, MT_RINGSPARKS); @@ -4187,7 +4187,7 @@ void A_AttractChase(mobj_t *actor) if (actor->extravalue1 >= 16) { if (!P_GivePlayerRings(actor->target->player, 1)) // returns 0 if addition failed - actor->target->player->kartstuff[k_ringboost] += K_GetKartRingPower(actor->target->player)+3; + actor->target->player->kartstuff[k_ringboost] += K_GetKartRingPower(actor->target->player, true) + 3; if (actor->cvmem) // caching S_StartSound(actor->target, sfx_s1c5); diff --git a/src/p_mobj.c b/src/p_mobj.c index 6a58fc907..653b934d6 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11769,7 +11769,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean // Steering amount if (mthing->args[1] == 0) { - mobj->extravalue2 = 2; + mobj->extravalue2 = 4; } else {