g_ground_radar_mode = 3 g_ground_radar_screenpos_x = 21.3 g_ground_radar_screenpos_y = 73.5 g_ground_radar_tiles = {} g_ground_radar_levels = {} g_ground_radar_cols_rows = 64 g_ground_radar_range = {1000, 2000, 3000, 4000, 5000} g_ground_radar_range_selected = 3 g_ground_radar_player_arrow_sprite = nil g_ground_radar_enemy_sprites = {} g_ground_radar_objective_sprites = {} g_ground_radar_max_enemies = 20 g_ground_radar_max_objectives = 10 g_ground_radar_key_pressed = false g_ground_radar_sweep_angle = 1 g_ground_radar_sweep_speed = 4 -- number of seconds to do one sweep g_ground_radar_motion_sweep = 1 function ground_radar_init(e) if addObjective == nil then function addObjective (i) g_objectiveCount = g_objectiveCount + 1 radarObjectives[g_objectiveCount] = i end end if g_ground_radar_mode == 2 then local radarImageBack = LoadImage ( "scriptbank\\radar\\radar2.png" ) local radarImageSweep = LoadImage ( "scriptbank\\radar\\radar_sweep.png" ) local radarImageBlob = LoadImage ( "scriptbank\\radar\\radar_blob.png" ) -- Sweep radar radar_back_sprite = CreateSprite(radarImageBack) SetSpritePosition (radar_back_sprite, g_ground_radar_screenpos_x, g_ground_radar_screenpos_y); SetSpriteSize (radar_back_sprite, 10, -1) SetSpriteDepth (radar_back_sprite, 100) radar_sweep_sprite = CreateSprite(radarImageSweep) SetSpritePosition (radar_sweep_sprite, g_ground_radar_screenpos_x + 5, g_ground_radar_screenpos_y + 8.9) SetSpriteSize (radar_sweep_sprite, 2.4, -1) SetSpriteOffset (radar_sweep_sprite , 2.4 , -1 ) SetSpriteDepth (radar_sweep_sprite, 90) -- Enemy sprites for i = 1, g_ground_radar_max_enemies do g_ground_radar_enemy_sprites[i] = CreateSprite(radarImageBlob) SetSpriteSize (g_ground_radar_enemy_sprites[i], 0.5, -1) SetSpriteColor (g_ground_radar_enemy_sprites[i], 0, 0, 0, 255) end elseif g_ground_radar_mode == 3 then local radarImageBack = LoadImage ( "scriptbank\\radar\\motion_back.png" ) local radarImageSweep = LoadImage ( "scriptbank\\radar\\motion_sweep.png" ) local radarImageBlob = LoadImage ( "scriptbank\\radar\\radar-white.png" ) -- Motion radar radar_back_sprite = CreateSprite(radarImageBack) SetSpritePosition (radar_back_sprite, g_ground_radar_screenpos_x, g_ground_radar_screenpos_y); SetSpriteSize (radar_back_sprite, 20, -1) SetSpriteDepth (radar_back_sprite, 100) radar_sweep_sprite = CreateSprite(radarImageSweep) SetSpritePosition (radar_sweep_sprite, 200, 200) SetSpriteDepth (radar_sweep_sprite, 90) -- Enemy sprites for i = 1, g_ground_radar_max_enemies do g_ground_radar_enemy_sprites[i] = CreateSprite(radarImageBlob) SetSpriteSize (g_ground_radar_enemy_sprites[i], 0.5, -1) SetSpriteColor (g_ground_radar_enemy_sprites[i], 0, 0, 0, 255) end else local radarImageTile = LoadImage ( "scriptbank\\radar\\radar-white.png" ) local playerImageArrow = LoadImage ( "scriptbank\\images\\new_radar_sprites\\arrow.png") local handheldImage = LoadImage ( "scriptbank\\images\\new_radar_sprites\\handheld.png") -- Main sprite handheld_sprite = CreateSprite(handheldImage) SetSpritePosition (handheld_sprite, g_ground_radar_screenpos_x - 5.8, g_ground_radar_screenpos_y - 4.3) SetSpriteSize (handheld_sprite, 18.3, -1) SetSpriteDepth (handheld_sprite, 100) -- Player sprite g_ground_radar_player_arrow_sprite = CreateSprite(playerImageArrow) SetSpriteOffset(g_ground_radar_player_arrow_sprite , 0.3 , -1 ) SetSpriteSize (g_ground_radar_player_arrow_sprite , 0.6 , -1 ) SetSpriteColor (g_ground_radar_player_arrow_sprite , 0, 0, 255, 255) -- Levels array for terrain map local index for i = 0, (g_ground_radar_cols_rows - 1) do for j = 1, g_ground_radar_cols_rows do index = (i * g_ground_radar_cols_rows) + j g_ground_radar_levels[index] = 0 g_ground_radar_tiles[index] = CreateSprite(radarImageTile) SetSpriteSize (g_ground_radar_tiles[index], 0.25, -1) SetSpriteColor (g_ground_radar_tiles[index], 0, 0, 0, 255) end end -- Enemy sprites for i = 1, g_ground_radar_max_enemies do g_ground_radar_enemy_sprites[i] = CreateSprite(radarImageTile) SetSpriteSize (g_ground_radar_enemy_sprites[i], 0.25, -1) SetSpriteColor (g_ground_radar_enemy_sprites[i], 0, 0, 0, 255) end -- Objective sprites for i = 1, g_ground_radar_max_objectives do g_ground_radar_objective_sprites[i] = CreateSprite(radarImageTile) SetSpriteSize (g_ground_radar_objective_sprites[i], 0.3, -1) SetSpriteColor (g_ground_radar_objective_sprites[i], 255, 255, 0, 255) end end end -- iterates a list in order defined by sort function or by key order if none given function sort_pairs(list, order_given) -- collect the keys local keys = {} for k in pairs(list) do keys[#keys+1] = k end if order_given then table.sort(keys, function(a,b) return order_given(list, a, b) end) else table.sort(keys) end -- return the iterator function local i = 0 return function() i = i + 1 if keys[i] then return keys[i], list[keys[i]] end end end function ground_radar_plot_enemies (x, y, range, xscale) local Px, Pz = g_PlayerPosX, g_PlayerPosZ local xmin, xmax = Px - range, Px + range local zmin, zmax = Pz - range, Pz + range local enemy_list = {} for k, v in pairs(ai_soldier_state) do if v ~= nil and g_Entity[k] ~= nil and g_Entity[k]['health'] > 0 and GetEntityVisibility(k) == 1 then local Ex, Ez = g_Entity[k]['x'], g_Entity[k]['z'] if Ex > xmin and Ex < xmax and Ez > zmin and Ez < zmax then -- in radar range so lets add it to the list enemy_list[k] = {x = Ex, z = Ez} end end end local sprite_index = 0 local max_enemies = g_ground_radar_max_enemies local sprites = g_ground_radar_enemy_sprites local step = g_ground_radar_range[g_ground_radar_range_selected] * 2 / g_ground_radar_cols_rows if enemy_list ~= nil then -- sort on distance from player local sorted_enemy_list = {} local xdiff, zdiff for k, _ in pairs(enemy_list) do xdiff = math.abs(enemy_list[k].x - Px) zdiff = math.abs(enemy_list[k].z - Pz) local rng_sqrd = xdiff*xdiff + zdiff*zdiff sorted_enemy_list[k] = rng_sqrd end for k, _ in sort_pairs(sorted_enemy_list, function(list,a,b) return list[a] < list[b] end) do if sprite_index < max_enemies then sprite_index = sprite_index + 1 else break end SetSpritePosition (sprites[sprite_index], x + ((enemy_list[k].x - xmin) / step / 4) * xscale, y + (range * 2 - (enemy_list[k].z - zmin)) / step / 4); if ai_soldier_state[k] ~= 'alerted' and ai_soldier_state[k] ~= 'combat' then SetSpriteColor (sprites[sprite_index], 120, 120, 200, 255) else SetSpriteColor (sprites[sprite_index], 255, 0, 0, 255) end end end for i = sprite_index + 1, max_enemies do SetSpritePosition (sprites[i], 200, 200) end -- now for objectives local objectives_list = {} for _, k in pairs(radarObjectives) do if g_Entity[k] ~= nil and g_Entity[k]['health'] > 0 then local Ex, Ez = g_Entity[k]['x'], g_Entity[k]['z'] if Ex > xmin and Ex < xmax and Ez > zmin and Ez < zmax then -- in radar range so lets add it to the list objectives_list[k] = {x = Ex, z = Ez} end end end sprite_index = 0 local max_objectives = g_ground_radar_max_objectives sprites = g_ground_radar_objective_sprites if objectives_list ~= nil then -- sort on distance from player local sorted_list = {} local xdiff, zdiff for k, _ in pairs(objectives_list) do xdiff = math.abs(objectives_list[k].x - Px) zdiff = math.abs(objectives_list[k].z - Pz) local rng_sqrd = xdiff*xdiff + zdiff*zdiff sorted_list[k] = rng_sqrd end for k, _ in sort_pairs(sorted_list, function(list,a,b) return list[a] < list[b] end) do if sprite_index < max_objectives then sprite_index = sprite_index + 1 else break end SetSpritePosition (sprites[sprite_index], x + ((objectives_list[k].x - xmin) / step / 4) * xscale, y + (range * 2 - (objectives_list[k].z - zmin)) / step / 4); end end for i = sprite_index + 1, max_objectives do SetSpritePosition (sprites[i], 200, 200) end end function ground_radar_main(e) scheduler(e) local x, y = g_ground_radar_screenpos_x, g_ground_radar_screenpos_y local xscale = (GetDeviceHeight()/GetDeviceWidth()) local playerX, playerZ = g_PlayerPosX, g_PlayerPosZ local range = g_ground_radar_range[g_ground_radar_range_selected] local gnd_x = 0 local gnd_z = playerZ + range local colour = 0 local min_level, max_level = math.huge, 0 local level = 0 local step = range * 2 / g_ground_radar_cols_rows if g_ground_radar_mode == 2 then SetSpriteAngle (radar_sweep_sprite, g_ground_radar_sweep_angle) if g_scheduler[e].frames_per_second > 0 then local sweep = 360 / g_ground_radar_sweep_speed / g_scheduler[e].frames_per_second g_ground_radar_sweep_angle = g_ground_radar_sweep_angle + sweep if g_ground_radar_sweep_angle > 360 then g_ground_radar_sweep_angle = g_ground_radar_sweep_angle - 360 end end local sprite_index = 0 local max_enemies = g_ground_radar_max_enemies local sprites = g_ground_radar_enemy_sprites local centreX, centreY = x + 4.8, y + 8.5 for k, v in pairs(ai_soldier_state) do if v ~= nil and g_Entity[k] ~= nil and g_Entity[k]['health'] > 0 and GetEntityVisibility(k) == 1 then local Ex, Ez = g_Entity[k]['x'], g_Entity[k]['z'] local angle, distance = ground_radar_angle_dist(Ex, playerX, Ez, playerZ) if distance < range then if sprite_index < max_enemies then sprite_index = sprite_index + 1 else break end -- make angle relative to player angle angle = angle - g_PlayerAngY if angle < 1 then angle = 360 + angle end local rad_angle = math.rad(angle) SetSpritePosition (sprites[sprite_index], centreX + ((math.sin(rad_angle) * ( distance / range * 8) * xscale)), centreY - (math.cos(rad_angle) * ( distance / range * 8))); local ang_diff = g_ground_radar_sweep_angle - angle local trans = 0 if ang_diff < 0 and angle > 270 and g_ground_radar_sweep_angle < 90 then ang_diff = g_ground_radar_sweep_angle + 360 - angle end if ang_diff > 0 and ang_diff <= 120 then trans = math.modf (255 / 120 * (120 - ang_diff )) end SetSpriteColor (sprites[sprite_index], 0, 255, 0, trans) end end end for i = sprite_index + 1, max_enemies do SetSpritePosition (sprites[i], 200, 200) end elseif g_ground_radar_mode == 3 then -- Motion sensor (Aliens) local sprite_index = 0 local max_enemies = g_ground_radar_max_enemies local sprites = g_ground_radar_enemy_sprites local scanX = x + ((20 - g_ground_radar_motion_sweep / 5) / 2) local scanY = y + (20 - g_ground_radar_motion_sweep / 5) SetSpritePosition (radar_sweep_sprite, scanX, scanY) SetSpriteSize (radar_sweep_sprite, g_ground_radar_motion_sweep / 5, -1) local centreX = x + 9.75 local centreY = y + 17.2 for k, v in pairs(ai_soldier_state) do if v ~= nil and g_Entity[k] ~= nil and g_Entity[k]['health'] > 0 and GetEntityVisibility(k) == 1 then local Ex, Ez = g_Entity[k]['x'], g_Entity[k]['z'] local angle, distance = ground_radar_angle_dist(Ex, playerX, Ez, playerZ) if distance < range then -- make angle relative to player angle angle = angle - g_PlayerAngY if angle < 1 then angle = 360 + angle end if angle > 270 or angle < 90 then if sprite_index < max_enemies then sprite_index = sprite_index + 1 else break end local rad_angle = math.rad(angle) SetSpritePosition (sprites[sprite_index], centreX + (math.sin(rad_angle) * (distance / range * 17) * xscale), centreY - (math.cos(rad_angle) * (distance / range * 17))); SetSpriteColor (sprites[sprite_index], 255, 255, 255, 255) end end end end for i = sprite_index + 1, max_enemies do SetSpritePosition (sprites[i], 200, 200) end if g_scheduler[e].frames_per_second > 0 then local sweep = 20 * g_ground_radar_sweep_speed / g_scheduler[e].frames_per_second g_ground_radar_motion_sweep = g_ground_radar_motion_sweep + sweep if g_ground_radar_motion_sweep > 100 then g_ground_radar_motion_sweep = g_ground_radar_motion_sweep - 100 end end else -- get levels from map for i = 0, (g_ground_radar_cols_rows - 1) do gnd_x = playerX - range for j = 1, g_ground_radar_cols_rows do index = (i * g_ground_radar_cols_rows) + j level = GetTerrainHeight(gnd_x, gnd_z) if level < min_level then min_level = level end if level > max_level then max_level = level end g_ground_radar_levels[index] = level gnd_x = gnd_x + step end gnd_z = gnd_z - step end local level_diff = max_level - min_level local level_correction = 0 if level_diff > 0 then level_correction = 255 / level_diff end local colour = 127 local level = 0 -- display gnd radar for i = 0, (g_ground_radar_cols_rows - 1) do for j = 1, g_ground_radar_cols_rows do index = (i * g_ground_radar_cols_rows) + j if level_diff > 0 then colour = math.modf((g_ground_radar_levels[index] - min_level) * level_correction) end SetSpriteColor (g_ground_radar_tiles[index], colour, colour, colour / 3, 255) SetSpritePosition (g_ground_radar_tiles[index] ,x + (j/4) * xscale , y + (i/4)) end end ground_radar_plot_enemies (x, y, range, xscale) SetSpritePosition (g_ground_radar_player_arrow_sprite , x + 4.7 , y + 8.5) SetSpriteAngle (g_ground_radar_player_arrow_sprite , g_PlayerAngY) end if g_KeyPressSHIFT == 1 and g_KeyPressE == 1 then if not g_ground_radar_key_pressed then g_ground_radar_key_pressed = true if g_ground_radar_range_selected < #g_ground_radar_range then g_ground_radar_range_selected = g_ground_radar_range_selected + 1 else g_ground_radar_range_selected = 1 end end else g_ground_radar_key_pressed = false end end ---------------------------------------------------------------- -- Scheduler, this nifty little function keeps track of time. -- -- The main routine should be called every frame and returns -- -- a flag indicating whether the passed in time has expired -- -- since the last time it was called. -- -- If no time period is specified 100ms is used by default, -- -- i.e. 1/10th of a second. -- -- The caller can either ignore the return flag or use it to -- -- trigger time sensitive functionality. -- -- A global value giving the frames per second count is also -- -- generated, this can be used in script to make animations -- -- independent of frames. -- -- The reason this function is tied to an entity rather than -- -- being global is so that each entity can be triggered at a -- -- different time rather than all being triggered in the same -- -- frame. ---------------------------------------------------------------- g_scheduler = {} function scheduler(e, period) period = period or 100 -- defaults to tenths of a second (100ms) if g_Time == nil then return false end if g_scheduler[e] == nil then g_scheduler[e] = {frames_per_second = 60, period_accumulated = math.random(0, period), accumulated_time = 0, timer_value_last_frame = g_Time, frame_counter = 0}; -- 'test' mode sometimes doesn't clear Lua globals so if -- this appears to be the case initialise everything elseif g_scheduler[e].accumulated_time > 2000 then g_scheduler[e].frames_per_second = 60 g_scheduler[e].period_accumulated = math.random(0, period) g_scheduler[e].accumulated_time = 0 g_scheduler[e].timer_value_last_frame = g_Time g_scheduler[e].frame_counter = 0 end local entry = g_scheduler[e] local do_this_frame_flag = false entry.frame_counter = entry.frame_counter + 1 local time_since_last_frame = g_Time - entry.timer_value_last_frame if (entry.period_accumulated + time_since_last_frame) > period then entry.period_accumulated = (entry.period_accumulated + time_since_last_frame) - period; do_this_frame_flag = true else entry.period_accumulated = entry.period_accumulated + time_since_last_frame end if (entry.accumulated_time + time_since_last_frame) > 1000 then -- more than a second passed? entry.accumulated_time = (entry.accumulated_time + time_since_last_frame) - 1000; entry.frames_per_second = entry.frame_counter entry.frame_counter = 0 else entry.accumulated_time = entry.accumulated_time + time_since_last_frame end entry.timer_value_last_frame = entry.timer_value_last_frame + time_since_last_frame return do_this_frame_flag end function ground_radar_angle_dist (x1, x2, z1, z2) x1 = x1 or 0 x2 = x2 or 0 z1 = z1 or 0 z2 = z2 or 0 local xdiff = x1 - x2 local zdiff = z1 - z2 local abs_xdiff = math.abs(xdiff) local abs_zdiff = math.abs(zdiff) if xdiff == 0 and zdiff == 0 then return 0, 0 end -- distance from object 1 to object 2 local dist_to_obj = math.sqrt((abs_xdiff*abs_xdiff) + (abs_zdiff*abs_zdiff)) local obj_angle = 0 -- angle from object to object, default north if abs_xdiff < 1 then if zdiff < 0 then -- straight south (ish) obj_angle = 180 end elseif abs_zdiff < 1 then if xdiff < 0 then -- straight west (ish) obj_angle = 270 else -- straight east (ish) obj_angle = 90 end else -- simple cases out of the way now for some math if xdiff > 0 then if zdiff > 0 then obj_angle = math.deg (math.asin (abs_xdiff / dist_to_obj)) else obj_angle = 90 + math.deg (math.asin (abs_zdiff / dist_to_obj)) end else if zdiff < 0 then obj_angle = 180 + math.deg (math.asin (abs_xdiff / dist_to_obj)) else obj_angle = 270 + math.deg (math.asin (abs_zdiff / dist_to_obj)) end end end return obj_angle, dist_to_obj end