00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include <stdio.h>
00017 #include <stdlib.h>
00018 #include <math.h>
00019 #include <sys/file.h>
00020
00021 #include "mech.h"
00022 #include "p.map.obj.h"
00023 #include "p.mech.sensor.h"
00024 #include "p.mech.los.h"
00025 #include "p.mech.utils.h"
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 float ActualElevation(MAP * map, int x, int y, MECH * mech)
00036 {
00037
00038 if(!map)
00039 return 0.0;
00040 if(!mech)
00041 return (float) (Elevation(map, x, y) + 0.1);
00042 if(MechType(mech) == CLASS_MECH && !Fallen(mech))
00043 return (float) MechZ(mech) + 1.5;
00044 else if(MechMove(mech) == MOVE_NONE)
00045 return (float) MechZ(mech) + 1.5;
00046 else if(IsDS(mech))
00047 return (float) MechZ(mech) + 2.5 + (MechType(mech) ==
00048 CLASS_DS ? 0 : 2);
00049 if(MechDugIn(mech))
00050 return (float) MechZ(mech) + 0.1;
00051 return (float) MechZ(mech) + 0.5;
00052 }
00053
00054 extern int TraceLOS(MAP * map, int ax, int ay, int bx, int by,
00055 lostrace_info ** result);
00056
00057
00058
00059
00060 int CalculateLOSFlag(MECH * mech, MECH * target, MAP * map, int x, int y,
00061 int ff, float hexRange)
00062 {
00063 int new_flag = (ff & (MECHLOSFLAG_SEEN)) + MECHLOSFLAG_SEEC2;
00064 int woods_count = 0;
00065 int water_count = 0;
00066 int height, i;
00067 int pos_x, pos_y;
00068 float pos_z, z_inc, end_z;
00069 int terrain;
00070 int dopartials = 0;
00071 int underwater, bothworlds, t_underwater, t_bothworlds;
00072 int uwatercount = 0, coordcount;
00073 lostrace_info *coords;
00074
00075 #ifndef BT_PARTIAL
00076 float partial_z, p_z_inc;
00077 #endif
00078
00079
00080 if(!target && ((x < 0 || y < 0 || x >= map->map_width ||
00081 y >= map->map_height)))
00082 return new_flag + MECHLOSFLAG_BLOCK;
00083
00084
00085 if(hexRange > (((MechSpecials(mech) & AA_TECH) || (target &&
00086 (MechSpecials(target) &
00087 AA_TECH))) ? 180 :
00088 map->maxvis))
00089 return new_flag + MECHLOSFLAG_BLOCK;
00090
00091
00092 pos_x = MechX(mech);
00093 pos_y = MechY(mech);
00094 pos_z = ActualElevation(map, pos_x, pos_y, mech);
00095 end_z = ActualElevation(map, x, y, target);
00096
00097
00098
00099
00100
00101
00102
00103
00104 if(end_z > 10 && pos_z > 10)
00105 return new_flag;
00106 bothworlds = IsWater(MechRTerrain(mech)) &&
00107 (((MechType(mech) == CLASS_MECH) && (MechZ(mech) == -1)) ||
00108 ((WaterBeast(mech)) && (MechZ(mech) == 0)) ||
00109 ((MechMove(mech) == MOVE_HOVER) && (MechZ(mech) == 0)));
00110 underwater = InWater(mech) && (pos_z < 0.0);
00111
00112
00113 if(!target && !underwater && GetRTerrain(map, x, y) == ICE)
00114 end_z = 0.0;
00115
00116 if(target) {
00117
00118 t_bothworlds = IsWater(MechRTerrain(target)) &&
00119 (((MechType(target) == CLASS_MECH) && (MechZ(target) == -1)) ||
00120 ((WaterBeast(target)) && (MechZ(target) == 0)) ||
00121 ((MechMove(target) == MOVE_HOVER) && (MechZ(target) == 0)));
00122
00123 t_underwater = InWater(target) && (end_z < 0.0);
00124 } else {
00125 if(GetRTerrain(map, x, y) == ICE)
00126 t_bothworlds = 1;
00127 else
00128 t_bothworlds = 0;
00129 t_underwater = (end_z < 0.0);
00130 }
00131
00132
00133 if(((underwater) && !(t_underwater)) ||
00134 ((t_underwater) && !(underwater))) {
00135 return new_flag + MECHLOSFLAG_BLOCK;
00136 }
00137
00138
00139 if(target && mech)
00140 dopartials = (MechType(target) == CLASS_MECH) && (!Fallen(target));
00141
00142
00143 if((x == pos_x) && (y == pos_y))
00144 return new_flag;
00145
00146
00147 coordcount = TraceLOS(map, pos_x, pos_y, x, y, &coords);
00148 if(coordcount > 0) {
00149 z_inc = (float) (end_z - pos_z) / coordcount;
00150 } else {
00151 z_inc = 0;
00152 }
00153
00154 #ifndef BT_PARTIAL
00155 partial_z = 0;
00156 p_z_inc = (float) 1 / coordcount;
00157 #endif
00158
00159 if(coordcount > 0) {
00160 for(i = 0; i < coordcount; i++) {
00161 pos_z += z_inc;
00162 #ifndef BT_PARTIAL
00163 partial_z += p_z_inc;
00164 #endif
00165 if(coords[i].x < 0 || coords[i].x >= map->map_width ||
00166 coords[i].y < 0 || coords[i].y >= map->map_height)
00167 continue;
00168
00169
00170 terrain = GetRTerrain(map, coords[i].x, coords[i].y);
00171
00172 height = Elevation(map, coords[i].x, coords[i].y);
00173
00174
00175
00176
00177 if(underwater) {
00178
00179
00180 if(!(IsWater(terrain)) || (terrain != BRIDGE &&
00181 height >= pos_z)) {
00182 new_flag |= MECHLOSFLAG_BLOCK;
00183 return new_flag;
00184 }
00185
00186
00187 if(!t_bothworlds && pos_z > 0.0) {
00188 new_flag |= MECHLOSFLAG_BLOCK;
00189 return new_flag;
00190 }
00191
00192
00193 if(pos_z <= 0.0)
00194 uwatercount++;
00195 water_count++;
00196 } else {
00197
00198 if(pos_z < (height + 2)) {
00199 switch (GetTerrain(map, coords[i].x, coords[i].y)) {
00200 case SMOKE:
00201 if(i < coordcount - 1)
00202 new_flag |= MECHLOSFLAG_SMOKE;
00203 break;
00204 case FIRE:
00205 if(i < coordcount - 1)
00206 new_flag |= MECHLOSFLAG_FIRE;
00207 break;
00208 }
00209 switch (terrain) {
00210 case LIGHT_FOREST:
00211 case HEAVY_FOREST:
00212 if(i < coordcount - 1)
00213 woods_count += (terrain == LIGHT_FOREST) ? 1 : 2;
00214 break;
00215 case HIGHWATER:
00216 water_count++;
00217 break;
00218 case ICE:
00219 if(pos_z < -0.0) {
00220 new_flag |= MECHLOSFLAG_BLOCK;
00221 return new_flag;
00222 }
00223 water_count++;
00224 break;
00225 case WATER:
00226
00227
00228 if(!bothworlds && (pos_z < 0.0)) {
00229 new_flag |= MECHLOSFLAG_BLOCK;
00230 return new_flag;
00231 }
00232
00233
00234 if(pos_z < 0.0)
00235 uwatercount++;
00236 water_count++;
00237 break;
00238 case MOUNTAINS:
00239 if(i < coordcount - 1)
00240 new_flag |= MECHLOSFLAG_MNTN;
00241 break;
00242 }
00243 }
00244
00245 if(height >= pos_z && terrain != BRIDGE) {
00246 new_flag |= MECHLOSFLAG_BLOCK;
00247 return new_flag;
00248 }
00249 #ifndef BT_PARTIAL
00250 else if(dopartials && height >= (pos_z - partial_z))
00251 new_flag |= MECHLOSFLAG_PARTIAL;
00252 #endif
00253 }
00254 }
00255 }
00256
00257 #ifdef BT_PARTIAL
00258 if(coordcount >= 2)
00259 if(dopartials) {
00260 if(MechZ(target) >= MechZ(mech) &&
00261 (Elevation(map, coords[coordcount - 2].x,
00262 coords[coordcount - 2].y) == (MechZ(target) + 1)))
00263 new_flag |= MECHLOSFLAG_PARTIAL;
00264 if(MechZ(target) == -1 && MechRTerrain(target) == WATER)
00265 new_flag |= MECHLOSFLAG_PARTIAL;
00266 }
00267 #endif
00268
00269 water_count = BOUNDED(0, water_count, MECHLOSMAX_WATER - 1);
00270 woods_count = BOUNDED(0, woods_count, MECHLOSMAX_WOOD - 1);
00271 new_flag += MECHLOSFLAG_WOOD * woods_count;
00272 new_flag += MECHLOSFLAG_WATER * water_count;
00273
00274
00275 if(uwatercount > 2)
00276 new_flag |= MECHLOSFLAG_MNTN;
00277 if(uwatercount > 6)
00278 new_flag |= MECHLOSFLAG_FIRE;
00279 return new_flag;
00280 }
00281
00282 int AddTerrainMod(MECH * mech, MECH * target, MAP * map, float hexRange,
00283 int wAmmoMode)
00284 {
00285
00286 if(mech && target) {
00287 if(MechToMech_LOSFlag(map, mech, target) & MECHLOSFLAG_PARTIAL)
00288 MechStatus(target) |= PARTIAL_COVER;
00289 else
00290 MechStatus(target) &= ~PARTIAL_COVER;
00291
00292 return Sensor_ToHitBonus(mech, target,
00293 MechToMech_LOSFlag(map, mech, target),
00294 map->maplight, hexRange, wAmmoMode);
00295 }
00296 return 0;
00297 }
00298
00299 int InLineOfSight_NB(MECH * mech, MECH * target, int x, int y, float hexRange)
00300 {
00301 int i;
00302
00303 if(mech == target)
00304 return 1;
00305
00306 i = InLineOfSight(mech, target, x, y, hexRange);
00307 if(i & MECHLOSFLAG_BLOCK)
00308 return 0;
00309 return i;
00310 }
00311
00312 int InLineOfSight(MECH * mech, MECH * target, int x, int y, float hexRange)
00313 {
00314 MAP *map;
00315 float x1, y1;
00316 int arc;
00317 int losflag;
00318
00319 map = getMap(mech->mapindex);
00320 if(!map) {
00321 mech_notify(mech, MECHPILOT,
00322 "You are on an invalid map! Map index reset!");
00323 SendError(tprintf("InLineOfSight:invalid map:Mech %d Index %d",
00324 mech->mynum, mech->mapindex));
00325 mech->mapindex = -1;
00326 return 0;
00327 }
00328 if(x < 0 || y < 0 || y >= map->map_height || x >= map->map_width) {
00329 SendError(tprintf("x:%d y:%d out of bounds for #%d (LOS check)", x,
00330 y, mech ? mech->mynum : -1));
00331 }
00332
00333
00334 if(MechCritStatus(mech) & CLAIRVOYANT)
00335 return 1;
00336
00337 if(target) {
00338 x1 = MechFX(target);
00339 y1 = MechFY(target);
00340 } else
00341 MapCoordToRealCoord(x, y, &x1, &y1);
00342 arc = InWeaponArc(mech, x1, y1);
00343
00344 if(mech && target) {
00345 #ifndef ADVANCED_LOS
00346 losflag = MechToMech_LOSFlag(map, mech, target);
00347 if(Sensor_CanSee(mech, target, &losflag, arc, hexRange, map->mapvis,
00348 map->maplight, map->cloudbase)) {
00349 map->LOSinfo[mech->mapnumber][target->mapnumber] |=
00350 (MECHLOSFLAG_SEEN | MECHLOSFLAG_SEESP | MECHLOSFLAG_SEESS);
00351 return 1;
00352 } else {
00353 map->LOSinfo[mech->mapnumber][target->mapnumber] &=
00354 ~(MECHLOSFLAG_SEEN | MECHLOSFLAG_SEESP | MECHLOSFLAG_SEESS);
00355 return 0;
00356 }
00357 #else
00358 if(MechToMech_LOSFlag(map, mech, target) & (MECHLOSFLAG_SEESP |
00359 MECHLOSFLAG_SEESS))
00360 return MechToMech_LOSFlag(map, mech, target) &
00361 (MECHLOSFLAG_SEESP | MECHLOSFLAG_SEESS | MECHLOSFLAG_BLOCK);
00362 #endif
00363 return 0;
00364 }
00365 losflag = CalculateLOSFlag(mech, NULL, map, x, y, 0, hexRange);
00366 return Sensor_CanSee(mech, NULL, &losflag, arc, hexRange, map->mapvis,
00367 map->maplight, map->cloudbase);
00368 }
00369
00370 void mech_losemit(dbref player, MECH * mech, char *buffer)
00371 {
00372 cch(MECH_USUALSP);
00373 MechLOSBroadcast(mech, buffer);
00374 notify(player, "Broadcast done.");
00375 }