/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2007 Warzone Resurrection Project Warzone 2100 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Warzone 2100 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* 25th September, 1998. Path Optimisation. An attempt to optimise the final path found for routing in order to stop units getting snagged on corners. Alex McLean. Will only be executed on my machine for now... */ // ------------------------------------------------------------------------- #include "lib/framework/frame.h" #include "base.h" #include "move.h" #include "map.h" #include "weapons.h" #include "statsdef.h" #include "droiddef.h" #include "lib/ivis_common/pietypes.h" #include "optimisepath.h" #include "raycast.h" #include "fpath.h" #include "effects.h" // ------------------------------------------------------------------------- /* Where to slide a path point according to bisecting angle */ signed char markerSteps[8][2] = { {0,0}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0}, {-1,-1} }; // ------------------------------------------------------------------------- /* Returns the index into the above table for a given angle */ UDWORD getStepIndexFromAngle(UDWORD angle); /* Attempts to make the droid's path more 'followable' by moving waypoints away from the nearest blocking tile. The direction in which the waypoint is moved is dependant on where the previous and next way points lie. Essentially, the angle that bisects the two vectors to the previous and next waypoint is found. We then calculate which way to make this angle (line) face. It needs to face into the larger of the two arcs, thereby moving _away_ from the blocking tile */ void optimisePathForDroid(DROID *psDroid) { #if 0 // didn't bother to get this to compile when I changed the PATH_POINT structure // I'll sort it out later - John. UDWORD index,stepIndex; UDWORD angleA,angleB; SDWORD x1,y1,x2,y2; UDWORD bisectingAngle; SDWORD xAlt,yAlt; UDWORD tileX,tileY; iVector pos; SDWORD xDif,yDif; SDWORD xRec,yRec; PATH_POINT *moveList; index = 0; /* Get a pointer to the movement list */ moveList = psDroid->sMove.MovementList; /* Go through the movement list */ while(moveList[index].XCoordinate != -1) { /* If it's not the start point and not the end point */ if(index!=0 AND moveList[index+1].XCoordinate != -1 AND moveList[index+1].YCoordinate != -1) { // ------------------------ // IMPORTANT // Maybe only do this waypoint movement // if the distance between this one // and the last is beyond a certain threshold. // To be found by experiment, or maybe vehicle speed. // ------------------------ /* Get the existing waypoint */ x1 = moveList[index].XCoordinate; y1 = moveList[index].YCoordinate; // xRec = x1; // yRec = y1; // - add a fire so we get to see where it is! */ pos.x = moveList[index].XCoordinate; pos.z = moveList[index].YCoordinate; pos.y = map_Height(pos.x,pos.z); effectGiveAuxVar(8); effectGiveAuxVarSec(100000); // addEffect(&pos,EFFECT_FIRE,FIRE_TYPE_LOCALISED,FALSE,NULL,0); /* Get the coordinates of the previous waypoint */ x2 = moveList[index-1].XCoordinate; y2 = moveList[index-1].YCoordinate; /* Get the angle to it */ angleA = rayPointsToAngle(x2,y2,x1,y1); /* Get the coordinates of the next waypoint */ x2 = moveList[index+1].XCoordinate; // x1,y1 stay the same y2 = moveList[index+1].YCoordinate; /* Get the angle to it, notice switch in relative order */ angleB = rayPointsToAngle(x2,y2,x1,y1); /* Establish the bisecting angle */ bisectingAngle = getBisectingDirectionAway(angleA,angleB); /* Find correct index to use into offset table */ stepIndex = getStepIndexFromAngle(bisectingAngle); // DBPRINTF(("First Angle %d, Second Angle %d, Bisecting %d, Index %d\n",angleA,angleB,bisectingAngle,stepIndex)); /* Get the alteration in terms of tile coordinates */ xAlt = markerSteps[stepIndex][0]; yAlt = markerSteps[stepIndex][1]; // DBPRINTF(("XShift %d, yShift %d\n",xAlt,yAlt)); /* Get the tile we're trying to move to */ tileX = x1>>TILE_SHIFT; tileY = y1>>TILE_SHIFT; tileX+=xAlt; tileY+=yAlt; /* Could we move the waypoint there? */ if(!fpathBlockingTile(tileX,tileY)) { // DBPRINTF(("Moved marker\n")); /* Move it */ moveList[index].XCoordinate = (x1 + (xAlt*TILE_UNITS)); moveList[index].YCoordinate = (y1 + (yAlt*TILE_UNITS)); /* xDif = xRec - (x1 + (xAlt*TILE_UNITS)); yDif = yRec - (y1 + (yAlt*TILE_UNITS)); if(abs(xDif)>128 OR abs(yDif)>128) { effectGiveAuxVar(4); } */ pos.x = moveList[index].XCoordinate; pos.z = moveList[index].YCoordinate; pos.y = map_Height(pos.x,pos.z) + 256; effectGiveAuxVar(8); effectGiveAuxVarSec(150000); addEffect(&pos,EFFECT_FIRE,FIRE_TYPE_SMOKY_BLUE,FALSE,NULL,0); } else { // DBPRINTF(("Could NOT move marker - blocking tile\n")); /* xDif = xRec - (x1 + (xAlt*TILE_UNITS)); yDif = yRec - (y1 + (yAlt*TILE_UNITS)); if(abs(xDif)>128 OR abs(yDif)>128) { effectGiveAuxVar(4); } pos.x = moveList[index].XCoordinate + (xAlt*TILE_UNITS); pos.z = moveList[index].YCoordinate + (yAlt*TILE_UNITS); pos.y = map_Height(pos.x,pos.z) + 512; effectGiveAuxVar(2); effectGiveAuxVarSec(150000); addEffect(&pos,EFFECT_FIRE,FIRE_TYPE_LOCALISED,FALSE,NULL,0); */ } } index++; } #endif } // ------------------------------------------------------------------------- /* Gets the angle that bisects the given to angles. the angle is given such that it points into the larger arc. This is of course ambiguous in the case where the two given angles are 180 degrees apart */ UDWORD getBisectingDirectionAway(UDWORD angleA,UDWORD angleB) { FRACT xVec,yVec; FRACT angle; UDWORD retVal; /* Get the component vectors */ xVec = trigSin(angleA) + trigSin(angleB); yVec = trigCos(angleA) + trigCos(angleB); /* Get the angle between them */ angle = RAD_TO_DEG(atan2(xVec,yVec)); angle+=360; /* Get it as an integer */ retVal = (MAKEINT(angle))%360; /* And make it point the other way - into larger arc */ retVal = (retVal + 180)%360; ASSERT( retVal<360,"Weird angle found" ); return(retVal); } // ------------------------------------------------------------------------- /* A hack function - could be done by dividing the angle by 45 and establishing the right quadrant */ UDWORD getStepIndexFromAngle(UDWORD angle) { FRACT accA; UDWORD retVal = 0; accA = MAKEFRACT(angle); ASSERT( angle<360,"Angle's too big!!!" ); if(accA<=22.5 OR accA>337.0) { retVal = 0; } else if(accA>22.5 AND accA <= 67.5) { retVal = 1; } else if(accA>67.5 AND accA <= 112.5) { retVal = 2; } else if(accA>112.5 AND accA <= 157.5) { retVal = 3; } else if(accA>157.5 AND accA <= 202.5) { retVal = 4; } else if(accA>202.5 AND accA <= 247.5) { retVal = 5; } else if(accA>247.5 AND accA <= 292.5) { retVal = 6; } else if(accA>292.5 AND accA <= 337.5) { retVal = 7; } return(retVal); } // -------------------------------------------------------------------------