Compare commits

...

2 Commits

6 changed files with 141 additions and 41 deletions

View File

@ -31,11 +31,18 @@ script.on_event(defines.events.on_built_entity, function(event)
radar.on_built_entity(event)
base.on_built_entity(event)
end)
script.on_event(defines.events.on_robot_built_entity, radar.on_built_entity)
script.on_nth_tick(60, radar.on_jam_tick)
script.on_event(defines.events.on_entity_died, base.on_entity_died)
script.on_event(defines.events.on_player_died, base.on_player_died)
script.on_event(defines.events.on_pre_player_mined_item, base.on_pre_player_mined_item)
script.on_event(defines.events.on_robot_pre_mined, base.on_robot_pre_mined)
script.on_nth_tick(1800, radar.on_orbital_scan_tick)
-- Fires once on the first tick after any save load, then unregisters itself.
script.on_nth_tick(1, function()
script.on_nth_tick(1, nil)
setup.on_save_loaded()
end)
script.on_event(defines.events.on_cargo_pod_finished_ascending, drop_zone.on_cargo_pod_finished_ascending)
script.on_event(defines.events.on_entity_damaged, drop_zone.on_entity_damaged)
script.on_event(defines.events.on_lua_shortcut, drop_zone.on_lua_shortcut)

View File

