src/hcode/btech/autopilot_ai.c

Go to the documentation of this file.
00001 
00002 /*
00003  * $Id: ai.c,v 1.2 2005/08/03 21:40:53 av1-op Exp $
00004  *
00005  * Author: Markus Stenberg <fingon@iki.fi>
00006  *
00007  *  Copyright (c) 1998 Markus Stenberg
00008  *  Copyright (c) 1998-2002 Thomas Wouters
00009  *  Copyright (c) 2000-2002 Cord Awtry 
00010  *       All rights reserved
00011  *
00012  * Created: Thu Mar 12 18:36:33 1998 fingon
00013  * Last modified: Thu Dec 10 21:46:30 1998 fingon
00014  *
00015  */
00016 
00017 /* Point of the excercise : move from point a,b to point c,d while
00018    eliminating opponents and stuff, avoiding enemies in rear/side arc
00019    and generally having fun */
00020 
00021 #include <math.h>
00022 
00023 #include "mech.h"
00024 #include "autopilot.h"
00025 #include "p.autopilot_command.h"
00026 #include "p.mech.utils.h"
00027 #include "p.map.obj.h"
00028 #include "spath.h"
00029 #include "p.mech.combat.h"
00030 #include "p.glue.hcode.h"
00031 
00032 #define SCORE_MOD 100
00033 #define SAFE_SCORE SCORE_MOD * 1000
00034 #define MIN_SAFE 8
00035 #define NORM_SAFE 32
00036 #define MAX_SIM_PATHS 40
00037 
00038 typedef struct {
00039         int e, t;
00040         float s, ds;
00041         float fx, fy;
00042         short x, y, lx, ly;
00043         int h;
00044         int dh;
00045 } LOC;
00046 
00047 enum {
00048         SP_OPT_NORM, SP_OPT_FASTER, SP_OPT_SLOWER, SP_OPT_C
00049 };
00050 int sp_opt[SP_OPT_C] = { 0, 1, -1 };    /* We prefer to keep at same speed, however */
00051 
00052 #define MNORM_COUNT 37
00053 
00054 int move_norm_opt[MNORM_COUNT][2] = {
00055         {0, SP_OPT_FASTER},
00056         {0, SP_OPT_NORM},
00057         {0, SP_OPT_SLOWER},
00058         {-1, SP_OPT_NORM},
00059         {1, SP_OPT_NORM},
00060         {-2, SP_OPT_NORM},
00061         {2, SP_OPT_NORM},
00062         {-3, SP_OPT_NORM},
00063         {3, SP_OPT_NORM},
00064         {-5, SP_OPT_NORM},
00065         {5, SP_OPT_NORM},
00066         {-10, SP_OPT_NORM},
00067         {10, SP_OPT_NORM},
00068         {-15, SP_OPT_NORM},
00069         {15, SP_OPT_NORM},
00070         {-20, SP_OPT_NORM},
00071         {20, SP_OPT_NORM},
00072         {-30, SP_OPT_NORM},
00073         {30, SP_OPT_NORM},
00074         {-40, SP_OPT_NORM},
00075         {40, SP_OPT_NORM},
00076         {-60, SP_OPT_NORM},
00077         {60, SP_OPT_NORM},
00078         {-60, SP_OPT_SLOWER},
00079         {60, SP_OPT_SLOWER},
00080         {-60, SP_OPT_FASTER},
00081         {60, SP_OPT_FASTER},
00082         {-80, SP_OPT_NORM},
00083         {80, SP_OPT_NORM},
00084         {-120, SP_OPT_NORM},
00085         {120, SP_OPT_NORM},
00086         {-160, SP_OPT_NORM},
00087         {160, SP_OPT_NORM},
00088         {-160, SP_OPT_SLOWER},
00089         {160, SP_OPT_SLOWER},
00090         {-160, SP_OPT_FASTER},
00091         {160, SP_OPT_FASTER}
00092 };
00093 
00094 #define CFAST_COUNT 9
00095 
00096 /* Update: Just do subset if we're in silly mood */
00097 int combat_fast_opt[CFAST_COUNT][2] = {
00098         {0, SP_OPT_FASTER},
00099         {0, SP_OPT_NORM},
00100         {0, SP_OPT_SLOWER},
00101         {-10, SP_OPT_NORM},
00102         {10, SP_OPT_NORM},
00103         {-30, SP_OPT_NORM},
00104         {30, SP_OPT_NORM},
00105         {-60, SP_OPT_NORM},
00106         {60, SP_OPT_NORM}
00107 };
00108 
00109 void sendAIM(AUTO * a, MECH * m, char *msg)
00110 {
00111         auto_reply(m, msg);
00112         SendAI(msg);
00113 }
00114 
00115 char *AI_Info(MECH * m, AUTO * a)
00116 {
00117         static char buf[MBUF_SIZE];
00118 
00119         sprintf(buf, "Unit#%d on #%d [A#%d]:", m->mynum, m->mapindex, a->mynum);
00120         return buf;
00121 }
00122 
00123 extern float terrain_speed(MECH * mech, float tempspeed, float maxspeed,
00124                                                    int terrain, int elev);
00125 
00126 static int ai_crash(MAP * map, MECH * m, LOC * l)
00127 {
00128         float newx = 0.0, newy = 0.0;
00129         float tempspeed, maxspeed, acc;
00130         int offset;
00131         int normangle;
00132         int mw_mod = 1;
00133         int ch = 0;
00134 
00135         if(!map)
00136                 return 0;
00137 
00138         maxspeed = MMaxSpeed(m);
00139 
00140         /* UpdateHeading */
00141         if(l->h != l->dh && !(MechCritStatus(m) & GYRO_DESTROYED)) {
00142                 ch = 1;
00143                 if(is_aero(m))
00144                         maxspeed = maxspeed * ACCEL_MOD;
00145                 normangle = l->h - l->dh;
00146                 if(MechType(m) == CLASS_MW || MechType(m) == CLASS_BSUIT)
00147                         mw_mod = 60;
00148                 /* XXX Jumping */
00149                 if(fabs(l->s) < 1.0)
00150                         offset = 3 * maxspeed * MP_PER_KPH * mw_mod;
00151                 else {
00152                         offset = 2 * maxspeed * MP_PER_KPH * mw_mod;
00153                         if((abs(normangle) > offset) && (l->s > (maxspeed * 2 / 3)))
00154                                 offset -= offset / 2 * (3.0 * l->s / maxspeed - 2.0);
00155                 }
00156                 offset = offset * MOVE_MOD;
00157                 if(normangle < 0)
00158                         normangle += 360;
00159                 if(IsDS(m) && offset >= 10)
00160                         offset = 10;
00161                 if(normangle > 180) {
00162                         l->h += offset;
00163                         if(l->h >= 360)
00164                                 l->h -= 360;
00165                         normangle += offset;
00166                         if(normangle >= 360)
00167                                 l->h = l->dh;
00168                 } else {
00169                         l->h -= offset;
00170                         if(l->h < 0)
00171                                 l->h += 360;
00172                         normangle -= offset;
00173                         if(normangle < 0)
00174                                 l->h = l->dh;
00175                 }
00176 
00177         }
00178         /* UpdateSpeed */
00179         /* XXX MASC */
00180         /* XXX heat (_hard_ to track) */
00181         tempspeed = l->ds;
00182         if(MechType(m) != CLASS_MW && MechMove(m) != MOVE_VTOL &&
00183            (MechMove(m) != MOVE_FLY || Landed(m)))
00184                 tempspeed = terrain_speed(m, tempspeed, maxspeed, l->t, l->e);
00185         if(ch) {
00186                 if(mudconf.btech_slowdown == 2) {
00187                         int dif = MechFacing(m) - MechDesiredFacing(m);
00188 
00189                         if(dif < 0)
00190                                 dif = -dif;
00191                         if(dif > 180)
00192                                 dif = 360 - dif;
00193                         if(dif) {
00194                                 dif = (dif - 1) / 30 + 2;
00195                                 tempspeed = tempspeed * (10 - dif) / 10;
00196                         }
00197                 } else if(mudconf.btech_slowdown == 1) {
00198                         if(l->h != l->dh)
00199                                 tempspeed = tempspeed * 2.0 / 3.0;
00200                         else
00201                                 tempspeed = tempspeed * 3.0 / 4.0;
00202                 }
00203         }
00204         if(MechIsQuad(m) && MechLateral(m))
00205                 tempspeed -= MP1;
00206         if(tempspeed <= 0.0) {
00207                 tempspeed = 0.0;
00208         }
00209         if(l->ds < 0.)
00210                 tempspeed = -tempspeed;
00211         if(tempspeed != l->s) {
00212                 if(MechIsQuad(m))
00213                         acc = maxspeed / 10.;
00214                 else
00215                         acc = maxspeed / 20.;
00216                 if(tempspeed < l->s) {
00217                         /* Decelerating */
00218                         l->s -= acc;
00219                         if(tempspeed > l->s)
00220                                 l->s = tempspeed;
00221                 } else {
00222                         /* Accelerating */
00223                         l->s += acc;
00224                         if(tempspeed < l->s)
00225                                 l->s = tempspeed;
00226                 }
00227         }
00228         /* move_mech + NewHexEntered */
00229         /* XXX Jumping mechs [aeros,VTOLs] */
00230         /* XXX Non-mechs */
00231         /* XXX Quads */
00232         FindComponents(l->s * MOVE_MOD, l->h, &newx, &newy);
00233         l->fx += newx;
00234         l->fy += newy;
00235         l->lx = l->x;
00236         l->ly = l->y;
00237         RealCoordToMapCoord(&l->x, &l->y, l->fx, l->fy);
00238         /* Ensure we don't run off map */
00239         if(BOUNDED(0, l->x, map->map_width - 1) != l->x)
00240                 return 1;
00241         if(BOUNDED(0, l->y, map->map_height - 1) != l->y)
00242                 return 1;
00243         if(l->lx != l->x || l->ly != l->y) {
00244                 int oz = l->e;
00245 
00246                 /* Ensure if mech m can do transition auto_lx,auto_ly => auto_x,auto_y */
00247 
00248                 /* XXX Decent handling of terrain choosing and stuff */
00249                 switch (GetRTerrain(map, l->x, l->y)) {
00250                 case HEAVY_FOREST:
00251                         if(MechType(m) != CLASS_MECH)
00252                                 return 1;
00253                         break;
00254                 case WATER:
00255                         if(MechMove(m) == MOVE_TRACK || MechMove(m) == MOVE_WHEEL)
00256                                 return 1;
00257                         else {
00258                                 /* Check floodability for mechs */
00259                                 if(MechType(m) == CLASS_MECH) {
00260                                         int t = Elevation(map, l->x, l->y);
00261 
00262                                         if(t < 0) {
00263                                                 if(t == -1) {
00264                                                         /* Check feet */
00265 #define Floodable(loc) (!GetSectArmor(m,loc) || (!GetSectRArmor(m,loc) && GetSectORArmor(m,loc)))
00266 #define FloodCheck(loc) if (GetSectInt(m,loc)) if (Floodable(loc)) return 1;
00267                                                         FloodCheck(LLEG);
00268                                                         FloodCheck(RLEG);
00269                                                         if(MechIsQuad(m)) {
00270                                                                 FloodCheck(LARM);
00271                                                                 FloodCheck(RARM);
00272                                                         }
00273                                                 } else {
00274                                                         int i;
00275 
00276                                                         for(i = 0; i < NUM_SECTIONS; i++)
00277                                                                 FloodCheck(i);
00278                                                 }
00279                                         }
00280                                 }
00281                         }
00282                         break;
00283                 case HIGHWATER:
00284                         return 1;
00285                         break;
00286                 }
00287                 l->e = Elevation(map, l->x, l->y);
00288                 if(MechMove(m) == MOVE_HOVER)
00289                         l->e = MAX(0, l->e);
00290                 l->t = GetRTerrain(map, l->x, l->y);
00291                 if(MechType(m) == CLASS_MECH)
00292                         if((l->e - oz) > 2 || (oz - l->e) > 2)
00293                                 return 1;
00294                 /* XXX Check flooding etc, should avoid _that_ */
00295                 if(MechType(m) == CLASS_VEH_GROUND)
00296                         if((l->e - oz) > 1 || (oz - l->e) > 1)
00297                                 return 1;
00298 
00299         }
00300         return 0;
00301 }
00302 
00303 static void LOCInit(LOC * l, MECH * m)
00304 {
00305         l->fx = MechFX(m);
00306         l->fy = MechFY(m);
00307         l->e = MechElevation(m);
00308         l->h = MechFacing(m);
00309         l->dh = MechDesiredFacing(m);
00310         l->s = MechSpeed(m);
00311         l->t = MechRTerrain(m);
00312         l->ds = MechDesiredSpeed(m);
00313         l->x = MechX(m);
00314         l->y = MechY(m);
00315         l->lx = MechX(m);
00316         l->ly = MechY(m);
00317 }
00318 
00319 static LOC enemy_l[MAX_MECHS_PER_MAP];
00320 static MECH *enemy_m[MAX_MECHS_PER_MAP];        /* Hopefully no more mechs on map */
00321 static int enemy_o[MAX_MECHS_PER_MAP];  /* 'out' = not moving anymore (crashed?) */
00322 static int enemy_i[MAX_MECHS_PER_MAP];  /* dbref */
00323 static int enemy_c = 0;                 /* Number of enemies */
00324 
00325 static LOC friend_l[MAX_MECHS_PER_MAP];
00326 static MECH *friend_m[MAX_MECHS_PER_MAP];       /* Hopefully no more mechs on map */
00327 static int friend_o[MAX_MECHS_PER_MAP]; /* 'out' = not moving anymore (crashed?) */
00328 static int friend_i[MAX_MECHS_PER_MAP]; /* dbref */
00329 static int friend_c = 0;                /* Number of enemies */
00330 
00331 int getEnemies(MECH * mech, MAP * map, int reset)
00332 {
00333         MECH *tempMech;
00334         int i;
00335 
00336         if(reset) {
00337                 for(i = 0; i < enemy_c; i++) {
00338                         LOCInit(&enemy_l[i], enemy_m[i]);       /* Just reset location */
00339                         enemy_o[i] = 0;
00340                 }
00341                 return 0;
00342         }
00343         enemy_c = 0;
00344         for(i = 0; i < map->first_free; i++) {
00345                 tempMech = FindObjectsData(map->mechsOnMap[i]);
00346                 if(!tempMech)
00347                         continue;
00348                 if(Destroyed(tempMech))
00349                         continue;
00350                 if(MechStatus(tempMech) & COMBAT_SAFE)
00351                         continue;
00352                 if(MechTeam(tempMech) == MechTeam(mech))
00353                         continue;
00354                 if(FlMechRange(map, mech, tempMech) > 50.0)
00355                         continue;                       /* Inconsequential */
00356                 /* Something that is _possibly_ unnerving */
00357                 LOCInit(&enemy_l[enemy_c], tempMech);   /* Location */
00358                 enemy_m[enemy_c] = tempMech;    /* Mech data */
00359                 enemy_i[enemy_c++] = i;
00360         }
00361         return enemy_c;
00362 }
00363 
00364 int getFriends(MECH * mech, MAP * map, int reset)
00365 {
00366         MECH *tempMech;
00367         int i;
00368 
00369         if(reset) {
00370                 for(i = 0; i < friend_c; i++) {
00371                         LOCInit(&friend_l[i], friend_m[i]);     /* Just reset location */
00372                         friend_o[i] = 0;
00373                 }
00374                 return 0;
00375         }
00376         friend_c = 0;
00377         for(i = 0; i < map->first_free; i++) {
00378                 tempMech = FindObjectsData(map->mechsOnMap[i]);
00379                 if(!tempMech)
00380                         continue;
00381                 if(Destroyed(tempMech))
00382                         continue;
00383                 if(MechTeam(tempMech) != MechTeam(mech))
00384                         continue;
00385                 if(MechType(tempMech) != CLASS_MECH)
00386                         continue;
00387                 if(FlMechRange(map, mech, tempMech) > 50.0)
00388                         continue;                       /* Inconsequential */
00389                 LOCInit(&friend_l[friend_c], tempMech); /* Location */
00390                 friend_m[friend_c] = tempMech;  /* Mech data */
00391                 friend_i[friend_c++] = i;
00392         }
00393         return friend_c;
00394 }
00395 
00396 /* This has been made .. slightly more complicated.
00397    Basic idea (now): Calculate all the [n] states _simultaneously_ ;
00398    our movement isn't affecting enemy/friend movement after all, just
00399    our own, therefore this is _almost_ [n] times faster than the old code
00400    (approx. 1/30 P60 per 'sheep outside combat, perhaps 1/10 P60 inside) */
00401 
00402 #define MAGIC_NUM -123456
00403 
00404 #define U1(a,b) if (a < b || a == MAGIC_NUM) a = b
00405 #define U2(a,b) if (a > b || a == MAGIC_NUM) a = b
00406 #define FU(a,b,c,s) ((b) == (c) ? 0 : (s) * ((a) - (b)) / ((c) - (b)))
00407 
00408 void ai_path_score(MECH * m, MAP * map, AUTO * a, int opts[][2], int num_o,
00409                                    int gotenemy, float dx, float dy, float delx, float dely,
00410                                    int *rl, int *bd, int *bscore)
00411 {
00412         int i, j, k, l, bearing;
00413         int sd, sc;
00414         LOC lo[MAX_SIM_PATHS];
00415         int dan[MAX_SIM_PATHS], tdan[MAX_SIM_PATHS], msc[MAX_SIM_PATHS],
00416                 bsc[MAX_SIM_PATHS];
00417         int out[MAX_SIM_PATHS], stack[MAX_SIM_PATHS], br[MAX_SIM_PATHS];
00418 
00419         for(i = 0; i < num_o; i++) {
00420                 msc[i] = 0;
00421                 bsc[i] = 0;
00422                 dan[i] = 0;
00423                 tdan[i] = 0;
00424                 out[i] = 0;
00425                 stack[i] = 0;
00426                 br[i] = 9999;
00427                 LOCInit(&lo[i], m);
00428                 lo[i].dh = AcceptableDegree(lo[i].dh + opts[i][0]);
00429                 sd = sp_opt[opts[i][1]];
00430                 if(sd) {
00431                         if(sd < 0) {
00432                                 lo[i].ds = lo[i].ds * 2.0 / 3.0;
00433                         } else if(sd == 1) {
00434                                 float ms = MMaxSpeed(m);
00435 
00436                                 lo[i].ds = (lo[i].ds < MP1 ? MP1 : lo[i].ds) * 4.0 / 3.0;
00437                                 if(lo[i].ds > ms)
00438                                         lo[i].ds = ms;
00439                         } else {
00440                                 float ms = MMaxSpeed(m);
00441 
00442                                 lo[i].ds = ms;
00443                         }
00444                 }
00445         }
00446         for(i = 0; i < NORM_SAFE; i++) {
00447                 dx += delx;
00448                 dy += dely;
00449                 for(k = 0; k < num_o; k++) {
00450                         if(out[k])
00451                                 continue;
00452                         if(ai_crash(map, m, &lo[k])) {  /* Simulate _one_ step */
00453                                 out[k] = i + 1;
00454                                 continue;
00455                         }
00456                         /* Base target-acquisition stuff */
00457                         if((l = FindXYRange(lo[k].fx, lo[k].fy, dx, dy)) < br[k])
00458                                 br[k] = l;
00459 
00460                         /* Generally speaking we're going to the point spesified */
00461                         msc[k] += 4 * (2 * (50 - br[k]) + (100 - l));
00462 
00463                         /* Heading change's inherently [slightly] evil */
00464                         if(lo[k].h != lo[k].dh)
00465                                 msc[k] -= 1;
00466 
00467                         /* Moving is a good thing */
00468                         if(lo[k].x != lo[k].lx || lo[k].y != lo[k].ly) {
00469                                 if(lo[k].t == WATER)
00470                                         msc[k] -= 5;
00471                                 msc[k] += 10;
00472                         }
00473                         /* Punish for not utilizing full speed (this is .. hm, flaky) */
00474                         if(opts[k][1] != SP_OPT_FASTER && MMaxSpeed(m) > 0.1) {
00475                                 sc = BOUNDED(0, 100 * lo[k].ds / MMaxSpeed(m), 100);
00476                                 msc[k] -= (100 - sc) / 30;      /* Basically, unused speed is bad */
00477                         }
00478                 }
00479                 if(MechType(m) == CLASS_MECH) {
00480                         /* Simulate friends */
00481                         for(j = 0; j < friend_c; j++) {
00482                                 if(friend_o[j])
00483                                         continue;
00484                                 if(ai_crash(map, friend_m[j], &friend_l[j]))
00485                                         friend_o[j] = 1;
00486                         }
00487                         for(k = 0; k < num_o; k++) {
00488                                 int sc = 0;
00489 
00490                                 if(out[k] || stack[k])
00491                                         continue;
00492                                 /* Meaning of stack: Someone moves _into_ the hex */
00493                                 for(j = 0; j < friend_c; j++)
00494                                         if(!friend_o[j])
00495                                                 if(lo[k].x == friend_l[j].x &&
00496                                                    lo[k].y == friend_l[j].y)
00497                                                         sc++;
00498                                 if(sc > 1) {    /* Possible stackage */
00499                                         int osc = sc;
00500 
00501                                         for(j = 0; j < friend_c; j++)
00502                                                 if(!friend_o[j])
00503                                                         if(lo[k].x == friend_l[j].x &&
00504                                                            lo[k].y == friend_l[j].y)
00505                                                                 if((lo[k].lx != lo[k].x ||
00506                                                                         lo[k].ly != lo[k].y) ||
00507                                                                    (friend_l[j].lx != friend_l[j].x ||
00508                                                                         friend_l[j].ly != friend_l[j].y))
00509                                                                         osc--;
00510                                         if(osc != sc)
00511                                                 stack[k] = i + 1;
00512                                 }
00513                                 if(gotenemy)
00514                                         tdan[k] = 0;
00515                         }
00516                 }
00517                 if(gotenemy) {
00518                         /* Update enemy locations as well */
00519                         for(j = 0; j < enemy_c; j++) {
00520                                 if(enemy_o[j])
00521                                         continue;
00522                                 if(ai_crash(map, enemy_m[j], &enemy_l[j]))
00523                                         enemy_o[j] = 1;
00524                                 for(k = 0; k < num_o; k++) {
00525                                         if(out[k])
00526                                                 continue;
00527                                         if((l = MyHexDist(lo[k].x, lo[k].y, enemy_l[j].x,
00528                                                                           enemy_l[j].y, 0)) >= 100)
00529                                                 continue;
00530                                         switch (a->auto_cmode) {
00531                                         case 0: /* Withdraw */
00532                                                 if(l > a->auto_cdist)
00533                                                         bsc[k] += 5 * a->auto_cdist + l - a->auto_cdist;
00534                                                 else
00535                                                         bsc[k] += 5 * l;
00536                                                 break;
00537                                         case 1: /* Score  = fulfilling goal (=> distance from cdist) */
00538                                                 if(l < a->auto_cdist)
00539                                                         bsc[k] -= 10 * (a->auto_cdist - l);     /* Not too close */
00540                                                 else
00541                                                         bsc[k] -= 2 * (l - a->auto_cdist);
00542                                                 break;
00543                                         case 2:
00544                                                 if(l < a->auto_cdist)
00545                                                         bsc[k] -= 2 * (a->auto_cdist - l);
00546                                                 else
00547                                                         bsc[k] -= 10 * (l - a->auto_cdist);
00548                                         }
00549                                         if(l > 28)
00550                                                 continue;
00551                                         /* Danger modifier ; it's _always_ dangerous to be close */
00552                                         tdan[k] += (40 - MIN(40, l));
00553                                         /* Arcs can be .. dangerous */
00554                                         if(MechType(m) == CLASS_MECH) {
00555                                                 bearing =
00556                                                         FindBearing(lo[k].fx, lo[k].fy, enemy_l[j].fx,
00557                                                                                 enemy_l[j].fy);
00558                                                 bearing = lo[k].h - bearing;
00559                                                 if(bearing < 0)
00560                                                         bearing += 360;
00561                                                 if(bearing >= 90 && bearing <= 270) {
00562                                                         /* Sides are moderately dangerous [potential rear arcs] */
00563                                                         tdan[k] += 5 * (29 - MIN(29, l));
00564                                                         if(bearing >= 120 && bearing <= 240) {
00565                                                                 /* Rear arc is VERY dangerous */
00566                                                                 tdan[k] += 20 * (29 - MIN(29, l));
00567                                                         }
00568                                                 }
00569                                         } else if(MechType(m) == CLASS_VEH_GROUND) {
00570                                                 bearing =
00571                                                         FindBearing(lo[k].fx, lo[k].fy, enemy_l[j].fx,
00572                                                                                 enemy_l[j].fy);
00573                                                 bearing = lo[k].h - bearing;
00574                                                 if(bearing < 0)
00575                                                         bearing += 360;
00576                                                 if(bearing >= 45 && bearing <= 315) {
00577                                                         if(bearing >= 135 && bearing <= 225) {
00578                                                                 /* Rear arc is VERY dangerous */
00579                                                                 tdan[k] +=
00580                                                                         10 * (29 - MIN(29,
00581                                                                                                    l)) * (100 -
00582                                                                                                                   100 *
00583                                                                                                                   GetSectArmor(m,
00584                                                                                                                                            BSIDE)
00585                                                                                                                   / MAX(1,
00586                                                                                                                                 GetSectOArmor
00587                                                                                                                                 (m,
00588                                                                                                                                  BSIDE))) /
00589                                                                         100;
00590                                                         } else if(bearing < 135) {
00591                                                                 /* right side */
00592                                                                 tdan[k] +=
00593                                                                         7 * (29 - MIN(29,
00594                                                                                                   l)) * (100 -
00595                                                                                                                  100 * GetSectArmor(m,
00596                                                                                                                                                         RSIDE)
00597                                                                                                                  / MAX(1,
00598                                                                                                                            GetSectOArmor
00599                                                                                                                            (m,
00600                                                                                                                                 RSIDE))) /
00601                                                                         100;
00602                                                         } else {
00603                                                                 tdan[k] +=
00604                                                                         7 * (29 - MIN(29,
00605                                                                                                   l)) * (100 -
00606                                                                                                                  100 * GetSectArmor(m,
00607                                                                                                                                                         LSIDE)
00608                                                                                                                  / MAX(1,
00609                                                                                                                            GetSectOArmor
00610                                                                                                                            (m,
00611                                                                                                                                 LSIDE))) /
00612                                                                         100;
00613                                                         }
00614                                                 } else
00615                                                         tdan[k] +=
00616                                                                 5 * (29 - MIN(29,
00617                                                                                           l)) * (100 -
00618                                                                                                          100 * GetSectArmor(m,
00619                                                                                                                                                 FSIDE)
00620                                                                                                          / MAX(1,
00621                                                                                                                    GetSectOArmor(m,
00622                                                                                                                                                  FSIDE)))
00623                                                                 / 100;
00624                                         }
00625                                 }
00626                                 for(k = 0; k < num_o; k++) {
00627                                         if(out[k])
00628                                                 continue;
00629                                         /* Dangerous to be far from buddy in fight */
00630                                         l = FindXYRange(lo[k].fx, lo[k].fy, dx, dy);
00631                                         if(gotenemy && (delx != 0.0 || dely != 0.0))
00632                                                 tdan[k] += MIN(100, l * l);
00633                                         if(enemy_c)
00634                                                 tdan[k] = tdan[k] / enemy_c;
00635                                         /* It's inherently dangerous to move slowly: */
00636                                         if(lo[k].s <= MP2)
00637                                                 tdan[k] += 400;
00638                                         else if(lo[k].s <= MP4)
00639                                                 tdan[k] += 300;
00640                                         else if(lo[k].s <= MP6)
00641                                                 tdan[k] += 200;
00642                                         else if(lo[k].s <= MP9)
00643                                                 tdan[k] += 100;
00644                                         dan[k] += tdan[k] * (NORM_SAFE - i) / (NORM_SAFE / 2);
00645                                 }
00646                         }
00647                 }
00648         }
00649         for(i = 0; i < num_o; i++) {
00650                 U2(a->w_msc, msc[i]);
00651                 U1(a->b_msc, msc[i]);
00652                 if(gotenemy) {
00653                         U2(a->w_bsc, bsc[i]);
00654                         U1(a->b_bsc, bsc[i]);
00655                         U2(a->w_dan, dan[i]);
00656                         U1(a->b_dan, dan[i]);
00657                 }
00658         }
00659         /* Now we have been.. calibrated */
00660         *bscore = 0;
00661         *bd = -1;
00662         /* Find best overall score */
00663         for(i = 0; i < num_o; i++) {
00664                 if(!out[i])
00665                         out[i] = NORM_SAFE + 1;
00666                 if(!stack[i])
00667                         stack[i] = out[i];
00668                 sc = (out[i] - (out[i] - stack[i]) / 2) * SAFE_SCORE + FU(msc[i],
00669                                                                                                                                   a->w_msc,
00670                                                                                                                                   a->b_msc,
00671                                                                                                                                   SCORE_MOD *
00672                                                                                                                                   a->
00673                                                                                                                                   auto_goweight);
00674                 if(gotenemy)
00675                         sc +=
00676                                 FU(bsc[i], a->w_bsc, a->b_bsc,
00677                                    SCORE_MOD * a->auto_fweight) - FU(dan[i], a->w_dan,
00678                                                                                                          a->b_dan,
00679                                                                                                          SCORE_MOD *
00680                                                                                                          (a->auto_fweight +
00681                                                                                                           a->auto_goweight));
00682                 if(sc > *bscore
00683                    || (*bd >= 0 && sc == *bscore
00684                            && sp_opt[opts[i][1]] > sp_opt[opts[*bd][1]])) {
00685                         *bscore = sc;
00686                         *bd = i;
00687                         *rl = out[i] - 1;
00688                 }
00689         }
00690 }
00691 
00692 int ai_max_speed(MECH * m, AUTO * a)
00693 {
00694         float ms;
00695 
00696         if(MechDesiredSpeed(m) != (ms = MMaxSpeed(m)))
00697                 return 0;
00698         return 1;
00699 }
00700 
00701 int ai_opponents(AUTO * a, MECH * m)
00702 {
00703         if(a->auto_nervous) {
00704                 a->auto_nervous--;
00705                 return 1;
00706         }
00707         if(MechNumSeen(m))
00708                 a->auto_nervous = 30;   /* We'll stay frisky for awhile even if cons are lost for one reason or another */
00709         return MechNumSeen(m);
00710 }
00711 
00712 void ai_run_speed(MECH * mech, AUTO * a)
00713 {
00714         /* Warp speed, cap'n! */
00715         char buf[128];
00716 
00717         if(!ai_max_speed(mech, a)) {
00718                 strcpy(buf, "run");
00719                 mech_speed(a->mynum, mech, buf);
00720         }
00721 }
00722 
00723 void ai_stop(MECH * mech, AUTO * a)
00724 {
00725         char buf[128];
00726 
00727         if(MechDesiredSpeed(mech) > 0.1) {
00728                 strcpy(buf, "stop");
00729                 mech_speed(a->mynum, mech, buf);
00730         }
00731 }
00732 
00733 #if 0
00734 void ai_set_speed(MECH * mech, AUTO * a, int s)
00735 {
00736         float ms;
00737         char buf[SBUF_SIZE];
00738 
00739         if(s >= 99) {                           /* To cover rounding errors */
00740                 ai_run_speed(mech, a);
00741                 return;
00742         }
00743         if(!s) {
00744                 ai_stop(mech, a);
00745                 return;
00746         }
00747         /* Send in percentage-based speed */
00748         ms = MMaxSpeed(mech);
00749         ms = ms * s / 100.0;
00750         if(MechDesiredSpeed(mech) != ms) {
00751                 sprintf(buf, "%f", ms);
00752                 mech_speed(a->mynum, mech, buf);
00753         }
00754 }
00755 #endif
00756 
00757 void ai_set_speed(MECH * mech, AUTO * a, float spd)
00758 {
00759         char buf[SBUF_SIZE];
00760         float newspeed;
00761 
00762         if(!mech || !a)
00763                 return;
00764 
00765         newspeed = FBOUNDED(0, spd, ((MMaxSpeed(mech) * a->speed) / 100.0));
00766 
00767         if(MechDesiredSpeed(mech) != newspeed) {
00768                 sprintf(buf, "%f", newspeed);
00769                 mech_speed(a->mynum, mech, buf);
00770         }
00771 }
00772 
00773 void ai_set_heading(MECH * mech, AUTO * a, int dir)
00774 {
00775         char buf[128];
00776 
00777         if(dir == MechDesiredFacing(mech))
00778                 return;
00779         sprintf(buf, "%d", dir);
00780         mech_heading(a->mynum, mech, buf);
00781 }
00782 
00783 #define UNREF(a,b,mod) if (a != MAGIC_NUM && b != MAGIC_NUM) { int c = a, d = b; a = (c * (mod - 1) + d) / mod; b = (c + d * (mod - 1)) / mod; }
00784 
00785 void ai_adjust_move(AUTO * a, MECH * m, char *text, int hmod, int smod,
00786                                         int b_score)
00787 {
00788         float ms;
00789 
00790         ai_set_heading(m, a, MechDesiredFacing(m) + hmod);
00791         switch (smod) {
00792         default:
00793                 SendAI("%s state: %s (hmod:%d) sc:%d", AI_Info(m, a), text, hmod,
00794                            b_score);
00795                 break;
00796         case SP_OPT_FASTER:
00797                 SendAI("%s state: %s+accelerating (hmod:%d) sc:%d", AI_Info(m, a),
00798                            text, hmod, b_score);
00799                 ms = MMaxSpeed(m);
00800                 ai_set_speed(m, a,
00801                                          (float) ((MechDesiredSpeed(m) <
00802                                                            MP1 ? MP1 : MechDesiredSpeed(m)) * 4.0 / 3.0));
00803                 break;
00804         case SP_OPT_SLOWER:
00805                 SendAI("%s state: %s+decelerating (hmod:%d) sc:%d", AI_Info(m, a),
00806                            text, hmod, b_score);
00807                 ms = MMaxSpeed(m);
00808                 ai_set_speed(m, a, (int) (MechDesiredSpeed(m) * 2.0 / 3.0));
00809                 break;
00810         }
00811 }
00812 
00813 int ai_check_path(MECH * m, AUTO * a, float dx, float dy, float delx,
00814                                   float dely)
00815 {
00816         int o;
00817         int b_len, bl, b, b_score;
00818         MAP *map = getMap(m->mapindex);
00819 
00820         o = ai_opponents(a, m);
00821         if(a->last_upd > mudstate.now || (mudstate.now - a->last_upd) > AUTO_GOET) {
00822                 if((muxevent_tick - a->last_upd) > AUTO_GOTT) {
00823                         a->b_msc = MAGIC_NUM;
00824                         a->w_msc = MAGIC_NUM;
00825                         a->b_bsc = MAGIC_NUM;
00826                         a->w_bsc = MAGIC_NUM;
00827                         a->b_dan = MAGIC_NUM;
00828                         a->b_dan = (40 + 20 * 29 + 100) * 30;   /* To stay focused */
00829                 } else {
00830                         /* Slight update ; Un-refine the goals somewhat */
00831                         UNREF(a->w_msc, a->b_msc, 3);
00832                         UNREF(a->w_bsc, a->b_bsc, 5);
00833                         UNREF(a->w_dan, a->b_dan, 8);
00834                         a->b_dan = MAX(a->b_dan, (40 + 20 * 29 + 100) * 30);    /* To stay focused */
00835                 }
00836                 a->last_upd = mudstate.now;
00837         }
00838         /* Got either opponents (nasty) or [possibly] blocked path (slightly nasty), i.e. 12sec */
00839         if(MechType(m) == CLASS_MECH)
00840                 getFriends(m, map, 0);
00841         if(o) {
00842                 getEnemies(m, map, 0);
00843                 if(!((muxevent_tick / AUTOPILOT_GOTO_TICK) % 4)) {      /* Just every fourth tick, i.e. 12sec */
00844                         /* Thorough check */
00845                         ai_path_score(m, map, a, move_norm_opt, MNORM_COUNT, 1, dx, dy,
00846                                                   delx, dely, &bl, &b, &b_score);
00847                         b_len = b_score / SAFE_SCORE;
00848                         if(b_len >= MIN_SAFE)
00849                                 ai_adjust_move(a, m, "combat(/twitchy)",
00850                                                            move_norm_opt[b][0], move_norm_opt[b][1],
00851                                                            b_score);
00852                 } else {
00853                         ai_path_score(m, map, a, combat_fast_opt, CFAST_COUNT, 1, dx,
00854                                                   dy, delx, dely, &bl, &b, &b_score);
00855                         b_len = b_score / SAFE_SCORE;
00856                         if(b_len >= MIN_SAFE)
00857                                 ai_adjust_move(a, m, "[f]combat(/twitchy)",
00858                                                            combat_fast_opt[b][0], combat_fast_opt[b][1],
00859                                                            b_score);
00860                 }
00861                 return 1;                               /* We want to keep fighting near foes */
00862         }
00863         if(!((muxevent_tick / AUTOPILOT_GOTO_TICK) % 4)) {      /* Just every fourth tick, i.e. 12sec */
00864                 /* Thorough check */
00865                 ai_path_score(m, map, a, move_norm_opt, MNORM_COUNT, 0, dx, dy,
00866                                           delx, dely, &bl, &b, &b_score);
00867                 b_len = b_score / SAFE_SCORE;
00868                 if(b_len >= MIN_SAFE)
00869                         ai_adjust_move(a, m, "moving", move_norm_opt[b][0],
00870                                                    move_norm_opt[b][1], b_score);
00871         } else {
00872                 ai_path_score(m, map, a, combat_fast_opt, CFAST_COUNT, 0, dx, dy,
00873                                           delx, dely, &bl, &b, &b_score);
00874                 b_len = b_score / SAFE_SCORE;
00875                 if(b_len >= MIN_SAFE)
00876                         ai_adjust_move(a, m, "[f]moving", combat_fast_opt[b][0],
00877                                                    combat_fast_opt[b][1], b_score);
00878         }
00879 
00880         if(b_len >= MIN_SAFE)
00881                 return 1;
00882 
00883         /* Slow down + stop - no sense in dying needlessly */
00884         ai_stop(m, a);
00885         SendAI("%s state: panic", AI_Info(m, a));
00886         sendAIM(a, m, "PANIC! Unable to comply with order.");
00887         return 0;
00888 }
00889 
00890 void ai_init(AUTO * a, MECH * m)
00891 {
00892 
00893         /* XXX Analyze our unit type ; set basic combat tactic */
00894         a->auto_cmode = 1;                      /* CHARGE! */
00895         a->auto_cdist = 2;                      /* Attempt to avoid kicking distance */
00896         a->auto_nervous = 0;
00897         a->auto_goweight = 44;          /* We're mainly concentrating on fighting */
00898         a->auto_fweight = 55;
00899         a->speed = 100;                         /* Reset to full speed */
00900         a->flags = 0;
00901         a->target = -1;
00902 }
00903 
00904 static MECH *target_mech;
00905 
00906 int artillery_round_flight_time(float fx, float fy, float tx, float ty);
00907 
00908 static int mech_snipe_func(MECH * mech, dbref player, int index, int high)
00909 {
00910         /* Simulate mech movements until flight_time <= now */
00911         int now = 0, crashed = 0;
00912         int flt_time;
00913         LOC t;
00914         MAP *map = getMap(mech->mapindex);
00915 
00916         LOCInit(&t, target_mech);
00917         while ((flt_time =
00918                         artillery_round_flight_time(MechFX(mech), MechFY(mech), t.fx,
00919                                                                                 t.fy)) > now) {
00920                 if(!crashed)
00921                         if(ai_crash(map, target_mech, &t))
00922                                 crashed = 1;
00923                 now++;
00924         }
00925         /* Fire at t.x, t.y */
00926         if(MechTargX(mech) != t.x || MechTargY(mech) != t.y)
00927                 mech_settarget(player, mech, tprintf("%d %d", t.x, t.y));
00928         mech_fireweapon(player, mech, tprintf("%d", index));
00929         return 0;
00930 }
00931 
00932 void mech_snipe(dbref player, MECH * mech, char *buffer)
00933 {
00934         char *args[3];
00935         dbref d;
00936 
00937         DOCHECK(!WizRoy(player), "Permission denied.");
00938         DOCHECK(mech_parseattributes(buffer, args, 3) != 2,
00939                         "Please supply target ID _and_ weapon(s) to use");
00940         DOCHECK((d =
00941                          FindTargetDBREFFromMapNumber(mech, args[0])) <= 0,
00942                         "Invalid target!");
00943         target_mech = getMech(d);
00944         multi_weap_sel(mech, player, args[1], 1, mech_snipe_func);
00945 
00946 }
00947 
00948 /* Experimental (highly) path finding system based on the A* 'a-star'
00949  * system used in many typical games.
00950  *
00951  * Dany - 08/2005 */
00952 
00953 /*
00954  * Create an astar node and return a pointer to it
00955  */
00956 static astar_node *auto_create_astar_node(short x, short y,
00957                                                                                   short x_parent, short y_parent,
00958                                                                                   short g_score, short h_score)
00959 {
00960 
00961         astar_node *temp;
00962         temp = malloc(sizeof(astar_node));
00963         if(temp == NULL)
00964                 return NULL;
00965 
00966         memset(temp, 0, sizeof(astar_node));
00967 
00968         temp->x = x;
00969         temp->y = y;
00970         temp->x_parent = x_parent;
00971         temp->y_parent = y_parent;
00972         temp->g_score = g_score;
00973         temp->h_score = h_score;
00974         temp->f_score = g_score + h_score;
00975         temp->hexoffset = x * MAPY + y;
00976 
00977         return temp;
00978 
00979 }
00980 
00981 /*
00982  * The A* (A-Star) path finding function for the AI
00983  *
00984  * Returns 1 if it found a path and 0 if it doesn't
00985  */
00986 int astar_compare(int a, int b, void *arg)
00987 {
00988         return a - b;
00989 }
00990 void astar_release(void *key, void *data)
00991 {
00992         free(data);
00993 }
00994 int auto_astar_generate_path(AUTO * autopilot, MECH * mech, short end_x,
00995                                                          short end_y)
00996 {
00997         MAP *map = getMap(autopilot->mapindex);
00998         int found_path = 0;
00999 
01000         /* Our bit arrays */
01001         unsigned char closed_list_bitfield[(MAPX * MAPY) / 8];
01002         unsigned char open_list_bitfield[(MAPX * MAPY) / 8];
01003 
01004         float x1, y1, x2, y2;           /* Floating point vars for real cords */
01005         short map_x1, map_y1, map_x2, map_y2;   /* The actual map 'hexes' */
01006         int i;
01007         int child_g_score, child_h_score;       /* the score values for the child hexes */
01008         int hexoffset;                          /* temp int to pass around as the hexoffset */
01009 
01010         /* Our lists using Hag's rbtree */
01011         /* Using two rbtree's to store the open_list so we can sort two
01012          * different ways */
01013         rbtree open_list_by_score;      /* open list sorted by score */
01014         rbtree open_list_by_xy; /* open list sorted by hexoffset */
01015         rbtree closed_list;             /* closed list sorted by hexoffset */
01016 
01017         /* Helper node for the final path */
01018         dllist_node *astar_path_node;
01019 
01020         /* Our astar_node helpers */
01021         astar_node *temp_astar_node;
01022         astar_node *parent_astar_node;
01023 
01024 #ifdef DEBUG_ASTAR
01025         /* Log File */
01026         FILE *logfile;
01027         char log_msg[MBUF_SIZE];
01028 
01029         /* Open the logfile */
01030         logfile = fopen("astar.log", "a");
01031 
01032         /* Write first message */
01033         snprintf(log_msg, MBUF_SIZE,
01034                          "\nStarting ASTAR Path finding for AI #%d from "
01035                          "%d, %d to %d, %d\n", autopilot->mynum, MechX(mech), MechY(mech),
01036                          end_x, end_y);
01037         fprintf(logfile, "%s", log_msg);
01038 #endif
01039 
01040         /* Zero the bitfields */
01041         memset(closed_list_bitfield, 0, sizeof(closed_list_bitfield));
01042         memset(open_list_bitfield, 0, sizeof(open_list_bitfield));
01043 
01044         /* Setup the trees */
01045         open_list_by_score = rb_init((void *)astar_compare, NULL);
01046         open_list_by_xy = rb_init((void *)astar_compare, NULL);
01047         closed_list = rb_init((void *)astar_compare, NULL);
01048 
01049         /* Setup the path */
01050         /* Destroy any existing path first */
01051         auto_destroy_astar_path(autopilot);
01052         autopilot->astar_path = dllist_create_list();
01053 
01054         /* Setup the start hex */
01055         temp_astar_node =
01056                 auto_create_astar_node(MechX(mech), MechY(mech), -1, -1, 0, 0);
01057 
01058         if(temp_astar_node == NULL) {
01061 #ifdef DEBUG_ASTAR
01062                 /* Write Log Message */
01063                 snprintf(log_msg, MBUF_SIZE,
01064                                  "AI ERROR - Unable to malloc astar node for " "hex %d, %d\n",
01065                                  MechX(mech), MechY(mech));
01066                 fprintf(logfile, "%s", log_msg);
01067 #endif
01068 
01069         }
01070 
01071         /* Add start hex to open list */
01072         rb_insert(open_list_by_score, (void *) temp_astar_node->f_score,
01073                           temp_astar_node);
01074         rb_insert(open_list_by_xy, (void *) temp_astar_node->hexoffset,
01075                           temp_astar_node);
01076         SetHexBit(open_list_bitfield, temp_astar_node->hexoffset);
01077 
01078 #ifdef DEBUG_ASTAR
01079         /* Log it */
01080         snprintf(log_msg, MBUF_SIZE, "Added hex %d, %d (%d %d) to open list\n",
01081                          temp_astar_node->x, temp_astar_node->y, temp_astar_node->g_score,
01082                          temp_astar_node->h_score);
01083         fprintf(logfile, "%s", log_msg);
01084 #endif
01085 
01086         /* Now loop till we find path */
01087         while (!found_path) {
01088 
01089                 /* Check to make sure there is still stuff in the open list
01090                  * if not, means we couldn't find a path so quit */
01091                 if(rb_size(open_list_by_score) == 0) {
01092                         break;
01093                 }
01094 
01095                 /* Get lowest cost node, then remove it from the open list */
01096                 parent_astar_node = (astar_node *) rb_search(open_list_by_score,
01097                                                                                                          SEARCH_FIRST, NULL);
01098 
01099                 rb_delete(open_list_by_score, (void *) parent_astar_node->f_score);
01100                 rb_delete(open_list_by_xy, (void *) parent_astar_node->hexoffset);
01101                 ClearHexBit(open_list_bitfield, parent_astar_node->hexoffset);
01102 
01103 #ifdef DEBUG_ASTAR
01104                 /* Log it */
01105                 snprintf(log_msg, MBUF_SIZE, "Removed hex %d, %d (%d %d) from open "
01106                                  "list - lowest cost node\n",
01107                                  parent_astar_node->x, parent_astar_node->y,
01108                                  parent_astar_node->g_score, parent_astar_node->h_score);
01109                 fprintf(logfile, "%s", log_msg);
01110 #endif
01111 
01112                 /* Add it to the closed list */
01113                 rb_insert(closed_list, (void *) parent_astar_node->hexoffset,
01114                                   parent_astar_node);
01115                 SetHexBit(closed_list_bitfield, parent_astar_node->hexoffset);
01116 
01117 #ifdef DEBUG_ASTAR
01118                 /* Log it */
01119                 snprintf(log_msg, MBUF_SIZE, "Added hex %d, %d (%d %d) to closed list"
01120                                  " - lowest cost node\n",
01121                                  parent_astar_node->x, parent_astar_node->y,
01122                                  parent_astar_node->g_score, parent_astar_node->h_score);
01123                 fprintf(logfile, "%s", log_msg);
01124 #endif
01125 
01126                 /* Now we check to see if we added the end hex to the closed list.
01127                  * When this happens it means we are done */
01128                 if(CheckHexBit(closed_list_bitfield, HexOffSet(end_x, end_y))) {
01129                         found_path = 1;
01130 
01131 #ifdef DEBUG_ASTAR
01132                         fprintf(logfile, "Found path for the AI\n");
01133 #endif
01134 
01135                         break;
01136                 }
01137 
01138                 /* Update open list */
01139                 /* Loop through the hexes around current hex and see if we can add
01140                  * them to the open list */
01141 
01142                 /* Set the parent hex of the new nodes */
01143                 map_x1 = parent_astar_node->x;
01144                 map_y1 = parent_astar_node->y;
01145 
01146                 /* Going around clockwise direction */
01147                 for(i = 0; i < 360; i += 60) {
01148 
01149                         /* Map coord to Real */
01150                         MapCoordToRealCoord(map_x1, map_y1, &x1, &y1);
01151 
01152                         /* Calc new hex */
01153                         FindXY(x1, y1, i, 1.0, &x2, &y2);
01154 
01155                         /* Real coord to Map */
01156                         RealCoordToMapCoord(&map_x2, &map_y2, x2, y2);
01157 
01158                         /* Make sure the hex is sane */
01159                         if(map_x2 < 0 || map_y2 < 0 ||
01160                            map_x2 >= map->map_width || map_y2 >= map->map_height)
01161                                 continue;
01162 
01163                         /* Generate hexoffset for the child node */
01164                         hexoffset = HexOffSet(map_x2, map_y2);
01165 
01166                         /* Check to see if its in the closed list 
01167                          * if so just ignore it */
01168                         if(CheckHexBit(closed_list_bitfield, hexoffset))
01169                                 continue;
01170 
01171                         /* Check to see if we can enter it */
01172                         if((MechType(mech) == CLASS_MECH) &&
01173                            (abs(Elevation(map, map_x1, map_y1)
01174                                         - Elevation(map, map_x2, map_y2)) > 2))
01175                                 continue;
01176 
01177                         if((MechType(mech) == CLASS_VEH_GROUND) &&
01178                            (abs(Elevation(map, map_x1, map_y1)
01179                                         - Elevation(map, map_x2, map_y2)) > 1))
01180                                 continue;
01181 
01182                         /* Score the hex */
01183                         /* Right now just assume movement cost from parent to child hex is
01184                          * the same (so 100) no matter which dir we go*/
01186                         child_g_score = 100;
01187 
01188                         /* Now add the g score from the parent */
01189                         child_g_score += parent_astar_node->g_score;
01190 
01191                         /* Next get range */
01192                         /* Using a varient of the Manhattan method since its perfectly
01193                          * logical for us to go diagonally
01194                          *
01195                          * Basicly just going to get the range,
01196                          * and multiply by 100 */
01199                         /* Get the end hex in real coords, using the old variables 
01200                          * to store the values */
01201                         MapCoordToRealCoord(end_x, end_y, &x1, &y1);
01202 
01203                         /* Re-using the x2 and y2 values we calc'd for the child hex 
01204                          * to find the range between the child hex and end hex */
01205                         child_h_score = 100 * FindHexRange(x2, y2, x1, y1);
01206 
01207                         /* Now add in some modifiers for terrain */
01208                         switch (GetTerrain(map, map_x2, map_y2)) {
01209                         case LIGHT_FOREST:
01210 
01211                                 /* Don't bother trying to enter a light forest 
01212                                  * hex unless we can */
01213                                 if((MechType(mech) == CLASS_VEH_GROUND) &&
01214                                    (MechMove(mech) != MOVE_TRACK))
01215                                         continue;
01216 
01217                                 child_g_score += 50;
01218                                 break;
01219                         case ROUGH:
01220                                 child_g_score += 50;
01221                                 break;
01222                         case HEAVY_FOREST:
01223 
01224                                 /* Don't bother trying to enter a heavy forest
01225                                  * hex unless we can */
01226                                 if(MechType(mech) == CLASS_VEH_GROUND)
01227                                         continue;
01228 
01229                                 child_g_score += 100;
01230                                 break;
01231                         case MOUNTAINS:
01232                                 child_g_score += 100;
01233                                 break;
01234                         case WATER:
01235 
01236                                 /* Don't bother trying to enter a water hex
01237                                  * unless we can */
01238                                 if((MechType(mech) == CLASS_VEH_GROUND) &&
01239                                    (MechMove(mech) != MOVE_HOVER) && !(MechSpecials2(mech) & WATERPROOF_TECH))
01240                                         continue;
01241 
01242                                 /* We really don't want them trying to enter water */
01243                                 child_g_score += 200;
01244                                 break;
01245                         case HIGHWATER:
01246 
01247                                 /* Don't bother trying to enter a water hex
01248                                  * unless we can */
01249                                 if((MechType(mech) == CLASS_VEH_GROUND) &&
01250                                    (MechMove(mech) != MOVE_HOVER) && !(MechSpecials2(mech) & WATERPROOF_TECH))
01251                                         continue;
01252 
01253                                 /* We really don't want them trying to enter water */
01254                                 child_g_score += 200;
01255                                 break;
01256                         default:
01257                                 break;
01258                         }
01259 
01260                         /* Is it already on the openlist */
01261                         if(CheckHexBit(open_list_bitfield, hexoffset)) {
01262 
01263                                 /* Ok need to compare the scores and if necessary recalc
01264                                  * and change stuff */
01265 
01266                                 /* Get the node off the open_list */
01267                                 temp_astar_node = (astar_node *) rb_find(open_list_by_xy,
01268                                                                                                                  (void *) hexoffset);
01269 
01270                                 /* Now compare the 'g_scores' to determine shortest path */
01271                                 /* If g_score is lower, this means better path 
01272                                  * from the current parent node */
01273                                 if(child_g_score < temp_astar_node->g_score) {
01274 
01275                                         /* Remove from open list */
01276                                         rb_delete(open_list_by_score,
01277                                                           (void *) temp_astar_node->f_score);
01278                                         rb_delete(open_list_by_xy,
01279                                                           (void *) temp_astar_node->hexoffset);
01280                                         ClearHexBit(open_list_bitfield,
01281                                                                 temp_astar_node->hexoffset);
01282 
01283 #ifdef DEBUG_ASTAR
01284                                         /* Log it */
01285                                         snprintf(log_msg, MBUF_SIZE,
01286                                                          "Removed hex %d, %d (%d %d) from "
01287                                                          "open list - score recal\n", temp_astar_node->x,
01288                                                          temp_astar_node->y, temp_astar_node->g_score,
01289                                                          temp_astar_node->h_score);
01290                                         fprintf(logfile, "%s", log_msg);
01291 #endif
01292 
01293                                         /* Recalc score */
01294                                         /* H-Score should be the same since the hex doesn't move */
01295                                         temp_astar_node->g_score = child_g_score;
01296                                         temp_astar_node->f_score = temp_astar_node->g_score +
01297                                                 temp_astar_node->h_score;
01298 
01299                                         /* Change parent hex */
01300                                         temp_astar_node->x_parent = map_x1;
01301                                         temp_astar_node->y_parent = map_y1;
01302 
01303                                         /* Will re-add the node below */
01304 
01305                                 } else {
01306 
01307                                         /* Don't need to do anything so we can skip 
01308                                          * to the next node */
01309                                         continue;
01310 
01311                                 }
01312 
01313                         } else {
01314 
01315                                 /* Node isn't on the open list so we have to create it */
01316                                 temp_astar_node =
01317                                         auto_create_astar_node(map_x2, map_y2, map_x1, map_y1,
01318                                                                                    child_g_score, child_h_score);
01319 
01320                                 if(temp_astar_node == NULL) {
01323 #ifdef DEBUG_ASTAR
01324                                         /* Log it */
01325                                         snprintf(log_msg, MBUF_SIZE,
01326                                                          "AI ERROR - Unable to malloc astar"
01327                                                          " node for hex %d, %d\n", map_x2, map_y2);
01328                                         fprintf(logfile, "%s", log_msg);
01329 #endif
01330 
01331                                 }
01332 
01333                         }
01334 
01335                         /* Now add (or re-add) the node to the open list */
01336 
01337                         /* Hack to check to make sure its score is not already on the open
01338                          * list. This slightly skews the results towards nodes found earlier
01339                          * then those found later */
01340                         while (1) {
01341 
01342                                 if(rb_exists
01343                                    (open_list_by_score, (void *) temp_astar_node->f_score)) {
01344                                         temp_astar_node->f_score++;
01345 
01346 #ifdef DEBUG_ASTAR
01347                                         fprintf(logfile, "Adjusting score for hex %d, %d - same"
01348                                                         " fscore already exists\n",
01349                                                         temp_astar_node->x, temp_astar_node->y);
01350 #endif
01351 
01352                                 } else {
01353                                         break;
01354                                 }
01355 
01356                         }
01357                         rb_insert(open_list_by_score, (void *) temp_astar_node->f_score,
01358                                           temp_astar_node);
01359                         rb_insert(open_list_by_xy, (void *) temp_astar_node->hexoffset,
01360                                           temp_astar_node);
01361                         SetHexBit(open_list_bitfield, temp_astar_node->hexoffset);
01362 
01363 #ifdef DEBUG_ASTAR
01364                         /* Log it */
01365                         snprintf(log_msg, MBUF_SIZE,
01366                                          "Added hex %d, %d (%d %d) to open list\n",
01367                                          temp_astar_node->x, temp_astar_node->y,
01368                                          temp_astar_node->g_score, temp_astar_node->h_score);
01369                         fprintf(logfile, "%s", log_msg);
01370 #endif
01371 
01372                 }                                               /* End of looking for hexes next to us */
01373 
01374         }                                                       /* End of looking for path */
01375 
01376         /* We Done lets go */
01377 
01378         /* Lets first see if we found a path */
01379         if(found_path) {
01380 
01381 #ifdef DEBUG_ASTAR
01382                 /* Log Message */
01383                 fprintf(logfile, "Building Path from closed list for AI\n");
01384 #endif
01385 
01386                 /* Found a path so we need to go through the closed list
01387                  * and generate it */
01388 
01389                 /* Get the end hex, find its parent hex and work back to
01390                  * start hex while building list */
01391 
01392                 /* Get end hex from closed list */
01393                 hexoffset = HexOffSet(end_x, end_y);
01394                 temp_astar_node = rb_find(closed_list, (void *) hexoffset);
01395 
01396                 /* Add end hex to path list */
01397                 astar_path_node = dllist_create_node(temp_astar_node);
01398                 dllist_insert_beginning(autopilot->astar_path, astar_path_node);
01399 
01400 #ifdef DEBUG_ASTAR
01401                 /* Log it */
01402                 fprintf(logfile, "Added hex %d, %d to path list\n",
01403                                 temp_astar_node->x, temp_astar_node->y);
01404 #endif
01405 
01406                 /* Remove it from closed list */
01407                 rb_delete(closed_list, (void *) temp_astar_node->hexoffset);
01408 
01409 #ifdef DEBUG_ASTAR
01410                 /* Log it */
01411                 fprintf(logfile,
01412                                 "Removed hex %d, %d from closed list - path list work\n",
01413                                 temp_astar_node->x, temp_astar_node->y);
01414 #endif
01415 
01416                 /* Check if the end hex is the start hex */
01417                 if(!(temp_astar_node->x == MechX(mech) &&
01418                          temp_astar_node->y == MechY(mech))) {
01419 
01420                         /* Its not so lets loop through the closed list
01421                          * building the path */
01422 
01423                         /* Loop */
01424                         while (1) {
01425 
01426                                 /* Get Parent Node Offset */
01427                                 hexoffset = HexOffSet(temp_astar_node->x_parent,
01428                                                                           temp_astar_node->y_parent);
01429 
01433                                 /* Get Parent Node from closed list */
01434                                 parent_astar_node = rb_find(closed_list, (void *) hexoffset);
01435 
01436                                 /* Check if start hex */
01437                                 /* If start hex quit */
01438                                 if(parent_astar_node->x == MechX(mech) &&
01439                                    parent_astar_node->y == MechY(mech)) {
01440                                         break;
01441                                 }
01442 
01443                                 /* Add to path list */
01444                                 astar_path_node = dllist_create_node(parent_astar_node);
01445                                 dllist_insert_beginning(autopilot->astar_path,
01446                                                                                 astar_path_node);
01447 
01448 #ifdef DEBUG_ASTAR
01449                                 /* Log it */
01450                                 fprintf(logfile, "Added hex %d, %d to path list\n",
01451                                                 parent_astar_node->x, parent_astar_node->y);
01452 #endif
01453 
01454                                 /* Remove from closed list */
01455                                 rb_delete(closed_list, (void *) parent_astar_node->hexoffset);
01456 
01457 #ifdef DEBUG_ASTAR
01458                                 /* Log it */
01459                                 fprintf(logfile,
01460                                                 "Removed hex %d, %d from closed list - path list work\n",
01461                                                 parent_astar_node->x, parent_astar_node->y);
01462 #endif
01463 
01464                                 /* Make parent new child */
01465                                 temp_astar_node = parent_astar_node;
01466 
01467                         }                                       /* End of while loop */
01468 
01469                 }
01470 
01471                 /* Done with the path its cleanup time */
01472 
01473         }
01474 
01475         /* Make sure we destroy all the objects we dont need any more */
01476 
01477 #ifdef DEBUG_ASTAR
01478         /* Log Message */
01479         fprintf(logfile, "Destorying the AI lists\n");
01480 #endif
01481 
01482         /* Destroy the open lists */
01483         rb_release(open_list_by_score, (void *)astar_release, NULL);
01484         rb_destroy(open_list_by_xy);
01485 
01486         /* Destroy the closed list */
01487         rb_release(closed_list, (void *)astar_release, NULL);
01488 
01489 #ifdef DEBUG_ASTAR
01490         /* Close Log file */
01491         fclose(logfile);
01492 #endif
01493 
01494         /* End */
01495         if(found_path) {
01496                 return 1;
01497         } else {
01498                 return 0;
01499         }
01500 
01501 }
01502 
01503 /* Function to Smooth out the AI path and remove
01504  * nodes we don't need */
01505 /* Not even close to being finished yet */
01506 void astar_smooth_path(AUTO * autopilot)
01507 {
01508 
01509         dllist_node *current, *next, *prev;
01510 
01511         float x1, y1, x2, y2;
01512         int degrees;
01513 
01514         /* Get the n node off the list */
01515 
01516         /* Get the n+1 node off the list */
01517 
01518         /* Get bearing from n to n+1 */
01519 
01520         /* Get n+2 node off list */
01521 
01522         /* Get bearing from n to n+2 node */
01523 
01524         /* Compare bearings */
01525         /* If in same direction as previous
01526          * don't need n+1 node */
01527 
01528         /* Keep looping till bearing doesn't match */
01529         /* Then reset n node to final node and continue */
01530 
01531         return;
01532 
01533 }
01534 
01535 void auto_destroy_astar_path(AUTO * autopilot)
01536 {
01537 
01538         astar_node *temp_astar_node;
01539 
01540         /* Make sure there is a path if not quit */
01541         if(!(autopilot->astar_path))
01542                 return;
01543 
01544         /* There is a path lets kill it */
01545         if(dllist_size(autopilot->astar_path) > 0) {
01546 
01547                 while (dllist_size(autopilot->astar_path)) {
01548                         temp_astar_node =
01549                                 dllist_remove_node_at_pos(autopilot->astar_path, 1);
01550                         free(temp_astar_node);
01551                 }
01552 
01553         }
01554 
01555         /* Finally destroying the path */
01556         dllist_destroy_list(autopilot->astar_path);
01557         autopilot->astar_path = NULL;
01558 
01559 }

Generated on Mon May 28 04:25:20 2007 for BattletechMUX by  doxygen 1.4.7