1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2024-12-16 10:19:27 +02:00
RedMew/map_gen/maps/crash_site/commands.lua
Jayefuu c79cbca22d Fixed /strike command range issue
Previously the airstrike was not reaching the corners of the map. There was a default max_range value of 1000 that limited how far the poison capsules could travel. Have set it to 1400 so that it can reach the corners of the map.

THIS NEEDS PLAY TESTING. Please don't merge until I've confirmed I've play tested it.
2020-12-08 09:12:12 +00:00

392 lines
13 KiB
Lua

local Command = require 'utils.command'
local Rank = require 'features.rank_system'
local Task = require 'utils.task'
local Token = require 'utils.token'
local Server = require 'features.server'
local Popup = require 'features.gui.popup'
local Global = require 'utils.global'
local Event = require 'utils.event'
local Retailer = require 'features.retailer'
local Ranks = require 'resources.ranks'
local Core = require 'utils.core'
local Color = require 'resources.color_presets'
local Toast = require 'features.gui.toast'
local Utils = require 'utils.core'
local set_timeout_in_ticks = Task.set_timeout_in_ticks
local Public = {}
function Public.control(config)
local server_player = {name = '<server>', print = print}
local global_data = {restarting = nil}
local airstrike_data = {radius_level = 1, count_level = 1}
Global.register({
global_data = global_data,
airstrike_data = airstrike_data
},
function(tbl)
global_data = tbl.global_data
airstrike_data = tbl.airstrike_data
end
)
local function double_print(str)
game.print(str)
print(str)
end
local callback
callback =
Token.register(
function(data)
if not global_data.restarting then
return
end
local state = data.state
if state == 0 then
Server.start_scenario(data.scenario_name)
double_print('restarting')
global_data.restarting = nil
return
elseif state == 1 then
local time_string = Core.format_time(game.ticks_played)
local discord_crashsite_role = '<@&762441731194748958>' -- @crash_site
--local discord_crashsite_role = '<@&593534612051984431>' -- @test
Server.to_discord_raw(discord_crashsite_role .. ' **Crash Site has just restarted! Previous map lasted: ' .. time_string .. '!**')
local unix_time = Server.get_current_time()
local game_time = game.ticks_played
Server.set_data('crash_site_data', tostring(unix_time), game_time) -- Store the server unix time as key and total game ticks in the Scenario Data
Popup.all('\nServer restarting!\nInitiated by ' .. data.name .. '\n')
end
double_print(state)
data.state = state - 1
Task.set_timeout_in_ticks(60, callback, data)
end
)
local static_entities_to_check = {
'spitter-spawner','biter-spawner',
'small-worm-turret', 'medium-worm-turret','big-worm-turret', 'behemoth-worm-turret',
'gun-turret', 'laser-turret', 'artillery-turret', 'flamethrower-turret'
}
local biter_entities_to_check = {
'small-spitter', 'medium-spitter', 'big-spitter', 'behemoth-spitter',
'small-biter', 'medium-biter', 'big-biter', 'behemoth-biter'
}
local function map_cleared(player)
player = player or server_player
local get_entity_count = game.forces["enemy"].get_entity_count
-- Check how many of each turrets, worms and spawners are left and return false if there are any of each left.
for i = 1, #static_entities_to_check do
local name = static_entities_to_check[i]
if get_entity_count(name) > 0 then
player.print('All enemy spawners, worms, buildings, biters and spitters must be cleared before crashsite can be restarted.')
return false
end
end
-- Count all the remaining biters and spitters
local biter_total = 0;
for i = 1, #biter_entities_to_check do
local name = biter_entities_to_check[i]
biter_total = biter_total + get_entity_count(name)
end
-- Return false if more than 20 left. Players have had problems finding the last few biters so set to a reasonable value.
if biter_total > 20 then
player.print('All enemy spawners, worms, buildings are dead. Crashsite can be restarted when all biters and spitters are killed.')
return false
end
return true
end
local function restart(args, player)
player = player or server_player
local sanitised_scenario = args.scenario_name
if global_data.restarting then
player.print('Restart already in progress')
return
end
if player ~= server_player and Rank.less_than(player.name, Ranks.admin) then
-- Check enemy count
if not map_cleared(player) then
return
end
-- Limit the ability of non-admins to call the restart function with arguments to change the scenario
-- If not an admin, restart the same scenario always
sanitised_scenario = config.scenario_name
end
global_data.restarting = true
double_print('#################-Attention-#################')
double_print('Server restart initiated by ' .. player.name)
double_print('###########################################')
for _, p in pairs(game.players) do
if p.admin then
p.print('Abort restart with /abort')
end
end
print('Abort restart with /abort')
Task.set_timeout_in_ticks(60, callback, {name = player.name, scenario_name = sanitised_scenario, state = 10})
end
local function abort(_, player)
player = player or server_player
if global_data.restarting then
global_data.restarting = nil
double_print('Restart aborted by ' .. player.name)
else
player.print('Cannot abort a restart that is not in progress.')
end
end
local chart_area_callback = Token.register(function(data)
local xpos = data.xpos
local ypos = data.ypos
local player = data.player
local s = player.surface
player.force.chart(s, {{xpos-32, ypos-32}, {xpos+32, ypos+32}})
end)
local function spy(args, player)
local player_name = player.name
local inv = player.get_inventory(defines.inventory.character_main)
local coin_count = inv.get_item_count("coin")
-- Parse the values from the location string
-- {location = "[gps=-110,-17,redmew]"}
local location_string = args.location
local coords = {}
for m in string.gmatch( location_string, "%-?%d+" ) do
table.insert(coords, tonumber(m))
end
-- Do some checks then reveal the pinged map and remove 1000 coins
if #coords < 2 then
player.print({'command_description.crash_site_spy_invalid'}, Color.fail)
return
elseif coin_count < 1000 then
player.print({'command_description.crash_site_spy_funds'}, Color.fail)
return
else
local xpos=coords[1]
local ypos=coords[2]
-- reveal 3x3 chunks centred on chunk containing pinged location
-- make sure it lasts 15 seconds
for j=1,15 do
set_timeout_in_ticks(
60*j,
chart_area_callback,
{player = player, xpos = xpos, ypos=ypos}
)
end
game.print({'command_description.crash_site_spy_success', player_name, xpos,ypos}, Color.success)
inv.remove({name = "coin", count = 1000})
end
end
local spawn_poison_callback = Token.register(function(data)
local r = data.r
data.s.create_entity{name = "poison-capsule", position={0,0}, target={data.xpos + math.random(-r,r), data.ypos + math.random(-r,r)}, speed=10, max_range=1400}
end)
local function strike(args, player)
local s = player.surface
local location_string = args.location
local coords = {}
local radius_level = airstrike_data.radius_level -- max radius of the strike area
local count_level = airstrike_data.count_level -- the number of poison capsules launched at the enemy
if count_level == 1 then
player.print({'command_description.crash_site_airstrike_not_researched'}, Color.fail)
return
end
local radius = 5+(radius_level*3)
local count = (count_level-1)*5+3
local strikeCost = count * 4 -- the number of poison-capsules required in the chest as payment
-- parse GPS coordinates from map ping
for m in string.gmatch( location_string, "%-?%d+" ) do
table.insert(coords, tonumber(m))
end
-- Do some checks on the coordinates passed in the argument
if #coords < 2 then
player.print({'command_description.crash_site_airstrike_invalid'}, Color.fail)
return
end
local xpos=coords[1]
local ypos=coords[2]
-- Check that the chest is where it should be.
local entities = s.find_entities_filtered {position = {-0.5, -3.5}, type = 'container', limit=1}
local dropbox = entities[1]
if dropbox == nil then
player.print("Chest not found. Replace it here: [gps=-0.5,-3.5,redmew]")
return
end
-- Check the contents of the chest by spawn for enough poison capsules to use as payment
local inv = dropbox.get_inventory(defines.inventory.chest)
local capCount = inv.get_item_count("poison-capsule")
if capCount < strikeCost then
player.print({'command_description.crash_site_airstrike_insufficient_currency_error',strikeCost - capCount}, Color.fail)
return
end
-- Do a simple check to make sure the player isn't trying to grief the base
--local enemyEntities = s.find_entities_filtered {position = {xpos, ypos}, radius = radius, force = "enemy"}
local enemyEntities = player.surface.count_entities_filtered {position = {xpos,ypos}, radius=radius+30, force = "enemy", limit=1}
if enemyEntities < 1 then
player.print({'command_description.crash_site_airstrike_friendly_fire_error'}, Color.fail)
Utils.print_admins(player.name .. " tried to airstrike the base here: [gps=" .. xpos .. "," .. ypos ..",redmew]", nil)
return
end
inv.remove({name = "poison-capsule", count = strikeCost})
game.print({'command_description.crash_site_airstrike_success', player.name, xpos, ypos})
for j=1,count do
set_timeout_in_ticks(
30*j,
spawn_poison_callback,
{s = s, xpos = xpos, ypos=ypos, count = count, r=radius}
)
set_timeout_in_ticks(
60*j,
chart_area_callback,
{player = player, xpos = xpos, ypos=ypos}
)
end
end
Event.add(Retailer.events.on_market_purchase, function(event)
local market_id = event.group_name
local group_label = Retailer.get_market_group_label(market_id)
if group_label ~= 'Spawn' then
return
end
local item = event.item
if item.type ~= 'airstrike' then
return
end
-- airstrike stuff
local radius_level = airstrike_data.radius_level -- max radius of the strike area
local count_level = airstrike_data.count_level -- the number of poison capsules launched at the enemy
local radius = 5+(radius_level*3)
local count = (count_level-1)*5+3
local strikeCost = count * 4
local name = item.name
if name == 'airstrike_damage' then
airstrike_data.count_level = airstrike_data.count_level + 1
Toast.toast_all_players(15, {'command_description.crash_site_airstrike_damage_upgrade_success', count_level})
item.name_label = {'command_description.crash_site_airstrike_count_name_label', (count_level + 1)}
item.price = math.floor(math.exp(airstrike_data.count_level^0.8)/2)*1000
item.description = {'command_description.crash_site_airstrike_count', (count_level + 1), count_level, count, tostring(strikeCost) .. ' poison capsules'}
Retailer.set_item(market_id, item) -- this updates the retailer with the new item values.
elseif name == 'airstrike_radius' then
airstrike_data.radius_level = airstrike_data.radius_level + 1
Toast.toast_all_players(15, {'command_description.crash_site_airstrike_radius_upgrade_success', radius_level})
item.name_label = {'command_description.crash_site_airstrike_radius_name_label', (radius_level + 1)}
item.description = {'command_description.crash_site_airstrike_radius', (radius_level + 1), radius_level, radius}
item.price = math.floor(math.exp(airstrike_data.radius_level^0.8)/2)*1000
Retailer.set_item(market_id, item) -- this updates the retailer with the new item values.
end
end)
Command.add(
'crash-site-restart-abort',
{
description = {'command_description.crash_site_restart_abort'},
required_rank = Ranks.admin,
allowed_by_server = true
},
abort
)
Command.add(
'abort',
{
description = {'command_description.crash_site_restart_abort'},
required_rank = Ranks.admin,
allowed_by_server = true
},
abort
)
local default_name = config.scenario_name or 'crashsite'
Command.add(
'crash-site-restart',
{
description = {'command_description.crash_site_restart'},
arguments = {'scenario_name'},
default_values = {scenario_name = default_name},
required_rank = Ranks.admin,
allowed_by_server = true
},
restart
)
Command.add(
'restart',
{
description = {'command_description.crash_site_restart'},
arguments = {'scenario_name'},
default_values = {scenario_name = default_name},
required_rank = Ranks.auto_trusted,
allowed_by_server = true
},
restart
)
Command.add(
'spy',
{
description = {'command_description.crash_site_spy'},
arguments = {'location'},
capture_excess_arguments = true,
required_rank = Ranks.guest,
allowed_by_server = false
},
spy
)
Command.add(
'strike',
{
description = {'command_description.strike'},
arguments = {'location'},
capture_excess_arguments = true,
required_rank = Ranks.guest,
allowed_by_server = false
},
strike
)
end
return Public