00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include "mech.h"
00013 #include "btmacros.h"
00014 #include "mech.sensor.h"
00015 #include "map.los.h"
00016 #include "p.mech.utils.h"
00017
00018 #define INDEX2X(i) ((i%(losmap.xsize))+(losmap.startx))
00019 #define INDEX2Y(i) ((i/(losmap.xsize))+(losmap.starty))
00020
00021 extern int TraceLOS(MAP * map, int ax, int ay, int bx, int by,
00022 lostrace_info ** result);
00023
00024 static hexlosmap_info losmap;
00025
00026 int LOSMap_Hex2Index(hexlosmap_info * losmap, int x, int y)
00027 {
00028 if(x < losmap->startx || x > losmap->startx + losmap->xsize ||
00029 y < losmap->starty || y > losmap->starty + losmap->ysize) {
00030 SendError(tprintf("LOSMap request from out of bounds hex: %d,%d",
00031 x, y));
00032 return 0;
00033 }
00034 return ((y - losmap->starty) * losmap->xsize) + (x - losmap->startx);
00035 }
00036
00037 static float MechHeight(MECH * mech)
00038 {
00039 switch (MechType(mech)) {
00040 case CLASS_MECH:
00041 return 0.2 + !Fallen(mech);
00042 case CLASS_SPHEROID_DS:
00043 return 4.2;
00044 case CLASS_DS:
00045 return 2.2;
00046 case CLASS_MW:
00047 case CLASS_VEH_NAVAL:
00048 return 0.01;
00049 }
00050 return 0.2;
00051 }
00052
00053 static void set_hexlosinfo(int x, int y, int flag)
00054 {
00055 if(x < (losmap.startx) || x >= (losmap.startx) + (losmap.xsize) ||
00056 y < (losmap.starty) || y >= (losmap.starty) + (losmap.ysize)) {
00057 return;
00058 }
00059 losmap.map[LOSMap_Hex2Index(&losmap, x, y)] |= (flag | MAPLOSHEX_SEEN);
00060 }
00061
00062 static int hexlit(int x, int y)
00063 {
00064 if(x < (losmap.startx) || x >= (losmap.startx) + (losmap.xsize) ||
00065 y < (losmap.starty) || y >= (losmap.starty) + (losmap.ysize)) {
00066 return 0;
00067 }
00068
00069 return (losmap.map[LOSMap_Hex2Index(&losmap, x, y)] & MAPLOSHEX_LIT);
00070 }
00071
00072 static void set_sliteinfo(int x, int y, int flag)
00073 {
00074 if(x < (losmap.startx) || x >= (losmap.startx) + (losmap.xsize) ||
00075 y < (losmap.starty) || y >= (losmap.starty) + (losmap.ysize)) {
00076 return;
00077 }
00078 losmap.flags |= MAPLOS_FLAG_SLITE;
00079 losmap.map[LOSMap_Hex2Index(&losmap, x, y)] |= flag;
00080 }
00081
00082
00083
00084
00085
00086
00087 static void set_hexlosall(int flag)
00088 {
00089 memset(&(losmap.map), flag | MAPLOSHEX_SEEN, losmap.xsize * losmap.ysize);
00090 }
00091
00092
00093
00094
00095
00096 static int MechSeesThroughWoods(MECH * mech, MAP * map, int nwoods,
00097 int sensor)
00098 {
00099 int sn = MechSensor(mech)[sensor];
00100 int fake_losflag = nwoods * MECHLOSFLAG_WOOD;
00101 int res = sensors[sn].cansee_func(mech, NULL, map, 1, fake_losflag);
00102
00103 return res;
00104 }
00105
00106 static int MechSeesOverMountain(MECH * mech, MAP * map, int sensor)
00107 {
00108 int sn = MechSensor(mech)[sensor];
00109 int fake_losflag = MECHLOSFLAG_MNTN;
00110
00111 return sensors[sn].cansee_func(mech, NULL, map, 1, fake_losflag);
00112 }
00113
00114 static int MechSeesThroughWater(MECH * mech, MAP * map, int nwater,
00115 int sensor)
00116 {
00117 int sn = MechSensor(mech)[sensor];
00118 int fake_losflag = nwater * MECHLOSFLAG_WATER;
00119
00120 return sensors[sn].cansee_func(mech, NULL, map, 1, fake_losflag);
00121 }
00122
00123 static int MechSeesRange(MECH * mech, MAP * map, int x, int y, int z,
00124 int sensor)
00125 {
00126 int sn = MechSensor(mech)[sensor];
00127 float fx, fy, range, maxvis = sensors[sn].maxvis;
00128
00129 MapCoordToRealCoord(x, y, &fx, &fy);
00130 range = FindRange(MechFX(mech), MechFY(mech), MechFZ(mech),
00131 fx, fy, ZSCALE * z);
00132
00133
00134
00135
00136
00137 if(sn < 2)
00138 maxvis = map->mapvis;
00139 if(sn == 1 && map->maplight == 0)
00140 maxvis *= 2;
00141
00142 if(!sensors[sn].fullvision) {
00143 int arc = InWeaponArc(mech, fx, fy);
00144
00145 if(!(arc & (FORWARDARC | TURRETARC))) {
00146 if(MechSensor(mech)[0] == MechSensor(mech)[1])
00147 maxvis = (maxvis * 200 / 300);
00148 else
00149 maxvis = 0;
00150 }
00151 }
00152
00153 if(sn == 0 && maxvis && range >= maxvis &&
00154 (losmap.flags & MAPLOS_FLAG_SLITE))
00155 return -1;
00156
00157 return range < maxvis;
00158 }
00159
00160 static int MechSLitesRange(MECH * mech, int x, int y, int z)
00161 {
00162 float fx, fy, range;
00163 int arc, maxvis = 60;
00164
00165 MapCoordToRealCoord(x, y, &fx, &fy);
00166 arc = InWeaponArc(mech, fx, fy);
00167 if(!(arc & (FORWARDARC | TURRETARC))) {
00168 return 0;
00169 }
00170
00171 range = FindRange(MechFX(mech), MechFY(mech), MechFZ(mech),
00172 fx, fy, ZSCALE * z);
00173 return range < maxvis;
00174 }
00175
00176 static int MechSeesTerrain(MECH * mech, int sn)
00177 {
00178 return MechSensor(mech)[sn] < 4;
00179 }
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223 static void trace_slitelos(MAP * map, MECH * mech, int index,
00224 float start_height)
00225 {
00226 float minangle = -20;
00227 lostrace_info *trace_coords;
00228 int trace_range = 0;
00229 int trace_x, trace_y, trace_height;
00230 float trace_a;
00231 int trace_coordnum = TraceLOS(map, MechX(mech), MechY(mech),
00232 INDEX2X(index), INDEX2Y(index),
00233 &trace_coords);
00234
00235 for(; trace_range < trace_coordnum; trace_range++) {
00236 trace_x = trace_coords[trace_range].x;
00237 trace_y = trace_coords[trace_range].y;
00238
00239 trace_height = MAX(0, Elevation(map, trace_x, trace_y));
00240
00241 if(!MechSLitesRange(mech, trace_x, trace_y, trace_height))
00242 return;
00243
00244 trace_a = (trace_height - start_height) / (trace_range + 1);
00245 switch (GetTerrain(map, trace_x, trace_y)) {
00246 case HEAVY_FOREST:
00247 case LIGHT_FOREST:
00248 case SMOKE:
00249 trace_a += 2;
00250 }
00251
00252 if(trace_a < minangle)
00253 continue;
00254
00255 set_sliteinfo(trace_x, trace_y, MAPLOSHEX_LIT);
00256 minangle = trace_a;
00257 }
00258 }
00259
00260 static void litemark_callback(MAP * map, int x, int y)
00261 {
00262 set_sliteinfo(x, y, MAPLOSHEX_LIT);
00263 }
00264
00265 static void litemark_map(MAP * map)
00266 {
00267 MECH *mech;
00268 int i;
00269 int index;
00270 mapobj *fire;
00271
00272 for(fire = first_mapobj(map, TYPE_FIRE); fire; fire = next_mapobj(fire)) {
00273 set_sliteinfo(fire->x, fire->y, MAPLOSHEX_LIT);
00274 visit_neighbor_hexes(map, fire->x, fire->y, litemark_callback);
00275 }
00276
00277 for(i = 0; i < map->first_free; i++) {
00278 if(map->mechsOnMap[i] < 0)
00279 continue;
00280 mech = FindObjectsData(map->mechsOnMap[i]);
00281 if(!mech)
00282 continue;
00283
00284 if(Jellied(mech)) {
00285 set_sliteinfo(MechX(mech), MechY(mech), MAPLOSHEX_LIT);
00286 visit_neighbor_hexes(map, MechX(mech), MechY(mech),
00287 litemark_callback);
00288 }
00289
00290 if(!MechLites(mech))
00291 continue;
00292
00293 for(index = 0; index < losmap.xsize * losmap.ysize; index++) {
00294 trace_slitelos(map, mech, index, MechZ(mech) + MechHeight(mech));
00295 }
00296 }
00297 }
00298
00299 #define DEF_MINA(mech, sn) (MechSeesTerrain(mech, sn) ? -20 : 1000)
00300
00301 static void trace_maphexlos(MAP * map, MECH * mech, int index, int tracew,
00302 float start_height)
00303 {
00304 int trace_water[MAX_SENSORS] = { tracew, tracew };
00305 float minangle[MAX_SENSORS] = { DEF_MINA(mech, 0), DEF_MINA(mech, 1) };
00306 float blockangle[MAX_SENSORS] = { DEF_MINA(mech, 0), DEF_MINA(mech, 1) };
00307 int woodcount[MAX_SENSORS] = { 0, 0 };
00308 int watercount[MAX_SENSORS] = { 0, 0 };
00309 lostrace_info *trace_coords;
00310 int trace_range = 0;
00311
00312 int trace_coordnum = TraceLOS(map, MechX(mech), MechY(mech),
00313 INDEX2X(index), INDEX2Y(index),
00314 &trace_coords);
00315
00316 for(; trace_range < trace_coordnum; trace_range++) {
00317 int seestate;
00318 int trace_x = trace_coords[trace_range].x;
00319 int trace_y = trace_coords[trace_range].y;
00320 int trace_height = Elevation(map, trace_x, trace_y);
00321
00322 float trace_a = (trace_height - start_height) / (trace_range + 1);
00323 float trace_ba =
00324 ((trace_height + 2 - start_height)) / (trace_range + 1);
00325 int trace_terrain = GetRTerrain(map, trace_x, trace_y);
00326 int nsensor, newwoods;
00327
00328 for(nsensor = 0; nsensor < NUMSENSORS(mech); nsensor++) {
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338 if(trace_ba < minangle[nsensor]) {
00339 set_hexlosinfo(trace_x, trace_y, MAPLOSHEX_NOLOS);
00340 goto hexinfluence;
00341
00342 }
00343
00344
00345 seestate = MechSeesRange(mech, map, trace_x, trace_y,
00346 trace_height, nsensor);
00347
00348 if(seestate == 0) {
00349 set_hexlosinfo(trace_x, trace_y, MAPLOSHEX_NOLOS);
00350 minangle[nsensor] = blockangle[nsensor] = 1000;
00351 goto hexinfluence;
00352 }
00353
00354
00355 newwoods = 0;
00356 switch (trace_terrain) {
00357 case HEAVY_FOREST:
00358 newwoods++;
00359
00360 case LIGHT_FOREST:
00361 newwoods++;
00362
00363 trace_water[nsensor] = 0;
00364 break;
00365 }
00366
00367 if(!newwoods) {
00368
00369 if(trace_a < minangle[nsensor] || (seestate < 0 &&
00370 !hexlit(trace_x,
00371 trace_y))) {
00372 set_hexlosinfo(trace_x, trace_y, MAPLOSHEX_NOLOS);
00373 } else {
00374 set_hexlosinfo(trace_x, trace_y, MAPLOSHEX_SEE);
00375 blockangle[nsensor] = minangle[nsensor] = trace_a;
00376 woodcount[nsensor] = 0;
00377 }
00378 goto hexinfluence;
00379 }
00380
00381 if(blockangle[nsensor] < trace_a) {
00382 minangle[nsensor] = trace_a;
00383 blockangle[nsensor] = trace_ba;
00384 woodcount[nsensor] = newwoods;
00385 } else if(!MechSeesThroughWoods(mech, map, woodcount[nsensor] +
00386 newwoods, nsensor)) {
00387 if(trace_ba >= blockangle[nsensor]) {
00388 minangle[nsensor] = blockangle[nsensor];
00389 blockangle[nsensor] = trace_ba;
00390 woodcount[nsensor] = newwoods;
00391 } else
00392 minangle[nsensor] = trace_ba;
00393 } else {
00394 minangle[nsensor] = trace_a;
00395 woodcount[nsensor] += newwoods;
00396 }
00397
00398 if(trace_terrain == WATER) {
00399 if(trace_water[nsensor])
00400 watercount[nsensor]++;
00401 if(!trace_water[nsensor] ||
00402 !MechSeesThroughWater(mech, map, watercount[nsensor],
00403 nsensor)) {
00404 if(seestate < 0 && !hexlit(trace_x, trace_y))
00405 set_hexlosinfo(trace_x, trace_y, MAPLOSHEX_NOLOS);
00406 else
00407 set_hexlosinfo(trace_x, trace_y,
00408 MAPLOSHEX_SEETERRAIN);
00409 }
00410 } else if(seestate < 0 && !hexlit(trace_x, trace_y))
00411 set_hexlosinfo(trace_x, trace_y, MAPLOSHEX_NOLOS);
00412 else
00413 set_hexlosinfo(trace_x, trace_y, MAPLOSHEX_SEE);
00414
00415 hexinfluence:
00416 if(trace_terrain == WATER &&
00417 !MechSeesThroughWater(mech, map, 1, nsensor)) {
00418 set_hexlosinfo(trace_x, trace_y, MAPLOSHEX_NOLOS);
00419 minangle[nsensor] = blockangle[nsensor] = 1000;
00420 continue;
00421 }
00422 trace_water[nsensor] = 0;
00423 if(trace_terrain == MOUNTAINS &&
00424 !MechSeesOverMountain(mech, map, nsensor)) {
00425 set_hexlosinfo(trace_x, trace_y, MAPLOSHEX_NOLOS);
00426 minangle[nsensor] = blockangle[nsensor] = 1000;
00427 continue;
00428 }
00429 }
00430 }
00431 }
00432
00433 hexlosmap_info *CalculateLOSMap(MAP * map, MECH * mech, int sx,
00434 int sy, int xsz, int ysz)
00435 {
00436 int index, underterrain, bothworlds;
00437 float start_height;
00438
00439
00440
00441 if(xsz > MAPLOS_MAXX || ysz > MAPLOS_MAXY) {
00442 SendError(tprintf("xsize (%d vs %d) or ysize (%d vs %d) "
00443 "to CalculateLOSMap too large, for mech #%d",
00444 xsz, MAPLOS_MAXX, ysz, MAPLOS_MAXY, mech->mynum));
00445 return NULL;
00446 }
00447
00448 losmap.startx = sx;
00449 losmap.starty = sy;
00450 losmap.xsize = xsz;
00451 losmap.ysize = ysz;
00452 losmap.flags = 0;
00453 memset(losmap.map, 0, xsz * ysz);
00454
00455 underterrain = MechZ(mech) <= -1;
00456 if(IsWater(MechRTerrain(mech))
00457 && ((MechType(mech) == CLASS_MECH && MechZ(mech) == -1)
00458 || ((WaterBeast(mech) || MechMove(mech) == MOVE_HOVER)
00459 && MechZ(mech) == 0))) {
00460 bothworlds = 1;
00461 } else
00462 bothworlds = 0;
00463
00464 start_height = MechZ(mech) + MechHeight(mech);
00465
00466 if(MechCritStatus(mech) & CLAIRVOYANT) {
00467 set_hexlosall(MAPLOSHEX_SEE);
00468 return &losmap;
00469 }
00470
00471 if(!MechSeesTerrain(mech, 0) && !MechSeesTerrain(mech, 1)) {
00472 set_hexlosall(MAPLOSHEX_NOLOS);
00473 return &losmap;
00474 }
00475
00476
00477
00478
00479
00480
00481 litemark_map(map);
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491 for(index = 0; index < xsz; index++) {
00492 if(losmap.map[index] & MAPLOSHEX_SEEN)
00493 continue;
00494 trace_maphexlos(map, mech, index, underterrain || bothworlds,
00495 start_height);
00496 }
00497 for(index = (ysz - 1) * xsz; index < ysz * xsz; index++) {
00498 if(losmap.map[index] & MAPLOSHEX_SEEN)
00499 continue;
00500 trace_maphexlos(map, mech, index, underterrain || bothworlds,
00501 start_height);
00502 }
00503 for(index = xsz; index < ysz * xsz; index += xsz) {
00504 if(losmap.map[index] & MAPLOSHEX_SEEN)
00505 continue;
00506 trace_maphexlos(map, mech, index, underterrain || bothworlds,
00507 start_height);
00508 }
00509 for(index = 2 * xsz - 1; index < ysz * xsz; index += xsz) {
00510 if(losmap.map[index] & MAPLOSHEX_SEEN)
00511 continue;
00512 trace_maphexlos(map, mech, index, underterrain || bothworlds,
00513 start_height);
00514 }
00515 for(index = 0; index < xsz * ysz; index++) {
00516 if(losmap.map[index] & MAPLOSHEX_SEEN)
00517 continue;
00518 trace_maphexlos(map, mech, index, underterrain || bothworlds,
00519 start_height);
00520 }
00521
00522 return &losmap;
00523 }