diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37ef22805..9e410e75e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -166,7 +166,10 @@ set(SRB2_CORE_GAME_SOURCES k_pathfind.c k_pwrlv.c k_waypoint.c + k_color.c k_bot.c + k_botitem.c + k_botsearch.c p_local.h p_maputl.h @@ -187,6 +190,7 @@ set(SRB2_CORE_GAME_SOURCES k_pathfind.h k_pwrlv.h k_waypoint.h + k_color.h k_bot.h ) diff --git a/src/Makefile b/src/Makefile index 663774144..101b64b9b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -507,12 +507,15 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/st_stuff.o \ $(OBJDIR)/k_kart.o \ $(OBJDIR)/k_collide.o\ + $(OBJDIR)/k_color.o \ $(OBJDIR)/k_battle.o \ $(OBJDIR)/k_pwrlv.o \ $(OBJDIR)/k_waypoint.o\ $(OBJDIR)/k_pathfind.o\ $(OBJDIR)/k_bheap.o \ - $(OBJDIR)/k_bot.o \ + $(OBJDIR)/k_bot.o \ + $(OBJDIR)/k_botitem.o \ + $(OBJDIR)/k_botsearch.o \ $(OBJDIR)/k_grandprix.o\ $(OBJDIR)/m_aatree.o \ $(OBJDIR)/m_anigif.o \ diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5cf93c1d7..774effd04 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -654,7 +654,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->bot_rival = players[i].botvars.rival; rsp->bot_itemdelay = players[i].botvars.itemdelay; rsp->bot_itemconfirm = players[i].botvars.itemconfirm; - rsp->bot_lastturn = players[i].botvars.lastturn; + rsp->bot_turnconfirm = players[i].botvars.turnconfirm; rsp->hasmo = false; //Transfer important mo information if the player has a body. @@ -786,7 +786,7 @@ static void resynch_read_player(resynch_pak *rsp) players[i].botvars.rival = rsp->bot_rival; players[i].botvars.itemdelay = rsp->bot_itemdelay; players[i].botvars.itemconfirm = rsp->bot_itemconfirm; - players[i].botvars.lastturn = rsp->bot_lastturn; + players[i].botvars.turnconfirm = rsp->bot_turnconfirm; //We get a packet for each player in game. if (!playeringame[i]) diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 349951ff6..95d847607 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -292,7 +292,7 @@ typedef struct boolean bot_rival; tic_t bot_itemdelay; tic_t bot_itemconfirm; - INT16 bot_lastturn; + SINT8 bot_turnconfirm; //player->mo stuff UINT8 hasmo; // Boolean diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f02d315a3..79a8e9870 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -50,6 +50,7 @@ #include "k_battle.h" #include "k_pwrlv.h" #include "y_inter.h" +#include "k_color.h" #include "k_grandprix.h" #ifdef NETGAME_DEVMODE @@ -393,12 +394,19 @@ static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, consvar_t cv_kartvoices = {"kartvoices", "Tasteful", CV_SAVE, kartvoices_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t kartbot_cons_t[] = { - {1, "MIN"}, - {9, "MAX"}, {0, "Off"}, + {1, "Lv.1"}, + {2, "Lv.2"}, + {3, "Lv.3"}, + {4, "Lv.4"}, + {5, "Lv.5"}, + {6, "Lv.6"}, + {7, "Lv.7"}, + {8, "Lv.8"}, + {9, "Lv.9"}, {0, NULL} }; -consvar_t cv_kartbot = {"kartbot", "5", CV_NETVAR|CV_CHEAT, kartbot_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_kartbot = {"kartbot", "0", CV_NETVAR|CV_CHEAT, kartbot_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_karteliminatelast = {"karteliminatelast", "Yes", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOSHOWHELP, CV_YesNo, KartEliminateLast_OnChange, 0, NULL, NULL, 0, 0, NULL}; diff --git a/src/d_player.h b/src/d_player.h index 90d2d3aa5..dc3f647f6 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -430,7 +430,7 @@ typedef struct botvars_s tic_t itemdelay; // Delay before using item at all tic_t itemconfirm; // When high enough, they will use their item - INT16 lastturn; // Last turn direction + SINT8 turnconfirm; // Confirm turn direction } botvars_t; // ======================================================================== diff --git a/src/dehacked.c b/src/dehacked.c index 45d6c8b80..1b57a2161 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8258,6 +8258,7 @@ static const char *COLOR_ENUMS[] = { // Rejigged for Kart. "GREY", // SKINCOLOR_GREY "NICKEL", // SKINCOLOR_NICKEL "BLACK", // SKINCOLOR_BLACK + "SKUNK", // SKINCOLOR_SKUNK "FAIRY", // SKINCOLOR_FAIRY "POPCORN", // SKINCOLOR_POPCORN "ARTICHOKE", // SKINCOLOR_ARTICHOKE @@ -8268,7 +8269,6 @@ static const char *COLOR_ENUMS[] = { // Rejigged for Kart. "PEACH", // SKINCOLOR_PEACH "BROWN", // SKINCOLOR_BROWN "LEATHER", // SKINCOLOR_LEATHER - "SALMON", // SKINCOLOR_SALMON "PINK", // SKINCOLOR_PINK "ROSE", // SKINCOLOR_ROSE "CINNAMON", // SKINCOLOR_CINNAMON @@ -8306,7 +8306,6 @@ static const char *COLOR_ENUMS[] = { // Rejigged for Kart. "PISTACHIO", // SKINCOLOR_PISTACHIO "MOSS", // SKINCOLOR_MOSS "CAMOUFLAGE", // SKINCOLOR_CAMOUFLAGE - "ROBOHOOD", // SKINCOLOR_ROBOHOOD "MINT", // SKINCOLOR_MINT "GREEN", // SKINCOLOR_GREEN "PINETREE", // SKINCOLOR_PINETREE @@ -8316,11 +8315,10 @@ static const char *COLOR_ENUMS[] = { // Rejigged for Kart. "PLAGUE", // SKINCOLOR_PLAGUE "EMERALD", // SKINCOLOR_EMERALD "ALGAE", // SKINCOLOR_ALGAE - "CARIBBEAN", // SKINCOLOR_CARIBBEAN - "AZURE", // SKINCOLOR_AZURE "AQUAMARINE", // SKINCOLOR_AQUAMARINE "TURQUOISE", // SKINCOLOR_TURQUOISE "TEAL", // SKINCOLOR_TEAL + "ROBIN", // SKINCOLOR_ROBIN "CYAN", // SKINCOLOR_CYAN "JAWZ", // SKINCOLOR_JAWZ "CERULEAN", // SKINCOLOR_CERULEAN @@ -8337,6 +8335,7 @@ static const char *COLOR_ENUMS[] = { // Rejigged for Kart. "ULTRAMARINE", // SKINCOLOR_ULTRAMARINE "PERIWINKLE", // SKINCOLOR_PERIWINKLE "BLUE", // SKINCOLOR_BLUE + "MIDNIGHT", // SKINCOLOR_MIDNIGHT "BLUEBERRY", // SKINCOLOR_BLUEBERRY "THISTLE", // SKINCOLOR_THISTLE "PURPLE", // SKINCOLOR_PURPLE @@ -8352,6 +8351,7 @@ static const char *COLOR_ENUMS[] = { // Rejigged for Kart. "BYZANTIUM", // SKINCOLOR_BYZANTIUM "POMEGRANATE", // SKINCOLOR_POMEGRANATE "LILAC", // SKINCOLOR_LILAC + "BLOSSOM", // SKINCOLOR_BLOSSOM "TAFFY", // SKINCOLOR_TAFFY // Special super colors diff --git a/src/doomdef.h b/src/doomdef.h index 6fd841c6b..55ee7e66f 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -262,6 +262,7 @@ typedef enum SKINCOLOR_GREY, SKINCOLOR_NICKEL, SKINCOLOR_BLACK, + SKINCOLOR_SKUNK, SKINCOLOR_FAIRY, SKINCOLOR_POPCORN, SKINCOLOR_ARTICHOKE, @@ -272,7 +273,6 @@ typedef enum SKINCOLOR_PEACH, SKINCOLOR_BROWN, SKINCOLOR_LEATHER, - SKINCOLOR_SALMON, SKINCOLOR_PINK, SKINCOLOR_ROSE, SKINCOLOR_CINNAMON, @@ -310,7 +310,6 @@ typedef enum SKINCOLOR_PISTACHIO, SKINCOLOR_MOSS, SKINCOLOR_CAMOUFLAGE, - SKINCOLOR_ROBOHOOD, SKINCOLOR_MINT, SKINCOLOR_GREEN, SKINCOLOR_PINETREE, @@ -320,11 +319,10 @@ typedef enum SKINCOLOR_PLAGUE, SKINCOLOR_EMERALD, SKINCOLOR_ALGAE, - SKINCOLOR_CARIBBEAN, - SKINCOLOR_AZURE, SKINCOLOR_AQUAMARINE, SKINCOLOR_TURQUOISE, SKINCOLOR_TEAL, + SKINCOLOR_ROBIN, SKINCOLOR_CYAN, SKINCOLOR_JAWZ, // Oni's torment SKINCOLOR_CERULEAN, @@ -341,6 +339,7 @@ typedef enum SKINCOLOR_ULTRAMARINE, SKINCOLOR_PERIWINKLE, SKINCOLOR_BLUE, + SKINCOLOR_MIDNIGHT, SKINCOLOR_BLUEBERRY, SKINCOLOR_THISTLE, SKINCOLOR_PURPLE, @@ -356,6 +355,7 @@ typedef enum SKINCOLOR_BYZANTIUM, SKINCOLOR_POMEGRANATE, SKINCOLOR_LILAC, + SKINCOLOR_BLOSSOM, SKINCOLOR_TAFFY, // "Careful! MAXSKINCOLORS cannot be greater than 0x40 -- Which it is now." diff --git a/src/doomstat.h b/src/doomstat.h index 78f412d1a..ffac81726 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -535,6 +535,7 @@ extern tic_t mapreset; extern boolean thwompsactive; extern SINT8 spbplace; +extern tic_t bombflashtimer; // Used to avoid causing seizures if multiple mines explode close to you :) extern boolean legitimateexit; extern boolean comebackshowninfo; extern tic_t curlap, bestlap; diff --git a/src/g_game.c b/src/g_game.c index a29ba5cbb..bd9ebfcf6 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -52,6 +52,7 @@ #include "k_kart.h" // SRB2kart #include "k_battle.h" #include "k_pwrlv.h" +#include "k_color.h" #include "k_grandprix.h" gameaction_t gameaction; @@ -289,6 +290,7 @@ boolean thwompsactive; // Thwomps activate on lap 2 SINT8 spbplace; // SPB exists, give the person behind better items // Client-sided, unsynched variables (NEVER use in anything that needs to be synced with other players) +tic_t bombflashtimer = 0; // Cooldown before another FlashPal can be intialized by a bomb exploding near a displayplayer. Avoids seizures. boolean legitimateexit; // Did this client actually finish the match? boolean comebackshowninfo; // Have you already seen the "ATTACK OR PROTECT" message? tic_t curlap; // Current lap time diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 303676eed..3d4f26df9 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -223,40 +223,27 @@ UINT8 HWR_FogBlockAlpha(INT32 light, extracolormap_t *colormap) // Let's see if return surfcolor.s.alpha; } -static FUINT HWR_CalcWallLight(FUINT lightnum, fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y) +// Lightnum = current light +// line = the seg to get the light offset from +static FUINT HWR_CalcWallLight(seg_t *line, FUINT lightnum) { INT16 finallight = lightnum; - if (cv_grfakecontrast.value != 0) + fixed_t extralight = 0; + + if (cv_grfakecontrast.value == 1) // Smooth setting + extralight += line->hwLightOffset; + else + extralight += line->lightOffset * 8; + + if (extralight != 0) { - const UINT8 contrast = 8; - fixed_t extralight = 0; + finallight += extralight; - if (cv_grfakecontrast.value == 2) // Smooth setting - { - extralight = (-(contrast<> FRACBITS; - } - else - { - if (v1y == v2y) - extralight = -contrast; - else if (v1x == v2x) - extralight = contrast; - } - - if (extralight != 0) - { - finallight += extralight; - - if (finallight < 0) - finallight = 0; - if (finallight > 255) - finallight = 255; - } + if (finallight < 0) + finallight = 0; + if (finallight > 255) + finallight = 255; } return (FUINT)finallight; @@ -618,7 +605,7 @@ void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum, FSurfa INT32 solid, i; lightlist_t * list = sector->lightlist; const UINT8 alpha = Surf->PolyColor.s.alpha; - FUINT lightnum = HWR_CalcWallLight(sector->lightlevel, v1x, v1y, v2x, v2y); + FUINT lightnum = HWR_CalcWallLight(gr_curline, sector->lightlevel); extracolormap_t *colormap = NULL; realtop = top = wallVerts[3].y; @@ -647,12 +634,12 @@ void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum, FSurfa { if (pfloor && (pfloor->flags & FF_FOG)) { - lightnum = HWR_CalcWallLight(pfloor->master->frontsector->lightlevel, v1x, v1y, v2x, v2y); + lightnum = HWR_CalcWallLight(gr_curline, pfloor->master->frontsector->lightlevel); colormap = pfloor->master->frontsector->extra_colormap; } else { - lightnum = HWR_CalcWallLight(*list[i].lightlevel, v1x, v1y, v2x, v2y); + lightnum = HWR_CalcWallLight(gr_curline, *list[i].lightlevel); colormap = list[i].extra_colormap; } } @@ -933,7 +920,7 @@ void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom cliphigh = (float)(texturehpeg + (gr_curline->flength*FRACUNIT)); } - lightnum = HWR_CalcWallLight(gr_frontsector->lightlevel, vs.x, vs.y, ve.x, ve.y); + lightnum = HWR_CalcWallLight(gr_curline, gr_frontsector->lightlevel); colormap = gr_frontsector->extra_colormap; if (gr_frontsector) @@ -1759,7 +1746,7 @@ void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom blendmode = PF_Fog|PF_NoTexture; - lightnum = HWR_CalcWallLight(rover->master->frontsector->lightlevel, vs.x, vs.y, ve.x, ve.y); + lightnum = HWR_CalcWallLight(gr_curline, rover->master->frontsector->lightlevel); colormap = rover->master->frontsector->extra_colormap; Surf.PolyColor.s.alpha = HWR_FogBlockAlpha(rover->master->frontsector->lightlevel, rover->master->frontsector->extra_colormap); @@ -1871,7 +1858,7 @@ void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom blendmode = PF_Fog|PF_NoTexture; - lightnum = HWR_CalcWallLight(rover->master->frontsector->lightlevel, vs.x, vs.y, ve.x, ve.y); + lightnum = HWR_CalcWallLight(gr_curline, rover->master->frontsector->lightlevel); colormap = rover->master->frontsector->extra_colormap; Surf.PolyColor.s.alpha = HWR_FogBlockAlpha(rover->master->frontsector->lightlevel, rover->master->frontsector->extra_colormap); @@ -2831,6 +2818,10 @@ static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float v static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) { + const fixed_t thingxpos = thing->x + thing->sprxoff; + const fixed_t thingypos = thing->y + thing->spryoff; + const fixed_t thingzpos = thing->z + thing->sprzoff; + GLPatch_t *gpatch; FOutVector shadowVerts[4]; FSurfaceInfo sSurf; @@ -2848,7 +2839,7 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) pslope_t *floorslope; floorz = R_GetShadowZ(thing, &floorslope); - floordiff = abs(thing->z - floorz); + floordiff = abs(thingzpos - floorz); alpha = floordiff / (4*FRACUNIT) + 75; if (alpha >= 255) return; @@ -2872,8 +2863,8 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) scalemul = FixedMul(scalemul, (thing->radius*2) / gpatch->height); fscale = FIXED_TO_FLOAT(scalemul); - fx = FIXED_TO_FLOAT(thing->x); - fy = FIXED_TO_FLOAT(thing->y); + fx = FIXED_TO_FLOAT(thingxpos); + fy = FIXED_TO_FLOAT(thingypos); // 3--2 // | /| @@ -3592,10 +3583,10 @@ static int CompareVisSprites(const void *p1, const void *p2) gr_vissprite_t* spr2 = *(gr_vissprite_t*const*)p2; int idiff; float fdiff; - + // make transparent sprites last // "boolean to int" - + int transparency1 = (spr1->mobj->flags2 & MF2_SHADOW) || (spr1->mobj->frame & FF_TRANSMASK); int transparency2 = (spr2->mobj->flags2 & MF2_SHADOW) || (spr2->mobj->frame & FF_TRANSMASK); idiff = transparency1 - transparency2; @@ -4096,6 +4087,10 @@ void HWR_AddSprites(sector_t *sec) // BP why not use xtoviexangle/viewangletox like in bsp ?.... void HWR_ProjectSprite(mobj_t *thing) { + const fixed_t thingxpos = thing->x + thing->sprxoff; + const fixed_t thingypos = thing->y + thing->spryoff; + const fixed_t thingzpos = thing->z + thing->sprzoff; + gr_vissprite_t *vis; float tr_x, tr_y; float tz; @@ -4120,8 +4115,8 @@ void HWR_ProjectSprite(mobj_t *thing) this_scale = FIXED_TO_FLOAT(thing->scale); // transform the origin point - tr_x = FIXED_TO_FLOAT(thing->x) - gr_viewx; - tr_y = FIXED_TO_FLOAT(thing->y) - gr_viewy; + tr_x = FIXED_TO_FLOAT(thingxpos) - gr_viewx; + tr_y = FIXED_TO_FLOAT(thingypos) - gr_viewy; // rotation around vertical axis tz = (tr_x * gr_viewcos) + (tr_y * gr_viewsin); @@ -4131,8 +4126,8 @@ void HWR_ProjectSprite(mobj_t *thing) return; // The above can stay as it works for cutting sprites that are too close - tr_x = FIXED_TO_FLOAT(thing->x); - tr_y = FIXED_TO_FLOAT(thing->y); + tr_x = FIXED_TO_FLOAT(thingxpos); + tr_y = FIXED_TO_FLOAT(thingypos); // decide which patch to use for sprite relative to player #ifdef RANGECHECK @@ -4168,9 +4163,9 @@ void HWR_ProjectSprite(mobj_t *thing) #endif if (thing->player) - ang = R_PointToAngle (thing->x, thing->y) - thing->player->frameangle; + ang = R_PointToAngle (thingxpos, thingypos) - thing->player->frameangle; else - ang = R_PointToAngle (thing->x, thing->y) - thing->angle; + ang = R_PointToAngle (thingxpos, thingypos) - thing->angle; if (sprframe->rotate == SRF_SINGLE) { @@ -4242,12 +4237,12 @@ void HWR_ProjectSprite(mobj_t *thing) if (vflip) { - gz = FIXED_TO_FLOAT(thing->z+thing->height) - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale; + gz = FIXED_TO_FLOAT(thingzpos + thing->height) - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale; gzt = gz + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale; } else { - gzt = FIXED_TO_FLOAT(thing->z) + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale; + gzt = FIXED_TO_FLOAT(thingzpos) + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale; gz = gzt - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale; } @@ -4266,12 +4261,12 @@ void HWR_ProjectSprite(mobj_t *thing) if (heightsec != -1 && phs != -1) // only clip things which are in special sectors { if (gr_viewz < FIXED_TO_FLOAT(sectors[phs].floorheight) ? - FIXED_TO_FLOAT(thing->z) >= FIXED_TO_FLOAT(sectors[heightsec].floorheight) : + FIXED_TO_FLOAT(thingzpos) >= FIXED_TO_FLOAT(sectors[heightsec].floorheight) : gzt < FIXED_TO_FLOAT(sectors[heightsec].floorheight)) return; if (gr_viewz > FIXED_TO_FLOAT(sectors[phs].ceilingheight) ? gzt < FIXED_TO_FLOAT(sectors[heightsec].ceilingheight) && gr_viewz >= FIXED_TO_FLOAT(sectors[heightsec].ceilingheight) : - FIXED_TO_FLOAT(thing->z) >= FIXED_TO_FLOAT(sectors[heightsec].ceilingheight)) + FIXED_TO_FLOAT(thingzpos) >= FIXED_TO_FLOAT(sectors[heightsec].ceilingheight)) return; } diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 53503fa5c..27ad45d9e 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -37,7 +37,7 @@ #include "../r_things.h" #include "../r_draw.h" #include "../p_tick.h" -#include "../k_kart.h" // colortranslations +#include "../k_color.h" // colortranslations #include "hw_model.h" #include "hw_main.h" @@ -652,15 +652,6 @@ spritemd2found: fclose(f); } -// Define for getting accurate color brightness readings according to how the human eye sees them. -// https://en.wikipedia.org/wiki/Relative_luminance -// 0.2126 to red -// 0.7152 to green -// 0.0722 to blue -// (See this same define in hw_md2.c!) -#define SETBRIGHTNESS(brightness,r,g,b) \ - brightness = (UINT8)(((1063*(UINT16)(r))/5000) + ((3576*(UINT16)(g))/5000) + ((361*(UINT16)(b))/5000)) - static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, GLMipmap_t *grmip, INT32 skinnum, skincolors_t color) { UINT16 w = gpatch->width, h = gpatch->height; @@ -748,7 +739,7 @@ static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, for (i = 0; i < translen; i++) // moved from inside the loop to here { RGBA_t tempc = V_GetColor(translation[i]); - SETBRIGHTNESS(colorbrightnesses[i], tempc.s.red, tempc.s.green, tempc.s.blue); // store brightnesses for comparison + colorbrightnesses[i] = K_ColorRelativeLuminance(tempc.s.red, tempc.s.green, tempc.s.blue); // store brightnesses for comparison } // generate lookup table for color brightness matching for (b = 0; b < 256; b++) @@ -826,8 +817,10 @@ static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, else { UINT16 imagebright, blendbright; - SETBRIGHTNESS(imagebright,image->s.red,image->s.green,image->s.blue); - SETBRIGHTNESS(blendbright,blendimage->s.red,blendimage->s.green,blendimage->s.blue); + + imagebright = K_ColorRelativeLuminance(image->s.red, image->s.green, image->s.blue); + blendbright = K_ColorRelativeLuminance(blendimage->s.red, blendimage->s.green, blendimage->s.blue); + // slightly dumb average between the blend image color and base image colour, usually one or the other will be fully opaque anyway brightness = (imagebright*(255-blendimage->s.alpha))/255 + (blendbright*blendimage->s.alpha)/255; } @@ -841,7 +834,7 @@ static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, } else { - SETBRIGHTNESS(brightness,blendimage->s.red,blendimage->s.green,blendimage->s.blue); + brightness = K_ColorRelativeLuminance(blendimage->s.red, blendimage->s.green, blendimage->s.blue); } } @@ -948,9 +941,12 @@ static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, UINT32 tempcolor; UINT16 colorbright; - SETBRIGHTNESS(colorbright,blendcolor.s.red,blendcolor.s.green,blendcolor.s.blue); + colorbright = K_ColorRelativeLuminance(blendcolor.s.red, blendcolor.s.green, blendcolor.s.blue); + if (colorbright == 0) + { colorbright = 1; // no dividing by 0 please + } tempcolor = (brightness * blendcolor.s.red) / colorbright; tempcolor = min(255, tempcolor); @@ -1010,9 +1006,6 @@ skippixel: return; } - -#undef SETBRIGHTNESS - static void HWR_GetBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, INT32 skinnum, const UINT8 *colormap, skincolors_t color) { // mostly copied from HWR_GetMappedPatch, hence the similarities and comment @@ -1121,6 +1114,10 @@ void HWR_DrawMD2(gr_vissprite_t *spr) // Look at HWR_ProjectSprite for more { + const fixed_t thingxpos = spr->mobj->x + spr->mobj->sprxoff; + const fixed_t thingypos = spr->mobj->y + spr->mobj->spryoff; + const fixed_t thingzpos = spr->mobj->z + spr->mobj->sprzoff; + GLPatch_t *gpatch; INT32 durs = spr->mobj->state->tics; INT32 tics = spr->mobj->tics; @@ -1266,13 +1263,13 @@ void HWR_DrawMD2(gr_vissprite_t *spr) #endif //Hurdler: it seems there is still a small problem with mobj angle - p.x = FIXED_TO_FLOAT(spr->mobj->x); - p.y = FIXED_TO_FLOAT(spr->mobj->y)+md2->offset; + p.x = FIXED_TO_FLOAT(thingxpos); + p.y = FIXED_TO_FLOAT(thingypos) + md2->offset; if (spr->mobj->eflags & MFE_VERTICALFLIP) - p.z = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height); + p.z = FIXED_TO_FLOAT(thingzpos + spr->mobj->height); else - p.z = FIXED_TO_FLOAT(spr->mobj->z); + p.z = FIXED_TO_FLOAT(thingzpos); if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY) sprdef = &((skin_t *)spr->mobj->skin)->spritedef; @@ -1292,7 +1289,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr) } else { - const fixed_t anglef = AngleFixed((R_PointToAngle(spr->mobj->x, spr->mobj->y))-ANGLE_180); + const fixed_t anglef = AngleFixed((R_PointToAngle(thingxpos, thingypos))-ANGLE_180); p.angley = FIXED_TO_FLOAT(anglef); } p.anglex = 0.0f; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index b6b188231..7338a232b 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -796,6 +796,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) case SKINCOLOR_GREY: case SKINCOLOR_NICKEL: case SKINCOLOR_BLACK: + case SKINCOLOR_SKUNK: case SKINCOLOR_PLATINUM: case SKINCOLOR_JET: cstart = "\x86"; // V_GRAYMAP @@ -811,11 +812,11 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) cstart = "\x8e"; // V_BROWNMAP break; case SKINCOLOR_FAIRY: - case SKINCOLOR_SALMON: case SKINCOLOR_PINK: case SKINCOLOR_ROSE: case SKINCOLOR_LEMONADE: case SKINCOLOR_LILAC: + case SKINCOLOR_BLOSSOM: case SKINCOLOR_TAFFY: cstart = "\x8d"; // V_PINKMAP break; @@ -866,7 +867,6 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) case SKINCOLOR_PISTACHIO: case SKINCOLOR_MOSS: case SKINCOLOR_CAMOUFLAGE: - case SKINCOLOR_ROBOHOOD: case SKINCOLOR_MINT: case SKINCOLOR_GREEN: case SKINCOLOR_PINETREE: @@ -878,14 +878,13 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) case SKINCOLOR_ALGAE: cstart = "\x83"; // V_GREENMAP break; - case SKINCOLOR_CARIBBEAN: - case SKINCOLOR_AZURE: case SKINCOLOR_AQUAMARINE: case SKINCOLOR_TURQUOISE: case SKINCOLOR_TEAL: cstart = "\x8b"; // V_AQUAMAP break; case SKINCOLOR_PIGEON: + case SKINCOLOR_ROBIN: case SKINCOLOR_CYAN: case SKINCOLOR_JAWZ: case SKINCOLOR_CERULEAN: @@ -897,6 +896,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) case SKINCOLOR_ULTRAMARINE: case SKINCOLOR_PERIWINKLE: case SKINCOLOR_BLUE: + case SKINCOLOR_MIDNIGHT: case SKINCOLOR_BLUEBERRY: case SKINCOLOR_NOVA: cstart = "\x84"; // V_BLUEMAP @@ -2510,7 +2510,7 @@ Ping_gfx_num (int ping) void HU_drawPing(INT32 x, INT32 y, UINT32 ping, INT32 flags) { INT32 gfxnum; // gfx to draw - UINT8 const *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_SALMON, GTC_CACHE); + UINT8 const *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_RASPBERRY, GTC_CACHE); gfxnum = Ping_gfx_num(ping); @@ -3095,7 +3095,7 @@ static void HU_DrawRankings(void) if (G_RaceGametype()) { if (circuitmap) - tab[scorelines].count = players[i].laps+1; + tab[scorelines].count = players[i].laps; else tab[scorelines].count = players[i].realtime; } diff --git a/src/info.c b/src/info.c index 2b1d8f545..6c695e705 100644 --- a/src/info.c +++ b/src/info.c @@ -6154,7 +6154,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate - SKINCOLOR_SALMON, // painchance + SKINCOLOR_RASPBERRY, // painchance sfx_s3kb1, // painsound S_NULL, // meleestate S_NULL, // missilestate @@ -6262,7 +6262,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate - SKINCOLOR_SALMON, // painchance + SKINCOLOR_RASPBERRY, // painchance sfx_s3kb1, // painsound S_NULL, // meleestate S_NULL, // missilestate @@ -6370,7 +6370,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate - SKINCOLOR_SALMON, // painchance + SKINCOLOR_RASPBERRY, // painchance sfx_s3kb1, // painsound S_NULL, // meleestate S_NULL, // missilestate diff --git a/src/k_bot.c b/src/k_bot.c index f9173aabd..c585c5790 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1,3 +1,14 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_bot.c +/// \brief Bot logic & ticcmd generation code + #include "doomdef.h" #include "d_player.h" #include "g_game.h" @@ -15,6 +26,12 @@ #include "m_random.h" #include "r_things.h" // numskins + +/*-------------------------------------------------- + boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p) + + See header file for description. +--------------------------------------------------*/ boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p) { UINT8 buf[3]; @@ -81,6 +98,11 @@ boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p) return true; } +/*-------------------------------------------------- + void K_UpdateMatchRaceBots(void) + + See header file for description. +--------------------------------------------------*/ void K_UpdateMatchRaceBots(void) { const UINT8 difficulty = cv_kartbot.value; @@ -89,6 +111,7 @@ void K_UpdateMatchRaceBots(void) UINT8 numbots = 0; UINT8 numwaiting = 0; SINT8 wantedbots = 0; + boolean skinusable[MAXSKINS]; UINT8 i; if (!server) @@ -96,38 +119,57 @@ void K_UpdateMatchRaceBots(void) return; } - if (difficulty != 0) + // init usable bot skins list + for (i = 0; i < MAXSKINS; i++) { - if (cv_ingamecap.value > 0) + if (i < numskins) { - pmax = min(pmax, cv_ingamecap.value); + skinusable[i] = true; } - - for (i = 0; i < MAXPLAYERS; i++) + else { - if (playeringame[i]) - { - if (!players[i].spectator) - { - if (players[i].bot) - { - numbots++; + skinusable[i] = false; + } + } - // While we're here, we should update bot difficulty to the proper value. - players[i].botvars.difficulty = difficulty; - } - else - { - numplayers++; - } - } - else if (players[i].pflags & PF_WANTSTOJOIN) + if (cv_ingamecap.value > 0) + { + pmax = min(pmax, cv_ingamecap.value); + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (!players[i].spectator) + { + skinusable[players[i].skin] = false; + + if (players[i].bot) { - numwaiting++; + numbots++; + + // While we're here, we should update bot difficulty to the proper value. + players[i].botvars.difficulty = difficulty; + } + else + { + numplayers++; } } + else if (players[i].pflags & PF_WANTSTOJOIN) + { + numwaiting++; + } } + } + if (difficulty == 0) + { + wantedbots = 0; + } + else + { wantedbots = pmax - numplayers - numwaiting; if (wantedbots < 0) @@ -135,15 +177,12 @@ void K_UpdateMatchRaceBots(void) wantedbots = 0; } } - else - { - wantedbots = 0; - } if (numbots < wantedbots) { // We require MORE bots! UINT8 newplayernum = 0; + boolean usedallskins = false; if (dedicated) { @@ -152,12 +191,39 @@ void K_UpdateMatchRaceBots(void) while (numbots < wantedbots) { - if (!K_AddBot(M_RandomKey(numskins), difficulty, &newplayernum)) + UINT8 skin = M_RandomKey(numskins); + + if (usedallskins == false) + { + UINT8 loops = 0; + + while (!skinusable[skin]) + { + if (loops >= numskins) + { + // no more skins, stick to our first choice + usedallskins = true; + break; + } + + skin++; + + if (skin >= numskins) + { + skin = 0; + } + + loops++; + } + } + + if (!K_AddBot(skin, difficulty, &newplayernum)) { // Not enough player slots to add the bot, break the loop. break; } + skinusable[skin] = false; numbots++; } } @@ -185,6 +251,11 @@ void K_UpdateMatchRaceBots(void) // We should have enough bots now :) } +/*-------------------------------------------------- + boolean K_PlayerUsesBotMovement(player_t *player) + + See header file for description. +--------------------------------------------------*/ boolean K_PlayerUsesBotMovement(player_t *player) { if (player->bot || player->exiting) @@ -193,6 +264,11 @@ boolean K_PlayerUsesBotMovement(player_t *player) return false; } +/*-------------------------------------------------- + boolean K_BotCanTakeCut(player_t *player) + + See header file for description. +--------------------------------------------------*/ boolean K_BotCanTakeCut(player_t *player) { if (!K_ApplyOffroad(player) @@ -205,6 +281,18 @@ boolean K_BotCanTakeCut(player_t *player) return false; } +/*-------------------------------------------------- + static UINT32 K_BotRubberbandDistance(player_t *player) + + Calculates the distance away from 1st place that the + bot should rubberband to. + + Input Arguments:- + player - Player to compare. + + Return:- + Distance to add, as an integer. +--------------------------------------------------*/ static UINT32 K_BotRubberbandDistance(player_t *player) { const UINT32 spacing = FixedDiv(512 * FRACUNIT, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; @@ -246,6 +334,11 @@ static UINT32 K_BotRubberbandDistance(player_t *player) return (pos * spacing); } +/*-------------------------------------------------- + fixed_t K_BotRubberband(player_t *player) + + See header file for description. +--------------------------------------------------*/ fixed_t K_BotRubberband(player_t *player) { fixed_t rubberband = FRACUNIT; @@ -259,15 +352,18 @@ fixed_t K_BotRubberband(player_t *player) for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator || players[i].exiting) + if (!playeringame[i] || players[i].spectator) { continue; } - /*if (players[i].bot) +#if 0 + // Only rubberband up to players. + if (players[i].bot) { continue; - }*/ + } +#endif if (firstplace == NULL || players[i].distancetofinish < firstplace->distancetofinish) { @@ -304,7 +400,12 @@ fixed_t K_BotRubberband(player_t *player) return rubberband; } -static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) +/*-------------------------------------------------- + fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) + + See header file for description. +--------------------------------------------------*/ +fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) { fixed_t v1toc[2] = {cx - v1x, cy - v1y}; fixed_t v1tov2[2] = {v2x - v1x, v2y - v1y}; @@ -328,336 +429,44 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, return P_AproxDistance(cx - px, cy - py); } -static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) -{ - switch (GETSECSPECIAL(sec->special, 1)) - { - case 1: // Damage - case 5: // Spikes - case 6: case 7: // Death Pit - case 8: // Instant Kill - return true; - //case 2: case 3: // Offroad (let's let them lawnmower) - case 4: // Offroad (Strong) - if (!K_BotCanTakeCut(player)) - return true; - default: - break; - } +/*-------------------------------------------------- + static botprediction_t *K_CreateBotPrediction(player_t *player) - return false; -} + Calculates a point further along the track to attempt to drive towards. -static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) -{ - const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP); - INT32 flag; - ffloor_t *rover; - - if (flip) - { - flag = SF_FLIPSPECIAL_CEILING; - } - else - { - flag = SF_FLIPSPECIAL_FLOOR; - } - - if (sec->flags & flag) - { - if (K_BotHatesThisSectorsSpecial(player, sec)) - { - return true; - } - } - - for (rover = sec->ffloors; rover; rover = rover->next) - { - if (!(rover->flags & FF_EXISTS)) - { - continue; - } - - if (!(rover->master->frontsector->flags & flag)) - { - continue; - } - - if (((*rover->bottomheight >= player->mo->z + player->mo->height) && (flip)) - || ((*rover->topheight <= player->mo->z) && (!flip))) - { - if (K_BotHatesThisSectorsSpecial(player, sec)) - { - return true; - } - } - } - - return false; -} - -mobj_t *botmo = NULL; -fixed_t distancetocheck = 0; - -fixed_t closestlinedist = INT32_MAX; - -INT16 badsteerglobal = 0; - -fixed_t eggboxx, eggboxy; -UINT8 randomitems = 0; -UINT8 eggboxes = 0; - -static boolean K_FindRandomItems(mobj_t *thing) -{ - fixed_t dist; - - if (thing->type != MT_RANDOMITEM) - { - return true; - } - - if (!thing->health) - { - return true; - } - - dist = P_AproxDistance(thing->x - eggboxx, thing->y - eggboxy); - - if (dist > distancetocheck) - { - return true; - } - - randomitems++; - return true; -} - -static boolean K_FindEggboxes(mobj_t *thing) -{ - fixed_t dist; - - if (thing->type != MT_EGGMANITEM) - { - return true; - } - - if (!thing->health) - { - return true; - } - - dist = P_AproxDistance(thing->x - eggboxx, thing->y - eggboxy); - - if (dist > distancetocheck) - { - return true; - } - - eggboxes++; - return true; -} - -static UINT8 K_EggboxStealth(fixed_t x, fixed_t y) -{ - INT32 xl, xh, yl, yh, bx, by; - - eggboxx = x; - eggboxy = y; - distancetocheck = (mapobjectscale * 256); - randomitems = 0; - eggboxes = 0; - - xl = (unsigned)(eggboxx - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(eggboxx + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(eggboxy - distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(eggboxy + distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - - BMBOUNDFIX(xl, xh, yl, yh); - - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockThingsIterator(bx, by, K_FindRandomItems); - } - } - - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockThingsIterator(bx, by, K_FindEggboxes); - } - } - - return randomitems * eggboxes; -} - -static inline boolean K_FindBlockingWalls(line_t *line) -{ - // Condensed version of PIT_CheckLine - const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); - fixed_t maxstep = maxstepmove; - fixed_t linedist = INT32_MAX; - INT32 lineside = 0; - - if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) - { - return false; - } - - if (line->polyobj && !(line->polyobj->flags & POF_SOLID)) - { - return true; - } - - if (tmbbox[BOXRIGHT] <= line->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= line->bbox[BOXRIGHT] - || tmbbox[BOXTOP] <= line->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= line->bbox[BOXTOP]) - { - return true; - } - - if (P_BoxOnLineSide(tmbbox, line) != -1) - { - return true; - } - - lineside = P_PointOnLineSide(botmo->x, botmo->y, line); - - // one sided line - if (!line->backsector) - { - if (lineside) - { - // don't hit the back side - return true; - } - - goto blocked; - } - - if ((line->flags & ML_IMPASSABLE) || (line->flags & ML_BLOCKPLAYERS)) - { - goto blocked; - } - - // set openrange, opentop, openbottom - P_LineOpening(line, botmo); - - if (botmo->player->kartstuff[k_waterskip]) - maxstep += maxstepmove; - - if (P_MobjTouchingSectorSpecial(botmo, 1, 13, false)) - maxstep <<= 1; - else if (P_MobjTouchingSectorSpecial(botmo, 1, 12, false)) - maxstep = 0; - - if ((openrange < botmo->height) // doesn't fit - || (opentop - botmo->z < botmo->height) // mobj is too high - || (openbottom - botmo->z > maxstep)) // too big a step up - { - goto blocked; - } - - if (!K_BotHatesThisSector(botmo->player, botmo->subsector->sector)) - { - // Treat damage sectors like walls - - if (lineside) - { - if (K_BotHatesThisSector(botmo->player, line->frontsector)) - goto blocked; - } - else - { - if (K_BotHatesThisSector(botmo->player, line->backsector)) - goto blocked; - } - } - - // We weren't blocked! - return true; - -blocked: - linedist = K_DistanceOfLineFromPoint(line->v1->x, line->v1->y, line->v2->x, line->v2->y, botmo->x, botmo->y); - linedist -= (botmo->radius * 8); // Maintain a reasonable distance away from it - - if (linedist > distancetocheck) - { - return true; - } - - if (linedist <= 0) - { - closestlinedist = 0; - return false; - } - - if (linedist < closestlinedist) - { - closestlinedist = linedist; - } - - return true; -} - -static fixed_t K_BotReducePrediction(player_t *player) -{ - INT32 xl, xh, yl, yh, bx, by; - - botmo = player->mo; - distancetocheck = player->mo->radius * 16; - closestlinedist = INT32_MAX; - - tmx = player->mo->x; - tmy = player->mo->y; - - xl = (unsigned)(tmx - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(tmx + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(tmy - distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(tmy + distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - - BMBOUNDFIX(xl, xh, yl, yh); - - tmbbox[BOXTOP] = tmy + distancetocheck; - tmbbox[BOXBOTTOM] = tmy - distancetocheck; - tmbbox[BOXRIGHT] = tmx + distancetocheck; - tmbbox[BOXLEFT] = tmx - distancetocheck; - - // Check for lines that the bot might collide with - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockLinesIterator(bx, by, K_FindBlockingWalls); - } - } - - if (closestlinedist == INT32_MAX) - { - return FRACUNIT; - } - - return FixedDiv(closestlinedist, distancetocheck); -} + Input Arguments:- + player - Player to compare. + Return:- + Bot prediction struct. +--------------------------------------------------*/ static botprediction_t *K_CreateBotPrediction(player_t *player) { const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to - const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict + const fixed_t distreduce = K_BotReducePrediction(player); const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); - const INT32 distance = (FixedMul(player->speed, distreduce) / FRACUNIT) * futuresight; - INT32 distanceleft = distance; + + const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict + const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy); + const INT32 distance = (FixedMul(speed, distreduce) / FRACUNIT) * futuresight; + botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); waypoint_t *wp = player->nextwaypoint; + + INT32 distanceleft = distance; fixed_t smallestradius = INT32_MAX; + angle_t angletonext = ANGLE_MAX; + size_t nwp; size_t i; + // Reduce distance left by your distance to the starting waypoint. + // This prevents looking too far ahead if the closest waypoint is really far away. distanceleft -= P_AproxDistance(player->mo->x - wp->mobj->x, player->mo->y - wp->mobj->y) / FRACUNIT; + // We don't want to look ahead at all, just go to the first waypoint. if (distanceleft <= 0) { predict->x = wp->mobj->x; @@ -666,610 +475,108 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) return predict; } + angletonext = R_PointToAngle2( + player->mo->x, player->mo->y, + wp->mobj->x, wp->mobj->y + ); + + // Go through waypoints until we've traveled the distance we wanted to predict ahead! while (distanceleft > 0) { + INT32 disttonext = INT32_MAX; + if (wp->mobj->radius < smallestradius) { smallestradius = wp->mobj->radius; } - nwp = 0; - if (wp->numnextwaypoints == 0) { + // Well, this is where I get off. distanceleft = 0; break; } + // Calculate nextwaypoints index to use + // nextwaypoints[0] by default + nwp = 0; + + // There are multiple nextwaypoints, + // so we need to find the most convenient one to us. + // Let's compare the angle to the player's! if (wp->numnextwaypoints > 1) { - fixed_t closest = INT32_MAX; - fixed_t dist = INT32_MAX; + angle_t delta = ANGLE_MAX; + angle_t a = ANGLE_MAX; for (i = 0; i < wp->numnextwaypoints; i++) { + if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) && !K_BotCanTakeCut(player)) { continue; } - dist = P_AproxDistance( - player->mo->x - wp->nextwaypoints[i]->mobj->x, - player->mo->y - wp->nextwaypoints[i]->mobj->y + // Unlike the other parts of this function, we're comparing the player's physical position, NOT the position of the waypoint!! + // This should roughly correspond with how players will think about path splits. + a = R_PointToAngle2( + player->mo->x, player->mo->y, + wp->nextwaypoints[i]->mobj->x, wp->nextwaypoints[i]->mobj->y ); + if (a > ANGLE_180) + { + a = InvAngle(a); + } - if (dist < closest) + a = player->mo->angle - a; + + if (a < delta) { nwp = i; - closest = dist; + delta = a; } } } + + angletonext = R_PointToAngle2( + wp->mobj->x, wp->mobj->y, + wp->nextwaypoints[nwp]->mobj->x, wp->nextwaypoints[nwp]->mobj->y + ); - if ((INT32)(wp->nextwaypointdistances[nwp]) > distanceleft) + disttonext = (INT32)wp->nextwaypointdistances[nwp]; + + if (disttonext > distanceleft) { break; } - distanceleft -= wp->nextwaypointdistances[nwp]; + distanceleft -= disttonext; wp = wp->nextwaypoints[nwp]; } + // Set our predicted point's coordinates, + // and use the smallest radius of all of the waypoints in the chain! predict->x = wp->mobj->x; predict->y = wp->mobj->y; predict->radius = FixedMul(smallestradius, radreduce); + // Set the prediction coordinates between the 2 waypoints if there's still distance left. if (distanceleft > 0) { - angle_t a = R_PointToAngle2(wp->mobj->x, wp->mobj->y, wp->nextwaypoints[nwp]->mobj->x, wp->nextwaypoints[nwp]->mobj->y); - - predict->x += P_ReturnThrustX(NULL, a, distanceleft * FRACUNIT); - predict->y += P_ReturnThrustY(NULL, a, distanceleft * FRACUNIT); + // Scaled with the leftover anglemul! + predict->x += P_ReturnThrustX(NULL, angletonext, distanceleft * FRACUNIT); + predict->y += P_ReturnThrustY(NULL, angletonext, distanceleft * FRACUNIT); } return predict; } -static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount) -{ - angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); - angle_t angle; - - amount = (amount * FixedDiv(distancetocheck - fulldist, distancetocheck)) / FRACUNIT; - - if (towards) - { - if (xdist < (bot->radius + thing->radius)) - { - // Don't need to turn any harder! - return; - } - - amount = -amount; - } - - angle = (bot->angle - destangle); - - if (angle < ANGLE_180) - { - badsteerglobal -= amount; - } - else - { - badsteerglobal += amount; - } -} - -static boolean K_BotSteerObjects(mobj_t *thing) -{ - INT16 anglediff; - fixed_t xdist, ydist, fulldist; - angle_t destangle, angle; - INT16 attack = ((9 - botmo->player->kartspeed) * KART_FULLTURN) / 8; // Acceleration chars are more aggressive - INT16 dodge = ((9 - botmo->player->kartweight) * KART_FULLTURN) / 8; // Handling chars dodge better - - if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) - { - return false; - } - - if (!thing->health) - { - return true; - } - - if (botmo == thing) - { - return true; - } - - xdist = K_DistanceOfLineFromPoint( - botmo->x, botmo->y, - botmo->x + FINECOSINE(botmo->angle >> ANGLETOFINESHIFT), botmo->y + FINESINE(botmo->angle >> ANGLETOFINESHIFT), - thing->x, thing->y - ) / 2; // weight x dist more heavily than y dist - - ydist = K_DistanceOfLineFromPoint( - botmo->x, botmo->y, - botmo->x + FINECOSINE((botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT), botmo->y + FINESINE((botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT), - thing->x, thing->y - ); - - fulldist = FixedHypot(xdist, ydist); - - if (fulldist > distancetocheck) - { - return true; - } - - if (!P_CheckSight(botmo, thing)) - { - return true; - } - - destangle = R_PointToAngle2(botmo->x, botmo->y, thing->x, thing->y); - angle = (botmo->angle - destangle); - - if (angle < ANGLE_180) - { - anglediff = AngleFixed(angle)>>FRACBITS; - } - else - { - anglediff = 360-(AngleFixed(angle)>>FRACBITS); - } - - anglediff = abs(anglediff); - -#define PlayerAttackSteer(botcond, thingcond) \ - if ((botcond) && !(thingcond)) \ - { \ - K_SteerFromObject(botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); \ - } \ - else if ((thingcond) && !(botcond)) \ - { \ - K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); \ - } - - switch (thing->type) - { - case MT_BANANA: - case MT_BANANA_SHIELD: - case MT_EGGMANITEM_SHIELD: - case MT_ORBINAUT: - case MT_ORBINAUT_SHIELD: - case MT_JAWZ: - case MT_JAWZ_DUD: - case MT_JAWZ_SHIELD: - case MT_SSMINE: - case MT_SSMINE_SHIELD: - case MT_BALLHOG: - case MT_SPB: - case MT_BUBBLESHIELDTRAP: - K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); - break; - case MT_RANDOMITEM: - if (anglediff > 90) - { - break; - } - - if (P_CanPickupItem(botmo->player, 1)) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); - } - break; - case MT_EGGMANITEM: - if (anglediff > 90) - { - break; - } - - if (P_CanPickupItem(botmo->player, 1)) // Can pick up an actual item - { - const UINT8 stealth = K_EggboxStealth(thing->x, thing->y); - const UINT8 requiredstealth = (botmo->player->botvars.difficulty * botmo->player->botvars.difficulty); - - if (stealth >= requiredstealth) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); - } - else - { - K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); - } - } - break; - case MT_FLOATINGITEM: - if (anglediff > 90) - { - break; - } - - if (P_CanPickupItem(botmo->player, 3)) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); - } - break; - case MT_RING: - case MT_FLINGRING: - if (anglediff > 90) - { - break; - } - - if ((RINGTOTAL(botmo->player) < 20 && !botmo->player->kartstuff[k_ringlock] - && P_CanPickupItem(botmo->player, 0)) - && !thing->extravalue1 - && (botmo->player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD)) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, - (RINGTOTAL(botmo->player) < 3 - ? (2 * (KART_FULLTURN + attack)) - : ((KART_FULLTURN + attack) / 2)) - ); - } - break; - case MT_PLAYER: - if (thing->player - && !thing->player->kartstuff[k_hyudorotimer] - && !botmo->player->kartstuff[k_hyudorotimer]) - { - // There REALLY ought to be a better way to handle this logic, right?! - // Squishing - PlayerAttackSteer( - botmo->scale > thing->scale + (mapobjectscale/8), - thing->scale > botmo->scale + (mapobjectscale/8) - ) - // Invincibility - else PlayerAttackSteer( - botmo->player->kartstuff[k_invincibilitytimer], - thing->player->kartstuff[k_invincibilitytimer] - ) - // Thunder Shield - else PlayerAttackSteer( - botmo->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD, - thing->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD - ) - // Bubble Shield - else PlayerAttackSteer( - botmo->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD, - thing->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD - ) - // Flame Shield - else PlayerAttackSteer( - botmo->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD, - thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD - ) - // Has held item shield - else PlayerAttackSteer( - (botmo->player->kartstuff[k_itemheld] || botmo->player->kartstuff[k_eggmanheld]), - (thing->player->kartstuff[k_itemheld] || thing->player->kartstuff[k_eggmanheld]) - ) - // Ring Sting - else PlayerAttackSteer( - thing->player->kartstuff[k_rings] <= 0, - botmo->player->kartstuff[k_rings] <= 0 - ) - else - { - // After ALL of that, we can do standard bumping - fixed_t ourweight = K_GetMobjWeight(botmo, thing); - fixed_t theirweight = K_GetMobjWeight(thing, botmo); - fixed_t weightdiff = 0; - - if (anglediff > 90) - { - weightdiff = theirweight - ourweight; - } - else - { - weightdiff = ourweight - theirweight; - } - - if (weightdiff > mapobjectscale) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); - } - else - { - K_SteerFromObject(botmo, thing, fulldist, xdist, false, KART_FULLTURN + dodge); - } - } - } - break; - case MT_BOTHINT: - if (thing->extravalue1 == 0) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, false, thing->extravalue2 * (KART_FULLTURN + dodge)); - } - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, thing->extravalue2 * (KART_FULLTURN + attack)); - } - break; - default: - if (thing->flags & (MF_SOLID|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE)) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); - } - break; - } - - return true; -} - -static INT16 K_BotFindObjects(player_t *player) -{ - INT32 xl, xh, yl, yh, bx, by; - - badsteerglobal = 0; - - botmo = player->mo; - distancetocheck = (player->mo->radius * 32) + (player->speed * 4); - - xl = (unsigned)(botmo->x - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(botmo->x + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(botmo->y - distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(botmo->y + distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - - BMBOUNDFIX(xl, xh, yl, yh); - - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockThingsIterator(bx, by, K_BotSteerObjects); - } - } - - return badsteerglobal; -} - -static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t radius) -{ - UINT8 i; - - if (player->pflags & PF_ATTACKDOWN) - { - return false; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->powers[pw_flashing]) - { - continue; - } - - dist = P_AproxDistance(P_AproxDistance( - player->mo->x - target->mo->x, - player->mo->y - target->mo->y), - (player->mo->z - target->mo->z) / 4 - ); - - if (dist <= radius) - { - cmd->buttons |= BT_ATTACK; - return true; - } - } - - return false; -} - -static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius) -{ - UINT8 i; - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->powers[pw_flashing]) - { - continue; - } - - dist = P_AproxDistance( - x - target->mo->x, - y - target->mo->y - ); - - if (dist <= radius) - { - return true; - } - } - - return false; -} - -static boolean K_BotRevealsBanana(player_t *player, INT16 turnamt, boolean mine) -{ - UINT8 i; - - // Only get out bananas if you have a target - - if (abs(turnamt) >= KART_FULLTURN/2) - { - return false; - } - else - { - UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); - fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - return true; - } - - if (mine) - { - airtime = FixedDiv((40 * player->mo->scale) + player->mo->momz, gravity); - throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) * 2; - estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - return true; - } - } - } - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->powers[pw_flashing] - || !P_CheckSight(player->mo, target->mo)) - { - continue; - } - - dist = P_AproxDistance(P_AproxDistance( - player->mo->x - target->mo->x, - player->mo->y - target->mo->y), - (player->mo->z - target->mo->z) / 4 - ); - - if (dist <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - return true; - } - } - } - - return false; -} - -static boolean K_BotRevealsEggbox(player_t *player) -{ - const UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); - const fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - const fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - const fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); - UINT8 i; - - if (stealth > 1) - { - return true; - } - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - return true; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->powers[pw_flashing] - || !P_CheckSight(player->mo, target->mo)) - { - continue; - } - - dist = P_AproxDistance(P_AproxDistance( - player->mo->x - target->mo->x, - player->mo->y - target->mo->y), - (player->mo->z - target->mo->z) / 4 - ); - - if (dist <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - return true; - } - } - } - - return false; -} +/*-------------------------------------------------- + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) + See header file for description. +--------------------------------------------------*/ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { botprediction_t *predict = NULL; @@ -1306,11 +613,19 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Start boost handler if (leveltime <= starttime) { - if (leveltime >= starttime-35) + tic_t boosthold = starttime - TICRATE; + + boosthold -= (MAXBOTDIFFICULTY - player->botvars.difficulty); + + if (leveltime >= boosthold) + { cmd->buttons |= BT_ACCELERATE; + } + return; } + // Handle steering towards waypoints! if (player->nextwaypoint != NULL && player->nextwaypoint->mobj != NULL && !P_MobjWasRemoved(player->nextwaypoint->mobj)) { SINT8 turnsign = 0; @@ -1377,18 +692,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Full speed ahead! cmd->forwardmove = 50; - if (anglediff > 60) - { - // Actually, don't go too fast... - cmd->forwardmove /= 2; - cmd->buttons |= BT_BRAKE; - } - else if (dirdist <= realrad) - { - // Steer towards/away from objects! - turnamt += K_BotFindObjects(player); - } - if (dirdist <= rad) { fixed_t speedmul = FixedMul(player->speed, K_GetKartSpeed(player, false)); @@ -1420,731 +723,26 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt /= 4; } } - } - } - if (player->kartstuff[k_userings] == 1) - { - if (!player->exiting) - { - INT32 saferingsval = 16 - K_GetKartRingPower(player); - - 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) - { - saferingsval -= 5; - } - - if (player->kartstuff[k_rings] > saferingsval) - { - cmd->buttons |= BT_ATTACK; - } - } - } - else if (player->kartstuff[k_itemroulette] && !(player->pflags & PF_ATTACKDOWN)) - { - // Mashing behaviors - - if (player->kartstuff[k_rings] < 0 && cv_superring.value) - { - // Uh oh, we need a loan! - // It'll be better in the long run for bots to lose an item set for 10 free rings. - cmd->buttons |= BT_ATTACK; - } - } - else - { - if (player->botvars.itemdelay) - { - player->botvars.itemdelay--; - player->botvars.itemconfirm = 0; - } - else if (player->kartstuff[k_eggmanexplode]) - { - if (player->kartstuff[k_position] == 1) + if (anglediff > 60) { + // Actually, don't go too fast... cmd->forwardmove /= 2; cmd->buttons |= BT_BRAKE; } - - K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale); - } - else if (player->kartstuff[k_eggmanheld]) - { - const UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); - const fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - const fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - const fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); - SINT8 throwdir = -1; - UINT8 i; - - player->botvars.itemconfirm++; - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) + else if (dirdist <= realrad) { - player->botvars.itemconfirm += player->botvars.difficulty / 2; - throwdir = 1; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->powers[pw_flashing] - || !P_CheckSight(player->mo, target->mo)) - { - continue; - } - - dist = P_AproxDistance(P_AproxDistance( - player->mo->x - target->mo->x, - player->mo->y - target->mo->y), - (player->mo->z - target->mo->z) / 4 - ); - - if (dist <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; - } - } - } - - if (stealth > 1) - { - player->botvars.itemconfirm += player->botvars.difficulty * 4; - throwdir = -1; - } - - if (player->kartstuff[k_itemroulette] > 0) // Just grabbed an item - { - player->botvars.itemconfirm += player->botvars.difficulty * 4; - throwdir = -1; - } - - if ((player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - else if (player->kartstuff[k_rocketsneakertimer] > 0) - { - if (player->botvars.itemconfirm > TICRATE) - { - if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - else - { - player->botvars.itemconfirm++; - } - } - else if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0) - { - switch (player->kartstuff[k_itemtype]) - { - case KITEM_INVINCIBILITY: - case KITEM_SPB: - case KITEM_GROW: - case KITEM_SHRINK: - case KITEM_HYUDORO: - case KITEM_SUPERRING: - if (!(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - break; - case KITEM_SNEAKER: - if ((player->kartstuff[k_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)/2 // Being slowed down too much - || player->kartstuff[k_speedboost] > (FRACUNIT/8) // Have another type of boost (tethering) - || player->botvars.itemconfirm > 4*TICRATE) // Held onto it for too long - { - if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 2*TICRATE; - } - } - else - { - player->botvars.itemconfirm++; - } - break; - case KITEM_ROCKETSNEAKER: - if (player->kartstuff[k_rocketsneakertimer] <= 0 && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - break; - case KITEM_BANANA: - if (!player->kartstuff[k_itemheld]) - { - if ((K_BotRevealsBanana(player, turnamt, false) || (player->botvars.itemconfirm++ > 5*TICRATE)) - && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - else - { - SINT8 throwdir = -1; - UINT8 i; - - player->botvars.itemconfirm++; - - if (abs(turnamt) >= KART_FULLTURN/2) - { - player->botvars.itemconfirm += player->botvars.difficulty / 2; - } - else - { - const UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); - const fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - const fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - const fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - player->botvars.itemconfirm += player->botvars.difficulty * 2; - throwdir = 1; - } - } - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->powers[pw_flashing] - || !P_CheckSight(player->mo, target->mo)) - { - continue; - } - - dist = P_AproxDistance(P_AproxDistance( - player->mo->x - target->mo->x, - player->mo->y - target->mo->y), - (player->mo->z - target->mo->z) / 4 - ); - - if (dist <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; - } - } - } - - if ((player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_EGGMAN: - if (!player->kartstuff[k_eggmanheld]) - { - if ((K_BotRevealsEggbox(player) || (player->botvars.itemconfirm++ > 20*TICRATE)) - && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_ORBINAUT: - if (!player->kartstuff[k_itemheld] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) - /* FALL-THRU */ - case KITEM_BALLHOG: - { - const fixed_t topspeed = K_GetKartSpeed(player, false); - fixed_t radius = (player->mo->radius * 32); - SINT8 throwdir = -1; - UINT8 i; - - if (player->speed > topspeed) - { - radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); - } - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->powers[pw_flashing] - || !P_CheckSight(player->mo, target->mo)) - { - continue; - } - - dist = P_AproxDistance(P_AproxDistance( - player->mo->x - target->mo->x, - player->mo->y - target->mo->y), - (player->mo->z - target->mo->z) / 4 - ); - - if (dist <= radius) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad <= cone) - { - player->botvars.itemconfirm += player->botvars.difficulty * 2; - throwdir = 1; - } - else if (ad >= 180-cone) - { - player->botvars.itemconfirm += player->botvars.difficulty; - } - } - } - - if ((player->botvars.itemconfirm > 5*TICRATE) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_JAWZ: - if (!player->kartstuff[k_itemheld] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) - { - SINT8 throwdir = 1; - UINT8 i; - - player->botvars.itemconfirm++; - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->powers[pw_flashing] - || !P_CheckSight(player->mo, target->mo)) - { - continue; - } - - dist = P_AproxDistance(P_AproxDistance( - player->mo->x - target->mo->x, - player->mo->y - target->mo->y), - (player->mo->z - target->mo->z) / 4 - ); - - if (dist <= (player->mo->radius * 32)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; - } - } - } - - if (player->kartstuff[k_lastjawztarget] != -1) - { - player->botvars.itemconfirm += player->botvars.difficulty * 2; - throwdir = 1; - } - - if ((player->botvars.itemconfirm > 5*TICRATE) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_MINE: - if (!player->kartstuff[k_itemheld]) - { - if ((K_BotRevealsBanana(player, turnamt, true) || (player->botvars.itemconfirm++ > 5*TICRATE)) - && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - else - { - SINT8 throwdir = 0; - UINT8 i; - - player->botvars.itemconfirm++; - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->powers[pw_flashing] - || !P_CheckSight(player->mo, target->mo)) - { - continue; - } - - dist = P_AproxDistance(P_AproxDistance( - player->mo->x - target->mo->x, - player->mo->y - target->mo->y), - (player->mo->z - target->mo->z) / 4 - ); - - if (dist <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; - } - } - } - - if (abs(turnamt) >= KART_FULLTURN/2) - { - player->botvars.itemconfirm += player->botvars.difficulty / 2; - throwdir = -1; - } - else - { - UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); - fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - player->botvars.itemconfirm += player->botvars.difficulty * 2; - throwdir = 0; - } - - airtime = FixedDiv((40 * player->mo->scale) + player->mo->momz, gravity); - throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) * 2; - estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); - esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - player->botvars.itemconfirm += player->botvars.difficulty / 2; - throwdir = 1; - } - } - - if (((player->botvars.itemconfirm > 2*TICRATE) - || (player->kartstuff[k_bananadrag] >= TICRATE)) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_THUNDERSHIELD: - if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) - { - if (player->botvars.itemconfirm > 10*TICRATE && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - else - { - player->botvars.itemconfirm++; - } - } - break; - case KITEM_BUBBLESHIELD: - { - boolean hold = false; - - if (player->kartstuff[k_bubbleblowup] <= 0) - { - UINT8 i; - - player->botvars.itemconfirm++; - - if (player->kartstuff[k_bubblecool] <= 0) - { - const fixed_t radius = 192 * player->mo->scale; - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->powers[pw_flashing]) - { - continue; - } - - dist = P_AproxDistance(P_AproxDistance( - player->mo->x - target->mo->x, - player->mo->y - target->mo->y), - (player->mo->z - target->mo->z) / 4 - ); - - if (dist <= radius) - { - hold = true; - break; - } - } - } - } - else if (player->kartstuff[k_bubbleblowup] >= bubbletime) - { - if (player->botvars.itemconfirm >= 10*TICRATE) - { - hold = true; - } - } - else if (player->kartstuff[k_bubbleblowup] < bubbletime) - { - hold = true; - } - - if (hold && player->kartstuff[k_holdready]) - { - cmd->buttons |= BT_ATTACK; - } - } - break; - case KITEM_FLAMESHIELD: - if (player->botvars.itemconfirm > 0) - { - player->botvars.itemconfirm--; - } - else if (player->kartstuff[k_holdready]) - { - INT32 flamemax = player->kartstuff[k_flamelength] * flameseg; - - if (player->kartstuff[k_flamemeter] < flamemax || flamemax == 0) - { - cmd->buttons |= BT_ATTACK; - } - else - { - player->botvars.itemconfirm = 3*flamemax/4; - } - } - break; - default: - if (player->kartstuff[k_itemtype] != KITEM_NONE && !(player->pflags & PF_ATTACKDOWN)) - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - break; + // Steer towards/away from objects! + turnamt += K_BotFindObjects(player, turnamt); } } } + // Handle item usage + K_BotItemUsage(player, cmd, turnamt); + if (turnamt != 0) { - const INT16 minturn = KART_FULLTURN/2; - if (turnamt > KART_FULLTURN) { turnamt = KART_FULLTURN; @@ -2154,28 +752,30 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt = -KART_FULLTURN; } - if ((turnamt > minturn && player->botvars.lastturn >= 0) - || (turnamt < -minturn && player->botvars.lastturn <= 0)) + if (turnamt > 0) { - if (turnamt > 0) + if (player->botvars.turnconfirm < BOTTURNCONFIRM) { - player->botvars.lastturn = 1; + player->botvars.turnconfirm++; } - else if (turnamt < 0) + } + else if (turnamt < 0) + { + if (player->botvars.turnconfirm > -BOTTURNCONFIRM) { - player->botvars.lastturn = -1; + player->botvars.turnconfirm--; } + } + if (abs(player->botvars.turnconfirm) >= BOTTURNCONFIRM) + { + // You're commiting to your turn, you're allowed! cmd->driftturn = turnamt; cmd->angleturn += K_GetKartTurnValue(player, turnamt); } - else - { - // Can reset turn dir - player->botvars.lastturn = 0; - } } + // Free the prediction we made earlier if (predict != NULL) { Z_Free(predict); diff --git a/src/k_bot.h b/src/k_bot.h index afdbf5ee9..505793ada 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -1,23 +1,215 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_bot.h +/// \brief Bot logic & ticcmd generation code + #ifndef __K_BOT__ #define __K_BOT__ #include "k_waypoint.h" #include "d_player.h" +// Maximum value of botvars.difficulty #define MAXBOTDIFFICULTY 9 -// Path that bot will attempt to take +// How many tics in a row do you need to turn in this direction before we'll let you turn. +// Made it as small as possible without making it look like the bots are twitching constantly. +#define BOTTURNCONFIRM 4 + +// Point for bots to aim for typedef struct botprediction_s { fixed_t x, y; fixed_t radius; - angle_t dir; } botprediction_t; -boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *newplayernum); -void K_UpdateMatchRaceBots(void); + +// AVAILABLE FOR LUA + + +/*-------------------------------------------------- + boolean K_PlayerUsesBotMovement(player_t *player); + + Tells if this player is being controlled via bot movement code (is a bot, or is exiting). + + Input Arguments:- + player - Player to check. + + Return:- + true if using bot movement code, otherwise false. +--------------------------------------------------*/ + boolean K_PlayerUsesBotMovement(player_t *player); + + +/*-------------------------------------------------- + boolean K_BotCanTakeCut(player_t *player); + + Tells if this bot is able to take shortcuts (currently unaffected by offroad, + or has certain items ready). + + Input Arguments:- + player - Player to check. + + Return:- + true if able to take shortcuts, otherwise false. +--------------------------------------------------*/ + boolean K_BotCanTakeCut(player_t *player); + + +/*-------------------------------------------------- + fixed_t K_BotRubberband(player_t *player); + + Gives a multiplier for a bot's rubberbanding. Meant to be used for top speed, + acceleration, and handling. + + Input Arguments:- + player - Player to check. + + Return:- + A multiplier in fixed point scale, between 0.875 and 2.0. +--------------------------------------------------*/ + fixed_t K_BotRubberband(player_t *player); + + +/*-------------------------------------------------- + fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy); + + Gets the distance of a point away from a line. + TODO: Could go in another file? + + Input Arguments:- + v1x - Line's first vertex x position. + v1y - Line's first vertex y position. + v2x - Line's second vertex x position. + v2y - Line's second vertex y position. + cx - Point's x position. + cy - Point's y position. + + Return:- + The distance between the point and the line. +--------------------------------------------------*/ + +fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy); + + +// NOT AVAILABLE FOR LUA + + +/*-------------------------------------------------- + boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *newplayernum); + + Returns the waypoint actually being used as the finish line. + + Input Arguments:- + skin - Skin number that the bot will use. + difficulty - Difficulty level this bot will use. + newplayernum - Pointer to the last valid player slot number. + Is a pointer so that this function can be called multiple times to add more than one bot. + + Return:- + true if a bot packet can be sent, otherwise false. +--------------------------------------------------*/ + +boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *newplayernum); + + +/*-------------------------------------------------- + void K_UpdateMatchRaceBots(void); + + Updates the number of bots in the server and their difficulties for Match Race. +--------------------------------------------------*/ + +void K_UpdateMatchRaceBots(void); + + +/*-------------------------------------------------- + UINT8 K_EggboxStealth(fixed_t x, fixed_t y); + + Gets a "stealth" value for a position, to figure out how + well Eggman boxes blend into random items. + + Input Arguments:- + x - X coordinate to check. + y - Y coordinate to check. + + Return:- + Stealth value for the position. +--------------------------------------------------*/ + +UINT8 K_EggboxStealth(fixed_t x, fixed_t y); + + +/*-------------------------------------------------- + fixed_t K_BotReducePrediction(player_t *player); + + Finds walls nearby the specified player, to create a multiplier + to pull bot predictions back by. + + Input Arguments:- + player - Player to compare. + + Return:- + Multiplier in fixed point scale. +--------------------------------------------------*/ + +fixed_t K_BotReducePrediction(player_t *player); + + +/*-------------------------------------------------- + INT16 K_BotFindObjects(player_t *player, INT16 turn); + + Generates a sum for objects to steer towards/away from. + + Input Arguments:- + player - Player to compare. + turn - Turn value before object steering. + + Return:- + Turn amount sum to add to final product. +--------------------------------------------------*/ + +INT16 K_BotFindObjects(player_t *player, INT16 turn); + + +/*-------------------------------------------------- + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); + + Gives a multiplier for a bot's rubberbanding. Meant to be used for top speed, + acceleration, and handling. + + Input Arguments:- + player - Player to generate the ticcmd for. + cmd - The player's ticcmd to modify. + + Return:- + None +--------------------------------------------------*/ + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); + +/*-------------------------------------------------- + void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt); + + Item usage part of ticcmd generation. + + Input Arguments:- + player - Player to generate the ticcmd for. + cmd - The player's ticcmd to modify. + turnamt - How hard the bot is turning. + + Return:- + None +--------------------------------------------------*/ + +void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt); + #endif diff --git a/src/k_botitem.c b/src/k_botitem.c new file mode 100644 index 000000000..55f8e15e4 --- /dev/null +++ b/src/k_botitem.c @@ -0,0 +1,1095 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_botitem.c +/// \brief Bot item usage logic + +#include "doomdef.h" +#include "d_player.h" +#include "g_game.h" +#include "r_main.h" +#include "p_local.h" +#include "k_bot.h" +#include "lua_hook.h" +#include "byteptr.h" +#include "d_net.h" // nodetoplayer +#include "k_kart.h" +#include "z_zone.h" +#include "i_system.h" +#include "p_maputl.h" +#include "d_ticcmd.h" +#include "m_random.h" +#include "r_things.h" // numskins + +/*-------------------------------------------------- + static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t radius) + + Looks for players around the bot, and presses the item button + if there is one in range. + + Input Arguments:- + player - Bot to compare against. + cmd - The bot's ticcmd. + radius - The radius to look for players in. + + Return:- + true if a player was found & we can press the item button, otherwise false. +--------------------------------------------------*/ +static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t radius) +{ + UINT8 i; + + if (player->pflags & PF_ATTACKDOWN) + { + return false; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *target = NULL; + fixed_t dist = INT32_MAX; + + if (!playeringame[i]) + { + continue; + } + + target = &players[i]; + + if (target->mo == NULL || P_MobjWasRemoved(target->mo) + || player == target || target->spectator + || target->powers[pw_flashing]) + { + continue; + } + + dist = P_AproxDistance(P_AproxDistance( + player->mo->x - target->mo->x, + player->mo->y - target->mo->y), + (player->mo->z - target->mo->z) / 4 + ); + + if (dist <= radius) + { + cmd->buttons |= BT_ATTACK; + return true; + } + } + + return false; +} + +/*-------------------------------------------------- + static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius) + + Looks for players around a specified x/y coordinate. + + Input Arguments:- + player - Bot to compare against. + x - X coordinate to look around. + y - Y coordinate to look around. + radius - The radius to look for players in. + + Return:- + true if a player was found around the coordinate, otherwise false. +--------------------------------------------------*/ +static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius) +{ + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *target = NULL; + fixed_t dist = INT32_MAX; + + if (!playeringame[i]) + { + continue; + } + + target = &players[i]; + + if (target->mo == NULL || P_MobjWasRemoved(target->mo) + || player == target || target->spectator + || target->powers[pw_flashing]) + { + continue; + } + + dist = P_AproxDistance( + x - target->mo->x, + y - target->mo->y + ); + + if (dist <= radius) + { + return true; + } + } + + return false; +} + +/*-------------------------------------------------- + static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra) + + Looks for players around the predicted coordinates of their thrown item. + + Input Arguments:- + player - Bot to compare against. + extra - Extra throwing distance, for aim forward on mines. + + Return:- + true if a player was found around the coordinate, otherwise false. +--------------------------------------------------*/ +static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra) +{ + const fixed_t dist = (30 + (extra * 10)) * player->mo->scale; + const UINT32 airtime = FixedDiv(dist + player->mo->momz, gravity); + const fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + const fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + const fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + return K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2); +} + +/*-------------------------------------------------- + static boolean K_PlayerInCone(player_t *player, UINT16 cone, boolean flip) + + Looks for players in the . + + Input Arguments:- + player - Bot to compare against. + radius - How far away the targets can be. + cone - Size of cone, in degrees as an integer. + flip - If true, look behind. Otherwise, check in front of the player. + + Return:- + true if a player was found in the cone, otherwise false. +--------------------------------------------------*/ +static boolean K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boolean flip) +{ + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *target = NULL; + fixed_t dist = INT32_MAX; + + if (!playeringame[i]) + { + continue; + } + + target = &players[i]; + + if (target->mo == NULL || P_MobjWasRemoved(target->mo) + || player == target || target->spectator + || target->powers[pw_flashing] + || !P_CheckSight(player->mo, target->mo)) + { + continue; + } + + dist = P_AproxDistance(P_AproxDistance( + player->mo->x - target->mo->x, + player->mo->y - target->mo->y), + (player->mo->z - target->mo->z) / 4 + ); + + if (dist <= radius) + { + angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); + INT16 ad = 0; + + if (a < ANGLE_180) + { + ad = AngleFixed(a)>>FRACBITS; + } + else + { + ad = 360-(AngleFixed(a)>>FRACBITS); + } + + ad = abs(ad); + + if (flip) + { + if (ad >= 180-cone) + { + return true; + } + } + else + { + if (ad <= cone) + { + return true; + } + } + } + } + + return false; +} + +/*-------------------------------------------------- + static boolean K_BotGenericPressItem(player_t *player, ticcmd_t *cmd, SINT8 dir) + + Presses the item button & aim buttons for the bot. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + dir - Aiming direction: 1 for forwards, -1 for backwards, 0 for neutral. + + Return:- + true if we could press, false if not. +--------------------------------------------------*/ +static boolean K_BotGenericPressItem(player_t *player, ticcmd_t *cmd, SINT8 dir) +{ + if (player->pflags & PF_ATTACKDOWN) + { + return false; + } + + if (dir == 1) + { + cmd->buttons |= BT_FORWARD; + } + else if (dir == -1) + { + cmd->buttons |= BT_BACKWARD; + } + + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + return true; +} + +/*-------------------------------------------------- + static void K_BotItemGenericTap(player_t *player, ticcmd_t *cmd) + + Item usage for generic items that you need to tap. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemGenericTap(player_t *player, ticcmd_t *cmd) +{ + if (!(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } +} + +/*-------------------------------------------------- + static boolean K_BotRevealsGenericTrap(player_t *player, INT16 turnamt, boolean mine) + + Decides if a bot is ready to reveal their trap item or not. + + Input Arguments:- + player - Bot that has the banana. + turnamt - How hard they currently are turning. + mine - Set to true to handle Mine-specific behaviors. + + Return:- + true if we want the bot to reveal their banana, otherwise false. +--------------------------------------------------*/ +static boolean K_BotRevealsGenericTrap(player_t *player, INT16 turnamt, boolean mine) +{ + if (abs(turnamt) >= KART_FULLTURN/2) + { + // DON'T reveal on turns, we can place bananas on turns whenever we have multiple to spare, + // or if you missed your intentioned throw/place on a player. + return false; + } + + // Check the predicted throws. + if (K_PlayerPredictThrow(player, 0)) + { + return true; + } + + if (mine) + { + if (K_PlayerPredictThrow(player, 1)) + { + return true; + } + } + + // Check your behind. + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + { + return true; + } + + return false; +} + +/*-------------------------------------------------- + static void K_BotItemGenericTrapShield(player_t *player, ticcmd_t *cmd, INT16 turnamt, boolean mine) + + Item usage for Eggman shields. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + turnamt - How hard they currently are turning. + mine - Set to true to handle Mine-specific behaviors. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemGenericTrapShield(player_t *player, ticcmd_t *cmd, INT16 turnamt, boolean mine) +{ + if (player->kartstuff[k_itemheld]) + { + return; + } + + if (K_BotRevealsGenericTrap(player, turnamt, mine) || (player->botvars.itemconfirm++ > 5*TICRATE)) + { + K_BotGenericPressItem(player, cmd, 0); + } +} + +/*-------------------------------------------------- + static void K_BotItemGenericOrbitShield(player_t *player, ticcmd_t *cmd) + + Item usage for orbitting shields. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemGenericOrbitShield(player_t *player, ticcmd_t *cmd) +{ + if (player->kartstuff[k_itemheld]) + { + return; + } + + K_BotGenericPressItem(player, cmd, 0); +} + +/*-------------------------------------------------- + static void K_BotItemSneaker(player_t *player, ticcmd_t *cmd) + + Item usage for sneakers. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemSneaker(player_t *player, ticcmd_t *cmd) +{ + if ((player->kartstuff[k_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)/2 // Being slowed down too much + || player->kartstuff[k_speedboost] > (FRACUNIT/8) // Have another type of boost (tethering) + || player->botvars.itemconfirm > 4*TICRATE) // Held onto it for too long + { + if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 2*TICRATE; + } + } + else + { + player->botvars.itemconfirm++; + } +} + +/*-------------------------------------------------- + static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd) + + Item usage for rocket sneakers. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd) +{ + if (player->botvars.itemconfirm > TICRATE) + { + if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + } + else + { + player->botvars.itemconfirm++; + } +} + + +/*-------------------------------------------------- + static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) + + Item usage for trap item 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_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) +{ + SINT8 throwdir = -1; + + player->botvars.itemconfirm++; + + if (abs(turnamt) >= KART_FULLTURN/2) + { + player->botvars.itemconfirm += player->botvars.difficulty / 2; + throwdir = -1; + } + else + { + if (K_PlayerPredictThrow(player, 0)) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 1; + } + } + + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + + if (player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) + { + K_BotGenericPressItem(player, cmd, throwdir); + } +} + +/*-------------------------------------------------- + static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) + + Item usage for trap item 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_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) +{ + SINT8 throwdir = 0; + + player->botvars.itemconfirm++; + + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + + if (abs(turnamt) >= KART_FULLTURN/2) + { + player->botvars.itemconfirm += player->botvars.difficulty / 2; + throwdir = -1; + } + else + { + if (K_PlayerPredictThrow(player, 0)) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 0; + } + + if (K_PlayerPredictThrow(player, 1)) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 1; + } + } + + + + if (player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) + { + K_BotGenericPressItem(player, cmd, throwdir); + } +} + +/*-------------------------------------------------- + static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) + + Item usage for Eggman item throwing. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) +{ + const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); + SINT8 throwdir = -1; + + player->botvars.itemconfirm++; + + if (K_PlayerPredictThrow(player, 0)) + { + player->botvars.itemconfirm += player->botvars.difficulty / 2; + throwdir = 1; + } + + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + + if (stealth > 1 || player->kartstuff[k_itemroulette] > 0) + { + player->botvars.itemconfirm += player->botvars.difficulty * 4; + throwdir = -1; + } + + if (player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) + { + K_BotGenericPressItem(player, cmd, throwdir); + } +} + +/*-------------------------------------------------- + static boolean K_BotRevealsEggbox(player_t *player) + + Decides if a bot is ready to place their Eggman item or not. + + Input Arguments:- + player - Bot that has the eggbox. + + Return:- + true if we want the bot to reveal their eggbox, otherwise false. +--------------------------------------------------*/ +static boolean K_BotRevealsEggbox(player_t *player) +{ + const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); + + // This is a stealthy spot for an eggbox, lets reveal it! + if (stealth > 1) + { + return true; + } + + // Check the predicted throws. + if (K_PlayerPredictThrow(player, 0)) + { + return true; + } + + // Check your behind. + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + { + return true; + } + + return false; +} + +/*-------------------------------------------------- + static void K_BotItemEggmanShield(player_t *player, ticcmd_t *cmd) + + Item usage for Eggman shields. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemEggmanShield(player_t *player, ticcmd_t *cmd) +{ + if (player->kartstuff[k_eggmanheld]) + { + return; + } + + if (K_BotRevealsEggbox(player) || (player->botvars.itemconfirm++ > 20*TICRATE)) + { + K_BotGenericPressItem(player, cmd, 0); + } +} + +/*-------------------------------------------------- + static void K_BotItemEggmanExplosion(player_t *player, ticcmd_t *cmd) + + Item usage for Eggman explosions. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemEggmanExplosion(player_t *player, ticcmd_t *cmd) +{ + if (player->kartstuff[k_position] == 1) + { + cmd->forwardmove /= 2; + cmd->buttons |= BT_BRAKE; + } + + K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale); +} + +/*-------------------------------------------------- + static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) + + Item usage for Orbinaut throwing. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) +{ + const fixed_t topspeed = K_GetKartSpeed(player, false); + fixed_t radius = (player->mo->radius * 32); + SINT8 throwdir = -1; + + if (player->speed > topspeed) + { + radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + } + + player->botvars.itemconfirm++; + + if (K_PlayerInCone(player, radius, 10, false)) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 1; + } + else if (K_PlayerInCone(player, radius, 10, true)) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + + if (player->botvars.itemconfirm > 5*TICRATE) + { + K_BotGenericPressItem(player, cmd, throwdir); + } +} + +/*-------------------------------------------------- + static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) + + Item usage for Jawz throwing. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) +{ + const fixed_t topspeed = K_GetKartSpeed(player, false); + fixed_t radius = (player->mo->radius * 32); + SINT8 throwdir = 1; + + if (player->speed > topspeed) + { + radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + } + + player->botvars.itemconfirm++; + + if (K_PlayerInCone(player, radius, 10, true)) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + + if (player->kartstuff[k_lastjawztarget] != -1) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 1; + } + + if (player->botvars.itemconfirm > 5*TICRATE) + { + K_BotGenericPressItem(player, cmd, throwdir); + } +} + +/*-------------------------------------------------- + static void K_BotItemThunder(player_t *player, ticcmd_t *cmd) + + Item usage for Thunder Shield. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemThunder(player_t *player, ticcmd_t *cmd) +{ + if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) + { + if (player->botvars.itemconfirm > 10*TICRATE) + { + K_BotGenericPressItem(player, cmd, 0); + } + else + { + player->botvars.itemconfirm++; + } + } +} + +/*-------------------------------------------------- + static void K_BotItemBubble(player_t *player, ticcmd_t *cmd) + + Item usage for Bubble Shield. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemBubble(player_t *player, ticcmd_t *cmd) +{ + boolean hold = false; + + if (player->kartstuff[k_bubbleblowup] <= 0) + { + UINT8 i; + + player->botvars.itemconfirm++; + + if (player->kartstuff[k_bubblecool] <= 0) + { + const fixed_t radius = 192 * player->mo->scale; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *target = NULL; + fixed_t dist = INT32_MAX; + + if (!playeringame[i]) + { + continue; + } + + target = &players[i]; + + if (target->mo == NULL || P_MobjWasRemoved(target->mo) + || player == target || target->spectator + || target->powers[pw_flashing]) + { + continue; + } + + dist = P_AproxDistance(P_AproxDistance( + player->mo->x - target->mo->x, + player->mo->y - target->mo->y), + (player->mo->z - target->mo->z) / 4 + ); + + if (dist <= radius) + { + hold = true; + break; + } + } + } + } + else if (player->kartstuff[k_bubbleblowup] >= bubbletime) + { + if (player->botvars.itemconfirm >= 10*TICRATE) + { + hold = true; + } + } + else if (player->kartstuff[k_bubbleblowup] < bubbletime) + { + hold = true; + } + + if (hold && player->kartstuff[k_holdready]) + { + cmd->buttons |= BT_ATTACK; + } +} + +/*-------------------------------------------------- + static void K_BotItemFlame(player_t *player, ticcmd_t *cmd) + + Item usage for Flame Shield. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemFlame(player_t *player, ticcmd_t *cmd) +{ + if (player->botvars.itemconfirm > 0) + { + player->botvars.itemconfirm--; + } + else if (player->kartstuff[k_holdready]) + { + INT32 flamemax = player->kartstuff[k_flamelength] * flameseg; + + if (player->kartstuff[k_flamemeter] < flamemax || flamemax == 0) + { + cmd->buttons |= BT_ATTACK; + } + else + { + player->botvars.itemconfirm = 3*flamemax/4; + } + } +} + +/*-------------------------------------------------- + static void K_BotItemRings(player_t *player, ticcmd_t *cmd) + + Item usage for rings. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemRings(player_t *player, ticcmd_t *cmd) +{ + INT32 saferingsval = 16 - K_GetKartRingPower(player); + + 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) + { + saferingsval -= 5; + } + + if (player->kartstuff[k_rings] > saferingsval) + { + cmd->buttons |= BT_ATTACK; + } +} + +/*-------------------------------------------------- + static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd) + + Item usage for item roulette mashing. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd) +{ + boolean mash = false; + + if (player->pflags & PF_ATTACKDOWN) + { + return; + } + + if (player->kartstuff[k_rings] < 0 && cv_superring.value) + { + // Uh oh, we need a loan! + // It'll be better in the long run for bots to lose an item set for 10 free rings. + mash = true; + } + + // TODO: Mash based on how far behind you are, when items are + // almost garantueed to be in your favor. + + if (mash == true) + { + cmd->buttons |= BT_ATTACK; + } +} + +/*-------------------------------------------------- + void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) + + See header file for description. +--------------------------------------------------*/ +void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) +{ + if (player->kartstuff[k_userings] == 1) + { + // Use rings! + + if (!player->exiting) + { + K_BotItemRings(player, cmd); + } + } + else + { + if (player->botvars.itemdelay) + { + player->botvars.itemdelay--; + player->botvars.itemconfirm = 0; + return; + } + + if (player->kartstuff[k_itemroulette]) + { + // Mashing behaviors + K_BotItemRouletteMash(player, cmd); + return; + } + + if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0) + { + if (player->kartstuff[k_eggmanexplode]) + { + K_BotItemEggmanExplosion(player, cmd); + } + else if (player->kartstuff[k_eggmanheld]) + { + K_BotItemEggman(player, cmd); + } + else if (player->kartstuff[k_rocketsneakertimer] > 0) + { + K_BotItemRocketSneaker(player, cmd); + } + else + { + switch (player->kartstuff[k_itemtype]) + { + default: + if (player->kartstuff[k_itemtype] != KITEM_NONE) + { + K_BotItemGenericTap(player, cmd); + } + + player->botvars.itemconfirm = 0; + break; + case KITEM_INVINCIBILITY: + case KITEM_SPB: + case KITEM_GROW: + case KITEM_SHRINK: + case KITEM_HYUDORO: + case KITEM_SUPERRING: + K_BotItemGenericTap(player, cmd); + break; + case KITEM_ROCKETSNEAKER: + if (player->kartstuff[k_rocketsneakertimer] <= 0) + { + K_BotItemGenericTap(player, cmd); + } + break; + case KITEM_SNEAKER: + K_BotItemSneaker(player, cmd); + break; + case KITEM_BANANA: + if (!player->kartstuff[k_itemheld]) + { + K_BotItemGenericTrapShield(player, cmd, turnamt, false); + } + else + { + K_BotItemBanana(player, cmd, turnamt); + } + break; + case KITEM_EGGMAN: + K_BotItemEggmanShield(player, cmd); + break; + case KITEM_ORBINAUT: + if (!player->kartstuff[k_itemheld]) + { + K_BotItemGenericOrbitShield(player, cmd); + } + else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) + /* FALL-THRU */ + case KITEM_BALLHOG: + { + K_BotItemOrbinaut(player, cmd); + } + break; + case KITEM_JAWZ: + if (!player->kartstuff[k_itemheld]) + { + K_BotItemGenericOrbitShield(player, cmd); + } + else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) + { + K_BotItemJawz(player, cmd); + } + break; + case KITEM_MINE: + if (!player->kartstuff[k_itemheld]) + { + K_BotItemGenericTrapShield(player, cmd, turnamt, true); + } + else + { + K_BotItemMine(player, cmd, turnamt); + } + break; + case KITEM_THUNDERSHIELD: + K_BotItemThunder(player, cmd); + break; + case KITEM_BUBBLESHIELD: + K_BotItemBubble(player, cmd); + break; + case KITEM_FLAMESHIELD: + K_BotItemFlame(player, cmd); + break; + } + } + } + } +} diff --git a/src/k_botsearch.c b/src/k_botsearch.c new file mode 100644 index 000000000..2a95da738 --- /dev/null +++ b/src/k_botsearch.c @@ -0,0 +1,746 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_botsearch.c +/// \brief Bot blockmap search functions + +#include "doomdef.h" +#include "d_player.h" +#include "g_game.h" +#include "r_main.h" +#include "p_local.h" +#include "k_bot.h" +#include "lua_hook.h" +#include "byteptr.h" +#include "d_net.h" // nodetoplayer +#include "k_kart.h" +#include "z_zone.h" +#include "i_system.h" +#include "p_maputl.h" +#include "d_ticcmd.h" +#include "m_random.h" +#include "r_things.h" // numskins + +struct globalsmuggle +{ + mobj_t *botmo; + fixed_t distancetocheck; + + fixed_t closestlinedist; + + INT16 curturn; + INT16 steer; + + fixed_t eggboxx, eggboxy; + UINT8 randomitems; + UINT8 eggboxes; +} globalsmuggle; + +/*-------------------------------------------------- + static boolean K_FindEggboxes(mobj_t *thing) + + Blockmap search function. + Increments the random items and egg boxes counters. + + Input Arguments:- + thing - Object passed in from iteration. + + Return:- + true continues searching, false ends the search early. +--------------------------------------------------*/ +static boolean K_FindEggboxes(mobj_t *thing) +{ + fixed_t dist; + + if (thing->type != MT_RANDOMITEM && thing->type != MT_EGGMANITEM) + { + return true; + } + + if (!thing->health) + { + return true; + } + + dist = P_AproxDistance(thing->x - globalsmuggle.eggboxx, thing->y - globalsmuggle.eggboxy); + + if (dist > globalsmuggle.distancetocheck) + { + return true; + } + + if (thing->type == MT_RANDOMITEM) + { + globalsmuggle.randomitems++; + } + else + { + globalsmuggle.eggboxes++; + } + + return true; +} + +/*-------------------------------------------------- + UINT8 K_EggboxStealth(fixed_t x, fixed_t y) + + See header file for description. +--------------------------------------------------*/ +UINT8 K_EggboxStealth(fixed_t x, fixed_t y) +{ + INT32 xl, xh, yl, yh, bx, by; + + globalsmuggle.eggboxx = x; + globalsmuggle.eggboxy = y; + globalsmuggle.distancetocheck = (mapobjectscale * 256); + globalsmuggle.randomitems = 0; + globalsmuggle.eggboxes = 0; + + xl = (unsigned)(globalsmuggle.eggboxx - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(globalsmuggle.eggboxx + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(globalsmuggle.eggboxy - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(globalsmuggle.eggboxy + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockThingsIterator(bx, by, K_FindEggboxes); + } + } + + return (globalsmuggle.randomitems * globalsmuggle.eggboxes); +} + +/*-------------------------------------------------- + static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) + + Tells us if a bot will play more careful around + this sector's special type. + + Input Arguments:- + player - Player to check against. + sec - Sector to check the specials of. + + Return:- + true if avoiding this sector special, false otherwise. +--------------------------------------------------*/ +static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) +{ + switch (GETSECSPECIAL(sec->special, 1)) + { + case 1: // Damage + case 5: // Spikes + case 6: case 7: // Death Pit + case 8: // Instant Kill + return true; + //case 2: case 3: // Offroad (let's let them lawnmower) + case 4: // Offroad (Strong) + if (!K_BotCanTakeCut(player)) + { + return true; + } + break; + default: + break; + } + + return false; +} + +/*-------------------------------------------------- + static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) + + Tells us if a bot will play more careful around + this sector. + + Input Arguments:- + player - Player to check against. + sec - Sector to check against. + + Return:- + true if avoiding this sector, false otherwise. +--------------------------------------------------*/ +static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) +{ + const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP); + INT32 flag; + ffloor_t *rover; + + if (flip) + { + flag = SF_FLIPSPECIAL_CEILING; + } + else + { + flag = SF_FLIPSPECIAL_FLOOR; + } + + if (sec->flags & flag) + { + if (K_BotHatesThisSectorsSpecial(player, sec)) + { + return true; + } + } + + for (rover = sec->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS)) + { + continue; + } + + if (!(rover->master->frontsector->flags & flag)) + { + continue; + } + + if (((*rover->bottomheight >= player->mo->z + player->mo->height) && (flip)) + || ((*rover->topheight <= player->mo->z) && (!flip))) + { + if (K_BotHatesThisSectorsSpecial(player, sec)) + { + return true; + } + } + } + + return false; +} + +/*-------------------------------------------------- + static boolean K_FindBlockingWalls(line_t *line) + + Blockmap search function. + Reels the bot prediction back in based on solid walls + or other obstacles surrounding the bot. + + Input Arguments:- + line - Linedef passed in from iteration. + + Return:- + true continues searching, false ends the search early. +--------------------------------------------------*/ +static boolean K_FindBlockingWalls(line_t *line) +{ + // Condensed version of PIT_CheckLine + const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); + fixed_t maxstep = maxstepmove; + fixed_t linedist = INT32_MAX; + INT32 lineside = 0; + + if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player) + { + return false; + } + + if (line->polyobj && !(line->polyobj->flags & POF_SOLID)) + { + return true; + } + + if (tmbbox[BOXRIGHT] <= line->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= line->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= line->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= line->bbox[BOXTOP]) + { + return true; + } + + if (P_BoxOnLineSide(tmbbox, line) != -1) + { + return true; + } + + lineside = P_PointOnLineSide(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line); + + // one sided line + if (!line->backsector) + { + if (lineside) + { + // don't hit the back side + return true; + } + + goto blocked; + } + + if ((line->flags & ML_IMPASSABLE) || (line->flags & ML_BLOCKPLAYERS)) + { + goto blocked; + } + + // set openrange, opentop, openbottom + P_LineOpening(line, globalsmuggle.botmo); + + if (globalsmuggle.botmo->player->kartstuff[k_waterskip]) + maxstep += maxstepmove; + + if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 13, false)) + maxstep <<= 1; + else if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 12, false)) + maxstep = 0; + + if ((openrange < globalsmuggle.botmo->height) // doesn't fit + || (opentop - globalsmuggle.botmo->z < globalsmuggle.botmo->height) // mobj is too high + || (openbottom - globalsmuggle.botmo->z > maxstep)) // too big a step up + { + goto blocked; + } + + if (!K_BotHatesThisSector(globalsmuggle.botmo->player, globalsmuggle.botmo->subsector->sector)) + { + // Treat damage sectors like walls + + if (lineside) + { + if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->frontsector)) + goto blocked; + } + else + { + if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->backsector)) + goto blocked; + } + } + + // We weren't blocked! + return true; + +blocked: + linedist = K_DistanceOfLineFromPoint(line->v1->x, line->v1->y, line->v2->x, line->v2->y, globalsmuggle.botmo->x, globalsmuggle.botmo->y); + linedist -= (globalsmuggle.botmo->radius * 8); // Maintain a reasonable distance away from it + + if (linedist > globalsmuggle.distancetocheck) + { + return true; + } + + if (linedist <= 0) + { + globalsmuggle.closestlinedist = 0; + return false; + } + + if (linedist < globalsmuggle.closestlinedist) + { + globalsmuggle.closestlinedist = linedist; + } + + return true; +} + +/*-------------------------------------------------- + fixed_t K_BotReducePrediction(player_t *player) + + See header file for description. +--------------------------------------------------*/ +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.closestlinedist = INT32_MAX; + + tmx = player->mo->x; + tmy = player->mo->y; + + xl = (unsigned)(tmx - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(tmx + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(tmy - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(tmy + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + tmbbox[BOXTOP] = tmy + globalsmuggle.distancetocheck; + tmbbox[BOXBOTTOM] = tmy - globalsmuggle.distancetocheck; + tmbbox[BOXRIGHT] = tmx + globalsmuggle.distancetocheck; + tmbbox[BOXLEFT] = tmx - globalsmuggle.distancetocheck; + + // Check for lines that the bot might collide with + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockLinesIterator(bx, by, K_FindBlockingWalls); + } + } + + if (globalsmuggle.closestlinedist == INT32_MAX) + { + return FRACUNIT; + } + + return FixedDiv(globalsmuggle.closestlinedist, globalsmuggle.distancetocheck); +} + +/*-------------------------------------------------- + static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount) + + Handles steering away/towards the specified object. + + 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. + + Return:- + None +--------------------------------------------------*/ +static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount) +{ + angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); + angle_t angle; + SINT8 flip = 1; + + amount = (amount * FixedDiv(globalsmuggle.distancetocheck - fulldist, globalsmuggle.distancetocheck)) / FRACUNIT; + + if (amount == 0) + { + // Shouldn't happen + return; + } + + if (towards) + { + 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; + } + + 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) + + Blockmap search function. + Finds objects around the bot to steer towards/away from. + + Input Arguments:- + thing - Object passed in from iteration. + + Return:- + true continues searching, false ends the search early. +--------------------------------------------------*/ +static boolean K_BotSteerObjects(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 + + if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player) + { + return false; + } + + if (!thing->health) + { + return true; + } + + if (globalsmuggle.botmo == 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); + + if (fulldist > globalsmuggle.distancetocheck) + { + return true; + } + + if (!P_CheckSight(globalsmuggle.botmo, thing)) + { + return true; + } + + destangle = R_PointToAngle2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, thing->x, thing->y); + angle = (globalsmuggle.botmo->angle - destangle); + + if (angle < ANGLE_180) + { + anglediff = AngleFixed(angle)>>FRACBITS; + } + else + { + anglediff = 360-(AngleFixed(angle)>>FRACBITS); + } + + 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: + case MT_BANANA_SHIELD: + case MT_EGGMANITEM_SHIELD: + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + case MT_JAWZ: + case MT_JAWZ_DUD: + case MT_JAWZ_SHIELD: + case MT_SSMINE: + case MT_SSMINE_SHIELD: + case MT_BALLHOG: + case MT_SPB: + case MT_BUBBLESHIELDTRAP: + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); + break; + case MT_RANDOMITEM: + if (anglediff >= 60) + { + break; + } + + if (P_CanPickupItem(globalsmuggle.botmo->player, 1)) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); + } + break; + case MT_EGGMANITEM: + if (anglediff >= 60) + { + break; + } + + if (P_CanPickupItem(globalsmuggle.botmo->player, 1)) // Can pick up an actual item + { + const UINT8 stealth = K_EggboxStealth(thing->x, thing->y); + const UINT8 requiredstealth = (globalsmuggle.botmo->player->botvars.difficulty * globalsmuggle.botmo->player->botvars.difficulty); + + if (stealth >= requiredstealth) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); + } + else + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); + } + } + break; + case MT_FLOATINGITEM: + if (anglediff >= 60) + { + break; + } + + if (P_CanPickupItem(globalsmuggle.botmo->player, 3)) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); + } + break; + case MT_RING: + case MT_FLINGRING: + if (anglediff >= 60) + { + break; + } + + if ((RINGTOTAL(globalsmuggle.botmo->player) < 20 && !globalsmuggle.botmo->player->kartstuff[k_ringlock] + && P_CanPickupItem(globalsmuggle.botmo->player, 0)) + && !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)) + ); + } + break; + case MT_PLAYER: + if (thing->player + && !thing->player->kartstuff[k_hyudorotimer] + && !globalsmuggle.botmo->player->kartstuff[k_hyudorotimer]) + { + // There REALLY ought to be a better way to handle this logic, right?! + // Squishing + PlayerAttackSteer( + globalsmuggle.botmo->scale > thing->scale + (mapobjectscale/8), + thing->scale > globalsmuggle.botmo->scale + (mapobjectscale/8) + ) + // Invincibility + else PlayerAttackSteer( + globalsmuggle.botmo->player->kartstuff[k_invincibilitytimer], + thing->player->kartstuff[k_invincibilitytimer] + ) + // Thunder Shield + else PlayerAttackSteer( + globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD, + thing->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD + ) + // Bubble Shield + else PlayerAttackSteer( + globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD, + thing->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD + ) + // Flame Shield + else PlayerAttackSteer( + globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD, + thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD + ) + // Has held item shield + else PlayerAttackSteer( + (globalsmuggle.botmo->player->kartstuff[k_itemheld] || globalsmuggle.botmo->player->kartstuff[k_eggmanheld]), + (thing->player->kartstuff[k_itemheld] || thing->player->kartstuff[k_eggmanheld]) + ) + // Ring Sting + else PlayerAttackSteer( + thing->player->kartstuff[k_rings] <= 0, + globalsmuggle.botmo->player->kartstuff[k_rings] <= 0 + ) + else + { + // After ALL of that, we can do standard bumping + fixed_t ourweight = K_GetMobjWeight(globalsmuggle.botmo, thing); + fixed_t theirweight = K_GetMobjWeight(thing, globalsmuggle.botmo); + fixed_t weightdiff = 0; + + if (anglediff >= 90) + { + weightdiff = theirweight - ourweight; + } + else + { + weightdiff = ourweight - theirweight; + } + + if (weightdiff > mapobjectscale) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); + } + else + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, KART_FULLTURN + dodge); + } + } + } + break; + case MT_BOTHINT: + if (anglediff >= 60) + { + break; + } + + 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)); + } + break; + default: + if (thing->flags & (MF_SOLID|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE)) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); + } + break; + } + + return true; +} + +/*-------------------------------------------------- + INT16 K_BotFindObjects(player_t *player, INT16 turn) + + See header file for description. +--------------------------------------------------*/ +INT16 K_BotFindObjects(player_t *player, INT16 turn) +{ + INT32 xl, xh, yl, yh, bx, by; + + globalsmuggle.steer = 0; + globalsmuggle.botmo = player->mo; + globalsmuggle.curturn = turn; + globalsmuggle.distancetocheck = (player->mo->radius * 32) + (player->speed * 4); + + xl = (unsigned)(globalsmuggle.botmo->x - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(globalsmuggle.botmo->x + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(globalsmuggle.botmo->y - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(globalsmuggle.botmo->y + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockThingsIterator(bx, by, K_BotSteerObjects); + } + } + + return globalsmuggle.steer; +} diff --git a/src/k_color.c b/src/k_color.c new file mode 100644 index 000000000..89112db29 --- /dev/null +++ b/src/k_color.c @@ -0,0 +1,525 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_color.c +/// \brief Skincolor & colormapping code + +#include "k_color.h" + +#include "doomdef.h" +#include "doomtype.h" +#include "r_draw.h" +#include "r_things.h" +#include "v_video.h" + +// These should be within 14 characters to fit on the character select screen +const char *KartColor_Names[MAXSKINCOLORS] = +{ + "None", // SKINCOLOR_NONE + "White", // SKINCOLOR_WHITE + "Silver", // SKINCOLOR_SILVER + "Grey", // SKINCOLOR_GREY + "Nickel", // SKINCOLOR_NICKEL + "Black", // SKINCOLOR_BLACK + "Skunk", // SKINCOLOR_SKUNK + "Fairy", // SKINCOLOR_FAIRY + "Popcorn", // SKINCOLOR_POPCORN + "Artichoke", // SKINCOLOR_ARTICHOKE + "Pigeon", // SKINCOLOR_PIGEON + "Sepia", // SKINCOLOR_SEPIA + "Beige", // SKINCOLOR_BEIGE + "Caramel", // SKINCOLOR_CARAMEL + "Peach", // SKINCOLOR_PEACH + "Brown", // SKINCOLOR_BROWN + "Leather", // SKINCOLOR_LEATHER + "Pink", // SKINCOLOR_PINK + "Rose", // SKINCOLOR_ROSE + "Cinnamon", // SKINCOLOR_CINNAMON + "Ruby", // SKINCOLOR_RUBY + "Raspberry", // SKINCOLOR_RASPBERRY + "Red", // SKINCOLOR_RED + "Crimson", // SKINCOLOR_CRIMSON + "Maroon", // SKINCOLOR_MAROON + "Lemonade", // SKINCOLOR_LEMONADE + "Scarlet", // SKINCOLOR_SCARLET + "Ketchup", // SKINCOLOR_KETCHUP + "Dawn", // SKINCOLOR_DAWN + "Sunslam", // SKINCOLOR_SUNSLAM + "Creamsicle", // SKINCOLOR_CREAMSICLE + "Orange", // SKINCOLOR_ORANGE + "Rosewood", // SKINCOLOR_ROSEWOOD + "Tangerine", // SKINCOLOR_TANGERINE + "Tan", // SKINCOLOR_TAN + "Cream", // SKINCOLOR_CREAM + "Gold", // SKINCOLOR_GOLD + "Royal", // SKINCOLOR_ROYAL + "Bronze", // SKINCOLOR_BRONZE + "Copper", // SKINCOLOR_COPPER + "Yellow", // SKINCOLOR_YELLOW + "Mustard", // SKINCOLOR_MUSTARD + "Banana", // SKINCOLOR_BANANA + "Olive", // SKINCOLOR_OLIVE + "Crocodile", // SKINCOLOR_CROCODILE + "Peridot", // SKINCOLOR_PERIDOT + "Vomit", // SKINCOLOR_VOMIT + "Garden", // SKINCOLOR_GARDEN + "Lime", // SKINCOLOR_LIME + "Handheld", // SKINCOLOR_HANDHELD + "Tea", // SKINCOLOR_TEA + "Pistachio", // SKINCOLOR_PISTACHIO + "Moss", // SKINCOLOR_MOSS + "Camouflage", // SKINCOLOR_CAMOUFLAGE + "Mint", // SKINCOLOR_MINT + "Green", // SKINCOLOR_GREEN + "Pinetree", // SKINCOLOR_PINETREE + "Turtle", // SKINCOLOR_TURTLE + "Swamp", // SKINCOLOR_SWAMP + "Dream", // SKINCOLOR_DREAM + "Plague", // SKINCOLOR_PLAGUE + "Emerald", // SKINCOLOR_EMERALD + "Algae", // SKINCOLOR_ALGAE + "Aquamarine", // SKINCOLOR_AQUAMARINE + "Turquoise", // SKINCOLOR_TURQUOISE + "Teal", // SKINCOLOR_TEAL + "Robin", // SKINCOLOR_ROBIN + "Cyan", // SKINCOLOR_CYAN + "Jawz", // SKINCOLOR_JAWZ + "Cerulean", // SKINCOLOR_CERULEAN + "Navy", // SKINCOLOR_NAVY + "Platinum", // SKINCOLOR_PLATINUM + "Slate", // SKINCOLOR_SLATE + "Steel", // SKINCOLOR_STEEL + "Thunder", // SKINCOLOR_THUNDER + "Nova", // SKINCOLOR_NOVA + "Rust", // SKINCOLOR_RUST + "Wristwatch", // SKINCOLOR_WRISTWATCH + "Jet", // SKINCOLOR_JET + "Sapphire", // SKINCOLOR_SAPPHIRE + "Ultramarine", // SKINCOLOR_ULTRAMARINE + "Periwinkle", // SKINCOLOR_PERIWINKLE + "Blue", // SKINCOLOR_BLUE + "Midnight", // SKINCOLOR_MIDNIGHT + "Blueberry", // SKINCOLOR_BLUEBERRY + "Thistle", // SKINCOLOR_THISTLE + "Purple", // SKINCOLOR_PURPLE + "Pastel", // SKINCOLOR_PASTEL + "Moonset", // SKINCOLOR_MOONSET + "Dusk", // SKINCOLOR_DUSK + "Violet", // SKINCOLOR_VIOLET + "Magenta", // SKINCOLOR_MAGENTA + "Fuchsia", // SKINCOLOR_FUCHSIA + "Toxic", // SKINCOLOR_TOXIC + "Mauve", // SKINCOLOR_MAUVE + "Lavender", // SKINCOLOR_LAVENDER + "Byzantium", // SKINCOLOR_BYZANTIUM + "Pomegranate", // SKINCOLOR_POMEGRANATE + "Lilac", // SKINCOLOR_LILAC + "Blossom", // SKINCOLOR_BLOSSOM + "Taffy" // SKINCOLOR_TAFFY +}; + +// Color_Opposite replacement; frame setting has not been changed from 8 for most, should be done later +const UINT8 KartColor_Opposite[MAXSKINCOLORS*2] = +{ + SKINCOLOR_NONE,8, // SKINCOLOR_NONE + SKINCOLOR_BLACK,8, // SKINCOLOR_WHITE + SKINCOLOR_NICKEL,8, // SKINCOLOR_SILVER + SKINCOLOR_GREY,8, // SKINCOLOR_GREY + SKINCOLOR_SILVER,8, // SKINCOLOR_NICKEL + SKINCOLOR_WHITE,8, // SKINCOLOR_BLACK + SKINCOLOR_VOMIT,8, // SKINCOLOR_SKUNK + SKINCOLOR_ARTICHOKE,12, // SKINCOLOR_FAIRY + SKINCOLOR_PIGEON,12, // SKINCOLOR_POPCORN + SKINCOLOR_FAIRY,12, // SKINCOLOR_ARTICHOKE + SKINCOLOR_POPCORN,12, // SKINCOLOR_PIGEON + SKINCOLOR_LEATHER,6, // SKINCOLOR_SEPIA + SKINCOLOR_BROWN,2, // SKINCOLOR_BEIGE + SKINCOLOR_CERULEAN,8, // SKINCOLOR_CARAMEL + SKINCOLOR_CYAN,8, // SKINCOLOR_PEACH + SKINCOLOR_BEIGE,8, // SKINCOLOR_BROWN + SKINCOLOR_SEPIA,8, // SKINCOLOR_LEATHER + SKINCOLOR_PISTACHIO,8, // SKINCOLOR_PINK + SKINCOLOR_MOSS,8, // SKINCOLOR_ROSE + SKINCOLOR_WRISTWATCH,6, // SKINCOLOR_CINNAMON + SKINCOLOR_SAPPHIRE,8, // SKINCOLOR_RUBY + SKINCOLOR_MINT,8, // SKINCOLOR_RASPBERRY + SKINCOLOR_GREEN,6, // SKINCOLOR_RED + SKINCOLOR_PINETREE,6, // SKINCOLOR_CRIMSON + SKINCOLOR_TOXIC,8, // SKINCOLOR_MAROON + SKINCOLOR_THUNDER,8, // SKINCOLOR_LEMONADE + SKINCOLOR_ALGAE,10, // SKINCOLOR_SCARLET + SKINCOLOR_MUSTARD,10, // SKINCOLOR_KETCHUP + SKINCOLOR_DUSK,8, // SKINCOLOR_DAWN + SKINCOLOR_MOONSET,8, // SKINCOLOR_SUNSLAM + SKINCOLOR_PERIWINKLE,8, // SKINCOLOR_CREAMSICLE + SKINCOLOR_BLUE,8, // SKINCOLOR_ORANGE + SKINCOLOR_BLUEBERRY,6, // SKINCOLOR_ROSEWOOD + SKINCOLOR_LIME,8, // SKINCOLOR_TANGERINE + SKINCOLOR_RUST,8, // SKINCOLOR_TAN + SKINCOLOR_COPPER,10, // SKINCOLOR_CREAM + SKINCOLOR_SLATE,8, // SKINCOLOR_GOLD + SKINCOLOR_PLATINUM,6, // SKINCOLOR_ROYAL + SKINCOLOR_STEEL,8, // SKINCOLOR_BRONZE + SKINCOLOR_CREAM,6, // SKINCOLOR_COPPER + SKINCOLOR_AQUAMARINE,8, // SKINCOLOR_YELLOW + SKINCOLOR_KETCHUP,8, // SKINCOLOR_MUSTARD + SKINCOLOR_EMERALD,8, // SKINCOLOR_BANANA + SKINCOLOR_TEAL,8, // SKINCOLOR_OLIVE + SKINCOLOR_VIOLET,8, // SKINCOLOR_CROCODILE + SKINCOLOR_NAVY,6, // SKINCOLOR_PERIDOT + SKINCOLOR_SKUNK,8, // SKINCOLOR_VOMIT + SKINCOLOR_LAVENDER,6, // SKINCOLOR_GARDEN + SKINCOLOR_TANGERINE,8, // SKINCOLOR_LIME + SKINCOLOR_ULTRAMARINE,8, // SKINCOLOR_HANDHELD + SKINCOLOR_BLOSSOM,8, // SKINCOLOR_TEA + SKINCOLOR_PINK,6, // SKINCOLOR_PISTACHIO + SKINCOLOR_ROSE,8, // SKINCOLOR_MOSS + SKINCOLOR_CAMOUFLAGE,8, // SKINCOLOR_CAMOUFLAGE + SKINCOLOR_RASPBERRY,8, // SKINCOLOR_MINT + SKINCOLOR_RED,8, // SKINCOLOR_GREEN + SKINCOLOR_CRIMSON,8, // SKINCOLOR_PINETREE + SKINCOLOR_MAGENTA,8, // SKINCOLOR_TURTLE + SKINCOLOR_BYZANTIUM,8, // SKINCOLOR_SWAMP + SKINCOLOR_POMEGRANATE,8, // SKINCOLOR_DREAM + SKINCOLOR_NOVA,8, // SKINCOLOR_PLAGUE + SKINCOLOR_BANANA,8, // SKINCOLOR_EMERALD + SKINCOLOR_SCARLET,10, // SKINCOLOR_ALGAE + SKINCOLOR_YELLOW,8, // SKINCOLOR_AQUAMARINE + SKINCOLOR_MAUVE,10, // SKINCOLOR_TURQUOISE + SKINCOLOR_OLIVE,8, // SKINCOLOR_TEAL + SKINCOLOR_THISTLE,8, // SKINCOLOR_ROBIN + SKINCOLOR_PEACH,8, // SKINCOLOR_CYAN + SKINCOLOR_LILAC,10, // SKINCOLOR_JAWZ + SKINCOLOR_CARAMEL,8, // SKINCOLOR_CERULEAN + SKINCOLOR_PERIDOT,8, // SKINCOLOR_NAVY + SKINCOLOR_ROYAL,8, // SKINCOLOR_PLATINUM + SKINCOLOR_GOLD,10, // SKINCOLOR_SLATE + SKINCOLOR_BRONZE,10, // SKINCOLOR_STEEL + SKINCOLOR_LEMONADE,8, // SKINCOLOR_THUNDER + SKINCOLOR_PLAGUE,10, // SKINCOLOR_NOVA + SKINCOLOR_TAN,8, // SKINCOLOR_RUST + SKINCOLOR_CINNAMON,8, // SKINCOLOR_WRISTWATCH + SKINCOLOR_TAFFY,8, // SKINCOLOR_JET + SKINCOLOR_RUBY,6, // SKINCOLOR_SAPPHIRE + SKINCOLOR_HANDHELD,10, // SKINCOLOR_ULTRAMARINE + SKINCOLOR_CREAMSICLE,8, // SKINCOLOR_PERIWINKLE + SKINCOLOR_ORANGE,8, // SKINCOLOR_BLUE + SKINCOLOR_ROSEWOOD,8, // SKINCOLOR_MIDNIGHT + SKINCOLOR_PURPLE,8, // SKINCOLOR_BLUEBERRY + SKINCOLOR_ROBIN,8, // SKINCOLOR_THISTLE + SKINCOLOR_MIDNIGHT,10, // SKINCOLOR_PURPLE + SKINCOLOR_FUCHSIA,11, // SKINCOLOR_PASTEL + SKINCOLOR_SUNSLAM,10, // SKINCOLOR_MOONSET + SKINCOLOR_DAWN,6, // SKINCOLOR_DUSK + SKINCOLOR_CROCODILE,8, // SKINCOLOR_VIOLET + SKINCOLOR_TURTLE,8, // SKINCOLOR_MAGENTA + SKINCOLOR_PASTEL,11, // SKINCOLOR_FUCHSIA + SKINCOLOR_MAROON,8, // SKINCOLOR_TOXIC + SKINCOLOR_TURQUOISE,8, // SKINCOLOR_MAUVE + SKINCOLOR_GARDEN,6, // SKINCOLOR_LAVENDER + SKINCOLOR_SWAMP,8, // SKINCOLOR_BYZANTIUM + SKINCOLOR_DREAM,8, // SKINCOLOR_POMEGRANATE + SKINCOLOR_JAWZ,6, // SKINCOLOR_LILAC + SKINCOLOR_TEA,8, // SKINCOLOR_BLOSSOM + SKINCOLOR_JET,8 // SKINCOLOR_TAFFY +}; + +UINT8 colortranslations[MAXTRANSLATIONS][16] = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // SKINCOLOR_NONE + { 0, 0, 0, 0, 1, 2, 5, 8, 9, 11, 14, 17, 20, 22, 25, 28}, // SKINCOLOR_WHITE + { 0, 1, 2, 3, 5, 7, 9, 12, 13, 15, 18, 20, 23, 25, 27, 30}, // SKINCOLOR_SILVER + { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}, // SKINCOLOR_GREY + { 3, 5, 8, 11, 15, 17, 19, 21, 23, 24, 25, 26, 27, 29, 30, 31}, // SKINCOLOR_NICKEL + { 4, 7, 11, 15, 20, 22, 24, 27, 28, 28, 28, 29, 29, 30, 30, 31}, // SKINCOLOR_BLACK + { 0, 1, 2, 3, 4, 10, 16, 21, 23, 24, 25, 26, 27, 28, 29, 31}, // SKINCOLOR_SKUNK + { 0, 0, 252, 252, 200, 201, 211, 14, 16, 18, 20, 22, 24, 26, 28, 31}, // SKINCOLOR_FAIRY + { 0, 80, 80, 81, 82, 218, 240, 11, 13, 16, 18, 21, 23, 26, 28, 31}, // SKINCOLOR_POPCORN + { 80, 88, 89, 98, 99, 91, 12, 14, 16, 18, 20, 22, 24, 26, 28, 31}, // SKINCOLOR_ARTICHOKE + { 0, 128, 129, 130, 146, 170, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31}, // SKINCOLOR_PIGEON + { 0, 1, 3, 5, 7, 9, 241, 242, 243, 245, 247, 249, 236, 237, 238, 239}, // SKINCOLOR_SEPIA + { 0, 208, 216, 217, 240, 241, 242, 243, 245, 247, 249, 250, 251, 237, 238, 239}, // SKINCOLOR_BEIGE + {208, 48, 216, 217, 218, 220, 221, 223, 224, 226, 228, 230, 232, 234, 236, 239}, // SKINCOLOR_CARAMEL + { 0, 208, 48, 216, 218, 221, 212, 213, 214, 215, 206, 207, 197, 198, 199, 254}, // SKINCOLOR_PEACH + {216, 217, 219, 221, 224, 225, 227, 229, 230, 232, 234, 235, 237, 239, 29, 30}, // SKINCOLOR_BROWN + {218, 221, 224, 227, 229, 231, 233, 235, 237, 239, 28, 28, 29, 29, 30, 31}, // SKINCOLOR_LEATHER + { 0, 208, 208, 209, 209, 210, 211, 211, 212, 213, 214, 215, 41, 43, 45, 46}, // SKINCOLOR_PINK + {209, 210, 211, 211, 212, 213, 214, 215, 41, 42, 43, 44, 45, 71, 46, 47}, // SKINCOLOR_ROSE + {216, 221, 224, 226, 228, 60, 61, 43, 44, 45, 71, 46, 47, 29, 30, 31}, // SKINCOLOR_CINNAMON + { 0, 208, 209, 210, 211, 213, 39, 40, 41, 43, 186, 186, 169, 169, 253, 254}, // SKINCOLOR_RUBY + { 0, 208, 209, 210, 32, 33, 34, 35, 37, 39, 41, 43, 44, 45, 46, 47}, // SKINCOLOR_RASPBERRY + {209, 210, 32, 34, 36, 38, 39, 40, 41, 42, 43, 44 , 45, 71, 46, 47}, // SKINCOLOR_RED + {210, 33, 35, 38, 40, 42, 43, 45, 71, 71, 46, 46, 47, 47, 30, 31}, // SKINCOLOR_CRIMSON + { 32, 33, 35, 37, 39, 41, 43, 237, 26, 26, 27, 27, 28, 29, 30, 31}, // SKINCOLOR_MAROON + { 0, 80, 81, 82, 83, 216, 210, 211, 212, 213, 214, 215, 43, 44, 71, 47}, // SKINCOLOR_LEMONADE + { 48, 49, 50, 51, 53, 34, 36, 38, 184, 185, 168, 168, 169, 169, 254, 31}, // SKINCOLOR_SCARLET + { 72, 73, 64, 51, 52, 54, 34, 36, 38, 40, 42, 43, 44, 71, 46, 47}, // SKINCOLOR_KETCHUP + { 0, 208, 216, 209, 210, 211, 212, 57, 58, 59, 60, 61, 63, 71, 47, 31}, // SKINCOLOR_DAWN + { 82, 72, 73, 64, 51, 53, 55, 213, 214, 195, 195, 173, 174, 175, 253, 254}, // SKINCOLOR_SUNSLAM + { 0, 0, 208, 208, 48, 49, 50, 52, 53, 54, 56, 57, 58, 60, 61, 63}, // SKINCOLOR_CREAMSICLE + {208, 48, 49, 50, 51, 52, 53, 54, 55, 57, 59, 60, 62, 44, 71, 47}, // SKINCOLOR_ORANGE + { 50, 52, 55, 56, 58, 59, 60, 61, 62, 63, 44, 45, 71, 46, 47, 30}, // SKINCOLOR_ROSEWOOD + { 80, 81, 82, 83, 64, 51, 52, 54, 55, 57, 58, 60, 61, 63, 71, 47}, // SKINCOLOR_TANGERINE + { 0, 80, 81, 82, 83, 84, 85, 86, 87, 245, 246, 248, 249, 251, 237, 239}, // SKINCOLOR_TAN + { 0, 80, 80, 81, 81, 49, 51, 222, 224, 227, 230, 233, 236, 239, 29, 31}, // SKINCOLOR_CREAM + { 0, 80, 81, 83, 64, 65, 66, 67, 68, 215, 69, 70, 44, 71, 46, 47}, // SKINCOLOR_GOLD + { 80, 81, 83, 64, 65, 223, 229, 196, 196, 197, 197, 198, 199, 29, 30, 31}, // SKINCOLOR_ROYAL + { 83, 64, 65, 66, 67, 215, 69, 70, 44, 44, 45, 71, 46, 47, 29, 31}, // SKINCOLOR_BRONZE + { 0, 82, 64, 65, 67, 68, 70, 237, 239, 28, 28, 29, 29, 30, 30, 31}, // SKINCOLOR_COPPER + { 0, 80, 81, 82, 83, 73, 84, 74, 64, 65, 66, 67, 68, 69, 70, 71}, // SKINCOLOR_YELLOW + { 80, 81, 82, 83, 64, 65, 65, 76, 76, 77, 77, 78, 79, 237, 239, 29}, // SKINCOLOR_MUSTARD + { 80, 81, 83, 72, 73, 74, 75, 76, 77, 78, 79, 236, 237, 238, 239, 30}, // SKINCOLOR_BANANA + { 80, 82, 73, 74, 75, 76, 77, 78, 79, 236, 237, 238, 239, 28, 29, 31}, // SKINCOLOR_OLIVE + { 0, 80, 81, 88, 88, 188, 189, 76, 76, 77, 78, 79, 236, 237, 238, 239}, // SKINCOLOR_CROCODILE + { 0, 80, 81, 88, 188, 189, 190, 191, 94, 94, 95, 95, 109, 110, 111, 31}, // SKINCOLOR_PERIDOT + { 0, 208, 216, 209, 218, 51, 65, 76, 191, 191, 126, 143, 138, 175, 169, 254}, // SKINCOLOR_VOMIT + { 81, 82, 83, 73, 64, 65, 66, 92, 92, 93, 93, 94, 95, 109, 110, 111}, // SKINCOLOR_GARDEN + { 0, 80, 81, 88, 188, 189, 114, 114, 115, 115, 116, 116, 117, 118, 119, 111}, // SKINCOLOR_LIME + { 83, 72, 73, 74, 75, 76, 102, 104, 105, 106, 107, 108, 109, 110, 111, 31}, // SKINCOLOR_HANDHELD + { 0, 80, 80, 81, 88, 89, 90, 91, 92, 93, 94, 95, 109, 110, 111, 31}, // SKINCOLOR_TEA + { 0, 80, 88, 88, 89, 90, 91, 102, 103, 104, 105, 106, 107, 108, 109, 110}, // SKINCOLOR_PISTACHIO + { 88, 89, 90, 91, 91, 92, 93, 94, 107, 107, 108, 108, 109, 109, 110, 111}, // SKINCOLOR_MOSS + {208, 84, 85, 240, 241, 243, 245, 94, 107, 108, 108, 109, 109, 110, 110, 111}, // SKINCOLOR_CAMOUFLAGE + { 0, 88, 88, 89, 89, 100, 101, 102, 125, 126, 143, 143, 138, 175, 169, 254}, // SKINCOLOR_MINT + { 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}, // SKINCOLOR_GREEN + { 97, 99, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 30, 30, 31}, // SKINCOLOR_PINETREE + { 96, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 119, 111}, // SKINCOLOR_TURTLE + { 96, 112, 113, 114, 115, 116, 117, 118, 119, 119, 29, 29, 30, 30, 31, 31}, // SKINCOLOR_SWAMP + { 0, 0, 208, 208, 48, 89, 98, 100, 148, 148, 172, 172, 173, 173, 174, 175}, // SKINCOLOR_DREAM + { 80, 88, 96, 112, 113, 124, 142, 149, 149, 173, 174, 175, 169, 253, 254, 31}, // SKINCOLOR_PLAGUE + { 0, 120, 121, 112, 113, 114, 115, 125, 125, 126, 126, 127, 138, 175, 253, 254}, // SKINCOLOR_EMERALD + {128, 129, 130, 131, 132, 133, 134, 115, 115, 116, 116, 117, 118, 119, 110, 111}, // SKINCOLOR_ALGAE + { 0, 128, 120, 121, 122, 123, 124, 125, 126, 126, 127, 127, 118, 118, 119, 111}, // SKINCOLOR_AQUAMARINE + {128, 120, 121, 122, 123, 141, 141, 142, 142, 143, 143, 138, 138, 139, 139, 31}, // SKINCOLOR_TURQUOISE + { 0, 120, 120, 121, 140, 141, 142, 143, 143, 138, 138, 139, 139, 254, 254, 31}, // SKINCOLOR_TEAL + { 0, 80, 81, 82, 83, 88, 121, 140, 133, 133, 134, 135, 136, 137, 138, 139}, // SKINCOLOR_ROBIN + { 0, 0, 128, 128, 255, 131, 132, 134, 142, 142, 143, 127, 118, 119, 110, 111}, // SKINCOLOR_CYAN + { 0, 0, 128, 128, 129, 146, 133, 134, 135, 149, 149, 173, 173, 174, 175, 31}, // SKINCOLOR_JAWZ + { 0, 128, 129, 130, 131, 132, 133, 135, 136, 136, 137, 137, 138, 138, 139, 31}, // SKINCOLOR_CERULEAN + {128, 129, 130, 132, 134, 135, 136, 137, 137, 138, 138, 139, 139, 29, 30, 31}, // SKINCOLOR_NAVY + { 0, 0, 0, 144, 144, 145, 9, 11, 14, 142, 136, 137, 138, 138, 139, 31}, // SKINCOLOR_PLATINUM + { 0, 0, 144, 144, 144, 145, 145, 145, 170, 170, 171, 171, 172, 173, 174, 175}, // SKINCOLOR_SLATE + { 0, 144, 144, 145, 145, 170, 170, 171, 171, 172, 172, 173, 173, 174, 175, 31}, // SKINCOLOR_STEEL + { 80, 81, 82, 83, 64, 65, 11, 171, 172, 173, 173, 157, 158, 159, 254, 31}, // SKINCOLOR_THUNDER + { 0, 83, 49, 50, 51, 32, 192, 148, 148, 172, 173, 174, 175, 29, 30, 31}, // SKINCOLOR_NOVA + {208, 48, 216, 217, 240, 241, 242, 171, 172, 173, 24, 25, 26, 28, 29, 31}, // SKINCOLOR_RUST + { 48, 218, 221, 224, 227, 231, 196, 173, 173, 174, 159, 159, 253, 253, 254, 31}, // SKINCOLOR_WRISTWATCH + {145, 146, 147, 148, 149, 173, 173, 174, 175, 175, 28, 28, 29, 29, 30, 31}, // SKINCOLOR_JET + { 0, 128, 129, 131, 133, 135, 149, 150, 152, 154, 156, 158, 159, 253, 254, 31}, // SKINCOLOR_SAPPHIRE + { 0, 0, 120, 120, 121, 133, 135, 149, 149, 166, 166, 167, 168, 169, 254, 31}, // SKINCOLOR_ULTRAMARINE + { 0, 0, 144, 144, 145, 146, 147, 149, 150, 152, 154, 155, 157, 159, 253, 254}, // SKINCOLOR_PERIWINKLE + {144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 155, 156, 158, 253, 254, 31}, // SKINCOLOR_BLUE + {146, 148, 149, 150, 152, 153, 155, 157, 159, 253, 253, 254, 254, 31, 31, 31}, // SKINCOLOR_MIDNIGHT + { 0, 144, 145, 146, 147, 171, 172, 166, 166, 167, 167, 168, 168, 175, 169, 253}, // SKINCOLOR_BLUEBERRY + { 0, 0, 0, 252, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 254}, // SKINCOLOR_THISTLE + { 0, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 168, 169, 169, 253, 254}, // SKINCOLOR_PURPLE + { 0, 128, 128, 129, 129, 146, 170, 162, 163, 164, 165, 166, 167, 168, 169, 254}, // SKINCOLOR_PASTEL + { 0, 144, 145, 146, 170, 162, 163, 184, 184, 207, 207, 44, 45, 46, 47, 31}, // SKINCOLOR_MOONSET + {252, 200, 201, 192, 193, 194, 172, 172, 173, 173, 174, 174, 175, 169, 253, 254}, // SKINCOLOR_DUSK + {176, 177, 178, 179, 180, 181, 182, 183, 184, 165, 165, 166, 167, 168, 169, 254}, // SKINCOLOR_VIOLET + {252, 200, 177, 177, 178, 179, 180, 181, 182, 183, 183, 184, 185, 186, 187, 31}, // SKINCOLOR_MAGENTA + {208, 209, 209, 32, 33, 182, 183, 184, 185, 185, 186, 186, 187, 253, 254, 31}, // SKINCOLOR_FUCHSIA + { 0, 0, 88, 88, 89, 6, 8, 10, 193, 194, 195, 184, 185, 186, 187, 31}, // SKINCOLOR_TOXIC + { 80, 81, 82, 83, 64, 50, 201, 192, 193, 194, 195, 173, 174, 175, 253, 254}, // SKINCOLOR_MAUVE + {252, 177, 179, 192, 193, 194, 195, 196, 196, 197, 197, 198, 198, 199, 30, 31}, // SKINCOLOR_LAVENDER + {145, 192, 193, 194, 195, 196, 197, 198, 199, 199, 29, 29, 30, 30, 31, 31}, // SKINCOLOR_BYZANTIUM + {208, 209, 210, 211, 212, 213, 214, 195, 195, 196, 196, 197, 198, 199, 29, 30}, // SKINCOLOR_POMEGRANATE + { 0, 0, 0, 252, 252, 176, 200, 201, 179, 192, 193, 194, 195, 196, 197, 198}, // SKINCOLOR_LILAC + { 0, 252, 252, 176, 200, 177, 201, 202, 202, 34, 36, 38, 40, 42, 45, 46}, // SKINCOLOR_BLOSSOM + { 0, 252, 252, 200, 200, 201, 202, 203, 204, 204, 205, 206, 207, 43, 45, 47}, // SKINCOLOR_TAFFY + + // THESE STILL NEED CONVERTED!!! + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 100, 104, 113, 116, 119}, // SKINCOLOR_SUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 96, 98, 101, 104, 113, 115, 117, 119}, // SKINCOLOR_SUPER2 + { 0, 0, 0, 0, 0, 0, 96, 98, 100, 102, 104, 113, 114, 116, 117, 119}, // SKINCOLOR_SUPER3 + { 0, 0, 0, 0, 96, 97, 99, 100, 102, 104, 113, 114, 115, 116, 117, 119}, // SKINCOLOR_SUPER4 + { 0, 0, 96, 0, 0, 0, 0, 0, 104, 113, 114, 115, 116, 117, 118, 119}, // SKINCOLOR_SUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 82, 85, 115, 117, 119}, // SKINCOLOR_TSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 80, 81, 83, 85, 115, 116, 117, 119}, // SKINCOLOR_TSUPER2 + { 0, 0, 0, 0, 0, 0, 80, 81, 82, 83, 85, 115, 116, 117, 118, 119}, // SKINCOLOR_TSUPER3 + { 0, 0, 0, 0, 80, 81, 82, 83, 84, 85, 115, 115, 116, 117, 118, 119}, // SKINCOLOR_TSUPER4 + { 0, 0, 80, 80, 81, 82, 83, 84, 85, 115, 115, 116, 117, 117, 118, 119}, // SKINCOLOR_TSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 123, 125, 127, 129, 132}, // SKINCOLOR_KSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 121, 122, 124, 125, 127, 128, 130, 132}, // SKINCOLOR_KSUPER2 + { 0, 0, 0, 0, 0, 0, 121, 122, 123, 124, 125, 127, 128, 129, 130, 132}, // SKINCOLOR_KSUPER3 + { 0, 0, 0, 0, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132}, // SKINCOLOR_KSUPER4 + { 0, 0, 121, 121, 122, 123, 124, 125, 126, 126, 127, 128, 129, 130, 131, 132}, // SKINCOLOR_KSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 122, 124, 248, 251, 255}, // SKINCOLOR_PSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 121, 122, 124, 248, 250, 252, 255}, // SKINCOLOR_PSUPER2 + { 0, 0, 0, 0, 0, 0, 1, 121, 122, 123, 124, 248, 249, 251, 253, 255}, // SKINCOLOR_PSUPER3 + { 0, 0, 0, 0, 1, 121, 122, 123, 124, 248, 249, 250, 251, 252, 253, 255}, // SKINCOLOR_PSUPER4 + { 0, 0, 1, 121, 122, 123, 124, 248, 248, 249, 250, 251, 252, 253, 254, 255}, // SKINCOLOR_PSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 225, 227, 228, 230, 232}, // SKINCOLOR_BSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 224, 225, 226, 227, 228, 229, 230, 232}, // SKINCOLOR_BSUPER2 + { 0, 0, 0, 0, 0, 0, 224, 224, 225, 226, 227, 228, 229, 230, 231, 232}, // SKINCOLOR_BSUPER3 + { 0, 0, 0, 0, 224, 224, 225, 226, 226, 227, 228, 229, 229, 230, 231, 232}, // SKINCOLOR_BSUPER4 + { 0, 0, 224, 224, 225, 225, 226, 227, 227, 228, 228, 229, 230, 230, 231, 232}, // SKINCOLOR_BSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 210, 212, 215, 220, 222}, // SKINCOLOR_ASUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 208, 209, 211, 213, 215, 220, 221, 223}, // SKINCOLOR_ASUPER2 + { 0, 0, 0, 0, 0, 0, 208, 209, 210, 211, 212, 213, 215, 220, 221, 223}, // SKINCOLOR_ASUPER3 + { 0, 0, 0, 0, 208, 209, 210, 211, 212, 213, 214, 215, 220, 221, 222, 223}, // SKINCOLOR_ASUPER4 + { 0, 0, 208, 208, 209, 210, 211, 211, 212, 213, 214, 215, 220, 221, 222, 223}, // SKINCOLOR_ASUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 160, 163, 167, 171, 175}, // SKINCOLOR_GSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 176, 176, 160, 163, 166, 169, 172, 175}, // SKINCOLOR_GSUPER2 + { 0, 0, 0, 0, 0, 0, 176, 176, 160, 162, 164, 166, 168, 170, 172, 175}, // SKINCOLOR_GSUPER3 + { 0, 0, 0, 0, 176, 176, 176, 160, 161, 163, 165, 167, 169, 171, 173, 175}, // SKINCOLOR_GSUPER4 + { 0, 0, 176, 176, 176, 160, 161, 163, 164, 166, 167, 169, 170, 172, 173, 175}, // SKINCOLOR_GSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // SKINCOLOR_WSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 9}, // SKINCOLOR_WSUPER2 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 8, 11}, // SKINCOLOR_WSUPER3 + { 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 4, 6, 8, 9, 11, 13}, // SKINCOLOR_WSUPER4 + { 0, 0, 0, 0, 1, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 15}, // SKINCOLOR_WSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 98, 99, 81, 73, 79}, // SKINCOLOR_CSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 96, 97, 98, 81, 81, 71, 75, 79}, // SKINCOLOR_CSUPER2 + { 0, 0, 0, 0, 0, 0, 96, 97, 98, 99, 81, 81, 70, 73, 76, 79}, // SKINCOLOR_CSUPER3 + { 0, 0, 0, 0, 96, 96, 97, 98, 99, 81, 81, 70, 72, 74, 76, 79}, // SKINCOLOR_CSUPER4 + { 0, 0, 96, 96, 97, 98, 98, 99, 81, 81, 69, 71, 73, 75, 77, 79}, // SKINCOLOR_CSUPER5 +}; + +/*-------------------------------------------------- + UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b) + + See header file for description. +--------------------------------------------------*/ + +UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b) +{ + UINT32 redweight = 1063 * r; + UINT32 greenweight = 3576 * g; + UINT32 blueweight = 361 * b; + UINT32 brightness = (redweight + greenweight + blueweight) / 5000; + return min(brightness, UINT8_MAX); +} + +/*-------------------------------------------------- + void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor) + + See header file for description. +--------------------------------------------------*/ + +void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor) +{ + INT32 i; + RGBA_t color; + UINT8 brightness; + INT32 j; + UINT8 colorbrightnesses[16]; + UINT16 brightdif; + INT32 temp; + + // first generate the brightness of all the colours of that skincolour + for (i = 0; i < 16; i++) + { + color = V_GetColor(colortranslations[skincolor][i]); + colorbrightnesses[i] = K_ColorRelativeLuminance(color.s.red, color.s.green, color.s.blue); + } + + // next, for every colour in the palette, choose the transcolor that has the closest brightness + for (i = 0; i < NUM_PALETTE_ENTRIES; i++) + { + if (i == 0 || i == 31) // pure black and pure white don't change + { + dest_colormap[i] = (UINT8)i; + continue; + } + + color = V_GetColor(i); + brightness = K_ColorRelativeLuminance(color.s.red, color.s.green, color.s.blue); + brightdif = 256; + + for (j = 0; j < 16; j++) + { + temp = abs((INT16)brightness - (INT16)colorbrightnesses[j]); + + if (temp < brightdif) + { + brightdif = (UINT16)temp; + dest_colormap[i] = colortranslations[skincolor][j]; + } + } + } +} + +/** \brief Generates a translation colormap for Kart, to replace R_GenerateTranslationColormap in r_draw.c + + \param dest_colormap colormap to populate + \param skinnum number of skin, TC_DEFAULT or TC_BOSS + \param color translation color + + \return void +*/ +void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color) +{ + INT32 i; + INT32 starttranscolor; + + // Handle a couple of simple special cases + if (skinnum == TC_BOSS + || skinnum == TC_ALLWHITE + || skinnum == TC_METALSONIC + || skinnum == TC_BLINK + || color == SKINCOLOR_NONE) + { + for (i = 0; i < NUM_PALETTE_ENTRIES; i++) + { + if (skinnum == TC_ALLWHITE) + dest_colormap[i] = 0; + else if (skinnum == TC_BLINK) + dest_colormap[i] = colortranslations[color][3]; + else + dest_colormap[i] = (UINT8)i; + } + + // White! + if (skinnum == TC_BOSS) + dest_colormap[31] = 0; + else if (skinnum == TC_METALSONIC) + dest_colormap[143] = 0; + + return; + } + else if (skinnum == TC_RAINBOW) + { + K_RainbowColormap(dest_colormap, color); + return; + } + + starttranscolor = (skinnum != TC_DEFAULT) ? skins[skinnum].starttranscolor : DEFAULT_STARTTRANSCOLOR; + + // Fill in the entries of the palette that are fixed + for (i = 0; i < starttranscolor; i++) + dest_colormap[i] = (UINT8)i; + + for (i = (UINT8)(starttranscolor + 16); i < NUM_PALETTE_ENTRIES; i++) + dest_colormap[i] = (UINT8)i; + + // Build the translated ramp + for (i = 0; i < SKIN_RAMP_LENGTH; i++) + { + // Sryder 2017-10-26: What was here before was most definitely not particularly readable, check above for new color translation table + dest_colormap[starttranscolor + i] = colortranslations[color][i]; + } +} + +/** \brief Pulls kart color by name, to replace R_GetColorByName in r_draw.c + + \param name color name + + \return 0 +*/ +UINT8 K_GetKartColorByName(const char *name) +{ + UINT8 color = (UINT8)atoi(name); + if (color > 0 && color < MAXSKINCOLORS) + return color; + for (color = 1; color < MAXSKINCOLORS; color++) + if (!stricmp(KartColor_Names[color], name)) + return color; + return 0; +} + +//} diff --git a/src/k_color.h b/src/k_color.h new file mode 100644 index 000000000..2a21473e2 --- /dev/null +++ b/src/k_color.h @@ -0,0 +1,87 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_color.h +/// \brief Skincolor & colormapping code + +#ifndef __K_COLOR__ +#define __K_COLOR__ + +#include "doomdef.h" +#include "doomtype.h" + +#define SKIN_RAMP_LENGTH 16 +#define DEFAULT_STARTTRANSCOLOR 96 +#define NUM_PALETTE_ENTRIES 256 + +extern UINT8 colortranslations[MAXTRANSLATIONS][16]; +extern const char *KartColor_Names[MAXSKINCOLORS]; +extern const UINT8 KartColor_Opposite[MAXSKINCOLORS*2]; + +/*-------------------------------------------------- + UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b); + + Gives you the brightness value of the provided RGB value, based on how the human eye interprets it. + See https://en.wikipedia.org/wiki/Relative_luminance for more info. + + Input Arguments:- + r - Red component + g - Green component + b - Blue component + + Return:- + Brightness value from 0 to 255. +--------------------------------------------------*/ + +UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b); + +/*-------------------------------------------------- + void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor); + + Generates a colormap to "colorize" all palette indicies + to the provided skincolor. + + Input Arguments:- + dest_colormap - Colormap to populate. + skincolor - Translation color. + + Return:- + None +--------------------------------------------------*/ + +void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor); + +/*-------------------------------------------------- + void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color); + + Generates a translation colormap for Kart, to replace R_GenerateTranslationColormap in r_draw.c + + Input Arguments:- + dest_colormap - Colormap to populate. + skinnum - Number of skin or translation mode (TC_ constants) + color - Translation color. + + Return:- + None +--------------------------------------------------*/ +void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color); + +/*-------------------------------------------------- + UINT8 K_GetKartColorByName(const char *name); + + Finds the corresponding SKINCOLOR_ constant to the string provided. + + Input Arguments:- + name - The name of the color desired. + + Return:- + SKINCOLOR_ constant, SKINCOLOR_NONE if invalid +--------------------------------------------------*/ +UINT8 K_GetKartColorByName(const char *name); + +#endif diff --git a/src/k_kart.c b/src/k_kart.c index 53963546d..2aaff27ee 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7,6 +7,7 @@ #include "k_kart.h" #include "k_battle.h" #include "k_pwrlv.h" +#include "k_color.h" #include "doomdef.h" #include "hu_stuff.h" #include "g_game.h" @@ -38,511 +39,6 @@ // indirectitemcooldown is timer before anyone's allowed another Shrink/SPB // mapreset is set when enough players fill an empty server -//{ SRB2kart Color Code - -#define SKIN_RAMP_LENGTH 16 -#define DEFAULT_STARTTRANSCOLOR 96 -#define NUM_PALETTE_ENTRIES 256 - -// These should be within 14 characters to fit on the character select screen -const char *KartColor_Names[MAXSKINCOLORS] = -{ - "None", // SKINCOLOR_NONE - "White", // SKINCOLOR_WHITE - "Silver", // SKINCOLOR_SILVER - "Grey", // SKINCOLOR_GREY - "Nickel", // SKINCOLOR_NICKEL - "Black", // SKINCOLOR_BLACK - "Fairy", // SKINCOLOR_FAIRY - "Popcorn", // SKINCOLOR_POPCORN - "Artichoke", // SKINCOLOR_ARTICHOKE - "Pigeon", // SKINCOLOR_PIGEON - "Sepia", // SKINCOLOR_SEPIA - "Beige", // SKINCOLOR_BEIGE - "Caramel", // SKINCOLOR_CARAMEL - "Peach", // SKINCOLOR_PEACH - "Brown", // SKINCOLOR_BROWN - "Leather", // SKINCOLOR_LEATHER - "Salmon", // SKINCOLOR_SALMON - "Pink", // SKINCOLOR_PINK - "Rose", // SKINCOLOR_ROSE - "Cinnamon", // SKINCOLOR_CINNAMON - "Ruby", // SKINCOLOR_RUBY - "Raspberry", // SKINCOLOR_RASPBERRY - "Red", // SKINCOLOR_RED - "Crimson", // SKINCOLOR_CRIMSON - "Maroon", // SKINCOLOR_MAROON - "Lemonade", // SKINCOLOR_LEMONADE - "Scarlet", // SKINCOLOR_SCARLET - "Ketchup", // SKINCOLOR_KETCHUP - "Dawn", // SKINCOLOR_DAWN - "Sunslam", // SKINCOLOR_SUNSLAM - "Creamsicle", // SKINCOLOR_CREAMSICLE - "Orange", // SKINCOLOR_ORANGE - "Rosewood", // SKINCOLOR_ROSEWOOD - "Tangerine", // SKINCOLOR_TANGERINE - "Tan", // SKINCOLOR_TAN - "Cream", // SKINCOLOR_CREAM - "Gold", // SKINCOLOR_GOLD - "Royal", // SKINCOLOR_ROYAL - "Bronze", // SKINCOLOR_BRONZE - "Copper", // SKINCOLOR_COPPER - "Yellow", // SKINCOLOR_YELLOW - "Mustard", // SKINCOLOR_MUSTARD - "Banana", // SKINCOLOR_BANANA - "Olive", // SKINCOLOR_OLIVE - "Crocodile", // SKINCOLOR_CROCODILE - "Peridot", // SKINCOLOR_PERIDOT - "Vomit", // SKINCOLOR_VOMIT - "Garden", // SKINCOLOR_GARDEN - "Lime", // SKINCOLOR_LIME - "Handheld", // SKINCOLOR_HANDHELD - "Tea", // SKINCOLOR_TEA - "Pistachio", // SKINCOLOR_PISTACHIO - "Moss", // SKINCOLOR_MOSS - "Camouflage", // SKINCOLOR_CAMOUFLAGE - "Robo-Hood", // SKINCOLOR_ROBOHOOD - "Mint", // SKINCOLOR_MINT - "Green", // SKINCOLOR_GREEN - "Pinetree", // SKINCOLOR_PINETREE - "Turtle", // SKINCOLOR_TURTLE - "Swamp", // SKINCOLOR_SWAMP - "Dream", // SKINCOLOR_DREAM - "Plague", // SKINCOLOR_PLAGUE - "Emerald", // SKINCOLOR_EMERALD - "Algae", // SKINCOLOR_ALGAE - "Caribbean", // SKINCOLOR_CARIBBEAN - "Azure", // SKINCOLOR_AZURE - "Aquamarine", // SKINCOLOR_AQUAMARINE - "Turquoise", // SKINCOLOR_TURQUOISE - "Teal", // SKINCOLOR_TEAL - "Cyan", // SKINCOLOR_CYAN - "Jawz", // SKINCOLOR_JAWZ - "Cerulean", // SKINCOLOR_CERULEAN - "Navy", // SKINCOLOR_NAVY - "Platinum", // SKINCOLOR_PLATINUM - "Slate", // SKINCOLOR_SLATE - "Steel", // SKINCOLOR_STEEL - "Thunder", // SKINCOLOR_THUNDER - "Nova", // SKINCOLOR_NOVA - "Rust", // SKINCOLOR_RUST - "Wristwatch", // SKINCOLOR_WRISTWATCH - "Jet", // SKINCOLOR_JET - "Sapphire", // SKINCOLOR_SAPPHIRE - "Ultramarine", // SKINCOLOR_ULTRAMARINE - "Periwinkle", // SKINCOLOR_PERIWINKLE - "Blue", // SKINCOLOR_BLUE - "Blueberry", // SKINCOLOR_BLUEBERRY - "Thistle", // SKINCOLOR_THISTLE - "Purple", // SKINCOLOR_PURPLE - "Pastel", // SKINCOLOR_PASTEL - "Moonset", // SKINCOLOR_MOONSET - "Dusk", // SKINCOLOR_DUSK - "Violet", // SKINCOLOR_VIOLET - "Magenta", // SKINCOLOR_MAGENTA - "Fuchsia", // SKINCOLOR_FUCHSIA - "Toxic", // SKINCOLOR_TOXIC - "Mauve", // SKINCOLOR_MAUVE - "Lavender", // SKINCOLOR_LAVENDER - "Byzantium", // SKINCOLOR_BYZANTIUM - "Pomegranate", // SKINCOLOR_POMEGRANATE - "Lilac", // SKINCOLOR_LILAC - "Taffy" // SKINCOLOR_TAFFY -}; - -// Color_Opposite replacement; frame setting has not been changed from 8 for most, should be done later -const UINT8 KartColor_Opposite[MAXSKINCOLORS*2] = -{ - SKINCOLOR_NONE,8, // SKINCOLOR_NONE - SKINCOLOR_BLACK,8, // SKINCOLOR_WHITE - SKINCOLOR_NICKEL,8, // SKINCOLOR_SILVER - SKINCOLOR_GREY,8, // SKINCOLOR_GREY - SKINCOLOR_SILVER,8, // SKINCOLOR_NICKEL - SKINCOLOR_WHITE,8, // SKINCOLOR_BLACK - SKINCOLOR_ARTICHOKE,12, // SKINCOLOR_FAIRY - SKINCOLOR_PIGEON,12, // SKINCOLOR_POPCORN - SKINCOLOR_FAIRY,12, // SKINCOLOR_ARTICHOKE - SKINCOLOR_POPCORN,12, // SKINCOLOR_PIGEON - SKINCOLOR_LEATHER,6, // SKINCOLOR_SEPIA - SKINCOLOR_BROWN,2, // SKINCOLOR_BEIGE - SKINCOLOR_CERULEAN,8, // SKINCOLOR_CARAMEL - SKINCOLOR_CYAN,8, // SKINCOLOR_PEACH - SKINCOLOR_BEIGE,8, // SKINCOLOR_BROWN - SKINCOLOR_SEPIA,8, // SKINCOLOR_LEATHER - SKINCOLOR_TEA,8, // SKINCOLOR_SALMON - SKINCOLOR_PISTACHIO,8, // SKINCOLOR_PINK - SKINCOLOR_MOSS,8, // SKINCOLOR_ROSE - SKINCOLOR_WRISTWATCH,6, // SKINCOLOR_CINNAMON - SKINCOLOR_SAPPHIRE,8, // SKINCOLOR_RUBY - SKINCOLOR_MINT,8, // SKINCOLOR_RASPBERRY - SKINCOLOR_GREEN,6, // SKINCOLOR_RED - SKINCOLOR_PINETREE,6, // SKINCOLOR_CRIMSON - SKINCOLOR_TOXIC,8, // SKINCOLOR_MAROON - SKINCOLOR_THUNDER,8, // SKINCOLOR_LEMONADE - SKINCOLOR_ALGAE,10, // SKINCOLOR_SCARLET - SKINCOLOR_MUSTARD,10, // SKINCOLOR_KETCHUP - SKINCOLOR_DUSK,8, // SKINCOLOR_DAWN - SKINCOLOR_MOONSET,8, // SKINCOLOR_SUNSLAM - SKINCOLOR_PERIWINKLE,8, // SKINCOLOR_CREAMSICLE - SKINCOLOR_BLUE,8, // SKINCOLOR_ORANGE - SKINCOLOR_BLUEBERRY,6, // SKINCOLOR_ROSEWOOD - SKINCOLOR_LIME,8, // SKINCOLOR_TANGERINE - SKINCOLOR_RUST,8, // SKINCOLOR_TAN - SKINCOLOR_COPPER,10, // SKINCOLOR_CREAM - SKINCOLOR_SLATE,8, // SKINCOLOR_GOLD - SKINCOLOR_PLATINUM,6, // SKINCOLOR_ROYAL - SKINCOLOR_STEEL,8, // SKINCOLOR_BRONZE - SKINCOLOR_CREAM,6, // SKINCOLOR_COPPER - SKINCOLOR_AQUAMARINE,8, // SKINCOLOR_YELLOW - SKINCOLOR_KETCHUP,8, // SKINCOLOR_MUSTARD - SKINCOLOR_EMERALD,8, // SKINCOLOR_BANANA - SKINCOLOR_TEAL,8, // SKINCOLOR_OLIVE - SKINCOLOR_VIOLET,8, // SKINCOLOR_CROCODILE - SKINCOLOR_NAVY,6, // SKINCOLOR_PERIDOT - SKINCOLOR_ROBOHOOD,8, // SKINCOLOR_VOMIT - SKINCOLOR_LAVENDER,6, // SKINCOLOR_GARDEN - SKINCOLOR_TANGERINE,8, // SKINCOLOR_LIME - SKINCOLOR_ULTRAMARINE,8, // SKINCOLOR_HANDHELD - SKINCOLOR_SALMON,8, // SKINCOLOR_TEA - SKINCOLOR_PINK,6, // SKINCOLOR_PISTACHIO - SKINCOLOR_ROSE,8, // SKINCOLOR_MOSS - SKINCOLOR_CAMOUFLAGE,8, // SKINCOLOR_CAMOUFLAGE - SKINCOLOR_VOMIT,8, // SKINCOLOR_ROBOHOOD - SKINCOLOR_RASPBERRY,8, // SKINCOLOR_MINT - SKINCOLOR_RED,8, // SKINCOLOR_GREEN - SKINCOLOR_CRIMSON,8, // SKINCOLOR_PINETREE - SKINCOLOR_MAGENTA,8, // SKINCOLOR_TURTLE - SKINCOLOR_BYZANTIUM,8, // SKINCOLOR_SWAMP - SKINCOLOR_POMEGRANATE,8, // SKINCOLOR_DREAM - SKINCOLOR_NOVA,8, // SKINCOLOR_PLAGUE - SKINCOLOR_BANANA,8, // SKINCOLOR_EMERALD - SKINCOLOR_SCARLET,10, // SKINCOLOR_ALGAE - SKINCOLOR_PURPLE,8, // SKINCOLOR_CARIBBEAN - SKINCOLOR_THISTLE,8, // SKINCOLOR_AZURE - SKINCOLOR_YELLOW,8, // SKINCOLOR_AQUAMARINE - SKINCOLOR_MAUVE,10, // SKINCOLOR_TURQUOISE - SKINCOLOR_OLIVE,8, // SKINCOLOR_TEAL - SKINCOLOR_PEACH,8, // SKINCOLOR_CYAN - SKINCOLOR_LILAC,10, // SKINCOLOR_JAWZ - SKINCOLOR_CARAMEL,8, // SKINCOLOR_CERULEAN - SKINCOLOR_PERIDOT,8, // SKINCOLOR_NAVY - SKINCOLOR_ROYAL,8, // SKINCOLOR_PLATINUM - SKINCOLOR_GOLD,10, // SKINCOLOR_SLATE - SKINCOLOR_BRONZE,10, // SKINCOLOR_STEEL - SKINCOLOR_LEMONADE,8, // SKINCOLOR_THUNDER - SKINCOLOR_PLAGUE,10, // SKINCOLOR_NOVA - SKINCOLOR_TAN,8, // SKINCOLOR_RUST - SKINCOLOR_CINNAMON,8, // SKINCOLOR_WRISTWATCH - SKINCOLOR_TAFFY,8, // SKINCOLOR_JET - SKINCOLOR_RUBY,6, // SKINCOLOR_SAPPHIRE - SKINCOLOR_HANDHELD,10, // SKINCOLOR_ULTRAMARINE - SKINCOLOR_CREAMSICLE,8, // SKINCOLOR_PERIWINKLE - SKINCOLOR_ORANGE,8, // SKINCOLOR_BLUE - SKINCOLOR_ROSEWOOD,8, // SKINCOLOR_BLUEBERRY - SKINCOLOR_AZURE,8, // SKINCOLOR_THISTLE - SKINCOLOR_CARIBBEAN,10, // SKINCOLOR_PURPLE - SKINCOLOR_FUCHSIA,11, // SKINCOLOR_PASTEL - SKINCOLOR_SUNSLAM,10, // SKINCOLOR_MOONSET - SKINCOLOR_DAWN,6, // SKINCOLOR_DUSK - SKINCOLOR_CROCODILE,8, // SKINCOLOR_VIOLET - SKINCOLOR_TURTLE,8, // SKINCOLOR_MAGENTA - SKINCOLOR_PASTEL,11, // SKINCOLOR_FUCHSIA - SKINCOLOR_MAROON,8, // SKINCOLOR_TOXIC - SKINCOLOR_TURQUOISE,8, // SKINCOLOR_MAUVE - SKINCOLOR_GARDEN,6, // SKINCOLOR_LAVENDER - SKINCOLOR_SWAMP,8, // SKINCOLOR_BYZANTIUM - SKINCOLOR_DREAM,8, // SKINCOLOR_POMEGRANATE - SKINCOLOR_JAWZ,6, // SKINCOLOR_LILAC - SKINCOLOR_JET,8 // SKINCOLOR_TAFFY -}; - -UINT8 colortranslations[MAXTRANSLATIONS][16] = { - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // SKINCOLOR_NONE - { 0, 0, 0, 0, 1, 2, 5, 8, 9, 11, 14, 17, 20, 22, 25, 28}, // SKINCOLOR_WHITE - { 0, 1, 2, 3, 5, 7, 9, 12, 13, 15, 18, 20, 23, 25, 27, 30}, // SKINCOLOR_SILVER - { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}, // SKINCOLOR_GREY - { 3, 5, 8, 11, 15, 17, 19, 21, 23, 24, 25, 26, 27, 29, 30, 31}, // SKINCOLOR_NICKEL - { 4, 7, 11, 15, 20, 22, 24, 27, 28, 28, 28, 29, 29, 30, 30, 31}, // SKINCOLOR_BLACK - { 0, 0, 252, 252, 200, 201, 211, 14, 16, 18, 20, 22, 24, 26, 28, 31}, // SKINCOLOR_FAIRY - { 0, 80, 80, 81, 82, 218, 240, 11, 13, 16, 18, 21, 23, 26, 28, 31}, // SKINCOLOR_POPCORN - { 80, 88, 89, 98, 99, 91, 12, 14, 16, 18, 20, 22, 24, 26, 28, 31}, // SKINCOLOR_ARTICHOKE - { 0, 128, 129, 130, 146, 170, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31}, // SKINCOLOR_PIGEON - { 0, 1, 3, 5, 7, 9, 241, 242, 243, 245, 247, 249, 236, 237, 238, 239}, // SKINCOLOR_SEPIA - { 0, 208, 216, 217, 240, 241, 242, 243, 245, 247, 249, 250, 251, 237, 238, 239}, // SKINCOLOR_BEIGE - {208, 48, 216, 217, 218, 220, 221, 223, 224, 226, 228, 230, 232, 234, 236, 239}, // SKINCOLOR_CARAMEL - { 0, 208, 48, 216, 218, 221, 212, 213, 214, 215, 206, 207, 197, 198, 199, 254}, // SKINCOLOR_PEACH - {216, 217, 219, 221, 224, 225, 227, 229, 230, 232, 234, 235, 237, 239, 29, 30}, // SKINCOLOR_BROWN - {218, 221, 224, 227, 229, 231, 233, 235, 237, 239, 28, 28, 29, 29, 30, 31}, // SKINCOLOR_LEATHER - { 0, 0, 0, 208, 208, 209, 210, 32, 34, 35, 36, 38, 40, 42, 44, 46}, // SKINCOLOR_SALMON - { 0, 208, 208, 209, 209, 210, 211, 211, 212, 213, 214, 215, 41, 43, 45, 46}, // SKINCOLOR_PINK - {209, 210, 211, 211, 212, 213, 214, 215, 41, 42, 43, 44, 45, 71, 46, 47}, // SKINCOLOR_ROSE - {216, 221, 224, 226, 228, 60, 61, 43, 44, 45, 71, 46, 47, 29, 30, 31}, // SKINCOLOR_CINNAMON - { 0, 208, 209, 210, 211, 213, 39, 40, 41, 43, 186, 186, 169, 169, 253, 254}, // SKINCOLOR_RUBY - { 0, 208, 209, 210, 32, 33, 34, 35, 37, 39, 41, 43, 44, 45, 46, 47}, // SKINCOLOR_RASPBERRY - {209, 210, 32, 34, 36, 38, 39, 40, 41, 42, 43, 44 , 45, 71, 46, 47}, // SKINCOLOR_RED - {210, 33, 35, 38, 40, 42, 43, 45, 71, 71, 46, 46, 47, 47, 30, 31}, // SKINCOLOR_CRIMSON - { 32, 33, 35, 37, 39, 41, 43, 237, 26, 26, 27, 27, 28, 29, 30, 31}, // SKINCOLOR_MAROON - { 0, 80, 81, 82, 83, 216, 210, 211, 212, 213, 214, 215, 43, 44, 71, 47}, // SKINCOLOR_LEMONADE - { 48, 49, 50, 51, 53, 34, 36, 38, 184, 185, 168, 168, 169, 169, 254, 31}, // SKINCOLOR_SCARLET - { 72, 73, 64, 51, 52, 54, 34, 36, 38, 40, 42, 43, 44, 71, 46, 47}, // SKINCOLOR_KETCHUP - { 0, 208, 216, 209, 210, 211, 212, 57, 58, 59, 60, 61, 63, 71, 47, 31}, // SKINCOLOR_DAWN - { 82, 72, 73, 64, 51, 53, 55, 213, 214, 195, 195, 173, 174, 175, 253, 254}, // SKINCOLOR_SUNSLAM - { 0, 0, 208, 208, 48, 49, 50, 52, 53, 54, 56, 57, 58, 60, 61, 63}, // SKINCOLOR_CREAMSICLE - {208, 48, 49, 50, 51, 52, 53, 54, 55, 57, 59, 60, 62, 44, 71, 47}, // SKINCOLOR_ORANGE - { 50, 52, 55, 56, 58, 59, 60, 61, 62, 63, 44, 45, 71, 46, 47, 30}, // SKINCOLOR_ROSEWOOD - { 80, 81, 82, 83, 64, 51, 52, 54, 55, 57, 58, 60, 61, 63, 71, 47}, // SKINCOLOR_TANGERINE - { 0, 80, 81, 82, 83, 84, 85, 86, 87, 245, 246, 248, 249, 251, 237, 239}, // SKINCOLOR_TAN - { 0, 80, 80, 81, 81, 49, 51, 222, 224, 227, 230, 233, 236, 239, 29, 31}, // SKINCOLOR_CREAM - { 0, 80, 81, 83, 64, 65, 66, 67, 68, 215, 69, 70, 44, 71, 46, 47}, // SKINCOLOR_GOLD - { 80, 81, 83, 64, 65, 223, 229, 196, 196, 197, 197, 198, 199, 29, 30, 31}, // SKINCOLOR_ROYAL - { 83, 64, 65, 66, 67, 215, 69, 70, 44, 44, 45, 71, 46, 47, 29, 31}, // SKINCOLOR_BRONZE - { 0, 82, 64, 65, 67, 68, 70, 237, 239, 28, 28, 29, 29, 30, 30, 31}, // SKINCOLOR_COPPER - { 0, 80, 81, 82, 83, 73, 84, 74, 64, 65, 66, 67, 68, 69, 70, 71}, // SKINCOLOR_YELLOW - { 80, 81, 82, 83, 64, 65, 65, 76, 76, 77, 77, 78, 79, 237, 239, 29}, // SKINCOLOR_MUSTARD - { 80, 81, 83, 72, 73, 74, 75, 76, 77, 78, 79, 236, 237, 238, 239, 30}, // SKINCOLOR_BANANA - { 80, 82, 73, 74, 75, 76, 77, 78, 79, 236, 237, 238, 239, 28, 29, 31}, // SKINCOLOR_OLIVE - { 0, 80, 81, 88, 88, 188, 189, 76, 76, 77, 78, 79, 236, 237, 238, 239}, // SKINCOLOR_CROCODILE - { 0, 80, 81, 88, 188, 189, 190, 191, 94, 94, 95, 95, 109, 110, 111, 31}, // SKINCOLOR_PERIDOT - { 0, 208, 216, 209, 218, 51, 65, 76, 191, 191, 126, 143, 138, 175, 169, 254}, // SKINCOLOR_VOMIT - { 81, 82, 83, 73, 64, 65, 66, 92, 92, 93, 93, 94, 95, 109, 110, 111}, // SKINCOLOR_GARDEN - { 0, 80, 81, 82, 83, 88, 89, 99, 100, 102, 104, 126, 143, 138, 139, 31}, // SKINCOLOR_LIME - { 83, 72, 73, 74, 75, 76, 102, 104, 105, 106, 107, 108, 109, 110, 111, 31}, // SKINCOLOR_HANDHELD - { 0, 80, 80, 81, 88, 89, 90, 91, 92, 93, 94, 95, 109, 110, 111, 31}, // SKINCOLOR_TEA - { 0, 80, 88, 88, 89, 90, 91, 102, 103, 104, 105, 106, 107, 108, 109, 110}, // SKINCOLOR_PISTACHIO - { 88, 89, 90, 91, 91, 92, 93, 94, 107, 107, 108, 108, 109, 109, 110, 111}, // SKINCOLOR_MOSS - {208, 84, 85, 240, 241, 243, 245, 94, 107, 108, 108, 109, 109, 110, 110, 111}, // SKINCOLOR_CAMOUFLAGE - { 0, 88, 98, 101, 103, 104, 105, 94, 94, 107, 95, 109, 110, 111, 30, 31}, // SKINCOLOR_ROBOHOOD - { 0, 88, 88, 89, 89, 100, 101, 102, 125, 126, 143, 143, 138, 175, 169, 254}, // SKINCOLOR_MINT - { 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}, // SKINCOLOR_GREEN - { 97, 99, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 30, 30, 31}, // SKINCOLOR_PINETREE - { 96, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 119, 111}, // SKINCOLOR_TURTLE - { 96, 112, 113, 114, 115, 116, 117, 118, 119, 119, 29, 29, 30, 30, 31, 31}, // SKINCOLOR_SWAMP - { 0, 0, 208, 208, 48, 89, 98, 100, 148, 148, 172, 172, 173, 173, 174, 175}, // SKINCOLOR_DREAM - { 80, 88, 96, 112, 113, 124, 142, 149, 149, 173, 174, 175, 169, 253, 254, 31}, // SKINCOLOR_PLAGUE - { 0, 120, 121, 112, 113, 114, 115, 125, 125, 126, 126, 127, 138, 175, 253, 254}, // SKINCOLOR_EMERALD - {128, 128, 129, 129, 130, 140, 124, 103, 104, 116, 116, 117, 118, 119, 111, 31}, // SKINCOLOR_ALGAE - { 0, 88, 89, 97, 113, 141, 135, 136, 136, 173, 173, 174, 174, 175, 199, 31}, // SKINCOLOR_CARIBBEAN - { 0, 80, 81, 82, 89, 140, 134, 135, 136, 172, 196, 197, 198, 199, 30, 31}, // SKINCOLOR_AZURE - { 0, 128, 120, 121, 122, 123, 124, 125, 126, 126, 127, 127, 118, 118, 119, 111}, // SKINCOLOR_AQUAMARINE - {128, 120, 121, 122, 123, 141, 141, 142, 142, 143, 143, 138, 138, 139, 139, 31}, // SKINCOLOR_TURQUOISE - { 0, 120, 120, 121, 140, 141, 142, 143, 143, 138, 138, 139, 139, 254, 254, 31}, // SKINCOLOR_TEAL - { 0, 0, 128, 128, 255, 131, 132, 134, 142, 142, 143, 127, 118, 119, 110, 111}, // SKINCOLOR_CYAN - { 0, 0, 128, 128, 129, 146, 133, 134, 135, 149, 149, 173, 173, 174, 175, 31}, // SKINCOLOR_JAWZ - { 0, 128, 129, 130, 131, 132, 133, 135, 136, 136, 137, 137, 138, 138, 139, 31}, // SKINCOLOR_CERULEAN - {128, 129, 130, 132, 134, 135, 136, 137, 137, 138, 138, 139, 139, 29, 30, 31}, // SKINCOLOR_NAVY - { 0, 0, 0, 144, 144, 145, 9, 11, 14, 142, 136, 137, 138, 138, 139, 31}, // SKINCOLOR_PLATINUM - { 0, 0, 144, 144, 144, 145, 145, 145, 170, 170, 171, 171, 172, 173, 174, 175}, // SKINCOLOR_SLATE - { 0, 144, 144, 145, 145, 170, 170, 171, 171, 172, 172, 173, 173, 174, 175, 31}, // SKINCOLOR_STEEL - { 80, 81, 82, 83, 64, 65, 11, 171, 172, 173, 173, 157, 158, 159, 254, 31}, // SKINCOLOR_THUNDER - { 0, 83, 49, 50, 51, 32, 192, 148, 148, 172, 173, 174, 175, 29, 30, 31}, // SKINCOLOR_NOVA - {208, 48, 216, 217, 240, 241, 242, 171, 172, 173, 24, 25, 26, 28, 29, 31}, // SKINCOLOR_RUST - { 48, 218, 221, 224, 227, 231, 196, 173, 173, 174, 159, 159, 253, 253, 254, 31}, // SKINCOLOR_WRISTWATCH - {145, 146, 147, 148, 149, 173, 173, 174, 175, 175, 28, 28, 29, 29, 30, 31}, // SKINCOLOR_JET - { 0, 128, 129, 131, 133, 135, 149, 150, 152, 154, 156, 158, 159, 253, 254, 31}, // SKINCOLOR_SAPPHIRE - { 0, 0, 120, 120, 121, 133, 135, 149, 149, 166, 166, 167, 168, 169, 254, 31}, // SKINCOLOR_ULTRAMARINE - { 0, 0, 144, 144, 145, 146, 147, 149, 150, 152, 154, 155, 157, 159, 253, 254}, // SKINCOLOR_PERIWINKLE - {144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 155, 156, 158, 253, 254, 31}, // SKINCOLOR_BLUE - {146, 148, 149, 150, 152, 153, 155, 157, 159, 253, 253, 254, 254, 31, 31, 31}, // SKINCOLOR_BLUEBERRY - { 0, 0, 0, 252, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 254}, // SKINCOLOR_THISTLE - { 0, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 168, 169, 169, 253, 254}, // SKINCOLOR_PURPLE - { 0, 128, 128, 129, 129, 146, 170, 162, 163, 164, 165, 166, 167, 168, 169, 254}, // SKINCOLOR_PASTEL - { 0, 144, 145, 146, 170, 162, 163, 184, 184, 207, 207, 44, 45, 46, 47, 31}, // SKINCOLOR_MOONSET - {252, 200, 201, 192, 193, 194, 172, 172, 173, 173, 174, 174, 175, 169, 253, 254}, // SKINCOLOR_DUSK - {176, 177, 178, 179, 180, 181, 182, 183, 184, 164, 165, 166, 167, 168, 169, 254}, // SKINCOLOR_VIOLET - {176, 177, 178, 179, 180, 181, 182, 183, 184, 184, 185, 185, 186, 187, 30, 31}, // SKINCOLOR_MAGENTA - {208, 209, 209, 32, 33, 182, 183, 184, 185, 185, 186, 186, 187, 253, 254, 31}, // SKINCOLOR_FUCHSIA - { 0, 0, 88, 88, 89, 6, 8, 10, 193, 194, 195, 184, 185, 186, 187, 31}, // SKINCOLOR_TOXIC - { 80, 81, 82, 83, 64, 50, 201, 192, 193, 194, 195, 173, 174, 175, 253, 254}, // SKINCOLOR_MAUVE - {252, 177, 179, 192, 193, 194, 195, 196, 196, 197, 197, 198, 198, 199, 30, 31}, // SKINCOLOR_LAVENDER - {145, 192, 193, 194, 195, 196, 197, 198, 199, 199, 29, 29, 30, 30, 31, 31}, // SKINCOLOR_BYZANTIUM - {208, 209, 210, 211, 212, 213, 214, 195, 195, 196, 196, 197, 198, 199, 29, 30}, // SKINCOLOR_POMEGRANATE - { 0, 0, 0, 252, 252, 176, 200, 201, 179, 192, 193, 194, 195, 196, 197, 198}, // SKINCOLOR_LILAC - { 0, 252, 252, 200, 200, 201, 202, 203, 204, 204, 205, 206, 207, 43, 45, 47}, // SKINCOLOR_TAFFY - - // THESE STILL NEED CONVERTED!!! - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 100, 104, 113, 116, 119}, // SKINCOLOR_SUPER1 - { 0, 0, 0, 0, 0, 0, 0, 0, 96, 98, 101, 104, 113, 115, 117, 119}, // SKINCOLOR_SUPER2 - { 0, 0, 0, 0, 0, 0, 96, 98, 100, 102, 104, 113, 114, 116, 117, 119}, // SKINCOLOR_SUPER3 - { 0, 0, 0, 0, 96, 97, 99, 100, 102, 104, 113, 114, 115, 116, 117, 119}, // SKINCOLOR_SUPER4 - { 0, 0, 96, 0, 0, 0, 0, 0, 104, 113, 114, 115, 116, 117, 118, 119}, // SKINCOLOR_SUPER5 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 82, 85, 115, 117, 119}, // SKINCOLOR_TSUPER1 - { 0, 0, 0, 0, 0, 0, 0, 0, 80, 81, 83, 85, 115, 116, 117, 119}, // SKINCOLOR_TSUPER2 - { 0, 0, 0, 0, 0, 0, 80, 81, 82, 83, 85, 115, 116, 117, 118, 119}, // SKINCOLOR_TSUPER3 - { 0, 0, 0, 0, 80, 81, 82, 83, 84, 85, 115, 115, 116, 117, 118, 119}, // SKINCOLOR_TSUPER4 - { 0, 0, 80, 80, 81, 82, 83, 84, 85, 115, 115, 116, 117, 117, 118, 119}, // SKINCOLOR_TSUPER5 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 123, 125, 127, 129, 132}, // SKINCOLOR_KSUPER1 - { 0, 0, 0, 0, 0, 0, 0, 0, 121, 122, 124, 125, 127, 128, 130, 132}, // SKINCOLOR_KSUPER2 - { 0, 0, 0, 0, 0, 0, 121, 122, 123, 124, 125, 127, 128, 129, 130, 132}, // SKINCOLOR_KSUPER3 - { 0, 0, 0, 0, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132}, // SKINCOLOR_KSUPER4 - { 0, 0, 121, 121, 122, 123, 124, 125, 126, 126, 127, 128, 129, 130, 131, 132}, // SKINCOLOR_KSUPER5 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 122, 124, 248, 251, 255}, // SKINCOLOR_PSUPER1 - { 0, 0, 0, 0, 0, 0, 0, 0, 1, 121, 122, 124, 248, 250, 252, 255}, // SKINCOLOR_PSUPER2 - { 0, 0, 0, 0, 0, 0, 1, 121, 122, 123, 124, 248, 249, 251, 253, 255}, // SKINCOLOR_PSUPER3 - { 0, 0, 0, 0, 1, 121, 122, 123, 124, 248, 249, 250, 251, 252, 253, 255}, // SKINCOLOR_PSUPER4 - { 0, 0, 1, 121, 122, 123, 124, 248, 248, 249, 250, 251, 252, 253, 254, 255}, // SKINCOLOR_PSUPER5 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 225, 227, 228, 230, 232}, // SKINCOLOR_BSUPER1 - { 0, 0, 0, 0, 0, 0, 0, 0, 224, 225, 226, 227, 228, 229, 230, 232}, // SKINCOLOR_BSUPER2 - { 0, 0, 0, 0, 0, 0, 224, 224, 225, 226, 227, 228, 229, 230, 231, 232}, // SKINCOLOR_BSUPER3 - { 0, 0, 0, 0, 224, 224, 225, 226, 226, 227, 228, 229, 229, 230, 231, 232}, // SKINCOLOR_BSUPER4 - { 0, 0, 224, 224, 225, 225, 226, 227, 227, 228, 228, 229, 230, 230, 231, 232}, // SKINCOLOR_BSUPER5 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 210, 212, 215, 220, 222}, // SKINCOLOR_ASUPER1 - { 0, 0, 0, 0, 0, 0, 0, 0, 208, 209, 211, 213, 215, 220, 221, 223}, // SKINCOLOR_ASUPER2 - { 0, 0, 0, 0, 0, 0, 208, 209, 210, 211, 212, 213, 215, 220, 221, 223}, // SKINCOLOR_ASUPER3 - { 0, 0, 0, 0, 208, 209, 210, 211, 212, 213, 214, 215, 220, 221, 222, 223}, // SKINCOLOR_ASUPER4 - { 0, 0, 208, 208, 209, 210, 211, 211, 212, 213, 214, 215, 220, 221, 222, 223}, // SKINCOLOR_ASUPER5 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 160, 163, 167, 171, 175}, // SKINCOLOR_GSUPER1 - { 0, 0, 0, 0, 0, 0, 0, 0, 176, 176, 160, 163, 166, 169, 172, 175}, // SKINCOLOR_GSUPER2 - { 0, 0, 0, 0, 0, 0, 176, 176, 160, 162, 164, 166, 168, 170, 172, 175}, // SKINCOLOR_GSUPER3 - { 0, 0, 0, 0, 176, 176, 176, 160, 161, 163, 165, 167, 169, 171, 173, 175}, // SKINCOLOR_GSUPER4 - { 0, 0, 176, 176, 176, 160, 161, 163, 164, 166, 167, 169, 170, 172, 173, 175}, // SKINCOLOR_GSUPER5 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // SKINCOLOR_WSUPER1 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 9}, // SKINCOLOR_WSUPER2 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 8, 11}, // SKINCOLOR_WSUPER3 - { 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 4, 6, 8, 9, 11, 13}, // SKINCOLOR_WSUPER4 - { 0, 0, 0, 0, 1, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 15}, // SKINCOLOR_WSUPER5 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 98, 99, 81, 73, 79}, // SKINCOLOR_CSUPER1 - { 0, 0, 0, 0, 0, 0, 0, 0, 96, 97, 98, 81, 81, 71, 75, 79}, // SKINCOLOR_CSUPER2 - { 0, 0, 0, 0, 0, 0, 96, 97, 98, 99, 81, 81, 70, 73, 76, 79}, // SKINCOLOR_CSUPER3 - { 0, 0, 0, 0, 96, 96, 97, 98, 99, 81, 81, 70, 72, 74, 76, 79}, // SKINCOLOR_CSUPER4 - { 0, 0, 96, 96, 97, 98, 98, 99, 81, 81, 69, 71, 73, 75, 77, 79}, // SKINCOLOR_CSUPER5 -}; - -// Define for getting accurate color brightness readings according to how the human eye sees them. -// https://en.wikipedia.org/wiki/Relative_luminance -// 0.2126 to red -// 0.7152 to green -// 0.0722 to blue -// (See this same define in hw_md2.c!) -#define SETBRIGHTNESS(brightness,r,g,b) \ - brightness = (UINT8)(((1063*(UINT16)(r))/5000) + ((3576*(UINT16)(g))/5000) + ((361*(UINT16)(b))/5000)) - -/** \brief Generates the rainbow colourmaps that are used when a player has the invincibility power - - \param dest_colormap colormap to populate - \param skincolor translation color -*/ -void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor) -{ - INT32 i; - RGBA_t color; - UINT8 brightness; - INT32 j; - UINT8 colorbrightnesses[16]; - UINT16 brightdif; - INT32 temp; - - // first generate the brightness of all the colours of that skincolour - for (i = 0; i < 16; i++) - { - color = V_GetColor(colortranslations[skincolor][i]); - SETBRIGHTNESS(colorbrightnesses[i], color.s.red, color.s.green, color.s.blue); - } - - // next, for every colour in the palette, choose the transcolor that has the closest brightness - for (i = 0; i < NUM_PALETTE_ENTRIES; i++) - { - if (i == 0 || i == 31) // pure black and pure white don't change - { - dest_colormap[i] = (UINT8)i; - continue; - } - color = V_GetColor(i); - SETBRIGHTNESS(brightness, color.s.red, color.s.green, color.s.blue); - brightdif = 256; - for (j = 0; j < 16; j++) - { - temp = abs((INT16)brightness - (INT16)colorbrightnesses[j]); - if (temp < brightdif) - { - brightdif = (UINT16)temp; - dest_colormap[i] = colortranslations[skincolor][j]; - } - } - } -} - -#undef SETBRIGHTNESS - -/** \brief Generates a translation colormap for Kart, to replace R_GenerateTranslationColormap in r_draw.c - - \param dest_colormap colormap to populate - \param skinnum number of skin, TC_DEFAULT or TC_BOSS - \param color translation color - - \return void -*/ -void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color) -{ - INT32 i; - INT32 starttranscolor; - - // Handle a couple of simple special cases - if (skinnum == TC_BOSS - || skinnum == TC_ALLWHITE - || skinnum == TC_METALSONIC - || skinnum == TC_BLINK - || color == SKINCOLOR_NONE) - { - for (i = 0; i < NUM_PALETTE_ENTRIES; i++) - { - if (skinnum == TC_ALLWHITE) - dest_colormap[i] = 0; - else if (skinnum == TC_BLINK) - dest_colormap[i] = colortranslations[color][3]; - else - dest_colormap[i] = (UINT8)i; - } - - // White! - if (skinnum == TC_BOSS) - dest_colormap[31] = 0; - else if (skinnum == TC_METALSONIC) - dest_colormap[143] = 0; - - return; - } - else if (skinnum == TC_RAINBOW) - { - K_RainbowColormap(dest_colormap, color); - return; - } - - starttranscolor = (skinnum != TC_DEFAULT) ? skins[skinnum].starttranscolor : DEFAULT_STARTTRANSCOLOR; - - // Fill in the entries of the palette that are fixed - for (i = 0; i < starttranscolor; i++) - dest_colormap[i] = (UINT8)i; - - for (i = (UINT8)(starttranscolor + 16); i < NUM_PALETTE_ENTRIES; i++) - dest_colormap[i] = (UINT8)i; - - // Build the translated ramp - for (i = 0; i < SKIN_RAMP_LENGTH; i++) - { - // Sryder 2017-10-26: What was here before was most definitely not particularly readable, check above for new color translation table - dest_colormap[starttranscolor + i] = colortranslations[color][i]; - } -} - -/** \brief Pulls kart color by name, to replace R_GetColorByName in r_draw.c - - \param name color name - - \return 0 -*/ -UINT8 K_GetKartColorByName(const char *name) -{ - UINT8 color = (UINT8)atoi(name); - if (color > 0 && color < MAXSKINCOLORS) - return color; - for (color = 1; color < MAXSKINCOLORS; color++) - if (!stricmp(KartColor_Names[color], name)) - return color; - return 0; -} - -//} - player_t *K_GetItemBoxPlayer(mobj_t *mobj) { fixed_t closest = INT32_MAX; @@ -2758,6 +2254,12 @@ boolean K_ApplyOffroad(player_t *player) return true; } +static fixed_t K_FlameShieldDashVar(INT32 val) +{ + // 1 second = 75% + 50% top speed + return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE) / 2); +} + // sets k_boostpower, k_speedboost, and k_accelboost to whatever we need it to be static void K_GetKartBoostPower(player_t *player) { @@ -2794,10 +2296,7 @@ static void K_GetKartBoostPower(player_t *player) ADDBOOST((3*FRACUNIT)/8, 3*FRACUNIT); // + 37.5% top speed, + 300% acceleration if (player->kartstuff[k_flamedash]) // Flame Shield dash - { - fixed_t dashval = ((player->kartstuff[k_flamedash]<kartstuff[k_flamedash]), 3*FRACUNIT); // + infinite top speed, + 300% acceleration if (player->kartstuff[k_startboost]) // Startup Boost ADDBOOST(FRACUNIT/4, 6*FRACUNIT); // + 25% top speed, + 600% acceleration @@ -3417,10 +2916,7 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b K_PlayPainSound(player->mo); if (P_IsDisplayPlayer(player)) - { - quake.intensity = 64*FRACUNIT; - quake.time = 5; - } + P_StartQuake(64<kartstuff[k_instashield] = 15; K_DropItems(player); @@ -3584,6 +3080,8 @@ void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 } } +#define MINEQUAKEDIST 4096 + // Spawns the purely visual explosion void K_SpawnMineExplosion(mobj_t *source, UINT8 color) { @@ -3592,6 +3090,28 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color) mobj_t *dust; mobj_t *truc; INT32 speed, speed2; + INT32 pnum; + player_t *p; + + // check for potential display players near the source so we can have a sick earthquake / flashpal. + for (pnum = 0; pnum < MAXPLAYERS; pnum++) + { + p = &players[pnum]; + + if (!playeringame[pnum] || !P_IsDisplayPlayer(p)) + continue; + + if (R_PointToDist2(p->mo->x, p->mo->y, source->x, source->y) < mapobjectscale*MINEQUAKEDIST) + { + P_StartQuake(55<mo, source)) + { + bombflashtimer = TICRATE*2; + P_FlashPal(p, 1, 1); + } + break; // we can break right now because quakes are global to all split players somehow. + } + } K_MatchGenericExtraFlags(smoldering, source); smoldering->tics = TICRATE*3; @@ -3662,6 +3182,8 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color) } } +#undef MINEQUAKEDIST + static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, INT32 flags2, fixed_t speed) { mobj_t *th; @@ -5504,7 +5026,7 @@ static void K_MoveHeldObjects(player_t *player) if (cur->type == MT_EGGMANITEM_SHIELD) { // Decided that this should use their "canon" color. - cur->color = SKINCOLOR_BLACK; + cur->color = SKINCOLOR_BLACK; } cur->flags &= ~MF_NOCLIPTHING; @@ -5873,7 +5395,7 @@ static void K_UpdateInvincibilitySounds(player_t *player) { if (player->kartstuff[k_invincibilitytimer] > 0) // Prioritize invincibility sfxnum = sfx_alarmi; - else if (player->kartstuff[k_growshrinktimer] > 0) + else if (player->kartstuff[k_growshrinktimer] > 0) sfxnum = sfx_alarmg; } else @@ -7079,12 +6601,20 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) turnvalue = 5*turnvalue/4; } + if (player->kartstuff[k_flamedash] > 0) + { + fixed_t multiplier = K_FlameShieldDashVar(player->kartstuff[k_flamedash]); + multiplier = FRACUNIT + (FixedDiv(multiplier, FRACUNIT/2) / 4); + turnvalue = FixedMul(turnvalue * FRACUNIT, multiplier) / FRACUNIT; + } + if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) { turnvalue = 3*turnvalue/2; } - turnvalue = FixedMul(turnvalue * FRACUNIT, weightadjust) / FRACUNIT; // Weight has a small effect on turning + // Weight has a small effect on turning + turnvalue = FixedMul(turnvalue * FRACUNIT, weightadjust) / FRACUNIT; return turnvalue; } @@ -7447,6 +6977,7 @@ void K_StripOther(player_t *player) static INT32 K_FlameShieldMax(player_t *player) { UINT32 disttofinish = 0; + UINT32 distv = DISTVAR; UINT8 numplayers = 0; UINT8 i; @@ -7468,8 +6999,8 @@ static INT32 K_FlameShieldMax(player_t *player) } disttofinish = player->distancetofinish - disttofinish; - - return min(16, 1 + (disttofinish / DISTVAR)); + distv = FixedDiv(distv * FRACUNIT, mapobjectscale) / FRACUNIT; + return min(16, 1 + (disttofinish / distv)); } // @@ -9948,7 +9479,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I y2 = y; - if (playerconsole[tab[i].num] == 0 && server_lagless) + if (netgame && playerconsole[tab[i].num] == 0 && server_lagless && !players[tab[i].num].bot) { y2 = ( y - 4 ); diff --git a/src/k_kart.h b/src/k_kart.h index 4021efb01..1bbfce1b9 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -12,12 +12,6 @@ #define KART_FULLTURN 800 -UINT8 colortranslations[MAXTRANSLATIONS][16]; -extern const char *KartColor_Names[MAXSKINCOLORS]; -extern const UINT8 KartColor_Opposite[MAXSKINCOLORS*2]; -void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor); -void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color); -UINT8 K_GetKartColorByName(const char *name); player_t *K_GetItemBoxPlayer(mobj_t *mobj); void K_RegisterKartStuff(void); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 421af0238..15eba4136 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -27,6 +27,7 @@ #include "console.h" #include "k_kart.h" // SRB2Kart #include "k_battle.h" +#include "k_color.h" #include "d_netcmd.h" // IsPlayerAdmin #include "lua_script.h" @@ -1518,8 +1519,7 @@ static int lib_pStartQuake(lua_State *L) quake.radius = luaL_optinteger(L, 4, 512*FRACUNIT); // These things are actually used in 2.1. - quake.intensity = q_intensity; - quake.time = q_time; + P_StartQuake(q_intensity, q_time); return 0; } diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c index 8c9d10062..b86a03c3b 100644 --- a/src/lua_mathlib.c +++ b/src/lua_mathlib.c @@ -16,7 +16,7 @@ #include "tables.h" #include "p_local.h" #include "doomstat.h" // for ALL7EMERALDS -#include "k_kart.h" // KartColor_Opposite +#include "k_color.h" // KartColor_Opposite #include "lua_script.h" #include "lua_libs.h" diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 47efd729f..cd5f4d267 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -87,7 +87,10 @@ enum mobj_e { #endif mobj_colorized, mobj_shadowscale, - mobj_whiteshadow + mobj_whiteshadow, + mobj_sprxoff, + mobj_spryoff, + mobj_sprzoff }; static const char *const mobj_opt[] = { @@ -153,6 +156,9 @@ static const char *const mobj_opt[] = { "colorized", "shadowscale", "whiteshadow", + "sprxoff", + "spryoff", + "sprzoff", NULL}; #define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field]) @@ -370,6 +376,15 @@ static int mobj_get(lua_State *L) case mobj_whiteshadow: lua_pushboolean(L, mo->whiteshadow); break; + case mobj_sprxoff: + lua_pushfixed(L, mo->sprxoff); + break; + case mobj_spryoff: + lua_pushfixed(L, mo->spryoff); + break; + case mobj_sprzoff: + lua_pushfixed(L, mo->sprzoff); + break; default: // extra custom variables in Lua memory lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); @@ -693,6 +708,15 @@ static int mobj_set(lua_State *L) case mobj_whiteshadow: mo->whiteshadow = luaL_checkboolean(L, 3); break; + case mobj_sprxoff: + mo->sprxoff = luaL_checkfixed(L, 3); + break; + case mobj_spryoff: + mo->spryoff = luaL_checkfixed(L, 3); + break; + case mobj_sprzoff: + mo->sprzoff = luaL_checkfixed(L, 3); + break; default: lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); diff --git a/src/m_cond.c b/src/m_cond.c index e91ac1273..6ed684ea9 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -20,7 +20,7 @@ #include "g_game.h" // record info #include "r_things.h" // numskins //#include "r_draw.h" // R_GetColorByName -#include "k_kart.h" // K_GetKartColorByName +#include "k_color.h" // K_GetKartColorByName #include "k_pwrlv.h" // Map triggers for linedef executors diff --git a/src/m_menu.c b/src/m_menu.c index 7dd5cf7c9..72efa88a5 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -59,6 +59,7 @@ #include "k_kart.h" // SRB2kart #include "k_pwrlv.h" #include "d_player.h" // KITEM_ constants +#include "k_color.h" #include "k_grandprix.h" #include "i_joy.h" // for joystick menu controls diff --git a/src/p_inter.c b/src/p_inter.c index d94b37ff9..57bd839c7 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -637,7 +637,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_SetTarget(&special->tracer, toucher); toucher->flags |= MF_NOGRAVITY; toucher->momz = (8*toucher->scale) * P_MobjFlip(toucher); - S_StartSound(toucher, sfx_s1b2); + S_StartSound(toucher, sfx_s1b2); return; // ***************************************** // @@ -3241,10 +3241,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da K_KartPainEnergyFling(player); if (P_IsDisplayPlayer(player)) - { - quake.intensity = 32*FRACUNIT; - quake.time = 5; - } + P_StartQuake(32<flags & ML_IMPASSABLE) // block objects from moving through this linedef. return false; - if (tmthing->player && ld->flags & ML_BLOCKPLAYERS) + if (tmthing->player && !tmthing->player->spectator && ld->flags & ML_BLOCKPLAYERS) return false; // SRB2Kart: Only block players, not items } @@ -3222,7 +3222,7 @@ static boolean PTR_SlideTraverse(intercept_t *in) if (li->flags & ML_IMPASSABLE) goto isblocking; - if (slidemo->player && li->flags & ML_BLOCKPLAYERS) + if (slidemo->player && !slidemo->player->spectator && li->flags & ML_BLOCKPLAYERS) goto isblocking; } diff --git a/src/p_mobj.c b/src/p_mobj.c index 2830ed509..9cb23cf0d 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -37,6 +37,7 @@ #include "k_kart.h" #include "k_battle.h" +#include "k_color.h" // protos. //static CV_PossibleValue_t viewheight_cons_t[] = {{16, "MIN"}, {56, "MAX"}, {0, NULL}}; @@ -8723,7 +8724,7 @@ void P_MobjThinker(mobj_t *mobj) fixed_t destx, desty; statenum_t curstate; statenum_t underlayst = S_NULL; - INT32 flamemax = mobj->target->player->kartstuff[k_flamelength] * flameseg; + INT32 flamemax = 0; if (!mobj->target || !mobj->target->health || !mobj->target->player || mobj->target->player->kartstuff[k_curshield] != KSHIELD_FLAME) @@ -8731,6 +8732,9 @@ void P_MobjThinker(mobj_t *mobj) P_RemoveMobj(mobj); return; } + + flamemax = mobj->target->player->kartstuff[k_flamelength] * flameseg; + P_SetScale(mobj, (mobj->destscale = (5*mobj->target->scale)>>2)); curstate = ((mobj->tics == 1) ? (mobj->state->nextstate) : ((statenum_t)(mobj->state-states))); @@ -8810,7 +8814,7 @@ void P_MobjThinker(mobj_t *mobj) if (curstate >= S_FLAMESHIELD1 && curstate < S_FLAMESHIELDDASH1 && ((curstate-S_FLAMESHIELD1) & 1)) viewingangle += ANGLE_180; - + destx = mobj->target->x + P_ReturnThrustX(mobj->target, viewingangle, mobj->scale>>4); desty = mobj->target->y + P_ReturnThrustY(mobj->target, viewingangle, mobj->scale>>4); } @@ -13005,16 +13009,30 @@ ML_NOCLIMB : Direction not controllable mobj->angle = FixedAngle(mthing->angle*FRACUNIT); + if ((mobj->flags & MF_SPRING) + && mobj->info->damage != 0 + && mobj->info->mass == 0) + { + // Offset sprite of horizontal springs + angle_t a = mobj->angle + ANGLE_180; + mobj->sprxoff = FixedMul(mobj->radius, FINECOSINE(a >> ANGLETOFINESHIFT)); + mobj->spryoff = FixedMul(mobj->radius, FINESINE(a >> ANGLETOFINESHIFT)); + } + if ((mthing->options & MTF_AMBUSH) && (mthing->options & MTF_OBJECTSPECIAL) && (mobj->flags & MF_PUSHABLE)) + { mobj->flags2 |= MF2_CLASSICPUSH; + } else { if (mthing->options & MTF_AMBUSH) { - if (mobj->flags & MF_SPRING && mobj->info->damage) + if ((mobj->flags & MF_SPRING) && mobj->info->damage) + { mobj->angle += ANGLE_22h; + } if (mobj->flags & MF_NIGHTSITEM) { diff --git a/src/p_mobj.h b/src/p_mobj.h index 5cc93a56d..746529360 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -378,6 +378,8 @@ typedef struct mobj_s fixed_t shadowscale; // If this object casts a shadow, and the size relative to radius boolean whiteshadow; // Use white shadow, set to true by default for fullbright objects + fixed_t sprxoff, spryoff, sprzoff; // Sprite offsets in real space, does NOT affect position or collision + // WARNING: New fields must be added separately to savegame and Lua. } mobj_t; diff --git a/src/p_polyobj.c b/src/p_polyobj.c index 03fb10d0f..12708a44b 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -1407,7 +1407,10 @@ static boolean Polyobj_rotate(polyobj_t *po, angle_t delta, UINT8 turnthings) { // update seg angles (used only by renderer) for (i = 0; i < po->segCount; ++i) + { po->segs[i]->angle += delta; + P_UpdateSegLightOffset(po->segs[i]); + } // update polyobject's angle po->angle += delta; diff --git a/src/p_saveg.c b/src/p_saveg.c index 354a96b1c..34e2eb425 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -275,7 +275,7 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].botvars.rival); WRITEUINT32(save_p, players[i].botvars.itemdelay); WRITEUINT32(save_p, players[i].botvars.itemconfirm); - WRITEINT16(save_p, players[i].botvars.lastturn); + WRITESINT8(save_p, players[i].botvars.turnconfirm); } } @@ -454,7 +454,7 @@ static void P_NetUnArchivePlayers(void) players[i].botvars.rival = (boolean)READUINT8(save_p); players[i].botvars.itemdelay = READUINT32(save_p); players[i].botvars.itemconfirm = READUINT32(save_p); - players[i].botvars.lastturn = READINT16(save_p); + players[i].botvars.turnconfirm = READSINT8(save_p); } } diff --git a/src/p_setup.c b/src/p_setup.c index 84b1586e7..2f73f38cb 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -448,6 +448,27 @@ static inline float P_SegLengthFloat(seg_t *seg) } #endif +/** Updates the light offset + * + * \param li Seg to update the light offsets of + */ +void P_UpdateSegLightOffset(seg_t *li) +{ + const UINT8 contrast = 16; + fixed_t extralight = 0; + + extralight = -((fixed_t)contrast*FRACUNIT) + + FixedDiv(AngleFixed(R_PointToAngle2(0, 0, + abs(li->v1->x - li->v2->x), + abs(li->v1->y - li->v2->y))), 90*FRACUNIT) * ((fixed_t)contrast * 2); + + // Between -2 and 2 for software, -16 and 16 for hardware + li->lightOffset = FixedFloor((extralight / 8) + (FRACUNIT / 2)) / FRACUNIT; +#ifdef HWRENDER + li->hwLightOffset = FixedFloor(extralight + (FRACUNIT / 2)) / FRACUNIT; +#endif +} + /** Loads the SEGS resource from a level. * * \param lump Lump number of the SEGS resource. @@ -494,6 +515,8 @@ static void P_LoadRawSegs(UINT8 *data, size_t i) li->numlights = 0; li->rlights = NULL; + + P_UpdateSegLightOffset(li); } } @@ -1093,7 +1116,7 @@ static void P_LoadThings(void) || mt->type == 1705 || mt->type == 1713 || mt->type == 1800) { sector_t *mtsector = R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS)->sector; - + mt->mobj = NULL; // Z for objects diff --git a/src/p_setup.h b/src/p_setup.h index 9abcfe5f6..c0b30ca6a 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -66,6 +66,7 @@ boolean P_DelWadFile(void); #endif boolean P_RunSOC(const char *socfilename); void P_WriteThings(lumpnum_t lump); +void P_UpdateSegLightOffset(seg_t *li); size_t P_PrecacheLevelFlats(void); void P_AllocMapHeader(INT16 i); diff --git a/src/p_spec.c b/src/p_spec.c index 0c75051ad..8678f31cc 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2209,7 +2209,8 @@ static void K_HandleLapDecrement(player_t *player) void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing) { // only used for the players currently - if (thing && thing->player) + if (!(thing && thing->player && !thing->player->spectator && !(thing->player->pflags & PF_TIMEOVER))) + return; { player_t *player = thing->player; switch (line->special) @@ -8232,3 +8233,11 @@ static void P_SearchForDisableLinedefs(void) } } } + +// Rudimentary function to start a earthquake. +// epicenter and radius are not yet used. +void P_StartQuake(fixed_t intensity, tic_t time) +{ + quake.intensity = intensity; + quake.time = time; +} diff --git a/src/p_spec.h b/src/p_spec.h index a49946c2a..6c918ef11 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -19,6 +19,10 @@ extern mobj_t *skyboxmo[2]; +// Something that should've been done long ago??? +// We won't be using epicenter or radius anytime soon so I don't think it's worth it yet. +void P_StartQuake(fixed_t intensity, tic_t time); + // GETSECSPECIAL (specialval, section) // // Pulls out the special # from a particular section. diff --git a/src/p_tick.c b/src/p_tick.c index 385985c34..d57a6dc43 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -722,6 +722,9 @@ void P_Ticker(boolean run) K_CalculateBattleWanted(); } + if (bombflashtimer) + bombflashtimer--; // Bomb seizure prevention + if (quake.time) { fixed_t ir = quake.intensity>>1; diff --git a/src/p_user.c b/src/p_user.c index bf1efe138..12050ae11 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1667,6 +1667,10 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) ghost->modeltilt = mobj->modeltilt; #endif + ghost->sprxoff = mobj->sprxoff; + ghost->spryoff = mobj->spryoff; + ghost->sprzoff = mobj->sprzoff; + if (mobj->flags2 & MF2_OBJECTFLIP) ghost->flags |= MF2_OBJECTFLIP; @@ -4173,7 +4177,12 @@ static void P_3dMovement(player_t *player) if (K_PlayerUsesBotMovement(player)) { - div = FixedMul(div, K_BotRubberband(player)); + fixed_t rubberband = K_BotRubberband(player); + + if (rubberband > FRACUNIT) + { + div = FixedMul(div, 4*rubberband); + } } newspeed = speed - FixedDiv((speed - airspeedcap), div); diff --git a/src/r_defs.h b/src/r_defs.h index 02c82bf56..b186fecdf 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -565,6 +565,12 @@ typedef struct seg_s polyobj_t *polyseg; boolean dontrenderme; #endif + + // Fake contrast calculated on level load + SINT8 lightOffset; +#ifdef HWRENDER + INT16 hwLightOffset; +#endif } seg_t; // diff --git a/src/r_draw.c b/src/r_draw.c index 70e487342..7746693bf 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -25,7 +25,7 @@ #include "w_wad.h" #include "z_zone.h" #include "console.h" // Until buffering gets finished -#include "k_kart.h" // SRB2kart +#include "k_color.h" // SRB2kart #ifdef HWRENDER #include "hardware/hw_main.h" diff --git a/src/r_segs.c b/src/r_segs.c index 2304540fe..efb1f4887 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -412,10 +412,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) if (rlight->extra_colormap && rlight->extra_colormap->fog) ; - else if (curline->v1->y == curline->v2->y) - lightnum--; - else if (curline->v1->x == curline->v2->x) - lightnum++; + else + lightnum += curline->lightOffset; rlight->lightnum = lightnum; } @@ -435,10 +433,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) if (colfunc == R_DrawFogColumn_8 || (frontsector->extra_colormap && frontsector->extra_colormap->fog)) ; - else if (curline->v1->y == curline->v2->y) - lightnum--; - else if (curline->v1->x == curline->v2->x) - lightnum++; + else + lightnum += curline->lightOffset; if (lightnum < 0) walllights = scalelight[0]; @@ -932,10 +928,8 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) if (pfloor->flags & FF_FOG || rlight->flags & FF_FOG || (rlight->extra_colormap && rlight->extra_colormap->fog)) ; - else if (curline->v1->y == curline->v2->y) - rlight->lightnum--; - else if (curline->v1->x == curline->v2->x) - rlight->lightnum++; + else + rlight->lightnum += curline->lightOffset; p++; } @@ -955,11 +949,10 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) lightnum = R_FakeFlat(frontsector, &tempsec, &templight, &templight, false) ->lightlevel >> LIGHTSEGSHIFT; - if (pfloor->flags & FF_FOG || (frontsector->extra_colormap && frontsector->extra_colormap->fog)); - else if (curline->v1->y == curline->v2->y) - lightnum--; - else if (curline->v1->x == curline->v2->x) - lightnum++; + if (pfloor->flags & FF_FOG || (frontsector->extra_colormap && frontsector->extra_colormap->fog)) + ; + else + lightnum += curline->lightOffset; if (lightnum < 0) walllights = scalelight[0]; @@ -1492,10 +1485,8 @@ static void R_RenderSegLoop (void) if (dc_lightlist[i].extra_colormap) ; - else if (curline->v1->y == curline->v2->y) - lightnum--; - else if (curline->v1->x == curline->v2->x) - lightnum++; + else + lightnum += curline->lightOffset; if (lightnum < 0) xwalllights = scalelight[0]; @@ -2639,10 +2630,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) // OPTIMIZE: get rid of LIGHTSEGSHIFT globally lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT); - if (curline->v1->y == curline->v2->y) - lightnum--; - else if (curline->v1->x == curline->v2->x) - lightnum++; + lightnum += curline->lightOffset; if (lightnum < 0) walllights = scalelight[0]; diff --git a/src/r_things.c b/src/r_things.c index ae625171c..c43f430c7 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -28,7 +28,7 @@ #include "dehacked.h" // get_number (for thok) #include "d_netfil.h" // blargh. for nameonly(). #include "m_cheat.h" // objectplace -#include "k_kart.h" // SRB2kart +#include "k_color.h" // SRB2kart #include "p_local.h" // stplyr #ifdef HWRENDER #include "hardware/hw_md2.h" @@ -1436,6 +1436,10 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, // static void R_ProjectSprite(mobj_t *thing) { + const fixed_t thingxpos = thing->x + thing->sprxoff; + const fixed_t thingypos = thing->y + thing->spryoff; + const fixed_t thingzpos = thing->z + thing->sprzoff; + fixed_t tr_x, tr_y; fixed_t gxt, gyt; fixed_t tx, tz; @@ -1472,8 +1476,8 @@ static void R_ProjectSprite(mobj_t *thing) fixed_t this_scale = thing->scale; // transform the origin point - tr_x = thing->x - viewx; - tr_y = thing->y - viewy; + tr_x = thingxpos - viewx; + tr_y = thingypos - viewy; gxt = FixedMul(tr_x, viewcos); gyt = -FixedMul(tr_y, viewsin); @@ -1539,9 +1543,9 @@ static void R_ProjectSprite(mobj_t *thing) if (sprframe->rotate != SRF_SINGLE || papersprite) { if (thing->player) - ang = R_PointToAngle (thing->x, thing->y) - thing->player->frameangle; + ang = R_PointToAngle (thingxpos, thingypos) - thing->player->frameangle; else - ang = R_PointToAngle (thing->x, thing->y) - thing->angle; + ang = R_PointToAngle (thingxpos, thingypos) - thing->angle; } if (sprframe->rotate == SRF_SINGLE) @@ -1554,7 +1558,7 @@ static void R_ProjectSprite(mobj_t *thing) else { // choose a different rotation based on player view - //ang = R_PointToAngle (thing->x, thing->y) - thing->angle; + //ang = R_PointToAngle (thingxpos, thingypos) - thing->angle; if ((ang < ANGLE_180) && (sprframe->rotate & SRF_RIGHT)) // See from right rot = 6; // F7 slot @@ -1701,7 +1705,7 @@ static void R_ProjectSprite(mobj_t *thing) if (x2 < portalclipstart || x1 > portalclipend) return; - if (P_PointOnLineSide(thing->x, thing->y, portalclipline) != 0) + if (P_PointOnLineSide(thingxpos, thingypos, portalclipline) != 0) return; } @@ -1711,12 +1715,12 @@ static void R_ProjectSprite(mobj_t *thing) // When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned. // sprite height - sprite topoffset is the proper inverse of the vertical offset, of course. // remember gz and gzt should be seperated by sprite height, not thing height - thing height can be shorter than the sprite itself sometimes! - gz = thing->z + thing->height - FixedMul(spritecachedinfo[lump].topoffset, this_scale); + gz = thingzpos + thing->height - FixedMul(spritecachedinfo[lump].topoffset, this_scale); gzt = gz + FixedMul(spritecachedinfo[lump].height, this_scale); } else { - gzt = thing->z + FixedMul(spritecachedinfo[lump].topoffset, this_scale); + gzt = thingzpos + FixedMul(spritecachedinfo[lump].topoffset, this_scale); gz = gzt - FixedMul(spritecachedinfo[lump].height, this_scale); } @@ -1733,7 +1737,7 @@ static void R_ProjectSprite(mobj_t *thing) light = thing->subsector->sector->numlights - 1; for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) { - fixed_t h = thing->subsector->sector->lightlist[lightnum].slope ? P_GetZAt(thing->subsector->sector->lightlist[lightnum].slope, thing->x, thing->y) + fixed_t h = thing->subsector->sector->lightlist[lightnum].slope ? P_GetZAt(thing->subsector->sector->lightlist[lightnum].slope, thingxpos, thingypos) : thing->subsector->sector->lightlist[lightnum].height; if (h <= gzt) { light = lightnum - 1; @@ -1762,12 +1766,12 @@ static void R_ProjectSprite(mobj_t *thing) if (heightsec != -1 && phs != -1) // only clip things which are in special sectors { if (viewz < sectors[phs].floorheight ? - thing->z >= sectors[heightsec].floorheight : + thingzpos >= sectors[heightsec].floorheight : gzt < sectors[heightsec].floorheight) return; if (viewz > sectors[phs].ceilingheight ? gzt < sectors[heightsec].ceilingheight && viewz >= sectors[heightsec].ceilingheight : - thing->z >= sectors[heightsec].ceilingheight) + thingzpos >= sectors[heightsec].ceilingheight) return; } @@ -1778,12 +1782,12 @@ static void R_ProjectSprite(mobj_t *thing) vis->scale = yscale; //<sortscale = sortscale; vis->dispoffset = thing->info->dispoffset; // Monster Iestyn: 23/11/15 - vis->gx = thing->x; - vis->gy = thing->y; + vis->gx = thingxpos; + vis->gy = thingypos; vis->gz = gz; vis->gzt = gzt; vis->thingheight = thing->height; - vis->pz = thing->z; + vis->pz = thingzpos; vis->pzt = vis->pz + vis->thingheight; vis->texturemid = vis->gzt - viewz; vis->scalestep = scalestep; diff --git a/src/screen.c b/src/screen.c index 5b38f82db..651c9f481 100644 --- a/src/screen.c +++ b/src/screen.c @@ -418,7 +418,7 @@ void SCR_DisplayTicRate(void) if (fpsgraph[i]) ++totaltics; - if (totaltics <= TICRATE/2) ticcntcolor = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_SALMON, GTC_CACHE); + if (totaltics <= TICRATE/2) ticcntcolor = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_RASPBERRY, GTC_CACHE); else if (totaltics == TICRATE) ticcntcolor = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MINT, GTC_CACHE); /*V_DrawString(vid.width-(24*vid.dupx), vid.height-(16*vid.dupy), diff --git a/src/v_video.c b/src/v_video.c index 7f1bcb7ce..e5fed2a16 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -84,7 +84,7 @@ static void CV_Gammaxxx_ONChange(void); // - You can change them in software, // but they won't do anything. static CV_PossibleValue_t grgamma_cons_t[] = {{1, "MIN"}, {255, "MAX"}, {0, NULL}}; -static CV_PossibleValue_t grfakecontrast_cons_t[] = {{0, "Off"}, {1, "Standard"}, {2, "Smooth"}, {0, NULL}}; +static CV_PossibleValue_t grfakecontrast_cons_t[] = {{0, "Standard"}, {1, "Smooth"}, {0, NULL}}; consvar_t cv_grshaders = {"gr_shaders", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grfovchange = {"gr_fovchange", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; diff --git a/src/y_inter.c b/src/y_inter.c index 52ff82dee..aafb2383c 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -38,7 +38,7 @@ #include "m_random.h" // M_RandomKey #include "g_input.h" // PLAYER1INPUTDOWN -#include "k_kart.h" // colortranslations +#include "k_color.h" // colortranslations #include "k_battle.h" #include "k_pwrlv.h" #include "console.h" // cons_menuhighlight @@ -539,7 +539,7 @@ void Y_IntermissionDrawer(void) y2 = y; - if (playerconsole[data.match.num[i]] == 0 && server_lagless) + if (netgame && playerconsole[data.match.num[i]] == 0 && server_lagless && !players[data.match.num[i]].bot) { static int alagles_timer = 0; patch_t *alagles;