1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2025-05-13 21:56:29 +02:00

Merge pull request #225 from ComfyFactory/scrap_fixes

scrap towny ffa fixes
This commit is contained in:
Gerkiz 2022-03-01 19:21:14 +01:00 committed by GitHub
commit 01dc8160b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 2611 additions and 772 deletions

View File

@ -6,6 +6,7 @@ require 'modules.biters_yield_coins'
require 'modules.scrap_towny_ffa.mining'
require 'modules.scrap_towny_ffa.on_tick_schedule'
require 'modules.scrap_towny_ffa.building'
require 'modules.scrap_towny_ffa.spaceship'
require 'modules.scrap_towny_ffa.town_center'
require 'modules.scrap_towny_ffa.market'
require 'modules.scrap_towny_ffa.slots'
@ -18,6 +19,8 @@ require 'modules.scrap_towny_ffa.trap'
require 'modules.scrap_towny_ffa.turrets_drop_ammo'
require 'modules.scrap_towny_ffa.combat_balance'
local Autostash = require 'modules.autostash'
local BottomFrame = require 'comfy_panel.bottom_frame'
local Table = require 'modules.scrap_towny_ffa.table'
local Nauvis = require 'modules.scrap_towny_ffa.nauvis'
local Biters = require 'modules.scrap_towny_ffa.biters'
@ -28,43 +31,91 @@ local Team = require 'modules.scrap_towny_ffa.team'
local Spawn = require 'modules.scrap_towny_ffa.spawn'
local Radar = require 'modules.scrap_towny_ffa.limited_radar'
local default_surface = 'nauvis'
-- for testing purposes only!!!
local testing_mode = false
-- how long in ticks between spawn and death will be considered spawn kill (10 seconds)
local max_ticks_between_spawns = 60 * 10
-- how many players must login before teams are teams_enabled
local min_players_for_enabling_towns = 0
local function load_buffs(player)
if player.force.name ~= 'player' and player.force.name ~= 'rogue' then return end
local ffatable = Table.get_table()
local player_index = player.index
if player.character == nil then return end
if ffatable.buffs[player_index] == nil then ffatable.buffs[player_index] = {} end
if ffatable.buffs[player_index].character_inventory_slots_bonus ~= nil then
player.character.character_inventory_slots_bonus = ffatable.buffs[player_index].character_inventory_slots_bonus
end
if ffatable.buffs[player_index].character_mining_speed_modifier ~= nil then
player.character.character_mining_speed_modifier = ffatable.buffs[player_index].character_mining_speed_modifier
end
if ffatable.buffs[player_index].character_crafting_speed_modifier ~= nil then
player.character.character_crafting_speed_modifier = ffatable.buffs[player_index].character_crafting_speed_modifier
end
end
local function on_player_joined_game(event)
local ffatable = Table.get_table()
local player = game.players[event.player_index]
local surface = game.surfaces[default_surface]
local surface = game.surfaces['nauvis']
player.game_view_settings.show_minimap = false
player.game_view_settings.show_map_view_options = false
player.game_view_settings.show_entity_info = true
player.map_view_settings = {
["show-logistic-network"] = false,
["show-electric-network"] = false,
["show-turret-range"] = false,
["show-pollution"] = false,
["show-train-station-names"] = false,
["show-player-names"] = false,
["show-networkless-logistic-members"] = false,
["show-non-standard-map-info"] = false
}
player.show_on_map = false
--player.game_view_settings.show_side_menu = false
Info.toggle_button(player)
Info.show(player)
Team.set_player_color(player)
if player.force ~= game.forces.player then
return
end
-- setup outlanders
Team.set_player_to_outlander(player)
if player.online_time == 0 then
Info.show(player)
if testing_mode then
ffatable.towns_enabled = true
else
ffatable.players = ffatable.players + 1
if ffatable.players >= min_players_for_enabling_towns then ffatable.towns_enabled = true end
end
player.teleport({0, 0}, game.surfaces['limbo'])
Team.give_outlander_items(player)
Team.give_key(player)
Team.set_player_to_outlander(player)
Team.give_player_items(player)
player.insert{name="coin", count="100"}
player.insert{name="stone-furnace", count="1"}
Team.give_key(player.index)
if (testing_mode == true) then
player.cheat_mode = true
player.force.research_all_technologies()
player.insert{name="coin", count="9900"}
end
-- first time spawn point
local spawn_point = Spawn.get_spawn_point(player, surface)
local spawn_point = Spawn.get_new_spawn_point(player, surface)
ffatable.strikes[player.name] = 0
Spawn.clear_spawn_point(spawn_point, surface)
-- reset cooldown
ffatable.cooldowns_town_placement[player.index] = 0
ffatable.last_respawn[player.name] = 0
player.teleport(spawn_point, surface)
return
end
load_buffs(player)
if not ffatable.requests[player.index] then
return
end
if ffatable.requests[player.index] ~= 'kill-character' then
if not ffatable.requests[player.index] or ffatable.requests[player.index] ~= 'kill-character' then
return
end
if player.character then
@ -79,39 +130,57 @@ local function on_player_respawned(event)
local ffatable = Table.get_table()
local player = game.players[event.player_index]
local surface = player.surface
Team.give_player_items(player)
if player.force == game.forces['rogue'] then
Team.set_player_to_outlander(player)
end
if player.force == game.forces['player'] then
Team.give_key(player)
Team.give_key(player.index)
end
-- TODO: this needs fixing!
-- 5 second cooldown
--local last_respawn = ffatable.cooldowns_last_respawn[player.name]
--if last_respawn == nil then last_respawn = 0 end
-- get_spawn_point will always return a valid spawn
local spawn_point = Spawn.get_spawn_point(player, surface)
-- reset cooldown
ffatable.cooldowns_last_respawn[player.name] = game.tick
player.teleport(surface.find_non_colliding_position('character', spawn_point, 0, 0.5, false), surface)
-- reset cooldown
ffatable.last_respawn[player.name] = game.tick
player.teleport(spawn_point, surface)
load_buffs(player)
end
local function on_player_died(event)
local ffatable = Table.get_table()
local player = game.players[event.player_index]
ffatable.cooldowns_last_death[player.name] = game.tick
if ffatable.strikes[player.name] == nil then ffatable.strikes[player.name] = 0 end
local ticks_elapsed = game.tick - ffatable.last_respawn[player.name]
if ticks_elapsed < max_ticks_between_spawns then
ffatable.strikes[player.name] = ffatable.strikes[player.name] + 1
else
ffatable.strikes[player.name] = 0
end
end
local function on_init()
local ffatable = Table.get_table()
Autostash.insert_into_furnace(true)
Autostash.insert_into_wagon(true)
Autostash.bottom_button(true)
BottomFrame.reset()
BottomFrame.activate_custom_buttons(true)
--log("on_init")
game.enemy_has_vision_on_land_mines = false
game.draw_resource_selection = true
game.disable_tutorial_triggers()
ffatable.cooldowns_last_respawn = {}
ffatable.cooldowns_last_death = {}
local ffatable = Table.get_table()
ffatable.last_respawn = {}
ffatable.last_death = {}
ffatable.strikes = {}
ffatable.testing_mode = testing_mode
ffatable.spawn_point = {}
ffatable.buffs = {}
ffatable.players = 0
ffatable.towns_enabled = true
Nauvis.initialize()
Team.initialize()
@ -136,6 +205,8 @@ local function on_nth_tick(event)
if not tick_actions[seconds] then
return
end
--game.surfaces['nauvis'].play_sound({path = 'utility/alert_destroyed', volume_modifier = 1})
--log('seconds = ' .. seconds)
tick_actions[seconds]()
end

View File