@ -1,3 +1,8 @@
-- Both mods define the same prototypes; give a clear error instead of duplicate-name crashes.
if mods["Planet-PvP"] then
error("Planet PvP Green cannot be enabled at the same time as the original Planet-PvP mod. Disable one of them.")
end
data:extend({
{
type = "selection-tool",
@ -83,7 +88,7 @@ data:extend({
{
type = "item",
name = "pvp-base-core",
icon = "__Planet-PvP__/Graphics/Base/base_core.png",
icon = "__Planet-PvP-Green__/Graphics/Base/base_core.png",
icon_size = 128,
subgroup = "other",
order = "z[pvp-base-core]",
@ -135,7 +140,7 @@ data:extend({
{
type = "technology",
name = "planet-discovery-nauvis",
icon = "__Planet-PvP__/Graphics/Tech/nauvis-tech.png",
icon = "__Planet-PvP-Green__/Graphics/Tech/nauvis-tech.png",
icon_size = 210,
essential = true,
effects = {

View File

@ -1,3 +1,5 @@
local radar = require("radar")
local drop_zone = {}
function drop_zone.teleport_to_planet(player, surface)
@ -22,7 +24,9 @@ function drop_zone.on_player_changed_surface(event)
local player = game.players[event.player_index]
if not (player and player.valid) then return end
local surface = player.surface
if event.surface_index == surface.index or surface.platform or player.render_mode == defines.render_mode.chart then return end
-- render_mode is only "game" when physically present; "chart"/"chart_zoomed_out"
-- mean the surface is just being viewed on the map, so don't teleport.
if event.surface_index == surface.index or surface.platform or player.render_mode ~= defines.render_mode.game then return end
storage.player_surfaces = storage.player_surfaces or {}
if storage.player_surfaces[player.index] == surface.index then return end
storage.player_surfaces[player.index] = surface.index
@ -62,15 +66,10 @@ function drop_zone.on_player_selected_area(event)
if not player.force.is_chunk_charted(surface, {math.floor(pos.x / 32), math.floor(pos.y / 32)}) then
player.print("Cannot lock drop zone: area is not scanned!")
else
for _, jammer in pairs(surface.find_entities_filtered{
area = {{pos.x - 120, pos.y - 120}, {pos.x + 120, pos.y + 120}},
type = "radar"
}) do
if jammer.force ~= player.force and jammer.force.name ~= "neutral" then
player.print("Cannot lock drop zone: area is jammed by an enemy!")
if player.cursor_stack then player.cursor_stack.clear() end
return
end
if radar.sector_jammed(surface, pos, player.force) then
player.print("Cannot lock drop zone: area is jammed by an enemy!")
if player.cursor_stack then player.cursor_stack.clear() end
return
end
storage.player_targets[player.force.name .. "_" .. surface.name] = {position = pos}
player.print("Drop Zone LOCKED for " .. surface.name .. " at [" .. math.floor(pos.x) .. ", " .. math.floor(pos.y) .. "]")

View File

@ -1,8 +1,8 @@
{
"name": "Planet-PvP",
"version": "1.2.1",
"title": "Planet PvP",
"author": "SpRiTz708",
"name": "Planet-PvP-Green",
"version": "1.0.0",
"title": "Planet PvP Green",
"author": "Blizliam",
"factorio_version": "2.1",
"dependencies": [
"base >= 2.0.77",

107
radar.lua
View File

@ -3,6 +3,36 @@ local radar = {}
local GRID_SIZE = 16
local MAX_RADIUS = 3200
local function powered(entity)
return entity.energy > 0
end
-- Half-size of the square active-vision footprint, matching what the entity reveals on the map.
local function vision_half_size(entity)
if entity.type == "radar" then
return (entity.prototype.max_distance_of_nearby_sector_revealed or 3) * 32
end
local cell = entity.logistic_cell
return cell and cell.construction_radius or 0
end
local function enemy_vision(entity, force)
return entity.force ~= force and entity.force.name ~= "neutral"
end
local function sector_jammed(surface, position, force)
for _, source in pairs(surface.find_entities_filtered{type = {"radar", "roboport"}}) do
if source.active and powered(source) and enemy_vision(source, force) then
local half = vision_half_size(source)
if math.abs(position.x - source.position.x) <= half and math.abs(position.y - source.position.y) <= half then
return true
end
end
end
return false
end
radar.sector_jammed = sector_jammed
local function scan_orbit(entity, planet_name)
local surface = game.planets[planet_name] and game.planets[planet_name].surface
if not surface then return end
@ -15,16 +45,7 @@ local function scan_orbit(entity, planet_name)
local state = storage.grid_pos[scan_key]
local position = {x = state.x, y = state.y}
local blocked = false
for _, jammer in pairs(surface.find_entities_filtered{
area = {{position.x - 120, position.y - 120}, {position.x + 120, position.y + 120}},
type = "radar"
}) do
if jammer.force ~= entity.force and jammer.force.name ~= "neutral" then
blocked = true
break
end
end
local blocked = sector_jammed(surface, position, entity.force)
local scan_range = 1
if not blocked then
@ -52,14 +73,63 @@ local function scan_orbit(entity, planet_name)
end
end
-- Stored charge as a fraction of the buffer; drives how fast a radar accumulates scans.
local function scan_power(entity)
local buffer = entity.electric_buffer_size
if not buffer or buffer == 0 then return 1 end
return math.min(1, entity.energy / buffer)
end
function radar.on_orbital_scan_tick()
storage.radar_scan_progress = storage.radar_scan_progress or {}
for _, surface in pairs(game.surfaces) do
local platform = surface.platform
local location = platform and platform.space_location
if location and game.planets[location.name] then
for _, entity in pairs(surface.find_entities_filtered{type = "radar"}) do
if entity.valid and entity.active then
scan_orbit(entity, location.name)
local progress = (storage.radar_scan_progress[entity.unit_number] or 0) + scan_power(entity)
if progress >= 1 then
progress = progress - 1
scan_orbit(entity, location.name)
end
storage.radar_scan_progress[entity.unit_number] = progress
end
end
end
end
end
-- Powered overlapping enemy vision squares jam each other. The engine cannot
-- darken only the overlap, so an overlapped radar is disabled entirely until
-- the other side dies, loses power, or moves out of range. Roboports count as
-- vision sources but are never disabled themselves.
function radar.on_jam_tick()
storage.jammed_entities = storage.jammed_entities or {}
for unit_number, entity in pairs(storage.jammed_entities) do
if entity.valid then entity.active = true end
storage.jammed_entities[unit_number] = nil
end
for _, surface in pairs(game.surfaces) do
if not surface.platform then
local sources = {}
for _, entity in pairs(surface.find_entities_filtered{type = {"radar", "roboport"}}) do
if entity.valid and entity.active and powered(entity) then sources[#sources + 1] = entity end
end
for i = 1, #sources do
for j = i + 1, #sources do
local a, b = sources[i], sources[j]
if enemy_vision(a, b.force) then
local reach = vision_half_size(a) + vision_half_size(b)
if math.abs(a.position.x - b.position.x) <= reach and math.abs(a.position.y - b.position.y) <= reach then
for _, entity in pairs({a, b}) do
if entity.type == "radar" then
entity.active = false
storage.jammed_entities[entity.unit_number] = entity
end
end
end
end
end
end
end
@ -70,24 +140,17 @@ function radar.on_built_entity(event)
local entity = event.entity
if not (entity and entity.valid) or entity.type ~= "radar" or entity.surface.platform then return end
local jammer_force, jammer_pos, jammer_radius = entity.force, entity.position, 120
-- A fresh vision source jams any enemy drop-zone lock inside its footprint.
local jammer_force, jammer_pos = entity.force, entity.position
local jammer_half = vision_half_size(entity)
for _, enemy_force in pairs(game.forces) do
if enemy_force.name ~= jammer_force.name and enemy_force.name ~= "neutral" then
local min_x = math.floor((jammer_pos.x - jammer_radius) / 32)
local min_y = math.floor((jammer_pos.y - jammer_radius) / 32)
local max_x = math.ceil((jammer_pos.x + jammer_radius) / 32)
local max_y = math.ceil((jammer_pos.y + jammer_radius) / 32)
for x = min_x, max_x do
for y = min_y, max_y do enemy_force.unchart_chunk({x = x, y = y}, entity.surface) end
end
storage.player_targets = storage.player_targets or {}
local key = enemy_force.name .. "_" .. entity.surface.name
local target = storage.player_targets[key]
if target then
local dx, dy = math.abs(target.position.x - jammer_pos.x), math.abs(target.position.y - jammer_pos.y)
-- Jam the same reach on each axis, rather than a circular radius.
if dx <= jammer_radius and dy <= jammer_radius then
if dx <= jammer_half and dy <= jammer_half then
storage.player_targets[key] = nil
local message = "[color=red][JAMMER] Your drop zone at [" .. math.floor(target.position.x)
.. ", " .. math.floor(target.position.y) .. "] on " .. entity.surface.name

View File

@ -1,5 +1,6 @@
local base = require("base")
local drop_zone = require("drop_zone")
local observer = require("observer")
local setup = {}
local PLANETS = {"nauvis", "vulcanus", "fulgora", "gleba"}
@ -154,6 +155,15 @@ local function unlock(force, planet_name)
end
end
-- Surfaces are cheap until chunks get requested; create them all so orbital
-- radar can scout planets before any player has landed.
local function init_planet_surfaces()
for _, planet_name in ipairs(PLANETS) do
local planet = game.planets[planet_name]
if planet then planet.create_surface() end
end
end
local function show_admin(player)
if player.gui.screen.pvp_admin_frame then player.gui.screen.pvp_admin_frame.destroy() end
local frame = player.gui.screen.add{type = "frame", name = "pvp_admin_frame", caption = "ADMIN: CONFIGURE PLANET TEAMS", direction = "vertical"}
@ -193,9 +203,13 @@ function setup.on_init(observer)
for _, player in pairs(game.players) do player.set_shortcut_available("lock_drop_zone_shortcut", true) end
end
function setup.on_configuration_changed()
for _, key in pairs({"team_kit_spawned", "player_surfaces", "force_bases", "permadead", "pending_spectator", "force_colors", "base_label_ids", "player_targets", "grid_pos", "player_lights", "player_team_joined"}) do storage[key] = storage[key] or {} end
function setup.on_configuration_changed(event)
if event and event.mod_changes and event.mod_changes["Planet-PvP"] then
game.print("Save migrated from Planet-PvP to Planet PvP Green. All teams, bases and drop zones carry over.")
end
for _, key in pairs({"planet_teams", "team_kit_spawned", "player_surfaces", "force_bases", "permadead", "pending_spectator", "force_colors", "base_label_ids", "player_targets", "grid_pos", "player_lights", "player_team_joined", "observers", "radar_scan_progress"}) do storage[key] = storage[key] or {} end
get_hub_surface()
if storage.game_started then init_planet_surfaces() end
for planet_name, force_name in pairs(storage.planet_teams or {}) do
local force = game.forces[force_name]
if force then
@ -206,7 +220,11 @@ function setup.on_configuration_changed()
end
local fulgora = game.planets.fulgora
if fulgora and fulgora.surface then assign_fulgora_attractors(fulgora.surface) end
for _, player in pairs(game.players) do player.set_shortcut_available("lock_drop_zone_shortcut", true) end
for _, player in pairs(game.players) do
player.set_shortcut_available("lock_drop_zone_shortcut", true)
-- Restore observer UI for spectators whose button may not have survived a mod swap.
if player.controller_type == defines.controllers.spectator then observer.create_button(player) end
end
for _, player in pairs(game.connected_players) do
if not has_selected_team(player) then
teleport_to_hub(player)
@ -215,6 +233,13 @@ function setup.on_configuration_changed()
end
end
-- No writable "save loaded" event exists, so control.lua fires this via a
-- one-shot nth_tick(1). Covers saves hosted from before surfaces were
-- initialized at game start, where on_configuration_changed may not run.
function setup.on_save_loaded()
if storage.game_started then init_planet_surfaces() end
end
function setup.on_chunk_generated(event)
if event.surface and event.surface.name == "fulgora" then
assign_fulgora_attractors(event.surface, event.area)
@ -257,6 +282,7 @@ function setup.on_gui_click(event)
if not game.forces[name] then game.create_force(name) end
unlock(game.forces[name], planet); base.assign_force_color(name); storage.planet_teams[planet] = name
end
init_planet_surfaces()
local fulgora = game.planets.fulgora
if fulgora and fulgora.surface then assign_fulgora_attractors(fulgora.surface) end
storage.game_started = true; element.parent.destroy()