@ -1,4 +1,3 @@
--luacheck: ignore
local Public = {}
local math_random = math.random
local math_floor = math.floor
@ -10,6 +9,7 @@ local table_remove = table.remove
local table_shuffle = table.shuffle_table
local Global = require 'utils.global'
local BiterHealthBooster = require 'modules.biter_health_booster_v2'
local tick_schedule = {}
Global.register(
@ -29,7 +29,7 @@ local function get_commmands(target, group)
local target_position = target.position
local distance_to_target = math_floor(math_sqrt((target_position.x - group_position.x) ^ 2 + (target_position.y - group_position.y) ^ 2))
local steps = math_floor(distance_to_target / step_length) + 1
local steps = math_floor((distance_to_target - 27) / step_length) + 1
local vector = {math_round((target_position.x - group_position.x) / steps, 3), math_round((target_position.y - group_position.y) / steps, 3)}
for _ = 1, steps, 1 do
@ -38,7 +38,7 @@ local function get_commmands(target, group)
local position = group.surface.find_non_colliding_position('small-biter', group_position, step_length, 2)
if position then
commands[#commands + 1] = {
type = defines.command.attack_area,
type = defines.command.go_to_location,
destination = {x = position.x, y = position.y},
radius = 16,
distraction = defines.distraction.by_damage
@ -46,17 +46,22 @@ local function get_commmands(target, group)
end
end
commands[#commands + 1] = {
type = defines.command.attack_area,
destination = target.position,
radius = 12,
distraction = defines.distraction.by_enemy
}
commands[#commands + 1] = {
type = defines.command.attack,
target = target,
distraction = defines.distraction.by_damage
}
commands[#commands + 1] = {
type = defines.command.attack_area,
destination = target.position,
radius = 27,
distraction = defines.distraction.by_anything
}
commands[#commands + 1] = {
type = defines.command.build_base,
destination = target.position,
distraction = defines.distraction.by_damage
}
return commands
end
@ -68,8 +73,13 @@ local function roll_market()
return
end
local keyset = {}
for town_name, _ in pairs(town_centers) do
table_insert(keyset, town_name)
for town_name, town_center in pairs(town_centers) do
local evolution = Evolution.get_biter_evolution(town_center.market.position)
local tries = math_floor(evolution * 20)
-- 1 try for each 5% of evolution
for _ = 1, tries, 1 do
table_insert(keyset, town_name)
end
end
local tc = math_random(1, #keyset)
return town_centers[keyset[tc]]
@ -107,6 +117,9 @@ end
function Public.validate_swarms()
local ffatable = Table.get_table()
if ffatable.testing_mode then
return
end
for k, swarm in pairs(ffatable.swarms) do
if not is_swarm_valid(swarm) then
table_remove(ffatable.swarms, k)
@ -116,6 +129,9 @@ end
function Public.unit_groups_start_moving()
local ffatable = Table.get_table()
if ffatable.testing_mode then
return
end
for _, swarm in pairs(ffatable.swarms) do
if swarm.group then
if swarm.group.valid then
@ -125,22 +141,34 @@ function Public.unit_groups_start_moving()
end
end
local function new_town(town_center)
-- cooldown for swarms on new towns is 15 minutes
local cooldown_ticks = 15 * 3600
local elapsed_ticks = game.tick - town_center.creation_tick
-- skip if within first 30 minutes and town evolution < 0.25
if elapsed_ticks < cooldown_ticks and town_center.evolution.biters < 0.15 then
return true
end
return false
end
function Public.swarm(town_center, radius)
if town_center == nil then
return
end
local ffatable = Table.get_table()
if ffatable.testing_mode then
return
end
local r = radius or 32
local tc = town_center or roll_market()
if not tc or r > 512 then
return
end
-- skip if town evolution < 0.25
if town_center.get_biter_evolution < 0.25 then
-- cancel if relatively new town
if new_town(tc) then
return
end
-- skip if we have to many swarms already
local count = table_size(ffatable.swarms)
local towns = table_size(ffatable.town_centers)
@ -167,15 +195,18 @@ function Public.swarm(town_center, radius)
return
end
-- get our evolution
local evolution = 0
-- get our evolution at the spawner location
local evolution
if spawner.name == 'spitter-spawner' then
evolution = Evolution.get_biter_evolution(spawner)
else
evolution = Evolution.get_spitter_evolution(spawner)
end
-- get our target amount of enemies
-- get the evolution at the market location
local market_evolution = Evolution.get_evolution(market.position)
-- get our target amount of enemies based on relative evolution
local count2 = (evolution * 124) + 4
local units = spawner.surface.find_enemy_units(spawner.position, 16, market.force)
@ -192,6 +223,18 @@ function Public.swarm(town_center, radius)
return
end
-- try up to 10 times to throw in a boss based on market evolution
-- evolution = 0%, chances 0 in 10
-- evoution = 10%, chances 1 in 10
-- evolution = 20%, chances 2 in 10
-- evolution = 100%, chances 10 in 10
local health_multiplier = 40 * market_evolution + 10
for _ = 1, math_floor(market_evolution * 10), 1 do
if math_random(1, 2) == 1 then
BiterHealthBooster.add_boss_unit(units[1], health_multiplier)
end
end
local unit_group_position = surface.find_non_colliding_position('biter-spawner', units[1].position, 256, 1)
if not unit_group_position then
return
@ -215,6 +258,30 @@ function Public.swarm(town_center, radius)
table_insert(ffatable.swarms, {group = unit_group, timeout = game.tick + 36000})
end
local function on_unit_group_finished_gathering(event)
local unit_group = event.group
local position = unit_group.position
local entities = unit_group.surface.find_entities_filtered({position = position, radius = 256, name = 'market'})
local target = entities[1]
if target ~= nil then
local force = target.force
local ffatable = Table.get_table()
local town_centers = ffatable.town_centers
local town_center = town_centers[force.name]
-- cancel if relatively new town
if new_town(town_center) then
return
end
unit_group.set_command(
{
type = defines.command.compound,
structure_type = defines.compound_command.return_last,
commands = get_commmands(target, unit_group)
}
)
end
end
local function on_tick()
if not tick_schedule[game.tick] then
return
@ -232,10 +299,13 @@ end
local on_init = function()
local ffatable = Table.get_table()
ffatable.swarms = {}
BiterHealthBooster.acid_nova(true)
BiterHealthBooster.check_on_entity_died(true)
end
local Event = require 'utils.event'
Event.on_init(on_init)
Event.add(defines.events.on_tick, on_tick)
Event.add(defines.events.on_unit_group_finished_gathering, on_unit_group_finished_gathering)
return Public

View File

@ -1,29 +1,89 @@
--luacheck: ignore
local Public = {}
local math_floor = math.floor
local table_insert = table.insert
local table_size = table.size
local Table = require 'modules.scrap_towny_ffa.table'
local town_radius = 27
local connection_radius = 7
local blacklist_entity_types = {
['car'] = true,
['character'] = true,
['combat-robot'] = true,
['construction-robot'] = true,
['logistic-robot'] = true,
['entity-ghost'] = true,
['character-corpse'] = true,
['corpse'] = true
}
-- these should be allowed to place inside any base by outlanders as neutral
local neutral_whitelist = {
['wooden-chest'] = true,
['burner-inserter'] = true,
['car'] = true,
['coin'] = true,
['express-loader'] = true,
['fast-inserter'] = true,
['fast-loader'] = true,
['filter-inserter'] = true,
['inserter'] = true,
['iron-chest'] = true,
['loader'] = true,
['long-handed-inserter'] = true,
['raw-fish'] = true,
['stack-filter-inserter'] = true,
['stack-inserter'] = true,
['steel-chest'] = true,
['raw-fish'] = true
['tank'] = true,
['wooden-chest'] = true
}
local entity_type_whitelist = {
-- these should be allowed to place outside any base by town players
local team_whitelist = {
['burner-inserter'] = true,
['car'] = true,
['cargo-wagon'] = true,
['coin'] = true,
['curved-rail'] = true,
['electric-pole'] = true,
['express-loader'] = true,
['fast-inserter'] = true,
['fast-loader'] = true,
['filter-inserter'] = true,
['inserter'] = true,
['fluid-wagon'] = true,
['iron-chest'] = true,
['loader'] = true,
['long-handed-inserter'] = true,
['locomotive'] = true,
['rail'] = true,
['rail-chain-signal'] = true,
['rail-signal'] = true,
['raw-fish'] = true,
['stack-filter-inserter'] = true,
['stack-inserter'] = true,
['steel-chest'] = true,
['straight-rail'] = true,
['tank'] = true,
['train-stop'] = true,
['wooden-chest'] = true
}
-- these need to be prototypes
local team_entities = {
['accumulator'] = true,
['ammo-turret'] = true,
['arithmetic-combinator'] = true,
['artillery-turret'] = true,
['assembling-machine'] = true,
['beacon'] = true,
['boiler'] = true,
['burner-generator'] = true,
['constant-combinator'] = true,
['container'] = true,
['curved-rail'] = true,
['decider-combinator'] = true,
['electric-energy-interface'] = true,
['electric-pole'] = true,
['electric-turret'] = true,
['fluid-turret'] = true,
@ -38,26 +98,27 @@ local entity_type_whitelist = {
['lab'] = true,
['lamp'] = true,
['land-mine'] = true,
['linked-belt'] = true,
['linked-container'] = true,
['loader'] = true,
['loader-1x1'] = true,
['logistic-container'] = true,
['market'] = true,
['mining-drill'] = true,
['offshore-pump'] = true,
['pipe'] = true,
['pipe-to-ground'] = true,
['player-port'] = true,
['power-switch'] = true,
['programmable-speaker'] = true,
['pump'] = true,
['radar'] = true,
['rail-chain-signal'] = true,
['rail-signal'] = true,
['reactor'] = true,
['roboport'] = true,
['rocket-silo'] = true,
['solar-panel'] = true,
['splitter'] = true,
['storage-tank'] = true,
['straight-rail'] = true,
['train-stop'] = true,
['transport-belt'] = true,
['underground-belt'] = true,
['wall'] = true
@ -70,7 +131,7 @@ local function isolated(surface, force, position)
local count = 0
for _, e in pairs(surface.find_entities_filtered({area = area, force = force.name})) do
if entity_type_whitelist[e.type] then
if team_entities[e.type] then
count = count + 1
if count > 1 then
return false
@ -85,12 +146,13 @@ local function refund_item(event, item_name)
if item_name == 'blueprint' then
return
end
if event.player_index then
if event.player_index ~= nil then
game.players[event.player_index].insert({name = item_name, count = 1})
return
end
if event.robot then
-- return item to robot, but don't replace ghost (otherwise might loop)
if event.robot ~= nil then
local inventory = event.robot.get_inventory(defines.inventory.robot_cargo)
inventory.insert({name = item_name, count = 1})
return
@ -108,7 +170,7 @@ local function error_floaty(surface, position, msg)
)
end
local function in_range(pos1, pos2, radius)
function Public.in_range(pos1, pos2, radius)
if pos1 == nil then
return false
end
@ -126,170 +188,399 @@ local function in_range(pos1, pos2, radius)
return false
end
-- is the position near a town?
function Public.near_town(position, surface, radius)
local ffatable = Table.get_table()
for _, town_center in pairs(ffatable.town_centers) do
if town_center ~= nil then
local market = town_center.market
if in_range(position, market.position, radius) and market.surface == surface then
return true
end
function Public.in_area(position, area_center, area_radius)
if position == nil then
return false
end
if area_center == nil then
return false
end
if area_radius < 1 then
return true
end
if position.x >= area_center.x - area_radius and position.x <= area_center.x + area_radius then
if position.y >= area_center.y - area_radius and position.y <= area_center.y + area_radius then
return true
end
end
return false
end
local function in_town(force, position)
-- is the position near another town?
function Public.near_another_town(force_name, position, surface, radius)
-- check for nearby town centers
if force_name == nil then
return false
end
local ffatable = Table.get_table()
local forces = {}
-- check for nearby town centers
local fail = false
if table_size(ffatable.town_centers) > 0 then
for _, town_center in pairs(ffatable.town_centers) do
if town_center ~= nil then
local market = town_center.market
if market ~= nil and market.valid then
local market_force = market.force
if market_force ~= nil then
if market_force.name ~= nil then
table_insert(forces, market_force.name)
if force_name ~= market_force.name then
if Public.in_range(position, market.position, radius) == true then
fail = true
break
end
end
end
end
end
end
end
if fail == true then
return true
end
end
-- check for nearby town entities
if table.size(forces) > 0 then
local entities = surface.find_entities_filtered({position = position, radius = radius, force = forces})
for _, e in pairs(entities) do
if e.valid and e.force ~= nil then
local entity_force_name = e.force.name
--if force_name ~= force.name and force_name ~= 'enemy' and force_name ~= 'neutral' and force_name ~= 'player' and force_name ~= 'rogue' then
if entity_force_name ~= nil then
if entity_force_name ~= force_name then
if blacklist_entity_types[e.type] ~= true then
fail = true
break
end
end
end
end
end
if fail == true then
return true
end
end
return false
end
local function in_own_town(force, position)
local ffatable = Table.get_table()
local town_center = ffatable.town_centers[force.name]
if town_center ~= nil then
local center = town_center.market.position
if position.x >= center.x - town_radius and position.x <= center.x + town_radius then
if position.y >= center.y - town_radius and position.y <= center.y + town_radius then
return true
local market = town_center.market
if market ~= nil then
local center = market.position
if position.x >= center.x - town_radius and position.x <= center.x + town_radius then
if position.y >= center.y - town_radius and position.y <= center.y + town_radius then
return true
end
end
end
end
return false
end
local function prevent_isolation_entity(event, player)
local p = player or nil
function Public.in_restricted_zone(surface, position)
if surface.name ~= 'nauvis' then
return false
end
local chunk_position = {}
chunk_position.x = math_floor(position.x / 32)
chunk_position.y = math_floor(position.y / 32)
if chunk_position.x <= -33 or chunk_position.x >= 32 or chunk_position.y <= -33 or chunk_position.y >= 32 then
return true
end
return false
end
local function prevent_entity_in_restricted_zone(event)
local player_index = event.player_index or nil
local entity = event.created_entity
local position = entity.position
if not entity.valid then
return
end
local entity_name = entity.name
local item = event.item
if item == nil then
return
end
local item_name = item.name
local force = entity.force
if force == game.forces.player then
return
end
if force == game.forces['rogue'] then
if entity == nil or not entity.valid then
return
end
local name = entity.name
local surface = entity.surface
local position = entity.position
local error = false
if not in_town(force, position) and isolated(surface, force, position) then
if Public.in_restricted_zone(surface, position) then
error = true
entity.destroy()
if entity_name ~= 'entity-ghost' and entity_name ~= 'tile-ghost' then
refund_item(event, item_name)
local item = event.item
if name ~= 'entity-ghost' and name ~= 'tile-ghost' and item ~= nil then
refund_item(event, item.name)
end
--return true
end
if error == true then
if p ~= nil then
p.play_sound({path = 'utility/cannot_build', position = p.position, volume_modifier = 0.75})
if player_index ~= nil then
local player = game.players[player_index]
player.play_sound({path = 'utility/cannot_build', position = player.position, volume_modifier = 0.75})
end
error_floaty(surface, position, 'Building is not connected to town!')
error_floaty(surface, position, 'Can not build in restricted zone!')
end
end
local function prevent_isolation_tile(event, player)
local p = player or nil
local tile = event.tile
if not tile.valid then
local function prevent_unconnected_town_entities(event)
local player_index = event.player_index or nil
local entity = event.created_entity
if entity == nil or not entity.valid then
return
end
local name = entity.name
local surface = entity.surface
local position = entity.position
local force = entity.force
if force.index == game.forces.player.index or force.index == game.forces['rogue'].index then
-- no town restrictions if outlander or rogue
return
end
local error = false
if name ~= 'entity-ghost' then
if not in_own_town(force, position) and isolated(surface, force, position) and not team_whitelist[name] then
error = true
entity.destroy()
local item = event.item
if item ~= nil then
refund_item(event, item.name)
end
end
end
if error == true then
if player_index ~= nil then
local player = game.players[player_index]
player.play_sound({path = 'utility/cannot_build', position = player.position, volume_modifier = 0.75})
end
error_floaty(surface, position, 'Building is not connected to your town!')
end
end
local function prevent_landfill_in_restricted_zone(event)
local player_index = event.player_index or nil
local tile = event.tile
if tile == nil or not tile.valid then
return
end
local surface = game.surfaces[event.surface_index]
local fail = false
local position
for _, t in pairs(event.tiles) do
local old_tile = t.old_tile
position = t.position
if Public.in_restricted_zone(surface, position) then
fail = true
surface.set_tiles({{name = old_tile.name, position = position}}, true)
refund_item(event, tile.name)
end
end
if fail == true then
if player_index ~= nil then
local player = game.players[player_index]
player.play_sound({path = 'utility/cannot_build', position = player.position, volume_modifier = 0.75})
end
error_floaty(surface, position, 'Can not build in restricted zone!')
end
return fail
end
local function prevent_unconnected_town_tiles(event)
local player_index = event.player_index or nil
local tile = event.tile
if tile == nil or not tile.valid then
return
end
local tile_name = tile.name
local surface = game.surfaces[event.surface_index]
local tiles = event.tiles
local force
if event.player_index then
force = game.players[event.player_index].force
if player_index ~= nil then
force = game.players[player_index].force
else
force = event.robot.force
end
local error = false
local fail = false
local position
for _, t in pairs(tiles) do
local old_tile = t.old_tile
position = t.position
if not in_town(force, position) and isolated(surface, force, position) then
error = true
surface.set_tiles({{name = old_tile.name, position = position}}, true)
if tile_name ~= 'tile-ghost' then
if tile_name == 'stone-path' then
tile_name = 'stone-brick'
if tile.name ~= 'tile-ghost' then
if not in_own_town(force, position) and isolated(surface, force, position) then
fail = true
surface.set_tiles({{name = old_tile.name, position = position}}, true)
if tile.name == 'stone-path' then
tile.name = 'stone-brick'
end
refund_item(event, tile_name)
refund_item(event, tile.name)
end
end
end
if error == true then
if p ~= nil then
p.play_sound({path = 'utility/cannot_build', position = p.position, volume_modifier = 0.75})
if fail == true then
if player_index ~= nil then
local player = game.players[player_index]
player.play_sound({path = 'utility/cannot_build', position = player.position, volume_modifier = 0.75})
end
error_floaty(surface, position, 'Tile is not connected to town!')
end
end
local function restrictions(event, player)
local p = player or nil
local function prevent_entities_near_towns(event)
local player_index = event.player_index or nil
local entity = event.created_entity
if not entity.valid then
if entity == nil or not entity.valid then
return
end
local entity_name = entity.name
local name = entity.name
local surface = entity.surface
local position = entity.position
local error = false
if entity.force == game.forces['player'] or entity.force == game.forces['rogue'] then
if Public.near_town(position, surface, 32) then
error = true
local force_name
if player_index ~= nil then
local player = game.players[player_index]
if player ~= nil then
local force = player.force
if force ~= nil then
force_name = force.name
end
end
else
local robot = event.robot
if robot ~= nil then
local force = robot.force
if force ~= nil then
force_name = force.name
end
end
end
if Public.near_another_town(force_name, position, surface, 32) == true then
if neutral_whitelist[name] then
entity.force = game.forces['neutral']
else
entity.destroy()
if entity_name ~= 'entity-ghost' then
if player_index ~= nil then
local player = game.players[player_index]
player.play_sound({path = 'utility/cannot_build', position = player.position, volume_modifier = 0.75})
end
error_floaty(surface, position, "Can't build near town!")
if name ~= 'entity-ghost' then
refund_item(event, event.stack.name)
end
else
entity.force = game.forces['neutral']
return
end
end
end
local function prevent_tiles_near_towns(event)
local player_index = event.player_index or nil
local tile = event.tile
if tile == nil or not tile.valid then
return
end
if error == true then
if p ~= nil then
p.play_sound({path = 'utility/cannot_build', position = p.position, volume_modifier = 0.75})
local surface = game.surfaces[event.surface_index]
local force_name
if player_index ~= nil then
local player = game.players[player_index]
if player ~= nil then
local force = player.force
if force ~= nil then
force_name = force.name
end
end
else
local robot = event.robot
if robot ~= nil then
local force = robot.force
if force ~= nil then
force_name = force.name
end
end
end
local fail = false
local position
for _, t in pairs(event.tiles) do
local old_tile = t.old_tile
position = t.position
if Public.near_another_town(force_name, position, surface, 32) == true then
fail = true
surface.set_tiles({{name = old_tile.name, position = position}}, true)
refund_item(event, tile.name)
end
end
if fail == true then
if player_index ~= nil then
local player = game.players[player_index]
player.play_sound({path = 'utility/cannot_build', position = player.position, volume_modifier = 0.75})
end
error_floaty(surface, position, "Can't build near town!")
end
return fail
end
if not neutral_whitelist[entity.type] then
return
local function prevent_neutral_deconstruct(event)
local player = game.players[event.player_index] or nil
local entity = event.entity
if entity.to_be_deconstructed() and entity.force.name == 'neutral' then
for _, f in pairs(game.forces) do
if entity.is_registered_for_deconstruction(f) then
entity.cancel_deconstruction(f, player)
end
end
end
entity.force = game.forces['neutral']
end
-- called when a player places an item, or a ghost
local function on_built_entity(event)
local player = game.players[event.player_index]
if prevent_isolation_entity(event, player) then
if prevent_entity_in_restricted_zone(event) then
return
end
restrictions(event, player)
if prevent_entities_near_towns(event) then
return
end
if player.force.index ~= game.forces['player'].index and player.force.index ~= game.forces['rogue'].index then
prevent_unconnected_town_entities(event)
end
end
local function on_robot_built_entity(event)
if prevent_isolation_entity(event) then
local robot = event.robot
if prevent_entity_in_restricted_zone(event) then
return
end
restrictions(event)
if prevent_entities_near_towns(event) then
return
end
if robot.force.index ~= game.forces['player'].index and robot.force.index ~= game.forces['rogue'].index then
prevent_unconnected_town_entities(event)
end
end
-- called when a player places landfill
local function on_player_built_tile(event)
local player = game.players[event.player_index]
prevent_isolation_tile(event, player)
if prevent_landfill_in_restricted_zone(event) then
return
end
if prevent_tiles_near_towns(event) then
return
end
if player.force.index ~= game.forces['player'].index and player.force.index ~= game.forces['rogue'].index then
prevent_unconnected_town_tiles(event)
end
end
local function on_robot_built_tile(event)
prevent_isolation_tile(event)
local robot = event.robot
if prevent_landfill_in_restricted_zone(event) then
return
end
if prevent_tiles_near_towns(event) then
return
end
if robot.force.index ~= game.forces['player'].index and robot.force.index ~= game.forces['rogue'].index then
prevent_unconnected_town_tiles(event)
end
end
local function on_marked_for_deconstruction(event)
prevent_neutral_deconstruct(event)
end
local Event = require 'utils.event'
@ -297,5 +588,6 @@ Event.add(defines.events.on_built_entity, on_built_entity)
Event.add(defines.events.on_player_built_tile, on_player_built_tile)
Event.add(defines.events.on_robot_built_entity, on_robot_built_entity)
Event.add(defines.events.on_robot_built_tile, on_robot_built_tile)
Event.add(defines.events.on_marked_for_deconstruction, on_marked_for_deconstruction)
return Public

View File

@ -0,0 +1,216 @@
local Public = {}
local table_shuffle = table.shuffle_table
local Table = require 'modules.scrap_towny_ffa.table'
local Color = require 'utils.color_presets'
local colors = {}
colors[1] = {name = 'Almond', rgb = {239, 222, 205}}
colors[2] = {name = 'Antique Brass', rgb = {205, 149, 117}}
colors[3] = {name = 'Apricot', rgb = {253, 217, 181}}
colors[4] = {name = 'Aquamarine', rgb = {120, 219, 226}}
colors[5] = {name = 'Asparagus', rgb = {135, 169, 107}}
colors[6] = {name = 'Atomic Tangerine', rgb = {255, 164, 116}}
colors[7] = {name = 'Banana Mania', rgb = {250, 231, 181}}
colors[8] = {name = 'Beaver', rgb = {159, 129, 112}}
colors[9] = {name = 'Bittersweet', rgb = {253, 124, 110}}
colors[10] = {name = 'Black', rgb = {0, 0, 0}}
colors[11] = {name = 'Blizzard Blue', rgb = {172, 229, 238}}
colors[12] = {name = 'Blue', rgb = {31, 117, 254}}
colors[13] = {name = 'Blue Bell', rgb = {162, 162, 208}}
colors[14] = {name = 'Blue Gray', rgb = {102, 153, 204}}
colors[15] = {name = 'Blue Green', rgb = {13, 152, 186}}
colors[16] = {name = 'Blue Violet', rgb = {115, 102, 189}}
colors[17] = {name = 'Blush', rgb = {222, 93, 131}}
colors[18] = {name = 'Brick Red', rgb = {203, 65, 84}}
colors[19] = {name = 'Brown', rgb = {180, 103, 77}}
colors[20] = {name = 'Burnt Orange', rgb = {255, 127, 73}}
colors[21] = {name = 'Burnt Sienna', rgb = {234, 126, 93}}
colors[22] = {name = 'Cadet Blue', rgb = {176, 183, 198}}
colors[23] = {name = 'Canary', rgb = {255, 255, 153}}
colors[24] = {name = 'Caribbean Green', rgb = {28, 211, 162}}
colors[25] = {name = 'Carnation Pink', rgb = {255, 170, 204}}
colors[26] = {name = 'Cerise', rgb = {221, 68, 146}}
colors[27] = {name = 'Cerulean', rgb = {29, 172, 214}}
colors[28] = {name = 'Chestnut', rgb = {188, 93, 88}}
colors[29] = {name = 'Copper', rgb = {221, 148, 117}}
colors[30] = {name = 'Cornflower', rgb = {154, 206, 235}}
colors[31] = {name = 'Cotton Candy', rgb = {255, 188, 217}}
colors[32] = {name = 'Dandelion', rgb = {253, 219, 109}}
colors[33] = {name = 'Denim', rgb = {43, 108, 196}}
colors[34] = {name = 'Desert Sand', rgb = {239, 205, 184}}
colors[35] = {name = 'Eggplant', rgb = {110, 81, 96}}
colors[36] = {name = 'Electric Lime', rgb = {206, 255, 29}}
colors[37] = {name = 'Fern', rgb = {113, 188, 120}}
colors[38] = {name = 'Forest Green', rgb = {109, 174, 129}}
colors[39] = {name = 'Fuchsia', rgb = {195, 100, 197}}
colors[40] = {name = 'Fuzzy Wuzzy', rgb = {204, 102, 102}}
colors[41] = {name = 'Gold', rgb = {231, 198, 151}}
colors[42] = {name = 'Goldenrod', rgb = {252, 217, 117}}
colors[43] = {name = 'Granny Smith Apple', rgb = {168, 228, 160}}
colors[44] = {name = 'Gray', rgb = {149, 145, 140}}
colors[45] = {name = 'Green', rgb = {28, 172, 120}}
colors[46] = {name = 'Green Blue', rgb = {17, 100, 180}}
colors[47] = {name = 'Green Yellow', rgb = {240, 232, 145}}
colors[48] = {name = 'Hot Magenta', rgb = {255, 29, 206}}
colors[49] = {name = 'Inchworm', rgb = {178, 236, 93}}
colors[50] = {name = 'Indigo', rgb = {93, 118, 203}}
colors[51] = {name = 'Jazzberry Jam', rgb = {202, 55, 103}}
colors[52] = {name = 'Jungle Green', rgb = {59, 176, 143}}
colors[53] = {name = 'Laser Lemon', rgb = {254, 254, 34}}
colors[54] = {name = 'Lavender', rgb = {252, 180, 213}}
colors[55] = {name = 'Lemon Yellow', rgb = {255, 244, 79}}
colors[56] = {name = 'Macaroni and Cheese', rgb = {255, 189, 136}}
colors[57] = {name = 'Magenta', rgb = {246, 100, 175}}
colors[58] = {name = 'Magic Mint', rgb = {170, 240, 209}}
colors[59] = {name = 'Mahogany', rgb = {205, 74, 76}}
colors[60] = {name = 'Maize', rgb = {237, 209, 156}}
colors[61] = {name = 'Manatee', rgb = {151, 154, 170}}
colors[62] = {name = 'Mango Tango', rgb = {255, 130, 67}}
colors[63] = {name = 'Maroon', rgb = {200, 56, 90}}
colors[64] = {name = 'Mauvelous', rgb = {239, 152, 170}}
colors[65] = {name = 'Melon', rgb = {253, 188, 180}}
colors[66] = {name = 'Midnight Blue', rgb = {26, 72, 118}}
colors[67] = {name = 'Mountain Meadow', rgb = {48, 186, 143}}
colors[68] = {name = 'Mulberry', rgb = {197, 75, 140}}
colors[69] = {name = 'Navy Blue', rgb = {25, 116, 210}}
colors[70] = {name = 'Neon Carrot', rgb = {255, 163, 67}}
colors[71] = {name = 'Olive Green', rgb = {186, 184, 108}}
colors[72] = {name = 'Orange', rgb = {255, 117, 56}}
colors[73] = {name = 'Orange Red', rgb = {255, 43, 43}}
colors[74] = {name = 'Orange Yellow', rgb = {248, 213, 104}}
colors[75] = {name = 'Orchid', rgb = {230, 168, 215}}
colors[76] = {name = 'Outer Space', rgb = {65, 74, 76}}
colors[77] = {name = 'Outrageous Orange', rgb = {255, 110, 74}}
colors[78] = {name = 'Pacific Blue', rgb = {28, 169, 201}}
colors[79] = {name = 'Peach', rgb = {255, 207, 171}}
colors[80] = {name = 'Periwinkle', rgb = {197, 208, 230}}
colors[81] = {name = 'Piggy Pink', rgb = {253, 221, 230}}
colors[82] = {name = 'Pine Green', rgb = {21, 128, 120}}
colors[83] = {name = 'Pink Flamingo', rgb = {252, 116, 253}}
colors[84] = {name = 'Pink Sherbert', rgb = {247, 143, 167}}
colors[85] = {name = 'Plum', rgb = {142, 69, 133}}
colors[86] = {name = 'Purple Heart', rgb = {116, 66, 200}}
colors[87] = {name = "Purple Mountain's Majesty", rgb = {157, 129, 186}}
colors[88] = {name = 'Purple Pizzazz', rgb = {254, 78, 218}}
colors[89] = {name = 'Radical Red', rgb = {255, 73, 108}}
colors[90] = {name = 'Raw Sienna', rgb = {214, 138, 89}}
colors[91] = {name = 'Raw Umber', rgb = {113, 75, 35}}
colors[92] = {name = 'Razzle Dazzle Rose', rgb = {255, 72, 208}}
colors[93] = {name = 'Razzmatazz', rgb = {227, 37, 107}}
colors[94] = {name = 'Red', rgb = {238, 32, 77}}
colors[95] = {name = 'Red Orange', rgb = {255, 83, 73}}
colors[96] = {name = 'Red Violet', rgb = {192, 68, 143}}
colors[97] = {name = "Robin's Egg Blue", rgb = {31, 206, 203}}
colors[98] = {name = 'Royal Purple', rgb = {120, 81, 169}}
colors[99] = {name = 'Salmon', rgb = {255, 155, 170}}
colors[100] = {name = 'Scarlet', rgb = {252, 40, 71}}
colors[101] = {name = "Screamin' Green", rgb = {118, 255, 122}}
colors[102] = {name = 'Sea Green', rgb = {159, 226, 191}}
colors[103] = {name = 'Sepia', rgb = {165, 105, 79}}
colors[104] = {name = 'Shadow', rgb = {138, 121, 93}}
colors[105] = {name = 'Shamrock', rgb = {69, 206, 162}}
colors[106] = {name = 'Shocking Pink', rgb = {251, 126, 253}}
colors[107] = {name = 'Silver', rgb = {205, 197, 194}}
colors[108] = {name = 'Sky Blue', rgb = {128, 218, 235}}
colors[109] = {name = 'Spring Green', rgb = {236, 234, 190}}
colors[110] = {name = 'Sunglow', rgb = {255, 207, 72}}
colors[111] = {name = 'Sunset Orange', rgb = {253, 94, 83}}
colors[112] = {name = 'Tan', rgb = {250, 167, 108}}
colors[113] = {name = 'Teal Blue', rgb = {24, 167, 181}}
colors[114] = {name = 'Thistle', rgb = {235, 199, 223}}
colors[115] = {name = 'Tickle Me Pink', rgb = {252, 137, 172}}
colors[116] = {name = 'Timberwolf', rgb = {219, 215, 210}}
colors[117] = {name = 'Tropical Rain Forest', rgb = {23, 128, 109}}
colors[118] = {name = 'Tumbleweed', rgb = {222, 170, 136}}
colors[119] = {name = 'Turquoise Blue', rgb = {119, 221, 231}}
colors[120] = {name = 'Unmellow Yellow', rgb = {255, 255, 102}}
colors[121] = {name = 'Violet (Purple)', rgb = {146, 110, 174}}
colors[122] = {name = 'Violet Blue', rgb = {50, 74, 178}}
colors[123] = {name = 'Violet Red', rgb = {247, 83, 148}}
colors[124] = {name = 'Vivid Tangerine', rgb = {255, 160, 137}}
colors[125] = {name = 'Vivid Violet', rgb = {143, 80, 157}}
colors[126] = {name = 'White', rgb = {255, 255, 255}}
colors[127] = {name = 'Wild Blue Yonder', rgb = {162, 173, 208}}
colors[128] = {name = 'Wild Strawberry', rgb = {255, 67, 164}}
colors[129] = {name = 'Wild Watermelon', rgb = {252, 108, 133}}
colors[130] = {name = 'Wisteria', rgb = {205, 164, 222}}
colors[131] = {name = 'Yellow', rgb = {252, 232, 131}}
colors[132] = {name = 'Yellow Green', rgb = {197, 227, 132}}
colors[133] = {name = 'Yellow Orange', rgb = {255, 174, 66}}
local function is_color_used(color, town_centers)
for _, center in pairs(town_centers) do
if center.color then
if center.color.r == color.r and center.color.g == color.g and center.color.b == color.b then
return true
end
end
end
end
function Public.get_random_color()
local ffatable = Table.get_table()
local town_centers = ffatable.town_centers
local rgb
local color = {}
local name
local shuffle_index = {}
for i = 1, #colors, 1 do
shuffle_index[i] = i
end
table_shuffle(shuffle_index)
for i = 1, #colors, 1 do
rgb = colors[shuffle_index[i]].rgb
name = colors[shuffle_index[i]].name
local red = rgb[1] / 255
local green = rgb[2] / 255
local blue = rgb[3] / 255
color.r = red
color.g = green
color.b = blue
if not is_color_used(color, town_centers) then
--log("color = " .. name)
return {name = name, color = color}
end
end
end
local function random_color(cmd)
local player = game.players[cmd.player_index]
if not player or not player.valid then
return
end
local force = player.force
if force.name == 'player' or force.name == 'rogue' then
player.print('You are not member of a town!', Color.fail)
return
end
local ffatable = Table.get_table()
local town_center = ffatable.town_centers[force.name]
local crayon = Public.get_random_color()
town_center.color = crayon.color
rendering.set_color(town_center.town_caption, town_center.color)
for _, p in pairs(force.players) do
if p.index == player.index then
player.print('Your town color is now ' .. crayon.name, crayon.color)
else
player.print(player.name .. ' has set the town color to ' .. crayon.name, crayon.color)
end
p.color = crayon.color
p.chat_color = crayon.color
end
end
commands.add_command(
'random-color',
'Randomly color your town..',
function(cmd)
random_color(cmd)
end
)
return Public

View File

@ -35,9 +35,8 @@ local balance_functions = {
local function on_research_finished(event)
local research_name = event.research.name
local force_name = event.research.force.name
local key
for b = 1, string_len(research_name), 1 do
key = string_sub(research_name, 0, b)
local key = string_sub(research_name, 0, b)
if balance_functions[key] then
balance_functions[key](force_name)
return

View File

@ -1,5 +1,6 @@
local Public = {}
local math_floor = math.floor
local math_log10 = math.log10
local Table = require 'modules.scrap_towny_ffa.table'
@ -29,6 +30,7 @@ local max_evolution_distance = 1024
local max_pollution_behemoth = 256
local max_pollution_big = 64
local max_pollution_medium = 16
-- max_factor < 1.0 means technology sum of weights will be greater than 1.0
local max_factor = 0.8
-- technology weights (biter, spitter, worm)
@ -106,7 +108,6 @@ local technology_weights = {
['gate'] = {biter = 1, spitter = 1, worm = 1},
['gun-turret'] = {biter = 1, spitter = 1, worm = 1},
['heavy-armor'] = {biter = 5, spitter = 5, worm = 5},
['improved-equipment'] = {biter = 1, spitter = 1, worm = 1},
['inserter-capacity-bonus-1'] = {biter = 1, spitter = 1, worm = 1},
['inserter-capacity-bonus-3'] = {biter = 1, spitter = 1, worm = 1},
['inserter-capacity-bonus-4'] = {biter = 1, spitter = 1, worm = 1},
@ -117,13 +118,13 @@ local technology_weights = {
['land-mine'] = {biter = 5, spitter = 5, worm = 5},
['landfill'] = {biter = 1, spitter = 1, worm = 1},
['laser'] = {biter = 5, spitter = 5, worm = 5},
['laser-turret-speed-1'] = {biter = 5, spitter = 5, worm = 5},
['laser-turret-speed-2'] = {biter = 5, spitter = 5, worm = 5},
['laser-turret-speed-3'] = {biter = 5, spitter = 5, worm = 5},
['laser-turret-speed-4'] = {biter = 5, spitter = 5, worm = 5},
['laser-turret-speed-5'] = {biter = 5, spitter = 5, worm = 5},
['laser-turret-speed-6'] = {biter = 5, spitter = 5, worm = 5},
['laser-turret-speed-7'] = {biter = 5, spitter = 5, worm = 5},
['laser-shooting-speed-1'] = {biter = 5, spitter = 5, worm = 5},
['laser-shooting-speed-2'] = {biter = 5, spitter = 5, worm = 5},
['laser-shooting-speed-3'] = {biter = 5, spitter = 5, worm = 5},
['laser-shooting-speed-4'] = {biter = 5, spitter = 5, worm = 5},
['laser-shooting-speed-5'] = {biter = 5, spitter = 5, worm = 5},
['laser-shooting-speed-6'] = {biter = 5, spitter = 5, worm = 5},
['laser-shooting-speed-7'] = {biter = 5, spitter = 5, worm = 5},
['laser-turret'] = {biter = 5, spitter = 5, worm = 5},
['logistic-robotics'] = {biter = 1, spitter = 1, worm = 1},
['logistic-science-pack'] = {biter = 25, spitter = 25, worm = 25},
@ -240,89 +241,128 @@ max_spitter_weight = max_spitter_weight * max_factor
max_worm_weight = max_worm_weight * max_factor
local function get_unit_size(evolution)
-- returns a value 0-3 that represents the unit size
-- returns a value 1-4 that represents the unit size
-- basically evo values of: 0%, 10%, 30%, 60%, 80%, 100%
-- small unit chances are 100%, 100%, 50%, 25%, 12.5%, 0%
-- medium unit chances are 0%, 0%, 50%, 25%, 12.5%, 0%
-- big unit chances are 0%, 0%, 0%, 50%, 25%, 0%
-- behemoth unit chances are 0%, 0%, 0%, 0%, 50%, 100%
-- basically evo values of: 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
-- -----------------------------------------------------------------------------------
-- small unit chances are 100% 60% 40% 30% 20% 15% 7.5% 0% 0% 0% 0%
-- medium unit chances are 0% 40% 40% 30% 20% 15% 7.5% 12.5% 25% 0% 0%
-- big unit chances are 0% 0% 20% 40% 60% 60% 75% 75% 50% 50% 0%
-- behemoth unit chances are 0% 0% 0% 0% 0% 10% 10% 12.5% 25% 50% 100%
-- and curve accordingly in between evo values
-- magic stuff happens here
if (evolution < 0.10) then
-- 0%
if (evolution < 0.1) then
return 1
end
if (evolution >= 0.10 and evolution < 0.40) then
local r = (evolution - 0.10) * 5
if math.random() < 0.5 then
return 1
end
if math.random() < r then
-- 10%
if (evolution >= 0.1 and evolution < 0.2) then
local r = math.random()
if r < 0.6 then
return 1
end
return 2
end
if (evolution >= 0.30 and evolution < 0.60) then
local r = (evolution - 0.30) * 3.3333
if math.random() < 0.5 then
if math.random() < 0.5 then
-- 20%
if (evolution >= 0.2 and evolution < 0.3) then
local r = math.random()
if r < 0.8 then
if r < 0.4 then
return 1
else
if math.random() < r then
return 2
end
end
return 3
end
-- 30%
if (evolution >= 0.3 and evolution < 0.4) then
local r = math.random()
if r < 0.6 then
if r < 0.3 then
return 1
else
return 2
end
end
return 3
end
-- 40%
if (evolution >= 0.4 and evolution < 0.5) then
local r = math.random()
if r < 0.4 then
if r < 0.2 then
return 1
else
return 2
end
end
return 3
end
-- 50%
if (evolution >= 0.5 and evolution < 0.6) then
local r = math.random()
if r < 0.9 then
if r < 0.3 then
if r < 0.15 then
return 1
else
return 2
end
end
else
if math.random() < r then
return 3
end
return 4
end
-- 60%
if (evolution >= 0.60 and evolution < 0.70) then
local r = math.random()
if r < 0.9 then
if r < 0.15 then
if r < 0.075 then
return 1
else
return 2
end
end
return 3
end
return 4
end
-- 70%
if (evolution >= 0.70 and evolution < 0.80) then
local r = math.random()
if r < 0.985 then
if r < 0.125 then
return 2
else
return 3
end
end
return 4
end
if (evolution >= 0.60 and evolution < 0.80) then
local r = (evolution - 0.60) * 5
if math.random() < 0.5 then
if math.random() < 0.5 then
if math.random() < r then
return 1
else
return 2
end
else
if math.random() < r then
return 2
else
return 3
end
end
else
if math.random() < r then
return 3
else
return 4
end
end
end
if (evolution >= 0.80 and evolution < 1.0) then
local r = (evolution - 0.80) * 5
if math.random() < 0.5 then
if math.random() < r then
if math.random() < r then
return 1
else
return 2
end
-- 80%
if (evolution >= 0.80 and evolution < 0.90) then
local r = math.random()
if r < 0.75 then
if r < 0.25 then
return 2
else
return 3
end
else
return 4
end
return 4
end
-- 90%
if (evolution >= 0.90 and evolution < 1) then
local r = math.random()
if r < 0.5 then
return 3
end
return 4
end
-- 100%
if (evolution >= 1.0) then
return 4
end
@ -336,6 +376,16 @@ local function distance_squared(pos1, pos2)
return d2
end
-- calculate the relative evolution based on evolution factor (0.0-1.0) and distance factor (0.0-1.0)
local function calculate_relative_evolution(evolution_factor, distance_factor)
-- distance factor will be from 0.0 to 1.0 but drop off dramatically towards zero
local log_distance_factor = math_log10(distance_factor * 10 + 1)
local evo = log_distance_factor * evolution_factor
if evo < 0.0 then evo = 0.0 end
if evo > 1.0 then evo = 1.0 end
return evo
end
local function get_relative_biter_evolution(position)
local ffatable = Table.get_table()
local relative_evolution = 0.0
@ -343,9 +393,10 @@ local function get_relative_biter_evolution(position)
-- for all of the teams
local teams = ffatable.town_centers
for _, town_center in pairs(teams) do
local market_position = town_center.market.position
local market = town_center.market
if market == nil or not market.valid then return relative_evolution end
-- calculate the distance squared
local d2 = distance_squared(position, market_position)
local d2 = distance_squared(position, market.position)
if d2 < max_d2 then
-- get the distance factor (0.0-1.0)
local distance_factor = 1.0 - d2 / max_d2
@ -356,8 +407,8 @@ local function get_relative_biter_evolution(position)
if town_center.evolution.biters == nil then
town_center.evolution.biters = 0.0
end
local evolution_factor = town_center.evolution.biters
local evo = distance_factor * evolution_factor
local evo = calculate_relative_evolution(town_center.evolution.biters, distance_factor)
-- get the highest of the relative evolutions of each town
relative_evolution = math.max(relative_evolution, evo)
end
end
@ -371,9 +422,10 @@ local function get_relative_spitter_evolution(position)
-- for all of the teams
local teams = ffatable.town_centers
for _, town_center in pairs(teams) do
local market_position = town_center.market.position
local market = town_center.market
if market == nil or not market.valid then return relative_evolution end
-- calculate the distance squared
local d2 = distance_squared(position, market_position)
local d2 = distance_squared(position, market.position)
if d2 < max_d2 then
-- get the distance factor (0.0-1.0)
local distance_factor = 1.0 - d2 / max_d2
@ -384,8 +436,8 @@ local function get_relative_spitter_evolution(position)
if town_center.evolution.spitters == nil then
town_center.evolution.spitters = 0.0
end
local evolution_factor = town_center.evolution.spitters
local evo = distance_factor * evolution_factor
local evo = calculate_relative_evolution(town_center.evolution.spitters, distance_factor)
-- get the highest of the relative evolutions of each town
relative_evolution = math.max(relative_evolution, evo)
end
end
@ -399,9 +451,10 @@ local function get_relative_worm_evolution(position)
-- for all of the teams
local teams = ffatable.town_centers
for _, town_center in pairs(teams) do
local market_position = town_center.market.position
local market = town_center.market
if market == nil or not market.valid then return relative_evolution end
-- calculate the distance squared
local d2 = distance_squared(position, market_position)
local d2 = distance_squared(position, market.position)
if d2 < max_d2 then
-- get the distance factor (0.0-1.0)
local distance_factor = 1.0 - d2 / max_d2
@ -412,8 +465,8 @@ local function get_relative_worm_evolution(position)
if town_center.evolution.worms == nil then
town_center.evolution.worms = 0.0
end
local evolution_factor = town_center.evolution.worms
local evo = distance_factor * evolution_factor
local evo = calculate_relative_evolution(town_center.evolution.worms, distance_factor)
-- get the highest of the relative evolutions of each town
relative_evolution = math.max(relative_evolution, evo)
end
end
@ -577,12 +630,13 @@ local function is_worm(entity)
return false
end
-- update evolution based on research completed (weighted)
-- sets the evolution to a value from 0.0 to 1.0 based on research progress
local function update_evolution(force_name, technology)
if technology == nil then
return
end
local ffatable = Table.get_table()
-- update evolution based on research completed (weighted)
local town_center = ffatable.town_centers[force_name]
-- town_center is a reference to a global table
if not town_center then
@ -600,6 +654,7 @@ local function update_evolution(force_name, technology)
local spitter_weight = weight.spitter
local worm_weight = weight.worm
-- update the evolution values (0.0 to 1.0)
-- max weights might be less than 1.0, to allow for evo > 1.0
local b = (biter_weight / max_biter_weight)
local s = (spitter_weight / max_spitter_weight)
local w = (worm_weight / max_worm_weight)
@ -613,9 +668,9 @@ end
local function on_research_finished(event)
local research = event.research
local force_name = research.force.name
local force = research.force
local technology = research.name
update_evolution(force_name, technology)
update_evolution(force.name, technology)
end
local function on_entity_spawned(event)

View File

@ -13,7 +13,7 @@ function Public.reproduce()
if #fishes == 0 then
return
end
if #fishes >= 100 then
if #fishes >= 128 then
return
end
-- pick a random fish
@ -52,15 +52,18 @@ local function on_player_used_capsule(event)
end
-- if using fish on water
if tile.name == 'water' and tile.name == 'water-green' and tile.name == 'deepwater' and tile.name == 'deepwater-green' then
if tile.name == 'water'
or tile.name == 'water-green'
or tile.name == 'water-mud'
or tile.name == 'water-shallow'
or tile.name == 'deepwater'
or tile.name == 'deepwater-green'
then
-- get the count of fish in the water nearby and test if can be repopulated
local fishes = surface.find_entities_filtered({name = 'fish', position = position, radius = 27})
if #fishes < 12 then
surface.create_entity({name = 'water-splash', position = position})
surface.create_entity({name = 'fish', position = position})
surface.play_sound({path = 'utility/achievement_unlocked', position = player.position, volume_modifier = 1})
return
end
surface.create_entity({name = 'water-splash', position = position})
surface.create_entity({name = 'fish', position = position})
surface.play_sound({path = 'utility/achievement_unlocked', position = player.position, volume_modifier = 1})
return
end
-- otherwise return the fish and make no sound
player.insert({name = 'raw-fish', count = 1})

View File

@ -10,17 +10,17 @@ The local inhabitants are indifferent to you at first, so long as you don't buil
by foreign technology. In fact, they get quite aggressive at the scent of it. If you were to hurt any of the natives you will be
brandished a rogue until your untimely death or until you find better digs.
To create a new town or outpost simply place a furnace down in a suitable spot that is not near any other towns or obstructed.
To create a new town simply place a furnace down in a suitable spot that is not near any other towns or obstructed.
The world seems to be limited in size with uninhabitable zones on four sides. News towns can only be built within these
borders and you must leave room for the town's size (radius of 27) when placing a new town.
borders and you must leave room for the town's size (radius of 27) when placing a new town. Each town costs 100 coins.
TIP: It's best to find a spot far from existing towns and pollution, as enemies will become aggressive once you form a town.
Once a town is formed, members may invite other players and teams using a raw fish. To invite another player, drop a fish
on that player (with the Z key). To accept an invite, offer a fish in return to the member. To leave a town, simply drop coal
Once a town is formed, members may invite other players and teams using a coin. To invite another player, drop a coin
on that player (with the Z key). To accept an invite, offer a coin in return to the member. To leave a town, simply drop coal
on the market. As a member of a town, your respawn point will change to that of the town.
To form any alliance with another town, drop a fish on a member or their market. If they agree they can reciprocate with a
fish offering.
To form any alliance with another town, drop a coin on a member or their market. If they agree they can reciprocate with a
coin offering.
The town market is the heart of your town. If it is destroyed, your town is destroyed and you will lose all research. So
protect it well, repair it whenever possible, and if you can afford, increase its health by purchasing upgrades. If your
@ -67,14 +67,14 @@ function Public.show(player)
t = t.add {type = 'table', column_count = 4}
local label2 = t.add {type = 'label', caption = 'Outlander' .. '(' .. #game.forces.player.connected_players .. ')'}
local label2 = t.add {type = 'label', caption = 'Outlander' .. ':' .. #game.forces.player.connected_players .. ' '}
label2.style.font_color = {170, 170, 170}
label2.style.font = 'heading-3'
label2.style.minimal_width = 80
for _, town_center in pairs(ffatable.town_centers) do
local force = town_center.market.force
local label3 = t.add {type = 'label', caption = force.name .. '(' .. #force.connected_players .. ')'}
local label3 = t.add {type = 'label', caption = force.name .. ':' .. #force.connected_players .. ' '}
label3.style.font = 'heading-3'
label3.style.minimal_width = 80
label3.style.font_color = town_center.color

View File

@ -1,6 +1,10 @@
local Public = {}
local Table = require 'modules.scrap_towny_ffa.table'
function Public.reset()
local ffatable = Table.get_table()
if ffatable.testing_mode then return end
for index = 1, table.size(game.forces), 1 do
local force = game.forces[index]
if force ~= nil then
@ -9,7 +13,47 @@ function Public.reset()
end
end
--local Event = require 'utils.event'
--Event.add(defines.events.on_chunk_charted, on_chunk_charted)
local function add_force(id, force_name)
local forces = rendering.get_forces(id)
for _, force in ipairs(forces) do
if force.name == force_name or force == force_name then return end
end
forces[#forces + 1] = force_name
rendering.set_forces(id, forces)
end
local function update_forces(id)
local forces = rendering.get_forces(id)
local new_forces = {}
for _, force in ipairs(forces) do
if force ~= nil and force.valid then
new_forces[#new_forces + 1] = force.name
end
end
rendering.set_forces(id, new_forces)
end
local function on_chunk_charted(event)
local surface = game.surfaces[event.surface_index]
local force = event.force
local area = event.area
local markets = surface.find_entities_filtered({area = area, name = 'market'})
for _, market in pairs(markets) do
local force_name = market.force.name
local ffatable = Table.get_table()
local town_center = ffatable.town_centers[force_name]
-- town caption
local town_caption = town_center.town_caption
update_forces(town_caption)
add_force(town_caption, force.name)
-- health text
local health_text = town_center.health_text
update_forces(health_text)
add_force(health_text, force.name)
end
end
local Event = require 'utils.event'
Event.add(defines.events.on_chunk_charted, on_chunk_charted)
return Public

View File

@ -0,0 +1,19 @@
local Public = {}
function Public.disable_world_map(player)
player.map_view_settings = {
["show-player-names"] = false,
}
player.show_on_map = false
player.game_view_settings.show_minimap = false
end
function Public.enable_world_map(player)
player.map_view_settings = {
["show-player-names"] = true,
}
player.show_on_map = true
player.game_view_settings.show_minimap = true
end
return Public

View File

@ -9,45 +9,75 @@ local upgrade_functions = {
local market = town_center.market
local surface = market.surface
if town_center.max_health > 500000 then
return
return false
end
town_center.health = town_center.health + town_center.max_health
town_center.max_health = town_center.max_health * 2
Town_center.set_market_health(market, 0)
surface.play_sound({path = 'utility/achievement_unlocked', position = player.position, volume_modifier = 1})
return true
end,
-- Upgrade Backpack
[2] = function(town_center, player)
local market = town_center.market
local force = market.force
local surface = market.surface
if force.character_inventory_slots_bonus > 100 then
return
if force.character_inventory_slots_bonus + 5 > 100 then
return false
end
force.character_inventory_slots_bonus = force.character_inventory_slots_bonus + 5
surface.play_sound({path = 'utility/achievement_unlocked', position = player.position, volume_modifier = 1})
return true
end,
-- Upgrade Mining Productivity
[3] = function(town_center, player)
local market = town_center.market
local force = market.force
local surface = market.surface
if town_center.upgrades.mining_prod >= 10 then
return
if town_center.upgrades.mining_prod + 1 > 10 then
return false
end
town_center.upgrades.mining_prod = town_center.upgrades.mining_prod + 1
force.mining_drill_productivity_bonus = force.mining_drill_productivity_bonus + 0.1
surface.play_sound({path = 'utility/achievement_unlocked', position = player.position, volume_modifier = 1})
return true
end,
-- Upgrade Pickaxe Speed
[4] = function(town_center, player)
local market = town_center.market
local force = market.force
local surface = market.surface
if town_center.upgrades.mining_speed + 1 > 10 then
return false
end
town_center.upgrades.mining_speed = town_center.upgrades.mining_speed + 1
force.manual_mining_speed_modifier = force.manual_mining_speed_modifier + 0.1
surface.play_sound({path = 'utility/achievement_unlocked', position = player.position, volume_modifier = 1})
return true
end,
-- Upgrade Crafting Speed
[5] = function(town_center, player)
local market = town_center.market
local force = market.force
local surface = market.surface
if town_center.upgrades.crafting_speed + 1 > 10 then
return false
end
town_center.upgrades.crafting_speed = town_center.upgrades.crafting_speed + 1
force.manual_crafting_speed_modifier = force.manual_crafting_speed_modifier + 0.1
surface.play_sound({path = 'utility/achievement_unlocked', position = player.position, volume_modifier = 1})
return true
end,
-- Laser Turret Slot
[4] = function(town_center, player)
[6] = function(town_center, player)
local market = town_center.market
local surface = market.surface
town_center.upgrades.laser_turret.slots = town_center.upgrades.laser_turret.slots + 1
surface.play_sound({path = 'utility/new_objective', position = player.position, volume_modifier = 1})
return true
end,
-- Set Spawn Point
[5] = function(town_center, player)
[7] = function(town_center, player)
local ffatable = Table.get_table()
local market = town_center.market
local force = market.force
@ -55,6 +85,7 @@ local upgrade_functions = {
local spawn_point = force.get_spawn_position(surface)
ffatable.spawn_point[player.name] = spawn_point
surface.play_sound({path = 'utility/scenario_message', position = player.position, volume_modifier = 1})
return false
end
}
@ -70,31 +101,39 @@ end
local function set_offers(town_center)
local market = town_center.market
local force = market.force
local market_items = {}
-- special offers
local special_offers = {}
if town_center.max_health < 500000 then
special_offers[1] = {{{'coin', town_center.max_health * 0.1}}, 'Upgrade Town Center Health'}
else
special_offers[1] = {{{'coin', 1}}, 'Maximum Health upgrades reached!'}
special_offers[1] = {{}, 'Maximum Health upgrades reached!'}
end
if force.character_inventory_slots_bonus <= 100 then
if force.character_inventory_slots_bonus + 5 <= 100 then
special_offers[2] = {{{'coin', (force.character_inventory_slots_bonus / 5 + 1) * 50}}, 'Upgrade Backpack +5 Slot'}
else
special_offers[2] = {{{'coin', 1}}, 'Maximum Backpack upgrades reached!'}
special_offers[2] = {{}, 'Maximum Backpack upgrades reached!'}
end
if town_center.upgrades.mining_prod < 10 then
if town_center.upgrades.mining_prod + 1 <= 10 then
special_offers[3] = {{{'coin', (town_center.upgrades.mining_prod + 1) * 400}}, 'Upgrade Mining Productivity +10%'}
else
special_offers[3] = {{{'coin', 1}}, 'Maximum Mining upgrades reached!'}
special_offers[3] = {{}, 'Maximum Productivity upgrades reached!'}
end
if town_center.upgrades.mining_speed + 1 <= 10 then
special_offers[4] = {{{'coin', (town_center.upgrades.mining_speed + 1) * 400}}, 'Upgrade Mining Speed +10%'}
else
special_offers[4] = {{}, 'Maximum Mining Speed upgrades reached!'}
end
if town_center.upgrades.crafting_speed + 1 <= 10 then
special_offers[5] = {{{'coin', (town_center.upgrades.crafting_speed + 1) * 400}}, 'Upgrade Crafting Speed +10%'}
else
special_offers[5] = {{}, 'Maximum Crafting Speed upgrades reached!'}
end
local laser_turret = 'Laser Turret Slot [#' .. tostring(town_center.upgrades.laser_turret.slots + 1) .. ']'
special_offers[4] = {{{'coin', 1000 + (town_center.upgrades.laser_turret.slots * 50)}}, laser_turret}
special_offers[6] = {{{'coin', 1000 + (town_center.upgrades.laser_turret.slots * 50)}}, laser_turret}
local spawn_point = 'Set Spawn Point'
special_offers[5] = {{{'coin', 1}}, spawn_point}
local market_items = {}
special_offers[7] = {{}, spawn_point}
for _, v in pairs(special_offers) do
table_insert(market_items, {price = v[1], offer = {type = 'nothing', effect_description = v[2]}})
end
@ -111,6 +150,7 @@ local function set_offers(town_center)
table_insert(market_items, {price = {{'coin', 600}}, offer = {type = 'give-item', item = 'fast-loader', count = 1}})
table_insert(market_items, {price = {{'coin', 900}}, offer = {type = 'give-item', item = 'express-loader', count = 1}})
-- scrap selling
table_insert(market_items, {price = {{'raw-fish', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'wood', 7}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'iron-ore', 7}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'copper-ore', 7}}, offer = {type = 'give-item', item = 'coin', count = 1}})
@ -129,6 +169,7 @@ end
local function refresh_offers(event)
local ffatable = Table.get_table()
local player = game.players[event.player_index]
local market = event.entity or event.market
if not market then
return
@ -143,8 +184,22 @@ local function refresh_offers(event)
if not town_center then
return
end
clear_offers(market)
set_offers(town_center)
if player.force == market.force then
clear_offers(market)
set_offers(town_center)
else
if player.opened ~= nil then
player.opened = nil
player.surface.create_entity(
{
name = 'flying-text',
position = {market.position.x - 1.75, market.position.y},
text = 'Sorry, we are closed.',
color = {r = 1, g = 0.68, b = 0.26}
}
)
end
end
end
local function offer_purchased(event)
@ -156,21 +211,30 @@ local function offer_purchased(event)
if not upgrade_functions[offer_index] then
return
end
local town_center = ffatable.town_centers[market.force.name]
if not town_center then
return
end
upgrade_functions[offer_index](town_center, player)
if count > 1 then
if upgrade_functions[offer_index](town_center, player) then
-- reimburse extra purchased
if count > 1 then
local offers = market.get_market_items()
if offers[offer_index].price ~= nil then
local price = offers[offer_index].price[1].amount
player.insert({name = 'coin', count = price * (count - 1)})
end
end
else
-- reimburse purchase
local offers = market.get_market_items()
local price = offers[offer_index].price[1].amount
player.insert({name = 'coin', count = price * (count - 1)})
if offers[offer_index].price ~= nil then
local price = offers[offer_index].price[1].amount
player.insert({name = 'coin', count = price * (count)})
end
end
end
-- called for all gui events
local function on_gui_opened(event)
local gui_type = event.gui_type
if gui_type ~= defines.gui_type.entity then
@ -180,25 +244,53 @@ local function on_gui_opened(event)
if entity == nil or not entity.valid then
return
end
if entity.type == 'market' then
if entity.name == 'market' then
refresh_offers(event)
end
end
-- called for all market events
local function on_market_item_purchased(event)
offer_purchased(event)
refresh_offers(event)
local market = event.market
if market.name == 'market' then
offer_purchased(event)
refresh_offers(event)
end
end
local function inside(pos, area)
return pos.x >= area.left_top.x and pos.x <= area.right_bottom.x and pos.y >= area.left_top.y and pos.y <= area.right_bottom.y
end
local function on_tick(_)
local ffatable = Table.get_table()
if not ffatable.town_centers then
return
local function equal(pos1, pos2)
return pos1.x == pos2.x and pos1.y == pos2.y
end
local function is_loader(entity)
return entity.name == 'loader' or entity.name == 'fast-loader' or entity.name == 'express-loader'
end
local function is_filtered_inserter(entity)
return entity.name == 'filter-inserter' or entity.name == "stack-filter-inserter"
end
local function max_stack_size(entity)
if is_loader(entity) then return 1 end
if (entity.name == "stack-inserter" or entity.name == "stack-filter-inserter") then
local override = entity.inserter_stack_size_override
if override > 0 then return override end
local capacity = entity.force.stack_inserter_capacity_bonus
return 1 + capacity
else
local override = entity.inserter_stack_size_override
if override > 0 then return override end
local bonus = entity.force.inserter_stack_size_bonus
return 1 + bonus
end
end
local function get_connected_entities(market)
if not market.valid then return {} end
local items = {
'burner-inserter',
'inserter',
@ -211,117 +303,211 @@ local function on_tick(_)
'fast-loader',
'express-loader'
}
for _, town_center in pairs(ffatable.town_centers) do
local market = town_center.market
local items2 = {
'long-handed-inserter',
}
local bb = market.bounding_box
local s = market.surface
local area = {left_top = {bb.left_top.x - 1, bb.left_top.y - 1}, right_bottom = {bb.right_bottom.x + 1, bb.right_bottom.y + 1}}
local entities = s.find_entities_filtered({area = area, name = items})
local area2 = {left_top = {bb.left_top.x - 2, bb.left_top.y - 2}, right_bottom = {bb.right_bottom.x + 2, bb.right_bottom.y + 2}}
local entities2 = s.find_entities_filtered({area = area2, name = items2})
for k,v in pairs(entities2) do
entities[k] = v
end
return entities
end
local function get_inserter_filter(entity)
-- return the first filter
local filter_mode = entity.inserter_filter_mode
if filter_mode == 'whitelist' then
return entity.get_filter(1)
end
return nil
end
local function get_loader_filter(entity, index)
-- return first two filter types
return entity.get_filter(index)
end
local function get_loader_market_position(entity)
-- gets the position of the market relative to the loader
local position = {x=entity.position.x, y=entity.position.y}
local orientation = entity.orientation
local type = entity.loader_type
if (orientation == 0.0 and type == "input") or (orientation == 0.5 and type == "output") then
position.y = position.y - 1.5
end
if (orientation == 0.25 and type == "input") or (orientation == 0.75 and type == "output") then
position.x = position.x + 1.5
end
if (orientation == 0.5 and type == "input") or (orientation == 0.0 and type == "output") then
position.y = position.y + 1.5
end
if (orientation == 0.75 and type == "input") or (orientation == 0.25 and type == "output") then
position.x = position.x - 1.5
end
return position
end
local function output_loader_items(town_center, trade, entity, index)
local item = trade.offer.item
local line = entity.get_transport_line(index)
if line.can_insert_at_back() and town_center.output_buffer[item] > 0 then
local stack = {name = item, count = 1}
town_center.output_buffer[item] = town_center.output_buffer[item] - 1
line.insert_at_back(stack)
end
end
local function output_inserter_items(town_center, trade, entity)
local item = trade.offer.item
local stack_size = max_stack_size(entity)
local count = 0
while town_center.output_buffer[item] > 0 and count < stack_size do
town_center.output_buffer[item] = town_center.output_buffer[item] - 1
count = count + 1
end
if count > 0 then
local stack = {name = item, count = count}
entity.held_stack.set_stack(stack)
end
end
local function trade_scrap_for_coin(town_center, market, trade, stack)
local item = stack.name
local amount = stack.count
-- buffer the input in an item buffer that can be sold for coin
if town_center.input_buffer[item] == nil then
town_center.input_buffer[item] = 0
end
town_center.input_buffer[item] = town_center.input_buffer[item] + amount
--log("input_buffer[" .. item .. "] = " .. town_center.input_buffer[item])
local price = trade.price[1].amount
local count = trade.offer.count
while town_center.input_buffer[item] >= price do
town_center.input_buffer[item] = town_center.input_buffer[item] - price
town_center.coin_balance = town_center.coin_balance + count
end
Town_center.update_coin_balance(market.force)
--log("input_buffer[" .. item .. "] = " .. town_center.input_buffer[item])
end
local function trade_coin_for_items(town_center, market, trade)
local item = trade.offer.item
local count = trade.offer.count
local price = trade.price[1].amount
if town_center.output_buffer[item] == nil then
town_center.output_buffer[item] = 0
end
while town_center.coin_balance - price >= 0 do
if town_center.output_buffer[item] == 0 then
town_center.coin_balance = town_center.coin_balance - price
town_center.output_buffer[item] = town_center.output_buffer[item] + count
else
break
end
end
Town_center.update_coin_balance(market.force)
end
local function handle_loader_output(town_center, market, entity, index)
local line = entity.get_transport_line(index)
-- get loader filters
local filter = get_loader_filter(entity, index)
if filter == nil then return end
if filter == 'coin' then
-- output for coins
while town_center.coin_balance > 0 and line.can_insert_at_back() do
town_center.coin_balance = town_center.coin_balance - 1
local stack = {name = 'coin', count = 1}
line.insert_at_back(stack)
end
Town_center.update_coin_balance(market.force)
else
-- output for matching purchases
local offers = market.get_market_items()
if offers == nil then
set_offers(town_center)
end
local s = market.surface
local force = market.force
-- get the bounding box for the market
local bb = market.bounding_box
local area = {left_top = {bb.left_top.x - 2, bb.left_top.y - 2}, right_bottom = {bb.right_bottom.x + 2, bb.right_bottom.y + 2}}
local entities = s.find_entities_filtered({area = area, name = items})
for _, e in pairs(entities) do
if e.name ~= 'loader' and e.name ~= 'fast-loader' and e.name ~= 'express-loader' then
local ppos = e.pickup_position
local dpos = e.drop_position
-- pulling an item from the market
if inside(ppos, bb) and e.drop_target then
local stack = e.held_stack
local spos = e.held_stack_position
if inside(spos, bb) then
local filter
local filter_mode = e.inserter_filter_mode
if filter_mode ~= nil then
for i = 1, e.filter_slot_count do
if e.get_filter(i) ~= nil then
filter = e.get_filter(i)
break
end
end
end
if (filter_mode == 'whitelist' and filter == 'coin') or (filter_mode == 'blacklist' and filter == nil) or (filter_mode == nil) then
if stack.valid and town_center.coin_balance > 0 then
-- pull coins
stack.set_stack({name = 'coin', count = 1})
town_center.coin_balance = town_center.coin_balance - 1
Town_center.update_coin_balance(force)
end
else
if filter_mode == 'whitelist' and filter ~= nil and stack.valid then
-- purchased and pull items if output buffer is empty
-- buffer the output in a item buffer since the stack might be too small
-- output items are shared among the output
for _, trade in ipairs(offers) do
local type = trade.offer.type
local item = trade.offer.item
local count = trade.offer.count or 1
local cost = trade.price[1].amount
if type == 'give-item' and item == filter then
if town_center.output_buffer[item] == nil then
town_center.output_buffer[item] = 0
end
if town_center.output_buffer[item] == 0 then
-- fill buffer
if town_center.coin_balance >= cost then
town_center.coin_balance = town_center.coin_balance - cost
Town_center.update_coin_balance(force)
town_center.output_buffer[item] = town_center.output_buffer[item] + count
--log("output_buffer[" .. item .. "] = " .. town_center.output_buffer[item])
end
end
if town_center.output_buffer[item] > 0 and not stack.valid_for_read then
-- output the item
local amount = 1
if stack.can_set_stack({name = item, count = amount}) then
town_center.output_buffer[item] = town_center.output_buffer[item] - amount
stack.set_stack({name = item, count = amount})
--log("output_buffer[" .. item .. "] = " .. town_center.output_buffer[item])
end
end
break
end
end
end
end
if offers ~= nil then
for _, trade in ipairs(offers) do
if trade.offer.type == 'give-item' then
local item = trade.price[1].name
if item == 'coin' and trade.offer.item == filter then
trade_coin_for_items(town_center, market, trade)
output_loader_items(town_center, trade, entity, index)
end
end
-- pushing an item to the market (coins or scrap)
if e.pickup_target and inside(dpos, bb) then
local stack = e.held_stack
local spos = e.held_stack_position
if stack.valid_for_read and inside(spos, bb) then
local name = stack.name
local amount = stack.count
if name == 'coin' then
-- push coins
e.remove_item(stack)
town_center.coin_balance = town_center.coin_balance + amount
Town_center.update_coin_balance(force)
else
-- push items to turn into coin
for _, trade in ipairs(offers) do
local type = trade.offer.type
local item = trade.price[1].name
local count = trade.price[1].amount
local cost = trade.offer.count
if type == 'give-item' and name == item and item ~= 'coin' then
e.remove_item(stack)
-- buffer the input in an item buffer that can be sold
if town_center.input_buffer[item] == nil then
town_center.input_buffer[item] = 0
end
town_center.input_buffer[item] = town_center.input_buffer[item] + amount
--log("input_buffer[" .. item .. "] = " .. town_center.input_buffer[item])
if town_center.input_buffer[item] >= count then
town_center.input_buffer[item] = town_center.input_buffer[item] - count
town_center.coin_balance = town_center.coin_balance + cost
Town_center.update_coin_balance(force)
--log("input_buffer[" .. item .. "] = " .. town_center.input_buffer[item])
end
end
end
end
end
end
local function handle_inserter_output(town_center, market, entity)
-- get inserter filter
local filter = get_inserter_filter(entity)
if filter == nil then return end
local amount = max_stack_size(entity)
local stack = {name = 'coin', count = amount}
if filter == 'coin' then
-- output coins
if amount > town_center.coin_balance then amount = town_center.coin_balance end
stack.count = amount
if town_center.coin_balance > 0 then
town_center.coin_balance = town_center.coin_balance - amount
entity.held_stack.set_stack(stack)
end
Town_center.update_coin_balance(market.force)
else
-- for matching coin purchases
local offers = market.get_market_items()
if offers == nil then
set_offers(town_center)
end
if offers ~= nil then
for _, trade in ipairs(offers) do
if trade.offer.type == 'give-item' and trade.offer.item == filter then
local item = trade.price[1].name
if item == 'coin' then
trade_coin_for_items(town_center, market, trade)
output_inserter_items(town_center, trade, entity)
end
end
end
end
end
end
local function handle_loader_input(town_center, market, entity, index)
local line = entity.get_transport_line(index)
-- check for a line item at the back where we can pull
if line.valid then
local length = #line
if length > 1 or (length == 1 and line.can_insert_at_back()) then
local line_item = line[length].name
local stack = {name = line_item, count = 1}
if line_item == 'coin' then
-- insert coins
line.remove_item(stack)
town_center.coin_balance = town_center.coin_balance + stack.count
Town_center.update_coin_balance(market.force)
else
local offers = market.get_market_items()
if offers == nil then
set_offers(town_center)
end
if offers ~= nil then
for _, trade in ipairs(offers) do
if trade.offer.type == 'give-item' then
local item = trade.price[1].name
if item == stack.name and trade.offer.item == 'coin' then
-- trade scrap for coin
line.remove_item(stack)
trade_scrap_for_coin(town_center, market, trade, stack)
end
end
end
@ -331,6 +517,142 @@ local function on_tick(_)
end
end
local function handle_inserter_input(town_center, market, entity)
-- check if stack is coin or resource
local stack = {name = entity.held_stack.name, count = entity.held_stack.count}
if stack.name == 'coin' and stack.count > 0 then
-- insert coins
entity.remove_item(stack)
town_center.coin_balance = town_center.coin_balance + stack.count
Town_center.update_coin_balance(market.force)
else
local offers = market.get_market_items()
if offers == nil then
set_offers(town_center)
end
if offers ~= nil then
for _, trade in ipairs(offers) do
if trade.offer.type == 'give-item' and trade.offer.item == 'coin' then
local item = trade.price[1].name
if item == stack.name and trade.offer.item == 'coin' then
-- trade scrap for coin
entity.remove_item(stack)
trade_scrap_for_coin(town_center, market, trade, stack)
end
end
end
end
end
end
local function handle_market_input(town_center, market, entity)
if is_loader(entity) then
-- handle loader input
-- we don't care about filters
local max_index = entity.get_max_transport_line_index()
for index = 1, max_index, 1 do
handle_loader_input(town_center, market, entity, index)
end
else
-- handle inserter input
-- we don't care about filters
local stack = entity.held_stack
if stack ~= nil then
-- if there is a pickup target
local spos = entity.held_stack_position
local dpos = entity.drop_position
if equal(spos, dpos) then
if stack.valid_for_read and stack.count > 0 then
-- if there is a stack
-- insert an item into the market
handle_inserter_input(town_center, market, entity)
end
end
end
end
end
local function handle_market_output(town_center, market, entity)
if is_loader(entity) then
-- handle loader output
local max_index = entity.get_max_transport_line_index()
for index = 1, max_index, 1 do
if get_loader_filter(entity, index) ~= nil then
handle_loader_output(town_center, market, entity, index)
end
end
else
if is_filtered_inserter(entity) then
-- handle inserter output
if entity.drop_target ~= nil then
-- if the pickup position is inside the market
--log("inside pickup position and there is a drop target")
local stack = entity.held_stack
local spos = entity.held_stack_position
local ppos = entity.pickup_position
if equal(spos, ppos) then
-- if the stack position is inside the market
if stack == nil or stack.count == 0 then
-- if there is space on the stack
-- pull an item from the market
handle_inserter_output(town_center, market, entity, stack)
end
end
end
end
end
end
local function get_entity_mode(market, entity)
local bb = market.bounding_box
if is_loader(entity) then
local mpos = get_loader_market_position(entity)
if inside(mpos, bb) then
return entity.loader_type
else
return 'none'
end
else
local dpos = entity.drop_position
local ppos = entity.pickup_position
if inside(dpos, bb) then
return 'input'
end
if inside(ppos, bb) then
return 'output'
end
return "none"
end
end
local function handle_connected_entity(town_center, market, entity)
local mode = get_entity_mode(market, entity)
if mode == "input" then
handle_market_input(town_center, market, entity)
end
if mode == "output" then
handle_market_output(town_center, market, entity)
end
end
local function on_tick(_)
local ffatable = Table.get_table()
if not ffatable.town_centers then
return
end
for _, town_center in pairs(ffatable.town_centers) do
-- get connected entities on markets
local market = town_center.market
local entities = get_connected_entities(market)
-- handle connected entity
for _, entity in pairs(entities) do
if entity.force == market.force then
handle_connected_entity(town_center, market, entity)
end
end
end
end
local Event = require 'utils.event'
Event.add(defines.events.on_tick, on_tick)
Event.add(defines.events.on_gui_opened, on_gui_opened)

View File

@ -1,5 +1,7 @@
local Public = {}
local math_random = math.random
--local Server = require 'utils.server'
local Table = require 'modules.scrap_towny_ffa.table'
local function create_limbo()
game.create_surface('limbo')
@ -7,6 +9,7 @@ end
local function initialize_nauvis()
local surface = game.surfaces['nauvis']
local map_seed = Table.get('map_seed')
-- this overrides what is in the map_gen_settings.json file
local mgs = surface.map_gen_settings
@ -17,14 +20,14 @@ local function initialize_nauvis()
['copper-ore'] = {frequency = 'none', size = 1, richness = 'normal'},
['iron-ore'] = {frequency = 'none', size = 1, richness = 'normal'},
['uranium-ore'] = {frequency = 'none', size = 1, richness = 'normal'},
['crude-oil'] = {frequency = 'none', size = 1, richness = 'normal'},
['crude-oil'] = {frequency = 'very-low', size = 'very-small', richness = 'normal'},
trees = {frequency = 2, size = 'normal', richness = 'normal'},
['enemy-base'] = {frequency = 8, size = 1, richness = 'normal'}
['enemy-base'] = {frequency = 'very-high', size = 2, richness = 'normal'}
}
mgs.autoplace_settings = {
entity = {
settings = {
['rock-huge'] = {frequency = 3, size = 12, richness = 'very-high'},
['rock-huge'] = {frequency = 2, size = 12, richness = 'very-high'},
['rock-big'] = {frequency = 3, size = 12, richness = 'very-high'},
['sand-rock-big'] = {frequency = 3, size = 12, richness = 1, 'very-high'}
}
@ -53,8 +56,9 @@ local function initialize_nauvis()
mgs.peaceful_mode = false
mgs.starting_area = 'none'
mgs.terrain_segmentation = 8
mgs.width = 2048
mgs.height = 2048
-- terrain size is 64 x 64 chunks, water size is 80 x 80
mgs.width = 2560
mgs.height = 2560
--mgs.starting_points = {
-- {x = 0, y = 0}
--}
@ -67,7 +71,7 @@ local function initialize_nauvis()
--moisture = 0,
-- here we are overriding the aux noise-layer with a fixed value to keep aux consistent across the map
-- it allows to free up the aux noise expression
-- it allows to free up the aux noise expression
-- aux should be not sand, nor red sand
--aux = 0.5,
@ -111,11 +115,11 @@ local function initialize_nauvis()
-- a constant intensity means base distribution will be consistent with regard to distance
['enemy-base-intensity'] = 1,
-- adjust this value to set how many nests spawn per tile
['enemy-base-frequency'] = 0.2,
['enemy-base-frequency'] = 0.4,
-- this will make and average base radius around 12 tiles
['enemy-base-radius'] = 12
}
mgs.seed = math_random(10000, 99999)
mgs.seed = map_seed
surface.map_gen_settings = mgs
surface.peaceful_mode = false
surface.always_day = false
@ -123,6 +127,20 @@ local function initialize_nauvis()
surface.clear(true)
surface.regenerate_entity({'rock-huge', 'rock-big', 'sand-rock-big'})
surface.regenerate_decorative()
-- this will force generate the entire map
--Server.to_discord_embed('ScrapTownyFFA Map Regeneration in Progress')
--surface.request_to_generate_chunks({x=0,y=0},64)
--surface.force_generate_chunk_requests()
--Server.to_discord_embed('Regeneration Complete')
end
local function initialize_limbo()
local surface = game.surfaces['limbo']
surface.generate_with_lab_tiles = true
surface.peaceful_mode = true
surface.always_day = true
surface.freeze_daytime = true
surface.clear(true)
end
function Public.initialize()
@ -167,8 +185,8 @@ function Public.initialize()
game.map_settings.enemy_expansion.max_expansion_cooldown = 3600 -- maximum time before next expansion
-- unit group settings
game.map_settings.unit_group.min_group_gathering_time = 600
game.map_settings.unit_group.max_group_gathering_time = 3600
game.map_settings.unit_group.min_group_gathering_time = 400
game.map_settings.unit_group.max_group_gathering_time = 2400
game.map_settings.unit_group.max_wait_time_for_late_members = 3600
game.map_settings.unit_group.max_group_radius = 30.0
game.map_settings.unit_group.min_group_radius = 5.0
@ -192,6 +210,7 @@ function Public.initialize()
--game.map_settings.steering.moving.force_unit_fuzzy_goto_behavior = false
create_limbo()
initialize_limbo()
initialize_nauvis()
end

View File

@ -47,15 +47,21 @@ local pollution_index = {
function Public.market_scent()
local ffatable = Table.get_table()
if ffatable.testing_mode then
return
end
local town_centers = ffatable.town_centers
if town_centers == nil then
return
end
for _, town_center in pairs(town_centers) do
local market = town_center.market
local pollution = pollution_index['market']
local amount = math_random(pollution.min, pollution.max)
market.surface.pollute(market.position, amount)
if market ~= nil and market.valid then
local pollution = pollution_index['market']
local amount = math_random(pollution.min, pollution.max)
local evolution = town_center.evolution.biters
market.surface.pollute(market.position, amount + evolution * 500)
end
end
end

View File

@ -4,7 +4,6 @@ local table_insert = table.insert
local table_shuffle = table.shuffle_table
local Table = require 'modules.scrap_towny_ffa.table'
local Evolution = require 'modules.scrap_towny_ffa.evolution'
local valid_entities = {
['rock-big'] = true,
@ -13,21 +12,21 @@ local valid_entities = {
}
local size_raffle = {
{'giant', 128, 256},
{'huge', 64, 128},
{'big', 32, 64},
{'small', 16, 32},
{'tiny', 8, 16}
{'giant', 1024, 2048},
{'huge', 512, 1024},
{'big', 256, 510},
{'small', 126, 256},
{'tiny', 64, 128}
}
local function get_chances()
local chances = {}
table_insert(chances, {'iron-ore', 25})
table_insert(chances, {'iron-ore', 24})
table_insert(chances, {'copper-ore', 18})
table_insert(chances, {'mixed', 15})
table_insert(chances, {'mixed', 12})
table_insert(chances, {'coal', 14})
table_insert(chances, {'stone', 8})
table_insert(chances, {'uranium-ore', 3})
table_insert(chances, {'stone', 12})
table_insert(chances, {'uranium-ore', 8})
return chances
end
@ -42,42 +41,52 @@ local function set_raffle()
ffatable.rocks_yield_ore_veins.mixed_ores = {'iron-ore', 'copper-ore', 'stone', 'coal'}
end
local function get_amount(position)
local base = 256
local relative_evolution = Evolution.get_evolution(position)
local tier = 4 + math_floor(relative_evolution * 16)
return (math_random(1, base) + math_random(1, 2 ^ tier))
local function get_amount()
local base = 256 + 2 ^ 8
local max = 2 ^ 12
return math_random(math_random(256, base), math_random(base, max))
end
local function draw_chain(surface, count, ore, ore_entities, ore_positions)
local ffatable = Table.get_table()
local vectors = {{0, -1}, {-1, 0}, {1, 0}, {0, 1}}
local vectors = {{0, -0.75}, {-0.75, 0}, {0.75, 0}, {0, 0.75}}
local r = math_random(1, #ore_entities)
local position = {x = ore_entities[r].position.x, y = ore_entities[r].position.y}
local position = {x = ore_entities[r].ore.position.x, y = ore_entities[r].ore.position.y}
for _ = 1, count, 1 do
table_shuffle(vectors)
for i = 1, 4, 1 do
local p = {x = position.x + vectors[i][1], y = position.y + vectors[i][2]}
-- dispersion will make patches more round
local dx = (math_random(0, 100) - 50) / 100
local dy = (math_random(0, 100) - 50) / 100
local dp = {x = p.x + dx, y = p.y + dy}
local name = ore
if ore == 'mixed' then
name = ffatable.rocks_yield_ore_veins.mixed_ores[math_random(1, #ffatable.rocks_yield_ore_veins.mixed_ores)]
end
if surface.can_place_entity({name = name, position = p, amount = 1}) then
if not ore_positions[p.x .. '_' .. p.y] then
position.x = p.x
position.y = p.y
ore_positions[p.x .. '_' .. p.y] = true
ore_entities[#ore_entities + 1] = {name = name, position = p, amount = get_amount(p)}
break
if surface.can_place_entity({name = name, position = p, force = 'neutral'}) then
if math_random(1, 2) == 1 then
if not ore_positions[p.x .. '_' .. p.y] then
position.x = p.x
position.y = p.y
ore_positions[p.x .. '_' .. p.y] = true
ore_entities[#ore_entities + 1] = {ore = {name = name, position = dp}, amount = get_amount()}
break
end
end
else
if surface.can_fast_replace({name = name, position = p}) then
if math_random(1, 2) == 1 then
-- existing ore of same name
if surface.can_fast_replace({name = name, position = p, force = 'neutral'}) then
local amount = get_amount()
local deposit = surface.find_entity(name, p)
if deposit ~= nil then
amount = amount + deposit
if not ore_positions[p.x .. '_' .. p.y] then
position.x = p.x
position.y = p.y
ore_positions[p.x .. '_' .. p.y] = true
ore_entities[#ore_entities + 1] = {name = name, position = p, amount = get_amount(p), fast_replace = true}
ore_entities[#ore_entities + 1] = {ore = {name = name, position = dp}, amount = amount, fast_replace = true}
break
end
end
@ -129,13 +138,15 @@ local function ore_vein(event)
end
local position = event.entity.position
local ore_entities = {{name = ore, position = {x = position.x, y = position.y}, amount = get_amount(position)}}
local ore_entities = {{ore = {name = ore, position = {x = position.x, y = position.y}}, amount = get_amount()}}
if ore == 'mixed' then
ore_entities = {
{
name = ffatable.rocks_yield_ore_veins.mixed_ores[math_random(1, #ffatable.rocks_yield_ore_veins.mixed_ores)],
position = {x = position.x, y = position.y},
amount = get_amount(position)
ore = {
name = ffatable.rocks_yield_ore_veins.mixed_ores[math_random(1, #ffatable.rocks_yield_ore_veins.mixed_ores)],
position = {x = position.x, y = position.y}
},
amount = get_amount()
}
}
end
@ -148,32 +159,43 @@ local function ore_vein(event)
if count < c then
c = count
end
local placed_ore_count = #ore_entities
draw_chain(surface, c, ore, ore_entities, ore_positions)
count = count - (#ore_entities - placed_ore_count)
if count <= 0 then
break
end
end
for _, e in pairs(ore_entities) do
surface.create_entity(e)
-- place the ore
for _, ore_entity in pairs(ore_entities) do
if ore_entity.fast_replace then
local e = surface.find_entity(ore_entity.ore.name, ore_entity.ore.position)
e.amount = ore_entity.amount
else
local e = surface.create_entity(ore_entity.ore)
e.amount = ore_entity.amount
end
end
end
local function on_player_mined_entity(event)
local ffatable = Table.get_table()
local rocks_yield_ore_veins = Table.get('rocks_yield_ore_veins')
if not rocks_yield_ore_veins then
return
end
local player = game.players[event.player_index]
if player.force.technologies['steel-processing'].researched == false then
return
end
if not event.entity.valid then
return
end
if not valid_entities[event.entity.name] then
return
end
if math_random(1, ffatable.rocks_yield_ore_veins.chance) ~= 1 then
if math_random(1, rocks_yield_ore_veins.chance) ~= 1 then
return
end
ore_vein(event)
@ -184,7 +206,7 @@ local function on_init()
ffatable.rocks_yield_ore_veins = {}
ffatable.rocks_yield_ore_veins.raffle = {}
ffatable.rocks_yield_ore_veins.mixed_ores = {}
ffatable.rocks_yield_ore_veins.chance = 4
ffatable.rocks_yield_ore_veins.chance = 10
set_raffle()
end

View File

@ -1,5 +1,3 @@
local table_size = table.size
local Table = require 'modules.scrap_towny_ffa.table'
-- called whenever a player places an item
@ -16,7 +14,7 @@ local function on_built_entity(event)
local force = player.force
local town_center = ffatable.town_centers[force.name]
local surface = entity.surface
if force == game.forces['player'] or force == game.forces['rogue'] or town_center == nil then
if force.index == game.forces['player'].index or force.index == game.forces['rogue'].index or town_center == nil then
surface.create_entity(
{
name = 'flying-text',
@ -31,7 +29,8 @@ local function on_built_entity(event)
end
local slots = town_center.upgrades.laser_turret.slots
local locations = town_center.upgrades.laser_turret.locations
if table_size(locations) >= slots then
if locations >= slots then
surface.create_entity(
{
name = 'flying-text',
@ -44,12 +43,24 @@ local function on_built_entity(event)
entity.destroy()
return
end
local position = entity.position
local key = tostring('{' .. position.x .. ',' .. position.y .. '}')
locations[key] = true
local key = script.register_on_entity_destroyed(entity)
if (ffatable.laser_turrets == nil) then
ffatable.laser_turrets = {}
end
ffatable.laser_turrets[key] = force.index
locations = locations + 1
town_center.upgrades.laser_turret.locations = locations
surface.create_entity(
{
name = 'flying-text',
position = entity.position,
text = 'Using ' .. locations .. '/' .. slots .. ' slots',
color = {r = 1.0, g = 1.0, b = 1.0}
}
)
end
-- called whenever a player places an item
-- called whenever a robot places an item
local function on_robot_built_entity(event)
local ffatable = Table.get_table()
local entity = event.created_entity
@ -63,7 +74,7 @@ local function on_robot_built_entity(event)
local force = robot.force
local town_center = ffatable.town_centers[force.name]
local surface = entity.surface
if force == game.forces['player'] or force == game.forces['rogue'] or town_center == nil then
if force.index == game.forces['player'].index or force.index == game.forces['rogue'].index or town_center == nil then
surface.create_entity(
{
name = 'flying-text',
@ -78,7 +89,7 @@ local function on_robot_built_entity(event)
end
local slots = town_center.upgrades.laser_turret.slots
local locations = town_center.upgrades.laser_turret.locations
if table_size(locations) >= slots then
if locations >= slots then
surface.create_entity(
{
name = 'flying-text',
@ -91,31 +102,51 @@ local function on_robot_built_entity(event)
entity.destroy()
return
end
local position = entity.position
local key = tostring('{' .. position.x .. ',' .. position.y .. '}')
locations[key] = true
local key = script.register_on_entity_destroyed(entity)
if (ffatable.laser_turrets == nil) then
ffatable.laser_turrets = {}
end
ffatable.laser_turrets[key] = force.index
locations = locations + 1
town_center.upgrades.laser_turret.locations = locations
surface.create_entity(
{
name = 'flying-text',
position = entity.position,
text = 'Using ' .. locations .. '/' .. slots .. ' slots',
color = {r = 1.0, g = 1.0, b = 1.0}
}
)
end
-- called whenever a player mines an entity but before it is removed from the map
-- will have the contents of the drops
local function on_player_mined_entity(event)
-- called whenever a laser-turret is removed from the map
local function on_entity_destroyed(event)
local key = event.registration_number
local ffatable = Table.get_table()
local player = game.players[event.player_index]
local force = player.force
local entity = event.entity
if entity.name == 'laser-turret' then
local town_center = ffatable.town_centers[force.name]
if force == game.forces['player'] or force == game.forces['rogue'] or town_center == nil then
return
if (ffatable.laser_turrets == nil) then
return
end
if (ffatable.laser_turrets[key] ~= nil) then
local index = ffatable.laser_turrets[key]
local force = game.forces[index]
if force ~= nil then
local town_center = ffatable.town_centers[force.name]
if town_center ~= nil then
if force.index == game.forces['player'].index or force.index == game.forces['rogue'].index or town_center == nil then
return
end
local locations = town_center.upgrades.laser_turret.locations
locations = locations - 1
if (locations < 0) then
locations = 0
end
town_center.upgrades.laser_turret.locations = locations
end
end
local locations = town_center.upgrades.laser_turret.locations
local position = entity.position
local key = tostring('{' .. position.x .. ',' .. position.y .. '}')
locations[key] = nil
end
end
local Event = require 'utils.event'
Event.add(defines.events.on_built_entity, on_built_entity)
Event.add(defines.events.on_robot_built_entity, on_robot_built_entity)
Event.add(defines.events.on_player_mined_entity, on_player_mined_entity)
Event.add(defines.events.on_entity_destroyed, on_entity_destroyed)

View File

@ -0,0 +1,268 @@
local table_insert = table.insert
local Table = require 'modules.scrap_towny_ffa.table'
local upgrade_functions = {
-- Upgrade Backpack
[1] = function(player)
local ffatable = Table.get_table()
local surface = player.surface
if player.character.character_inventory_slots_bonus + 5 > 100 then
return false
end
player.character.character_inventory_slots_bonus = player.character.character_inventory_slots_bonus + 5
if not ffatable.buffs[player.index] then
ffatable.buffs[player.index] = {}
end
if not ffatable.buffs[player.index].character_inventory_slots_bonus then
ffatable.buffs[player.index].character_inventory_slots_bonus = 0
end
ffatable.buffs[player.index].character_inventory_slots_bonus = player.character.character_inventory_slots_bonus
surface.play_sound({path = 'utility/achievement_unlocked', position = player.position, volume_modifier = 1})
return true
end,
-- Upgrade Pickaxe Speed
[2] = function(player)
local ffatable = Table.get_table()
local surface = player.surface
if player.character.character_mining_speed_modifier + 0.1 > 1 then
return false
end
player.character.character_mining_speed_modifier = player.character.character_mining_speed_modifier + 0.1
if not ffatable.buffs[player.index] then
ffatable.buffs[player.index] = {}
end
if not ffatable.buffs[player.index].character_mining_speed_modifier then
ffatable.buffs[player.index].character_mining_speed_modifier = 0
end
ffatable.buffs[player.index].character_mining_speed_modifier = player.character.character_mining_speed_modifier
surface.play_sound({path = 'utility/achievement_unlocked', position = player.position, volume_modifier = 1})
return true
end,
-- Upgrade Crafting Speed
[3] = function(player)
local ffatable = Table.get_table()
local surface = player.surface
if player.character.character_crafting_speed_modifier + 0.1 > 1 then
return false
end
player.character.character_crafting_speed_modifier = player.character.character_crafting_speed_modifier + 0.1
if not ffatable.buffs[player.index] then
ffatable.buffs[player.index] = {}
end
if not ffatable.buffs[player.index].character_crafting_speed_modifier then
ffatable.buffs[player.index].character_crafting_speed_modifier = 0
end
ffatable.buffs[player.index].character_crafting_speed_modifier = player.character.character_crafting_speed_modifier
surface.play_sound({path = 'utility/achievement_unlocked', position = player.position, volume_modifier = 1})
return true
end,
-- Set Spawn Point
[4] = function(player)
local ffatable = Table.get_table()
local surface = player.surface
local position = player.position
position = surface.find_non_colliding_position('character', position, 0, 0.25)
if position ~= nil and player ~= nil then
ffatable.spawn_point[player.name] = {x = position.x, y = position.y}
surface.play_sound({path = 'utility/scenario_message', position = player.position, volume_modifier = 1})
else
surface.create_entity(
{
name = 'flying-text',
position = position,
text = 'Could not find open space for spawnpoint!',
color = {r = 0.77, g = 0.0, b = 0.0}
}
)
end
return false
end
}
local function clear_offers(market)
for _ = 1, 256, 1 do
local a = market.remove_market_item(1)
if a == false then
return
end
end
end
local function set_offers(market, player)
local market_items = {}
-- special offers are only for outlanders and rogues
local special_offers = {}
local force = player.force
if force.index == game.forces['player'].index or force.index == game.forces['rogue'].index then
if player.character.character_inventory_slots_bonus + 5 <= 100 then
special_offers[1] = {{{'coin', (player.character.character_inventory_slots_bonus / 5 + 1) * 50}}, 'Upgrade Backpack +5 Slot'}
else
special_offers[1] = {{}, 'Maximum Backpack upgrades reached!'}
end
if player.character.character_mining_speed_modifier + 0.1 <= 1 then
special_offers[2] = {{{'coin', (player.character.character_mining_speed_modifier * 10 + 1) * 400}}, 'Upgrade Mining Speed +10%'}
else
special_offers[2] = {{}, 'Maximum Mining Speed upgrades reached!'}
end
if player.character.character_crafting_speed_modifier + 0.1 <= 1 then
special_offers[3] = {{{'coin', (player.character.character_crafting_speed_modifier * 10 + 1) * 400}}, 'Upgrade Crafting Speed +10%'}
else
special_offers[3] = {{}, 'Maximum Crafting Speed upgrades reached!'}
end
local spawn_point = 'Set Spawn Point'
special_offers[4] = {{}, spawn_point}
else
local spawn_point = 'Set Spawn Point'
special_offers[1] = {{}, spawn_point}
end
for _, v in pairs(special_offers) do
table_insert(market_items, {price = v[1], offer = {type = 'nothing', effect_description = v[2]}})
end
-- coin purchases
table_insert(market_items, {price = {{'coin', 1}}, offer = {type = 'give-item', item = 'raw-fish', count = 1}})
table_insert(market_items, {price = {{'coin', 4}}, offer = {type = 'give-item', item = 'firearm-magazine', count = 5}})
table_insert(market_items, {price = {{'coin', 10}}, offer = {type = 'give-item', item = 'grenade', count = 6}})
table_insert(market_items, {price = {{'coin', 40}}, offer = {type = 'give-item', item = 'piercing-rounds-magazine', count = 10}})
table_insert(market_items, {price = {{'coin', 75}}, offer = {type = 'give-item', item = 'heavy-armor', count = 1}})
table_insert(market_items, {price = {{'coin', 150}}, offer = {type = 'give-item', item = 'modular-armor', count = 1}})
-- scrap selling
table_insert(market_items, {price = {{'raw-fish', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'wood', 7}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'copper-cable', 12}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'copper-plate', 5}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'iron-stick', 12}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'iron-gear-wheel', 3}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'iron-plate', 5}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'steel-plate', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'empty-barrel', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'crude-oil-barrel', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'heavy-oil-barrel', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'light-oil-barrel', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'lubricant-barrel', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'petroleum-gas-barrel', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'sulfuric-acid-barrel', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'water-barrel', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'electronic-circuit', 5}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'advanced-circuit', 1}}, offer = {type = 'give-item', item = 'coin', count = 5}})
table_insert(market_items, {price = {{'processing-unit', 1}}, offer = {type = 'give-item', item = 'coin', count = 10}})
table_insert(market_items, {price = {{'plastic-bar', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'green-wire', 5}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'red-wire', 5}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'battery', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'heat-pipe', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'pipe', 8}}, offer = {type = 'give-item', item = 'coin', count = 1}})
table_insert(market_items, {price = {{'pipe-to-ground', 1}}, offer = {type = 'give-item', item = 'coin', count = 1}})
for _, item in pairs(market_items) do
market.add_market_item(item)
end
end
local function refresh_offers(event)
local player_index = event.player_index
if player_index == nil then
return
end
local player = game.players[event.player_index]
local ffatable = Table.get_table()
local market = event.entity or event.market
if not market then
return
end
if not market.valid then
return
end
if market.name ~= 'crash-site-spaceship-market' then
return
end
local position = market.position
local spaceship = ffatable.spaceships[position.x][position.y]
if not spaceship then
return
end
clear_offers(market)
set_offers(market, player)
end
local function offer_purchased(event)
local ffatable = Table.get_table()
local player = game.players[event.player_index]
local market = event.market
local offer_index = event.offer_index
local count = event.count
if not upgrade_functions[offer_index] then
return
end
local spaceship = ffatable.spaceships[market.position.x][market.position.y]
if not spaceship then
return
end
if upgrade_functions[offer_index](player) then
-- reimburse extra purchased
if count > 1 then
local offers = market.get_market_items()
if offers[offer_index].price ~= nil then
local price = offers[offer_index].price[1].amount
player.insert({name = 'coin', count = price * (count - 1)})
end
end
else
-- reimburse purchase
local offers = market.get_market_items()
if offers[offer_index].price ~= nil then
local price = offers[offer_index].price[1].amount
player.insert({name = 'coin', count = price * (count)})
end
end
end
local function on_gui_opened(event)
local gui_type = event.gui_type
if gui_type == defines.gui_type.entity then
local entity = event.entity
if entity ~= nil or entity.valid then
-- crash-site-spaceship
if entity.name == 'crash-site-spaceship-market' then
refresh_offers(event)
end
end
end
end
local function on_market_item_purchased(event)
local market = event.market
if market.name == 'crash-site-spaceship-market' then
offer_purchased(event)
refresh_offers(event)
end
end
local function kill_spaceship(entity)
local ffatable = Table.get_table()
local spaceship = ffatable.spaceships[entity.position.x][entity.position.y]
if spaceship ~= nil then
ffatable.spaceships[entity.position.x][entity.position.y] = nil
end
end
local function on_entity_died(event)
local entity = event.entity
if entity.name == 'crash-site-spaceship-market' then
kill_spaceship(entity)
end
end
local on_init = function()
local ffatable = Table.get_table()
ffatable.spaceships = {}
end
local Event = require 'utils.event'
Event.on_init(on_init)
Event.add(defines.events.on_gui_opened, on_gui_opened)
Event.add(defines.events.on_market_item_purchased, on_market_item_purchased)
Event.add(defines.events.on_entity_died, on_entity_died)

View File

@ -1,4 +1,3 @@
--luacheck: ignore
local Public = {}
local table_size = table.size
@ -69,11 +68,19 @@ end
-- is the position empty
local function is_empty(position, surface)
local entity_radius = 1
local tile_radius = 1
local entities = surface.count_entities_filtered({position = position, radius = entity_radius, collision_mask = {'object-layer', 'player-layer'}})
--log("entities = " .. entities)
if entities > 0 then
local chunk_position = {}
chunk_position.x = math_floor(position.x / 32)
chunk_position.y = math_floor(position.y / 32)
if not surface.is_chunk_generated(chunk_position) then
-- force load the chunk
surface.request_to_generate_chunks(position, 0)
surface.force_generate_chunk_requests()
end
local entity_radius = 3
local tile_radius = 2
local entities = surface.find_entities_filtered({position = position, radius = entity_radius})
--log("entities = " .. #entities)
if #entities > 0 then
return false
end
local tiles = surface.count_tiles_filtered({position = position, radius = tile_radius, collision_mask = 'water-tile'})
@ -87,14 +94,17 @@ local function is_empty(position, surface)
end
-- finds a valid spawn point that is not near a town and not in a polluted area
local function find_valid_spawn_point(surface)
local function find_valid_spawn_point(force_name, surface)
local ffatable = Table.get_table()
-- check center of map first if valid
local position = {x = 0, y = 0}
--log("testing {" .. position.x .. "," .. position.y .. "}")
force_load(position, surface, 1)
-- is the point near any buildings
if in_use(position) == false then
if Building.near_town(position, surface, spawn_point_town_buffer) == false then
if Building.near_another_town(force_name, position, surface, spawn_point_town_buffer) == false then
-- force load the position
if is_empty(position, surface) == true then
--log("found valid spawn point at {" .. position.x .. "," .. position.y .. "}")
@ -133,7 +143,7 @@ local function find_valid_spawn_point(surface)
force_load(position, surface, 1)
if in_use(target) == false then
if has_pollution(target, surface) == false then
if Building.near_town(target, surface, spawn_point_town_buffer) == false then
if Building.near_another_town(force_name, target, surface, spawn_point_town_buffer) == false then
if is_empty(target, surface) == true then
--log("found valid spawn point at {" .. target.x .. "," .. target.y .. "}")
position = target
@ -145,7 +155,6 @@ local function find_valid_spawn_point(surface)
end
-- near a town, increment the radius and select another angle
radius = radius + math_random(1, spawn_point_incremental_distance)
angle = math_random(0, 360)
tries = tries + 1
end
return {x = 0, y = 0}
@ -154,7 +163,14 @@ end
function Public.get_new_spawn_point(player, surface)
local ffatable = Table.get_table()
-- get a new spawn point
local position = find_valid_spawn_point(surface)
local position = {0, 0}
if player ~= nil then
local force = player.force
if force ~= nil then
local force_name = force.name
position = find_valid_spawn_point(force_name, surface)
end
end
-- should never be invalid or blocked
ffatable.spawn_point[player.name] = position
--log("player " .. player.name .. " assigned new spawn point at {" .. position.x .. "," .. position.y .. "}")
@ -164,21 +180,16 @@ end
-- gets a new or existing spawn point for the player
function Public.get_spawn_point(player, surface)
local ffatable = Table.get_table()
if ffatable.spawn_point == nil then
ffatable.spawn_point = {}
end
-- test the existing spawn point
local position = ffatable.spawn_point[player.name]
if position ~= nil then
-- if there is a spawn point and less than three strikes
if position ~= nil and ffatable.strikes[player.name] < 3 then
-- check that the spawn point is not blocked
if surface.can_place_entity({name = 'character', position = position}) then
--log("player " .. player.name .. "using existing spawn point at {" .. position.x .. "," .. position.y .. "}")
return position
else
position = surface.find_non_colliding_position('character', position, 16, 0.25)
if (position ~= nil) then
return position
end
position = surface.find_non_colliding_position('character', position, 0, 0.25)
return position
end
end
-- otherwise get a new spawn point

View File

@ -2,7 +2,9 @@ local Public = {}
-- one table to rule them all!
local Global = require 'utils.global'
local ffatable = {}
local ffatable = {
map_seed = 3747269588
}
Global.register(
ffatable,
function(tbl)
@ -20,6 +22,25 @@ function Public.get_table()
return ffatable
end
function Public.get(key)
if key then
return ffatable[key]
else
return ffatable
end
end
function Public.set(key, value)
if key and (value or value == false) then
ffatable[key] = value
return ffatable[key]
elseif key then
return ffatable[key]
else
return ffatable
end
end
local on_init = function()
Public.reset_table()
end

View File

@ -1,10 +1,12 @@
--luacheck: ignore
local Public = {}
local math_random = math.random
local table_size = table.size
local string_match = string.match
local string_lower = string.lower
local Server = require 'utils.server'
local Map = require 'modules.scrap_towny_ffa.map'
local Table = require 'modules.scrap_towny_ffa.table'
local outlander_color = {150, 150, 150}
@ -13,70 +15,118 @@ local rogue_color = {150, 150, 150}
local rogue_chat_color = {170, 170, 170}
local item_drop_radius = 1.65
local destroy_wall_types = {
['gate'] = true,
['wall'] = true
}
local destroy_military_types = {
['ammo-turret'] = true,
['artillery-turret'] = true,
['artillery-wagon'] = true,
['electric-turret'] = true,
['fluid-turret'] = true,
['lab'] = true,
['land-mine'] = true,
['logistic-robot'] = true,
['radar'] = true,
['reactor'] = true,
['roboport'] = true,
['rocket-silo'] = true
}
local destroy_robot_types = {
['combat-robot'] = true,
['construction-robot'] = true,
['logistic-robot'] = true
}
local function min_slots(slots)
local min = 0
for i = 1, 3, 1 do
if slots[i] > min then
min = slots[i]
end
end
return min
end
local function can_force_accept_member(force)
local ffatable = Table.get_table()
local town_centers = ffatable.town_centers
local size_of_town_centers = ffatable.size_of_town_centers
local member_limit = 0
if size_of_town_centers <= 1 then
return true
if ffatable.member_limit == nil then
ffatable.member_limit = 1
end
for _, town in pairs(town_centers) do
member_limit = member_limit + table_size(town.market.force.connected_players)
-- get the members of each force name into a table
local slots = {0, 0, 0}
for _, town_center in pairs(town_centers) do
local players = table_size(town_center.market.force.players)
-- get min value for all slots
local min = min_slots(slots)
-- if our value greater than min of all three replace that slot
if players > min then
for i = 1, 3, 1 do
if slots[i] == min then
slots[i] = players
break
end
end
end
end
member_limit = math.floor(member_limit / size_of_town_centers) + 4
-- get the min of all slots
local member_limit = min_slots(slots) + 1
ffatable.member_limit = member_limit
if #force.connected_players >= member_limit then
game.print('>> Town ' .. force.name .. ' has too many settlers! Current limit (' .. member_limit .. ')', {255, 255, 0})
return
end
return true
end
local function is_towny(force)
if force == game.forces['player'] or force == game.forces['rogue'] then
return false
end
return true
end
function Public.has_key(player)
if not (player and player.valid) then
return
local function is_towny(force)
if force.index == game.forces['player'].index or force.index == game.forces['rogue'].index then
return false
end
local ffatable = Table.get_table()
return ffatable.key[player.index]
return true
end
function Public.give_key(player)
if not (player and player.valid) then
return
end
function Public.has_key(player_index)
local ffatable = Table.get_table()
ffatable.key[player.index] = true
if ffatable.key == nil then
ffatable.key = {}
end
if ffatable.key[player_index] ~= nil then
return ffatable.key[player_index]
end
return false
end
function Public.remove_key(player)
if not (player and player.valid) then
return
end
function Public.give_key(player_index)
local ffatable = Table.get_table()
if ffatable.key == nil then
ffatable.key = {}
end
ffatable.key[player_index] = true
end
ffatable.key[player.index] = false
function Public.remove_key(player_index)
local ffatable = Table.get_table()
if ffatable.key == nil then
ffatable.key = {}
end
ffatable.key[player_index] = false
end
function Public.set_player_color(player)
local ffatable = Table.get_table()
if player.force == game.forces['player'] then
if player.force.index == game.forces['player'].index then
player.color = outlander_color
player.chat_color = outlander_chat_color
return
end
if player.force == game.forces['rogue'] then
if player.force.index == game.forces['rogue'].index then
player.color = rogue_color
player.chat_color = rogue_chat_color
return
@ -114,51 +164,62 @@ function Public.set_all_player_colors()
end
end
local function reset_player(player)
if player.character ~= nil then
local character = player.character
character.character_crafting_speed_modifier = 0.0
character.character_mining_speed_modifier = 0.0
character.character_inventory_slots_bonus = 0
end
end
function Public.add_player_to_town(player, town_center)
local ffatable = Table.get_table()
local market = town_center.market
local force = market.force
local surface = market.surface
reset_player(player)
player.force = market.force
Public.remove_key(player)
Public.remove_key(player.index)
ffatable.spawn_point[player.name] = force.get_spawn_position(surface)
game.permissions.get_group(force.name).add_player(player)
player.tag = ''
Map.enable_world_map(player)
Public.set_player_color(player)
end
function Public.give_outlander_items(player)
player.insert({name = 'stone-furnace', count = 1})
-- given to player upon respawn
function Public.give_player_items(player)
player.clear_items_inside()
player.insert({name = 'raw-fish', count = 3})
player.insert({name = 'coal', count = 3})
end
function Public.set_player_to_outlander(player)
local ffatable = Table.get_table()
player.force = game.forces.player
if ffatable.spawn_point[player.name] then
ffatable.spawn_point[player.name] = nil
if player == nil then
return
end
player.force = game.forces.player
if game.permissions.get_group('outlander') == nil then
game.permissions.create_group('outlander')
end
game.permissions.get_group('outlander').add_player(player)
player.tag = '[Outlander]'
Map.disable_world_map(player)
Public.set_player_color(player)
Public.give_key(player)
end
local function set_player_to_rogue(player)
local ffatable = Table.get_table()
player.force = game.forces['rogue']
if ffatable.spawn_point[player.name] then
ffatable.spawn_point[player.name] = nil
if player == nil then
return
end
player.force = 'rogue'
if game.permissions.get_group('rogue') == nil then
game.permissions.create_group('rogue')
end
game.permissions.get_group('rogue').add_player(player)
player.tag = '[Rogue]'
Map.disable_world_map(player)
Public.set_player_color(player)
end
@ -167,6 +228,11 @@ local function ally_outlander(player, target)
local requesting_force = player.force
local target_force = target.force
-- don't handle if towns not yet enabled
if not ffatable.towns_enabled then
player.print('You must wait for more players to join!', {255, 255, 0})
return false
end
-- don't handle request if target is not a town
if not is_towny(requesting_force) and not is_towny(target_force) then
return false
@ -181,7 +247,7 @@ local function ally_outlander(player, target)
if not is_towny(requesting_force) and is_towny(target_force) then
ffatable.requests[player.index] = target_force.name
local target_player = false
local target_player
if target.type == 'character' then
target_player = target.player
else
@ -333,7 +399,10 @@ local function declare_war(player, item)
requesting_force.set_friend(target_force, false)
target_force.set_friend(requesting_force, false)
game.print('>> ' .. player.name .. ' has dropped the coal! Town ' .. target_force.name .. ' and ' .. requesting_force.name .. ' are now at war!', {255, 255, 0})
game.print(
'>> ' .. player.name .. ' has dropped the coal! Town ' .. target_force.name .. ' and ' .. requesting_force.name .. ' are now at war!',
{255, 255, 0}
)
end
local function delete_chart_tag_for_all_forces(market)
@ -351,13 +420,15 @@ local function delete_chart_tag_for_all_forces(market)
end
end
function Public.add_chart_tag(force, market)
function Public.add_chart_tag(town_center)
local market = town_center.market
local force = market.force
local position = market.position
local tags = force.find_chart_tags(market.surface, {{position.x - 0.1, position.y - 0.1}, {position.x + 0.1, position.y + 0.1}})
if tags[1] then
return
end
force.add_chart_tag(market.surface, {icon = {type = 'item', name = 'stone-furnace'}, position = position, text = market.force.name .. "'s Town"})
force.add_chart_tag(market.surface, {icon = {type = 'item', name = 'stone-furnace'}, position = position, text = town_center.town_name})
end
function Public.update_town_chart_tags()
@ -366,9 +437,11 @@ function Public.update_town_chart_tags()
local forces = game.forces
for _, town_center in pairs(town_centers) do
local market = town_center.market
for _, force in pairs(forces) do
if force.is_chunk_visible(market.surface, town_center.chunk_position) then
Public.add_chart_tag(force, market)
if market ~= nil and market.valid then
for _, force in pairs(forces) do
if force.is_chunk_visible(market.surface, town_center.chunk_position) then
Public.add_chart_tag(town_center)
end
end
end
end
@ -386,6 +459,35 @@ local function reset_permissions(permission_group)
end
end
local function enable_blueprints(permission_group)
local defs = {
defines.input_action.alt_select_blueprint_entities,
defines.input_action.cancel_new_blueprint,
defines.input_action.change_blueprint_record_label,
defines.input_action.clear_selected_blueprint,
defines.input_action.create_blueprint_like,
defines.input_action.cycle_blueprint_backwards,
defines.input_action.cycle_blueprint_forwards,
defines.input_action.delete_blueprint_library,
defines.input_action.delete_blueprint_record,
defines.input_action.drop_blueprint_record,
defines.input_action.drop_to_blueprint_book,
defines.input_action.export_blueprint,
defines.input_action.grab_blueprint_record,
defines.input_action.import_blueprint,
defines.input_action.import_blueprint_string,
defines.input_action.open_blueprint_library_gui,
defines.input_action.open_blueprint_record,
defines.input_action.select_blueprint_entities,
defines.input_action.setup_blueprint,
defines.input_action.setup_single_blueprint_record,
defines.input_action.upgrade_open_blueprint
}
for _, d in pairs(defs) do
permission_group.set_allows_action(d, true)
end
end
local function disable_blueprints(permission_group)
local defs = {
defines.input_action.alt_select_blueprint_entities,
@ -408,7 +510,30 @@ local function disable_blueprints(permission_group)
defines.input_action.select_blueprint_entities,
defines.input_action.setup_blueprint,
defines.input_action.setup_single_blueprint_record,
defines.input_action.upgrade_open_blueprint,
defines.input_action.upgrade_open_blueprint
}
for _, d in pairs(defs) do
permission_group.set_allows_action(d, false)
end
end
local function enable_deconstruct(permission_group)
local defs = {
defines.input_action.deconstruct,
defines.input_action.clear_selected_deconstruction_item,
defines.input_action.cancel_deconstruct,
defines.input_action.toggle_deconstruction_item_entity_filter_mode,
defines.input_action.toggle_deconstruction_item_tile_filter_mode,
defines.input_action.set_deconstruction_item_tile_selection_mode,
defines.input_action.set_deconstruction_item_trees_and_rocks_only
}
for _, d in pairs(defs) do
permission_group.set_allows_action(d, true)
end
end
local function disable_deconstruct(permission_group)
local defs = {
defines.input_action.deconstruct,
defines.input_action.clear_selected_deconstruction_item,
defines.input_action.cancel_deconstruct,
@ -425,12 +550,12 @@ end
local function enable_artillery(force, permission_group)
permission_group.set_allows_action(defines.input_action.use_artillery_remote, true)
force.technologies['artillery'].enabled = true
force.technologies['artillery-shell-range-1'].enabled = false
force.technologies['artillery-shell-speed-1'].enabled = false
force.recipes['artillery-turret'].enabled = true
force.recipes['artillery-wagon'].enabled = true
force.recipes['artillery-targeting-remote'].enabled = true
force.recipes['artillery-shell'].enabled = true
force.technologies['artillery-shell-range-1'].enabled = true
force.technologies['artillery-shell-speed-1'].enabled = true
force.recipes['artillery-turret'].enabled = false
force.recipes['artillery-wagon'].enabled = false
force.recipes['artillery-targeting-remote'].enabled = false
force.recipes['artillery-shell'].enabled = false
end
local function disable_artillery(force, permission_group)
@ -490,11 +615,13 @@ end
-- setup a team force
function Public.add_new_force(force_name)
local ffatable = Table.get_table()
-- disable permissions
local force = game.create_force(force_name)
local permission_group = game.permissions.create_group(force_name)
reset_permissions(permission_group)
disable_blueprints(permission_group)
enable_blueprints(permission_group)
enable_deconstruct(permission_group)
enable_artillery(force, permission_group)
disable_spidertron(force, permission_group)
disable_rockets(force)
@ -510,15 +637,29 @@ function Public.add_new_force(force_name)
-- balance initial combat
force.set_ammo_damage_modifier('landmine', -0.75)
force.set_ammo_damage_modifier('grenade', -0.5)
if (ffatable.testing_mode == true) then
local e_force = game.forces['enemy']
e_force.set_friend(force, true) -- team force should not be attacked by turrets
e_force.set_cease_fire(force, true) -- team force should not be attacked by units
force.enable_all_prototypes()
force.research_all_technologies()
end
return force
end
local function kill_force(force_name)
local function kill_force(force_name, cause)
local ffatable = Table.get_table()
local force = game.forces[force_name]
local market = ffatable.town_centers[force_name].market
local town_center = ffatable.town_centers[force_name]
local market = town_center.market
local position = market.position
local surface = market.surface
surface.create_entity({name = 'big-artillery-explosion', position = market.position})
local balance = town_center.coin_balance
local town_name = town_center.town_name
surface.create_entity({name = 'big-artillery-explosion', position = position})
for _, player in pairs(force.players) do
ffatable.spawn_point[player.name] = nil
ffatable.cooldowns_town_placement[player.index] = game.tick + 3600 * 15
if player.character then
player.character.die()
else
@ -526,22 +667,108 @@ local function kill_force(force_name)
end
player.force = game.forces.player
Public.set_player_color(player)
Public.give_key(player.index)
end
for _, e in pairs(surface.find_entities_filtered({force = force_name})) do
if e.valid then
if e.type == 'wall' or e.type == 'gate' then
if destroy_military_types[e.type] == true then
surface.create_entity({name = 'big-artillery-explosion', position = position})
e.die()
else
if destroy_robot_types[e.type] == true then
surface.create_entity({name = 'explosion', position = position})
e.die()
else
if destroy_wall_types[e.type] == true then
e.die()
end
end
end
end
end
for _, e in pairs(surface.find_entities_filtered({force = force_name})) do
if e.valid then
e.force = game.forces['neutral']
local damage = math_random() * 2.5 - 0.5
if damage > 0 then
if damage >= 1 or e.health == nil then
e.die()
else
local health = e.health
e.health = health * damage
end
end
end
end
local r = 27
for _, e in pairs(
surface.find_entities_filtered({area = {{position.x - r, position.y - r}, {position.x + r, position.y + r}}, force = 'neutral', type = 'resource'})
) do
if e.name ~= 'crude-oil' then
e.destroy()
end
end
game.merge_forces(force_name, 'neutral')
ffatable.town_centers[force_name] = nil
ffatable.size_of_town_centers = ffatable.size_of_town_centers - 1
ffatable.number_of_towns = ffatable.number_of_towns - 1
delete_chart_tag_for_all_forces(market)
game.print('>> ' .. force_name .. "'s town has fallen! [gps=" .. math.floor(market.position.x) .. ',' .. math.floor(market.position.y) .. ']', {255, 255, 0})
-- reward the killer
if cause == nil or not cause.valid then
Server.to_discord_embed(town_name .. ' has fallen!')
game.print('>> ' .. town_name .. ' has fallen!', {255, 255, 0})
return
end
if cause.force == nil then
Server.to_discord_embed(town_name .. ' has fallen!')
game.print('>> ' .. town_name .. ' has fallen!', {255, 255, 0})
return
end
if cause.force.name == 'player' or cause.force.name == 'rogue' then
local items = {name = 'coin', count = balance}
town_center.coin_balance = 0
if balance > 0 then
if cause.can_insert(items) then
cause.insert(items)
else
local chest = surface.create_entity({name = 'steel-chest', position = position, force = 'neutral'})
chest.insert(items)
end
end
if cause.force.name == 'player' then
Server.to_discord_embed(town_name .. ' has fallen to outlanders!')
game.print('>> ' .. town_name .. ' has fallen to outlanders!', {255, 255, 0})
else
Server.to_discord_embed(town_name .. ' has fallen to rogues!')
game.print('>> ' .. town_name .. ' has fallen to rogues!', {255, 255, 0})
end
else
if cause.force.name ~= 'enemy' then
if ffatable.town_centers[cause.force.name] ~= nil then
local killer_town_center = ffatable.town_centers[cause.force.name]
if balance > 0 then
killer_town_center.coin_balance = killer_town_center.coin_balance + balance
end
Server.to_discord_embed(town_name .. ' has fallen to ' .. killer_town_center.town_name .. '!')
game.print('>> ' .. town_name .. ' has fallen to ' .. killer_town_center.town_name .. '!', {255, 255, 0})
end
else
Server.to_discord_embed(town_name .. ' has fallen!')
game.print('>> ' .. town_name .. ' has fallen!', {255, 255, 0})
end
end
end
local player_force_disabled_recipes = {'lab', 'automation-science-pack', 'stone-brick', 'radar'}
-- hand craftable
local player_force_disabled_recipes = {
'lab',
'automation-science-pack',
'steel-furnace',
'electric-furnace',
'stone-wall',
'stone-brick',
'radar'
}
local player_force_enabled_recipes = {
'submachine-gun',
'assembling-machine-1',
@ -552,24 +779,40 @@ local player_force_enabled_recipes = {
'splitter',
'steel-plate',
'car',
'cargo-wagon',
'constant-combinator',
'tank',
'engine-unit',
'constant-combinator',
'green-wire',
'locomotive',
'rail',
'train-stop',
'red-wire',
'arithmetic-combinator',
'decider-combinator'
}
local function setup_neutral_force()
local force = game.forces['neutral']
force.technologies['military'].researched = true
force.technologies['automation'].researched = true
force.technologies['logistic-science-pack'].researched = true
force.technologies['steel-processing'].researched = true
force.technologies['engine'].researched = true
force.recipes['submachine-gun'].enabled = true
force.recipes['engine-unit'].enabled = true
force.recipes['stone-brick'].enabled = false
force.recipes['radar'].enabled = false
force.recipes['lab'].enabled = false
force.recipes['automation-science-pack'].enabled = false
force.recipes['logistic-science-pack'].enabled = false
end
-- setup the player force (this is the default for Outlanders)
local function setup_player_force()
local ffatable = Table.get_table()
local force = game.forces.player
local permission_group = game.permissions.create_group('outlander')
-- disable permissions
reset_permissions(permission_group)
disable_blueprints(permission_group)
disable_deconstruct(permission_group)
disable_artillery(force, permission_group)
disable_spidertron(force, permission_group)
disable_rockets(force)
@ -593,15 +836,20 @@ local function setup_player_force()
end
force.set_ammo_damage_modifier('landmine', -0.75)
force.set_ammo_damage_modifier('grenade', -0.5)
if (ffatable.testing_mode == true) then
force.enable_all_prototypes()
end
end
local function setup_rogue_force()
local ffatable = Table.get_table()
local force_name = 'rogue'
local force = game.create_force(force_name)
local permission_group = game.permissions.create_group(force_name)
-- disable permissions
reset_permissions(permission_group)
disable_blueprints(permission_group)
disable_deconstruct(permission_group)
disable_artillery(force, permission_group)
disable_spidertron(force, permission_group)
disable_rockets(force)
@ -625,22 +873,31 @@ local function setup_rogue_force()
end
force.set_ammo_damage_modifier('landmine', -0.75)
force.set_ammo_damage_modifier('grenade', -0.5)
if (ffatable.testing_mode == true) then
force.enable_all_prototypes()
end
end
local function setup_enemy_force()
local ffatable = Table.get_table()
local e_force = game.forces['enemy']
e_force.evolution_factor = 1 -- this should never change since we are changing biter types on spawn
e_force.set_friend(game.forces.player, true) -- outlander force (player) should not be attacked by turrets
e_force.set_cease_fire(game.forces.player, true) -- outlander force (player) should not be attacked by units
e_force.set_friend(game.forces['rogue'], false) -- rogue force (rogue) should be attacked by turrets
e_force.set_cease_fire(game.forces['rogue'], false) -- rogue force (rogue) should be attacked by units
-- note, these don't prevent an outlander or rogue from attacking a unit or spawner, we need to handle separately
if (ffatable.testing_mode == true) then
e_force.set_friend(game.forces['rogue'], true) -- rogue force (rogue) should not be attacked by turrets
e_force.set_cease_fire(game.forces['rogue'], true) -- rogue force (rogue) should not be attacked by units
else
-- note, these don't prevent an outlander or rogue from attacking a unit or spawner, we need to handle separately
e_force.set_friend(game.forces['rogue'], false) -- rogue force (rogue) should be attacked by turrets
e_force.set_cease_fire(game.forces['rogue'], false) -- rogue force (rogue) should be attacked by units
end
end
local function on_player_dropped_item(event)
local player = game.players[event.player_index]
local entity = event.entity
if entity.stack.name == 'raw-fish' then
if entity.stack.name == 'coin' then
ally_town(player, entity)
return
end
@ -669,9 +926,9 @@ local function on_entity_damaged(event)
-- special case to handle enemies attacked by outlanders
if entity.force == game.forces['enemy'] then
if cause ~= nil then
if cause.type == 'character' and force == game.forces['player'] then
if cause.type == 'character' and force.index == game.forces['player'].index then
local player = cause.player
if force == game.forces['player'] then
if player ~= nil and force.index == game.forces['player'].index then
-- set the force of the player to rogue until they die or create a town
set_player_to_rogue(player)
end
@ -679,12 +936,12 @@ local function on_entity_damaged(event)
-- cars and tanks
if cause.type == 'car' or cause.type == 'tank' then
local driver = cause.get_driver()
if driver ~= nil and driver.force == game.forces['player'] then
if driver ~= nil and driver.force.index == game.forces['player'].index then
-- set the force of the player to rogue until they die or create a town
set_player_to_rogue(driver)
end
local passenger = cause.get_passenger()
if passenger ~= nil and passenger.force == game.forces['player'] then
if passenger ~= nil and passenger.force.index == game.forces['player'].index then
-- set the force of the player to rogue until they die or create a town
set_player_to_rogue(passenger)
end
@ -693,16 +950,18 @@ local function on_entity_damaged(event)
if cause.type == 'locomotive' or cause.type == 'cargo-wagon' or cause.type == 'fluid-wagon' or cause.type == 'artillery-wagon' then
local train = cause.train
for _, passenger in pairs(train.passengers) do
if passenger ~= nil and passenger.force == game.forces['player'] then
if passenger ~= nil and passenger.force.index == game.forces['player'].index then
set_player_to_rogue(passenger)
end
end
end
-- combat robots
if cause.type == 'combat-robot' and force == game.forces['player'] then
if cause.type == 'combat-robot' then
local owner = cause.last_user
-- set the force of the player to rogue until they die or create a town
set_player_to_rogue(owner)
if owner ~= nil and owner.force == game.forces['player]'] then
-- set the force of the player to rogue until they die or create a town
set_player_to_rogue(owner)
end
end
end
end
@ -710,8 +969,9 @@ end
local function on_entity_died(event)
local entity = event.entity
if entity.name == 'market' then
kill_force(entity.force.name)
local cause = event.cause
if entity ~= nil and entity.valid and entity.name == 'market' then
kill_force(entity.force.name, cause)
end
end
@ -723,7 +983,7 @@ local function on_post_entity_died(event)
local entities = game.surfaces[event.surface_index].find_entities_filtered({position = event.position, radius = 1})
for _, e in pairs(entities) do
if e.type == 'character-corpse' then
Public.remove_key(e)
Public.remove_key(e.character_corpse_player_index)
end
end
end
@ -735,12 +995,16 @@ end
local function on_console_chat(event)
local player = game.players[event.player_index]
if string_match(string_lower(event.message), '%[armor%=') then
if string_match(event.message, player.name) then
return
end
player.clear_console()
game.print('>> ' .. player.name .. ' is trying to gain an unfair advantage!')
end
end
function Public.initialize()
setup_neutral_force()
setup_player_force()
setup_rogue_force()
setup_enemy_force()

View File

@ -5,13 +5,17 @@ local table_insert = table.insert
local math_floor = math.floor
local table_shuffle = table.shuffle_table
local Server = require 'utils.server'
local Table = require 'modules.scrap_towny_ffa.table'
local Team = require 'modules.scrap_towny_ffa.team'
local Building = require 'modules.scrap_towny_ffa.building'
local Colors = require 'modules.scrap_towny_ffa.colors'
local Enemy = require 'modules.scrap_towny_ffa.enemy'
local Color = require 'utils.color_presets'
local town_radius = 27
local radius_between_towns = 160
local ore_amount = 250
local radius_between_towns = 64
local ore_amount = 1000 * (200/168.5)
local colors = {}
local c1 = 250
@ -63,8 +67,8 @@ end
local resource_vectors = {}
resource_vectors[1] = {}
for x = 7, 24, 1 do
for y = 7, 24, 1 do
for x = 10, 22, 1 do
for y = 10, 22, 1 do
table_insert(resource_vectors[1], {x, y})
end
end
@ -103,12 +107,17 @@ for _, vector in pairs(additional_resource_vectors[3]) do
table_insert(additional_resource_vectors[4], {vector[1], vector[2] * -1})
end
local clear_whitelist_types = {
['simple-entity'] = true,
['resource'] = true,
['cliff'] = true,
['tree'] = true
}
--local clear_whitelist_types = {
-- ['character'] = true,
-- ['market'] = true,
-- ['simple-entity'] = true,
-- ['simple-entity-with-owner'] = true,
-- ['container'] = true,
-- ['car'] = true,
-- ['resource'] = true,
-- ['cliff'] = true,
-- ['tree'] = true
--}
local starter_supplies = {
{name = 'raw-fish', count = 3},
@ -121,7 +130,6 @@ local starter_supplies = {
{name = 'shotgun', count = 1},
{name = 'shotgun-shell', count = 8},
{name = 'firearm-magazine', count = 16},
{name = 'firearm-magazine', count = 16},
{name = 'gun-turret', count = 2}
}
@ -140,14 +148,14 @@ local function draw_town_spawn(player_name)
local position = market.position
local surface = market.surface
local area = {{position.x - (town_radius + 1), position.y - (town_radius + 1)}, {position.x + (town_radius + 1), position.y + (town_radius + 1)}}
--local area = {{position.x - (town_radius + 1), position.y - (town_radius + 1)}, {position.x + (town_radius + 1), position.y + (town_radius + 1)}}
-- remove other than cliffs, rocks and ores and trees
for _, e in pairs(surface.find_entities_filtered({area = area, force = 'neutral'})) do
if not clear_whitelist_types[e.type] then
e.destroy()
end
end
--for _, e in pairs(surface.find_entities_filtered({area = area, force = 'neutral'})) do
-- if not clear_whitelist_types[e.type] then
-- e.destroy()
-- end
--end
-- create walls
for _, vector in pairs(gate_vectors_horizontal) do
@ -196,7 +204,7 @@ local function draw_town_spawn(player_name)
local p = {position.x + m1, position.y + m2}
p = surface.find_non_colliding_position('wooden-chest', p, 64, 1)
if p then
local e = surface.create_entity({name = 'wooden-chest', position = p, force = player_name})
local e = surface.create_entity({name = 'iron-chest', position = p, force = player_name})
local inventory = e.get_inventory(defines.inventory.chest)
inventory.insert(item_stack)
end
@ -225,7 +233,7 @@ local function draw_town_spawn(player_name)
local y = position.y + vector[2]
local p = {x = x, y = y}
if surface.get_tile(p).name ~= 'out-of-map' then
surface.set_tiles({{name = 'water-green', position = p}})
surface.set_tiles({{name = 'water-shallow', position = p}})
end
end
@ -265,30 +273,28 @@ local function draw_town_spawn(player_name)
--end
end
local function is_valid_location(surface, position)
local function is_valid_location(force_name, surface, position)
local ffatable = Table.get_table()
if not surface.can_place_entity({name = 'market', position = position}) then
surface.create_entity(
{
name = 'flying-text',
position = position,
text = 'Position is obstructed - no room for market!',
color = {r = 0.77, g = 0.0, b = 0.0}
}
{
name = 'flying-text',
position = position,
text = 'Position is obstructed - no room for market!',
color = {r = 0.77, g = 0.0, b = 0.0}
}
)
return false
end
for _, vector in pairs(town_wall_vectors) do
local p = {x = math_floor(position.x + vector[1]), y = math_floor(position.y + vector[2])}
local tile = surface.get_tile(p.x, p.y)
if tile.name == 'out-of-map' then
if Building.in_restricted_zone(surface, p) then
surface.create_entity(
{
name = 'flying-text',
position = position,
text = 'Town would be off-map!',
text = 'Can not build in restricted zone!',
color = {r = 0.77, g = 0.0, b = 0.0}
}
)
@ -296,52 +302,53 @@ local function is_valid_location(surface, position)
end
end
if ffatable.size_of_town_centers > 48 then
if ffatable.number_of_towns > 48 then
surface.create_entity(
{
name = 'flying-text',
position = position,
text = 'Too many town centers on the map!',
text = 'Too many towns on the map!',
color = {r = 0.77, g = 0.0, b = 0.0}
}
)
return false
end
if Building.near_town(position, surface, radius_between_towns) then
if Building.near_another_town(force_name, position, surface, radius_between_towns) == true then
surface.create_entity(
{
name = 'flying-text',
position = position,
text = 'Town location is too close to another town center!',
text = 'Town location is too close to others!',
color = {r = 0.77, g = 0.0, b = 0.0}
}
)
return false
end
local area = {{position.x - town_radius, position.y - town_radius}, {position.x + town_radius, position.y + town_radius}}
local count = 0
for _, e in pairs(surface.find_entities_filtered({area = area})) do
if e.force.name == 'enemy' then
count = count + 1
end
end
if count > 1 then
surface.create_entity(
{
name = 'flying-text',
position = position,
text = 'I got a bad feeling about this! There are enemies nearby.',
color = {r = 0.77, g = 0.0, b = 0.0}
}
)
end
return true
end
function Public.in_any_town(position)
local ffatable = Table.get_table()
local town_centers = ffatable.town_centers
for _, town_center in pairs(town_centers) do
local market = town_center.market
if market ~= nil then
if Building.in_area(position, market.position, town_radius) == true then
return true
end
end
end
return false
end
function Public.update_town_name(force)
local ffatable = Table.get_table()
local town_center = ffatable.town_centers[force.name]
rendering.set_text(town_center.town_caption, town_center.town_name)
end
function Public.set_market_health(entity, final_damage_amount)
local ffatable = Table.get_table()
local town_center = ffatable.town_centers[entity.force.name]
@ -360,137 +367,121 @@ function Public.update_coin_balance(force)
rendering.set_text(town_center.coins_text, 'Coins: ' .. town_center.coin_balance)
end
local function is_color_used(color, town_centers)
for _, center in pairs(town_centers) do
if center.color then
if center.color.r == color.r and center.color.g == color.g and center.color.b == color.b then
return true
end
end
end
end
local function get_color()
local ffatable = Table.get_table()
local town_centers = ffatable.town_centers
local c
local shuffle_index = {}
for i = 1, #colors, 1 do
shuffle_index[i] = i
end
table_shuffle(shuffle_index)
for i = 1, #colors, 1 do
c = {r = colors[shuffle_index[i]][1], g = colors[shuffle_index[i]][2], b = colors[shuffle_index[i]][3]}
if not is_color_used(c, town_centers) then
return c
end
end
return c
end
local function found_town(event)
local entity = event.created_entity
if entity == nil or not entity.valid then
return true
end -- cancel, not a valid entity placed
if entity.name ~= 'stone-furnace' then
return false
end -- cancel, player did not place a stone-furnace
-- is a valid entity placed?
if entity == nil or not entity.valid then return end
local player = game.players[event.player_index]
if player.force ~= game.forces.player and player.force ~= game.forces['rogue'] then
return false
end -- cancel, player is in a team already
local force_name = tostring(player.name)
if game.forces[force_name] then
-- is player not a character?
local character = player.character
if character == nil then return end
-- is it a stone-furnace?
if entity.name ~= 'stone-furnace' then
return
end -- cancel, player is mayor of town
if Team.has_key(player) == false then
return false
end -- cancel, player has already placed a town
end
-- is player in a town already?
if player.force.index ~= game.forces.player.index and player.force.index ~= game.forces['rogue'].index then
return
end
-- try to place the town
local force_name = tostring(player.name)
local surface = entity.surface
local ffatable = Table.get_table()
local position = entity.position
entity.destroy()
-- are towns enabled?
local ffatable = Table.get_table()
if not ffatable.towns_enabled then
player.print('You must wait for more players to join!', {255, 255, 0})
player.insert({name = 'stone-furnace', count = 1})
return
end
-- is player mayor of town that still exists?
if game.forces[force_name] then
player.insert({name = 'stone-furnace', count = 1})
return
end
-- has player placed a town already?
if Team.has_key(player.index) == false then
player.insert({name = 'stone-furnace', count = 1})
return
end
-- is town placement on cooldown?
if ffatable.cooldowns_town_placement[player.index] then
if game.tick < ffatable.cooldowns_town_placement[player.index] then
surface.create_entity(
{
name = 'flying-text',
position = entity.position,
text = 'Town founding is on cooldown for ' .. math.ceil((ffatable.cooldowns_town_placement[player.index] - game.tick) / 3600) .. ' minutes.',
color = {r = 0.77, g = 0.0, b = 0.0}
}
{
name = 'flying-text',
position = position,
text = 'Town founding is on cooldown for ' .. math.ceil((ffatable.cooldowns_town_placement[player.index] - game.tick) / 3600) .. ' minutes.',
color = {r = 0.77, g = 0.0, b = 0.0}
}
)
player.insert({name = 'stone-furnace', count = 1})
entity.destroy()
return true
return
end
end
local position = entity.position
entity.destroy()
if not is_valid_location(surface, position) then
-- is it a valid location to place a town?
if not is_valid_location(force_name, surface, position) then
player.insert({name = 'stone-furnace', count = 1})
return true
return
end
Team.add_new_force(force_name)
-- does player have 100 coins?
local inventory = character.get_main_inventory()
if inventory == nil or inventory.get_item_count('coin') < 100 then
player.print('Towns cost 100 coins!', {255, 255, 0})
player.insert({name = 'stone-furnace', count = 1})
return
else
inventory.remove({name='coin',count=100})
end
local force = Team.add_new_force(force_name)
ffatable.town_centers[force_name] = {}
local town_center = ffatable.town_centers[force_name]
town_center.town_name = player.name .. "'s Town"
town_center.market = surface.create_entity({name = 'market', position = position, force = force_name})
town_center.chunk_position = {math.floor(town_center.market.position.x / 32), math.floor(town_center.market.position.y / 32)}
town_center.max_health = 1000
town_center.max_health = 100
town_center.coin_balance = 0
town_center.input_buffer = {}
town_center.output_buffer = {}
town_center.health = town_center.max_health
town_center.color = get_color()
local crayola = Colors.get_random_color()
town_center.color = crayola.color
town_center.research_counter = 1
town_center.upgrades = {}
town_center.upgrades.mining_prod = 0
town_center.upgrades.mining_speed = 0
town_center.upgrades.crafting_speed = 0
town_center.upgrades.laser_turret = {}
town_center.upgrades.laser_turret.slots = 0
town_center.upgrades.laser_turret.locations = {}
town_center.upgrades.laser_turret.locations = 0
town_center.evolution = {}
town_center.evolution.biters = 0
town_center.evolution.spitters = 0
town_center.evolution.worms = 0
town_center.coins_text =
rendering.draw_text {
text = 'Coins: ' .. town_center.coin_balance,
surface = surface,
target = town_center.market,
target_offset = {0, -2.75},
color = {200, 200, 200},
scale = 1.00,
font = 'default-game',
alignment = 'center',
scale_with_zoom = false
}
town_center.health_text =
rendering.draw_text {
text = 'HP: ' .. town_center.health .. ' / ' .. town_center.max_health,
surface = surface,
target = town_center.market,
target_offset = {0, -3.25},
color = {200, 200, 200},
scale = 1.00,
font = 'default-game',
alignment = 'center',
scale_with_zoom = false
}
town_center.creation_tick = game.tick
town_center.town_caption =
rendering.draw_text {
text = player.name .. "'s Town",
rendering.draw_text {
text = town_center.town_name,
surface = surface,
forces = {force_name},
target = town_center.market,
target_offset = {0, -4.25},
color = town_center.color,
@ -500,27 +491,51 @@ local function found_town(event)
scale_with_zoom = false
}
ffatable.size_of_town_centers = ffatable.size_of_town_centers + 1
town_center.health_text =
rendering.draw_text {
text = 'HP: ' .. town_center.health .. ' / ' .. town_center.max_health,
surface = surface,
forces = {force_name},
target = town_center.market,
target_offset = {0, -3.25},
color = {200, 200, 200},
scale = 1.00,
font = 'default-game',
alignment = 'center',
scale_with_zoom = false
}
town_center.coins_text =
rendering.draw_text {
text = 'Coins: ' .. town_center.coin_balance,
surface = surface,
forces = {force_name},
target = town_center.market,
target_offset = {0, -2.75},
color = {200, 200, 200},
scale = 1.00,
font = 'default-game',
alignment = 'center',
scale_with_zoom = false
}
ffatable.number_of_towns = ffatable.number_of_towns + 1
Enemy.clear_enemies(position, surface, town_radius * 2)
draw_town_spawn(force_name)
Team.add_player_to_town(player, town_center)
Team.add_chart_tag(game.forces.player, town_center.market)
local force = player.force
-- set the spawn point
local pos = {x = town_center.market.position.x, y = town_center.market.position.y + 4}
--log("setting spawn point = {" .. spawn_point.x .. "," .. spawn_point.y .. "}")
force.set_spawn_position(pos, surface)
ffatable.spawn_point[player.name] = pos
ffatable.cooldowns_town_placement[player.index] = game.tick + 3600 * 15
Team.add_player_to_town(player, town_center)
Team.remove_key(player)
Team.add_chart_tag(town_center)
game.print('>> ' .. player.name .. ' has founded a new town!', {255, 255, 0})
return true
Server.to_discord_embed(player.name .. ' has founded a new town!')
player.print('Your town color is ' .. crayola.name, crayola.color)
end
local function on_built_entity(event)
@ -554,10 +569,52 @@ end
local on_init = function()
local ffatable = Table.get_table()
ffatable.town_centers = {}
ffatable.size_of_town_centers = 0
ffatable.number_of_towns = 0
ffatable.cooldowns_town_placement = {}
end
local function rename_town(cmd)
local player = game.players[cmd.player_index]
if not player or not player.valid then
return
end
local force = player.force
if force.name == 'player' or force.name == 'rogue' then
player.print('You are not member of a town!', Color.fail)
return
end
local name = cmd.parameter
if name == nil then
player.print('Must specify new town name!', Color.fail)
return
end
local ffatable = Table.get_table()
local town_center = ffatable.town_centers[force.name]
local old_name = town_center.town_name
town_center.town_name = name
Public.update_town_name(force)
for _, p in pairs(force.players) do
if p == player then
player.print('Your town name is now ' .. name, town_center.color)
else
player.print(player.name .. ' has renamed the town to ' .. name, town_center.color)
end
Team.set_player_color(p)
end
game.print('>> ' .. old_name .. " is now known as " .. '"' .. name .. '"', {255, 255, 0})
Server.to_discord_embed(old_name .. ' is now known as ' .. '"' .. name .. '"')
end
commands.add_command(
'rename-town',
'Renames your town..',
function(cmd)
rename_town(cmd)
end
)
local Event = require 'utils.event'
Event.on_init(on_init)
Event.add(defines.events.on_built_entity, on_built_entity)

View File

@ -1,7 +1,6 @@
--luacheck: ignore
--Towny balance things by Gerkiz --
local player_ammo_starting_modifiers = {
--[[ local player_ammo_starting_modifiers = {
['artillery-shell'] = -0.75,
['biological'] = -0.5,
['bullet'] = -0.25,
@ -18,8 +17,7 @@ local player_ammo_starting_modifiers = {
['railgun'] = 1,
['rocket'] = -0.75,
['shotgun-shell'] = -0.20
}
} ]]
local player_gun_speed_modifiers = {
['artillery-shell'] = -0.75,
['biological'] = -0.5,
@ -101,7 +99,7 @@ local enemy_ammo_evolution_modifiers = {
--['rocket'] = 1,
--['shotgun-shell'] = 1
}
--[[
function init_player_weapon_damage(force)
for k, v in pairs(player_ammo_starting_modifiers) do
force.set_ammo_damage_modifier(k, v)
@ -111,8 +109,8 @@ function init_player_weapon_damage(force)
force.set_gun_speed_modifier(k, v)
end
end
function init_enemy_weapon_damage()
]]
local function init_enemy_weapon_damage()
local e_force = game.forces['enemy']
for k, v in pairs(enemy_ammo_starting_modifiers) do

View File

@ -1,6 +1,6 @@
local math_random = math.random
local Evolution = require 'modules.scrap_towny_ffa.evolution'
local Building = require 'modules.scrap_towny_ffa.building'
local Town_center = require 'modules.scrap_towny_ffa.town_center'
local Scrap = require 'modules.scrap_towny_ffa.scrap'
local unearthing_worm = require 'modules.scrap_towny_ffa.unearthing_worm'
local unearthing_biters = require 'modules.scrap_towny_ffa.unearthing_biters'
@ -8,29 +8,24 @@ local tick_tack_trap = require 'modules.scrap_towny_ffa.tick_tack_trap'
local function trap(entity)
-- check if within 32 blocks of market
if entity.type == 'tree' or Scrap.is_scrap(entity) then
if entity.type == 'tree' or Scrap.is_scrap(entity) and not Town_center.in_any_town(entity.position) then
if math_random(1, 1024) == 1 then
if not Building.near_town(entity.position, entity.surface, 32) then
tick_tack_trap(entity.surface, entity.position)
return
end
tick_tack_trap(entity.surface, entity.position)
end
if math_random(1, 256) == 1 then
if not Building.near_town(entity.position, entity.surface, 32) then
unearthing_worm(entity.surface, entity.position, Evolution.get_worm_evolution(entity))
end
unearthing_worm(entity.surface, entity.position, Evolution.get_worm_evolution(entity))
end
if math_random(1, 128) == 1 then
if not Building.near_town(entity.position, entity.surface, 32) then
unearthing_biters(entity.surface, entity.position, math_random(4, 8), Evolution.get_biter_evolution(entity))
end
unearthing_biters(entity.surface, entity.position, math_random(4, 8), Evolution.get_biter_evolution(entity))
end
end
end
local function on_player_mined_entity(event)
local entity = event.entity
trap(entity)
if entity and entity.valid then
trap(entity)
end
end
local Event = require 'utils.event'

View File

@ -6,10 +6,10 @@ local Scrap = require 'modules.scrap_towny_ffa.scrap'
-- loot chances and amounts for scrap entities
local entity_loot_chance = {
{name = 'advanced-circuit', chance = 5},
{name = 'advanced-circuit', chance = 15},
--{name = "artillery-shell", chance = 1},
{name = 'battery', chance = 20},
{name = 'cannon-shell', chance = 2},
{name = 'battery', chance = 15},
{name = 'cannon-shell', chance = 4},
--{name = "cluster-grenade", chance = 2},
{name = 'construction-robot', chance = 1},
{name = 'copper-cable', chance = 250},
@ -19,9 +19,9 @@ local entity_loot_chance = {
{name = 'destroyer-capsule', chance = 1},
{name = 'distractor-capsule', chance = 2},
{name = 'electric-engine-unit', chance = 2},
{name = 'electronic-circuit', chance = 200},
{name = 'electronic-circuit', chance = 150},
{name = 'empty-barrel', chance = 10},
{name = 'engine-unit', chance = 4},
{name = 'engine-unit', chance = 5},
{name = 'explosive-cannon-shell', chance = 2},
--{name = "explosive-rocket", chance = 3},
--{name = "explosive-uranium-cannon-shell", chance = 1},
@ -55,15 +55,16 @@ local entity_loot_chance = {
--{name = "uranium-fuel-cell", chance = 1},
--{name = "used-up-uranium-fuel-cell", chance = 1},
{name = 'water-barrel', chance = 10},
{name = 'tank', chance = 1},
{name = 'car', chance = 1}
{name = 'tank', chance = 2},
{name = 'car', chance = 3}
}
-- positive numbers can scale, 0 is disabled, and negative numbers are fixed absolute values
local entity_loot_amounts = {
['advanced-circuit'] = 2,
['advanced-circuit'] = 6,
--["artillery-shell"] = 0.3,
['battery'] = 2,
['cannon-shell'] = 2,
['cannon-shell'] = 4,
--["cluster-grenade"] = 0.3,
['construction-robot'] = 0.3,
['copper-cable'] = 24,
@ -81,13 +82,13 @@ local entity_loot_amounts = {
--["explosive-uranium-cannon-shell"] = 2,
['explosives'] = 4,
['green-wire'] = 8,
['grenade'] = 2,
['grenade'] = 6,
['heat-pipe'] = 1,
['heavy-oil-barrel'] = 3,
['iron-gear-wheel'] = 8,
['iron-plate'] = 16,
['iron-stick'] = 16,
['land-mine'] = 1,
['land-mine'] = 6,
['light-oil-barrel'] = 3,
['logistic-robot'] = 0.3,
['low-density-structure'] = 0.3,
@ -97,7 +98,7 @@ local entity_loot_amounts = {
['pipe'] = 8,
['pipe-to-ground'] = 1,
['plastic-bar'] = 4,
['processing-unit'] = 1,
['processing-unit'] = 2,
['red-wire'] = 8,
--["rocket"] = 2,
--["rocket-control-unit"] = 0.3,
@ -109,8 +110,8 @@ local entity_loot_amounts = {
--["uranium-fuel-cell"] = 0.3,
--["used-up-uranium-fuel-cell"] = 1,
['water-barrel'] = 3,
['tank'] = 1,
['car'] = 1
['tank'] = -1,
['car'] = -1
}
local scrap_raffle = {}
@ -141,9 +142,16 @@ local function on_player_mined_entity(event)
local scrap = scrap_raffle[math.random(1, size_of_scrap_raffle)]
local amount_bonus = (game.forces.enemy.evolution_factor * 2) + (game.forces.player.mining_drill_productivity_bonus * 2)
local r1 = math.ceil(entity_loot_amounts[scrap] * (0.3 + (amount_bonus * 0.3)))
local r2 = math.ceil(entity_loot_amounts[scrap] * (1.7 + (amount_bonus * 1.7)))
local amount = math.random(r1, r2)
local amount
if entity_loot_amounts[scrap] <= 0 then
amount = math.abs(entity_loot_amounts[scrap])
else
local m1 = 0.3 + (amount_bonus * 0.3)
local m2 = 1.7 + (amount_bonus * 1.7)
local r1 = math.ceil(entity_loot_amounts[scrap] * m1)
local r2 = math.ceil(entity_loot_amounts[scrap] * m2)
amount = math.random(r1, r2)
end
local player = game.players[event.player_index]
local inserted_count = player.insert({name = scrap, count = amount})

View File

@ -4,6 +4,7 @@ local math_floor = math.floor
local math_abs = math.abs
local get_noise = require 'utils.get_noise'
local Table = require 'modules.scrap_towny_ffa.table'
local Scrap = require 'modules.scrap_towny_ffa.scrap'
require 'modules.no_deconstruction_of_neutral_entities'
@ -75,6 +76,7 @@ local container_loot_chance = {
{name = 'cannon-shell', chance = 2},
{name = 'cliff-explosives', chance = 5},
--{name = "cluster-grenade", chance = 2},
{name = 'coin', chance = 1},
{name = 'construction-robot', chance = 1},
{name = 'copper-cable', chance = 250},
{name = 'copper-plate', chance = 500},
@ -128,6 +130,7 @@ local container_loot_amounts = {
['cannon-shell'] = 2,
['cliff-explosives'] = 2,
--["cluster-grenade"] = 0.3,
['coin'] = 2,
['construction-robot'] = 0.3,
['copper-cable'] = 24,
['copper-plate'] = 16,
@ -184,6 +187,8 @@ end
local size_of_scrap_raffle = #scrap_raffle
local function place_scrap(surface, position)
local ffatable = Table.get_table()
if ffatable.spaceships == nil then ffatable.spaceships = {} end
-- place turrets
if math_random(1, 700) == 1 then
if position.x ^ 2 + position.x ^ 2 > 4096 then
@ -195,18 +200,16 @@ local function place_scrap(surface, position)
end
end
-- place spaceship with loot
if math_random(1, 8192) == 1 then
local e = surface.create_entity({name = 'crash-site-spaceship', position = position, force = 'neutral'})
e.minable = true
local i = e.get_inventory(defines.inventory.chest)
if i then
for _ = 1, math_random(1, 5), 1 do
local loot = scrap_raffle[math_random(1, size_of_scrap_raffle)]
local amount = container_loot_amounts[loot]
local count = math_floor(amount * math_random(5, 35) * 0.1) + 1
i.insert({name = loot, count = count})
end
-- place market spaceship
if math_random(1, 4096) == 1 then
local spaceship = {}
spaceship.market = surface.create_entity({name = 'crash-site-spaceship-market', position = position, force = 'neutral'})
spaceship.market.minable = false
spaceship.max_health = 300
spaceship.health = spaceship.max_health
if spaceship.market ~= nil then
if ffatable.spaceships[position.x] == nil then ffatable.spaceships[position.x] = {} end
ffatable.spaceships[position.x][position.y] = spaceship
end
return
end
@ -294,12 +297,51 @@ end
local function on_chunk_generated(event)
--log("scrap_towny_ffa::on_chunk_generated")
local surface = event.surface
if (surface.name ~= 'nauvis') then
return
end
local seed = surface.map_gen_settings.seed
local left_top_x = event.area.left_top.x
local left_top_y = event.area.left_top.y
--log(" chunk = {" .. left_top_x/32 .. ", " .. left_top_y/32 .. "}")
local position
local noise
local chunk_position = event.position
--log('chunk_position = {' .. chunk_position.x .. ',' .. chunk_position.y .. '}')
if chunk_position.x >= -33 and chunk_position.x <= 32 and chunk_position.y >= -33 and chunk_position.y <= 32 then
if chunk_position.x == -33 or chunk_position.x == 32 or chunk_position.y == -33 or chunk_position.y == 32 then
local area = {{x = left_top_x, y = left_top_y},{x = left_top_x + 31, y = left_top_y + 31}}
local entities = surface.find_entities(area)
for _, e in pairs(entities) do
e.destroy()
end
for x = 0, 31, 1 do
for y = 0, 31, 1 do
position = {x = left_top_x + x, y = left_top_y + y}
if not surface.get_tile(position).collides_with('water-tile') then
surface.set_tiles({{name = 'water-shallow', position = position}}, true)
end
end
end
return
end
end
if chunk_position.x < -33 or chunk_position.x > 32 or chunk_position.y < -33 or chunk_position.y > 32 then
local area = {{x = left_top_x, y = left_top_y},{x = left_top_x + 31, y = left_top_y + 31}}
local entities = surface.find_entities(area)
for _, e in pairs(entities) do
e.destroy()
end
for x = 0, 31, 1 do
for y = 0, 31, 1 do
position = {x = left_top_x + x, y = left_top_y + y}
surface.set_tiles({{name = 'deepwater', position = position}}, true)
end
end
return
end
for x = 0, 31, 1 do
for y = 0, 31, 1 do
if math_random(1, 3) > 1 then

View File

@ -116,7 +116,13 @@ local noises = {
{modifier = 0.004, weight = 1},
{modifier = 0.02, weight = 0.05}
},
['journey_swamps'] = {{modifier = 0.02, weight = 1}, {modifier = 0.04, weight = 0.35}, {modifier = 0.1, weight = 0.08}},
['scrap_towny_ffa'] = {
{modifier = 0.005, weight = 1},
{modifier = 0.025, weight = 0.25},
{modifier = 0.1, weight = 0.125},
{modifier = 0.01, weight = 0.025}
},
['journey_swamps'] = {{modifier = 0.02, weight = 1}, {modifier = 0.04, weight = 0.35}, {modifier = 0.1, weight = 0.08}}
}
--returns a float number between -1 and 1