1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2025-03-17 20:58:13 +02:00

Merge pull request #244 from danielmartin0/develop

Merge in Pirate Ship now that the main branch has deflate
This commit is contained in:
Gerkiz 2022-04-08 16:54:45 +02:00 committed by GitHub
commit 0d8c05f760
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
97 changed files with 30299 additions and 1 deletions

5
.gitignore vendored
View File

@ -2,3 +2,8 @@
.history/
.git/
maps/biter_battles_v3/
#==Mac ignores==
# General
.DS_Store

View File

@ -110,6 +110,10 @@ require 'utils.freeplay'
--![[Comfylatron has seized the Fish Train and turned it into a time machine]]--
--require 'maps.chronosphere.main'
--![[Adventure as a crew of pirates]]--
--NOTE: This scenario is intended to be used with utils.gui.group, utils.gui.poll, and utils.gui.score disabled
--require 'maps.pirates.main'
--![[Launch rockets in increasingly harder getting worlds.]]--
--require 'maps.journey.main'

48
locale/en/pirates.cfg Normal file
View File

@ -0,0 +1,48 @@
[pirates]
softmod_info_header_before_version_number==== Pirate Ship v
softmod_info_header_after_version_number= ===
softmod_info_body_1=News and voice chat: getcomfy.eu/discord
softmod_info_game_description_1=Game Description
softmod_info_game_description_2=Set sail in this multiplayer scenario. Collect resources and fuel the ship in order to survive as many leagues as possible. The ship moves with code magic. Doubloons can be spent at docks and other markets, to upgrade players, upgrade the ship, and buy rare items. Each crew has a captain, who can perform actions such as deciding when the boat leaves.\n\nGame progression is significantly slowed down the smaller the crew.\n\n[font=default-bold]Win condition:[/font] Travel 1000 leagues.\n[font=default-bold]Lose condition:[/font] The ship runs out of fuel, or a cannon is destroyed.
softmod_info_bugs_1=Known issues
softmod_info_bugs_2=• Very rarely (only observed once), the deck locomotive can disappear, and an admin needs to make a new one.
softmod_info_new_players_1=For New Players
softmod_info_new_players_2=Mine coal and other resources and bring them to the ship to keep things going, or try asking the captain for more specific tasks.
softmod_info_tips_1=Features of the game that are hard to work out alone
softmod_info_tips_2=• You can steer the boat from the crow's nest by placing 100 rail signals in one of the blue boxes.\n• Resources granted to the ship appear in the captain's cabin.\n• Charging a silo launches a rocket. This causes pollution and evo, but gives a reward of fuel and doubloons.\n• Charging a silo drains power from everything else on its network.\n• The quantity of ore available on an island is independent of the order in which you break rocks.\n• Passive pollution ramps up over time on each island.\n• The strength of attacks is proportional to the number of remaining nests. (The time-based rate of evolution is proportional to nests too, but destroying a nest will immediately jump evolution by the amount it 'would have' made had it survived.)\n• Covered markets give back any plates spent to unlock them.\n• Lab productivity increases with each league.\n• The player who spent the longest as captain between leagues 0 and 1000 (exclusive) is written into the highscores table.\n• Logged-out players keep their items with them for a while — except 'important' items that are returned to the crew immediately.\n• Commands: /ccolor gives you a fun color. To manage your class, use /classinfo {classname}, /take {classname}, or /giveup. Captains also have /undock, /req, /officer, /plank.
softmod_info_updates_1=Development
softmod_info_updates_2=Recent significant changes: Silo death is no longer a lose condition. Game made much easier for small crews. New islands and classes. The ship now shares power across each of its floors. Officers can now access chests in the captain's cabin directly.
softmod_info_credits_1=Credits
softmod_info_credits_2=Pirate Ship designed and coded by thesixthroc. Comfy codebase and help from Gerkiz, Hanakocz and Mew @ Comfy Industries (https://getcomfy.eu). Some island structure blueprints contributed by Mattisso.\n\nContact us via Discord: https://getcomfy.eu/discord\n\n"Those white gloves. I'll never forget them 'till the day I die." - Dr. John
softmod_info_credits_2_old=Softmod designed and written by thesixthroc. Comfy codebase help from Gerkiz, Hanakocz and Mew @ Comfy Industries (https://getcomfy.eu). Some island structure blueprints were contributed by Mattisso. Gold sprite by Clint Bellanger. Parrot sprites by @pixelthen.\n\n"Those white gloves. I'll never forget them 'till the day I die." - Dr. John
softmod_info_body_promote=by thesixthroc
softmod_info_body_promote_old2=patreon.com/thesixthroc
softmod_info_body_clicky=Click to dismiss.
softmod_info_body_some_old_stuff="Those white gloves. I'll never forget them 'till the day I die." - Dr. John
proposal_displayform=__1__ — __2__
run_displayform= __2__
crewmember_displayform=[color=__2__,__3__,__4__]__5__[/color] [color=1,1,1]__6__[/color]
capacity_tooltip=Capacity. Sets the maximum number of crewmembers allowed.
difficulty_tooltip=Difficulty.\n\nHigher difficulties have higher pollution and evo, higher biter damage, lower gold loot, but higher chest loot, along with small effects on the time per island, quest requirements, and silo position.\n\nDifficulty also determines the material the ship is made out of.
mode_tooltip=Mode.
auto_undock_tooltip=The maximum time to stay at this location.\n\nOnce this time is reached, the boat undocks automatically. The captain can choose to leave earlier by pressing this button.
atsea_loading_tooltip=The next destination is loading.
leave_anytime_tooltip=The captain chooses when to undock the ship.\n\nThey can undock by pressing this button.
resources_needed_tooltip_1=At the next destination, resources will be needed in order to undock early.\n\nFewer resources will be needed the longer you stay, eventually dropping to zero.
resources_needed_tooltip_2=The captain can undock early by clicking this button, but only if enough resources have been stored in the captain's cabin.\n\nCost on arrival: __1__\nLeaving now will spend: __2__
resources_needed_tooltip_3=The captain can undock by clicking this button, but only if enough resources are stored in the captain's cabin.
resources_needed_tooltip_1_rocketvariant=At the next destination, these resources will be needed in order to undock early.\n\nFewer resources will be needed the longer you stay, eventually dropping to zero..\n\nThe silo represents a rocket launch.
resources_needed_tooltip_2_rocketvariant=The captain can undock early by clicking this button, but only if enough resources have been stored in the captain's cabin.\n\nThe silo represents a rocket launch rather than a resource.\n\nnCost on arrival: __1__\nLeaving now will spend: __2__
resources_needed_tooltip_3_rocketvariant=The captain can undock by clicking this button, but only if enough resources are stored in the captain's cabin.\n\nThe silo represents a rocket launch rather than a resource.
fuel_tooltip=__1__ stored fuel.\n\nTo store more, send it to the captain's cabin. If the ship runs out of fuel, the crew loses.\n\n*Click* to open the Captain's Store, which only the captain and their officers are authorised to use.

View File

@ -99,4 +99,4 @@ function Public.get(key)
end
end
return Public
return Public

864
maps/pirates/ai.lua Normal file
View File

@ -0,0 +1,864 @@
local Memory = require 'maps.pirates.memory'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
-- local Utils = require 'maps.pirates.utils_local'
local Math = require 'maps.pirates.math'
local _inspect = require 'utils.inspect'.inspect
-- local Structures = require 'maps.pirates.structures.structures'
local Boats = require 'maps.pirates.structures.boats.boats'
local Surfaces = require 'maps.pirates.surfaces.surfaces'
local Islands = require 'maps.pirates.surfaces.islands.islands'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
-- local Sea = require 'maps.pirates.surfaces.sea.sea'
-- local Crew = require 'maps.pirates.crew'
-- local Quest = require 'maps.pirates.quest'
local Public = {}
local function fake_boat_target()
local memory = Memory.get_crew_memory()
if memory.boat and memory.boat.position then
return {valid = true, position = {x = memory.boat.position.x - 60, y = memory.boat.position.y} or nil, name = 'boatarea'}
end
end
-- fff 283 discussed pollution mechanics: https://factorio.com/blog/post/fff-283
local side_attack_target_names = {
'character',
'pumpjack',
'radar',
'electric-mining-drill',
'assembling-machine-1',
'solar-panel',
'nuclear-reactor',
'oil-refinery',
'centrifuge',
}
--=== Tick Actions
function Public.Tick_actions(tickinterval)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
if (not destination.type) or (not destination.type == Surfaces.enum.ISLAND) then return end
if (not memory.boat.state) or (not (memory.boat.state == Boats.enum_state.LANDED or memory.boat.state == Boats.enum_state.RETREATING)) then return end
if (memory.game_lost) or (destination.dynamic_data.timeratlandingtime and destination.dynamic_data.timer < destination.dynamic_data.timeratlandingtime + Common.seconds_after_landing_to_enable_AI) then return end
if game.tick % (tickinterval * 2) == 0 and memory.boat.state == Boats.enum_state.LANDED then
local extra_evo = 2 * tickinterval/60 * Balance.evolution_per_second()
Common.increment_evo(extra_evo)
destination.dynamic_data.evolution_accrued_time = destination.dynamic_data.evolution_accrued_time + extra_evo
end
-- if destination.subtype and destination.subtype == Islands.enum.RED_DESERT then return end -- This was a hack to stop biter boats causing attacks, but, it has the even worse effect of stopping all floating_pollution gathering.
local minute_cycle = {-- even seconds only
[2] = Public.eat_up_fraction_of_all_pollution_wrapped,
[4] = Public.try_rogue_attack,
[6] = Public.poke_script_groups,
[16] = Public.try_main_attack,
[20] = Public.poke_script_groups,
-- [18] = Public.try_secondary_attack, --commenting out: less attacks per minute, but stronger. @TODO need to do more here
[24] = Public.tell_biters_near_silo_to_attack_it,
[28] = Public.eat_up_fraction_of_all_pollution_wrapped,
[30] = Public.try_secondary_attack,
[36] = Public.poke_script_groups,
[46] = Public.poke_script_groups,
[50] = Public.tell_biters_near_silo_to_attack_it,
[52] = Public.create_mail_delivery_biters,
[56] = Public.poke_script_groups,
[58] = Public.poke_inactive_scripted_biters,
}
if minute_cycle[(game.tick / 60) % 60] then
minute_cycle[(game.tick / 60) % 60]()
end
end
function Public.eat_up_fraction_of_all_pollution_wrapped()
-- local memory = Memory.get_crew_memory()
local surface = game.surfaces[Common.current_destination().surface_name]
Public.eat_up_fraction_of_all_pollution(surface, 0.05)
end
function Public.eat_up_fraction_of_all_pollution(surface, fraction_of_global_pollution)
local memory = Memory.get_crew_memory()
-- local enemy_force_name = memory.enemy_force_name
local pollution_available = memory.floating_pollution
local chunk_positions = {}
for i = 1, Math.ceil(surface.map_gen_settings.width/32),1 do
for j = 1, Math.ceil(surface.map_gen_settings.height/32),1 do
chunk_positions[#chunk_positions + 1] = {x = 16 + i * 32 - surface.map_gen_settings.width/2, y = 16 + j * 32 - surface.map_gen_settings.height/2}
end
end
for i = 1, #chunk_positions do
local p = chunk_positions[i]
local pollution = surface.get_pollution(p)
local pollution_to_eat = pollution * fraction_of_global_pollution
surface.pollute(p, - pollution_to_eat)
-- Radioactive world doesn't absorb map pollution:
if not (Common.current_destination().subtype and Common.current_destination().subtype == Islands.enum.RADIOACTIVE) then
pollution_available = pollution_available + pollution_to_eat
end
end
-- if _DEBUG then
-- game.print(string.format('ate %f pollution', pollution_available))
-- end
memory.floating_pollution = pollution_available
end
function Public.try_main_attack()
local wave_size_multiplier = 1
local memory = Memory.get_crew_memory()
if memory.overworldx > 0 then
if Math.random(2) == 1 then
-- log('attack aborted by chance')
return nil
end --variance in attack sizes
if Math.random(10) == 1 then wave_size_multiplier = 1.7 end --variance in attack sizes
if Math.random(70) == 1 then wave_size_multiplier = 3.2 end --variance in attack sizes
if Math.random(500) == 1 then wave_size_multiplier = 5 end --variance in attack sizes
end
local group = Public.spawn_group_of_scripted_biters(2/3, 6, 180, wave_size_multiplier)
local target = Public.generate_main_attack_target()
if not group or not group.valid or not target or not target.valid then return end
-- group.set_command(Public.attack_target(target))
Public.group_set_commands(group, Public.attack_target(target))
-- if _DEBUG then game.print(game.tick .. string.format(": sending main attack of %s units from {%f,%f} to %s", #group.members, group.position.x, group.position.y, target.name)) end
end
function Public.try_secondary_attack()
local wave_size_multiplier = 1
local memory = Memory.get_crew_memory()
if memory.overworldx > 0 then
if Math.random(2) == 1 then
log('attack aborted by chance')
end --variance in attack sizes
if Math.random(10) == 1 then wave_size_multiplier = 1.7 end --variance in attack sizes
if Math.random(70) == 1 then wave_size_multiplier = 3.2 end --variance in attack sizes
if Math.random(500) == 1 then wave_size_multiplier = 5 end --variance in attack sizes
end
local surface = game.surfaces[Common.current_destination().surface_name]
local group = Public.spawn_group_of_scripted_biters(2/3, 12, 180, wave_size_multiplier)
if not (group and group.valid) then return end
local target
if Math.random(2) == 1 then
target = Public.generate_main_attack_target()
else
target = Public.generate_side_attack_target(surface, group.position)
end
if not group or not group.valid or not target or not target.valid then log('target invalid') return end
-- group.set_command(Public.attack_target(target))
Public.group_set_commands(group, Public.attack_target(target))
-- if _DEBUG then game.print(game.tick .. string.format(": sending main attack of %s units from {%f,%f} to %s", #group.members, group.position.x, group.position.y, target.name)) end
end
function Public.try_rogue_attack()
local wave_size_multiplier = 1
local memory = Memory.get_crew_memory()
if memory.overworldx > 0 then
if Math.random(2) == 1 then
log('attack aborted by chance')
end --variance in attack sizes
if Math.random(10) == 1 then wave_size_multiplier = 1.7 end --variance in attack sizes
if Math.random(70) == 1 then wave_size_multiplier = 3.2 end --variance in attack sizes
if Math.random(500) == 1 then wave_size_multiplier = 5 end --variance in attack sizes
end
local surface = game.surfaces[Common.current_destination().surface_name]
local group = Public.spawn_group_of_scripted_biters(1/2, 6, 180, wave_size_multiplier)
if not (group and group.valid) then return end
local target = Public.generate_side_attack_target(surface, group.position)
if not (target and target.valid) then return end
-- group.set_command(Public.attack_target(target))
Public.group_set_commands(group, Public.attack_target(target))
-- if _DEBUG then game.print(game.tick .. string.format(": sending rogue attack of %s units from {%f,%f} to %s", #group.members, group.position.x, group.position.y, target.name)) end
end
function Public.tell_biters_near_silo_to_attack_it()
-- careful with this function, you don't want to pull biters onto the silo before any aggro has happened
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
local enemy_force_name = memory.enemy_force_name
-- don't do this too early
if destination.dynamic_data.timer < destination.dynamic_data.timeratlandingtime + Common.seconds_after_landing_to_enable_AI * 4 then return end
if not (destination.dynamic_data.rocketsilos and destination.dynamic_data.rocketsilos[1] and destination.dynamic_data.rocketsilos[1].valid and destination.dynamic_data.rocketsilos[1].destructible) then return end
local attackcommand = Public.attack_target_entity(destination.dynamic_data.rocketsilos[1])
if attackcommand then
surface.set_multi_command(
{
command = attackcommand,
unit_count = Math.random(1, Math.floor(1 + memory.evolution_factor * 100)),
force = enemy_force_name,
unit_search_distance = 10
}
)
end
end
function Public.poke_script_groups()
local memory = Memory.get_crew_memory()
for index, group in pairs(memory.scripted_unit_groups) do
local groupref = group.ref
if not groupref.valid or groupref.surface.index ~= game.surfaces[Common.current_destination().surface_name].index or #groupref.members < 1 then
memory.scripted_unit_groups[index] = nil
else
if groupref.state == defines.group_state.finished then
if Math.random(20) == 20 then
local command = Public.attack_obstacles(groupref.surface, {x = groupref.position.x, y = groupref.position.y})
groupref.set_command(command)
else
groupref.set_autonomous() --means go home, really
end
elseif group.state == defines.group_state.gathering then
groupref.start_moving()
-- elseif group.state == defines.group_state.wander_in_group then
-- groupref.set_autonomous() --means go home, really
end
end
end
end
function Public.poke_inactive_scripted_biters()
local memory = Memory.get_crew_memory()
for unit_number, biter in pairs(memory.scripted_biters) do
if Public.is_biter_inactive(biter) then
memory.scripted_biters[unit_number] = nil
if biter.entity and biter.entity.valid then
local target = Public.nearest_target()
if target and target.valid then
Public.group_set_commands(biter.entity, Public.attack_target(target))
end
end
end
end
end
function Public.create_mail_delivery_biters() --these travel cross-map between bases to add a little life and spookyness
local memory = Memory.get_crew_memory()
local surface = game.surfaces[Common.current_destination().surface_name]
local enemy_force_name = memory.enemy_force_name
local spawners = surface.find_entities_filtered{name = 'biter-spawner', force = enemy_force_name}
local try_how_many_groups = Math.min(Math.max(0, (#spawners - 8) / 100), 4)
for i = 1, try_how_many_groups do
if Math.random(2) == 1 then
local s1 = spawners[Math.random(#spawners)]
local far_spawners = {}
for j = 1, #spawners do
local s2 = spawners[i]
if not (i == j or Math.distance(s1.position, s2.position) < 250) then
far_spawners[#far_spawners + 1] = s2
end
end
if #far_spawners > 0 then
local s2 = far_spawners[Math.random(#far_spawners)]
memory.floating_pollution = memory.floating_pollution + 64
local units = Public.try_spawner_spend_fraction_of_available_pollution_on_biters(s1.position, 1/4, 4, 32, 1, 'small-biter')
memory.floating_pollution = memory.floating_pollution - 64
if (not units) or (not #units) or (#units == 0) then return end
local start_p = surface.find_non_colliding_position('rocket-silo', s1.position, 256, 2) or s1.position
local unit_group = surface.create_unit_group({position = start_p, force = enemy_force_name})
for _, unit in pairs(units) do
unit_group.add_member(unit)
end
memory.scripted_unit_groups[unit_group.group_number] = {ref = unit_group, script_type = 'mail-delivery'}
Public.group_set_commands(unit_group, {
Public.move_to(s2.position),
Public.wander_around(),
})
-- game.print(string.format('%f biters delivering mail from %f, %f to %f, %f', #units, s1.position.x, s1.position.y, s2.position.x, s2.position.y))
end
end
end
end
--=== Spawn scripted biters
function Public.spawn_group_of_scripted_biters(fraction_of_floating_pollution, minimum_avg_units, maximum_units, wave_size_multiplier)
local memory = Memory.get_crew_memory()
local surface = game.surfaces[Common.current_destination().surface_name]
local enemy_force_name = memory.enemy_force_name
local spawner = Public.get_random_spawner(surface)
if not spawner then log('no spawner found') return end
local nearby_units_to_bring
if Public.get_scripted_biter_count() >= 9/10 * CoreData.total_max_biters then
-- pick up nearby units that might be idle:
nearby_units_to_bring = surface.find_units{area = {{spawner.position.x - 8, spawner.position.y - 8}, {spawner.position.x + 8, spawner.position.y + 8}}, force = enemy_force_name, condition = 'same'}
else
nearby_units_to_bring = {}
end
local new_units = Public.try_spawner_spend_fraction_of_available_pollution_on_biters(spawner.position, fraction_of_floating_pollution, minimum_avg_units, maximum_units, 1/wave_size_multiplier)
if (new_units and nearby_units_to_bring and (#new_units + #nearby_units_to_bring) == 0) then return end
local position = surface.find_non_colliding_position('rocket-silo', spawner.position, 256, 2) or spawner.position
local unit_group = surface.create_unit_group({position = position, force = enemy_force_name})
for _, unit in pairs(nearby_units_to_bring) do
unit_group.add_member(unit)
end
for _, unit in pairs(new_units) do
unit_group.add_member(unit)
end
memory.scripted_unit_groups[unit_group.group_number] = {ref = unit_group, script_type = 'attacker'}
return unit_group
end
function Public.try_spawner_spend_fraction_of_available_pollution_on_biters(spawnposition, fraction_of_floating_pollution, minimum_avg_units, maximum_units, unit_pollutioncost_multiplier, enforce_type)
maximum_units = maximum_units or 256
-- log('ai spawning attempt params: ' .. (fraction_of_floating_pollution or '') .. ' ' .. (minimum_avg_units or '') .. ' ' .. (maximum_units or '') .. ' ' .. (unit_pollutioncost_multiplier or '') .. ' ' .. (enforce_type or ''))
local memory = Memory.get_crew_memory()
local surface = game.surfaces[Common.current_destination().surface_name]
-- local surface = spawner.surface
-- local spawnposition = spawner.position
-- local difficulty = memory.difficulty
local enemy_force_name = memory.enemy_force_name
local evolution = memory.evolution_factor
local units_created_count = 0
local units_created = {}
local temp_floating_pollution = memory.floating_pollution
local budget = fraction_of_floating_pollution * temp_floating_pollution
local initialpollution = memory.floating_pollution
-- local initialbudget = budget
local base_pollution_cost_multiplier = 1
local destination = Common.current_destination()
if destination.dynamic_data and destination.dynamic_data.initial_spawner_count then
local initial_spawner_count = destination.dynamic_data.initial_spawner_count
if initial_spawner_count > 0 then
local spawnerscount = Common.spawner_count(surface)
if spawnerscount > 0 then
-- if Common.current_destination().subtype and Common.current_destination().subtype == Islands.enum.RADIOACTIVE then
-- -- destroying spawners doesn't do quite as much here:
-- base_pollution_cost_multiplier = (initial_spawner_count/spawnerscount)^(1/3)
-- else
-- base_pollution_cost_multiplier = (initial_spawner_count/spawnerscount)^(1/2)
-- end
-- base_pollution_cost_multiplier = (initial_spawner_count/spawnerscount)^(1/2)
-- Now directly proportional:
base_pollution_cost_multiplier = initial_spawner_count/spawnerscount
if memory.overworldx == 0 then
base_pollution_cost_multiplier = Math.max(1, base_pollution_cost_multiplier)
end -- The first map not being fully loaded when you get there commonly means it records too few initial spawners, which this helps fix
else
base_pollution_cost_multiplier = 1000000
end
end
end
if memory.overworldx == 0 then
-- less biters:
base_pollution_cost_multiplier = base_pollution_cost_multiplier * 2.30 --tuned to teach players to defend, but then to feel relaxing once they do
end
base_pollution_cost_multiplier = base_pollution_cost_multiplier * unit_pollutioncost_multiplier
base_pollution_cost_multiplier = base_pollution_cost_multiplier * Balance.scripted_biters_pollution_cost_multiplier()
if destination.subtype and destination.subtype == IslandsCommon.enum.SWAMP then
base_pollution_cost_multiplier = base_pollution_cost_multiplier * 0.9 --biters 10% more aggressive
end
-- if destination.subtype and destination.subtype == IslandsCommon.enum.MAZE then
-- base_pollution_cost_multiplier = base_pollution_cost_multiplier * 1.2 --biters 20% less aggressive
-- end
if budget >= minimum_avg_units * Common.averageUnitPollutionCost(evolution) * base_pollution_cost_multiplier then
local function spawn(name2)
units_created_count = units_created_count + 1
local unittype_pollutioncost = CoreData.biterPollutionValues[name2] * base_pollution_cost_multiplier
local p = surface.find_non_colliding_position(name2, spawnposition, 60, 1)
if not p then
p = spawnposition
log('no position found, using spawnposition')
end
local biter = surface.create_entity({name = name2, force = enemy_force_name, position = p})
units_created[#units_created + 1] = biter
memory.scripted_biters[biter.unit_number] = {entity = biter, created_at = game.tick}
temp_floating_pollution = temp_floating_pollution - unittype_pollutioncost
budget = budget - unittype_pollutioncost
-- flow statistics should reflect the number of biters generated. Therefore it should mismatch the actual pollution spent, because it should miss all the factors that can vary:
game.pollution_statistics.on_flow(name2, - CoreData.biterPollutionValues[name2] * Balance.scripted_biters_pollution_cost_multiplier())
return biter.unit_number
end
local mixed = (Math.random(3) <= 2)
if mixed then
local whilesafety = 1000
local next_name = enforce_type or Common.get_random_unit_type(evolution)
while units_created_count < maximum_units and budget >= CoreData.biterPollutionValues[next_name] * base_pollution_cost_multiplier and #memory.scripted_biters < CoreData.total_max_biters and whilesafety > 0 do
whilesafety = whilesafety - 1
spawn(next_name)
next_name = enforce_type or Common.get_random_unit_type(evolution)
end
else
local name = enforce_type or Common.get_random_unit_type(evolution)
local whilesafety = 1000
while units_created_count < maximum_units and budget >= CoreData.biterPollutionValues[name] * base_pollution_cost_multiplier and #memory.scripted_biters < CoreData.total_max_biters and whilesafety > 0 do
whilesafety = whilesafety - 1
spawn(name)
end
end
memory.floating_pollution = temp_floating_pollution
end
if units_created_count > 0 then
log('Spent ' .. Math.floor(100 * (initialpollution - temp_floating_pollution) / initialpollution) .. '% of ' .. Math.ceil(initialpollution) .. ' pollution budget on biters, at ' .. Math.ceil(base_pollution_cost_multiplier*100)/100 .. 'x price.')
end
return units_created
end
--=== Misc Functions
function Public.generate_main_attack_target()
-- local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local target
local fractioncharged = 0
if (not destination.dynamic_data.rocketlaunched) and destination.dynamic_data.rocketsilos and destination.dynamic_data.rocketsilos[1] and destination.dynamic_data.rocketsilos[1].valid and destination.dynamic_data.rocketsilos[1].destructible and destination.dynamic_data.rocketsiloenergyconsumed and destination.dynamic_data.rocketsiloenergyneeded and destination.dynamic_data.rocketsiloenergyneeded > 0 then
fractioncharged = destination.dynamic_data.rocketsiloenergyconsumed / destination.dynamic_data.rocketsiloenergyneeded
end
local rng = Math.random()
if rng <= fractioncharged^(1/2) then
target = destination.dynamic_data.rocketsilos[1]
else
target = fake_boat_target()
end
return target
end
function Public.generate_side_attack_target(surface, position)
local entities = surface.find_entities_filtered{name = side_attack_target_names}
if not entities then return end
if Math.random(20) >= #entities then return end
entities = Math.shuffle(entities)
entities = Math.shuffle_distancebiased(entities, position)
local weights = {}
for index, _ in pairs(entities) do
weights[#weights + 1] = 1 + Math.floor((#entities - index) / 2)
end
return Math.raffle(entities, weights)
end
function Public.nearest_target(surface, position)
local names = {'rocket-silo'}
for _, name in pairs(side_attack_target_names) do
names[#names + 1] = name
end
local entities = surface.find_entities_filtered{name = names}
local d = 9999
local nearest = nil
for i = 1, #entities do
local e = entities[i]
if e and e.valid and Math.distance(e.position, position) < d then
nearest = e
end
end
return nearest
end
-- function Public.try_spend_pollution(surface, position, amount, flow_statistics_source)
-- local memory = Memory.get_crew_memory()
-- local force_name = memory.force_name
-- flow_statistics_source = flow_statistics_source or 'medium-biter'
-- if not (position and surface and surface.valid) then return end
-- local pollution = surface.get_pollution(position)
-- if pollution > amount then
-- surface.pollute(position, -amount)
-- game.forces[force_name].pollution_statistics.on_flow(flow_statistics_source, -amount)
-- return true
-- end
-- return false
-- end
function Public.get_random_spawner(surface)
local memory = Memory.get_crew_memory()
local spawners = surface.find_entities_filtered({type = 'unit-spawner', force = memory.enemy_force_name})
if (not spawners) or (not spawners[1]) then return end
return spawners[Math.random(#spawners)]
end
function Public.is_biter_inactive(biter)
if (not biter.entity) or (not biter.entity.valid) then
return true
end
if game.tick - biter.created_at > 30*60*60 then
biter.entity.destroy()
return true
end
return false
end
function Public.get_scripted_biter_count()
local memory = Memory.get_crew_memory()
local count = 0
for k, biter in pairs(memory.scripted_biters) do
if biter.entity and biter.entity.valid then
count = count + 1
else
memory.scripted_biters[k] = nil
end
end
return count
end
-----------commands-----------
function Public.stop()
local command = {
type = defines.command.stop,
distraction = defines.distraction.none
}
return command
end
function Public.move_to(position)
local command = {
type = defines.command.go_to_location,
destination = position,
distraction = defines.distraction.by_anything
}
return command
end
function Public.attack_target_entity(target)
if not target and target.valid then return end
local command = {
type = defines.command.attack,
target = target,
distraction = defines.distraction.by_anything
}
return command
end
function Public.attack_area(position, radius)
local command = {
type = defines.command.attack_area,
destination = position,
radius = radius or 25,
distraction = defines.distraction.by_anything
}
return command
end
function Public.attack_obstacles(surface, position)
local commands = {}
local obstacles = surface.find_entities_filtered {position = position, radius = 25, type = {'simple-entity', 'tree', 'simple-entity-with-owner'}, limit = 100}
if obstacles then
Math.shuffle(obstacles)
Math.shuffle_distancebiased(obstacles, position)
for i = 1, #obstacles, 1 do
if obstacles[i].valid then
commands[#commands + 1] = {
type = defines.command.attack,
target = obstacles[i],
distraction = defines.distraction.by_anything
}
end
end
end
commands[#commands + 1] = Public.move_to(position)
local command = {
type = defines.command.compound,
structure_type = defines.compound_command.return_last,
commands = commands
}
return command
end
function Public.wander_around(ticks_to_wait) --wander individually inside group radius
local command = {
type = defines.command.wander,
distraction = defines.distraction.by_anything,
ticks_to_wait = ticks_to_wait,
}
return command
end
function Public.group_set_commands(group, commands)
if #commands > 0 then
local command = {
type = defines.command.compound,
structure_type = defines.compound_command.return_last,
commands = commands
}
group.set_command(command)
end
end
function Public.attack_target(target)
if not target then return end
local commands
if target.name == 'boatarea' then
commands = {
Public.attack_area(target.position, 32),
Public.attack_area(target.position, 32),
}
else
commands = {
Public.attack_area(target.position, 8),
Public.attack_target_entity(target),
}
end
-- if Math.random(20) == 20 then
-- commands = {
-- Public.attack_obstacles(group.surface, {x = (group.position.x * 0.90 + target.position.x * 0.10), y = (group.position.y * 0.90 + target.position.y * 0.10)}),
-- attackcommand,
-- }
-- else
-- commands = {attackcommand}
-- end
return commands
end
--- small group of revenge biters ---
function Public.revenge_group(surface, p, target, type, bonus_evo, amount_multiplier)
amount_multiplier = amount_multiplier or 1
bonus_evo = bonus_evo or 0
type = type or 'biter'
local memory = Memory.get_crew_memory()
local enemy_force_name = memory.enemy_force_name
local name, count
if type == 'biter' then
name = Common.get_random_biter_type(memory.evolution_factor + bonus_evo)
if name == 'small-biter' then
count = 5
elseif name == 'medium-biter' then
count = 3
elseif name == 'big-biter' then
count = 2
elseif name == 'behemoth-biter' then
count = 1
end
elseif type == 'spitter' then
name = Common.get_random_spitter_type(memory.evolution_factor + bonus_evo)
if name == 'small-spitter' then
count = 7
elseif name == 'medium-spitter' then
count = 5
elseif name == 'big-spitter' then
count = 3
elseif name == 'behemoth-spitter' then
count = 2
end
end
if (not (name and count and count>0)) then return end
local units = {}
for i = 1, Math.floor(count * amount_multiplier) do
local p2 = surface.find_non_colliding_position('wooden-chest', p, 5, 0.5)
if p2 then
local biter = surface.create_entity({name = name, force = enemy_force_name, position = p})
-- local biter = surface.create_entity({name = name, force = enemy_force_name, position = p2})
units[#units + 1] = biter
end
end
if #units > 0 then
local unit_group = surface.create_unit_group({position = p, force = enemy_force_name})
for _, unit in pairs(units) do
unit_group.add_member(unit)
end
if target and target.valid then
Public.group_set_commands(unit_group, Public.attack_target(target))
end
unit_group.set_autonomous()
end
end
----------- biter raiding parties -----------
function Public.spawn_boat_biters(boat, max_evo, count, width)
-- max_evolution_bonus = max_evolution_bonus or 0.3
local memory = Memory.get_crew_memory()
local surface = game.surfaces[boat.surface_name]
-- local difficulty = memory.difficulty
local enemy_force_name = boat.force_name
-- local evolution = memory.evolution_factor
local p = {boat.position.x - width/2, boat.position.y}
local units = {}
for i = 1, count do
local name = Common.get_random_unit_type(max_evo - i * 0.04)
-- local name = Common.get_random_unit_type(evolution + i/15 * max_evolution_bonus)
-- local name = Common.get_random_unit_type(evolution + 3 * i/100)
local p2 = surface.find_non_colliding_position('wooden-chest', p, 8, 0.5) --needs to be wooden-chest for collisions to work properly
if p2 then
local biter = surface.create_entity({name = name, force = enemy_force_name, position = p2})
memory.scripted_biters[biter.unit_number] = {entity = biter, created_at = game.tick}
units[#units + 1] = biter
end
end
if #units > 0 then
local unit_group = surface.create_unit_group({position = p, force = enemy_force_name})
for _, unit in pairs(units) do
unit_group.add_member(unit)
end
boat.unit_group = {ref = unit_group, script_type = 'landing-party'}
end
end
function Public.update_landing_party_unit_groups(boat, step_distance)
local memory = Memory.get_crew_memory()
-- move unit groups:
local group = boat.unit_group
local surface = game.surfaces[boat.surface_name]
if not (group and surface and surface.valid) then return end
local groupref = group.ref
if not (groupref and groupref.valid) then return end
local p2 = groupref.position
if not p2 then return end
local enemy_force_name = memory.enemy_force_name
local m = groupref.members
groupref.destroy()
local new_group = surface.create_unit_group({position = {x = p2.x + step_distance, y = p2.y}, force = enemy_force_name})
boat.unit_group = {ref = new_group, script_type = 'landing-party'}
for i = 1, #m do
local b = m[i]
new_group.add_member(b)
end
-- if boat.spawner and boat.spawner.valid then
-- new_group.set_command(Public.move_to(boat.spawner.position))
-- end
end
-- function Public.destroy_inactive_scripted_biters()
-- local memory = Memory.get_crew_memory()
-- local floating_pollution_accrued = 0
-- for unit_number, biter in pairs(memory.scripted_biters) do
-- if Public.is_biter_inactive(biter) then
-- memory.floating_pollution = memory.floating_pollution + CoreData.biterPollutionValues[biter.entity.name]
-- floating_pollution_accrued = floating_pollution_accrued + CoreData.biterPollutionValues[biter.entity.name]
-- memory.scripted_biters[unit_number] = nil
-- end
-- end
-- if _DEBUG and floating_pollution_accrued > 0 then game.print(game.tick .. string.format(":%f of spare pollution accrued", floating_pollution_accrued)) end
-- end
--=== Data
return Public

614
maps/pirates/balance.lua Normal file
View File

@ -0,0 +1,614 @@
local Public = {}
local Math = require 'maps.pirates.math'
-- local Memory = require 'maps.pirates.memory'
local Common = require 'maps.pirates.common'
local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
-- this file is an API to all the balance tuning knobs
Public.base_extra_character_speed = 0.20
Public.technology_price_multiplier = 1
function Public.starting_boatEEIpower_production_MW()
-- return 3 * Math.sloped(Common.capacity_scale(), 1/2) / 2 --/2 as we have 2
return 3/2
end
function Public.starting_boatEEIelectric_buffer_size_MJ() --maybe needs to be at least the power_production
-- return 3 * Math.sloped(Common.capacity_scale(), 1/2) / 2 --/2 as we have 2
return 3/2
end
Public.EEI_stages = { --multipliers
1,2,4,7,11
}
function Public.scripted_biters_pollution_cost_multiplier()
return 1.3 --tuned
end
function Public.cost_to_leave_multiplier()
-- return Math.sloped(Common.difficulty(), 7/10) --should scale with difficulty similar to, but slightly slower than, passive fuel depletion rate --Edit: not sure about this?
-- return Math.sloped(Common.difficulty(), 9/10)
-- extra factor now that the cost scales with time:
return Math.sloped(Common.difficulty(), 8/10)
end
Public.rocket_launch_coin_reward = 5000
function Public.crew_scale()
local ret = Common.activecrewcount()/10
if ret == 0 then ret = 1/10 end --if all players are afk
if ret > 2.4 then ret = 2.4 end --we have to cap this because you need time to mine the ore... and big crews are a mess anyway. currently this value matches the 24 player cap
return ret
end
function Public.silo_base_est_time()
local T = Public.expected_time_on_island() * Public.crew_scale()^(2/10) --to undo some of the time scaling
local est_secs
if T > 0 then
est_secs = T/6
else
est_secs = 60 * 6
end
if Common.overworldx() == 0 then est_secs = 60 * 2 end
return est_secs
end
function Public.time_quest_seconds()
return 2.8 * Public.silo_base_est_time()
end
function Public.silo_energy_needed_MJ()
local est_secs = Public.silo_base_est_time()
local est_base_power = 2*Public.starting_boatEEIpower_production_MW() * (1 + 0.05 * (Common.overworldx()/40)^(5/3))
return est_secs * est_base_power
-- return est_secs * est_base_power * Math.sloped(Common.difficulty(), 1/3)
end
function Public.silo_count()
local E = Public.silo_energy_needed_MJ()
return Math.ceil(E/(16.8 * 300)) --no more than this many seconds to charge it. Players can in fact go even faster using beacons
-- return Math.ceil(E/(16.8 * 210)) --no more than this many seconds to charge it. Players can in fact go even faster using beacons
end
function Public.game_slowness_scale()
-- return 1 / Public.crew_scale()^(55/100) / Math.sloped(Common.difficulty(), 1/4) --changed crew_scale factor significantly to help smaller crews
return 1 / Public.crew_scale()^(50/100) / Math.sloped(Common.difficulty(), 1/4) --changed crew_scale factor significantly to help smaller crews
end
function Public.max_time_on_island_formula() --always >0 --tuned
return 60 * (
-- (32 + 2.2 * (Common.overworldx()/40)^(1/3))
(33 + 0.2 * (Common.overworldx()/40)^(1/3)) --based on observing x=2000, lets try killing the extra time
) * Public.game_slowness_scale()
end
function Public.max_time_on_island()
if Common.overworldx() == 0 or ((Common.overworldx()/40) > 20) then
-- if Common.overworldx() == 0 or ((Common.overworldx()/40) > 20 and (Common.overworldx()/40) < 25) then
return -1
else
return Math.ceil(Public.max_time_on_island_formula())
end
end
Public.expected_time_fraction = 3/5
function Public.expected_time_on_island() --always >0
return Public.expected_time_fraction * Public.max_time_on_island_formula()
end
function Public.fuel_depletion_rate_static()
if (not Common.overworldx()) then return 0 end
local T = Public.expected_time_on_island()
local rate
if Common.overworldx() > 0 then
rate = 550 * (0 + (Common.overworldx()/40)^(9/10)) * Public.crew_scale()^(1/7) * Math.sloped(Common.difficulty(), 65/100) / T --most of the crewsize dependence is through T, i.e. the coal cost per island stays the same... but the extra player dependency accounts for the fact that even in compressed time, more players seem to get more resources per island
else
rate = 0
end
return -rate
end
function Public.fuel_depletion_rate_sailing()
if (not Common.overworldx()) then return 0 end
return - 7.35 * (1 + 0.135 * (Common.overworldx()/40)^(100/100)) * Math.sloped(Common.difficulty(), 1/20) --shouldn't depend on difficulty much if at all, as available resources don't depend much on difficulty
end
function Public.silo_total_pollution()
return (
365 * (Common.difficulty()^(1.2)) * Public.crew_scale()^(2/5) * (3.2 + 0.7 * (Common.overworldx()/40)^(1.6)) --shape of the curve with x is tuned
)
end
function Public.boat_passive_pollution_per_minute(time)
local boost = 1
local T = Public.max_time_on_island_formula()
if (Common.overworldx()/40) > 25 then T = T * 0.9 end
if time then
if time >= 100/100 * T then --will still happen regularly, on islands without an auto-undock timer
boost = 20
elseif time >= 95/100 * T then
boost = 16
elseif time >= 90/100 * T then
boost = 12
elseif time >= 85/100 * T then
boost = 8
elseif time >= 80/100 * T then
boost = 5
elseif time >= 70/100 * T then
boost = 3
elseif time >= 60/100 * T then
boost = 2
elseif time >= 50/100 * T then
boost = 1.5
end
end
return boost * (
2.73 * Common.difficulty() * (Common.overworldx()/40)^(1.8) * (Public.crew_scale())^(55/100)
) -- There is no _explicit_ T dependence, but it depends almost the same way on the crew_scale as T does.
end
function Public.base_evolution_leagues(leagues)
local evo
local overworldx = leagues
if overworldx == 0 then
evo = 0
else
evo = (0.0201 * (overworldx/40)) * Math.sloped(Common.difficulty(), 1/5)
if overworldx > 600 and overworldx < 1000 then
evo = evo + (0.0025 * (overworldx - 600)/40)
elseif overworldx > 1000 then
evo = evo + 0.0025 * 10
end --extra slope from 600 to 1000 adds 2.5% evo
end
return evo
end
function Public.expected_time_evo()
return 0.14
end
function Public.evolution_per_second()
local destination = Common.current_destination()
local T = Public.expected_time_on_island() --always greater than 0
local rate = Public.expected_time_evo() / T
if Common.overworldx() == 0 then rate = 0 end
-- scale by biter nests remaining:
if destination and destination.dynamic_data then
local initial_spawner_count = destination.dynamic_data.initial_spawner_count
if initial_spawner_count and initial_spawner_count > 0 then
local surface = game.surfaces[destination.surface_name]
if surface and surface.valid then
rate = rate * Common.spawner_count(surface) / destination.dynamic_data.initial_spawner_count
end
end
end
-- if _DEBUG then
-- local surface = game.surfaces[destination.surface_name]
-- game.print(Common.spawner_count(surface) .. ' ' .. destination.dynamic_data.initial_spawner_count)
-- end
return rate
end
function Public.evolution_per_nest_kill() --it's important to have evo go up with biter base kills, to provide resistance if you try to plow through all the bases
local destination = Common.current_destination()
if Common.overworldx() == 0 then return 0 end
if destination and destination.dynamic_data and destination.dynamic_data.timer and destination.dynamic_data.timer > 0 and destination.dynamic_data.initial_spawner_count and destination.dynamic_data.initial_spawner_count > 0 then
local initial_spawner_count = destination.dynamic_data.initial_spawner_count
local time = destination.dynamic_data.timer
-- local time_to_jump_to = Public.expected_time_on_island() * ((1/Public.expected_time_fraction)^(2/3))
local time_to_jump_to = Public.max_time_on_island_formula()
if time > time_to_jump_to then return 0
else
-- evo it 'would have' contributed:
return (1/initial_spawner_count) * Public.expected_time_evo() * (time_to_jump_to - time)/time_to_jump_to
end
else
return 0
end
-- return 0.003 * Common.difficulty()
end
function Public.evolution_per_full_silo_charge()
--too low and you always charge immediately, too high and you always charge late
-- return 0.05
-- observed x=2000 run, changed this to:
return 0.05 + 0.03 * Common.overworldx()/1000
end
function Public.bonus_damage_to_humans()
local ret = 0.025
local diff = Common.difficulty()
if diff <= 0.7 then ret = 0 end
if diff >= 1.3 then ret = 0.050 end
return ret
end
function Public.periodic_free_resources_per_x()
return {
}
-- return {
-- {name = 'iron-plate', count = Math.ceil(5 * (Common.overworldx()/40)^(2/3))},
-- {name = 'copper-plate', count = Math.ceil(1 * (Common.overworldx()/40)^(2/3))},
-- }
end
function Public.periodic_free_resources_per_destination_5_seconds()
return {
}
-- return {
-- {name = 'iron-ore', count = Math.ceil(7 * (Common.overworldx()/40)^(0.6))},
-- {name = 'copper-ore', count = Math.ceil(3 * (Common.overworldx()/40)^(0.6))},
-- }
end
function Public.class_resource_scale()
return 1 / (Public.crew_scale()^(2/5)) --already helped by longer timescales
end
function Public.biter_base_density_scale()
local p = Public.crew_scale()
if p >= 1 then
return p^(1/2)
else
return Math.max((p*10/6)^(1/2), 0.6)
end
end
function Public.launch_fuel_reward()
return Math.ceil(1000 * (1 + 0.13 * (Common.overworldx()/40)^(9/10)))
-- return Math.ceil(1000 * (1 + 0.1 * (Common.overworldx()/40)^(8/10)) / Math.sloped(Common.difficulty(), 1/4))
end
function Public.quest_reward_multiplier()
return (0.4 + 0.08 * (Common.overworldx()/40)^(8/10)) * Math.sloped(Common.difficulty(), 1/3) * (Public.crew_scale())^(1/8)
end
function Public.island_richness_avg_multiplier()
local ret
-- local base = 0.7 + 0.1 * (Common.overworldx()/40)^(7/10) --tuned tbh
local base = 0.73 + 0.110 * (Common.overworldx()/40)^(7/10) --tuned tbh
ret = base * Math.sloped(Public.crew_scale(), 1/40) --we don't really have resources scaling by player count in this resource-constrained scenario, but we scale a little, to accommodate each player filling their inventory with useful tools. also, I would do higher than 1/40, but we go even slightly lower because we're applying this somewhat sooner than players actually get there.
return ret
end
function Public.resource_quest_multiplier()
return (1.0 + 0.075 * (Common.overworldx()/40)^(8/10)) * Math.sloped(Common.difficulty(), 1/3) * (Public.crew_scale())^(1/8)
end
function Public.apply_crew_buffs_per_x(force)
force.laboratory_productivity_bonus = Math.max(0, 7/100 * (Common.overworldx()/40) - (10*(Common.difficulty()) - 5)) --difficulty causes lab productivity boosts to start later
end
function Public.class_cost()
return 8000
-- return Math.ceil(10000 / (Public.crew_scale()*10/4)^(1/6))
end
Public.covered_first_appears_at = 40
Public.starting_fuel = 4000
Public.silo_max_hp = 8000
function Public.pistol_damage_multiplier() return 2.25 end --2.0 slightly too low, 2.5 causes players to yell at each other for not using pistol
Public.kraken_spawns_base_extra_evo = 0.35
function Public.kraken_evo_increase_per_shot()
return 1/100 * 0.07
end
function Public.sandworm_evo_increase_per_spawn()
if _DEBUG then
return 1/100
else
return 1/100 * 1/8 * Math.sloped(Common.difficulty(), 3/5)
end
end
function Public.kraken_kill_reward()
return {{name = 'sulfuric-acid-barrel', count = 10}}
end
function Public.kraken_health()
return Math.ceil(3500 * Math.max(1, 1 + 0.08 * ((Common.overworldx()/40)^(13/10)-6)) * (Public.crew_scale()^(5/8)) * Math.sloped(Common.difficulty(), 3/4))
end
Public.kraken_regen_scale = 0.1 --starting off low
function Public.krakens_per_slot()
local rng = Math.random()
if rng < 0.03 then
return 2
elseif rng < 0.25 then
return 1
else
return 0
end
end
function Public.krakens_per_free_slot(overworldx)
local rng = Math.random()
local multiplier = 1
if overworldx and overworldx > 600 then
multiplier = 1 + (overworldx-600)/600
end
if rng < 0.0025 * multiplier then
return 3
elseif rng < 0.075 * multiplier then
return 1
elseif rng < 0.5 * multiplier then
return 1
else
return 0
end
end
function Public.main_shop_cost_multiplier()
return 1
end
function Public.barter_decay_parameter()
return 0.95
end
function Public.sandworm_speed()
return 6.4 * Math.sloped(Common.difficulty(), 1/5)
end
-- function Public.island_otherresources_prospect_decay_parameter()
-- return 0.95
-- end
Public.research_buffs = { --currently disabled anyway
-- these already give .1 productivity so we're adding .1 to get to 20%
['mining-productivity-1'] = {['mining-drill-productivity-bonus'] = .1},
['mining-productivity-2'] = {['mining-drill-productivity-bonus'] = .1},
['mining-productivity-3'] = {['mining-drill-productivity-bonus'] = .1},
['mining-productivity-4'] = {['mining-drill-productivity-bonus'] = .1},
-- -- these already give .1 productivity so we're adding .1 to get to 20%
-- ['mining-productivity-1'] = {['mining-drill-productivity-bonus'] = .1, ['character-inventory-slots-bonus'] = 5},
-- ['mining-productivity-2'] = {['mining-drill-productivity-bonus'] = .1, ['character-inventory-slots-bonus'] = 5},
-- ['mining-productivity-3'] = {['mining-drill-productivity-bonus'] = .1, ['character-inventory-slots-bonus'] = 5},
-- ['mining-productivity-4'] = {['mining-drill-productivity-bonus'] = .1, ['character-inventory-slots-bonus'] = 5},
}
function Public.flamers_tech_multipliers()
return 0.8
end
function Public.flamers_base_nerf()
return -0.2
end
function Public.player_ammo_damage_modifiers() -- modifiers are fractional. bullet affects gun turrets, but flamethrower does not affect flamer turrets
local data = {
['artillery-shell'] = 0,
['biological'] = 0,
['bullet'] = 0.1,
['cannon-shell'] = 0,
['capsule'] = 0,
['electric'] = 0,
['flamethrower'] = 0, --these nerfs are elsewhere for finer control
['grenade'] = -0.05,
['landmine'] = 0,
['melee'] = 0, -- doesn't do anything apparently
['rocket'] = 0,
['shotgun-shell'] = 0
}
return data
end
function Public.player_turret_attack_modifiers()
local data = {
['gun-turret'] = 0,
['artillery-turret'] = 0,
['laser-turret'] = 0,
}
return data
end
function Public.player_gun_speed_modifiers()
local data = {
['artillery-shell'] = 0,
['biological'] = 0,
['bullet'] = 0,
['cannon-shell'] = 0,
['capsule'] = 0,
['electric'] = 0,
['flamethrower'] = 0, --these nerfs are elsewhere for finer control
['grenade'] = -0.25,
['landmine'] = 0,
['melee'] = 0, -- doesn't do anything apparently
['rocket'] = 0,
['shotgun-shell'] = 0
}
return data
end
Public.starting_items_player = {['pistol'] = 1, ['firearm-magazine'] = 12, ['raw-fish'] = 1, ['iron-plate'] = 12, ['medium-electric-pole'] = 4}
Public.starting_items_player_late = {['pistol'] = 1, ['firearm-magazine'] = 5}
function Public.starting_items_crew_upstairs()
return {
{['steel-plate'] = 38},
{['stone-brick'] = 60},
{['grenade'] = 3},
{['shotgun'] = 2, ['shotgun-shell'] = 36},
-- {['raw-fish'] = 5},
{['coin'] = 1000},
}
end
function Public.starting_items_crew_downstairs()
return {
{['transport-belt'] = Math.random(600,650)},
{['underground-belt'] = 80},
{['splitter'] = Math.random(50,56)},
{['inserter'] = Math.random(120,140)},
{['storage-tank'] = 4},
{['medium-electric-pole'] = Math.random(15,21)},
{['coin'] = 2000},
{['solar-panel'] = 3},
{['accumulator'] = 1},
}
end
function Public.covered_entry_price_scale()
return 0.85 * (1 + 0.033 * (Common.overworldx()/40 - 1)) * ((1 + Public.crew_scale())^(1/3)) * Math.sloped(Common.difficulty(), 1/2) --whilst resource scales tend to be held fixed with crew size, we account slightly for the fact that more players tend to handcraft more
end
-- if the prices are too high, players will accidentally throw too much in when they can't do it
Public.covered1_entry_price_data_raw = { --watch out that the raw_materials chest can only hold e.g. 4.8 iron-plates
-- choose things that are easy to make at outposts
{1, 0, 1, false, {
price = {name = 'iron-stick', count = 1500},
raw_materials = {{name = 'iron-plate', count = 750}}}, {}},
{0.85, 0, 1, false, {
price = {name = 'copper-cable', count = 1500},
raw_materials = {{name = 'copper-plate', count = 750}}}, {}},
{1, 0, 0.3, false, {
price = {name = 'small-electric-pole', count = 450},
raw_materials = {{name = 'copper-plate', count = 900}}}, {}},
{1, 0.1, 1, false, {
price = {name = 'assembling-machine-1', count = 80},
raw_materials = {{name = 'iron-plate', count = 1760}, {name = 'copper-plate', count = 360}}}, {}},
{1, 0, 0.15, false, {
price = {name = 'burner-mining-drill', count = 150},
raw_materials = {{name = 'iron-plate', count = 1350}}}, {}},
{0.75, 0, 0.6, false, {
price = {name = 'burner-inserter', count = 300},
raw_materials = {{name = 'iron-plate', count = 900}}}, {}},
{1, 0.05, 0.7, false, {
price = {name = 'small-lamp', count = 300},
raw_materials = {{name = 'iron-plate', count = 600}, {name = 'copper-plate', count = 900}}}, {}},
{1, 0, 1, false, {
price = {name = 'firearm-magazine', count = 700},
raw_materials = {{name = 'iron-plate', count = 2800}}}, {}},
{1, 0, 1, false, {
price = {name = 'constant-combinator', count = 276},
raw_materials = {{name = 'iron-plate', count = 552}, {name = 'copper-plate', count = 1518}}}, {}},
{1, 0.05, 1, false, {
price = {name = 'stone-furnace', count = 250},
raw_materials = {}}, {}},
{1, 0.4, 1.6, true, {
price = {name = 'advanced-circuit', count = 180},
raw_materials = {{name = 'iron-plate', count = 360}, {name = 'copper-plate', count = 900}, {name = 'plastic-bar', count = 360}}}, {}},
{0.5, -0.5, 0.5, true, {
price = {name = 'wooden-chest', count = 400},
raw_materials = {}}, {}},
{0.5, 0, 1, true, {
price = {name = 'iron-chest', count = 250},
raw_materials = {{name = 'iron-plate', count = 2000}}}, {}},
{0.5, 0.25, 1.75, true, {
price = {name = 'steel-chest', count = 125},
raw_materials = {{name = 'steel-plate', count = 1000}}}, {}},
}
function Public.covered1_entry_price_data()
local ret = {}
local data = Public.covered1_entry_price_data_raw
for i = 1, #data do
local data_item = data[i]
ret[#ret + 1] = {
weight = data_item[1],
game_completion_progress_min = data_item[2],
game_completion_progress_max = data_item[3],
scaling = data_item[4],
item = data_item[5],
map_subtypes = data_item[6],
}
end
return ret
end
function Public.covered1_entry_price()
-- local rng = Math.random()
-- local memory = Memory.get_crew_memory()
-- local overworldx = memory.overworldx or 0
local game_completion_progress = Math.max(Math.min(Math.sloped(Common.difficulty(),1/2) * Common.game_completion_progress(), 1), 0)
local data = Public.covered1_entry_price_data()
local types, weights = {}, {}
for i = 1, #data, 1 do
table.insert(types, data[i].item)
local destination = Common.current_destination()
if not (data[i].map_subtypes and #data[i].map_subtypes > 0 and destination and destination.subtype and data[i].map_subtypes and (not Utils.contains(data[i].map_subtypes, destination.subtype))) then
if data[i].scaling then -- scale down weights away from the midpoint 'peak' (without changing the mean)
local midpoint = (data[i].game_completion_progress_max + data[i].game_completion_progress_min) / 2
local difference = (data[i].game_completion_progress_max - data[i].game_completion_progress_min)
table.insert(weights, data[i].weight * Math.max(0, 1 - (Math.abs(game_completion_progress - midpoint) / (difference / 2))))
else -- no scaling
if data[i].game_completion_progress_min <= game_completion_progress and data[i].game_completion_progress_max >= game_completion_progress then
table.insert(weights, data[i].weight)
else
table.insert(weights, 0)
end
end
end
end
local res = Utils.deepcopy(Math.raffle(types, weights))
res.price.count = Math.ceil(res.price.count * Public.covered_entry_price_scale())
for i, _ in pairs(res.raw_materials) do
res.raw_materials[i].count = Math.ceil(res.raw_materials[i].count * Public.covered_entry_price_scale() * (0.9 + 0.2 * Math.random()))
end
return res
end
return Public

View File

@ -0,0 +1,66 @@
local Memory = require 'maps.pirates.memory'
-- local Roles = require 'maps.pirates.roles.roles'
-- local Balance = require 'maps.pirates.balance'
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
-- local Loot = require 'maps.pirates.loot'
local _inspect = require 'utils.inspect'.inspect
-- local Structures = require 'maps.pirates.structures.structures'
-- local Surfaces = require 'maps.pirates.surfaces.surfaces'
local Boats = require 'maps.pirates.structures.boats.boats'
local Hold = require 'maps.pirates.surfaces.hold'
local Public = {}
-- September 2021: Reworking the game so that you start on a 'sloop with hold', but can customize the ship with upgrades.
-- I'm thinking these can start by simply being shop icons.
-- In the hold, we can extend the hold size by placing tiles. Perhaps the space that is extended could be random, as usual to dissuade repetitive builds.
-- For the deck, we don't really want to do that. It's probably safest to stick to things like upgrading the accumulator.
local enum = {
EXTRA_HOLD = 'extra_hold',
MORE_POWER = 'upgrade_power',
UNLOCK_MERCHANTS = 'unlock_merchants',
ROCKETS_FOR_SALE = 'rockets_for_sale',
}
Public.enum = enum
Public.List = {
enum.EXTRA_HOLD,
enum.MORE_POWER,
enum.UNLOCK_MERCHANTS,
enum.ROCKETS_FOR_SALE,
}
Public.crowsnest_display_form = {
[enum.EXTRA_HOLD] = 'Extra Hold',
[enum.MORE_POWER] = 'Power',
[enum.UNLOCK_MERCHANTS] = 'Unlock Merchants',
[enum.ROCKETS_FOR_SALE] = 'Unlock Rockets',
}
function Public.execute_upgade(upgrade_type)
local memory = Memory.get_crew_memory()
local boat = memory.boat
if upgrade_type == enum.EXTRA_HOLD then
Hold.add_another_hold_surface()
elseif upgrade_type == enum.MORE_POWER then
boat.EEI_stage = boat.EEI_stage + 1
Boats.update_EEIs(boat)
elseif upgrade_type == enum.UNLOCK_MERCHANTS then
memory.merchant_ships_unlocked = true
elseif upgrade_type == enum.ROCKETS_FOR_SALE then
memory.rockets_for_sale = true
end
end
return Public

969
maps/pirates/commands.lua Normal file
View File

@ -0,0 +1,969 @@
--luacheck: ignore
--luacheck ignores because mass requires is a code templating choice...
local Color = require 'utils.color_presets'
local Server = require 'utils.server'
local Math = require 'maps.pirates.math'
local Ai = require 'maps.pirates.ai'
local Memory = require 'maps.pirates.memory'
local Gui = require 'maps.pirates.gui.gui'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local PlayerColors = require 'maps.pirates.player_colors'
local Utils = require 'maps.pirates.utils_local'
local Balance = require 'maps.pirates.balance'
local Crew = require 'maps.pirates.crew'
local Roles = require 'maps.pirates.roles.roles'
local Structures = require 'maps.pirates.structures.structures'
local Boats = require 'maps.pirates.structures.boats.boats'
local Surfaces = require 'maps.pirates.surfaces.surfaces'
local Overworld = require 'maps.pirates.overworld'
local Islands = require 'maps.pirates.surfaces.islands.islands'
local Progression = require 'maps.pirates.progression'
local Crowsnest = require 'maps.pirates.surfaces.crowsnest'
local Hold = require 'maps.pirates.surfaces.hold'
local Interface = require 'maps.pirates.interface'
local Upgrades = require 'maps.pirates.boat_upgrades'
local Effects = require 'maps.pirates.effects'
local Kraken = require 'maps.pirates.surfaces.sea.kraken'
local _inspect = require 'utils.inspect'.inspect
local simplex_noise = require 'utils.simplex_noise'.d2
local Token = require 'utils.token'
local Task = require 'utils.task'
local Highscore = require 'maps.pirates.highscore'
local CustomEvents = require 'maps.pirates.custom_events'
local Classes = require 'maps.pirates.roles.classes'
local GUIcolor = require 'maps.pirates.gui.color'
commands.add_command(
'ok',
'is used to accept captainhood.',
function(cmd)
local player = game.players[cmd.player_index]
if not Common.validate_player(player) then return end
local crew_id = tonumber(string.sub(game.players[cmd.player_index].force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
Roles.player_confirm_captainhood(player)
end)
-- Disabled for information-flow reasons:
-- commands.add_command(
-- 'classes',
-- 'Prints the available classes in the game.',
-- function(cmd)
-- local player = game.players[cmd.player_index]
-- if not Common.validate_player(player) then return end
-- player.print('[color=gray]' .. Roles.get_classes_print_string() .. '[/color]')
-- end)
commands.add_command(
'classinfo',
'{classname} returns the definition of the named class.',
function(cmd)
local param = tostring(cmd.parameter)
local player = game.players[cmd.player_index]
if not Common.validate_player(player) then return end
if param and param ~= 'nil' then
local string = Roles.get_class_print_string(param)
if string then
Common.notify_player_expected(player, 'Class definition for ' .. string)
else
Common.notify_player_error(player, 'Command error: Class \'' .. param .. '\' not found.')
end
else
Common.notify_player_expected(player, '/classinfo {classname} returns the definition of the named class.')
end
end)
commands.add_command(
'take',
'{classname} takes a spare class with the given name for yourself.',
function(cmd)
local param = tostring(cmd.parameter)
local player = game.players[cmd.player_index]
if not Common.validate_player(player) then return end
if param and param ~= 'nil' then
for _, class in ipairs(Classes.Class_List) do
if Classes.display_form[class]:lower() == param:lower() then
Classes.assign_class(player.index, class, true)
return true
end
end
--fallthrough:
Common.notify_player_error(player, 'Command error: Class \'' .. param .. '\' not found.')
return false
else
Common.notify_player_expected(player, '/take {classname} takes a spare class with the given name for yourself.')
end
end)
commands.add_command(
'giveup',
'gives up your current class, making it available for others.',
function(cmd)
local param = tostring(cmd.parameter)
local player = game.players[cmd.player_index]
if not Common.validate_player(player) then return end
if param and param == 'nil' then
Classes.try_renounce_class(player, true)
else
Common.notify_player_error(player, 'Command error: parameter not needed.')
end
end)
commands.add_command(
'ccolor',
'is an extension to the built-in /color command, with more colors.',
function(cmd)
local param = tostring(cmd.parameter)
local player_index = cmd.player_index
if player_index then
local player = game.players[player_index]
if player and player.valid then
if cmd.parameter then
if PlayerColors.colors[param] then
local rgb = PlayerColors.colors[param]
player.color = rgb
player.chat_color = rgb
local message = '[color=' .. rgb.r .. ',' .. rgb.g .. ',' .. rgb.b .. ']' .. player.name .. ' chose the color ' .. param .. '[/color] (via /ccolor).'
Common.notify_game(message)
else
Common.notify_player_error(player, 'Command error: Color \'' .. param .. '\' not found.')
end
else
local color = PlayerColors.bright_color_names[Math.random(#PlayerColors.bright_color_names)]
local rgb = PlayerColors.colors[color]
if not rgb then return end
player.color = rgb
player.chat_color = rgb
local message = '[color=' .. rgb.r .. ',' .. rgb.g .. ',' .. rgb.b .. ']' .. player.name .. '\'s color randomized to ' .. color .. '[/color] (via /ccolor).' --'randomly became' was amusing, but let's not
Common.notify_game(message)
-- disabled due to lag:
-- GUIcolor.toggle_window(player)
end
end
end
end)
local go_2 = Token.register(
function(data)
Memory.set_working_id(1)
local memory = Memory.get_crew_memory()
memory.mapbeingloadeddestination_index = 1
memory.loadingticks = 0
local surface = game.surfaces[Common.current_destination().surface_name]
-- surface.request_to_generate_chunks({x = 0, y = 0}, 10)
-- surface.force_generate_chunk_requests()
Progression.go_from_starting_dock_to_first_destination()
end
)
local go_1 = Token.register(
function(data)
Memory.set_working_id(1)
local memory = Memory.get_crew_memory()
Overworld.ensure_lane_generated_up_to(0, Crowsnest.Data.visibilitywidth/2)
Overworld.ensure_lane_generated_up_to(24, Crowsnest.Data.visibilitywidth/2)
Overworld.ensure_lane_generated_up_to(-24, Crowsnest.Data.visibilitywidth/2)
memory.currentdestination_index = 1
script.raise_event(CustomEvents.enum['update_crew_progress_gui'], {})
Surfaces.create_surface(Common.current_destination())
Task.set_timeout_in_ticks(60, go_2, {})
end
)
local function check_admin(cmd)
local Session = require 'utils.datastore.session_data'
local player = game.players[cmd.player_index]
local trusted = Session.get_trusted_table()
local p
if player then
if player ~= nil then
p = player.print
if not player.admin then
p('[ERROR] Only admins are allowed to run this command!', Color.fail)
return false
end
else
p = log
end
end
return true
end
local function check_captain(cmd)
local player = game.players[cmd.player_index]
local p
if player then
if player ~= nil then
p = player.print
if not Common.validate_player(player) then return end
local crew_id = tonumber(string.sub(game.players[cmd.player_index].force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
if not (Roles.player_privilege_level(player) >= Roles.privilege_levels.CAPTAIN) then
p('[ERROR] Only captains are allowed to run this command!', Color.fail)
return false
end
else
p = log
end
end
return true
end
local function check_captain_or_admin(cmd)
local player = game.players[cmd.player_index]
local p
if player then
if player ~= nil then
p = player.print
if not Common.validate_player(player) then return end
local crew_id = tonumber(string.sub(game.players[cmd.player_index].force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
if not (player.admin or Roles.player_privilege_level(player) >= Roles.privilege_levels.CAPTAIN) then
p('[ERROR] Only captains are allowed to run this command!', Color.fail)
return false
end
else
p = log
end
end
return true
end
local function check_trusted(cmd)
local Session = require 'utils.datastore.session_data'
local player = game.players[cmd.player_index]
local trusted = Session.get_trusted_table()
local p
if player then
if player ~= nil then
p = player.print
if not (trusted[player.name] or player.admin) then
p('[ERROR] Only admins and trusted weebs are allowed to run this command!', Color.fail)
return false
end
else
p = log
end
end
return true
end
commands.add_command(
'set_max_crews',
'is an admin command to set the maximum number of concurrent crews allowed on the server.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local global_memory = Memory.get_global_memory()
if tonumber(param) then
global_memory.active_crews_cap = tonumber(param)
Common.notify_player_expected(player, 'The maximum number of concurrent crews has been set to ' .. param .. '.')
end
end
end)
commands.add_command(
'plank',
'is a captain command to remove a player by making them a spectator.',
function(cmd)
local player = game.players[cmd.player_index]
local param = tostring(cmd.parameter)
if check_captain_or_admin(cmd) then
if param and game.players[param] and game.players[param].index then
Crew.plank(player, game.players[param])
else
Common.notify_player_error(player, 'Command error: Invalid player name.')
end
end
end)
commands.add_command(
'officer',
'is a captain command to make a player into an officer, or remove them as one.',
function(cmd)
local player = game.players[cmd.player_index]
local param = tostring(cmd.parameter)
if check_captain_or_admin(cmd) then
local memory = Memory.get_crew_memory()
if param and game.players[param] and game.players[param].index then
if memory.officers_table and memory.officers_table[game.players[param].index] then
Roles.unmake_officer(player, game.players[param])
else
Roles.make_officer(player, game.players[param])
end
else
Common.notify_player_error(player, 'Command error: Invalid player name.')
end
end
end)
commands.add_command(
'undock',
'is a captain command to undock the ship.',
function(cmd)
local param = tostring(cmd.parameter)
if check_captain_or_admin(cmd) then
local player = game.players[cmd.player_index]
local memory = Memory.get_crew_memory()
if memory.boat.state == Boats.enum_state.DOCKED then
Progression.undock_from_dock(true)
elseif memory.boat.state == Boats.enum_state.LANDED then
Progression.try_retreat_from_island(player, true)
end
end
end)
commands.add_command(
'req',
'is a captain command to take \'important\' items from the crew into your inventory.',
function(cmd)
local param = tostring(cmd.parameter)
if check_captain(cmd) then
local player = game.players[cmd.player_index]
local memory = Memory.get_crew_memory()
Roles.captain_requisition(memory.playerindex_captain)
end
end)
commands.add_command(
'dump_highscores',
'is a dev command.',
function(cmd)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
if not Common.validate_player(player) then return end
local crew_id = tonumber(string.sub(game.players[cmd.player_index].force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
Highscore.dump_highscores()
player.print('Highscores dumped.')
end
end)
commands.add_command(
'setcaptain',
'{player} is an admin command to set the crew\'s captain to {player}.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
if param and game.players[param] and game.players[param].index then
Roles.make_captain(game.players[param])
else
Common.notify_player_error(player, 'Command error: Invalid player name.')
end
end
end)
commands.add_command(
'summoncrew',
'is an admin command to summon the crew to the ship.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
Crew.summon_crew()
end
end)
commands.add_command(
'setclass',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
if not Common.validate_player(player) then return end
if not memory.classes_table then memory.classes_table = {} end
memory.classes_table[player.index] = tonumber(param)
player.print('Set own class to ' .. param .. '.')
end
end)
commands.add_command(
'setevo',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
Common.set_evo(tonumber(param))
end
end)
commands.add_command(
'modi',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
local surface = game.surfaces[Common.current_destination().surface_name]
local entities = surface.find_entities_filtered{position = player.position, radius = 500}
for _, e in pairs(entities) do
if e and e.valid then
-- e.force = memory.force
e.minable = true
e.destructible = true
e.rotatable = true
end
end
player.print('nearby entities made modifiable')
end
end)
commands.add_command(
'ret',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
Progression.retreat_from_island(true)
end
end)
commands.add_command(
'jump',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
Overworld.try_overworld_move_v2({x = 40, y = 0})
end
end)
commands.add_command(
'advu',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
Overworld.try_overworld_move_v2{x = 0, y = -24}
end
end)
commands.add_command(
'advd',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
Overworld.try_overworld_move_v2{x = 0, y = 24}
end
end)
commands.add_command(
'overwrite_scores_specific',
'is a dev command.',
function(cmd)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
if not Common.validate_player(player) then return end
local crew_id = tonumber(string.sub(game.players[cmd.player_index].force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
if Highscore.overwrite_scores_specific() then player.print('Highscores overwritten.') end
end
end)
if _DEBUG then
commands.add_command(
'go',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local proposal = {
capacity_option = 3,
difficulty_option = 2,
-- mode_option = 'left',
endorserindices = { 1 },
name = "AdminRun"
}
Memory.set_working_id(1)
Crew.initialise_crew(proposal)
Crew.initialise_crowsnest() --contains a Task
local memory = Memory.get_crew_memory()
local boat = Utils.deepcopy(Surfaces.Lobby.StartingBoats[memory.id])
for _, p in pairs(game.connected_players) do
p.teleport({x = -30, y = boat.position.y}, game.surfaces[boat.surface_name])
end
Progression.set_off_from_starting_dock()
-- local memory = Memory.get_crew_memory()
-- local boat = Utils.deepcopy(Surfaces.Lobby.StartingBoats[memory.id])
-- memory.boat = boat
-- boat.dockedposition = boat.position
-- boat.decksteeringchests = {}
-- boat.crowsneststeeringchests = {}
Task.set_timeout_in_ticks(120, go_1, {})
end
end)
commands.add_command(
'chnk',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
for i = 0, 13 do
for j = 0, 13 do
Interface.event_on_chunk_generated({surface = player.surface, area = {left_top = {x = -7 * 32 + i * 32, y = -7 * 32 + j * 32}}})
end
end
game.print('chunks generated')
end
end)
commands.add_command(
'spd',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
memory.boat.speed = 60
end
end)
commands.add_command(
'stp',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
memory.boat.speed = 0
end
end)
commands.add_command(
'rms',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local rms = 0
local n = 100000
local seed = Math.random(n^2)
for i = 1,n do
local noise = simplex_noise(i, 7.11, seed)
rms = rms + noise^2
end
rms = rms/n
game.print(rms)
end
end)
commands.add_command(
'pro',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local global_memory = Memory.get_global_memory()
local proposal = {
capacity_option = 3,
difficulty_option = 2,
-- mode_option = 'left',
endorserindices = { 2 },
name = "TestRun"
}
global_memory.crewproposals[#global_memory.crewproposals + 1] = proposal
end
end)
commands.add_command(
'lev',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
Memory.set_working_id(1)
local memory = Memory.get_crew_memory()
Progression.go_from_currentdestination_to_sea()
end
end)
commands.add_command(
'hld',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
Memory.set_working_id(1)
local memory = Memory.get_crew_memory()
Upgrades.execute_upgade(Upgrades.enum.EXTRA_HOLD)
end
end)
commands.add_command(
'pwr',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
Memory.set_working_id(1)
local memory = Memory.get_crew_memory()
Upgrades.execute_upgade(Upgrades.enum.MORE_POWER)
end
end)
commands.add_command(
'score',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
game.print('faking a highscore...')
Highscore.write_score(memory.secs_id, 'fakers', 0, 40, CoreData.version_float, 1, 1)
end
end)
commands.add_command(
'scrget',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
game.print('running Highscore.load_in_scores()')
Highscore.load_in_scores()
end
end)
commands.add_command(
'tim',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
Memory.set_working_id(1)
local memory = Memory.get_crew_memory()
Common.current_destination().dynamic_data.timer = 88
game.print('time set to 88 seconds')
end
end)
commands.add_command(
'gld',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
Memory.set_working_id(1)
local memory = Memory.get_crew_memory()
memory.stored_fuel = memory.stored_fuel + 20000
end
end)
commands.add_command(
'bld',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
Memory.set_working_id(1)
local memory = Memory.get_crew_memory()
memory.classes_table = {[1] = 1}
end
end)
commands.add_command(
'rad',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
Memory.set_working_id(1)
local memory = Memory.get_crew_memory()
Islands.spawn_enemy_boat(Boats.enum.RAFT)
local boat = memory.enemyboats[1]
Ai.spawn_boat_biters(boat, 0.89, Boats.get_scope(boat).Data.capacity, Boats.get_scope(boat).Data.width)
game.print('enemy boat spawned')
end
end)
commands.add_command(
'rad2',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
Memory.set_working_id(1)
local memory = Memory.get_crew_memory()
Islands.spawn_enemy_boat(Boats.enum.RAFTLARGE)
local boat = memory.enemyboats[1]
Ai.spawn_boat_biters(boat, 0.89, Boats.get_scope(boat).Data.capacity, Boats.get_scope(boat).Data.width)
game.print('large enemy boat spawned')
end
end)
commands.add_command(
'krk',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
Memory.set_working_id(1)
local memory = Memory.get_crew_memory()
Kraken.try_spawn_kraken()
end
end)
commands.add_command(
'1',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
game.speed = 1
end
end)
commands.add_command(
'2',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
game.speed = 2
end
end)
commands.add_command(
'4',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
game.speed = 4
end
end)
commands.add_command(
'8',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
game.speed = 8
end
end)
commands.add_command(
'16',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
game.speed = 16
end
end)
commands.add_command(
'32',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
game.speed = 32
end
end)
commands.add_command(
'64',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
game.speed = 64
end
end)
commands.add_command(
'ef1',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
local surface = game.surfaces[Common.current_destination().surface_name]
Effects.worm_movement_effect(surface, {x = -45, y = 0}, false, true)
end
end)
commands.add_command(
'ef2',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
local surface = game.surfaces[Common.current_destination().surface_name]
Effects.worm_movement_effect(surface, {x = -45, y = 0}, false, false)
end
end)
commands.add_command(
'ef3',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
local surface = game.surfaces[Common.current_destination().surface_name]
Effects.worm_movement_effect(surface, {x = -45, y = 0}, true, false)
end
end)
commands.add_command(
'ef4',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
local surface = game.surfaces[Common.current_destination().surface_name]
Effects.worm_emerge_effect(surface, {x = -45, y = 0})
end
end)
commands.add_command(
'ef5',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
local surface = game.surfaces[Common.current_destination().surface_name]
Effects.biters_emerge(surface, {x = -30, y = 0})
end
end)
commands.add_command(
'emoji',
'is a dev command.',
function(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
Server.to_discord_embed_raw(CoreData.comfy_emojis.monkas)
end
end)
end

1323
maps/pirates/common.lua Normal file

File diff suppressed because it is too large Load Diff

287
maps/pirates/coredata.lua Normal file
View File

@ -0,0 +1,287 @@
local Math = require 'maps.pirates.math'
local _inspect = require 'utils.inspect'.inspect
local Public = {}
Public.scenario_id_name = 'pirates'
Public.version_string = '1.1.1.5.4'
Public.version_float = 1.1154
Public.blueprint_library_allowed = true
Public.blueprint_importing_allowed = true
Public.rocket_silo_death_causes_loss = false
Public.victory_x = 1000
Public.total_max_biters = 2000
Public.lobby_surface_name = '000-000-Lobby'
Public.colors = {
coal = {r=0.5, g=0.5, b=0.5},
wood = {r=204, g=158, b=67},
stone = {r=230, g=220, b=190},
coin = {r=242, g=193, b=97},
['raw-fish'] = {r=0, g=237, b=170},
['iron-plate'] = {r=170, g=180, b=190},
['iron-ore'] = {r=170, g=180, b=190},
['copper-plate'] = {r=219, g=149, b=96},
['copper-ore'] = {r=219, g=149, b=96},
notify_error = {r=170, g=170, b=170},
notify_player_expected = {r=255, g=231, b=46},
notify_player_announce = {r=244, g=255, b=145},
notify_game = {r=249, g=103, b=56},
notify_lobby = {r=249, g=153, b=56},
notify_force = {r=249, g=153, b=56},
notify_force_light = {r=255, g=220, b=161},
parrot = {r=87, g=255, b=148},
notify_victory = {r=84, g=249, b=84},
notify_gameover = {r=249, g=84, b=84},
renderingtext_green = {r=88, g=219, b=88},
renderingtext_yellow = {r=79, g=136, b=209},
quartermaster_rendering = {r=237, g=157, b=45, a=0.15},
}
Public.static_boat_floor = 'brown-refined-concrete'
Public.moving_boat_floor = 'lab-dark-2'
Public.world_concrete_tile = 'black-refined-concrete'
Public.walkway_tile = 'orange-refined-concrete'
Public.landing_tile = 'red-refined-concrete'
Public.enemy_landing_tile = 'purple-refined-concrete'
Public.overworld_loading_tile = 'yellow-refined-concrete'
Public.overworld_presence_tile = 'green-refined-concrete'
Public.kraken_tile = 'pink-refined-concrete'
Public.enemy_units = {
'small-biter',
'small-spitter',
'medium-biter',
'medium-spitter',
'big-biter',
'big-spitter',
'behemoth-biter',
'behemoth-spitter',
}
Public.water_tile_names = {'water', 'deepwater', 'water-green', 'deepwater-green'}
Public.edgemost_tile_names = {'sand-1'}
Public.tiles_that_conflict_with_resource_layer = {'water', 'deepwater', 'water-green', 'deepwater-green', 'water-shallow', 'water-mud', 'out-of-map'}
Public.tiles_that_conflict_with_resource_layer_extended = {'water', 'deepwater', 'water-green', 'deepwater-green', 'water-shallow', 'water-mud', 'out-of-map', 'red-refined-concrete', 'brown-refined-concrete', 'orange-refined-concrete'}
Public.noworm_tile_names = {'red-refined-concrete', 'purple-refined-concrete', 'green-refined-concrete', 'orange-refined-concrete', 'brown-refined-concrete', 'lab-dark-2', 'sand-1', 'red-desert-3'}
Public.worm_solid_tile_names = {'black-refined-concrete', 'stone-path', 'concrete', 'refined-concrete', 'red-refined-concrete', 'purple-refined-concrete', 'brown-refined-concrete', 'lab-dark-2', 'sand-1', 'red-desert-3'}
Public.unteleportable_names = {'transport-belt', 'underground-belt', 'splitter', 'loader', 'fast-transport-belt', 'fast-underground-belt', 'fast-splitter', 'fast-loader', 'express-transport-belt', 'express-underground-belt', 'express-splitter', 'express-loader', 'pipe', 'pipe-to-ground', 'offshore-pump', 'chemical-plant', 'oil-refinery', 'flamethrower-turret', 'storage-tank', 'assembling-machine-2', 'assembling-machine-3', 'boiler', 'steam-engine', 'heat-exchanger', 'steam-turbine', 'pump', 'straight-rail', 'curved-rail', 'cargo-wagon', 'artillery-turret', 'electric-energy-interface', 'accumulator', 'linked-belt'}
Public.comfy_emojis = {
monkas = '<:monkas:555120573752279056>',
trashbin = '<:trashbin:835887736253710396>',
pogkot = '<:pogkot:763854655612518420>',
goldenobese = '<:goldenobese:491135683508043786>',
wut = '<:wut:493320605592977443>',
smolfish = '<:smolfish:673942701682589731>',
mjau = '<:mjau:789611417132073010>',
spurdo = '<:spurdo:669546779360100382>',
loops = '<:loops:783508194755346462>',
ree1 = '<:ree1:555118905090244618>',
derp = '<:derp:527570293850505266>',
doge = '<:doge:491152224681066496>',
yum1 = '<:yum1:740341272451219517>',
feel = '<:feel:491147760553164800>',
kewl = '<:kewl:837016976937189418>',
}
Public.capacity_options = {
{value = 2, icon = 'virtual-signal/signal-2', text = '2', text2 = '/2', text3 = '2'},
{value = 4, icon = 'virtual-signal/signal-4', text = '4', text2 = '/4', text3 = '4'},
{value = 8, icon = 'virtual-signal/signal-8', text = '8', text2 = '/8', text3 = '8'},
{value = 24, icon = 'virtual-signal/signal-blue', text = '24', text2 = '/24', text3 = '24'},
{value = 999, icon = 'virtual-signal/signal-white', text = 'Inf.', text2 = '', text3 = 'Inf'},
-- {value = 64, icon = 'item/storage-tank', text = '64'},
}
Public.difficulty_options = {
-- The difficulty values we currently offer
--For the value of Easy difficulty, we are pulled in two directions: We wish to make the game comfy to play for those who haven't played it, but we also wish to represent the game mechanics faithfully so that Normal is not a crazy distance away.
{value = 0.55, icon = 'item/firearm-magazine', text = 'Easy', associated_color = {r = 50, g = 255, b = 50}},
{value = 0.9, icon = 'item/piercing-rounds-magazine', text = 'Normal', associated_color = {r = 255, g = 255, b = 50}},
{value = 1.5, icon = 'item/uranium-rounds-magazine', text = 'Hard', associated_color = {r = 255, g = 50, b = 50}},
{value = 3, icon = 'item/atomic-bomb', text = 'Nightmare', associated_color = {r = 120, g = 35, b = 35}},
}
function Public.get_difficulty_name_from_value(difficulty_value)
-- Functions will reference this when given a difficulty value and want to present a difficulty name to the player; just make it consistent with the above
if difficulty_value <= 0.7 then
return 'Easy'
elseif difficulty_value < 1.3 then
return 'Normal'
elseif difficulty_value <= 2 then
return 'Hard'
else
return 'Nightmare'
end
end
-- Public.mode_options = {
-- left = {value = 'speedrun', icon = 'achievement/watch-your-step', text = 'Speedrun'},
-- right = {value = 'infinity', icon = 'achievement/mass-production-1', text = 'Infinity'},
-- }
Public.daynightcycle_types = {
{displayname = 'Static', 0},
{displayname = 'Slow Cyclic', ticksperday = 100000},
{displayname = 'Cyclic', ticksperday = 80000},
{displayname = 'Fast Cyclic', ticksperday = 60000},
{displayname = 'Rapid Cyclic', ticksperday = 40000},
}
Public.ore_types = {
{name = 'iron-ore', sprite_name = 'entity/iron-ore'},
{name = 'copper-ore', sprite_name = 'entity/copper-ore'},
{name = 'coal', sprite_name = 'entity/coal'},
{name = 'stone', sprite_name = 'entity/stone'},
{name = 'uranium-ore', sprite_name = 'entity/uranium-ore'},
{name = 'crude-oil', sprite_name = 'entity/crude-oil'},
}
Public.cost_items = {
{name = 'electronic-circuit', display_name = 'Electronic circuit', sprite_name = 'item/electronic-circuit', color={r=0,g=255,b=0}},
{name = 'engine-unit', display_name = 'Engine unit', sprite_name = 'item/engine-unit', color={r=255,g=255,b=0}},
{name = 'advanced-circuit', display_name = 'Advanced circuit', sprite_name = 'item/advanced-circuit', color={r=0,g=0,b=255}},
{name = 'flying-robot-frame', display_name = 'Flying robot frame', sprite_name = 'item/flying-robot-frame', color={r=0,g=255,b=255}},
{name = 'uranium-235', display_name = 'Uranium-235', sprite_name = 'item/uranium-235', color={r=0,g=255,b=0}},
{name = 'fluid-wagon', display_name = 'Fluid Wagon', sprite_name = 'item/fluid-wagon', color={r=255,g=255,b=255}},
}
Public.fallthrough_destination = {
dynamic_data = {},
static_params = {},
type = 'Lobby',
surface_name = Public.lobby_surface_name,
}
-- hacked to make spitters 25% cheaper:
Public.biterPollutionValues = {
['behemoth-biter'] = 400,
['behemoth-spitter'] = 150,
['big-biter'] = 80,
['big-spitter'] = 22,
['medium-biter'] = 20,
['medium-spitter'] = 9,
['small-biter'] = 4,
['small-spitter'] = 3
}
-- base game:
-- Public.biterPollutionValues = {
-- ['behemoth-biter'] = 400,
-- ['behemoth-spitter'] = 200,
-- ['big-biter'] = 80,
-- ['big-spitter'] = 30,
-- ['medium-biter'] = 20,
-- ['medium-spitter'] = 12,
-- ['small-biter'] = 4,
-- ['small-spitter'] = 4
-- }
Public.max_extra_seconds_at_sea = 8 * 60
Public.loco_bp_1 = [[0eNqV0ttqwzAMBuB30bVTVufsVxljpKloBYkcbLdrCH73Oi6UMrxDLm3zf7KEFjgMF5wMsQO1APWaLaj3BSyduBvWOzdPCArI4QgCuBvX06B7PWpHVwQvgPiIN1B7L/4Mmo6Gl4j0HwKQHTnCR+F4mD/5Mh7QBDNVUsCkbYhoXusEJmsFzKCqAGtDgegej2/rj76J8il+aX1EzvozWpcwm10ZVbkrfcLJ/+u0vzvF07EuTOd0dlkc0k9NJpFyI1KnkGrrZJp0R/XWyUQnLEJcFfWykgKuaGxMyGZf1K2sC5nnTVl5fwdTR+VL]]
function Public.Dock_iconized_map()
local tiles = {}
for x = -15.5, 3.5 do
for y = 19.5, 0.5, -1 do
if (y <7 and y>2 and x == -2.5)
or (y == 6.5 and x<2 and x>-6)
then
tiles[#tiles + 1] = {name = Public.walkway_tile, position = {x = x, y = y}}
elseif y < 3 - Math.abs(x+5)^2/20 then --'island'
if y < 0.5 and x<-3 and x>-7 then
tiles[#tiles + 1] = {name = 'grass-1', position = {x = x, y = y}}
elseif y < 3 + Math.abs(x+5)^2/10 then
tiles[#tiles + 1] = {name = 'dirt-3', position = {x = x, y = y}}
else
tiles[#tiles + 1] = {name = 'dry-dirt', position = {x = x, y = y}}
end
elseif y<7 then
tiles[#tiles + 1] = {name = 'water', position = {x = x, y = y}}
end
end
end
return {
tiles = tiles,
entities = {},
}
end
-- function Public.Dock_iconized_map()
-- local tiles = {}
-- for x = -15.5, 3.5 do
-- for y = -19.5, -0.5 do
-- if (y >-7 and y<-2 and x == -2.5)
-- or (y == -6.5 and x<2 and x>-6)
-- then
-- tiles[#tiles + 1] = {name = Public.walkway_tile, position = {x = x, y = y}}
-- elseif y > -3 + Math.abs(x+5)^2/20 then --'island'
-- if y > -0.5 and x<-3 and x>-7 then
-- tiles[#tiles + 1] = {name = 'grass-1', position = {x = x, y = y}}
-- elseif y > -3 + Math.abs(x+5)^2/10 then
-- tiles[#tiles + 1] = {name = 'dirt-3', position = {x = x, y = y}}
-- else
-- tiles[#tiles + 1] = {name = 'dry-dirt', position = {x = x, y = y}}
-- end
-- elseif y>-7 then
-- tiles[#tiles + 1] = {name = 'water', position = {x = x, y = y}}
-- end
-- end
-- end
-- return {
-- tiles = tiles,
-- entities = {},
-- }
-- end
function Public.Lobby_iconized_map()
local tiles = {}
local width = 4
-- local height = 20
for x = -100, width do
for y = -35.5, 35.5 do
local negx = width - x
local negxnoisy = negx + Math.random(3)-2
if negxnoisy >= 50 then
tiles[#tiles + 1] = {name = 'grass-3', position = {x = x, y = y}}
elseif negxnoisy >= 30 and (negxnoisy-30) >= Math.abs(y)^2/200 then
tiles[#tiles + 1] = {name = 'dirt-4', position = {x = x, y = y}}
elseif negxnoisy >= 15 and (negxnoisy-15) >= Math.abs(y)^2/150 then
tiles[#tiles + 1] = {name = 'dirt-2', position = {x = x, y = y}}
else
if negx >= 5 and (negx-5) >= Math.abs(y)^2/100 then
tiles[#tiles + 1] = {name = 'sand-2', position = {x = x, y = y}}
elseif (negx <= 8 and Math.abs(y)<1) or (negx < 1 and Math.abs(y)<3) then
tiles[#tiles + 1] = {name = Public.walkway_tile, position = {x = x, y = y}}
else
tiles[#tiles + 1] = {name = 'water', position = {x = x, y = y}}
end
end
end
end
return {
tiles = tiles,
entities = {},
}
end
return Public

1004
maps/pirates/crew.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
local Event = require 'utils.event'
local Public = {}
-- just beginning this, gotta finish reformulating the gui updates in terms of events:
local enum = {
update_crew_progress_gui = Event.generate_event_name('update_crew_progress_gui'),
update_crew_fuel_gui = Event.generate_event_name('update_crew_fuel_gui'),
}
Public.enum = enum
return Public

122
maps/pirates/effects.lua Normal file
View File

@ -0,0 +1,122 @@
local Math = require 'maps.pirates.math'
-- local Memory = require 'maps.pirates.memory'
local _inspect = require 'utils.inspect'.inspect
local Token = require 'utils.token'
local Task = require 'utils.task'
local Public = {}
function Public.worm_movement_effect(surface, position, solid_ground, big_bool)
if not (surface and surface.valid) then return end
if solid_ground then big_bool = false end
local number, rmax, particles, sound
if big_bool then
number = 80
rmax = 4
particles = {'huge-rock-stone-particle-big', 'huge-rock-stone-particle-medium', 'red-desert-1-stone-particle-medium'}
-- sound = 'utility/build_blueprint_large'
sound = 'utility/build_blueprint_large'
else
number = 40
rmax = 2.5
particles = {'huge-rock-stone-particle-medium', 'red-desert-1-stone-particle-medium', 'red-desert-1-stone-particle-small'}
sound = 'utility/build_blueprint_medium'
end
if solid_ground then
particles = {'refined-concrete-stone-particle-medium', 'refined-concrete-stone-particle-small'}
sound = 'utility/build_blueprint_small'
end
local function p(r, theta) return {x = position.x + r*Math.sin(theta), y = position.y + r*Math.cos(theta)} end
for i=1,number do
local r = rmax * Math.sqrt(Math.random())
local theta = Math.random()*6.283
local name = particles[Math.random(#particles)]
local _p = p(r,theta)
surface.create_particle{name = name, position = _p, movement = {0/10, 0/10}, height = 0, vertical_speed = 0.02 + Math.sqrt(rmax - r)*rmax/50, frame_speed = 1}
if i<=5 then
surface.play_sound{path = sound, position = _p, override_sound_type = 'walking', volume_modifier=0.75}
end
end
end
function Public.worm_emerge_effect(surface, position)
if not (surface and surface.valid) then return end
if position then
local function p(r, theta) return {x = position.x + r*Math.sin(theta), y = position.y + r*Math.cos(theta)} end
for theta=0,6,0.5 do
local r = 3
surface.create_entity{name = 'blood-explosion-huge', position = p(r,theta), color={1,1,1}}
end
end
end
function Public.biters_emerge(surface, position)
if not (surface and surface.valid) then return end
surface.create_entity{name = 'spitter-spawner-die', position = position}
end
function Public.kraken_effect_1(surface, position, angle)
if not (surface and surface.valid) then return end
local r = 9
local p = {position.x + r*Math.sin(angle), position.y + r*Math.cos(angle)}
surface.create_entity{name = 'blood-explosion-huge', position = p, color={1,1,1}}
end
function Public.kraken_effect_2(surface, position)
if not (surface and surface.valid) then return end
surface.create_entity{name = 'blood-explosion-big', position = position, color={1,1,1}}
end
local kraken_effect_3_token =
Token.register(
function(data)
Public.kraken_effect_3(data.surface, data.position, data.r)
end
)
function Public.kraken_effect_3(surface, position, r)
r = r or 3
if not (surface and surface.valid) then return end
for theta=0,6.283,6.283/32 do
local p = {position.x + r*Math.sin(theta), position.y + r*Math.cos(theta)}
surface.create_entity{name = 'water-splash', position = p, color={1,1,1}}
end
local rmax = 100
if r < rmax then
Task.set_timeout_in_ticks(4, kraken_effect_3_token, {surface = surface, position = position, r = r + 2})
end
end
function Public.kraken_effect_4(surface, position)
if not (surface and surface.valid) then return end
local r = 6
for theta=0,6.283,6.283/32 do
local p = {position.x + r*Math.sin(theta), position.y + r*Math.cos(theta)}
surface.create_entity{name = 'blood-explosion-big', position = p, color={1,1,1}}
end
end
function Public.kraken_effect_5(surface, position)
if not (surface and surface.valid) then return end
local r = 6
for theta=0,6.283,6.283/32 do
local p = {position.x + r*Math.sin(theta), position.y + r*Math.cos(theta)}
surface.create_entity{name = 'blood-explosion-huge', position = p, color={1,1,1}}
end
end
return Public

View File

@ -0,0 +1,59 @@
-- local Memory = require 'maps.pirates.memory'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local GuiCommon = require 'maps.pirates.gui.common'
local PlayerColors = require 'maps.pirates.player_colors'
local Public = {}
local window_name = 'color'
function Public.toggle_window(player)
local flow, flow2
if player.gui.screen[window_name .. '_piratewindow'] then player.gui.screen[window_name .. '_piratewindow'].destroy() return end
flow = GuiCommon.new_window(player, window_name)
flow.caption = 'Colors!'
flow.style.width = 500
flow.style.height = 500
-- local label = ''
-- for i, v in ipairs(PlayerColors.names) do
-- if i>1 then label = label .. ', ' end
-- local c = PlayerColors.colors[v]
-- label = label .. ', [color=' .. c.r .. ',' .. c.g .. ',' .. c.b .. ']' .. v .. '[/color]'
-- -- label = label .. v
-- end
-- log(label)
flow2 = flow.add({
name = 'colors',
type = 'text-box',
text = PlayerColors.printable,
})
flow2.word_wrap = true
flow2.read_only = true
flow2.selectable = true
flow2.style.width = 450
flow2.style.height = 400
GuiCommon.flow_add_close_button(flow, window_name .. '_piratebutton')
end
-- function Public.regular_update(player)
-- end
function Public.full_update(player)
if Public.regular_update then Public.regular_update(player) end
end
-- function Public.click(event)
-- end
return Public

531
maps/pirates/gui/common.lua Normal file
View File

@ -0,0 +1,531 @@
local Memory = require 'maps.pirates.memory'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
-- local Utils = require 'maps.pirates.utils_local'
local Math = require 'maps.pirates.math'
local _inspect = require 'utils.inspect'.inspect
local Crew = require 'maps.pirates.crew'
-- local Progression = require 'maps.pirates.progression'
-- local Structures = require 'maps.pirates.structures.structures'
local Shop = require 'maps.pirates.shop.shop'
local Boats = require 'maps.pirates.structures.boats.boats'
local Surfaces = require 'maps.pirates.surfaces.surfaces'
local Public = {}
Public.bold_font_color = {255, 230, 192}
Public.default_font_color = {1, 1, 1}
Public.section_header_font_color = {r=0.40, g=0.80, b=0.60}
Public.subsection_header_font_color = {229, 255, 242}
Public.friendly_font_color = {240, 200, 255}
Public.sufficient_font_color = {66, 220, 124}
Public.insufficient_font_color = {1, 0.62, 0.19}
Public.achieved_font_color = {227, 250, 192}
Public.fuel_color_1 = {r=255, g=255, b=255}
Public.fuel_color_2 = {r=255, g=0, b=60}
Public.rage_font_color_1 = {r=1, g=1, b=1}
Public.rage_font_color_2 = {r=1, g=0.5, b=0.1}
Public.rage_font_color_3 = {r=1, g=0.1, b=0.05}
Public.default_window_positions = {
runs = {x = 10, y = 48},
crew = {x = 40, y = 48},
progress = {x = 250, y = 48},
fuel = {x = 468, y = 48},
minimap = {x = 10, y = 48},
color = {x = 160, y = 96},
}
function Public.new_window(player, name)
local global_memory = Memory.get_global_memory()
local gui_memory = global_memory.player_gui_memories[player.index]
local flow
flow = player.gui.screen.add{
type = 'frame',
name = name .. '_piratewindow',
direction = 'vertical'
}
if gui_memory and gui_memory[name] and gui_memory[name].position then
flow.location = gui_memory[name].position
else
flow.location = Public.default_window_positions[name]
end
flow.style = 'map_details_frame'
flow.style.minimal_width = 210
flow.style.natural_width = 210
flow.style.maximal_width = 270
flow.style.minimal_height = 80
flow.style.natural_height = 80
flow.style.maximal_height = 700
flow.style.padding = 10
return flow
end
function Public.update_gui_memory(player, namespace, key, value)
local global_memory = Memory.get_global_memory()
if not global_memory.player_gui_memories[player.index] then
global_memory.player_gui_memories[player.index] = {}
end
local gui_memory = global_memory.player_gui_memories[player.index]
if not gui_memory[namespace] then
gui_memory[namespace] = {}
end
gui_memory[namespace][key] = value
end
function Public.flow_add_floating_sprite_button(flow1, button_name, width)
width = width or 40
local flow2, flow3
flow2 = flow1.add({
name = button_name .. '_frame',
type = 'frame',
})
flow2.style.height = 40
flow2.style.margin = 0
flow2.style.left_padding = -4
flow2.style.top_padding = -4
flow2.style.right_margin = -2
flow2.style.width = width
flow3 = flow2.add({
name = button_name,
type = 'sprite-button',
})
flow3.style.height = 40
flow3.style.width = width
-- flow3.style.padding = -4
return flow3
end
function Public.flow_add_floating_button(flow1, button_name)
local flow2, flow3
flow2 = flow1.add({
name = button_name .. '_flow_1',
type = 'flow',
direction = 'vertical',
})
flow2.style.height = 40
-- flow2.style.left_padding = 4
-- flow2.style.top_padding = 0
-- flow2.style.right_margin = -2
flow2.style.natural_width = 40
flow3 = flow2.add({
name = button_name,
type = 'button',
})
flow3.style = 'dark_rounded_button'
-- flow3.style.minimal_width = 60
-- flow3.style.natural_width = 60
flow3.style.minimal_height = 40
flow3.style.maximal_height = 40
flow3.style.left_padding = 10
flow3.style.right_padding = 4
flow3.style.top_padding = 3
-- flow3.style.padding = -4
flow3.style.natural_width = 40
flow3.style.horizontally_stretchable = true
flow3 = flow2.add({
name = button_name .. '_flow_2',
type = 'flow',
})
flow3.style.natural_width = 20
flow3.style.top_margin = -37
flow3.style.left_margin = 10
flow3.style.right_margin = 9
flow3.ignored_by_interaction=true
return flow3
end
function Public.flow_add_shop_item(flow, name)
local flow2, flow3
local shop_data_1 = Shop.Captains.main_shop_data_1
local shop_data_2 = Shop.Captains.main_shop_data_2
local trade_data = shop_data_1[name] or shop_data_2[name]
if not trade_data then return end
flow2 = flow.add({
name = name,
type = 'flow',
direction = 'horizontal',
})
flow2.style.top_margin = 3
flow2.style.horizontal_align = 'center'
flow2.style.vertical_align = 'center'
flow2.tooltip = trade_data.tooltip
for k, v in pairs(trade_data.what_you_get_sprite_buttons) do
flow3 = flow2.add({
type = 'sprite-button',
name = k,
sprite = k,
enabled = false,
})
flow3.style.minimal_height = 40
flow3.style.maximal_height = 40
if v == false then
flow3.number = nil
else
flow3.number = v
end
flow3.tooltip = trade_data.tooltip
end
flow3 = flow2.add({
type = 'label',
name = 'for',
caption = 'for'
})
flow3.style.font = 'default-large'
flow3.style.font_color = Public.default_font_color
flow3.tooltip = trade_data.tooltip
for k, _ in pairs(trade_data.base_cost) do
flow3 = flow2.add({
name = 'cost_' .. k,
type = 'sprite-button',
enabled = false,
})
flow3.style.minimal_height = 40
flow3.style.maximal_height = 40
flow3.tooltip = trade_data.tooltip
if k == 'fuel' then
flow3.sprite = 'item/coal'
elseif k == 'coins' then
flow3.sprite = 'item/coin'
elseif k == 'iron_plates' then
flow3.sprite = 'item/iron-plate'
elseif k == 'copper_plates' then
flow3.sprite = 'item/copper-plate'
end
end
flow3 = flow2.add({
name = 'spacing',
type = 'flow',
direction = 'horizontal',
})
flow3.style.horizontally_stretchable = true
flow3 = flow2.add({
type = 'sprite-button',
name = 'buy_button',
caption = 'Buy'
})
flow3.style.font = 'default-large'
flow3.style.font_color = Public.default_font_color
flow3.style.height = 32
flow3.style.width = 50
flow3.style.padding = 0
flow3.style.margin = 0
return flow2
end
function Public.flow_add_section(flow, name, caption)
local flow2, flow3
flow2 = flow.add({
name = name,
type = 'flow',
direction = 'vertical',
})
flow2.style.bottom_margin = 5
flow3 = flow2.add({
type = 'label',
name = 'header',
caption = caption
})
flow3.style.font = 'heading-2'
flow3.style.font_color = Public.section_header_font_color
flow3.style.maximal_width = 300
-- flow3.style.maximal_width = 220
-- flow3.style.single_line = false
flow3 = flow2.add({
name = 'body',
type = 'flow',
direction = 'vertical',
})
flow3.style.left_margin = 5
return flow3
end
function Public.flow_add_subpanel(flow, name)
local flow2
flow2 = flow.add({
name = name,
type = 'frame',
direction = 'vertical',
})
flow2.style = 'subpanel_frame'
flow2.style.natural_width = 100
flow2.style.top_padding = -2
flow2.style.top_margin = -8
return flow2
end
function Public.flow_add_close_button(flow, close_button_name)
local flow2, flow3, flow4
flow2 = flow.add({
name = 'close_button_flow',
type = 'flow',
direction = 'vertical',
})
flow2.style.top_margin = -3
flow2.style.bottom_margin = -3
flow3 = flow2.add{type="flow", name='hflow', direction="horizontal"}
flow3.style.vertical_align = 'center'
flow4 = flow3.add{type="flow", name='spacing', direction="horizontal"}
flow4.style.horizontally_stretchable = true
flow4 = flow3.add({
type = 'button',
name = close_button_name,
caption = 'Close',
})
flow4.style = 'back_button'
flow4.style.minimal_width = 90
flow4.style.font = 'default-bold'
flow4.style.height = 28
flow4.style.horizontal_align = 'center'
return flow3
end
function Public.crew_overall_state_bools(player_index)
local global_memory = Memory.get_global_memory()
local memory = Memory.get_crew_memory()
--*** PLAYER STATUS ***--
local ret = {
adventuring = false,
spectating = false,
endorsing = false,
proposing = false,
sloops_full = false,
needs_more_capacity = false,
crew_count_capped = false,
needs_more_endorsers = false,
leaving = false,
proposal_can_launch = false,
}
if memory.crewstatus == Crew.enum.ADVENTURING then
for _, playerindex in pairs(memory.crewplayerindices) do
if player_index == playerindex then ret.adventuring = true end
end
for _, playerindex in pairs(memory.spectatorplayerindices) do
if player_index == playerindex then ret.spectating = true end
end
end
if memory.crewstatus == nil then
for _, crewid in pairs(global_memory.crew_active_ids) do
if global_memory.crew_memories[crewid].crewstatus == Crew.enum.LEAVING_INITIAL_DOCK then
for _, endorser_index in pairs(global_memory.crew_memories[crewid].original_proposal.endorserindices) do
if endorser_index == player_index then ret.leaving = true end
end
end
end
for _, proposal in pairs(global_memory.crewproposals) do
if #proposal.endorserindices > 0 and proposal.endorserindices[1] == player_index then
ret.proposing = true
if #global_memory.crew_active_ids >= 3 then
ret.sloops_full = true
elseif #global_memory.crew_active_ids >= global_memory.active_crews_cap then
ret.crew_count_capped = true
elseif global_memory.active_crews_cap > 1 and #global_memory.crew_active_ids == (global_memory.active_crews_cap - 1) and not ((global_memory.crew_memories[1] and global_memory.crew_memories[1].capacity >= Common.minimum_run_capacity_to_enforce_space_for) or (global_memory.crew_memories[2] and global_memory.crew_memories[2].capacity >= Common.minimum_run_capacity_to_enforce_space_for) or (global_memory.crew_memories[3] and global_memory.crew_memories[3].capacity >= Common.minimum_run_capacity_to_enforce_space_for)) and not (CoreData.capacity_options[proposal.capacity_option].value >= Common.minimum_run_capacity_to_enforce_space_for) then
ret.needs_more_capacity = true
elseif proposal.endorserindices and #global_memory.crew_active_ids > 0 and #proposal.endorserindices < Math.min(4, Math.ceil((#game.connected_players or 0)/5)) then
ret.needs_more_endorsers = true
end
if (not (ret.sloops_full or ret.needs_more_capacity or ret.needs_more_endorsers or ret.crew_count_capped)) then
ret.proposal_can_launch = true
end
end
for _, i in pairs(proposal.endorserindices) do
if player_index == i then ret.endorsing = true end
end
end
end
return ret
end
function Public.player_and_crew_state_bools(player)
local memory = Memory.get_crew_memory()
-- if memory.player_and_crew_state_bools_memoized and memory.player_and_crew_state_bools_memoized.tick > game.tick - 30 then -- auto-memoize and only update every half-second
-- return memory.player_and_crew_state_bools_memoized.ret
-- else
local destination = Common.current_destination()
local dynamic_data = destination.dynamic_data --assumes this always exists
local in_crowsnest_bool, in_hold_bool, in_cabin_bool, onmap_bool, eta_bool, approaching_bool, retreating_bool, atsea_sailing_bool, landed_bool, quest_bool, silo_bool, charged_bool, launched_bool, captain_bool, atsea_loading_bool, character_on_deck_bool, on_deck_standing_near_loco_bool, on_deck_standing_near_cabin_bool, on_deck_standing_near_crowsnest_bool, cost_bool, cost_includes_rocket_launch_bool, approaching_dock_bool, leaving_dock_bool, leave_anytime_bool
captain_bool = Common.is_captain(player)
in_crowsnest_bool = string.sub(player.surface.name, 9, 17) == 'Crowsnest'
in_hold_bool = string.sub(player.surface.name, 9, 12) == 'Hold'
in_cabin_bool = string.sub(player.surface.name, 9, 13) == 'Cabin'
onmap_bool = destination.surface_name and (player.surface.name == destination.surface_name or (
memory.boat and memory.boat.surface_name == destination.surface_name and (in_crowsnest_bool or in_hold_bool or in_cabin_bool)
))
if destination then
eta_bool = dynamic_data.time_remaining and dynamic_data.time_remaining > 0 and onmap_bool
approaching_bool = memory.boat and memory.boat.state == Boats.enum_state.APPROACHING and onmap_bool
retreating_bool = memory.boat and memory.boat.state == Boats.enum_state.RETREATING and onmap_bool
-- approaching_bool = memory.boat and memory.boat.state == Boats.enum_state.APPROACHING
atsea_sailing_bool = memory.boat and memory.boat.state == Boats.enum_state.ATSEA_SAILING
landed_bool = memory.boat and memory.boat.state == Boats.enum_state.LANDED
quest_bool = (dynamic_data.quest_type ~= nil) and onmap_bool
silo_bool = dynamic_data.rocketsilos and dynamic_data.rocketsilos[1] and dynamic_data.rocketsilos[1].valid and onmap_bool
charged_bool = dynamic_data.silocharged
launched_bool = dynamic_data.rocketlaunched
cost_bool = destination.static_params.base_cost_to_undock and (not atsea_sailing_bool) and (not retreating_bool)
cost_includes_rocket_launch_bool = cost_bool and destination.static_params.base_cost_to_undock['launch_rocket']
leave_anytime_bool = (landed_bool and not (eta_bool or cost_bool))
end
if memory.boat then
atsea_loading_bool = memory.boat.state == Boats.enum_state.ATSEA_LOADING_MAP and memory.loadingticks
character_on_deck_bool = player.character and player.character.position and player.surface.name and player.surface.name == memory.boat.surface_name
if character_on_deck_bool then
local BoatData = Boats.get_scope(memory.boat).Data
on_deck_standing_near_loco_bool = Math.distance(player.character.position, Math.vector_sum(memory.boat.position, BoatData.loco_pos)) < 3
on_deck_standing_near_cabin_bool = Math.distance(player.character.position, Math.vector_sum(memory.boat.position, BoatData.cabin_car)) < 2.5
on_deck_standing_near_crowsnest_bool = Math.distance(player.character.position, Math.vector_sum(memory.boat.position, BoatData.crowsnest_center)) < 2.7
end
approaching_dock_bool = destination.type == Surfaces.enum.DOCK and memory.boat.state == Boats.enum_state.APPROACHING
leaving_dock_bool = destination.type == Surfaces.enum.DOCK and memory.boat.state == Boats.enum_state.LEAVING_DOCK
end
local ret = {
in_crowsnest_bool = in_crowsnest_bool,
in_hold_bool = in_hold_bool,
in_cabin_bool = in_cabin_bool,
-- onmap_bool = onmap_bool,
eta_bool = eta_bool,
approaching_bool = approaching_bool,
retreating_bool = retreating_bool,
atsea_sailing_bool = atsea_sailing_bool,
-- landed_bool = landed_bool,
quest_bool = quest_bool,
silo_bool = silo_bool,
charged_bool = charged_bool,
launched_bool = launched_bool,
captain_bool = captain_bool,
atsea_loading_bool = atsea_loading_bool,
-- character_on_deck_bool = character_on_deck_bool,
on_deck_standing_near_loco_bool = on_deck_standing_near_loco_bool,
on_deck_standing_near_cabin_bool = on_deck_standing_near_cabin_bool,
on_deck_standing_near_crowsnest_bool = on_deck_standing_near_crowsnest_bool,
cost_bool = cost_bool,
cost_includes_rocket_launch_bool = cost_includes_rocket_launch_bool,
approaching_dock_bool = approaching_dock_bool,
leaving_dock_bool = leaving_dock_bool,
leave_anytime_bool = leave_anytime_bool
}
-- memory.player_and_crew_state_bools_memoized = {ret = ret, tick = game.tick}
return ret
-- end
end
function Public.update_listbox(listbox, table)
-- pass a table of strings of the form {'locale', unique_id, ...}
-- remove any that shouldn't be there
local marked_for_removal = {}
for index, item in pairs(listbox.items) do
local exists = false
for _, i in pairs(table) do
if tostring(i[2]) == item[2] then
exists = true
end
end
if exists == false then
marked_for_removal[#marked_for_removal + 1] = index
end
end
for i = #marked_for_removal, 1, -1 do
listbox.remove_item(marked_for_removal[i])
end
local indexalreadyat
for _, i in pairs(table) do
local contained = false
for index, item in pairs(listbox.items) do
if tostring(i[2]) == item[2] then
contained = true
indexalreadyat = index
end
end
if contained then
listbox.set_item(indexalreadyat, i)
else
listbox.add_item(i)
end
end
end
return Public

666
maps/pirates/gui/crew.lua Normal file
View File

@ -0,0 +1,666 @@
local Memory = require 'maps.pirates.memory'
local Common = require 'maps.pirates.common'
local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
-- local Surfaces = require 'maps.pirates.surfaces.surfaces'
local Roles = require 'maps.pirates.roles.roles'
local Classes = require 'maps.pirates.roles.classes'
local Crew = require 'maps.pirates.crew'
-- local Progression = require 'maps.pirates.progression'
-- local Structures = require 'maps.pirates.structures.structures'
local _inspect = require 'utils.inspect'.inspect
-- local Boats = require 'maps.pirates.structures.boats.boats'
local GuiCommon = require 'maps.pirates.gui.common'
local CoreData = require 'maps.pirates.coredata'
local Server = require 'utils.server'
local Public = {}
local window_name = 'crew'
function Public.toggle_window(player)
local memory = Memory.get_crew_memory()
local flow, flow2, flow3, flow4
--*** OVERALL FLOW ***--
if player.gui.screen[window_name .. '_piratewindow'] then player.gui.screen[window_name .. '_piratewindow'].destroy() return end
if not memory.id then return end
flow = GuiCommon.new_window(player, window_name)
flow.caption = 'Crew'
--*** PARAMETERS OF RUN ***--
flow2 = flow.add({
name = 'crew_capacity_and_difficulty',
type = 'label',
})
flow2.style.left_margin = 5
flow2.style.top_margin = 0
flow2.style.bottom_margin = -3
flow2.style.single_line = false
flow2.style.maximal_width = 190
flow2.style.font = 'default'
flow2 = flow.add({
name = 'crew_age',
type = 'label',
})
flow2.style.left_margin = 5
flow2.style.top_margin = -3
flow2.style.bottom_margin = 0
flow2.style.single_line = true
flow2.style.maximal_width = 200
flow2.style.font = 'default'
-- flow2 = flow.add({
-- name = 'crew_difficulty',
-- type = 'label',
-- })
-- flow2.style.left_margin = 5
-- flow2.style.top_margin = -3
-- flow2.style.bottom_margin = 0
-- flow2.style.single_line = false
-- flow2.style.maximal_width = 190
-- flow2.style.font = 'default'
--*** MEMBERSHIP BUTTONS ***--
flow2 = flow.add({
name = 'membership_buttons',
type = 'flow',
direction = 'horizontal',
})
flow3 = flow2.add({
name = 'leave_crew',
type = 'button',
caption = 'Quit Crew',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'Return to the lobby.'
flow3 = flow2.add({
name = 'leave_spectators',
type = 'button',
caption = 'Return to Lobby',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3 = flow2.add({
name = 'spectator_join_crew',
type = 'button',
caption = 'Join Crew',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3 = flow2.add({
name = 'crewmember_join_spectators',
type = 'button',
caption = 'Spectate',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'You won\t be able to rejoin the crew for a short while after you do this.'
--*** MEMBERS AND SPECTATORS ***--
flow2 = GuiCommon.flow_add_section(flow, 'members', 'Crew Members')
flow3 = flow2.add({
name = 'members_listbox',
type = 'list-box',
})
flow3.style.margin = 2
flow3.style.maximal_height = 350
flow3 = flow2.add({
name = 'class_renounce',
type = 'button',
caption = 'Give Up Class',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'The class will become available for other crewmembers to take.'
flow3 = flow2.add({
name = 'officer_resign',
type = 'button',
caption = 'Resign as Officer',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'Give up the officer role.'
flow2 = GuiCommon.flow_add_section(flow, 'spectators', 'Spectators')
flow3 = flow2.add({
name = 'spectators_listbox',
type = 'list-box',
})
flow3.style.margin = 2
flow3.style.maximal_height = 150
--*** DIFFICULTY VOTE ***--
flow2 = GuiCommon.flow_add_section(flow, 'difficulty_vote', 'Vote for Difficulty')
for i, o in ipairs(CoreData.difficulty_options) do
flow3 = flow2.add({
name = 'difficulty_option_' .. i,
type = 'button',
caption = o.text,
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
end
--*** SPARE CLASSES ***--
flow2 = GuiCommon.flow_add_section(flow, 'spare_classes', 'Spare Classes')
flow3 = flow2.add({
name = 'list',
type = 'label',
})
flow3.style.left_margin = 5
flow3.style.top_margin = -3
flow3.style.bottom_margin = -3
flow3.style.single_line = false
flow3.style.maximal_width = 160
flow3.style.font = 'default-dropdown'
flow3 = flow2.add({
name = 'assign_flow',
type = 'flow',
direction = 'vertical',
})
flow3.style.top_margin = 3
for _, c in ipairs(Classes.Class_List) do
flow4 = flow3.add({
name = 'assign_class_' .. c,
type = 'button',
caption = 'Give class: ' .. Classes.display_form[c],
})
flow4.style.minimal_width = 95
flow4.style.font = 'default-bold'
flow4.style.font_color = {r=0.10, g=0.10, b=0.10}
flow4.tooltip = 'Give the selected player the class ' .. Classes.display_form[c] .. '.\n\n Class description: ' .. Classes.explanation[c]
-- flow4.tooltip = 'Give this class to the selected player.'
end
for _, c in ipairs(Classes.Class_List) do
flow4 = flow3.add({
name = 'selfassign_class_' .. c,
type = 'button',
caption = 'Take class: ' .. Classes.display_form[c],
})
flow4.style.minimal_width = 95
flow4.style.font = 'default-bold'
flow4.style.font_color = {r=0.10, g=0.10, b=0.10}
flow4.tooltip = 'Give yourself the spare class ' .. Classes.display_form[c] .. '.\n\nClass description: ' .. Classes.explanation[c]
end
--*** CAPTAIN's ACTIONS ***--
flow2 = GuiCommon.flow_add_section(flow, 'captain', 'Captain\'s Actions')
flow3 = flow2.add({
name = 'capn_disband_crew',
type = 'button',
caption = 'Disband Crew',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'End the run. You will be prompted again after clicking.'
flow3 = flow2.add({
name = 'capn_disband_are_you_sure',
type = 'button',
caption = 'ARE YOU SURE?',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'Click to disband the crew.'
flow3 = flow2.add({
name = 'capn_renounce',
type = 'button',
caption = 'Renounce Captainhood',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'You will no longer be captain, and the role will be passed around until a crewmember takes it.'
flow3 = flow2.add({
name = 'capn_pass',
type = 'button',
caption = 'Pass Captain To',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'Make the selected crewmember into the Captain.'
flow3 = flow2.add({
name = 'capn_plank',
type = 'button',
caption = 'Plank (Make Spectator)',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'The player will be returned to the lobby and can\'t join your crew for a while.'
flow3 = flow2.add({
name = 'line',
type = 'line',
})
flow3.style.width = 50
flow3.style.left_margin = 20
flow3.style.top_margin = 4
flow3.style.bottom_margin = 4
-- flow3 = flow2.add({
-- name = 'capn_undock_normal',
-- type = 'button',
-- caption = 'Undock Boat',
-- })
-- flow3.style.minimal_width = 95
-- flow3.style.font = 'default-bold'
-- flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3 = flow2.add({
name = 'make_officer',
type = 'button',
caption = 'Make Officer',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'Make this player an Officer.'
flow3 = flow2.add({
name = 'unmake_officer',
type = 'button',
caption = 'Unamake Officer',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'Remove this player as an Officer.'
flow3 = flow2.add({
name = 'revoke_class',
type = 'button',
caption = 'Revoke Class',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'Put this player\'s class back in the Spare Classes pool.'
flow3 = flow2.add({
name = 'capn_summon_crew',
type = 'button',
caption = 'Summon Crew to Ship',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'Teleport crewmembers to the ship.'
flow3 = flow2.add({
name = 'capn_requisition',
type = 'button',
caption = 'Requisition Items',
})
flow3.style.minimal_width = 95
flow3.style.font = 'default-bold'
flow3.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3.tooltip = 'Take doubloons and uranium-235 from each non-officer.'
flow2 = flow.add({
name = 'undock_tip',
type = 'label',
})
flow2.style.left_margin = 5
flow2.style.top_margin = -8
flow2.style.bottom_margin = 7
flow2.style.single_line = false
flow2.style.maximal_width = 190
flow2.style.font = 'default'
flow2.caption = 'To undock, use the top toolbar.'
--
GuiCommon.flow_add_close_button(flow, window_name .. '_piratebutton')
end
-- function Public.regular_update(player)
-- end
function Public.full_update(player)
if Public.regular_update then Public.regular_update(player) end
if not player.gui.screen[window_name .. '_piratewindow'] then return end
local flow = player.gui.screen[window_name .. '_piratewindow']
local memory = Memory.get_crew_memory()
local playercrew_status = GuiCommon.crew_overall_state_bools(player.index)
-- local destination = Common.current_destination()
--*** WHAT TO SHOW ***--
flow.difficulty_vote.visible = memory.overworldx and memory.overworldx == 0
flow.members.body.class_renounce.visible = memory.classes_table and memory.classes_table[player.index]
flow.members.body.officer_resign.visible = memory.officers_table and memory.officers_table[player.index]
flow.spare_classes.visible = memory.spare_classes and #memory.spare_classes > 0
local other_player_selected = flow.members.body.members_listbox.selected_index ~= 0 and tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2]) ~= player.index
local any_class_button = false
for _, c in pairs(Classes.Class_List) do
if memory.spare_classes and Utils.contains(memory.spare_classes, c) and (not (player.controller_type == defines.controllers.spectator)) then
if Common.is_captain(player) and memory.crewplayerindices and #memory.crewplayerindices > 1 then
if other_player_selected and (not (memory.classes_table[tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])])) then
flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = false
flow.spare_classes.body.assign_flow['assign_class_' .. c].visible = true
any_class_button = true
else
flow.spare_classes.body.assign_flow['assign_class_' .. c].visible = false
if (not memory.classes_table[player.index]) then
flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = true
any_class_button = true
else
flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = false
end
end
else
flow.spare_classes.body.assign_flow['assign_class_' .. c].visible = false
if (not memory.classes_table[player.index]) then
flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = true
any_class_button = true
else
flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = false
end
end
else
flow.spare_classes.body.assign_flow['assign_class_' .. c].visible = false
flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = false
end
end
flow.spare_classes.body.assign_flow.visible = any_class_button
flow.captain.visible = Common.is_captain(player)
flow.undock_tip.visible = Common.is_captain(player)
flow.captain.body.capn_pass.visible = other_player_selected
flow.captain.body.capn_plank.visible = flow.captain.body.capn_pass.visible
flow.captain.body.make_officer.visible = other_player_selected and (not (memory.officers_table and memory.officers_table[tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])]))
flow.captain.body.unmake_officer.visible = other_player_selected and ((memory.officers_table and memory.officers_table[tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])]))
flow.captain.body.revoke_class.visible = other_player_selected and (memory.classes_table[tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])])
-- flow.captain.body.capn_undock_normal.visible = memory.boat and memory.boat.state and ((memory.boat.state == Boats.enum_state.LANDED) or (memory.boat.state == Boats.enum_state.APPROACHING) or (memory.boat.state == Boats.enum_state.DOCKED))
flow.captain.body.capn_summon_crew.visible = false
flow.captain.body.capn_requisition.visible = true
-- flow.captain.body.capn_summon_crew.visible = memory.boat and memory.boat.state and (memory.boat.state == Boats.enum_state.RETREATING or memory.boat.state == Boats.enum_state.LEAVING_DOCK)
flow.captain.body.capn_disband_are_you_sure.visible = memory.disband_are_you_sure_ticks and memory.disband_are_you_sure_ticks[player.index] and memory.disband_are_you_sure_ticks[player.index] > game.tick - 60*2
flow.captain.body.capn_disband_crew.visible = not flow.captain.body.capn_disband_are_you_sure.visible
flow.members.visible = true
flow.spectators.visible = (#memory.spectatorplayerindices > 0)
-- flow.crew_age.visible = true
-- -- flow.crew_age.visible = memory.mode and memory.mode == 'speedrun'
-- flow.crew_difficulty.visible = true
local count = 0
if playercrew_status.spectating then
for _, v in pairs(memory.crewplayerindices) do
if Common.validate_player(game.players[v]) then count = count + 1 end
end
end
flow.membership_buttons.spectator_join_crew.visible = playercrew_status.spectating and (not (count >= memory.capacity))
flow.membership_buttons.leave_crew.visible = playercrew_status.adventuring
-- flow.membership_buttons.crewmember_join_spectators.visible = playercrew_status.adventuring
flow.membership_buttons.crewmember_join_spectators.visible = false --disabled spectators for now... might not play well with maze world
flow.membership_buttons.leave_spectators.visible = playercrew_status.spectating
flow.membership_buttons.spectator_join_crew.visible = flow.membership_buttons.spectator_join_crew.visible and (not (memory.tempbanned_from_joining_data[player.index] and game.tick < memory.tempbanned_from_joining_data[player.index] + Common.ban_from_rejoining_crew_ticks))
--== UPDATE CONTENT ==--
if memory.id then
flow.caption = memory.name
flow.crew_age.caption = 'Age: ' .. Utils.time_mediumform((memory.age or 0)/60)
-- flow.crew_difficulty.caption = 'Difficulty: ' .. CoreData.difficulty_options[memory.difficulty_option].text
flow.crew_capacity_and_difficulty.caption = CoreData.difficulty_options[memory.difficulty_option].text .. ', Capacity ' .. CoreData.capacity_options[memory.capacity_option].text
if flow.spare_classes.visible then
local str = ''
for i, c in ipairs(memory.spare_classes) do
if i>1 then str = str .. ', ' end
str = str .. Classes.display_form[c]
end
str = str .. '.'
flow.spare_classes.body.list.caption = str
end
end
if flow.members.visible then
local wrappedcrew = {}
for _, index in pairs(memory.crewplayerindices) do
local player2 = game.players[index]
local tag_text = Roles.tag_text(player2)
wrappedcrew[#wrappedcrew + 1] = {'pirates.crewmember_displayform', index, player2.color.r, player2.color.g, player2.color.b, player2.name, tag_text}
end
GuiCommon.update_listbox(flow.members.body.members_listbox, wrappedcrew)
flow.members.header.caption = 'Crew Members (' .. (#memory.crewplayerindices or 0) .. ')'
end
if flow.spectators.visible then
local wrappedspectators = {}
for _, index in pairs(memory.spectatorplayerindices) do
local player2 = game.players[index]
wrappedspectators[#wrappedspectators + 1] = {'pirates.crewmember_displayform', index, player2.color.r, player2.color.g, player2.color.b, player2.name, ''}
end
GuiCommon.update_listbox(flow.spectators.body.spectators_listbox, wrappedspectators)
end
-- if flow.captain.body.capn_undock_normal.visible then
-- flow.captain.body.capn_undock_normal.enabled = ((memory.boat.state == Boats.enum_state.LANDED) and Common.query_can_pay_cost_to_leave()) or (memory.boat.state == Boats.enum_state.DOCKED)
-- end
end
function Public.click(event)
local player = game.players[event.element.player_index]
local eventname = event.element.name
if not player.gui.screen[window_name .. '_piratewindow'] then return end
local flow = player.gui.screen[window_name .. '_piratewindow']
local memory = Memory.get_crew_memory()
if eventname == 'crewmember_join_spectators' then
Crew.join_spectators(player, memory.id)
return
end
if eventname == 'leave_spectators' then
Crew.leave_spectators(player)
return
end
if eventname == 'spectator_join_crew' then
Crew.join_crew(player, memory.id)
return
end
if eventname == 'leave_crew' then
Crew.leave_crew(player, true)
return
end
-- if eventname == 'promote_officer' then
-- Roles.promote_to_officer(player)
-- return
-- end
-- if eventname == 'demote_officer' then
-- Roles.demote_to_officer(player)
-- return
-- end
if string.sub(eventname, 1, 13) and string.sub(eventname, 1, 13) == 'assign_class_' then
local other_id = tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])
Classes.assign_class(other_id, tonumber(string.sub(eventname, 14, -1)))
return
end
if string.sub(eventname, 1, 17) and string.sub(eventname, 1, 17) == 'selfassign_class_' then
Classes.assign_class(player.index, tonumber(string.sub(eventname, 18, -1)), true)
return
end
if string.sub(eventname, 1, 18) and string.sub(eventname, 1, 18) == 'difficulty_option_' then
Crew.difficulty_vote(player.index, tonumber(string.sub(eventname, 19, -1)))
return
end
if eventname == 'capn_summon_crew' then
--double check:
if Roles.player_privilege_level(player) >= Roles.privilege_levels.CAPTAIN then
Crew.summon_crew()
end
return
end
if eventname == 'capn_requisition' then
--double check:
if Roles.player_privilege_level(player) >= Roles.privilege_levels.CAPTAIN then
Roles.captain_requisition(memory.playerindex_captain)
end
return
end
if eventname == 'class_renounce' then
Classes.try_renounce_class(player, true)
return
end
if eventname == 'capn_renounce' then
Roles.renounce_captainhood(player)
return
end
if eventname == 'officer_resign' then
Roles.resign_as_officer(player)
return
end
if eventname == 'capn_disband_crew' then
--double check:
if Roles.player_privilege_level(player) >= Roles.privilege_levels.CAPTAIN then
if not memory.disband_are_you_sure_ticks then memory.disband_are_you_sure_ticks = {} end
memory.disband_are_you_sure_ticks[player.index] = game.tick
end
return
end
if eventname == 'capn_disband_are_you_sure' then
--double check:
if Roles.player_privilege_level(player) >= Roles.privilege_levels.CAPTAIN then
local force = memory.force
if force and force.valid then
local message = player.name .. ' disbanded ' .. memory.name .. ', after ' .. Utils.time_longform((memory.real_age or 0)/60) .. '.'
Common.notify_game(message)
Server.to_discord_embed_raw(CoreData.comfy_emojis.trashbin .. '[' .. memory.name .. '] ' .. message)
end
Crew.disband_crew(true)
end
return
end
if eventname == 'capn_pass' then
local other_id = tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])
Roles.pass_captainhood(player, game.players[other_id])
return
end
if eventname == 'make_officer' then
local other_id = tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])
Roles.make_officer(player, game.players[other_id])
return
end
if eventname == 'unmake_officer' then
local other_id = tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])
Roles.unmake_officer(player, game.players[other_id])
return
end
if eventname == 'revoke_class' then
local other_id = tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])
Roles.revoke_class(player, game.players[other_id])
return
end
if eventname == 'capn_plank' then
local other_id = tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])
Crew.plank(player, game.players[other_id])
return
end
end
return Public

144
maps/pirates/gui/evo.lua Normal file
View File

@ -0,0 +1,144 @@
local Common = require 'maps.pirates.common'
local Balance = require 'maps.pirates.balance'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local _inspect = require 'utils.inspect'.inspect
local Boats = require 'maps.pirates.structures.boats.boats'
local Memory = require 'maps.pirates.memory'
local Kraken = require 'maps.pirates.surfaces.sea.kraken'
local Public = {}
-- local GuiCommon = require 'maps.pirates.gui.common'
-- local button_sprites = {
-- ['small-biter'] = 0,
-- ['medium-biter'] = 0.2,
-- ['small-spitter'] = 0.25,
-- ['medium-spitter'] = 0.4,
-- ['big-spitter'] = 0.5,
-- ['big-biter'] = 0.501,
-- ['behemoth-spitter'] = 0.9,
-- ['behemoth-biter'] = 0.901
-- }
-- local function get_alien_name(evolution_factor)
-- local last_match = 'fish'
-- for name, alien_threshold in pairs(button_sprites) do
-- if evolution_factor == alien_threshold then
-- return name
-- end
-- -- next alien evolution_factor isn't reached
-- if alien_threshold > evolution_factor then
-- return last_match
-- end
-- -- surpassed this alien evolution_factor
-- if alien_threshold < evolution_factor then
-- last_match = name
-- end
-- end
-- return last_match
-- end
-- function Public.regular_update(player)
-- end
function Public.full_update(player)
if Public.regular_update then Public.regular_update(player) end
local memory = Memory.get_crew_memory()
local pirates_flow = player.gui.top
local button = pirates_flow.evo_piratebutton_frame.evo_piratebutton
if button and button.valid then
local evo = memory.evolution_factor or 0
-- local evo = Math.floor(evo * 1000) * 0.001
-- local current_alien = get_alien_name(evolution_factor)
-- local sprite = 'entity/' .. current_alien
-- if evolution_factor == 0 or (memory.boat and (memory.boat.state == Boats.enum_state.ATSEA_SAILING or memory.boat.state == Boats.enum_state.ATSEA_LOADING_MAP)) then
-- button.number = 0
-- button.tooltip = 'Local biter evolution\n\n0'
-- else
local destination = Common.current_destination()
local evolution_leagues
local evolution_kraken
local evolution_time
local evolution_silo
local evolution_nests
local evolution_sandwurms
local evolution_total
local types = {'leagues', 'kraken', 'time', 'silo', 'nests', 'sandwurms'}
local str = 'Local biter evolution\n\n'
if memory.boat and memory.boat.state and (memory.boat.state == Boats.enum_state.ATSEA_SAILING or memory.boat.state == Boats.enum_state.ATSEA_LOADING_MAP) then
evolution_leagues = evo - (memory.kraken_evo or 0)
local krakens = false
if memory.active_sea_enemies and memory.active_sea_enemies.krakens then
for i = 1, Kraken.kraken_slots do
if memory.active_sea_enemies.krakens[i] then krakens = true break end
end
end
if krakens then
evolution_kraken = Balance.kraken_spawns_base_extra_evo + (memory.kraken_evo or 0)
evolution_total = evolution_leagues + Balance.kraken_spawns_base_extra_evo
else
evolution_total = evolution_leagues
end
else
if destination and destination.dynamic_data then
evolution_leagues = destination.dynamic_data.evolution_accrued_leagues
evolution_time = destination.dynamic_data.evolution_accrued_time
evolution_nests = destination.dynamic_data.evolution_accrued_nests
evolution_silo = destination.dynamic_data.evolution_accrued_silo
evolution_sandwurms = destination.dynamic_data.evolution_accrued_sandwurms
end
evolution_total = (evolution_leagues or 0) + (evolution_time or 0) + (evolution_nests or 0) + (evolution_silo or 0) + (evolution_sandwurms or 0)
end
for _, type in ipairs(types) do
if type == 'leagues' then
if evolution_leagues then
str = str .. string.format('Leagues: %.2f\n', evolution_leagues)
end
elseif type == 'kraken' then
if evolution_kraken then
str = str .. string.format('Kraken: %.2f\n', evolution_kraken)
end
elseif type == 'time' then
if evolution_time then
str = str .. string.format('Time: %.2f\n', evolution_time)
end
elseif type == 'silo' then
if evolution_silo then
str = str .. string.format('Silo: %.2f\n', evolution_silo)
end
elseif type == 'nests' then
if evolution_nests then
str = str .. string.format('Nests: %.2f\n', evolution_nests)
end
elseif type == 'sandwurms' then
if evolution_sandwurms then
str = str .. string.format('Sandwurms: %.2f\n', evolution_sandwurms)
end
end
end
str = str .. string.format('Total: %.2f', evolution_total)
button.number = evolution_total
button.tooltip = str
end
end
return Public

192
maps/pirates/gui/fuel.lua Normal file
View File

@ -0,0 +1,192 @@
local Memory = require 'maps.pirates.memory'
-- local Common = require 'maps.pirates.common'
-- local CoreData = require 'maps.pirates.coredata'
local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local Balance = require 'maps.pirates.balance'
-- local Surfaces = require 'maps.pirates.surfaces.surfaces'
local Roles = require 'maps.pirates.roles.roles'
local Crew = require 'maps.pirates.crew'
local Shop = require 'maps.pirates.shop.shop'
-- local Progression = require 'maps.pirates.progression'
-- local Structures = require 'maps.pirates.structures.structures'
local _inspect = require 'utils.inspect'.inspect
-- local Boats = require 'maps.pirates.structures.boats.boats'
local GuiCommon = require 'maps.pirates.gui.common'
local Public = {}
local window_name = 'fuel'
function Public.toggle_window(player)
local flow, flow2, flow3
local shop_data_1 = Shop.Captains.main_shop_data_1
local shop_data_2 = Shop.Captains.main_shop_data_2
if player.gui.screen[window_name .. '_piratewindow'] then player.gui.screen[window_name .. '_piratewindow'].destroy() return end
flow = GuiCommon.new_window(player, window_name)
flow.caption = 'Captain\'s Store'
flow2 = flow.add({
name = 'trades',
type = 'flow',
direction = 'vertical',
})
flow2.style.top_margin = 3
flow2.style.bottom_margin = 3
flow2.style.horizontal_align = 'center'
flow2.style.vertical_align = 'center'
for k, _ in pairs(shop_data_1) do
GuiCommon.flow_add_shop_item(flow2, k)
end
flow3 = flow2.add({
name = 'line_1',
type = 'line',
})
flow3.style.width = 100
for k, _ in pairs(shop_data_2) do
GuiCommon.flow_add_shop_item(flow2, k)
end
flow2 = GuiCommon.flow_add_close_button(flow, window_name .. '_piratebutton')
flow2.add({
name = 'tospend',
type = 'sprite-button',
sprite = 'item/coin',
index = 1,
enabled = false,
})
end
function Public.regular_update(player)
local flow
-- local memory = Memory.get_crew_memory()
if not player.gui.screen[window_name .. '_piratewindow'] then return end
flow = player.gui.screen[window_name .. '_piratewindow']
if Roles.player_privilege_level(player) >= Roles.privilege_levels.OFFICER then
flow.close_button_flow.hflow.tospend.visible = true
local inv = player.get_inventory(defines.inventory.character_main)
if inv and inv.valid then
local coin_amount = inv.get_item_count('coin') or 0
flow.close_button_flow.hflow.tospend.number = coin_amount
flow.close_button_flow.hflow.tospend.tooltip = string.format("You're holding " .. Utils.bignumber_abbrevform2(coin_amount) .. " doubloons.")
end
else
flow.close_button_flow.hflow.tospend.visible = false
end
end
function Public.full_update(player)
if Public.regular_update then Public.regular_update(player) end
local flow
local memory = Memory.get_crew_memory()
local shop_data = Utils.nonrepeating_join_dict(Shop.Captains.main_shop_data_1, Shop.Captains.main_shop_data_2)
local shop_data_1 = Shop.Captains.main_shop_data_1
local shop_data_2 = Shop.Captains.main_shop_data_2
local availability_data = memory.mainshop_availability_bools
if not player.gui.screen[window_name .. '_piratewindow'] then return end
flow = player.gui.screen[window_name .. '_piratewindow']
--*** WHAT TO SHOW ***--
-- if memory.stored_fuel then
-- flow.close_button_flow.hflow.tospend.number = memory.stored_fuel
-- flow.close_button_flow.hflow.tospend.tooltip = string.format('The crew has %01d stored coal.', memory.stored_fuel)
-- else
-- flow.close_button_flow.hflow.tospend.number = 0
-- flow.close_button_flow.hflow.tospend.tooltip = string.format('The crew has %01d stored coal.', 0)
-- end
if memory.crewstatus == Crew.enum.ADVENTURING then
flow.trades.visible = true
else
flow.trades.visible = false
end
local anything_in_shop_1 = false
for k, _ in pairs(shop_data_1) do
if availability_data and availability_data[k] == true then
flow.trades[k].visible = true
anything_in_shop_1 = true
if Roles.player_privilege_level(player) >= Roles.privilege_levels.OFFICER then
flow.trades[k].buy_button.visible = true
else
flow.trades[k].buy_button.visible = false
end
else
flow.trades[k].visible = false
end
end
flow.trades.line_1.visible = anything_in_shop_1
for k, _ in pairs(shop_data_2) do
if availability_data and availability_data[k] == true then
flow.trades[k].visible = true
if Roles.player_privilege_level(player) >= Roles.privilege_levels.OFFICER then
flow.trades[k].buy_button.visible = true
else
flow.trades[k].buy_button.visible = false
end
else
flow.trades[k].visible = false
end
end
--*** UPDATE CONTENT ***--
local multiplier = Balance.main_shop_cost_multiplier()
for k, v in pairs(shop_data) do
for k2, v2 in pairs(v.base_cost) do
if v2 == false then
flow.trades[k]['cost_' .. k2].number = nil
else
flow.trades[k]['cost_' .. k2].number = multiplier * v2
end
end
end
end
function Public.click(event)
local player = game.players[event.element.player_index]
local eventname = event.element.name
if not player.gui.screen[window_name .. '_piratewindow'] then return end
-- local flow = player.gui.screen[window_name .. '_piratewindow']
-- local memory = Memory.get_crew_memory()
if eventname == 'buy_button' then
Shop.Captains.main_shop_try_purchase(player, event.element.parent.name)
end
end
return Public

1174
maps/pirates/gui/gui.lua Normal file

File diff suppressed because it is too large Load Diff

236
maps/pirates/gui/info.lua Normal file
View File

@ -0,0 +1,236 @@
-- local Memory = require 'maps.pirates.memory'
-- local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
-- local Surfaces = require 'maps.pirates.surfaces.surfaces'
-- local Lobby = require 'maps.pirates.surfaces.lobby'
local _inspect = require 'utils.inspect'.inspect
-- local Boats = require 'maps.pirates.structures.boats.boats'
local GuiCommon = require 'maps.pirates.gui.common'
local Public = {}
local window_name = 'info'
local width = 430
function Public.toggle_window(player)
local flow, flow2, flow3, flow4
if player.gui.screen[window_name .. '_piratewindow'] then player.gui.screen[window_name .. '_piratewindow'].destroy() return end
flow = player.gui.screen.add{
type = 'tabbed-pane',
name = window_name .. '_piratewindow',
direction = 'vertical'
}
flow.location = {x = 90, y = 90}
flow.selected_tab_index = 1
flow.style = 'frame_tabbed_pane'
flow.style.width = width
flow.style.height = 420
flow2 = Public.flow_add_info_tab(flow, 'Info')
flow3 = flow2.parent.last_info_flow_1.last_info_flow_2
flow4 = flow3.add{type = "label", caption = {"pirates.softmod_info_body_1"}}
flow4.style.font_color = GuiCommon.friendly_font_color
flow4.style.single_line = false
flow4.style.font = 'debug'
flow4.style.top_margin = -2
flow4.style.bottom_margin = 0
-- flow4.style.bottom_margin = 16
Public.flow_add_info_sections(flow2, {'game_description'})
flow2 = Public.flow_add_info_tab(flow, 'Updates')
Public.flow_add_info_sections(flow2, {'updates', 'bugs'})
flow2 = Public.flow_add_info_tab(flow, 'Tips')
Public.flow_add_info_sections(flow2, {'new_players', 'tips'})
flow2 = Public.flow_add_info_tab(flow, 'Credits')
Public.flow_add_info_sections(flow2, {'credits'})
end
function Public.flow_add_info_sections(flow, sections_list)
local flow2
for j = 1, #sections_list do
local i = sections_list[j]
flow2 = flow.add{type = "label", caption = {"pirates.softmod_info_" .. i .. "_1"}}
flow2.style.font_color = GuiCommon.friendly_font_color
flow2.style.single_line = false
flow2.style.font = 'heading-3'
flow2.style.bottom_margin = -4
flow2 = flow.add{type = "label", caption = {"pirates.softmod_info_" .. i .. "_2"}}
flow2.style.font_color = GuiCommon.friendly_font_color
flow2.style.single_line = false
flow2.style.font = 'default'
flow2.style.bottom_margin = 12
flow2.style.left_margin = 8
end
end
function Public.flow_add_info_tab(flow, tab_name)
local tab, contents, ret, flow3, flow4, flow5
tab = flow.add{type='tab', caption=tab_name}
tab.style = 'frame_tab'
contents = flow.add({
type = 'frame',
direction = 'vertical',
})
contents.style.vertically_stretchable = true
contents.style.width = width
contents.style.natural_height = 2000
contents.style.top_margin = -8
contents.style.bottom_margin = -12
contents.style.left_margin = -7
contents.style.right_margin = -11
flow3 = contents.add({
type = 'flow',
name = 'header_flow_1',
direction = 'horizontal',
})
flow3.style.horizontally_stretchable = true
flow3.style.horizontal_align = 'center'
flow4 = flow3.add({
type = 'flow',
name = 'header_flow_2',
direction = 'vertical',
})
flow4.style.horizontally_stretchable = true
flow4.style.horizontal_align = 'center'
flow5 = flow4.add{type = "label", caption = {"", {"pirates.softmod_info_header_before_version_number"}, CoreData.version_string, {"pirates.softmod_info_header_after_version_number"}}}
flow5.style.font_color = GuiCommon.friendly_font_color
flow5.style.font = 'heading-1'
flow5.style.bottom_margin = 2
flow5 = flow4.add{type = "label", caption = {"pirates.softmod_info_body_promote"}}
flow5.style.font_color = GuiCommon.friendly_font_color
flow5.style.single_line = false
flow5.style.font = 'default-small'
flow5.style.top_margin = -12
flow5.style.bottom_margin = 8
ret = contents.add({
type = 'flow',
name = 'main_flow_1',
direction = 'vertical',
})
ret.style.horizontally_stretchable = true
flow3 = contents.add({
type = 'flow',
name = 'last_info_flow_1',
direction = 'horizontal',
})
flow3.style.horizontally_stretchable = true
flow3.style.horizontal_align = 'center'
flow4 = flow3.add({
type = 'flow',
name = 'last_info_flow_2',
direction = 'vertical',
})
flow4.style.horizontally_stretchable = true
flow4.style.horizontal_align = 'center'
flow3 = contents.add({
type = 'flow',
direction = 'vertical',
})
flow3.style.vertically_stretchable = true
flow3.style.horizontally_stretchable = true
flow3 = contents.add({
type = 'flow',
direction = 'horizontal',
})
flow3.style.horizontally_stretchable = true
flow3.style.horizontal_align = 'center'
flow4 = flow3.add{type = "label", caption = {"pirates.softmod_info_body_clicky"}}
flow4.style.font_color = GuiCommon.friendly_font_color
flow4.style.single_line = false
flow4.style.font = 'default'
flow4.style.bottom_margin = 4
flow4.style.top_margin = 3
flow.add_tab(tab, contents)
return ret
end
function Public.click(event)
local player = game.players[event.element.player_index]
-- local name = 'info'
local element = event.element
local eventtype = element.type
if not player.gui.screen[window_name .. '_piratewindow'] then return end
-- local memory = Memory.get_crew_memory()
if eventtype ~= 'tab' and (
element.name == (window_name .. '_piratewindow') or
(element.parent and element.parent.name == (window_name .. '_piratewindow')) or
(element.parent and element.parent.parent and element.parent.parent.name == (window_name .. '_piratewindow')) or
(element.parent and element.parent.parent and element.parent.parent.parent and element.parent.parent.parent.name == (window_name .. '_piratewindow')) or
(element.parent and element.parent.parent and element.parent.parent.parent and element.parent.parent.parent.parent and element.parent.parent.parent.parent.name == (window_name .. '_piratewindow')) or
(element.parent and element.parent.parent and element.parent.parent.parent and element.parent.parent.parent.parent and element.parent.parent.parent.parent.parent and element.parent.parent.parent.parent.parent.name == (window_name .. '_piratewindow'))
) then
Public.toggle_window(player)
end
end
-- function Public.regular_update(player)
-- end
function Public.full_update(player)
if Public.regular_update then Public.regular_update(player) end
if not player.gui.screen[window_name .. '_piratewindow'] then return end
local flow = player.gui.screen[window_name .. '_piratewindow']
local flow2 = flow
-- warning, if you make these too small, it loses 'Click to dismiss.'
if flow2.selected_tab_index == 1 then
flow2.style.height = 400
elseif flow2.selected_tab_index == 2 then
flow2.style.height = 330
elseif flow2.selected_tab_index == 3 then
flow2.style.height = 660
elseif flow2.selected_tab_index == 4 then
flow2.style.height = 330
end
end
return Public

View File

@ -0,0 +1,251 @@
local Memory = require 'maps.pirates.memory'
local Common = require 'maps.pirates.common'
-- local CoreData = require 'maps.pirates.coredata'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
local Surfaces = require 'maps.pirates.surfaces.surfaces'
-- local Roles = require 'maps.pirates.roles.roles'
-- local Crew = require 'maps.pirates.crew'
-- local Progression = require 'maps.pirates.progression'
-- local Structures = require 'maps.pirates.structures.structures'
local _inspect = require 'utils.inspect'.inspect
-- local Boats = require 'maps.pirates.structures.boats.boats'
local GuiCommon = require 'maps.pirates.gui.common'
local Public = {}
local window_name = 'minimap'
local default_zoom = 0.1
local default_size = 320
function Public.toggle_window(player)
local flow, flow2
local window = player.gui.screen[window_name .. '_piratewindow']
if window then
local switch_state = window.close_button_flow.hflow.switch_auto_map.switch_state
local auto_map = true
if switch_state == 'right' then
auto_map = false
end
GuiCommon.update_gui_memory(player, window_name, 'auto_map', auto_map)
window.destroy()
return
end -- else:
flow = GuiCommon.new_window(player, window_name)
flow.caption = 'Outside View'
flow.style.maximal_width = 800
local memory = Memory.get_crew_memory()
local global_memory = Memory.get_global_memory()
local gui_memory = global_memory.player_gui_memories[player.index]
local auto_map
if gui_memory and gui_memory[window_name] then
auto_map = gui_memory[window_name].auto_map
else
auto_map = true
end
local switch_state = 'right'
if auto_map then
switch_state = 'left'
end
if not (memory.boat and memory.boat.position and memory.boat.surface_name) then return end
local position = memory.boat.position
local destination = Common.current_destination()
if (destination and destination.type and destination.type == Surfaces.enum.ISLAND and destination.static_params and destination.static_params.boat_starting_xposition) then
-- nicer viewing position:
position = {x = destination.static_params.boat_starting_xposition + 50, y = destination.static_params.boat_starting_yposition or 0}
end
local zoom
if gui_memory and gui_memory[window_name] and gui_memory[window_name].zoom then
zoom = gui_memory[window_name].zoom
else
zoom = default_zoom
end
local size
if gui_memory and gui_memory[window_name] and gui_memory[window_name].size then
size = gui_memory[window_name].size
else
size = default_size
end
local element = flow['camera']
if not element then
element =
flow.add(
{
type = 'camera',
name = 'camera',
position = position,
surface_index = game.surfaces[memory.boat.surface_name].index,
zoom = zoom,
tooltip = 'LMB: Zoom in.\nRMB: Zoom out.\nMMB: Scale window.'
}
)
element.style.margin = 1
element.style.minimal_height = size
element.style.minimal_width = size
element.style.maximal_height = size
element.style.maximal_width = size
end
flow2 = GuiCommon.flow_add_close_button(flow, window_name .. '_piratebutton')
flow2.add(
{
type = 'switch',
name = 'switch_auto_map',
index = 1,
allow_none_state = false,
switch_state = switch_state,
left_label_caption = 'Auto Show Map — On',
right_label_caption = 'Off'
}
)
end
-- function Public.regular_update(player)
-- end
function Public.full_update(player)
if Public.regular_update then Public.regular_update(player) end
local flow
local memory = Memory.get_crew_memory()
if not player.gui.screen[window_name .. '_piratewindow'] then return end
flow = player.gui.screen[window_name .. '_piratewindow']
local element = flow['camera']
if element then
local position = memory.boat.position
local destination = Common.current_destination()
if (destination and destination.type and destination.type == Surfaces.enum.ISLAND and memory.boat.surface_name and memory.boat.surface_name == destination.surface_name and destination.static_params and destination.static_params.boat_starting_xposition) then
-- nicer viewing position:
position = {x = destination.static_params.boat_starting_xposition + 50, y = destination.static_params.boat_starting_yposition or 0}
end
if position then
element.position = position
end
if memory.boat.surface_name and game.surfaces[memory.boat.surface_name] and game.surfaces[memory.boat.surface_name].valid then
element.surface_index = game.surfaces[memory.boat.surface_name].index
end
end
end
function Public.click(event)
local player = game.players[event.element.player_index]
local eventname = event.element.name
if not player.gui.screen[window_name .. '_piratewindow'] then return end
-- local flow = player.gui.screen[window_name .. '_piratewindow']
-- local memory = Memory.get_crew_memory()
-- local shop_data = Shop.main_shop_data
-- if eventname == 'buy_button' then
-- Shop.Captains.main_shop_try_purchase(event.element.parent.name)
-- end
if eventname ~= 'camera' then return end
local zoom = default_zoom
local size = default_size
local global_memory = Memory.get_global_memory()
local gui_memory = global_memory.player_gui_memories[player.index]
if gui_memory and gui_memory[window_name] then
zoom = gui_memory[window_name].zoom or default_zoom
size = gui_memory[window_name].size or default_size
end
if event.button == defines.mouse_button_type.right then
if zoom == 0.15 then
zoom = 0.11
elseif zoom == 0.11 then
zoom = 0.07
else
zoom = 0.04
end
event.element.zoom = zoom
end
if event.button == defines.mouse_button_type.left then
if zoom == 0.04 then
zoom = 0.07
elseif zoom == 0.07 then
zoom = 0.11
else
zoom = 0.15
end
event.element.zoom = zoom
end
if event.button == defines.mouse_button_type.middle then
if size == 340 then
size = 440
elseif size == 440 then
size = 560
elseif size == 560 then
size = 700
elseif size == 700 then
size = 280
else
size = 340
end
event.element.style.minimal_height = size
event.element.style.minimal_width = size
event.element.style.maximal_height = size
event.element.style.maximal_width = size
end
GuiCommon.update_gui_memory(player, window_name, 'zoom', zoom)
GuiCommon.update_gui_memory(player, window_name, 'size', size)
end
local function on_player_changed_surface(event)
local player = game.players[event.player_index]
if not Common.validate_player_and_character(player) then
return
end
local window = player.gui.screen[window_name .. '_piratewindow']
local from_hold_bool = string.sub(game.surfaces[event.surface_index].name, 9, 12) == 'Hold'
local to_hold_bool = string.sub(player.surface.name, 9, 12) == 'Hold'
if from_hold_bool and (not to_hold_bool) then
if window then
Public.toggle_window(player)
end
elseif to_hold_bool and (not from_hold_bool) then
local global_memory = Memory.get_global_memory()
local gui_memory = global_memory.player_gui_memories[player.index]
if (gui_memory and gui_memory[window_name] and gui_memory[window_name].auto_map) or (not gui_memory) or (gui_memory and (not gui_memory[window_name])) then --if no gui memory exists for this, default to opening the minimap
Public.toggle_window(player)
end
end
end
local event = require 'utils.event'
event.add(defines.events.on_player_changed_surface, on_player_changed_surface)
return Public

View File

@ -0,0 +1,175 @@
local Memory = require 'maps.pirates.memory'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local Surfaces = require 'maps.pirates.surfaces.surfaces'
local Lobby = require 'maps.pirates.surfaces.lobby'
local _inspect = require 'utils.inspect'.inspect
-- local Boats = require 'maps.pirates.structures.boats.boats'
local GuiCommon = require 'maps.pirates.gui.common'
local Public = {}
local window_name = 'progress'
function Public.toggle_window(player)
if player.gui.screen[window_name .. '_piratewindow'] then player.gui.screen[window_name .. '_piratewindow'].destroy() return end
local flow, flow2, flow3
flow = GuiCommon.new_window(player, window_name)
flow.caption = 'Progress'
flow2 = GuiCommon.flow_add_section(flow, 'distance_travelled', 'Distance Travelled:')
flow3 = flow2.add({
name = 'leagues',
type = 'label',
})
flow3.style.left_margin = 5
flow3.style.top_margin = -3
flow3.style.bottom_margin = -3
flow3.style.single_line = false
flow3.style.maximal_width = 160
flow3.style.font = 'default-dropdown'
flow2 = GuiCommon.flow_add_section(flow, 'current_location', 'Current location: ')
-- flow3 = flow2.add({
-- name = 'location_name',
-- type = 'label',
-- })
-- flow3.style.left_margin = 5
-- flow3.style.top_margin = -3
-- flow3.style.bottom_margin = -3
-- flow3.style.single_line = false
-- flow3.style.maximal_width = 160
-- flow3.style.font = 'default-dropdown'
-- flow3 = flow2.add({type = 'label', name = 'hidden_ores_yes', caption = 'Ores detected:'})
-- flow3 = flow2.add({type = 'table', name = 'hidden_ores_yes_table', column_count = 3})
-- flow3.style.left_margin = 5
-- flow3.style.bottom_margin = 4
-- for _, ore in ipairs(CoreData.ore_types) do
-- flow3.add({type = 'sprite-button', name = ore.name, sprite = ore.sprite_name, enabled = false, number = 0})
-- end
-- flow3 = flow2.add({type = 'label', name = 'hidden_ores_no', caption = 'Ores detected: None'})
-- -- flow3 = flow2.add({type = 'label', name = 'daynight', caption = ''})
-- flow3 = flow2.add({type = 'label', name = 'patch_size', caption = ''})
-- flow3.style.top_margin = -3
flow3 = flow2.add({type = 'label', name = 'daynight', caption = ''})
flow3.style.top_margin = -3
-- flow2 = GuiCommon.flow_add_section(flow, 'departure_items', 'Resources needed for departure:')
-- flow3.style.bottom_margin = -2
-- flow3 = flow2.add({type = 'table', name = 'needed', column_count = 4})
-- flow3.style.left_margin = 5
-- for _, item in ipairs(CoreData.departure_items) do
-- flow3.add({type = 'sprite-button', name = item.name, sprite = item.sprite_name, enabled = false, number = 0})
-- end
GuiCommon.flow_add_close_button(flow, window_name .. '_piratebutton')
return nil
end
-- function Public.regular_update(player)
-- end
function Public.full_update(player)
if Public.regular_update then Public.regular_update(player) end
if not player.gui.screen[window_name .. '_piratewindow'] then return end
local flow = player.gui.screen[window_name .. '_piratewindow']
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
-- local type = destination.type
-- local subtype = destination.subtype
local scope = Surfaces.get_scope(destination)
local name
if scope then
name = (destination and destination.static_params and destination.static_params.name) and destination.static_params.name or scope.Data.display_name
else
name = Lobby.Data.display_name
end
flow.current_location.header.caption = string.format('Current location: %s', name)
flow.distance_travelled.body.leagues.caption = string.format('%d leagues', memory.overworldx or 0)
-- local daynighttype
-- if destination.static_params and destination.static_params.daynightcycletype then
-- daynighttype = destination.static_params.daynightcycletype
-- else
-- daynighttype = 1
-- end
-- flow.current_location.body.daynight.caption = string.format('Day/night cycle: %s', CoreData.daynightcycle_types[daynighttype].displayname)
-- if destination.static_params and destination.static_params.radius_squared_modifier then
-- local radius_squared_modifier = destination.static_params.radius_squared_modifier
-- flow.current_location.body.patch_size.visible = true
-- if radius_squared_modifier <= 0.65 then
-- flow.current_location.body.patch_size.caption = 'Patch sizing: ' .. 'Nano'
-- elseif radius_squared_modifier <= 0.85 then
-- flow.current_location.body.patch_size.caption = 'Patch sizing: ' .. 'Small'
-- elseif radius_squared_modifier <= 1.5 then
-- flow.current_location.body.patch_size.caption = 'Patch sizing: ' .. 'Normal'
-- else
-- flow.current_location.body.patch_size.caption = 'Patch sizing: ' .. 'Large'
-- end
-- else
-- flow.current_location.body.patch_size.visible = false
-- end
-- if destination.static_params and destination.static_params.daynightcycletype then
-- flow.current_location.body.daynight.visible = true
-- local daynightcycletype = destination.static_params.daynightcycletype
-- flow.current_location.body.daynight.caption = 'Daynight cycle: ' .. CoreData.daynightcycle_types[daynightcycletype].displayname
-- else
-- flow.current_location.body.daynight.visible = false
-- end
local daynightcycletype = destination.static_params.daynightcycletype or 1
flow.current_location.body.daynight.caption = 'Time of day: ' .. CoreData.daynightcycle_types[daynightcycletype].displayname
-- local ores
-- -- if destination.static_params and destination.static_params.abstract_ore_amounts then ores = destination.static_params.abstract_ore_amounts end
-- if destination.dynamic_data and destination.dynamic_data.hidden_ore_remaining_abstract then ores = destination.dynamic_data.hidden_ore_remaining_abstract end
-- if ores then
-- flow.current_location.body.hidden_ores_yes.visible = true
-- flow.current_location.body.hidden_ores_yes_table.visible = true
-- flow.current_location.body.patch_size.visible = true
-- flow.current_location.body.hidden_ores_no.visible = false
-- for _, ore in ipairs(CoreData.ore_types) do
-- if ores[ore.name] then
-- flow.current_location.body.hidden_ores_yes_table[ore.name].number = Math.ceil(ores[ore.name])
-- else
-- flow.current_location.body.hidden_ores_yes_table[ore.name].number = 0
-- end
-- end
-- else
-- flow.current_location.body.hidden_ores_yes.visible = false
-- flow.current_location.body.hidden_ores_yes_table.visible = false
-- flow.current_location.body.patch_size.visible = false
-- flow.current_location.body.hidden_ores_no.visible = true
-- end
end
return Public

682
maps/pirates/gui/runs.lua Normal file
View File

@ -0,0 +1,682 @@
local Memory = require 'maps.pirates.memory'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local Utils = require 'maps.pirates.utils_local'
local Math = require 'maps.pirates.math'
-- local Surfaces = require 'maps.pirates.surfaces.surfaces'
-- local Roles = require 'maps.pirates.roles.roles'
local Crew = require 'maps.pirates.crew'
local Progression = require 'maps.pirates.progression'
-- local Structures = require 'maps.pirates.structures.structures'
local _inspect = require 'utils.inspect'.inspect
local Boats = require 'maps.pirates.structures.boats.boats'
local GuiCommon = require 'maps.pirates.gui.common'
-- local Server = require 'utils.server'
local Public = {}
local window_name = 'runs'
local function flow_add_proposal_slider(flow, name, displayname, indices_count, starting_index, tooltip)
local flow2, flow3, flow4
flow2 = flow.add({
name = name,
type = 'flow',
direction = 'vertical',
})
flow2.style.horizontal_align = 'left'
flow2.style.width = 130
flow3 = flow2.add({
type = 'label',
caption = displayname,
})
flow3.style.font = 'heading-3'
flow3.style.height = 20
flow3.style.margin = 0
flow3.style.padding = 0
flow3.style.top_padding = -4
flow3.style.bottom_margin = 0
flow3.style.font_color = GuiCommon.subsection_header_font_color
flow3.tooltip = tooltip
flow3 = flow2.add({
name = name,
type = 'flow',
direction = 'vertical',
})
flow3.style.horizontal_align = 'center'
flow3.style.width = 130
flow4 = flow3.add({
name = 'slider',
type = 'slider',
value_step = 1,
minimum_value = 1,
maximum_value = indices_count,
value = starting_index,
discrete_values = true,
discrete_slider = true,
})
flow4.style.width = 100
flow4.style.margin = 0
flow4.tooltip = tooltip
flow4 = flow3.add({
name = 'readoff_text',
type = 'label',
caption = '',
})
flow4.style.font = 'default-listbox'
flow4.style.height = 20
flow4.style.margin = 0
flow4.style.padding = 0
flow4.style.top_padding = 0
flow4.style.bottom_margin = 16
flow4.tooltip = tooltip
flow2 = flow.add({
name = name .. '_readoff_icon',
type = 'sprite-button',
enabled = false,
})
flow2.style.width = 48
flow2.style.height = 48
flow2.tooltip = tooltip
return flow2
end
-- commented out for luacheck:
-- local function flow_add_proposal_switch(flow, name, displayname, starting_position, tooltip)
-- local flow2, flow3, flow4
-- flow2 = flow.add({
-- name = name,
-- type = 'flow',
-- direction = 'vertical',
-- })
-- flow2.style.horizontal_align = 'left'
-- flow2.style.width = 130
-- flow3 = flow2.add({
-- type = 'label',
-- caption = displayname,
-- })
-- flow3.style.font = 'heading-3'
-- flow3.style.height = 20
-- flow3.style.margin = 0
-- flow3.style.padding = 0
-- flow3.style.top_padding = -4
-- flow3.style.bottom_margin = 0
-- flow3.style.font_color = GuiCommon.subsection_header_font_color
-- flow3.tooltip = tooltip
-- flow3 = flow2.add({
-- name = name,
-- type = 'flow',
-- direction = 'vertical',
-- })
-- flow3.style.horizontal_align = 'center'
-- flow3.style.width = 130
-- flow4 = flow3.add({
-- name = 'switch',
-- type = 'switch',
-- switch_state = starting_position,
-- })
-- -- flow4.style.width = 80
-- -- flow4.style.height = 40
-- flow4.style.margin = 0
-- flow4.tooltip = tooltip
-- flow4 = flow3.add({
-- name = 'readoff_text',
-- type = 'label',
-- caption = '',
-- })
-- flow4.style.font = 'default-listbox'
-- flow4.style.height = 20
-- flow4.style.margin = 0
-- flow4.style.padding = 0
-- flow4.style.top_padding = 0
-- flow4.style.bottom_margin = 16
-- flow4.tooltip = tooltip
-- flow2 = flow.add({
-- name = name .. '_readoff_icon',
-- type = 'sprite-button',
-- enabled = false,
-- })
-- flow2.style.width = 48
-- flow2.style.height = 48
-- flow2.tooltip = tooltip
-- return flow2
-- end
function Public.toggle_window(player)
local flow, flow2, flow3, flow4, flow5
--*** OVERALL FLOW ***--
if player.gui.screen[window_name .. '_piratewindow'] then player.gui.screen[window_name .. '_piratewindow'].destroy() return end
flow = GuiCommon.new_window(player, window_name)
flow.caption = 'Play'
--*** ONGOING RUNS ***--
flow2 = GuiCommon.flow_add_section(flow, 'ongoing_runs', 'Ongoing Runs')
flow3 = flow2.add({
name = 'helpful_tip',
type = 'label',
caption = 'To join a run, first select it in the table below.',
})
flow3.style.font_color = {r=0.90, g=0.90, b=0.90}
flow3.style.single_line = false
flow3.style.maximal_width = 160
flow3 = flow2.add({
name = 'ongoing_runs_listbox',
type = 'list-box',
})
flow3.style.margin = 2
flow3.style.horizontally_stretchable = true
flow3 = flow2.add({
name = 'flow_buttons',
type = 'flow',
direction = 'horizontal',
})
flow4 = flow3.add({
name = 'join_spectators',
type = 'button',
caption = 'Spectate',
})
flow4.style.minimal_width = 95
flow4.style.font = 'default-bold'
flow4.style.font_color = {r=0.10, g=0.10, b=0.10}
flow4 = flow3.add({
name = 'join_crew',
type = 'button',
caption = 'Join Crew',
})
flow4.style.minimal_width = 95
flow4.style.font = 'default-bold'
flow4.style.font_color = {r=0.10, g=0.10, b=0.10}
flow4 = flow3.add({
name = 'leave_spectators',
type = 'button',
caption = 'Return to Lobby',
})
flow4.style.minimal_width = 95
flow4.style.font = 'default-bold'
flow4.style.font_color = {r=0.10, g=0.10, b=0.10}
flow3 = flow2.add({
name = 'wait_to_join',
type = 'label',
})
flow3.style.left_margin = 5
flow3 = flow2.add({
name = 'leaving_prompt',
type = 'label',
caption = 'Hop on board.',
})
flow3.style.left_margin = 5
-- PROPOSALS --
flow2 = GuiCommon.flow_add_section(flow, 'proposals', 'Proposals')
flow3 = flow2.add({
name = 'proposals_listbox',
type = 'list-box',
})
flow3.style.margin = 2
flow3 = flow2.add({
name = 'flow_buttons',
type = 'flow',
direction = 'horizontal',
})
flow4 = flow3.add({
name = 'endorse_proposal',
type = 'button',
caption = 'Endorse Proposal',
})
flow4.style.minimal_width = 150
flow4.style.font = 'default-bold'
flow4.style.font_color = {r=0.10, g=0.10, b=0.10}
flow4.style.bottom_margin = 9
flow4 = flow3.add({
name = 'retract_endorsement',
type = 'button',
caption = 'Retract Endorsement',
})
flow4.style.minimal_width = 150
flow4.style.font = 'default-bold'
flow4.style.font_color = {r=0.10, g=0.10, b=0.10}
flow4.style.bottom_margin = 9
flow4 = flow3.add({
name = 'abandon_proposal',
type = 'button',
caption = 'Abandon Proposal',
})
flow4.style.minimal_width = 150
flow4.style.font = 'default-bold'
flow4.style.font_color = {r=0.10, g=0.10, b=0.10}
flow4.style.bottom_margin = 9
-- PROPOSAL MAKER --
flow3 = GuiCommon.flow_add_subpanel(flow2, 'proposal_maker')
flow4 = flow3.add({
name = 'body',
type = 'flow',
direction = 'vertical',
})
flow4.style.horizontal_align = 'center'
flow4.style.vertical_align = 'center'
flow5 = flow4.add({
type = 'label',
caption = 'Run name',
})
flow5.style.font = 'heading-3'
flow5 = flow4.add({
name = 'namefield',
type = 'textfield',
caption = 'Name',
text = '',
})
flow5.style.width = 150
flow5.style.height = 24
flow5.style.top_margin = -3
flow5.style.bottom_margin = 3
flow5 = flow4.add({
name = 'options',
type = 'table',
column_count = 2,
})
flow5.style.width = 200
flow5.style.margin = 0
flow_add_proposal_slider(flow5, 'capacity', 'Capacity', #CoreData.capacity_options, 5, {'pirates.capacity_tooltip'})
-- flow_add_proposal_slider(flow5, 'difficulty', 'Difficulty', #CoreData.difficulty_options, 2, {'pirates.difficulty_tooltip'})
-- flow_add_proposal_switch(flow5, 'mode', 'Mode', 'left', {'pirates.mode_tooltip'})
-- flow5 = flow4.add({
-- name = 'proposal_cant_do_infinity_mode',
-- type = 'label',
-- caption = 'Infinity mode isn\'t available at the moment.',
-- })
-- flow5.style.single_line = false
-- flow5.style.maximal_width = 200
flow5 = flow4.add({
name = 'proposal_disabled_low_crew_caps',
type = 'label',
caption = 'This capacity setting isn\'t available at the moment.',
})
flow5.style.single_line = false
flow5.style.maximal_width = 200
flow5 = flow4.add({
name = 'propose_crew',
type = 'button',
caption = 'Propose',
})
flow5.style.minimal_width = 75
flow5.style.font = 'default-bold'
flow5.style.font_color = {r=0.10, g=0.10, b=0.10}
-- LAUNCH YOUR PROPOSAL --
flow3 = flow2.add({
name = 'flow_proposal_launch',
type = 'flow',
direction = 'vertical',
})
flow4 = flow3.add({
name = 'proposal_insufficient_endorsers',
type = 'label',
caption = 'Gather support from more pirates.',
})
flow4.style.single_line = false
flow4 = flow3.add({
name = 'proposal_crew_count_capped',
type = 'label',
caption = 'The number of concurrent runs on the server has reached the cap set by the admins.',
})
flow4.style.single_line = false
flow4 = flow3.add({
name = 'proposal_insufficient_player_capacity',
type = 'label',
caption = "Can't launch; at least one run needs high player capacity.",
})
flow4.style.single_line = false
flow4 = flow3.add({
name = 'proposal_insufficient_sloops',
type = 'label',
caption = 'No sloops available. Join an existing run instead.',
})
flow4.style.single_line = false
flow4 = flow3.add({
name = 'launch_crew',
type = 'button',
caption = 'Launch run',
})
flow4.style.minimal_width = 150
flow4.style.font = 'default-bold'
flow4.style.font_color = {r=0.10, g=0.10, b=0.10}
GuiCommon.flow_add_close_button(flow, window_name .. '_piratebutton')
end
-- function Public.regular_update(player)
-- end
function Public.full_update(player)
if Public.regular_update then Public.regular_update(player) end
local global_memory = Memory.get_global_memory()
local memory = Memory.get_crew_memory()
if not player.gui.screen['runs_piratewindow'] then return end
local flow = player.gui.screen['runs_piratewindow']
local playercrew_status = GuiCommon.crew_overall_state_bools(player.index)
if not playercrew_status then return end
--*** WHAT TO SHOW ***--
flow.ongoing_runs.visible = (#global_memory.crew_active_ids > 0)
if flow.ongoing_runs.visible then
local bool1 = (not playercrew_status.leaving) and (not playercrew_status.adventuring) and (not playercrew_status.spectating) and (flow.ongoing_runs.body.ongoing_runs_listbox.selected_index ~= 0)
local selected_joinable_bool = false
local crewid
if bool1 then
crewid = tonumber((flow.ongoing_runs.body.ongoing_runs_listbox.get_item(flow.ongoing_runs.body.ongoing_runs_listbox.selected_index))[2])
selected_joinable_bool = bool1 and crewid and (global_memory.crew_memories[crewid].crewstatus == Crew.enum.ADVENTURING)
end
flow.ongoing_runs.body.helpful_tip.visible = not (playercrew_status.leaving or playercrew_status.adventuring or playercrew_status.spectating)
flow.ongoing_runs.body.flow_buttons.visible = selected_joinable_bool or playercrew_status.spectating
flow.ongoing_runs.body.flow_buttons.join_spectators.visible = selected_joinable_bool
flow.ongoing_runs.body.flow_buttons.leave_spectators.visible = playercrew_status.spectating
flow.ongoing_runs.body.flow_buttons.join_crew.visible = selected_joinable_bool and (not (crewid and global_memory.crew_memories[crewid] and (global_memory.crew_memories[crewid].crewstatus == Crew.enum.LEAVING_INITIAL_DOCK or #global_memory.crew_memories[crewid].crewplayerindices >= global_memory.crew_memories[crewid].capacity or (global_memory.crew_memories[crewid].tempbanned_from_joining_data and global_memory.crew_memories[crewid].tempbanned_from_joining_data[player.index] and game.tick < global_memory.crew_memories[crewid].tempbanned_from_joining_data[player.index] + Common.ban_from_rejoining_crew_ticks))))
flow.ongoing_runs.body.wait_to_join.visible = selected_joinable_bool and crewid and global_memory.crew_memories[crewid] and (global_memory.crew_memories[crewid].tempbanned_from_joining_data and global_memory.crew_memories[crewid].tempbanned_from_joining_data[player.index] and game.tick < global_memory.crew_memories[crewid].tempbanned_from_joining_data[player.index] + Common.ban_from_rejoining_crew_ticks) and (not (global_memory.crew_memories[crewid].crewstatus == Crew.enum.LEAVING_INITIAL_DOCK or #global_memory.crew_memories[crewid].crewplayerindices >= global_memory.crew_memories[crewid].capacity))
if flow.ongoing_runs.body.wait_to_join.visible then
flow.ongoing_runs.body.wait_to_join.caption = 'Wait to join... ' .. Math.ceil(((global_memory.crew_memories[crewid].tempbanned_from_joining_data[player.index] - (game.tick - Common.ban_from_rejoining_crew_ticks)))/60)
end
if not selected_joinable_bool then flow.ongoing_runs.body.ongoing_runs_listbox.selected_index = 0 end
flow.ongoing_runs.body.leaving_prompt.visible = playercrew_status.leaving
end
flow.proposals.visible = (memory.crewstatus == nil and not playercrew_status.leaving)
if flow.proposals.visible then
if playercrew_status.proposing then
flow.proposals.body.proposals_listbox.selected_index = 0
flow.proposals.body.proposals_listbox.selected_index = 0
end
flow.proposals.body.proposals_listbox.visible = (not playercrew_status.leaving) and (#global_memory.crewproposals > 0)
flow.proposals.body.flow_buttons.endorse_proposal.visible = (not playercrew_status.leaving) and (not playercrew_status.endorsing) and (#global_memory.crewproposals > 0) and flow.proposals.body.proposals_listbox.selected_index ~= 0
flow.proposals.body.flow_buttons.abandon_proposal.visible = (not playercrew_status.leaving) and playercrew_status.endorsing and playercrew_status.endorsing and playercrew_status.proposing and (#global_memory.crewproposals > 0)
flow.proposals.body.flow_buttons.retract_endorsement.visible = (not playercrew_status.leaving) and playercrew_status.endorsing and (not playercrew_status.proposing) and (#global_memory.crewproposals > 0)
flow.proposals.body.proposal_maker.visible = (not playercrew_status.leaving) and (not playercrew_status.endorsing)
flow.proposals.body.flow_proposal_launch.proposal_insufficient_sloops.visible = playercrew_status.sloops_full
flow.proposals.body.flow_proposal_launch.proposal_insufficient_player_capacity.visible = playercrew_status.needs_more_capacity
flow.proposals.body.flow_proposal_launch.proposal_crew_count_capped.visible = playercrew_status.crew_count_capped
flow.proposals.body.flow_proposal_launch.proposal_insufficient_endorsers.visible = playercrew_status.needs_more_endorsers
-- flow.proposals.body.proposal_maker.body.proposal_cant_do_infinity_mode.visible = (flow.proposals.body.proposal_maker.body.options.mode.mode.switch.switch_state == 'right')
-- flow.proposals.body.proposal_maker.body.proposal_disabled_low_crew_caps.visible = false
flow.proposals.body.proposal_maker.body.proposal_disabled_low_crew_caps.visible = (flow.proposals.body.proposal_maker.body.options.capacity.capacity.slider.slider_value < global_memory.minimum_capacity_slider_value)
flow.proposals.body.proposal_maker.body.propose_crew.visible = (flow.proposals.body.proposal_maker.body.proposal_disabled_low_crew_caps.visible == false)
-- flow.proposals.body.proposal_maker.body.propose_crew.visible = (flow.proposals.body.proposal_maker.body.proposal_cant_do_infinity_mode.visible == false) and (flow.proposals.body.proposal_maker.body.proposal_disabled_low_crew_caps.visible == false)
flow.proposals.body.flow_proposal_launch.launch_crew.visible = playercrew_status.proposal_can_launch
end
--*** UPDATE CONTENT ***--
if flow.ongoing_runs.visible then
local wrappedmemories = {}
for _, mem in pairs(global_memory.crew_memories) do
local count = 0
if mem.crewstatus and mem.crewstatus == Crew.enum.LEAVING_INITIAL_DOCK then
count = Boats.players_on_boat_count(mem.boat)
elseif mem.crewplayerindices then
count = #mem.crewplayerindices
end
wrappedmemories[#wrappedmemories + 1] = {'pirates.run_displayform', mem.id, mem.name .. ', ' .. CoreData.difficulty_options[mem.difficulty_option].text .. ', [item=light-armor]' .. count .. CoreData.capacity_options[mem.capacity_option].text2 .. ', [item=rail] ' .. (mem.overworldx or 0)}
-- wrappedmemories[#wrappedmemories + 1] = {'pirates.run_displayform', mem.id, mem.name, Utils.spritepath_to_richtext(CoreData.difficulty_options[mem.difficulty_option].icon), count, CoreData.capacity_options[mem.capacity_option].text2, ' [item=rail] ', mem.overworldx or 0}
end
GuiCommon.update_listbox(flow.ongoing_runs.body.ongoing_runs_listbox, wrappedmemories)
end
if flow.proposals.visible then
local wrappedproposals = {}
for _, proposal in pairs(global_memory.crewproposals) do
wrappedproposals[#wrappedproposals + 1] = {'pirates.proposal_displayform', proposal.name, Utils.spritepath_to_richtext(CoreData.capacity_options[proposal.capacity_option].icon)}
-- wrappedproposals[#wrappedproposals + 1] = {'pirates.proposal_displayform', proposal.name, Utils.spritepath_to_richtext(CoreData.difficulty_options[proposal.difficulty_option].icon), Utils.spritepath_to_richtext(CoreData.capacity_options[proposal.capacity_option].icon)}
end
GuiCommon.update_listbox(flow.proposals.body.proposals_listbox, wrappedproposals)
end
-- update proposal maker
if flow.proposals.body.proposal_maker.visible then
local capacity_slider_value = flow.proposals.body.proposal_maker.body.options.capacity.capacity.slider.slider_value
for i, opt in pairs(CoreData.capacity_options) do
if capacity_slider_value == i then
flow.proposals.body.proposal_maker.body.options.capacity.capacity.readoff_text.caption = opt.text
flow.proposals.body.proposal_maker.body.options.capacity_readoff_icon.sprite = opt.icon
end
end
if flow.proposals.body.proposal_maker.body.options.capacity.capacity.readoff_text.caption == '' then flow.proposals.body.proposal_maker.body.options.capacity.capacity.readoff_text.caption = 'No limit' end
-- local difficulty_slider_value = flow.proposals.body.proposal_maker.body.options.difficulty.difficulty.slider.slider_value
-- for i, opt in pairs(CoreData.difficulty_options) do
-- if difficulty_slider_value == i then
-- flow.proposals.body.proposal_maker.body.options.difficulty.difficulty.readoff_text.caption = opt.text
-- flow.proposals.body.proposal_maker.body.options.difficulty_readoff_icon.sprite = opt.icon
-- end
-- end
-- local mode_switch_state = flow.proposals.body.proposal_maker.body.options.mode.mode.switch.switch_state
-- for i, opt in pairs(CoreData.mode_options) do
-- if mode_switch_state == i then
-- flow.proposals.body.proposal_maker.body.options.mode.mode.readoff_text.caption = opt.text
-- flow.proposals.body.proposal_maker.body.options.mode_readoff_icon.sprite = opt.icon
-- end
-- end
end
end
function Public.click(event)
local player = game.players[event.element.player_index]
local eventname = event.element.name
if not player.gui.screen[window_name .. '_piratewindow'] then return end
local flow = player.gui.screen[window_name .. '_piratewindow']
local global_memory = Memory.get_global_memory()
-- local memory = Memory.get_crew_memory()
if eventname == 'join_spectators' then
local listbox = flow.ongoing_runs.body.ongoing_runs_listbox
Crew.join_spectators(player, tonumber(listbox.get_item(listbox.selected_index)[2]))
return
end
if eventname == 'leave_spectators' then
Crew.leave_spectators(player)
return
end
if eventname == 'join_crew' then
local listbox = flow.ongoing_runs.body.ongoing_runs_listbox
Crew.join_crew(player, tonumber(listbox.get_item(listbox.selected_index)[2]))
return
end
if eventname == 'propose_crew' then
local proposal_name = flow.proposals.body.proposal_maker.body.namefield.text
-- local proposal_name = string.sub(flow.proposals.body.proposal_maker.body.namefield.text, 1, 30)
local capacity_option = flow.proposals.body.proposal_maker.body.options.capacity.capacity.slider.slider_value
local difficulty_option = 1
-- local difficulty_option = flow.proposals.body.proposal_maker.body.options.difficulty.difficulty.slider.slider_value
-- local mode_option = flow.proposals.body.proposal_maker.body.options.mode.mode.switch.switch_state
if (not proposal_name) or (proposal_name == '') then proposal_name = 'NoName' end
-- make name unique
-- local unique, changed = true, false
-- local check_unique = function(name)
-- unique = true
-- for _, proposal in pairs(global_memory.crewproposals) do
-- if name == proposal.name then
-- unique = false
-- changed = true
-- break
-- end
-- end
-- end
-- local i = 0
-- check_unique()
-- while i < 10 and not unique do
-- check_unique(proposal_name .. i)
-- i = i + 1
-- end
-- if not unique then return end
-- if changed then proposal_name = proposal_name .. i end
local unique = true
for _, proposal in pairs(global_memory.crewproposals) do
if proposal_name == proposal.name then
unique = false
break
end
end
if not unique then return end
local proposal = {
name = proposal_name,
difficulty_option = difficulty_option,
capacity_option = capacity_option,
-- mode_option = mode_option,
endorserindices = {player.index},
}
global_memory.crewproposals[#global_memory.crewproposals + 1] = proposal
local message = player.name .. ' proposed the run ' .. proposal_name .. ' [capacity ' .. CoreData.capacity_options[capacity_option].text3 .. '].'
-- local message = player.name .. ' proposed the run ' .. proposal_name .. ' (difficulty ' .. CoreData.difficulty_options[difficulty_option].text .. ', capacity ' .. CoreData.capacity_options[capacity_option].text3 .. ').'
Common.notify_lobby(message)
return
end
if eventname == 'endorse_proposal' then
local lb = flow.proposals.body.proposals_listbox
local index = lb.selected_index
if index ~= 0 then
local name2 = lb.get_item(lb.selected_index)[2]
for _, proposal in pairs(global_memory.crewproposals) do
if proposal.name == name2 and #proposal.endorserindices < CoreData.capacity_options[proposal.capacity_option].value then
proposal.endorserindices[#proposal.endorserindices + 1] = player.index
end
end
end
return
end
if eventname == 'abandon_proposal' then
Crew.player_abandon_proposal(player)
Crew.player_abandon_endorsements(player)
return
end
if eventname == 'retract_endorsement' then
Crew.player_abandon_endorsements(player)
return
end
if eventname == 'launch_crew' then
if GuiCommon.crew_overall_state_bools(player.index).proposal_can_launch then --double check
for k, proposal in pairs(global_memory.crewproposals) do
if #proposal.endorserindices > 0 and proposal.endorserindices[1] == player.index then
Crew.initialise_crew(proposal)
global_memory.crewproposals[k] = nil
Progression.set_off_from_starting_dock()
return
end
end
end
end
end
return Public

619
maps/pirates/highscore.lua Normal file
View File

@ -0,0 +1,619 @@
-- Adapted from mountain_fortress_v3 highscores.lua
local Event = require 'utils.event'
local Global = require 'utils.global'
local Server = require 'utils.server'
local Math = require 'maps.pirates.math'
local Token = require 'utils.token'
local Tabs = require 'utils.gui'
require 'utils.core'
local _inspect = require 'utils.inspect'.inspect
local SpamProtection = require 'utils.spam_protection'
-- local Memory = require 'maps.pirates.memory'
local Utils = require 'maps.pirates.utils_local'
local CoreData = require 'maps.pirates.coredata'
local module_name = 'Highscore'
local score_dataset = 'highscores'
local score_key = 'pirate_ship_scores'
local score_key_debug = 'pirate_ship_scores_debug'
local score_key_modded = 'pirate_ship_scores_modded'
local Public = {}
local insert = table.insert
local this = {
score_table = {player = {}},
sort_by = {}
}
Global.register(
this,
function(t)
this = t
end
)
local function sort_list(method, column_name, score_list)
local comparators = {
['ascending'] = function(a, b)
if column_name == 'completion_time' then
return (a[column_name] < b[column_name]) and not (a[column_name] == 0 and b[column_name] ~= 0)
else
return a[column_name] < b[column_name]
end
end,
['descending'] = function(a, b)
if column_name == 'completion_time' then
return (a[column_name] > b[column_name])
else
return a[column_name] > b[column_name]
end
end
}
Utils.stable_sort(score_list, comparators[method])
-- table.sort(score_list, comparators[method])
return score_list
end
local function get_tables_of_scores_by_type(scores)
local completion_times = {}
local leagues_travelled = {}
local completion_times_mediump_latestv = {}
local leagues_travelled_mediump_latestv = {}
local completion_times_hard = {}
local leagues_travelled_hard = {}
local completion_times_nightmare = {}
local leagues_travelled_nightmare = {}
local completion_times_latestv = {}
local leagues_travelled_latestv = {}
local versions = {}
for _, score in pairs(scores) do
if score.version and score.version > 0 then
versions[#versions + 1] = score.version
end
if score.completion_time and score.completion_time > 0 then
completion_times[#completion_times + 1] = score.completion_time
end
if score.leagues_travelled and score.leagues_travelled > 0 then
leagues_travelled[#leagues_travelled + 1] = score.leagues_travelled
end
if score.difficulty and score.difficulty >= 1.5 then
if score.completion_time and score.completion_time > 0 then
completion_times_hard[#completion_times_hard + 1] = score.completion_time
end
if score.leagues_travelled and score.leagues_travelled > 0 then
leagues_travelled_hard[#leagues_travelled_hard + 1] = score.leagues_travelled
end
end
if score.difficulty and score.difficulty >= 3 then
if score.completion_time and score.completion_time > 0 then
completion_times_nightmare[#completion_times_nightmare + 1] = score.completion_time
end
if score.leagues_travelled and score.leagues_travelled > 0 then
leagues_travelled_nightmare[#leagues_travelled_nightmare + 1] = score.leagues_travelled
end
end
end
local latest_version = 0
for _, v in pairs(versions) do
if v > latest_version then latest_version = v end
end
for _, score in pairs(scores) do
if score.version and score.version == latest_version then
if score.completion_time and score.completion_time > 0 then
completion_times_latestv[#completion_times_latestv + 1] = score.completion_time
end
if score.leagues_travelled and score.leagues_travelled > 0 then
leagues_travelled_latestv[#leagues_travelled_latestv + 1] = score.leagues_travelled
end
if score.difficulty and score.difficulty >= 1 then
if score.completion_time and score.completion_time > 0 then
completion_times_mediump_latestv[#completion_times_mediump_latestv + 1] = score.completion_time
end
if score.leagues_travelled and score.leagues_travelled > 0 then
leagues_travelled_mediump_latestv[#leagues_travelled_mediump_latestv + 1] = score.leagues_travelled
end
end
end
end
table.sort(completion_times)
table.sort(leagues_travelled)
table.sort(completion_times_mediump_latestv)
table.sort(leagues_travelled_mediump_latestv)
table.sort(completion_times_hard)
table.sort(leagues_travelled_hard)
table.sort(completion_times_nightmare)
table.sort(leagues_travelled_nightmare)
table.sort(completion_times_latestv)
table.sort(leagues_travelled_latestv)
return {
latest_version = latest_version,
completion_times = completion_times,
leagues_travelled = leagues_travelled,
completion_times_mediump_latestv = completion_times_mediump_latestv,
leagues_travelled_mediump_latestv = leagues_travelled_mediump_latestv,
completion_times_hard = completion_times_hard,
leagues_travelled_hard = leagues_travelled_hard,
completion_times_nightmare = completion_times_nightmare,
leagues_travelled_nightmare = leagues_travelled_nightmare,
completion_times_latestv = completion_times_latestv,
leagues_travelled_latestv = leagues_travelled_latestv,
}
end
local function get_score_cuttofs(tables_of_scores_by_type)
local completion_times_cutoff = #tables_of_scores_by_type.completion_times > 8 and tables_of_scores_by_type.completion_times[8] or 9999999
local completion_times_mediump_latestv_cutoff = #tables_of_scores_by_type.completion_times_mediump_latestv > 4 and tables_of_scores_by_type.completion_times_mediump_latestv[4] or 9999999
local completion_times_hard_cutoff = #tables_of_scores_by_type.completion_times_hard > 4 and tables_of_scores_by_type.completion_times_hard[4] or 9999999
local completion_times_nightmare_cutoff = #tables_of_scores_by_type.completion_times_hard > 2 and tables_of_scores_by_type.completion_times_hard[2] or 9999999
local completion_times_latestv_cutoff = #tables_of_scores_by_type.completion_times_latestv > 8 and tables_of_scores_by_type.completion_times_latestv[8] or 9999999
local leagues_travelled_cutoff = #tables_of_scores_by_type.leagues_travelled > 8 and tables_of_scores_by_type.leagues_travelled[-8] or 0
local leagues_travelled_mediump_latestv_cutoff = #tables_of_scores_by_type.leagues_travelled_mediump_latestv > 4 and tables_of_scores_by_type.leagues_travelled_mediump_latestv[-4] or 0
local leagues_travelled_hard_cutoff = #tables_of_scores_by_type.leagues_travelled_hard > 4 and tables_of_scores_by_type.leagues_travelled_hard[-4] or 0
local leagues_travelled_nightmare_cutoff = #tables_of_scores_by_type.leagues_travelled_hard > 2 and tables_of_scores_by_type.leagues_travelled_hard[-2] or 0
local leagues_travelled_latestv_cutoff = #tables_of_scores_by_type.leagues_travelled_latestv > 86 and tables_of_scores_by_type.leagues_travelled_latestv[-8] or 0
return {
completion_times_cutoff = completion_times_cutoff,
completion_times_mediump_latestv_cutoff = completion_times_mediump_latestv_cutoff,
completion_times_hard_cutoff = completion_times_hard_cutoff,
completion_times_nightmare_cutoff = completion_times_nightmare_cutoff,
completion_times_latestv_cutoff = completion_times_latestv_cutoff,
leagues_travelled_cutoff = leagues_travelled_cutoff,
leagues_travelled_mediump_latestv_cutoff = leagues_travelled_mediump_latestv_cutoff,
leagues_travelled_hard_cutoff = leagues_travelled_hard_cutoff,
leagues_travelled_nightmare_cutoff = leagues_travelled_nightmare_cutoff,
leagues_travelled_latestv_cutoff = leagues_travelled_latestv_cutoff,
}
end
local function saved_scores_trim(scores)
-- the goal here is to trim away highscores so we don't have too many.
local tables_of_scores_by_type = get_tables_of_scores_by_type(scores)
local cutoffs = get_score_cuttofs(tables_of_scores_by_type)
-- log(_inspect{completion_times_cutoff,completion_times_mediump_latestv_cutoff,completion_times_hard_cutoff,completion_times_latestv_cutoff,leagues_travelled_cutoff,leagues_travelled_mediump_latestv_cutoff,leagues_travelled_hard_cutoff,leagues_travelled_latestv_cutoff})
local delete = {}
for secs_id, score in pairs(scores) do
local include = false
if cutoffs.completion_times_cutoff and score.completion_time and score.completion_time < cutoffs.completion_times_cutoff then include = true
elseif cutoffs.completion_times_mediump_latestv_cutoff and score.completion_time and score.completion_time < cutoffs.completion_times_mediump_latestv_cutoff and score.version == cutoffs.latest_version and score.difficulty >= 1 then include = true
elseif cutoffs.completion_times_hard_cutoff and score.completion_time and score.completion_time < cutoffs.completion_times_hard_cutoff and score.difficulty >= 1.5 then include = true
elseif cutoffs.completion_times_nightmare_cutoff and score.completion_time and score.completion_time < cutoffs.completion_times_nightmare_cutoff and score.difficulty >=3 then include = true
elseif cutoffs.completion_times_latestv_cutoff and score.completion_time and score.completion_time < cutoffs.completion_times_latestv_cutoff and score.version == cutoffs.latest_version then include = true
elseif cutoffs.leagues_travelled_cutoff and score.leagues_travelled and score.leagues_travelled > cutoffs.leagues_travelled_cutoff then include = true
elseif cutoffs.leagues_travelled_mediump_latestv_cutoff and score.leagues_travelled and score.leagues_travelled > cutoffs.leagues_travelled_mediump_latestv_cutoff and score.version == cutoffs.latest_version and score.difficulty >= 1 then include = true
elseif cutoffs.leagues_travelled_hard_cutoff and score.leagues_travelled and score.leagues_travelled > cutoffs.leagues_travelled_hard_cutoff and score.difficulty >= 1.5 then include = true
elseif cutoffs.leagues_travelled_nightmare_cutoff and score.leagues_travelled and score.leagues_travelled > cutoffs.leagues_travelled_nightmare_cutoff and score.difficulty >= 3 then include = true
elseif cutoffs.leagues_travelled_latestv_cutoff and score.leagues_travelled and score.leagues_travelled > cutoffs.leagues_travelled_latestv_cutoff and score.version == cutoffs.latest_version then include = true
end
if not include then delete[#delete + 1] = secs_id end
end
-- log(_inspect(delete))
for _, secs_id in pairs(delete) do
scores[secs_id] = nil
end
return scores
end
local function local_highscores_write_stats(crew_secs_id, name, captain_name, completion_time, leagues_travelled, version, difficulty, max_players)
if not this.score_table['player'] then this.score_table['player'] = {} end
if not this.score_table['player'].runs then this.score_table['player'].runs = {} end
local t = this.score_table['player']
if t then
-- if name then
-- t.name = name
-- end
-- if version then
-- t.version = version
-- end
-- if completion_time then
-- t.completion_time = completion_time
-- end
-- if leagues_travelled then
-- t.leagues_travelled = leagues_travelled
-- end
-- if difficulty then
-- t.difficulty = difficulty
-- end
-- if max_players then
-- t.max_players = max_players
-- end
if crew_secs_id then
t.runs[crew_secs_id] = {name = name, captain_name = captain_name, version = version, completion_time = completion_time, leagues_travelled = leagues_travelled, difficulty = difficulty, max_players = max_players}
-- log(_inspect(t))
saved_scores_trim(t.runs)
end
end
this.score_table['player'] = t
-- log(_inspect(t))
end
local load_in_scores =
Token.register(
function(data)
local value = data.value
if not this.score_table['player'] then
this.score_table['player'] = {}
end
this.score_table['player'] = value
end
)
function Public.load_in_scores()
local secs = Server.get_current_time()
-- if secs then game.print('secs2: ' .. secs) else game.print('secs: false') end
if not secs then
return
else
-- FULL CLEAN task (erases everything...):
-- server_set_data(score_dataset, score_key, {})
if is_game_modded() then
Server.try_get_data(score_dataset, score_key_modded, load_in_scores)
elseif _DEBUG then
Server.try_get_data(score_dataset, score_key_debug, load_in_scores)
else
Server.try_get_data(score_dataset, score_key, load_in_scores)
end
end
end
function Public.dump_highscores()
log(_inspect(this.score_table['player']))
end
function Public.overwrite_scores_specific()
-- the correct format is to put _everything_ from a dump into the third argument:
-- Server.set_data(score_dataset, score_key, )
-- return true
return nil
end
function Public.write_score(crew_secs_id, name, captain_name, completion_time, leagues_travelled, version, difficulty, max_players)
local secs = Server.get_current_time()
-- if secs then game.print('secs1: ' .. secs) else game.print('secs: false') end
if not secs then
return
else
local_highscores_write_stats(crew_secs_id, name, captain_name, completion_time, leagues_travelled, version, difficulty, max_players)
if is_game_modded() then
Server.set_data(score_dataset, score_key_modded, this.score_table['player'])
elseif _DEBUG then
Server.set_data(score_dataset, score_key_debug, this.score_table['player'])
else
Server.set_data(score_dataset, score_key, this.score_table['player'])
end
end
end
local function on_init()
local secs = Server.get_current_time()
if not secs then
local_highscores_write_stats() --just to init tables presumably
return
end
end
local sorting_symbol = {ascending = '', descending = ''}
local function get_saved_scores_for_displaying()
local score_data = this.score_table['player']
local score_list = {}
if score_data and score_data.runs then
for _, score in pairs(score_data.runs or {}) do
insert(
score_list,
{
name = score and score.name,
captain_name = score and score.captain_name,
completion_time = score and score.completion_time or 99999,
leagues_travelled = score and score.leagues_travelled or 0,
version = score and score.version or 0,
difficulty = score and score.difficulty or 0,
max_players = score and score.max_players or 0,
}
)
end
else
score_list[#score_list + 1] = {
name = 'Nothing here yet',
captain_name = '',
completion_time = 0,
leagues_travelled = 0,
version = 0,
difficulty = 0,
max_players = 0,
}
end
return score_list
end
local function score_gui(data)
local player = data.player
local frame = data.frame
frame.clear()
local columnwidth = 96
-- local flow = frame.add {type = 'flow'}
-- local sFlow = flow.style
-- sFlow.horizontally_stretchable = true
-- sFlow.horizontal_align = 'center'
-- sFlow.vertical_align = 'center'
-- local stats = flow.add {type = 'label', caption = 'Highest score so far:'}
-- local s_stats = stats.style
-- s_stats.font = 'heading-1'
-- s_stats.font_color = {r = 0.98, g = 0.66, b = 0.22}
-- s_stats.horizontal_align = 'center'
-- s_stats.vertical_align = 'center'
-- -- Global stats : rockets, biters kills
-- add_global_stats(frame)
-- -- Separator
-- local line = frame.add {type = 'line'}
-- line.style.top_margin = 8
-- line.style.bottom_margin = 8
-- Score per player
local t = frame.add {type = 'table', column_count = 7}
-- Score headers
local headers = {
{name = '_name', caption = 'Crew'},
{name = '_captain_name', caption = 'Captain'},
{column = 'completion_time', name = '_completion_time', caption = 'Completion'},
{column = 'leagues_travelled', name = '_leagues_travelled', caption = 'Leagues'},
{column = 'version', name = '_version', caption = 'Version'},
{column = 'difficulty', name = '_difficulty', caption = 'Difficulty'},
{column = 'max_players', name = '_max_players', caption = 'PeakPlayers'},
}
local sorting_pref = this.sort_by[player.index] or {}
for _, header in ipairs(headers) do
local cap = header.caption
-- log(header.caption)
-- Add sorting symbol if any
if header.column and sorting_pref[1] and sorting_pref[1].column == header.column then
local symbol = sorting_symbol[sorting_pref[1].method]
cap = symbol .. cap
end
-- Header
local label =
t.add {
type = 'label',
caption = cap,
name = header.name
}
label.style.font = 'default-listbox'
label.style.font_color = {r = 0.98, g = 0.66, b = 0.22} -- yellow
label.style.minimal_width = columnwidth
label.style.horizontal_align = 'right'
end
-- Score list
local score_list = get_saved_scores_for_displaying()
-- log(_inspect(score_list))
for i = #sorting_pref, 1, -1 do
local sort = sorting_pref[i]
if sort then
score_list = sort_list(sort.method, sort.column, score_list)
end
end
-- New pane for scores (while keeping headers at same position)
local scroll_pane =
frame.add(
{
type = 'scroll-pane',
name = 'score_scroll_pane',
direction = 'vertical',
horizontal_scroll_policy = 'never',
vertical_scroll_policy = 'auto'
}
)
scroll_pane.style.maximal_height = 400
t = scroll_pane.add {type = 'table', column_count = 7}
-- Score entries
for _, entry in pairs(score_list) do
local p = {color = {r = Math.random(1, 255), g = Math.random(1, 255), b = Math.random(1, 255)}}
-- local p
-- if not (entry and entry.name) then
-- p = {color = {r = random(1, 255), g = random(1, 255), b = random(1, 255)}}
-- else
-- p = game.players[entry.name]
-- if not p then
-- p = {color = {r = random(1, 255), g = random(1, 255), b = random(1, 255)}}
-- end
-- end
local special_color = {
r = p.color.r * 0.6 + 0.4,
g = p.color.g * 0.6 + 0.4,
b = p.color.b * 0.6 + 0.4,
a = 1,
}
-- displayforms:
local n = entry.completion_time > 0 and Utils.time_mediumform(entry.completion_time or 0) or 'N/A'
local l = entry.leagues_travelled > 0 and entry.leagues_travelled or '?'
local v = entry.version > 0 and entry.version or '?'
local d = entry.difficulty > 0 and CoreData.get_difficulty_name_from_value(entry.difficulty) or '?'
local c = entry.max_players > 0 and entry.max_players or '?'
local line = {
{caption = entry.name, color = special_color},
{caption = entry.captain_name or '?'},
{caption = tostring(n)},
{caption = tostring(l)},
{caption = tostring(v)},
{caption = tostring(d)},
{caption = tostring(c)},
}
local default_color = {r = 0.9, g = 0.9, b = 0.9}
for _, column in ipairs(line) do
local label =
t.add {
type = 'label',
caption = column.caption,
color = column.color or default_color,
}
label.style.font = 'default'
label.style.minimal_width = columnwidth
label.style.maximal_width = columnwidth
label.style.horizontal_align = 'right'
end -- foreach column
end -- foreach entry
end
local score_gui_token = Token.register(score_gui)
local function on_gui_click(event)
if not event then
return
end
if not event.element then
return
end
if not event.element.valid then
return
end
local player = game.players[event.element.player_index]
local frame = Tabs.comfy_panel_get_active_frame(player)
if not frame then
return
end
if frame.name ~= module_name then
return
end
local is_spamming = SpamProtection.is_spamming(player, nil, 'HighScore Gui Click')
if is_spamming then
return
end
local name = event.element.name
-- Handles click on a score header
local element_to_column = {
['_version'] = 'version',
['_completion_time'] = 'completion_time',
['_leagues_travelled'] = 'leagues_travelled',
['_difficulty'] = 'difficulty',
['_max_players'] = 'max_players',
}
if element_to_column[name] then
--@TODO: Extend
local sorting_pref = this.sort_by[player.index]
local found_index = nil
local new_method = 'descending'
for i, sort in ipairs(sorting_pref) do
if sort.column == element_to_column[name] then
found_index = i
if sort.method == 'descending' and i==1 then new_method = 'ascending' end
end
end
if found_index then
--remove this and shuffle everything before it up by 1:
for j = found_index, 2, -1 do
sorting_pref[j] = Utils.deepcopy(sorting_pref[j-1]) --deepcopy just as I'm slightly unsure about refernces here
end
else
--prepend:
for j = #sorting_pref + 1, 2, -1 do
sorting_pref[j] = Utils.deepcopy(sorting_pref[j-1]) --deepcopy just as I'm slightly unsure about references here
end
end
sorting_pref[1] = {column = element_to_column[name], method = new_method}
score_gui({player = player, frame = frame})
return
end
end
local function on_player_joined_game(event)
local player = game.players[event.player_index]
if player.index and this.sort_by and (not this.sort_by[player.index]) then
this.sort_by[player.index] = {{method = 'ascending', column = 'completion_time'}, {method = 'descending', column = 'leagues_travelled'}, {method = 'descending', column = 'version'}, {method = 'descending', column = 'difficulty'}}
end
end
local function on_player_left_game(event)
local player = game.players[event.player_index]
if this.sort_by[player.index] then
this.sort_by[player.index] = nil
end
end
Server.on_data_set_changed(
score_dataset,
function(data)
local key
if is_game_modded() then
key = score_key_modded
elseif _DEBUG then
key = score_key_debug
else
key = score_key
end
if data.key == key then
if data.value then
this.score_table['player'] = data.value
end
end
end
)
Tabs.add_tab_to_gui({name = module_name, id = score_gui_token, admin = false, only_server_sided = false})
Event.on_init(on_init)
Event.add(defines.events.on_player_left_game, on_player_left_game)
Event.add(defines.events.on_player_joined_game, on_player_joined_game)
Event.add(defines.events.on_gui_click, on_gui_click)
Event.add(Server.events.on_server_started, Public.load_in_scores)
return Public

1672
maps/pirates/interface.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,150 @@
-- by mewmew
-- modified by Gerkiz
-- this fork by thesixthroc to use forces except 'enemy'
local Event = require 'utils.event'
local Global = require 'utils.global'
local traps = {}
Global.register(
traps,
function(t)
traps = t
end
)
local tick_tacks = {'*tick*', '*tick*', '*tack*', '*tak*', '*tik*', '*tok*'}
local kaboom_weights = {
{name = 'grenade', chance = 7},
{name = 'cluster-grenade', chance = 1},
{name = 'destroyer-capsule', chance = 1},
{name = 'defender-capsule', chance = 4},
{name = 'distractor-capsule', chance = 3},
{name = 'poison-capsule', chance = 2},
{name = 'explosive-uranium-cannon-projectile', chance = 3},
{name = 'explosive-cannon-projectile', chance = 5}
}
local kabooms = {}
for _, t in pairs(kaboom_weights) do
for _ = 1, t.chance, 1 do
table.insert(kabooms, t.name)
end
end
local function create_flying_text(surface, position, text)
if not surface.valid then
return
end
surface.create_entity(
{
name = 'flying-text',
position = position,
text = text,
color = {r = 0.75, g = 0.75, b = 0.75}
}
)
if text == '...' then
return
end
surface.play_sound({path = 'utility/armor_insert', position = position, volume_modifier = 0.75})
end
local function create_kaboom(force_name, surface, position, name)
if not surface.valid then
return
end
local target = position
local speed = 0.5
if name == 'defender-capsule' or name == 'destroyer-capsule' or name == 'distractor-capsule' then
surface.create_entity(
{
name = 'flying-text',
position = position,
text = '(((Sentries Engaging Target)))',
color = {r = 0.8, g = 0.0, b = 0.0}
}
)
local nearest_player_unit = surface.find_nearest_enemy({position = position, max_distance = 128, force = force_name})
if nearest_player_unit then
target = nearest_player_unit.position
end
speed = 0.001
end
surface.create_entity(
{
name = name,
position = position,
force = force_name,
target = target,
speed = speed
}
)
end
local function tick_tack_trap(force_name, surface, position)
if not surface then
return
end
if not surface.valid then
return
end
if not position then
return
end
if not position.x then
return
end
if not position.y then
return
end
local tick_tack_count = math.random(5, 9)
for t = 60, tick_tack_count * 60, 60 do
local tick = game.tick - (game.tick % 10) + t
if not traps[tick] then
traps[tick] = {}
end
if t < tick_tack_count * 60 then
traps[tick][#traps[tick] + 1] = {
callback = 'create_flying_text',
params = {surface, {x = position.x, y = position.y}, tick_tacks[math.random(1, #tick_tacks)]}
}
else
if math.random(1, 10) == 1 then
traps[tick][#traps[tick] + 1] = {
callback = 'create_flying_text',
params = {surface, {x = position.x, y = position.y}, '...'}
}
else
traps[tick][#traps[tick] + 1] = {
callback = 'create_kaboom',
params = {force_name, surface, {x = position.x, y = position.y}, kabooms[math.random(1, #kabooms)]}
}
end
end
end
end
local function on_tick()
if not traps[game.tick] then
return
end
for _, token in pairs(traps[game.tick]) do
local callback = token.callback
local params = token.params
if callback == 'create_kaboom' then
create_kaboom(params[1], params[2], params[3], params[4])
elseif callback == 'create_flying_text' then
create_flying_text(params[1], params[2], params[3])
end
end
traps[game.tick] = nil
end
Event.on_nth_tick(10, on_tick)
return tick_tack_trap

446
maps/pirates/loot.lua Normal file
View File

@ -0,0 +1,446 @@
local Math = require 'maps.pirates.math'
-- local Memory = require 'maps.pirates.memory'
-- local Balance = require 'maps.pirates.balance'
-- local CoreData = require 'maps.pirates.coredata'
local Common = require 'maps.pirates.common'
local _inspect = require 'utils.inspect'.inspect
local Public = {}
Public.buried_treasure_loot_data_raw = {
{100, 0, 1, false, 'coin', 1, 600},
{50, 0, 1, false, 'steel-plate', 100, 150},
{50, 0, 1, false, 'defender-capsule', 5, 18},
{25, 0, 1, false, 'distractor-capsule', 5, 18},
{10, 0, 1, false, 'destroyer-capsule', 3, 8},
-- {20, 0, 1, false, 'flying-robot-frame', 20, 35},
{40, 0, 1, false, 'construction-robot', 15, 25},
-- {100, 0, 1, false, 'electronic-circuit', 150, 250},
{70, 0, 1, false, 'advanced-circuit', 20, 40},
{150, 0, 1, false, 'crude-oil-barrel', 25, 45},
{70, 0, 1, false, 'effectivity-module-3', 3, 4},
{70, 0, 1, false, 'speed-module-3', 3, 4},
{10, 0, 1, false, 'productivity-module-2', 2, 2}, --3s are banned because of game mech
{70, 0, 1, false, 'plastic-bar', 40, 70},
{60, 0, 1, false, 'chemical-science-pack', 12, 24},
{70, 0, 1, false, 'assembling-machine-3', 2, 2},
{65, 0, 1, false, 'solar-panel', 7, 8},
{20, 0, 1, false, 'radar', 10, 20},
{10, 0, 1, false, 'production-science-pack', 12, 24},
{10, 0, 1, false, 'utility-science-pack', 4, 4},
{5, 0, 1, false, 'modular-armor', 1, 1},
{5, 0, 1, false, 'laser-turret', 1, 1},
{5, 0, 1, false, 'cannon-shell', 5, 10},
{50, 0, 1, false, 'artillery-shell', 4, 8},
{50, 0, 1, false, 'express-transport-belt', 8, 20},
{35, 0, 1, false, 'express-underground-belt', 4, 4},
{35, 0, 1, false, 'express-splitter', 4, 11},
{50, 0, 1, false, 'stack-inserter', 4, 12},
{0.1, 0, 1, false, 'atomic-bomb', 1, 1},
}
Public.chest_loot_data_raw = {
{80, 0, 1, false, 'coin', 250, 400},
{30, 0, 1, false, 'rail-signal', 25, 110},
{1, 0.2, 1, false, 'electric-engine-unit', 1, 2},
{3, 0, 1, false, 'small-lamp', 4, 16},
{10, 0, 1, false, 'coal', 60, 100},
{12, 0, 1, false, 'artillery-shell', 1, 1},
{4, 0, 0.8, false, 'pistol', 1, 3},
{3, 0, 0.2, false, 'storage-tank', 2, 4},
{0.25, 0, 0.5, false, 'uranium-238', 5, 8},
{8, 0, 1.2, true, 'steel-chest', 4, 12},
{2, 0, 0.8, false, 'empty-barrel', 1, 10},
{5, -0.2, 0.8, true, 'iron-gear-wheel', 20, 80},
{5, -0.2, 0.8, true, 'copper-cable', 30, 100},
-- {5, -0.2, 0.8, true, 'electronic-circuit', 15, 60},
{2, 0.3, 1, true, 'rocket-fuel', 1, 5},
{1, 0.2, 1.2, true, 'battery', 10, 30},
{4, 0.2, 1.4, true, 'advanced-circuit', 10, 40},
{8, -0.3, 0.3, true, 'small-electric-pole', 16, 30},
{30, 0, 1, false, 'medium-electric-pole', 2, 10},
{3, 0.2, 1.4, true, 'big-electric-pole', 4, 8},
{1, 0.2, 1.8, true, 'substation', 1, 3},
{5, 0, 1.2, true, 'accumulator', 1, 4},
{10, 0, 1, false, 'solar-panel', 2, 3},
{15, -0.6, 0.6, true, 'transport-belt', 20, 60},
{10, 0, 1, false, 'fast-transport-belt', 8, 40},
{3, 0.2, 1.8, true, 'express-transport-belt', 5, 12},
{12, -1, 0.5, true, 'splitter', 4, 10},
{8, 0, 1, false, 'fast-splitter', 2, 5},
{5, 0, 2, true, 'express-splitter', 1, 3},
{5, -1, 0.5, true, 'underground-belt', 6, 6},
{2, 0, 1, false, 'fast-underground-belt', 6, 6},
{3, 0, 2, true, 'express-underground-belt', 4, 4},
{0.5, 0, 0.1, false, 'loader', 1, 1},
{0.5, 0.1, 0.5, false, 'fast-loader', 1, 1},
{0.5, 0.5, 1, false, 'express-loader', 1, 1},
{4, -0.3, 0.3, true, 'inserter', 8, 20},
{4, 0, 1, true, 'fast-inserter', 2, 12},
{3, 0, 1, false, 'filter-inserter', 2, 9},
{3, 0, 1, false, 'long-handed-inserter', 4, 16},
{3, 0, 0.5, false, 'stack-inserter', 1, 4},
{3, 0.5, 1, false, 'stack-inserter', 5, 8},
{1, 0, 0.5, false, 'stack-filter-inserter', 1, 4},
{1, 0.5, 1, false, 'stack-filter-inserter', 5, 8},
{40, -0.5, 0.5, true, 'firearm-magazine', 10, 32},
{60, -1, 1, true, 'piercing-rounds-magazine', 8, 16},
{10, 0, 1, false, 'uranium-rounds-magazine', 3, 7},
{1, -0.3, 0.3, true, 'assembling-machine-1', 2, 4},
{20, 0, 0.9, false, 'assembling-machine-2', 1, 3},
{2, 0, 1, false, 'electric-mining-drill', 2, 4},
{3, 0.25, 1.75, true, 'assembling-machine-3', 2, 4},
{5, 0, 1, true, 'steel-furnace', 4, 8},
{4, 0, 2, true, 'electric-furnace', 1, 3},
{3, 0.2, 1, true, 'chemical-plant', 1, 3},
{50, -1, 0.5, true, 'speed-module', 1, 3},
{25, 0, 1.5, true, 'speed-module-2', 1, 2},
{12, 0, 2, true, 'speed-module-3', 1, 1},
{4, -1, 1, true, 'effectivity-module', 1, 3},
-- {4, 0, 1, true, 'effectivity-module-2', 1, 3}, --disabled to reduce 'avalanche of crap' effect
{4, 0, 2, true, 'effectivity-module-3', 1, 1},
{3, 0, 1, false, 'productivity-module', 1, 1}, --not many of these, merely to make them availabile for future features
{4, 0, 0.1, false, 'light-armor', 1, 1},
{2, 0, 0.5, true, 'heavy-armor', 1, 1},
{2, 0, 0.1, false, 'submachine-gun', 1, 1},
{1, 0, 1, false, 'gun-turret', 2, 4},
{6, 0, 1, false, 'grenade', 2, 12},
{4, 0, 1, false, 'stone-wall', 12, 50},
{2, 0, 2, true, 'rocket-launcher', 1, 1},
{4, 0, 2, true, 'rocket', 4, 10},
{5, 0.2, 1.8, true, 'explosive-rocket', 2, 8},
{0.2, 0, 1, false, 'poison-capsule', 2, 5},
{0.2, 0, 1, false, 'slowdown-capsule', 2, 5},
{0.2, 0, 1, false, 'defender-capsule', 2, 4},
{0.15, 0, 1, false, 'distractor-capsule', 2, 4},
{0.1, 0, 1, false, 'destroyer-capsule', 2, 2},
{10, 0.2, 1.2, true, 'piercing-shotgun-shell', 6, 9},
{2, 0.3, 1.2, true, 'combat-shotgun', 1, 1},
{5, 0.2, 1.8, true, 'cluster-grenade', 8, 16},
{0.04, 0.25, 1.75, true, 'modular-armor', 1, 1},
{0.01, 0.5, 1.5, true, 'power-armor', 1, 1},
{0.3, 0.1, 1, true, 'solar-panel-equipment', 1, 2},
{0.2, 0.1, 1, true, 'battery-equipment', 1, 1},
{0.16, 0.2, 1, true, 'energy-shield-equipment', 1, 2},
{0.08, 0.1, 1, true, 'night-vision-equipment', 1, 1},
{0.04, 0.5, 1.5, true, 'personal-laser-defense-equipment', 1, 1},
{8, -0.5, 0.5, true, 'automation-science-pack', 4, 24},
{8, -0.6, 0.6, true, 'logistic-science-pack', 4, 24},
{6, -0.1, 1, true, 'military-science-pack', 8, 20},
{6, -0.5, 1.5, true, 'chemical-science-pack', 8, 16},
{6, 0, 1.5, true, 'production-science-pack', 8, 16},
{6, 0, 2, true, 'utility-science-pack', 3, 3},
{0.5, 0.2, 1.4, true, 'construction-robot', 5, 15},
{2, 0.5, 1.5, true, 'roboport', 1, 1},
{2, 0.5, 1.5, false, 'logistic-chest-passive-provider', 2, 2},
{2, 0.5, 1.5, false, 'logistic-robot', 5, 15},
{4, 0.2, 1.2, true, 'lubricant-barrel', 1, 4},
-- copying over most of those i made for chronotrain:
--always there (or normally always there):
-- {4, 0, 1, false, 'gate', 14, 32}, --can beat biters with them
-- {1, 0, 1, false, 'radar', 1, 2}, --disabled to reduce 'avalanche of crap' effect
-- {0.25, 0, 1, false, 'rail', 50, 100},
-- {0.25, 0, 1, false, 'uranium-rounds-magazine', 1, 4},
-- {1, 0.15, 1, false, 'pump', 1, 2}, --disabled to reduce 'avalanche of crap' effect
-- {2, 0.15, 1, false, 'pumpjack', 1, 3}, --disabled to reduce 'avalanche of crap' effect
-- {0.02, 0.15, 1, false, 'oil-refinery', 1, 2}, --disabled to reduce 'avalanche of crap' effect
--shotgun meta:
-- {10, -0.2, 0.4, true, 'shotgun-shell', 12, 24}, --disabled to reduce 'avalanche of crap' effect
-- {5, 0, 0.4, true, 'shotgun', 1, 1}, --disabled to reduce 'avalanche of crap' effect
--modular armor meta:
-- {0.5, -1,3, true, "power-armor-mk2", 1, 1},
--loader meta:
--science meta:
-- {4, 0.4, 1.5, true, 'utility-science-pack', 16, 32},
-- {10, 0.5, 1.5, true, 'space-science-pack', 16, 32},
--early-game:
--{3, -0.1, 0.2, false, "railgun-dart", 2, 4},
-- {3, -0.1, 0.1, true, 'wooden-chest', 8, 40},
-- {5, -0.1, 0.1, true, 'burner-inserter', 8, 20}, --disabled to reduce 'avalanche of crap' effect
-- {1, -0.2, 0.2, true, 'offshore-pump', 1, 3}, --disabled to reduce 'avalanche of crap' effect
-- {3, -0.2, 0.2, true, 'boiler', 3, 6}, --disabled to reduce 'avalanche of crap' effect
-- {3, 0, 0.1, true, 'lab', 1, 3}, --disabled to reduce 'avalanche of crap' effect
-- {3, -0.2, 0.2, true, 'steam-engine', 2, 4}, --disabled to reduce 'avalanche of crap' effect
-- {3, -0.2, 0.2, true, 'burner-mining-drill', 2, 4},
-- {3, 0, 0.3, true, 'iron-chest', 8, 40}, --disabled to reduce 'avalanche of crap' effect
-- {6, -0.4, 0.4, true, 'stone-furnace', 8, 16}, --disabled to reduce 'avalanche of crap' effect
--mid-game:
--{6, 0.2, 0.5, false, "railgun-dart", 4, 8},
-- {5, -0.2, 0.7, true, 'pipe', 30, 50}, --disabled to reduce 'avalanche of crap' effect
-- {1, -0.2, 0.7, true, 'pipe-to-ground', 4, 8}, --disabled to reduce 'avalanche of crap' effect
-- {3, 0, 0.7, true, 'steel-plate', 15, 80},
-- {8, 0, 0.9, true, 'piercing-rounds-magazine', 10, 64},
-- {4, 0.2, 0.6, true, 'engine-unit', 8, 16},
--late-game:
--{9, 0.5, 0.8, false, "railgun-dart", 8, 16},
-- {5, 0, 1.2, true, 'land-mine', 16, 32},
-- {5, 0.4, 0.7, true, 'cannon-shell', 16, 32},
-- {5, 0.4, 0.8, true, 'explosive-cannon-shell', 16, 32},
-- {2, 0.25, 1.75, true, 'logistic-robot', 5, 25},
-- {1, 0.25, 1.75, true, 'laser-turret', 1, 1},
-- {4, 0.4, 1.6, true, 'processing-unit', 30, 200},
-- super late-game:
--{9, 0.8, 1.2, false, "railgun-dart", 12, 20},
-- {1, 0.9, 1.1, true, 'power-armor-mk2', 1, 1},
-- {1, 0.8, 1.2, true, 'fusion-reactor-equipment', 1, 1}
--{2, 0, 1, , "computer", 1, 1},
--{1, 0.2, 1, , "railgun", 1, 1},
--{1, 0.9, 1, , "personal-roboport-mk2-equipment", 1, 1},
}
function Public.wooden_chest_loot()
local num = 1
return Public.chest_loot(num,
Math.max(0,Math.min(1, Math.sloped(Common.difficulty(),1/2) * Common.game_completion_progress())) --enforce 0 to 1
)
end
function Public.iron_chest_loot()
local num = 2
local loot = Public.chest_loot(num,
Math.max(0,Math.min(1, Math.sloped(Common.difficulty(),1/2) * (5/100 + Common.game_completion_progress()))) --enforce 0 to 1
) --reward higher difficulties with better loot
loot[#loot + 1] = {name = 'coin', count = Math.random(1,1500)}
return loot
end
function Public.covered_wooden_chest_loot()
local num = 2
local loot = Public.chest_loot(num,
Math.max(0,Math.min(1, Math.sloped(Common.difficulty(),1/2) * (10/100 + Common.game_completion_progress()))) --enforce 0 to 1
) --reward higher difficulties with better loot
return loot
end
function Public.stone_furnace_loot()
return {
{name = 'coal', count = 50},
}
end
function Public.storage_tank_fluid_loot(force_type)
local ret
local rng = Math.random(10)
if force_type == 'crude-oil' then
ret = {name = 'crude-oil', amount = Math.random(3000, 12500)}
elseif force_type == 'petroleum-gas' then
ret = {name = 'petroleum-gas', amount = Math.random(1500, 7500)}
elseif rng < 6 then
ret = {name = 'crude-oil', amount = Math.random(1500, 4500)}
elseif rng == 7 then
ret = {name = 'light-oil', amount = Math.random(1500, 3500)}
elseif rng == 8 then
ret = {name = 'heavy-oil', amount = Math.random(1500, 3500)}
-- elseif rng == 9 then
-- ret = {name = 'lubricant', amount = Math.random(1000, 2000)}
else
ret = {name = 'petroleum-gas', amount = Math.random(1500, 2000)}
end
return ret
end
function Public.swamp_storage_tank_fluid_loot()
local ret
ret = {name = 'sulfuric-acid', amount = Math.random(500, 1500)}
return ret
end
function Public.roboport_bots_loot()
return {
{name = 'logistic-robot', count = 5},
}
-- construction robots
end
function Public.random_plates(multiplier)
multiplier = multiplier or 1
local platesrng = Math.random(5)
if platesrng <= 2 then
return {name = 'iron-plate', count = 120 * multiplier}
elseif platesrng <= 4 then
return {name = 'copper-plate', count = 120 * multiplier}
else
return {name = 'steel-plate', count = 20 * multiplier}
end
end
function Public.chest_loot(number_of_items, game_completion_progress)
local ret = Common.raffle_from_processed_loot_data(Common.processed_loot_data(Public.chest_loot_data_raw), number_of_items, game_completion_progress)
ret[#ret + 1] = ret[1]
ret[1] = Public.random_plates()
return ret
end
function Public.buried_treasure_loot()
local ret = Common.raffle_from_processed_loot_data(Common.processed_loot_data(Public.buried_treasure_loot_data_raw), 1, Math.sloped(Common.difficulty(),1/2) * Common.game_completion_progress_capped())
if ret and ret[1] then return ret[1] end
end
function Public.maze_camp_loot()
if Math.random(10) <= 7 then
return {Public.random_plates()}
else
return Common.raffle_from_processed_loot_data(Common.processed_loot_data(Public.chest_loot_data_raw), 1, Math.max(0,Math.min(1, Math.sloped(Common.difficulty(),1/2) * (15/100 + Common.game_completion_progress()))))
end
end
Public.maze_lab_loot_data_raw = {
{8, -0.5, 0.5, true, 'automation-science-pack', 5, 20},
{8, -0.6, 0.6, true, 'logistic-science-pack', 5, 20},
{6, -0.1, 1, true, 'military-science-pack', 5, 18},
{6, -0.5, 1.5, true, 'chemical-science-pack', 4, 12},
{6, 0, 1.5, true, 'production-science-pack', 3, 11},
{2, 0, 2, true, 'utility-science-pack', 2, 3},
-- {4, 0.4, 1.5, true, 'utility-science-pack', 16, 32},
-- {10, 0.5, 1.5, true, 'space-science-pack', 16, 32},
}
function Public.maze_lab_loot()
return Common.raffle_from_processed_loot_data(Common.processed_loot_data(Public.maze_lab_loot_data_raw), 1, Math.max(0,Math.min(1, Math.sloped(Common.difficulty(),1/2) * (Common.game_completion_progress()))))
end
Public.maze_treasure_data_raw = {
{2, -1, 1, true, 'rocket', 18, 24},
{2, -0.8, 0.8, false, 'stack-inserter', 8, 10},
{2, -0.8, 0.8, false, 'stack-filter-inserter', 5, 6},
{2, 0, 1, false, 'poison-capsule', 22, 22},
{2, 0, 1, false, 'destroyer-capsule', 8, 8},
{2, 0, 1, false, 'slowdown-capsule', 14, 14},
{2, 0, 1, false, 'uranium-rounds-magazine', 15, 25},
{2, 0, 1, false, 'artillery-shell', 5, 7},
{2, 0, 1, false, 'rail-signal', 400, 500},
{2, 0, 1, false, 'electric-engine-unit', 3, 4},
{2, 0, 1, false, 'cluster-grenade', 8, 12},
{0.5, 0, 1, false, 'nuclear-fuel', 1, 1},
{1, 0, 0.8, false, 'speed-module-3', 2, 2},
{1, 0, 0.8, false, 'effectivity-module-3', 3, 3},
{0.5, 0, 1, false, 'productivity-module-2', 2, 2}, --3s are banned because of game mech
{2, 0, 1.5, true, 'production-science-pack', 20, 25},
{2, 0, 2, true, 'utility-science-pack', 7, 8},
{2, 0, 1.5, true, 'coin', 3000, 6500},
{3, 0, 0.9, false, 'beacon', 1, 1},
{1, 0, 1, false, 'construction-robot', 30, 40},
{3, 0, 1, false, 'logistic-chest-passive-provider', 2, 2},
{1, 0.2, 1.8, true, 'explosive-rocket', 6, 8},
{1, 0, 0.9, false, 'express-transport-belt', 20, 60},
{0.5, 0, 0.9, false, 'express-underground-belt', 6, 6},
{0.5, 0, 0.9, false, 'express-splitter', 10, 10},
{1, 0, 0.9, false, 'express-loader', 2, 2},
{0.5, 0, 0.5, false, 'substation', 2, 2},
{0.5, 0, 0.8, false, 'assembling-machine-3', 3, 3},
{1, 0, 0.7, false, 'electric-furnace', 4, 6},
{1, 0, 0.8, false, 'modular-armor', 1, 1},
{1, 0, 2, true, 'power-armor', 1, 1},
{0.1, 0, 2, true, 'power-armor-mk2', 1, 1},
{2, -1, 1, true, 'solar-panel-equipment', 3, 4},
{2, -1, 1, true, 'battery-equipment', 1, 1},
{1, 0, 2, true, 'battery-mk2-equipment', 1, 1},
{2, -1, 1, true, 'energy-shield-equipment', 1, 2},
{1, 0, 2, true, 'energy-shield-mk2-equipment', 1, 1},
{1, -1, 1, true, 'personal-roboport-equipment', 1, 1},
{0.5, 0, 2, true, 'personal-roboport-mk2-equipment', 1, 1},
{0.5, 0, 0.8, false, 'night-vision-equipment', 1, 1},
{1, 0, 1, false, 'personal-laser-defense-equipment', 1, 1},
{0.5, 0, 1, false, 'fusion-reactor-equipment', 1, 1},
{2, 0, 1, false, 'exoskeleton-equipment', 1, 1},
{2, -0.7, 1.3, true, 'advanced-circuit', 40, 90},
{2, 0, 0.5, false, 'laser-turret', 1, 2},
{2, 0.6, 1, false, 'laser-turret', 4, 5},
{1, 0, 0.5, false, 'roboport', 1, 1},
{1, 0, 1, false, 'atomic-bomb', 1, 1},
}
function Public.maze_treasure_loot()
if Math.random(5) == 1 then
return {Public.random_plates(8)}
else
return Common.raffle_from_processed_loot_data(Common.processed_loot_data(Public.maze_treasure_data_raw), 1, Math.max(0,Math.min(1, Math.sloped(Common.difficulty(),1/2) * (Common.game_completion_progress()))))
end
end
Public.dredger_loot_raw = {
{8, 0, 1, false, 'iron-plate', 1, 1},
{8, 0, 1, false, 'copper-plate', 1, 1},
{8, 0, 1, false, 'coal', 1, 1},
{10, 0, 1, false, 'coin', 1, 1},
{2, 0, 1, false, 'small-electric-pole', 1, 1},
{1, 0, 1, false, 'medium-electric-pole', 1, 1},
{0.1, 0, 1, false, 'big-electric-pole', 1, 1},
{0.2, 0, 1, false, 'poison-capsule', 1, 1},
{0.2, 0, 1, false, 'slowdown-capsule', 1, 1},
{0.1, 0, 1, false, 'destroyer-capsule', 1, 1},
{0.2, 0, 1, false, 'defender-capsule', 1, 1},
{0.2, 0, 1, false, 'distractor-capsule', 1, 1},
{0.05, 0, 1, false, 'speed-module-3', 1, 1},
{0.05, 0, 1, false, 'effectivity-module-3', 1, 1},
{0.05, 0, 1, false, 'productivity-module-2', 1, 1}, --3s are banned because of game mech
{0.1, 0, 1, false, 'rocket', 1, 1},
{0.01, 0, 1, false, 'explosive-rocket', 1, 1},
{4, 0, 1, false, 'automation-science-pack', 1, 1},
{4, 0, 1, false, 'logistic-science-pack', 1, 1},
{2, 0, 1, false, 'military-science-pack', 1, 1},
{1, 0, 1, false, 'chemical-science-pack', 1, 1},
{1, 0, 1, false, 'production-science-pack', 1, 1},
{0.5, 0, 1, false, 'utility-science-pack', 1, 1},
{0.012, 0, 0.8, false, 'night-vision-equipment', 1, 1},
{0.01, 0, 1, false, 'exoskeleton-equipment', 1, 1},
{0.01, -1, 1, true, 'energy-shield-equipment', 1, 1},
{0.01, -1, 1, true, 'solar-panel-equipment', 1, 1},
{0.01, -1, 1, true, 'battery-equipment', 1, 1},
{0.008, 0, 2, true, 'battery-mk2-equipment', 1, 1},
{0.008, 0, 1, false, 'personal-laser-defense-equipment', 1, 1},
{0.008, 0, 2, true, 'energy-shield-mk2-equipment', 1, 1},
{0.008, -1, 1, true, 'personal-roboport-equipment', 1, 1},
{0.004, 0, 1, false, 'fusion-reactor-equipment', 1, 1},
{0.004, 0, 2, true, 'personal-roboport-mk2-equipment', 1, 1},
}
function Public.dredger_loot()
return Common.raffle_from_processed_loot_data(Common.processed_loot_data(Public.dredger_loot_raw), 1, Math.max(0,Math.min(1, Common.game_completion_progress())))
end
return Public

396
maps/pirates/main.lua Normal file
View File

@ -0,0 +1,396 @@
--[[
Pirate Ship is maintained by thesixthroc and hosted by Comfy.
Want to host it? Ask us at getcomfy.eu/discord!
]]
--[[
== Tips for Developers! ==
The scenario is quite complex, but there are ways to get started, even if you don't know much Lua. Some ideas (incomplete):
Go to pirates/surfaces/islands/first and edit stuff there to see the effect it has on the first island
Ask thesixthroc for access to the ToDo list on Github Projects, to see what needs doing
]]
--[[personal note for thesixthroc in XX years: my design notes are all in Obsidian (edit: mostly moved to Github Projects)]]
-- require 'modules.biters_yield_coins'
require 'modules.biter_noms_you'
require 'modules.no_deconstruction_of_neutral_entities'
require 'maps.pirates.custom_events' --probably do this before anything else
require 'utils.server'
local _inspect = require 'utils.inspect'.inspect
-- local Modifers = require 'player_modifiers'
local BottomFrame = require 'utils.gui.bottom_frame'
local Autostash = require 'modules.autostash'
require 'modules.inserter_drops_pickup'
local TickFunctions = require 'maps.pirates.tick_functions'
local ClassTickFunctions = require 'maps.pirates.tick_functions_classes'
require 'maps.pirates.commands'
require 'maps.pirates.math'
local Memory = require 'maps.pirates.memory'
require 'maps.pirates.gui.gui'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
require 'maps.pirates.utils_local'
local Balance = require 'maps.pirates.balance'
local Crew = require 'maps.pirates.crew'
local Roles = require 'maps.pirates.roles.roles'
local Structures = require 'maps.pirates.structures.structures'
local Surfaces = require 'maps.pirates.surfaces.surfaces'
local Interface = require 'maps.pirates.interface'
require 'maps.pirates.structures.boats.boats'
local Progression = require 'maps.pirates.progression'
local Ai = require 'maps.pirates.ai'
require 'maps.pirates.ores'
require 'maps.pirates.quest'
require 'maps.pirates.parrot'
require 'maps.pirates.shop.shop'
require 'maps.pirates.boat_upgrades'
local Token = require 'utils.token'
local Task = require 'utils.task'
require 'utils.profiler'
local Public = {}
-- parrot sprites from https://elthen.itch.io/2d-pixel-art-parrot-sprites, licensed appropriately
local jetty_delayed = Token.register(
-- function(data)
function()
Surfaces.Lobby.place_lobby_jetty_and_boats()
end
)
local function on_init()
Memory.global_reset_memory()
local global_memory = Memory.get_global_memory()
game.reset_time_played()
-- local spectator = game.create_force('spectator')
-- local spectator_permissions = game.permissions.create_group('spectator')
-- spectator_permissions.set_allows_action(defines.input_action.start_walking,false)
Autostash.insert_into_furnace(true)
-- Autostash.insert_into_wagon(true)
Autostash.bottom_button(true)
BottomFrame.reset()
BottomFrame.activate_custom_buttons(true)
-- BottomFrame.bottom_right(true)
local mgs = game.surfaces['nauvis'].map_gen_settings
mgs.width = 16
mgs.height = 16
game.surfaces['nauvis'].map_gen_settings = mgs
game.surfaces['nauvis'].clear()
game.create_surface('piratedev1', Common.default_map_gen_settings(100, 100))
game.surfaces['piratedev1'].clear()
Common.init_game_settings(Balance.technology_price_multiplier)
global_memory.active_crews_cap = Common.active_crews_cap
global_memory.minimum_capacity_slider_value = Common.minimum_capacity_slider_value
Surfaces.Lobby.create_starting_dock_surface()
local lobby = game.surfaces[CoreData.lobby_surface_name]
game.forces.player.set_spawn_position(Common.lobby_spawnpoint, lobby)
game.forces.player.character_running_speed_modifier = Balance.base_extra_character_speed
game.create_force('environment')
for id = 1, 3, 1 do
game.create_force(string.format('enemy-%03d', id))
game.create_force(string.format('ancient-friendly-%03d', id))
game.create_force(string.format('ancient-hostile-%03d', id))
local crew_force = game.create_force(string.format('crew-%03d', id))
Crew.reset_crew_and_enemy_force(id)
crew_force.research_queue_enabled = true
end
-- Delay.global_add(Delay.global_enum.PLACE_LOBBY_JETTY_AND_BOATS)
Task.set_timeout_in_ticks(2, jetty_delayed, {})
if _DEBUG then
game.print('Debug mode on. Use /go to get started (sometimes crashes)')
end
end
local event = require 'utils.event'
event.on_init(on_init)
local function crew_tick()
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local tick = game.tick
TickFunctions.boat_movement_tick(5) --arguments are tick intervals
-- TickFunctions.parrot_tick(5)
if tick % 10 == 0 then
TickFunctions.prevent_disembark(10)
TickFunctions.prevent_unbarreling_off_ship(10)
end
if memory.age and memory.overworldx and memory.overworldx > 0 then
memory.age = memory.age + 5
end
if memory.real_age then
memory.real_age = memory.real_age + 5
end
if tick % 60 == 0 then
TickFunctions.captain_warn_afk(60)
end
if tick % Common.loading_interval == 0 then
TickFunctions.loading_update(Common.loading_interval)
end
if tick % 5 == 0 then
TickFunctions.quest_progress_tick(5)
end
if tick % 5 == 0 then
TickFunctions.strobe_player_colors(5)
end
if tick % 10 == 0 then
TickFunctions.shop_ratelimit_tick(10)
end
if tick % 30 == 0 then
TickFunctions.silo_update(30)
end
if tick % 60 == 0 then
TickFunctions.ship_deplete_fuel(60)
end
if tick % 10 == 0 then
TickFunctions.pick_up_tick(10)
end
if tick % 60 == 0 then
if memory.boat and memory.boat.state == Structures.Boats.enum_state.ATSEA_SAILING then
TickFunctions.crowsnest_natural_move(120)
end
end
if tick % 60 == 15 or tick % 60 == 45 then
if memory.boat and memory.boat.state == Structures.Boats.enum_state.ATSEA_SAILING then
TickFunctions.overworld_check_collisions(120)
end
end
if tick % 60 == 30 then
if memory.boat and memory.boat.state == Structures.Boats.enum_state.ATSEA_SAILING then
TickFunctions.crowsnest_steer(120)
end
end
if tick % 60 == 0 then
TickFunctions.slower_boat_tick(60)
end
if tick % 10 == 0 then
TickFunctions.update_boat_stored_resources(10)
end
if tick % 10 == 0 then
TickFunctions.covered_requirement_check(10)
end
if tick % 30 == 0 then
TickFunctions.buried_treasure_check(30)
end
if tick % 60 == 0 then
TickFunctions.raft_raids(60)
end
if tick % 60 == 0 then
TickFunctions.place_cached_structures(60)
end
if tick % 240 == 0 then
TickFunctions.check_all_spawners_dead(240)
end
if tick % 60 == 0 then
if destination.dynamic_data.timer then
destination.dynamic_data.timer = destination.dynamic_data.timer + 1
end
if memory.captain_acceptance_timer then
memory.captain_acceptance_timer = memory.captain_acceptance_timer - 1
if memory.captain_acceptance_timer == 0 then
Roles.assign_captain_based_on_priorities()
end
end
if memory.captain_accrued_time_data and memory.playerindex_captain and memory.overworldx and memory.overworldx > 0 and memory.overworldx < CoreData.victory_x then --only count time in the 'main game'
local player = game.players[memory.playerindex_captain]
if player and player.name then
if (not memory.captain_accrued_time_data[player.name]) then memory.captain_accrued_time_data[player.name] = 0 end
memory.captain_accrued_time_data[player.name] = memory.captain_accrued_time_data[player.name] + 1
end
end
if destination.dynamic_data.time_remaining and destination.dynamic_data.time_remaining > 0 then
destination.dynamic_data.time_remaining = destination.dynamic_data.time_remaining - 1
if destination.dynamic_data.time_remaining == 0 then
if memory.boat and memory.boat.surface_name then
local surface_name_decoded = Surfaces.SurfacesCommon.decode_surface_name(memory.boat.surface_name)
local type = surface_name_decoded.type
if type == Surfaces.enum.ISLAND then
Progression.retreat_from_island(false)
elseif type == Surfaces.enum.DOCK then
Progression.undock_from_dock(false)
end
end
end
end
end
if tick % 240 == 0 then
if memory.max_players_recorded then
local count_now = #Common.crew_get_crew_members()
if count_now and count_now > memory.max_players_recorded then
memory.max_players_recorded = count_now
end
end
end
if tick % 240 == 0 then
TickFunctions.Kraken_Destroyed_Backup_check(240)
end
if tick % 300 == 0 then
TickFunctions.periodic_free_resources(300)
end
if tick % 30 == 0 then
ClassTickFunctions.update_character_properties(30)
end
if tick % 30 == 0 then
ClassTickFunctions.class_renderings(30)
end
if tick % 120 == 0 then
Ai.Tick_actions(120)
end
if tick % 240 == 0 then
TickFunctions.LOS_tick(240)
end
if tick % 420 == 0 then
ClassTickFunctions.class_rewards_tick(420)
end
if tick % 300 == 0 then
TickFunctions.update_recentcrewmember_list(300)
end
if tick % 1800 == 0 then
TickFunctions.transfer_pollution(1800)
end
if tick % 3600 == 0 then
TickFunctions.prune_offline_characters_list(3600)
end
-- if tick % (60*60*60) == 0 then
-- Parrot.parrot_say_tip()
-- end
if memory.crew_disband_tick then
if memory.crew_disband_tick < tick then
memory.crew_disband_tick = nil
Crew.disband_crew()
end
return
end
end
local function global_tick()
local global_memory = Memory.get_global_memory()
local tick = game.tick
if tick % 60 == 0 then
TickFunctions.update_players_second()
end
if tick % 30 == 0 then
for _, player in pairs(game.connected_players) do
-- figure out which crew this is about:
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or 0
Memory.set_working_id(crew_id)
Roles.update_tags(player)
end
end
for _, id in pairs(global_memory.crew_active_ids) do
Memory.set_working_id(id)
crew_tick()
end
TickFunctions.update_player_guis(5)
end
event.on_nth_tick(5, global_tick)
local function instatick()
local global_memory = Memory.get_global_memory()
for _, id in pairs(global_memory.crew_active_ids) do
Memory.set_working_id(id)
TickFunctions.minimap_jam(1)
TickFunctions.silo_insta_update()
end
end
event.on_nth_tick(1, instatick)
----- FOR BUGFIXING HARD CRASHES (segfaults) ------
-- often, segfaults are due to an error during chunk generation (as of 1.1.0 or so, anyway.)
-- to help debug, comment this out, and instead use the command /chnk to generate some chunks manually
event.add(defines.events.on_chunk_generated, Interface.event_on_chunk_generated)
----- FOR DESYNC BUGFIXING -----
local gMeta = getmetatable(_ENV)
if not gMeta then
gMeta = {}
setmetatable(_ENV, gMeta)
end
gMeta.__newindex = function(_, n, v)
log('Desync warning: attempt to write to undeclared var ' .. n)
global[n] = v
end
gMeta.__index = function(_, n)
return global[n]
end
return Public

136
maps/pirates/math.lua Normal file

File diff suppressed because one or more lines are too long

161
maps/pirates/memory.lua Normal file
View File

@ -0,0 +1,161 @@
local Global = require 'utils.global'
-- local CoreData = require 'maps.pirates.coredata'
local pirates_global_memory = {}
local Public = {}
-- register only this
Global.register(
pirates_global_memory,
function(tbl)
pirates_global_memory = tbl
end
)
function Public.global_reset_memory()
for k, _ in pairs(pirates_global_memory) do
pirates_global_memory[k] = nil
end
pirates_global_memory.config = {}
pirates_global_memory.afk_player_indices = {}
pirates_global_memory.playerindex_to_time_played_continuously = {}
pirates_global_memory.playerindex_to_captainhood_priority = {}
pirates_global_memory.player_gui_memories = {}
pirates_global_memory.crew_memories = {}
pirates_global_memory.crew_active_ids = {}
pirates_global_memory.working_id = nil --should only ever be nil, 1, 2 or 3
pirates_global_memory.lobby_boats = {}
pirates_global_memory.active_crews_cap = nil
pirates_global_memory.crew_capacity_min = nil
pirates_global_memory.crewproposals = {}
pirates_global_memory.global_delayed_tasks = {}
pirates_global_memory.global_buffered_tasks = {}
end
function Public.reset_crew_memory(id) --mostly serves as a dev reference of memory entries
-- but not _everything_ is stored here, it's just a guide to the most important things
pirates_global_memory.crew_memories[id] = {}
local memory = pirates_global_memory.crew_memories[id]
memory.secs_id = nil
memory.id = nil
memory.age = nil
memory.real_age = nil
memory.completion_time = nil
memory.force_name = nil
memory.enemy_force_name = nil
memory.original_proposal = nil
memory.name = nil
memory.difficulty_option = nil
memory.capacity_option = nil
-- memory.mode_option = nil
memory.difficulty = nil
memory.capacity = nil
-- memory.mode = nil
memory.destinations = nil
memory.currentdestination_index = nil
memory.hold_surface_count = nil
memory.merchant_ships_unlocked = nil
memory.boat = nil
memory.available_classes_pool = nil
memory.crewplayerindices = nil
memory.spectatorplayerindices = nil
memory.tempbanned_from_joining_data = nil
memory.playerindex_captain = nil
memory.captain_accrued_time_data = nil
memory.max_players_recorded = nil
memory.temporarily_logged_off_characters = nil
memory.speed_boost_characters = nil
memory.enemyboats = nil
memory.overworld_krakens = nil
memory.active_sea_enemies = nil
memory.kraken_stream_registrations = nil
memory.mainshop_availability_bools = nil
memory.delayed_tasks = nil
memory.buffered_tasks = nil
memory.game_lost = false
memory.game_won = false
memory.crew_disband_tick = nil
memory.destinationsvisited_indices = nil
memory.overworldx = nil
memory.overworldy = nil
memory.mapbeingloadeddestination_index = nil
memory.loadingticks = nil
memory.stored_fuel = nil
memory.spawnpoint = nil
memory.evolution_factor = nil
memory.scripted_biters = nil
memory.scripted_unit_groups = nil
memory.floating_pollution = nil
end
function Public.fallthrough_crew_memory() --could make this a metatable, but metatables and factorio global seem not to play nicely
return {
id = 0,
difficulty = 1,
force_name = 'player',
boat = {},
destinations = {},
spectatorplayerindices = {},
crewplayerindices = {},
--[[boat = {
type = nil,
state = nil,
speed = nil,
speedticker1 = nil,
speedticker2 = nil,
speedticker3 = nil,
stored_resources = {},
position = nil, --the far right edge of the boat
decksteeringchests = nil,
crowsneststeeringchests = nil,
cannons = nil,
EEI = nil,
EEIpower_production = nil,
EEIelectric_buffer_size = nil,
dockedposition = nil,
surface_name = nil,
}]]
}
end
function Public.get_crew_memory()
if pirates_global_memory.working_id and pirates_global_memory.working_id > 0 then
return pirates_global_memory.crew_memories[pirates_global_memory.working_id] or Public.fallthrough_crew_memory()
else
return Public.fallthrough_crew_memory()
end
end
function Public.get_global_memory()
return pirates_global_memory
end
function Public.set_working_id(id)
pirates_global_memory.working_id = id
end
return Public

View File

@ -0,0 +1,29 @@
local Public = {}
Public.encoding = [[!#$%&'()*+'-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ/^_`abcdefghijklmnopqrstuvwxyz{}|~]]
Public.encoding_length = 91
Public.enc = {}
Public.dec = {}
for i=1,Public.encoding_length do
Public.enc[i]=Public.encoding:sub(i,i)
Public.dec[Public.encoding:sub(i,i)]=i
end
Public.island1 = {}
Public.island1.Data = require 'maps.pirates.noise_pregen.perlinwavelength100boxsize1000octaves5gain0p8lacunity2lengthpower1rms0p05423'
Public.island1.upperscale = 100
Public.island1.boxsize = 1000
Public.island1.wordlength = 5
Public.island1.factor = 0.1925/0.05423
Public.forest1 = {}
Public.forest1.Data = require 'maps.pirates.noise_pregen.simplexwavelength100boxsize1000octaves5gain0p65lacunity2lengthpower1p2rms0p06243'
Public.forest1.upperscale = 100
Public.forest1.boxsize = 1000
Public.forest1.wordlength = 5
Public.forest1.factor = 0.1925/0.06243
return Public

File diff suppressed because one or more lines are too long

208
maps/pirates/ores.lua Normal file
View File

@ -0,0 +1,208 @@
-- local Balance = require 'maps.pirates.balance'
-- local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
local CoreData = require 'maps.pirates.coredata'
local _inspect = require 'utils.inspect'.inspect
local Common = require 'maps.pirates.common'
local Utils = require 'maps.pirates.utils_local'
local simplex_noise = require 'utils.simplex_noise'.d2
-- local CustomEvents = require 'maps.pirates.custom_events'
local Public = {}
function Public.try_ore_spawn(surface, realp, source_name, density_bonus)
density_bonus = density_bonus or 0
-- local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local choices = destination.dynamic_data.hidden_ore_remaining_abstract
local ret = false
if choices and Utils.length(choices) > 0 then
local choices_possible = {}
local choices_to_prioitise = {}
for k, v in pairs(choices) do
if v>0 then choices_possible[k] = v end
if (not destination.dynamic_data.ore_types_spawned[k]) then
choices_to_prioitise[#choices_to_prioitise + 1] = k
end
end
if Utils.length(choices_possible) > 0 then
local choice
if Utils.length(choices_to_prioitise) > 0 then
choice = choices_to_prioitise[Math.random(Utils.length(choices_to_prioitise))]
else
choice = Math.raffle2(choices_possible)
end
local placed
if choice == 'crude-oil' then
placed = Common.oil_abstract_to_real(6 + 0.7*choices[choice]/(Math.max(1, Math.ceil((choices[choice]/4)^(1/2))))) * (0.8 + 0.4 * Math.random()) --thesixthroc's magic function, just plot this to see that it makes sense
placed = Math.min(placed, Common.oil_abstract_to_real(choices[choice]))
local tile = surface.get_tile(realp)
if (not (tile and tile.name and Utils.contains(CoreData.tiles_that_conflict_with_resource_layer_extended, tile.name))) then
surface.create_entity{name = 'crude-oil', amount = placed, position = realp}
else
placed = 0
end
if placed then
choices[choice] = Math.max(0, choices[choice] - Common.oil_real_to_abstract(placed))
if placed > 0 and not destination.dynamic_data.ore_types_spawned[choice] then
destination.dynamic_data.ore_types_spawned[choice] = true
end
ret = true
end
else
local real_amount = Math.max(Common.minimum_ore_placed_per_tile, Common.ore_abstract_to_real(choices[choice]))
local density = (density_bonus + 17 + 4 * Math.random()) -- not too big, and not too much variation; it makes players have to stay longer
local radius_squared = (destination.static_params and destination.static_params.radius_squared_modifier or 1) * (12 + 45 * Math.slopefromto(Common.ore_abstract_to_real(choices[choice]), 800, 20000)) * (0.6 + Math.random()) --tuned
if source_name == 'rock-huge' then
radius_squared = radius_squared * 1.5
end
placed = Public.draw_noisy_ore_patch(surface, realp, choice, real_amount, radius_squared, density)
if placed then
choices[choice] = Math.max(0, choices[choice] - Common.ore_real_to_abstract(placed))
if placed > 0 and not destination.dynamic_data.ore_types_spawned[choice] then
destination.dynamic_data.ore_types_spawned[choice] = true
end
ret = true
end
end
end
end
-- script.raise_event(CustomEvents.enum['update_crew_progress_gui'], {})
return ret
end
function Public.draw_noisy_ore_patch(surface, position, name, budget, radius_squared, density, forced, flat)
flat = flat or false
budget = budget or 999999999
forced = forced or false
local amountplaced = 0
local radius = Math.sqrt(radius_squared)
position = {x = Math.ceil(position.x) - 0.5, y = Math.ceil(position.y) - 0.5}
if not position then return 0 end
if not name then return 0 end
if not surface then return 0 end
if not radius then return 0 end
if not density then return 0 end
local mixed_ore_raffle = {
'iron-ore', 'iron-ore', 'iron-ore', 'copper-ore', 'copper-ore', 'coal', 'stone'
}
local seed = surface.map_gen_settings.seed
local function try_draw_at_relative_position(x, y, strength)
local absx = x + position.x
local absy = y + position.y
local absp = {x = absx, y = absy}
local amount_to_place_here = Math.min(density * strength, budget - amountplaced)
if amount_to_place_here >= Common.minimum_ore_placed_per_tile then
if name == 'mixed' then
local noise = simplex_noise(x * 0.005, y * 0.005, seed) + simplex_noise(x * 0.01, y * 0.01, seed) * 0.3 + simplex_noise(x * 0.05, y * 0.05, seed) * 0.2
local i = (Math.floor(noise * 100) % #mixed_ore_raffle) + 1
name = mixed_ore_raffle[i]
end
local entity = {name = name, position = absp, amount = amount_to_place_here}
-- local area = {{absx - 0.05, absy - 0.05}, {absx + 0.05, absy + 0.05}}
local area2 = {{absx - 0.1, absy - 0.1}, {absx + 0.1, absy + 0.1}}
local area3 = {{absx - 2, absy - 2}, {absx + 2, absy + 2}}
local preexisting_ores = surface.find_entities_filtered{area = area2, type = 'resource'}
local added
if #preexisting_ores >= 1 then
local addedbool = false
for _, ore in pairs(preexisting_ores) do
if ore.name == name then
ore.amount = ore.amount + amount_to_place_here
amountplaced = amountplaced + amount_to_place_here
addedbool = true
break
end
end
if not addedbool then
added = surface.create_entity(entity)
end
else
local tile = surface.get_tile(absp)
local silos = surface.find_entities_filtered{area=area3, name='rocket-silo'}
if #silos == 0 and (not (tile and tile.name and Utils.contains(CoreData.tiles_that_conflict_with_resource_layer_extended, tile.name))) then
if forced then
surface.destroy_decoratives{area = area2}
for _, tree in pairs(surface.find_entities_filtered{area=area2, type='tree'}) do
tree.destroy()
end
added = surface.create_entity(entity)
else
local pos2 = surface.find_non_colliding_position(name, absp, 10, 1, true)
pos2 = pos2 or absp
entity = {name = name, position = pos2, amount = amount_to_place_here}
surface.destroy_decoratives{area = area2}
if pos2 and surface.can_place_entity(entity) then
added = surface.create_entity(entity)
end
end
end
end
if added and added.valid then
amountplaced = amountplaced + amount_to_place_here
end
end
end
for _, p in ipairs(Math.points_in_m20t20_squared_sorted_by_distance_to_origin) do
local x, y = p[1], p[2]
local distance_to_center = Math.sqrt(x^2 + y^2)
local noise
if flat then
noise = 0.99 * simplex_noise((position.x + x) * 1/3, (position.y + y) * 1/3, seed) * simplex_noise((position.x + x) * 1/9, (position.y + y) * 1/9, seed+100)
else --put noise on the unit circle
if distance_to_center > 0 then
noise = 0.99 * simplex_noise((position.x + x/distance_to_center) * 1/3, (position.y + y/distance_to_center) * 1/3, seed) * simplex_noise((position.x + x/distance_to_center) * 1/9, (position.y + y/distance_to_center) * 1/9, seed+100)
else
noise = 0.99 * simplex_noise((position.x) * 1/3, (position.y) * 1/3, seed) * simplex_noise((position.x) * 1/9, (position.y) * 1/9, seed+100)
end
end
local radius_noisy = radius * (1 + noise)
if distance_to_center < radius_noisy then
local strength
if flat then
-- if noise > -0.5 then strength = 1 else strength = 0 end
-- its hard to make it both noncircular and flat in per-tile count
strength = 1
else
strength = (3/2) * (1 - (distance_to_center/radius_noisy)^2)
end
try_draw_at_relative_position(x, y, strength)
end
if amountplaced >= budget then break end
end
return amountplaced
end
return Public

744
maps/pirates/overworld.lua Normal file
View File

@ -0,0 +1,744 @@
local Public = {}
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
-- local Server = require 'utils.server'
-- local Structures = require 'maps.pirates.structures.structures'
local Boats = require 'maps.pirates.structures.boats.boats'
local Surfaces = require 'maps.pirates.surfaces.surfaces'
local Crowsnest = require 'maps.pirates.surfaces.crowsnest'
local Dock = require 'maps.pirates.surfaces.dock'
-- local Islands = require 'maps.pirates.surfaces.islands.islands'
-- local Sea = require 'maps.pirates.surfaces.sea.sea'
local Crew = require 'maps.pirates.crew'
-- local Roles = require 'maps.pirates.roles.roles'
local Classes = require 'maps.pirates.roles.classes'
-- local Quest = require 'maps.pirates.quest'
-- local Parrot = require 'maps.pirates.parrot'
-- local Hold = require 'maps.pirates.surfaces.hold'
-- local Cabin = require 'maps.pirates.surfaces.cabin'
local Shop = require 'maps.pirates.shop.shop'
local Upgrades = require 'maps.pirates.boat_upgrades'
local Kraken = require 'maps.pirates.surfaces.sea.kraken'
local Highscore = require 'maps.pirates.highscore'
local CustomEvents = require 'maps.pirates.custom_events'
local infront_positions = {}
for x = -6, -3 do
for y = - 3, 3 do
infront_positions[#infront_positions + 1] = {x = x, y = y}
end
end
local interior_positions = {}
for x = 1, 14 do
for y = - 3, 3 do
interior_positions[#interior_positions + 1] = {x = x, y = y}
end
end
function Public.generate_destination_type_and_subtype(overworld_position)
local memory = Memory.get_crew_memory()
local macro_p = {x = overworld_position.x/40, y = overworld_position.y/24}
local macro_x = macro_p.x
local macro_y = macro_p.y
local type, subtype
local island_subtype_raffle = {'none', 'none', Surfaces.Island.enum.STANDARD, Surfaces.Island.enum.STANDARD_VARIANT, Surfaces.Island.enum.RED_DESERT, Surfaces.Island.enum.HORSESHOE}
if macro_x >= 6 then island_subtype_raffle[#island_subtype_raffle + 1] = Surfaces.Island.enum.WALKWAYS end
if macro_x >= 6 then island_subtype_raffle[#island_subtype_raffle + 1] = 'none' end
-- if macro_x >= 13 and macro_y == 1 then
-- island_subtype_raffle[#island_subtype_raffle + 1] = Surfaces.Island.enum.MAZE
-- island_subtype_raffle[#island_subtype_raffle + 1] = Surfaces.Island.enum.MAZE
-- end
if macro_x >= 21 then island_subtype_raffle[#island_subtype_raffle + 1] = Surfaces.Island.enum.SWAMP end
if macro_x >= 21 then island_subtype_raffle[#island_subtype_raffle + 1] = 'none' end
--avoid duplicate subtype twice in a row in the same lane
for _, d in pairs(memory.destinations) do
if d.subtype and d.overworld_position.x == macro_p.x - 40 and d.overworld_position.y == macro_p.y then
local new_island_subtype_raffle = Utils.ordered_table_with_values_removed(island_subtype_raffle, d.subtype)
-- if _DEBUG and #new_island_subtype_raffle ~= #island_subtype_raffle then
-- game.print('Removed ' .. d.subtype .. ' from raffle at ' .. p.x .. ',' .. p.y)
-- end
island_subtype_raffle = new_island_subtype_raffle
end
end
-- some other raffle removals for smoothness:
if macro_x == 4 then
island_subtype_raffle = Utils.ordered_table_with_values_removed(island_subtype_raffle, Surfaces.Island.enum.STANDARD)
end
if macro_x == 18 then
island_subtype_raffle = Utils.ordered_table_with_values_removed(island_subtype_raffle, 'none') --flying-robot-frame cost is here, and we just make sure there's an island to see it
end
if macro_x == 19 then
island_subtype_raffle = Utils.ordered_table_with_values_removed(island_subtype_raffle, Surfaces.Island.enum.SWAMP)
end
if macro_x == 0 then
if macro_y == 0 then
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.FIRST
elseif macro_y == -1 then
type = Surfaces.enum.DOCK
else
type = nil
end
elseif macro_x == 1 then
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.HORSESHOE --map where you break rocks
elseif macro_x == 2 then
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.STANDARD_VARIANT --aesthetically different to first map
elseif (macro_x > 25 and (macro_x - 22) % 20 == 0) then --we want this to overwrite dock, so putting it here.
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.RADIOACTIVE
elseif (macro_x > 25 and (macro_x - 22) % 20 == 18) then --we want this to overwrite dock, so putting it here. should be even so rocket launch is forced
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.MAZE
elseif macro_x == 23 then --overwrite dock. rocket launch cost
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.WALKWAYS
elseif macro_y == -1 and (((macro_x % 4) == 3 and macro_x ~= 15) or macro_x == 14) then --avoid x=15 because radioactive is there
type = Surfaces.enum.DOCK
elseif macro_x == 3 then
type = nil
elseif macro_x == 5 then --biter boats appear. large island works well so players run off
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.STANDARD
elseif macro_x == 6 then
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.MAZE
elseif macro_x == 8 then --game length decrease, pending more content
type = nil
-- elseif macro_x == 9 then --just before krakens
-- type = Surfaces.enum.ISLAND
-- subtype = Surfaces.Island.enum.RED_DESERT
elseif macro_x == 10 then --krakens appear
type = nil
-- elseif macro_x == 11 then
-- if macro_y == -1 then
-- type = Surfaces.enum.ISLAND
-- subtype = Surfaces.Island.enum.MAZE
-- else
-- type = nil
-- end
elseif macro_x == 11 then --just after krakens, but dock is here too, so there's a choice
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.SWAMP
elseif macro_x == 16 then
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.RADIOACTIVE
--electric engines needed at 20
-- elseif macro_x == 17 then --game length decrease, pending more content
-- type = nil
elseif macro_x == 20 then --game length decrease, pending more content
type = nil
elseif macro_x == 21 then --rocket launch cost
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.SWAMP
elseif macro_x == 22 then --game length decrease, pending more content. also kinda fun to have to steer in realtime due to double space
type = nil
elseif macro_x == 24 then --rocket launch cost
type = Surfaces.enum.ISLAND
subtype = Surfaces.Island.enum.MAZE
elseif macro_x == 25 then
type = nil --finish line
else
type = Surfaces.enum.ISLAND
if #island_subtype_raffle > 0 then
subtype = island_subtype_raffle[Math.random(#island_subtype_raffle)]
else
subtype = 'none'
end
if subtype == 'none' then
type = nil
subtype = nil
end
end
--== DEBUG override to test islands:
-- if _DEBUG and type == Surfaces.enum.ISLAND then
-- subtype = Surfaces.Island.enum.MAZE
-- -- subtype = nil
-- -- type = Surfaces.enum.DOCK
-- end
-- warning: the first map is unique in that it isn't all loaded by the time you arrive, which can cause issues. For example, structures might get placed after ore, thereby deleting the ore underneath them.
-- if _DEBUG and ((macro_p.x > 0 and macro_p.x < 25)) and type ~= Surfaces.enum.DOCK then
-- type = nil
-- subtype = nil
-- end
return {type = type, subtype = subtype}
end
function Public.generate_overworld_destination(p)
-- be careful when calling any Balance functions that depend on overworldx — they will be evaluated when the island is chosen, not on arrival
local memory = Memory.get_crew_memory()
local macro_p = {x = p.x/40, y = p.y/24}
local type_data = Public.generate_destination_type_and_subtype(p)
local type = type_data.type
local subtype = type_data.subtype
if type == Surfaces.enum.ISLAND then
local scope = Surfaces[Surfaces.enum.ISLAND][subtype]
local static_params = Utils.deepcopy(scope.Data.static_params_default)
local base_cost_to_undock
local normal_costitems = {'electronic-circuit', 'advanced-circuit'}
-- These need to scale up slightly slower than the static fuel depletion rate, so you're increasingly incentivised to leave:
local base_cost_1 = {
['electronic-circuit'] = Math.ceil(((macro_p.x-2)^(2/3))*120),
}
local base_cost_2 = {
['electronic-circuit'] = Math.ceil(((macro_p.x-2)^(2/3))*180),
['advanced-circuit'] = Math.ceil(((macro_p.x-14)^(2/3))*18),
-- the below got this response from a new player: "This feels... underwhelming."
-- ['electronic-circuit'] = Math.ceil(((macro_p.x-2)^(2/3))*120),
-- ['advanced-circuit'] = Math.ceil(((macro_p.x-14)^(2/3))*18),
}
local base_cost_2b = {
['electronic-circuit'] = Math.ceil(((macro_p.x-2)^(2/3))*180),
['flying-robot-frame'] = 3,
}
local base_cost_3 = {
['electronic-circuit'] = Math.ceil(((macro_p.x-2)^(2/3))*140),
['advanced-circuit'] = Math.ceil(((macro_p.x-14)^(2/3))*18),
['launch_rocket'] = true,
}
local base_cost_4 = {
['electronic-circuit'] = Math.ceil(((macro_p.x-2)^(2/3))*100),
['advanced-circuit'] = Math.ceil(((macro_p.x-14)^(2/3))*18),
['flying-robot-frame'] = Math.ceil(((macro_p.x-18)^(2/3))*10),
['launch_rocket'] = true,
}
local base_cost_5 = {
['electronic-circuit'] = Math.ceil(((macro_p.x-2)^(2/3))*100),
['advanced-circuit'] = Math.ceil(((macro_p.x-14)^(2/3))*18),
['flying-robot-frame'] = Math.ceil(((macro_p.x-18)^(2/3))*10),
-- ['electronic-circuit'] = Math.ceil(((macro_p.x-2)^(2/3))*100),
-- ['advanced-circuit'] = Math.ceil(((macro_p.x-14)^(2/3))*18),
-- ['flying-robot-frame'] = Math.ceil(((macro_p.x-18)^(2/3))*10),
}
-- if macro_p.x == 0 then
-- if _DEBUG then
-- base_cost_to_undock = {
-- ['electronic-circuit'] = 5,
-- ['engine-unit'] = 5,
-- ['advanced-circuit'] = 5,
-- ['flying-robot-frame'] = 5,
-- }
-- end
-- base_cost_to_undock = nil
-- elseif macro_p.x <= 6 then
if macro_p.x <= 6 then
-- base_cost_to_undock = {['electronic-circuit'] = 5}
base_cost_to_undock = nil
elseif macro_p.x <= 9 then
base_cost_to_undock = base_cost_1
elseif macro_p.x <= 15 then
if macro_p.x % 3 > 0 then
base_cost_to_undock = base_cost_1
else
base_cost_to_undock = nil
end
elseif macro_p.x == 18 then --a super small amount of flying-robot-frame on a relatively early level so that they see they need lubricant
base_cost_to_undock = base_cost_2b
elseif macro_p.x <= 20 then
if macro_p.x % 3 > 0 then
base_cost_to_undock = base_cost_2
else
base_cost_to_undock = nil
end
-- after this point, mandatory
elseif macro_p.x <= 23 then
base_cost_to_undock = base_cost_3
elseif macro_p.x <= 24 then
base_cost_to_undock = base_cost_4
else
base_cost_to_undock = Utils.deepcopy(base_cost_5)
local delete = normal_costitems[Math.random(#normal_costitems)]
base_cost_to_undock[delete] = nil
if macro_p.x < 50 then
if macro_p.x % 2 == 0 then
base_cost_to_undock['launch_rocket'] = true
end
else --now we're just trying to kill you
base_cost_to_undock['launch_rocket'] = true
end
end
-- override:
if subtype == Surfaces.Island.enum.RADIOACTIVE then
base_cost_to_undock = {
['uranium-235'] = Math.ceil(Math.ceil(80 + (macro_p.x - 1))),
-- ['uranium-235'] = Math.ceil(Math.ceil(80 + (macro_p.x)/2)), --tried adding beacons instead of this
}
end
-- -- debug override:
-- if _DEBUG then
-- base_cost_to_undock = {
-- ['electronic-circuit'] = 200,
-- ['launch_rocket'] = true,
-- }
-- end
static_params.base_cost_to_undock = base_cost_to_undock -- Multiplication by Balance.cost_to_leave_multiplier() happens later, in destination_on_collide.
--scheduled raft raids moved to destination_on_arrival
local ores_multiplier = Balance.island_richness_avg_multiplier()
if macro_p.x == 0 then ores_multiplier = 0.9 end
local base_ores = scope.Data.base_ores()
local rngs = {}
local rngsum = 0
local rngcount = 0
for k, _ in pairs(base_ores) do
if k ~= 'coal' then
local rng = 2*Math.random()
-- local rng = 1 + ((2*Math.random() - 1)^3) --lower variances
rngs[k] = rng
rngsum = rngsum + rng
rngcount = rngcount + 1
end
end
local abstract_ore_amounts = {}
for k, v in pairs(base_ores) do
local rng = 1
if not (k == 'coal' or macro_p.x == 0) then
rng = rngs[k] / (rngsum/rngcount) --average of 1
end
abstract_ore_amounts[k] = ores_multiplier * v * rng
end
static_params.abstract_ore_amounts = abstract_ore_amounts
static_params.radius_squared_modifier = (2 + 2 * Math.random())
if macro_p.x == 0 then static_params.radius_squared_modifier = 1.75 end
static_params.discord_emoji = scope.Data.discord_emoji
local rng = 0.5 + 1 * Math.random()
static_params.starting_treasure_maps = Math.ceil((static_params.base_starting_treasure_maps or 0) * rng)
static_params.starting_wood = Math.ceil(static_params.base_starting_wood or 1000)
static_params.starting_rock_material = Math.ceil(static_params.base_starting_rock_material or 300) * Balance.island_richness_avg_multiplier()
rng = 0.5 + 1 * Math.random()
static_params.starting_treasure = Math.ceil((static_params.base_starting_treasure or 1000) * Balance.island_richness_avg_multiplier() * rng)
static_params.name = scope.Data.display_names[Math.random(#scope.Data.display_names)]
local dest = Surfaces.initialise_destination{
static_params = static_params,
type = type,
subtype = subtype,
overworld_position = p,
}
Crowsnest.draw_destination(dest)
elseif type == Surfaces.enum.DOCK then
local boat_for_sale_type
-- if macro_p.x == 3 then
-- boat_for_sale_type = Boats.enum.CUTTER
-- elseif macro_p.x == 7 or macro_p.x == 0 then
-- boat_for_sale_type = Boats.enum.SLOOP_WITH_HOLD
-- end
boat_for_sale_type = Boats.enum.SLOOP
local upgrade_for_sale
if macro_p.x == 0 then
upgrade_for_sale = nil
elseif macro_p.x == 3 then
upgrade_for_sale = Upgrades.enum.MORE_POWER
elseif macro_p.x == 7 then
upgrade_for_sale = Upgrades.enum.EXTRA_HOLD
elseif macro_p.x % 16 < 8 then
upgrade_for_sale = Upgrades.enum.MORE_POWER
else
upgrade_for_sale = Upgrades.enum.EXTRA_HOLD
end --upgrades like UNLOCK_MERCHANTS will slot themselves in when necessary, due to .overwrite_a_dock_upgrade()
-- one day it's worth making this system more readable
local static_params = Utils.deepcopy(Dock.Data.static_params_default)
static_params.upgrade_for_sale = upgrade_for_sale
static_params.boat_for_sale_type = boat_for_sale_type
static_params.name = Dock.Data.display_names[Math.random(#Dock.Data.display_names)]
local dest = Surfaces.initialise_destination{
static_params = static_params,
type = type,
subtype = subtype,
overworld_position = {x = p.x, y = -36},
-- overworld_position = {x = p.x, y = 36},
}
Crowsnest.draw_destination(dest)
-- renderings e.g. for docks
local surface = Crowsnest.get_crowsnest_surface()
local x = Crowsnest.platformrightmostedge + dest.overworld_position.x
local y = dest.overworld_position.y
if dest.static_params.upgrade_for_sale then
local display_form = Upgrades.crowsnest_display_form[dest.static_params.upgrade_for_sale]
if not dest.dynamic_data.crowsnest_renderings then
dest.dynamic_data.crowsnest_renderings = {}
end
dest.dynamic_data.crowsnest_renderings.base_text_rendering = rendering.draw_text{
text = display_form .. ':',
surface = surface,
target = {x = x + 5.5, y = y + 2.5},
color = CoreData.colors.renderingtext_green,
scale = 7,
font = 'default-game',
alignment = 'right',
visible = false,
}
local i = 1
for price_name, price_count in pairs(Shop.Captains.main_shop_data_1[dest.static_params.upgrade_for_sale].base_cost) do
local sprite
if price_name == 'fuel' then
sprite = 'item/coal'
else
sprite = 'item/coin'
end
dest.dynamic_data.crowsnest_renderings[price_name] = {
text_rendering = rendering.draw_text{
text = Utils.bignumber_abbrevform2(price_count),
surface = surface,
target = {x = x + 6, y = y + 8.3 - i * 3.5},
color = CoreData.colors.renderingtext_green,
scale = 5.2,
font = 'default-game',
alignment = 'left',
visible = false,
},
sprite_rendering = rendering.draw_sprite{
sprite = sprite,
surface = surface,
target = {x = x + 14.1, y = y + 10.8 - i * 3.5},
x_scale = 4.5,
y_scale = 4.5,
visible = false,
}
}
i = i + 1
end
end
end
--== krakens ==--
local kraken_count = 0
local position_candidates
if type == nil then
kraken_count = Balance.krakens_per_free_slot(p.x)
position_candidates = interior_positions
elseif type ~= Surfaces.enum.DOCK then
kraken_count = Balance.krakens_per_slot()
position_candidates = infront_positions
end
-- override:
if macro_p.x < 10 then
kraken_count = 0
elseif macro_p.x == 10 then
kraken_count = 1
end
-- if _DEBUG then
-- kraken_count = 1
-- end
if position_candidates then
local positions_placed = {}
local whilesafety = 0
while whilesafety < 10 and (#positions_placed < Math.min(kraken_count, 10)) do
local p_chosen, p_kraken
local whilesafety2 = 0
while whilesafety2 < 50 and ((not p_kraken) or Utils.contains(positions_placed, p_chosen)) do
p_chosen = position_candidates[Math.random(#position_candidates)]
p_kraken = Utils.psum{p_chosen, p}
whilesafety2 = whilesafety2 + 1
end
Crowsnest.draw_kraken(p_kraken)
positions_placed[#positions_placed + 1] = p_kraken
memory.overworld_krakens[#memory.overworld_krakens + 1] = p_kraken
whilesafety = whilesafety + 1
end
-- game.print(#positions_placed .. ' krakens placed for' .. macro_p.x .. ', ' .. macro_p.y)
end
end
function Public.ensure_lane_generated_up_to(lane_yvalue, x)
-- make sure lane_yvalue=0 is painted first
local memory = Memory.get_crew_memory()
local highest_x = memory['greatest_overworldx_generated_for_' .. lane_yvalue] or -40
local whilesafety = 0
while whilesafety < 10 and highest_x < x do
whilesafety = whilesafety + 1
highest_x = highest_x + 32 + 7 + 1 -- should be at least maximum island size plus crowsnest platform size plus 1
if lane_yvalue == 0 then
Crowsnest.paint_water_between_overworld_positions(highest_x + 32 + 7 + 1, highest_x + 32 + 7 + 1 + 40)
-- a little hack that we're updating this here rather than Crowsnest, due to the dependency on Shop to avoid a loop... almost finished 1.0, so too late to figure out how to restructure things for now!
for _, dest in pairs(memory.destinations) do
if dest.static_params.upgrade_for_sale and dest.dynamic_data.crowsnest_renderings then
if rendering.is_valid(dest.dynamic_data.crowsnest_renderings.base_text_rendering) then
rendering.set_text(dest.dynamic_data.crowsnest_renderings.base_text_rendering, Upgrades.crowsnest_display_form[dest.static_params.upgrade_for_sale] .. ':')
end
for rendering_name, r in pairs(dest.dynamic_data.crowsnest_renderings) do
if type(r) == 'table' and r.text_rendering and rendering.is_valid(r.text_rendering) then
rendering.set_text(r.text_rendering, Utils.bignumber_abbrevform2(Shop.Captains.main_shop_data_1[dest.static_params.upgrade_for_sale].base_cost[rendering_name]))
end
end
end
end
Crowsnest.update_destination_renderings()
end
Public.generate_overworld_destination{x = highest_x, y = lane_yvalue}
end
memory['greatest_overworldx_generated_for_' .. lane_yvalue] = highest_x
end
function Public.is_position_free_to_move_to(p)
local memory = Memory.get_crew_memory()
local ret = true
for _, destination_data in pairs(memory.destinations) do
if p.x >= destination_data.overworld_position.x + 1 and p.x <= destination_data.overworld_position.x + destination_data.iconized_map_width + Crowsnest.platformwidth - 1 and p.y >= destination_data.overworld_position.y - destination_data.iconized_map_width/2 - Crowsnest.platformheight/2 + 1 and p.y <= destination_data.overworld_position.y + destination_data.iconized_map_width/2 + Crowsnest.platformheight/2 - 1 then
ret = false
break
end
end
return ret
end
function Public.check_for_kraken_collisions()
local memory = Memory.get_crew_memory()
local krakens = memory.overworld_krakens
for i, k in ipairs(krakens) do
local relativex = Crowsnest.platformrightmostedge + k.x - memory.overworldx
local relativey = k.y - memory.overworldy
if (relativex <= 3.5 and relativex >= -3.5 and relativey >= -4 and relativey <= 4) then
Kraken.try_spawn_kraken()
memory.overworld_krakens = Utils.ordered_table_with_index_removed(krakens, i)
end
end
end
function Public.check_for_destination_collisions()
local memory = Memory.get_crew_memory()
-- if memory.overworldx > CoreData.victory_x - 10 then return end
-- to avoid crashing into the finish line...
for _, destination_data in pairs(memory.destinations) do
local relativex = Crowsnest.platformrightmostedge + destination_data.overworld_position.x - memory.overworldx
local relativey = destination_data.overworld_position.y - memory.overworldy
if (relativex == 4 and relativey + destination_data.iconized_map_height/2 >= -3.5 and relativey - destination_data.iconized_map_height/2 <= 3.5) then
--or (relativey - destination_data.iconized_map.height/2 == 5 and (relativex >= -3.5 or relativex <= 4.5)) or (relativey + destination_data.iconized_map.height/2 == -4 and (relativex >= -3.5 or relativex <= 4.5))
Surfaces.clean_up(Common.current_destination())
Surfaces.create_surface(destination_data)
local index = destination_data.destination_index
memory.loadingticks = 0
memory.mapbeingloadeddestination_index = index
memory.currentdestination_index = index
memory.boat.state = Boats.enum_state.ATSEA_LOADING_MAP
script.raise_event(CustomEvents.enum['update_crew_progress_gui'], {})
local destination = Common.current_destination()
Surfaces.destination_on_collide(destination)
return true
end
end
return false
end
function Public.try_overworld_move_v2(vector) --islands stay, crowsnest moves
local memory = Memory.get_crew_memory()
if memory.game_lost or (memory.victory_pause_until_tick and game.tick < memory.victory_pause_until_tick) then return end
if memory.victory_continue_message then
memory.victory_continue_message = false
local message = 'The run now continues on \'Freeplay\'.'
Common.notify_force(memory.force, message, CoreData.colors.notify_victory)
end
Public.ensure_lane_generated_up_to(0, memory.overworldx + Crowsnest.Data.visibilitywidth)
Public.ensure_lane_generated_up_to(24, memory.overworldx + Crowsnest.Data.visibilitywidth)
Public.ensure_lane_generated_up_to(-24, memory.overworldx + Crowsnest.Data.visibilitywidth)
Public.overwrite_a_dock_upgrade()
if not Public.is_position_free_to_move_to{x = memory.overworldx + vector.x, y = memory.overworldy+ vector.y} then
if _DEBUG then log(string.format('can\'t move by ' .. vector.x .. ', ' .. vector.y)) end
return false
else
Crowsnest.move_crowsnest(vector.x, vector.y)
if vector.x > 0 then
-- crew bonus resources per x:
local crew = Common.crew_get_crew_members()
for _, player in pairs(crew) do
if Common.validate_player_and_character(player) then
local player_index = player.index
if memory.classes_table and memory.classes_table[player_index] and memory.classes_table[player_index] == Classes.enum.MERCHANT then
Common.flying_text_small(player.surface, player.position, '[color=0.97,0.9,0.2]+[/color]')
Common.give_items_to_crew{{name = 'coin', count = 40 * vector.x}}
end
end
end
-- other freebies:
for i=1,vector.x do
Common.give_items_to_crew(Balance.periodic_free_resources_per_x())
Balance.apply_crew_buffs_per_x(memory.force)
end
-- add some evo: (this will get reset upon arriving at a destination anyway, so this is just relevant for sea monsters and the like:)
local extra_evo = Balance.base_evolution_leagues(memory.overworldx) - Balance.base_evolution_leagues(memory.overworldx - vector.x)
Common.increment_evo(extra_evo)
end
if memory.overworldx >= CoreData.victory_x then
Crew.try_win()
end
if memory.overworldx % 40 == 0 then
local modal_captain = nil
local modal_captain_time = 0
for name, time in pairs(memory.captain_accrued_time_data) do
if time > modal_captain_time then
modal_captain_time = time
modal_captain = name
end
end
Highscore.write_score(memory.secs_id, memory.name, modal_captain, memory.completion_time or 0, memory.overworldx, CoreData.version_float, memory.difficulty, memory.max_players_recorded or 0)
end
return true
end
end
function Public.overwrite_a_dock_upgrade()
local memory = Memory.get_crew_memory()
if (memory.overworldx % (40*8)) == (40*4-1) then -- pick a point that _must_ be visited, i.e. right before a destination
if (memory.overworldx) == (40*4-1) then -- LEAVE A GAP at x=40*11, because we haven't developed an upgrade to put there yet
for _, dest in pairs(memory.destinations) do
if dest.type == Surfaces.enum.DOCK then
if dest.overworld_position.x == memory.overworldx + 1 + (40*7) then
dest.static_params.upgrade_for_sale = Upgrades.enum.MORE_POWER
end
end
end
else
local upgrade_to_overwrite_with
if not memory.dock_overwrite_variable then memory.dock_overwrite_variable = 1 end
local possible_overwrites = {}
if (not memory.merchant_ships_unlocked) then
possible_overwrites[#possible_overwrites + 1] = Upgrades.enum.UNLOCK_MERCHANTS
end
if (not memory.rockets_for_sale) then
possible_overwrites[#possible_overwrites + 1] = Upgrades.enum.ROCKETS_FOR_SALE
end
if #possible_overwrites > 0 then
if memory.dock_overwrite_variable > #possible_overwrites then memory.dock_overwrite_variable = 1 end
upgrade_to_overwrite_with = possible_overwrites[memory.dock_overwrite_variable]
-- bump the variable up, but only if the list hasn't reduced in length. use a second variable to track this:
if memory.dock_overwrite_variable_2 and memory.dock_overwrite_variable_2 == #possible_overwrites then
memory.dock_overwrite_variable = memory.dock_overwrite_variable + 1
end
memory.dock_overwrite_variable_2 = #possible_overwrites
end
if upgrade_to_overwrite_with then
for _, dest in pairs(memory.destinations) do
if dest.type == Surfaces.enum.DOCK then
if dest.overworld_position.x == memory.overworldx + 1 + (40*7) then
dest.static_params.upgrade_for_sale = upgrade_to_overwrite_with
end
end
end
end
end
end
end
return Public

303
maps/pirates/parrot.lua Normal file
View File

@ -0,0 +1,303 @@
-- local Math = require 'maps.pirates.math'
local Memory = require 'maps.pirates.memory'
local _inspect = require 'utils.inspect'.inspect
-- local Token = require 'utils.token'
-- local CoreData = require 'maps.pirates.coredata'
-- local Task = require 'utils.task'
-- local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
local Public = {}
local enum = {
IDLE_FLY = 'idle_fly',
FLY = 'fly',
TIP_FLYING_1 = 'tip_flying_1',
TIP_FLYING_2 = 'tip_flying_2',
TIP_LANDED_1 = 'tip_landed_1',
TIP_LANDED_2 = 'tip_landed_2',
TIP_SQUAWK = 'tip_squawk',
}
Public.enum = enum
-- local parrot_tip_interval = 15*60
Public.framecounts = {
idle_fly = 5,
fly = 8,
fly_right = 8,
squawk = 6,
freak = 2,
walk = 8,
chill = 1,
}
-- local parrot_tips = {
-- "Why not buy the map designer a coffee! ko-fi.com/thesixthroc!",
-- "Why not buy the map designer a coffee! ko-fi.com/thesixthroc!",
-- "Make suggestions at getcomfy.eu/discord!",
-- "To launch a second run, you need a fifth of the server's pirates to endorse it!",
-- "Resources granted to the ship appear in the captain's cabin!",
-- "On each island after the first, the ship generates ore!",
-- "Charge the silo to launch a rocket!",
-- "Launching rockets makes fuel and doubloon!",
-- "Charging silos makes pollution and evo!",
-- "The number of non-afk crewmembers affects pollution, evo and maximum stay time!",
-- "Once a silo has launched a rocket, biters will ignore it!",
-- "Charging a silo drains power from everything else on the network...",
-- "You can steer the boat from the crow's nest by placing 100 rail signals in one of the blue boxes!",
-- "When you visit a dock, the shop is updated with special trades!",
-- "Labs produce more research the further you've travelled!",
-- "On radioactive islands, biters don\'t care if you emit pollution! They only care how long you stay...",
-- "If X marks the spot - use inserters to dig!",
-- }
function Public.parrot_0()
local memory = Memory.get_crew_memory()
Common.parrot_speak(memory.force, 'We can wait here for as long as we like.')
end
-- function Public.parrot_80()
-- local memory = Memory.get_crew_memory()
-- Common.parrot_speak(memory.force, 'Let\'s build out the ship?')
-- end
function Public.parrot_boats_warning()
local memory = Memory.get_crew_memory()
Common.parrot_speak(memory.force, '200 leagues? I think we\'re being chased...')
end
function Public.parrot_kraken_warning()
local memory = Memory.get_crew_memory()
Common.parrot_speak(memory.force, '400 leagues coming up? What are those pink things I spy...')
end
function Public.parrot_radioactive_tip_1()
local memory = Memory.get_crew_memory()
Common.parrot_speak(memory.force, 'We\'ll need uranium-235 to push away from this island...')
end
function Public.parrot_radioactive_tip_2()
local memory = Memory.get_crew_memory()
Common.parrot_speak(memory.force, 'The biters don\'t care if we pollute here, but they evolve fast...')
end
function Public.parrot_maze_tip_1()
local memory = Memory.get_crew_memory()
Common.parrot_speak(memory.force, 'Something seems wrong with our minimap.')
end
function Public.parrot_800_tip()
local memory = Memory.get_crew_memory()
Common.parrot_speak(memory.force, 'The resources needed to leave will get a bit harder now...')
end
function Public.parrot_overstay_tip()
local memory = Memory.get_crew_memory()
Common.parrot_speak(memory.force, 'We\'ve been here quite a while! Check the evo...')
end
-- function Public.parrot_say_tip()
-- local memory = Memory.get_crew_memory()
-- local crew_force = memory.force
-- local tip = parrot_tips[Math.random(#parrot_tips)]
-- Common.parrot_speak(crew_force, tip)
-- end
-- function Public.parrot_tick()
-- local memory = Memory.get_crew_memory()
-- if not (memory.boat and memory.boat.surface_name) then return end
-- local surface = game.surfaces[memory.boat.surface_name]
-- if not surface and surface.valid then return end
-- local destination = Common.current_destination()
-- if destination.dynamic_data and destination.dynamic_data.timer and destination.dynamic_data.timer == Math.ceil(Balance.expected_time_on_island()) and (not destination.dynamic_data.parrot_gave_overstay_tip) then
-- destination.dynamic_data.parrot_gave_overstay_tip = true
-- local spawners = surface.find_entities_filtered({type = 'unit-spawner', force = memory.enemy_force_name})
-- local spawnerscount = #spawners or 0
-- if spawnerscount > 0 then --check biter bases actually exist
-- Public.parrot_overstay_tip()
-- end
-- end
-- local boat = memory.boat
-- local parrot = boat.parrot
-- local frame = parrot.frame
-- local render = parrot.render
-- local render_name = parrot.render_name
-- local state = parrot.state
-- local state_counter = parrot.state_counter or parrot_tip_interval*60/5
-- local resting_position_relative_to_boat = parrot.resting_position_relative_to_boat
-- local position_relative_to_boat = parrot.position_relative_to_boat
-- local sprite_extra_offset = boat.parrot.sprite_extra_offset
-- local text_extra_offset = boat.parrot.text_extra_offset
-- local real_position = Utils.psum{position_relative_to_boat, boat.position}
-- if state == enum.IDLE_FLY then
-- local ate_fish
-- if boat.state and boat.state == 'landed' and state_counter >= parrot_tip_interval*60/5 then
-- local nearby_characters = surface.find_entities_filtered{position = real_position, radius = 4, name = 'character'}
-- local nearby_characters_count = #nearby_characters
-- if nearby_characters_count > 0 then
-- local j = 1
-- while j <= nearby_characters_count do
-- if nearby_characters[j] and nearby_characters[j].valid and nearby_characters[j].player and Common.validate_player(nearby_characters[j].player) then
-- local player = nearby_characters[j].player
-- local inv = player.get_inventory(defines.inventory.character_main)
-- if inv and inv.get_item_count('raw-fish') >= 2 then
-- Common.give(player, {{name = 'raw-fish', count = -2, color = CoreData.colors.fish}})
-- ate_fish = true
-- break
-- end
-- end
-- j = j + 1
-- end
-- end
-- end
-- state_counter = state_counter + 1
-- if ate_fish then
-- Common.parrot_speak(memory.force, 'Tasty...')
-- local p1 = {x = boat.position.x - 15 - Math.random(35), y = boat.position.y - 8 + Math.random(15)}
-- local p2 = surface.find_non_colliding_position('stone-furnace', p1, 6, 0.5)
-- parrot.spot_to_fly_from = position_relative_to_boat
-- local real_fly_to = p2 or p1
-- parrot.spot_to_fly_to = {x = real_fly_to.x - boat.position.x, y = real_fly_to.y - boat.position.y}
-- parrot.fly_distance = Math.distance(parrot.spot_to_fly_from, parrot.spot_to_fly_to)
-- state = enum.TIP_FLYING_1
-- state_counter = 0
-- else
-- if game.tick % 10 == 0 then
-- frame = frame + 1
-- end
-- if boat.speed and boat.speed > 0 then
-- state = enum.FLY
-- end
-- end
-- elseif state == enum.TIP_FLYING_1 then
-- if boat.speed and boat.speed > 0 then
-- state_counter = 0
-- state = enum.IDLE_FLY
-- position_relative_to_boat = resting_position_relative_to_boat
-- else
-- if game.tick % 10 == 0 then
-- frame = frame + 1
-- end
-- if state_counter < parrot.fly_distance then
-- position_relative_to_boat = Utils.interpolate(parrot.spot_to_fly_from, parrot.spot_to_fly_to, state_counter/parrot.fly_distance)
-- state_counter = state_counter + 0.5
-- else
-- state_counter = 0
-- state = enum.TIP_LANDED_1
-- end
-- end
-- elseif state == enum.TIP_LANDED_1 then
-- if boat.speed and boat.speed > 0 then
-- state_counter = 0
-- state = enum.IDLE_FLY
-- position_relative_to_boat = resting_position_relative_to_boat
-- else
-- if state_counter < 20 then
-- state_counter = state_counter + 1
-- else
-- state_counter = 0
-- state = enum.TIP_SQUAWK
-- end
-- end
-- elseif state == enum.TIP_SQUAWK then
-- if boat.speed and boat.speed > 0 then
-- state_counter = 0
-- state = enum.IDLE_FLY
-- position_relative_to_boat = resting_position_relative_to_boat
-- else
-- if state_counter == 0 then
-- Public.parrot_say_tip()
-- end
-- if state_counter < 18 then
-- if game.tick % 15 == 0 then
-- frame = frame + 1
-- end
-- state_counter = state_counter + 1
-- else
-- state_counter = 0
-- state = enum.TIP_LANDED_2
-- end
-- end
-- elseif state == enum.TIP_LANDED_2 then
-- if boat.speed and boat.speed > 0 then
-- state_counter = 0
-- state = enum.IDLE_FLY
-- position_relative_to_boat = resting_position_relative_to_boat
-- else
-- if state_counter < 20 then
-- state_counter = state_counter + 1
-- else
-- state_counter = 0
-- state = enum.TIP_FLYING_2
-- local hold = parrot.spot_to_fly_to
-- parrot.spot_to_fly_to = parrot.spot_to_fly_from
-- parrot.spot_to_fly_from = hold
-- end
-- end
-- elseif state == enum.TIP_FLYING_2 then
-- if boat.speed and boat.speed > 0 then
-- state_counter = 0
-- state = enum.IDLE_FLY
-- position_relative_to_boat = resting_position_relative_to_boat
-- else
-- if game.tick % 10 == 0 then
-- frame = frame + 1
-- end
-- if state_counter < parrot.fly_distance then
-- position_relative_to_boat = Utils.interpolate(parrot.spot_to_fly_from, parrot.spot_to_fly_to, state_counter/parrot.fly_distance)
-- state_counter = state_counter + 0.5
-- else
-- state_counter = 0
-- state = enum.IDLE_FLY
-- end
-- end
-- elseif state == enum.FLY then
-- if game.tick % 10 == 0 then
-- frame = frame + 1
-- end
-- if (not boat.speed) or (boat.speed == 0) then state = enum.IDLE_FLY end
-- end
-- local sprite_name = state
-- if state == enum.TIP_FLYING_1 then sprite_name = 'fly' end
-- if state == enum.TIP_FLYING_2 then sprite_name = 'fly_right' end
-- if state == enum.TIP_LANDED_1 or state == enum.TIP_LANDED_2 then sprite_name = 'chill' end
-- if state == enum.TIP_SQUAWK then sprite_name = 'squawk' end
-- if frame > Public.framecounts[sprite_name] then frame = 1 end
-- parrot.state = state
-- parrot.frame = frame
-- parrot.state_counter = state_counter
-- parrot.position_relative_to_boat = position_relative_to_boat
-- rendering.set_sprite(render, "file/parrot/parrot_" .. sprite_name .. "_" .. frame .. ".png")
-- rendering.set_target(render, rendering.get_target(render).entity, Utils.psum{sprite_extra_offset, position_relative_to_boat})
-- rendering.set_visible(render, true)
-- rendering.set_target(render_name, rendering.get_target(render_name).entity, Utils.psum{text_extra_offset, position_relative_to_boat})
-- rendering.set_visible(render_name, true)
-- end
return Public

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,597 @@
local Public = {}
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
local CustomEvents = require 'maps.pirates.custom_events'
-- local Structures = require 'maps.pirates.structures.structures'
local Boats = require 'maps.pirates.structures.boats.boats'
local Surfaces = require 'maps.pirates.surfaces.surfaces'
local Crowsnest = require 'maps.pirates.surfaces.crowsnest'
local Server = require 'utils.server'
local Dock = require 'maps.pirates.surfaces.dock'
-- local Islands = require 'maps.pirates.surfaces.islands.islands'
local Sea = require 'maps.pirates.surfaces.sea.sea'
local Crew = require 'maps.pirates.crew'
local Roles = require 'maps.pirates.roles.roles'
-- local Parrot = require 'maps.pirates.parrot'
-- local Quest = require 'maps.pirates.quest'
local Shop = require 'maps.pirates.shop.shop'
local Overworld = require 'maps.pirates.overworld'
local Hold = require 'maps.pirates.surfaces.hold'
local Cabin = require 'maps.pirates.surfaces.cabin'
local Upgrades = require 'maps.pirates.boat_upgrades'
local Task = require 'utils.task'
local Token = require 'utils.token'
local ShopDock = require 'maps.pirates.shop.dock'
function Public.fuel_depletion_rate()
local memory = Memory.get_crew_memory()
local state = memory.boat.state
if state == Boats.enum_state.ATSEA_SAILING or state == Boats.enum_state.APPROACHING then
return Balance.fuel_depletion_rate_sailing()
elseif state == Boats.enum_state.LEAVING_DOCK then
return Balance.fuel_depletion_rate_sailing() * 2
elseif state == Boats.enum_state.RETREATING then
return Balance.fuel_depletion_rate_sailing() / 4
elseif state == Boats.enum_state.LANDED then
return Balance.fuel_depletion_rate_static()
elseif state == Boats.enum_state.DOCKED then
return -0.1
else
return 0
end
end
function Public.set_off_from_starting_dock()
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
memory.crewstatus = Crew.enum.LEAVING_INITIAL_DOCK
memory.boat.state = Boats.enum_state.LEAVING_DOCK
Boats.place_boat(memory.boat, CoreData.moving_boat_floor, false, false)
Common.current_destination().type = Surfaces.enum.LOBBY
memory.mapbeingloadeddestination_index = 1 -- whatever the index of the first island is
memory.loadingticks = 0
local surface = game.surfaces[CoreData.lobby_surface_name]
local p = Utils.psum{memory.boat.position, Boats.get_scope(memory.boat).Data.crewname_rendering_position}
memory.boat.rendering_crewname_text = rendering.draw_text{
text = memory.name,
-- render_layer = '125', --does nothing
surface = surface,
target = p,
color = CoreData.colors.renderingtext_yellow,
scale = 8,
font = 'default-game',
alignment = 'left'
}
end
function Public.go_from_starting_dock_to_first_destination()
local memory = Memory.get_crew_memory()
local boat = memory.boat
local crew_members = Crew.choose_crew_members()
local crew_members_count = #memory.crewplayerindices
if crew_members_count > 0 then
memory.crewstatus = Crew.enum.ADVENTURING
local message = '[' .. memory.name .. '] Crew members: '
local b = false
for _, index in pairs(memory.crewplayerindices) do
if game.players[index] and game.players[index].name then
if b == true then
message = message .. ', '
else b = true end
message = message .. game.players[index].name
end
end
message = message .. '.'
Server.to_discord_embed_raw(CoreData.comfy_emojis.pogkot .. message)
Roles.assign_captain_based_on_priorities()
for _, player in pairs(crew_members) do
Crew.player_abandon_endorsements(player)
for item, amount in pairs(Balance.starting_items_player) do
player.insert({name = item, count = amount})
end
end
boat.stored_resources = {}
Shop.Captains.initialise_captains_shop()
Hold.create_hold_surface(1)
Cabin.create_cabin_surface()
local items = Balance.starting_items_crew_upstairs()
-- Boats.deck_place_random_obstacle_boxes(boat, 6, items, 0)
-- Let's try just adding the items to nearby boxes
local scope = Boats.get_scope(boat)
local surface = game.surfaces[boat.surface_name]
local boxes = surface.find_entities_filtered{
name = 'wooden-chest',
area = {
{x = boat.position.x - scope.Data.width/2, y = boat.position.y - scope.Data.height/2},
{x = boat.position.x + scope.Data.width/2, y = boat.position.y + scope.Data.height/2}
},
}
boxes = Math.shuffle(boxes)
for i = 1, #items do
if boxes[i] then
local inventory = boxes[i].get_inventory(defines.inventory.chest)
for name, count in pairs(items[i]) do
inventory.insert{name = name, count = count}
end
else
game.print('fail at ' .. boxes[i].position.x .. ' ' .. boxes[i].position.y)
end
end
Public.progress_to_destination(1) --index of first destination
-- local scope = Boats.get_scope(boat)
-- local boatwidth, boatheight = scope.Data.width, scope.Data.height
-- Common.surface_place_random_obstacle_boxes(game.surfaces[boat.surface_name], {x = boat.position.x - boatwidth*0.575, y = boat.position.y}, boatwidth*0.85, boatheight*0.8, 'oil-refinery', {[1] = 3, [2] = 3, [3] = 0, [4] = 0}, items)
-- go:
-- Public.progress_to_destination(1) --index of first destination
boat.EEI_stage = 1
Boats.update_EEIs(boat)
-- if Common.difficulty() == 1 then
-- Boats.upgrade_chests(boat, 'iron-chest')
-- Hold.upgrade_chests(1, 'iron-chest')
-- Crowsnest.upgrade_chests('iron-chest')
-- elseif Common.difficulty() > 1 then
-- Boats.upgrade_chests(boat, 'steel-chest')
-- Hold.upgrade_chests(1, 'steel-chest')
-- Crowsnest.upgrade_chests('steel-chest')
-- end
memory.age = 0
memory.real_age = 0
else
Boats.destroy_boat(boat)
Crew.disband_crew()
end
end
local place_dock_jetty_and_boats = Token.register(
function(data)
Memory.set_working_id(data.crew_id)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
Surfaces.Dock.place_dock_jetty_and_boats()
local destination = Common.current_destination()
ShopDock.create_dock_markets(game.surfaces[destination.surface_name], Surfaces.Dock.Data.markets_position)
end
)
function Public.progress_to_destination(destination_index)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
local boat = memory.boat
local oldsurface = game.surfaces[boat.surface_name]
local old_type = Surfaces.SurfacesCommon.decode_surface_name(oldsurface.name).type
local destination_data = memory.destinations[destination_index]
local static_params = destination_data.static_params
local type = destination_data.type
local subtype = destination_data.subtype
local newsurface_name = Surfaces.SurfacesCommon.encode_surface_name(memory.id, destination_index, type, subtype)
local newsurface = game.surfaces[newsurface_name]
local initial_boatspeed, starting_boatposition
if type == Surfaces.enum.ISLAND then --moved from overworld generation, so that it updates properly
local covered1_requirement = Balance.covered1_entry_price()
destination_data.dynamic_data.covered1_requirement = covered1_requirement
end
if type == Surfaces.enum.DOCK then
local BoatData = Boats.get_scope(boat).Data
starting_boatposition = Utils.snap_coordinates_for_rails({x = Dock.Data.playerboat_starting_xcoord, y = Dock.Data.player_boat_top + BoatData.height/2})
-- starting_boatposition = {x = -destination_data.static_params.width/2 + BoatData.width + 10, y = Dock.Data.player_boat_top - BoatData.height/2}
Common.current_destination().dynamic_data.time_remaining = 180
-- memory.mainshop_availability_bools.sell_iron = true
memory.mainshop_availability_bools.buy_iron = true
memory.mainshop_availability_bools.buy_copper = true
-- memory.mainshop_availability_bools.buy_fast_loader = true
-- memory.mainshop_availability_bools.sell_copper = true
memory.mainshop_availability_bools.repair_cannons = true
local boat_for_sale_type = Common.current_destination().static_params.boat_for_sale_type
if boat_for_sale_type then
if boat_for_sale_type == Boats.enum.CUTTER then
memory.mainshop_availability_bools.new_boat_cutter = true
elseif boat_for_sale_type == Boats.enum.CUTTER_WITH_HOLD then
memory.mainshop_availability_bools.new_boat_cutter_with_hold = true
elseif boat_for_sale_type == Boats.enum.SLOOP_WITH_HOLD then
memory.mainshop_availability_bools.new_boat_sloop_with_hold = true
end
end
local upgrade_for_sale = Common.current_destination().static_params.upgrade_for_sale
if upgrade_for_sale then
for _, u in pairs(Upgrades.List) do
if upgrade_for_sale == u then
memory.mainshop_availability_bools[u] = true
end
end
end
script.raise_event(CustomEvents.enum['update_crew_fuel_gui'], {})
-- Delay.add(Delay.enum.PLACE_DOCK_JETTY_AND_BOATS)
Task.set_timeout_in_ticks(2, place_dock_jetty_and_boats, {crew_id = memory.id})
else
starting_boatposition = {x = static_params.boat_starting_xposition, y = static_params.boat_starting_yposition or 0}
end
-- if oldsurface.name == CoreData.lobby_surface_name then
-- initial_boatspeed = 3
-- else
-- initial_boatspeed = 1.5
-- end
initial_boatspeed = 1.4
boat.speed = initial_boatspeed
boat.state = destination_data.init_boat_state
boat.dockedposition = nil
memory.enemyboats = {}
local old_water = 'deepwater'
if old_type == Surfaces.enum.LOBBY or old_type == Surfaces.enum.DOCK then old_water = 'water' end
Boats.teleport_boat(boat, newsurface_name, starting_boatposition, CoreData.moving_boat_floor, old_water)
if old_type == Surfaces.enum.LOBBY then
Crowsnest.draw_extra_bits()
end
Crowsnest.paint_around_destination(destination_index, CoreData.overworld_presence_tile)
if memory.loadingticks then memory.loadingticks = -120 end
if old_type == Surfaces.enum.SEA then
game.delete_surface(oldsurface)
end
memory.destinationsvisited_indices[#memory.destinationsvisited_indices + 1] = destination_index
memory.currentdestination_index = destination_index --already done when we collide with it typically
local destination = Common.current_destination()
script.raise_event(CustomEvents.enum['update_crew_progress_gui'], {})
destination.dynamic_data.timer = 0
destination.dynamic_data.timeratlandingtime = nil
memory.extra_time_at_sea = 0
if old_type == Surfaces.enum.SEA or old_type == Surfaces.enum.CHANNEL or old_type == Surfaces.enum.DOCK then
-- move over anyone who was left behind, such as dead and spectating players
for _, player in pairs(game.connected_players) do
if type == Surfaces.enum.ISLAND and player.controller_type == defines.controllers.spectator then
if player.surface == oldsurface then --avoid moving players in hold etc
-- put them at a nice viewing position:
player.teleport({x = memory.spawnpoint.x + 120, y = memory.spawnpoint.y}, newsurface)
end
elseif player.surface == oldsurface then
player.teleport(memory.spawnpoint, newsurface)
end
end
end
Surfaces.destination_on_arrival(Common.current_destination())
end
function Public.check_for_end_of_boat_movement(boat)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local collided = Boats.collision_infront(boat)
local approaching_island = boat.state == Boats.enum_state.APPROACHING and destination.type == Surfaces.enum.ISLAND
local retreating_island = boat.state == Boats.enum_state.RETREATING and destination.type == Surfaces.enum.ISLAND
local approaching_dock = destination.type == Surfaces.enum.DOCK and boat.state == Boats.enum_state.APPROACHING
local leaving_dock = destination.type == Surfaces.enum.DOCK and boat.state == Boats.enum_state.LEAVING_DOCK
--=== Collision
if approaching_island and collided then
boat.state = Boats.enum_state.LANDED
boat.speed = 0
boat.dockedposition = boat.position
game.surfaces[boat.surface_name].play_sound{path = "utility/axe_fighting"}
game.surfaces[boat.surface_name].play_sound{path = "utility/axe_fighting"}
end
--=== Enemy
if boat.force_name == memory.enemy_force_name then
if approaching_island then
if collided then
boat.landing_time = destination.dynamic_data.timer
Boats.place_landingtrack(boat, CoreData.enemy_landing_tile)
return true
-- elseif boat.spawner and boat.spawner.valid and boat.spawner.destructible then
-- -- This code was somehow making the spawners destructible but still able to be shot at.
-- local boat2 = Utils.deepcopy(boat)
-- boat2.position = {x = boat.position.x + 6, y = boat.position.y}
-- if Boats.collision_infront(boat2) then
-- boat.spawner.destructible = false
-- end
end
end
--=== Friendly
elseif boat.force_name == memory.force_name then
if approaching_island and collided then
Surfaces.destination_on_crewboat_hits_shore(destination)
return true
elseif retreating_island and boat.position.x < ((boat.dockedposition.x or 999) - Boats.get_scope(boat).Data.width - 2 * Boats.get_scope(boat).Data.rightmost_gate_position - 8) then
Public.go_from_currentdestination_to_sea()
return true
elseif approaching_dock and boat.position.x + Boats.get_scope(boat).Data.rightmost_gate_position >= Dock.Data.rightmostgate_stopping_xposition then
boat.state = Boats.enum_state.DOCKED
boat.speed = 0
boat.dockedposition = boat.position
destination.dynamic_data.timeratlandingtime = destination.dynamic_data.timer
Boats.place_boat(boat, CoreData.static_boat_floor, false, false)
return true
elseif leaving_dock and boat.position.x >= game.surfaces[boat.surface_name].map_gen_settings.width/2 - 60 then
memory.mainshop_availability_bools.new_boat_cutter = false
memory.mainshop_availability_bools.new_boat_cutter_with_hold = false
memory.mainshop_availability_bools.new_boat_sloop_with_hold = false
-- memory.mainshop_availability_bools.sell_iron = false
memory.mainshop_availability_bools.buy_iron = false
memory.mainshop_availability_bools.buy_copper = false
-- memory.mainshop_availability_bools.buy_fast_loader = false
-- memory.mainshop_availability_bools.sell_copper = false
memory.mainshop_availability_bools.repair_cannons = false
memory.mainshop_availability_bools.extra_hold = false
memory.mainshop_availability_bools.upgrade_power = false
memory.mainshop_availability_bools.unlock_merchants = false
memory.mainshop_availability_bools.rockets_for_sale = false
script.raise_event(CustomEvents.enum['update_crew_fuel_gui'], {})
Public.go_from_currentdestination_to_sea()
return true
--=== Fallthrough right-hand side
elseif destination.type == Surfaces.enum.ISLAND and boat.position.x >= game.surfaces[boat.surface_name].map_gen_settings.width/2 - 10 then
Public.go_from_currentdestination_to_sea()
return true
end
end
return false
end
function Public.try_retreat_from_island(player, manual) -- Assumes the cost can be paid
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
local destination = Common.current_destination()
if Common.query_can_pay_cost_to_leave() then
if destination.dynamic_data.timeratlandingtime and destination.dynamic_data.timer < destination.dynamic_data.timeratlandingtime + 10 then
if player and Common.validate_player(player) then
Common.notify_player_error(player, 'Undock error: Can\'t undock in the first 10 seconds.')
end
else
local cost = destination.static_params.base_cost_to_undock
if cost then
local adjusted_cost = Common.time_adjusted_departure_cost(cost)
Common.spend_stored_resources(adjusted_cost)
end
Public.retreat_from_island(manual)
end
else
if player and Common.validate_player(player) then
Common.notify_player_error(player.force, 'Undock error: Not enough resources stored in the captain\'s cabin.')
end
end
end
function Public.retreat_from_island(manual)
local memory = Memory.get_crew_memory()
local boat = memory.boat
if boat.state and boat.state == Boats.enum_state.RETREATING then return end
boat.state = Boats.enum_state.RETREATING
boat.speed = 1.25
Boats.place_boat(boat, CoreData.moving_boat_floor, false, false)
local force = memory.force
if not (force and force.valid) then return end
if manual then
Common.notify_force(force,'[font=heading-1]Ship undocked[/font] by captain.')
else
Common.notify_force(force,'[font=heading-1]Ship auto-undocked[/font]. Return to ship.')
end
Surfaces.destination_on_departure(Common.current_destination())
end
function Public.undock_from_dock(manual)
local memory = Memory.get_crew_memory()
local boat = memory.boat
local destination = Common.current_destination()
boat.state = Boats.enum_state.LEAVING_DOCK
destination.dynamic_data.time_remaining = -1
Boats.place_boat(boat, CoreData.moving_boat_floor, false, false)
memory.mainshop_availability_bools.new_boat_cutter = false
memory.mainshop_availability_bools.new_boat_cutter_with_hold = false
memory.mainshop_availability_bools.new_boat_sloop_with_hold = false
script.raise_event(CustomEvents.enum['update_crew_fuel_gui'], {})
Crew.summon_crew()
local force = memory.force
if not (force and force.valid) then return end
if manual then
Common.notify_force(force,'[font=heading-1]Ship undocked[/font].')
else
Common.notify_force(force,'[font=heading-1]Ship auto-undocked[/font].')
end
end
function Public.go_from_currentdestination_to_sea()
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
if memory.game_lost then return end
local oldsurface = game.surfaces[destination.surface_name]
Sea.ensure_sea_surface()
local seaname = memory.sea_name
local boat = memory.boat
local new_boatposition = Utils.snap_coordinates_for_rails({x = Boats.get_scope(memory.boat).Data.width / 2, y = 0})
Boats.teleport_boat(boat, seaname, new_boatposition, CoreData.static_boat_floor, 'water')
if memory.overworldx == 0 and memory.boat then
local difficulty_name = CoreData.get_difficulty_name_from_value(Common.difficulty())
if difficulty_name == CoreData.difficulty_options[#CoreData.difficulty_options].text then
Boats.upgrade_chests(boat, 'steel-chest')
Hold.upgrade_chests(1, 'steel-chest')
Crowsnest.upgrade_chests('steel-chest')
Common.parrot_speak(memory.force, 'Steel chests for steel players! Squawk!')
elseif difficulty_name ~= CoreData.difficulty_options[1].text then
Boats.upgrade_chests(boat, 'iron-chest')
Hold.upgrade_chests(1, 'iron-chest')
Crowsnest.upgrade_chests('iron-chest')
Common.parrot_speak(memory.force, 'Iron chests for iron players! Squawk!')
end
end
memory.boat.state = Boats.enum_state.ATSEA_SAILING
memory.boat.speed = 0
memory.boat.position = new_boatposition
memory.boat.surface_name = seaname
memory.enemy_force.reset_evolution()
--@FIX: This doesn't change the evo during sea travel, which is relevant now that krakens are in the game:
local base_evo = Balance.base_evolution_leagues(memory.overworldx)
Common.set_evo(base_evo)
memory.kraken_evo = 0
memory.loadingticks = nil
memory.mapbeingloadeddestination_index = nil
local d = destination.iconized_map_width + Crowsnest.platformwidth
Crowsnest.paint_around_destination(destination.destination_index, 'deepwater')
Overworld.try_overworld_move_v2{x = d, y = 0}
local players_marooned_count = 0
for _, player in pairs(game.connected_players) do
if (player.surface == oldsurface and player.character and player.character.valid) then
players_marooned_count = players_marooned_count + 1
end
end
if players_marooned_count == 0 then
Surfaces.clean_up(destination)
end
end
return Public

401
maps/pirates/quest.lua Normal file
View File

@ -0,0 +1,401 @@
local Memory = require 'maps.pirates.memory'
-- local Roles = require 'maps.pirates.roles.roles'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
local Math = require 'maps.pirates.math'
-- local Loot = require 'maps.pirates.loot'
local CoreData = require 'maps.pirates.coredata'
local _inspect = require 'utils.inspect'.inspect
local Public = {}
local enum = {
TIME = 'Time',
FIND = 'Find',
NODAMAGE = 'No_Damage',
RESOURCEFLOW = 'Resource_Flow',
RESOURCECOUNT = 'Resource_Count',
WORMS = 'Worms',
}
Public.enum = enum
Public.quest_icons = {
[enum.TIME] = '[img=utility.time_editor_icon]',
[enum.NODAMAGE] = '[item=stone-wall]',
[enum.WORMS] = '[entity=small-worm-turret]',
[enum.FIND] = '[img=utility.ghost_time_to_live_modifier_icon]',
[enum.RESOURCEFLOW] = '',
[enum.RESOURCECOUNT] = '',
}
-- @TODO remake into a loot-style table:
function Public.quest_reward()
local ret
local multiplier = Balance.quest_reward_multiplier()
local rng = Math.random()
if rng <= 0.3 then
ret = {name = 'iron-plate', count = Math.ceil(2000 * multiplier), display_sprite = '[item=iron-plate]', display_amount = string.format('%.1fk', 2 * multiplier), chat_name = '[item=iron-plate]'}
elseif rng <= 0.5 then
ret = {name = 'copper-plate', count = Math.ceil(2000 * multiplier), display_sprite = '[item=copper-plate]', display_amount = string.format('%.1fk', 2 * multiplier), chat_name = '[item=copper-plate]'}
elseif rng <= 0.7 then
ret = {name = 'solid-fuel', count = Math.ceil(450 * multiplier), display_sprite = '[item=solid-fuel]', display_amount = string.format('%.0f', Math.ceil(450 * multiplier)), chat_name = '[item=solid-fuel]'}
elseif rng <= 0.9 then
ret = {name = 'coin', count = Math.ceil(6000 * (multiplier^(1/2))), display_sprite = '[item=coin]', display_amount = string.format('%.1fk', Math.ceil(6 * (multiplier^(1/2)))), chat_name = 'doubloons'}
else
ret = {name = 'piercing-rounds-magazine', count = Math.ceil(250 * multiplier), display_sprite = '[item=piercing-rounds-magazine]', display_amount = string.format('%.0f', Math.ceil(250 * multiplier)), chat_name = '[item=piercing-rounds-magazine]'}
end
return ret
end
function Public.initialise_random_quest()
local destination = Common.current_destination()
destination.dynamic_data.quest_complete = false
if destination.destination_index == 2 then return end
local rng = Math.random(10)
if rng == 1 then
Public.initialise_nodamage_quest()
elseif rng <= 3 then
Public.initialise_worms_quest()
elseif rng <= 5 then
Public.initialise_time_quest()
elseif rng <= 7 then
Public.initialise_find_quest()
elseif rng <= 10 then
Public.initialise_resourcecount_quest()
-- Public.initialise_resourceflow_quest()
end
-- Public.initialise_time_quest()
end
function Public.initialise_time_quest()
local destination = Common.current_destination()
destination.dynamic_data.quest_type = enum.TIME
destination.dynamic_data.quest_reward = Public.quest_reward()
destination.dynamic_data.quest_progress = Balance.time_quest_seconds()
destination.dynamic_data.quest_progressneeded = 9999999
return true
end
function Public.initialise_find_quest()
local destination = Common.current_destination()
-- @FIXME: Magic numbers
if destination.subtype and destination.subtype == '1' or destination.subtype == '5' or destination.subtype == '6' then
destination.dynamic_data.quest_type = enum.FIND
destination.dynamic_data.quest_reward = Public.quest_reward()
destination.dynamic_data.quest_progress = 0
if #Common.crew_get_crew_members() > 15 then
destination.dynamic_data.quest_progressneeded = 2
else
destination.dynamic_data.quest_progressneeded = 1
end
return true
else
log('Find quest not appropriate, rerolling')
Public.initialise_random_quest() --@FIXME: mild danger of loop
return false
end
end
function Public.initialise_nodamage_quest()
local destination = Common.current_destination()
if not destination and destination.dynamic_data and destination.dynamic_data.rocketsilomaxhp then return end
destination.dynamic_data.quest_type = enum.NODAMAGE
destination.dynamic_data.quest_reward = Public.quest_reward()
destination.dynamic_data.quest_progress = 0
destination.dynamic_data.quest_progressneeded = destination.dynamic_data.rocketsilomaxhp
return true
end
function Public.initialise_resourceflow_quest()
local destination = Common.current_destination()
if not destination and destination.dynamic_data and destination.dynamic_data.rocketsilomaxhp then return end
destination.dynamic_data.quest_type = enum.RESOURCEFLOW
destination.dynamic_data.quest_reward = Public.quest_reward()
destination.dynamic_data.quest_progress = 0
local generated_flow_quest = Public.generate_flow_quest()
destination.dynamic_data.quest_params = {item = generated_flow_quest.item}
local progressneeded_before_rounding = generated_flow_quest.base_rate * Balance.resource_quest_multiplier()
destination.dynamic_data.quest_progressneeded = Math.ceil(progressneeded_before_rounding/10)*10
return true
end
function Public.initialise_resourcecount_quest()
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
if not destination and destination.dynamic_data and destination.dynamic_data.rocketsilomaxhp then return end
destination.dynamic_data.quest_type = enum.RESOURCECOUNT
destination.dynamic_data.quest_reward = Public.quest_reward()
destination.dynamic_data.quest_progress = 0
local generated_production_quest = Public.generate_resourcecount_quest()
destination.dynamic_data.quest_params = {item = generated_production_quest.item}
local force = memory.force
if force and force.valid then
destination.dynamic_data.quest_params.initial_count = force.item_production_statistics.get_flow_count{name = generated_production_quest.item, input = true, precision_index = defines.flow_precision_index.one_thousand_hours, count = true}
end
local progressneeded_before_rounding = generated_production_quest.base_rate * Balance.resource_quest_multiplier() * Common.difficulty()
destination.dynamic_data.quest_progressneeded = Math.ceil(progressneeded_before_rounding/10)*10
return true
end
function Public.initialise_worms_quest()
local destination = Common.current_destination()
if not (destination.surface_name and game.surfaces[destination.surface_name]) then return end
local surface = game.surfaces[destination.surface_name]
local worms = surface.find_entities_filtered{type = 'turret'}
local count = 0
for i = 1, #worms do
local w = worms[i]
if w.destructible then count = count + 1 end
end
local needed = Math.ceil(
1 + 9 * Math.slopefromto(count, 0, 20) + 10 * Math.slopefromto(count, 20, 70)
)
if Common.difficulty() < 1 then needed = Math.max(1, needed - 3) end
if Common.difficulty() > 1 then needed = Math.max(1, needed + 2) end
local difficulty_name = CoreData.get_difficulty_name_from_value(Common.difficulty())
if difficulty_name == CoreData.difficulty_options[1].text then
needed = Math.max(1, needed - 3)
elseif difficulty_name ~= CoreData.difficulty_options[2].text then
needed = Math.max(1, needed + 2)
end
if needed >= 5 then
destination.dynamic_data.quest_type = enum.WORMS
destination.dynamic_data.quest_reward = Public.quest_reward()
destination.dynamic_data.quest_progress = 0
destination.dynamic_data.quest_progressneeded = needed
return true
else
log('Worms quest not appropriate, rerolling')
Public.initialise_random_quest() --@FIXME: mild danger of loop
return false
end
end
function Public.try_resolve_quest()
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
if destination.dynamic_data.quest_type and destination.dynamic_data.quest_progress and destination.dynamic_data.quest_progressneeded and destination.dynamic_data.quest_progress >= destination.dynamic_data.quest_progressneeded and (not destination.dynamic_data.quest_complete) then
local force = memory.force
if not (force and force.valid) then return end
Common.notify_force_light(force,'Granted ' .. destination.dynamic_data.quest_reward.display_amount .. ' ' .. destination.dynamic_data.quest_reward.chat_name)
local name = destination.dynamic_data.quest_reward.name
local count = destination.dynamic_data.quest_reward.count
-- destination.dynamic_data.quest_type = nil
-- destination.dynamic_data.quest_reward = nil
-- destination.dynamic_data.quest_progress = nil
-- destination.dynamic_data.quest_progressneeded = nil
destination.dynamic_data.quest_complete = true
local boat = memory.boat
if not boat then return end
local surface_name = boat.surface_name
if not surface_name then return end
local surface = game.surfaces[surface_name]
if not (surface and surface.valid) then return end
local chest = boat.output_chest
if not chest and chest.valid then return end
local inventory = chest.get_inventory(defines.inventory.chest)
local inserted = inventory.insert{name = name, count = count}
if inserted < count then
local chest2 = boat.backup_output_chest
if chest2 and chest2.valid then
local inventory2 = chest2.get_inventory(defines.inventory.chest)
local inserted2 = inventory2.insert{name = name, count = count - inserted}
if (inserted + inserted2) < count then
Common.notify_force(force,'Sadly, there wasn\'t space in the cabin for all of your reward.')
end
end
end
end
end
-- Public.flow_quest_data_raw = {
-- {0.2, 0, 1, false, 'submachine-gun', 3 * 12},
-- {1, 0, 1, false, 'electronic-circuit', 3 * 120},
-- {0.2, 0.1, 1, false, 'big-electric-pole', 1 * 120},
-- {0.4, 0.2, 1, false, 'engine-unit', 3 * 6},
-- -- {1, 0.5, 1, false, 'advanced-circuit', 1 * 10},
-- -- {0.3, 0.8, 1, false, 'electric-engine-unit', 1 * 6},
-- }
-- function Public.flow_quest_data()
-- local ret = {}
-- local data = Public.flow_quest_data_raw
-- for i = 1, #data do
-- local datum = data[i]
-- ret[#ret + 1] = {
-- weight = datum[1],
-- game_completion_progress_min = datum[2],
-- game_completion_progress_max = datum[3],
-- scaling = datum[4],
-- item = datum[5],
-- base_rate = datum[6],
-- }
-- end
-- return ret
-- end
function Public.generate_flow_quest()
local game_completion_progress = Common.game_completion_progress_capped()
local data = Public.flow_quest_data()
local v, w = {}, {}
for i = 1, #data, 1 do
table.insert(v, {item = data[i].item, base_rate = data[i].base_rate})
local destination = Common.current_destination()
if not (destination and destination.subtype and data[i].map_subtype and data[i].map_subtype == destination.subtype) then
if data[i].scaling then -- scale down weights away from the midpoint 'peak' (without changing the mean)
local midpoint = (data[i].game_completion_progress_max + data[i].game_completion_progress_min) / 2
local difference = (data[i].game_completion_progress_max - data[i].game_completion_progress_min)
table.insert(w, data[i].weight * Math.max(0, 1 - (Math.abs(game_completion_progress - midpoint) / (difference / 2))))
else -- no scaling
if data[i].game_completion_progress_min <= game_completion_progress and data[i].game_completion_progress_max >= game_completion_progress then
table.insert(w, data[i].weight)
else
table.insert(w, 0)
end
end
end
end
return Math.raffle(v, w)
end
Public.resourcecount_quest_data_raw = {
{1, 0, 1, false, 'iron-gear-wheel', 2400},
-- {1, 0, 1, false, 'electronic-circuit', 1400},
{1.2, 0, 1, false, 'transport-belt', 1600},
{1, 0, 1, false, 'repair-pack', 350},
-- {0.1, 0, 1, false, 'red-wire', 500},
{0.5, 0, 1, false, 'empty-barrel', 200},
{0.3, 0, 0.2, false, 'underground-belt', 200},
{0.3, 0, 0.2, false, 'splitter', 150},
{0.4, 0.2, 1, false, 'fast-splitter', 60},
{0.4, 0.2, 1, false, 'fast-underground-belt', 75},
{0.5, 0.3, 1, false, 'big-electric-pole', 160},
{1.2, 0.61, 1, false, 'advanced-circuit', 350},
{1, 0, 1, false, 'shotgun-shell', 600},
{1.5, 0.9, 1, false, 'processing-unit', 40},
-- {0.3, 0.8, 1, false, 'electric-engine-unit', 1 * 6},
}
function Public.resourcecount_quest_data()
local ret = {}
local data = Public.resourcecount_quest_data_raw
for i = 1, #data do
local datum = data[i]
ret[#ret + 1] = {
weight = datum[1],
game_completion_progress_min = datum[2],
game_completion_progress_max = datum[3],
scaling = datum[4],
item = datum[5],
base_rate = datum[6],
}
end
return ret
end
function Public.generate_resourcecount_quest()
local game_completion_progress = Common.game_completion_progress_capped()
local data = Public.resourcecount_quest_data()
local v, w = {}, {}
for i = 1, #data, 1 do
table.insert(v, {item = data[i].item, base_rate = data[i].base_rate})
local destination = Common.current_destination()
if not (destination and destination.subtype and data[i].map_subtype and data[i].map_subtype == destination.subtype) then
if data[i].scaling then -- scale down weights away from the midpoint 'peak' (without changing the mean)
local midpoint = (data[i].game_completion_progress_max + data[i].game_completion_progress_min) / 2
local difference = (data[i].game_completion_progress_max - data[i].game_completion_progress_min)
table.insert(w, data[i].weight * Math.max(0, 1 - (Math.abs(game_completion_progress - midpoint) / (difference / 2))))
else -- no scaling
if data[i].game_completion_progress_min <= game_completion_progress and data[i].game_completion_progress_max >= game_completion_progress then
table.insert(w, data[i].weight)
else
table.insert(w, 0)
end
end
end
end
return Math.raffle(v, w)
end
return Public

View File

@ -0,0 +1,233 @@
-- data:extend(
-- {
-- {
-- type='map-settings',
-- name='map-settings',
-- pollution=
-- {
-- enabled=true,
-- -- these are values for 60 ticks (1 simulated second)
-- --
-- -- amount that is diffused to neighboring chunk
-- -- (possibly repeated for other directions as well)
-- diffusion_ratio=0.02,
-- -- this much PUs must be on the chunk to start diffusing
-- min_to_diffuse=15,
-- -- constant modifier a percentage of 1 - the pollution eaten by a chunks tiles
-- ageing=1,
-- -- anything bigger than this is visualised as this value
-- expected_max_per_chunk=150,
-- -- anything lower than this (but > 0) is visualised as this value
-- min_to_show_per_chunk=50,
-- min_pollution_to_damage_trees = 60,
-- pollution_with_max_forest_damage = 150,
-- pollution_per_tree_damage = 50,
-- pollution_restored_per_tree_damage = 10,
-- max_pollution_to_restore_trees = 20,
-- enemy_attack_pollution_consumption_modifier = 1
-- },
-- enemy_evolution=
-- {
-- enabled=true,
-- -- percentual increase in the evolve factor for every second (60 ticks)
-- time_factor = 0.000004,
-- -- percentual increase in the evolve factor for every destroyed spawner
-- destroy_factor = 0.002,
-- -- percentual increase in the evolve factor for 1 pollution unit
-- pollution_factor = 0.0000009
-- },
-- enemy_expansion=
-- {
-- enabled = true,
-- -- Distance in chunks from the furthest base around.
-- -- This prevents expansions from reaching too far into the
-- -- player's territory
-- max_expansion_distance = 7,
-- friendly_base_influence_radius = 2,
-- enemy_building_influence_radius = 2,
-- -- A candidate chunk's score is given as follows:
-- -- player = 0
-- -- for neighbour in all chunks within enemy_building_influence_radius from chunk:
-- -- player += number of player buildings on neighbour
-- -- * building_coefficient
-- -- * neighbouring_chunk_coefficient^distance(chunk, neighbour)
-- --
-- -- base = 0
-- -- for neighbour in all chunk within friendly_base_influence_radius from chunk:
-- -- base += num of enemy bases on neighbour
-- -- * other_base_coefficient
-- -- * neighbouring_base_chunk_coefficient^distance(chunk, neighbour)
-- --
-- -- score(chunk) = 1 / (1 + player + base)
-- --
-- -- The iteration is over a square region centered around the chunk for which the calculation is done,
-- -- and includes the central chunk as well. distance is the Manhattan distance, and ^ signifies exponentiation.
-- building_coefficient = 0.1,
-- other_base_coefficient = 2.0,
-- neighbouring_chunk_coefficient = 0.5,
-- neighbouring_base_chunk_coefficient = 0.4,
-- -- A chunk has to have at most this much percent unbuildable tiles for it to be considered a candidate.
-- -- This is to avoid chunks full of water to be marked as candidates.
-- max_colliding_tiles_coefficient = 0.9,
-- -- Size of the group that goes to build new base (in game this is multiplied by the
-- -- evolution factor).
-- settler_group_min_size = 5,
-- settler_group_max_size = 20,
-- -- Ticks to expand to a single
-- -- position for a base is used.
-- --
-- -- cooldown is calculated as follows:
-- -- cooldown = lerp(max_expansion_cooldown, min_expansion_cooldown, -e^2 + 2 * e),
-- -- where lerp is the linear interpolation function, and e is the current evolution factor.
-- min_expansion_cooldown = 4 * 3600,
-- max_expansion_cooldown = 60 * 3600
-- },
-- unit_group=
-- {
-- -- pollution triggered group waiting time is a random time between min and max gathering time
-- min_group_gathering_time = 3600,
-- max_group_gathering_time = 10 * 3600,
-- -- after the gathering is finished the group can still wait for late members,
-- -- but it doesn't accept new ones anymore
-- max_wait_time_for_late_members = 2 * 3600,
-- -- limits for group radius (calculated by number of numbers)
-- max_group_radius = 30.0,
-- min_group_radius = 5.0,
-- -- when a member falls behind the group he can speedup up till this much of his regular speed
-- max_member_speedup_when_behind = 1.4,
-- -- When a member gets ahead of its group, it will slow down to at most this factor of its speed
-- max_member_slowdown_when_ahead = 0.6,
-- -- When members of a group are behind, the entire group will slow down to at most this factor of its max speed
-- max_group_slowdown_factor = 0.3,
-- -- If a member falls behind more than this times the group radius, the group will slow down to max_group_slowdown_factor
-- max_group_member_fallback_factor = 3,
-- -- If a member falls behind more than this time the group radius, it will be removed from the group.
-- member_disown_distance = 10,
-- tick_tolerance_when_member_arrives = 60,
-- -- Maximum number of automatically created unit groups gathering for attack at any time.
-- max_gathering_unit_groups = 30,
-- -- Maximum size of an attack unit group. This only affects automatically-created unit groups;
-- -- manual groups created through the API are unaffected.
-- max_unit_group_size = 200
-- },
-- steering=
-- {
-- default=
-- {
-- -- not including the radius of the unit
-- radius = 1.2,
-- separation_force = 0.005,
-- separation_factor = 1.2,
-- force_unit_fuzzy_goto_behavior = false
-- },
-- moving=
-- {
-- radius = 3,
-- separation_force = 0.01,
-- separation_factor = 3,
-- -- used only for special 'to look good' purposes (like in trailer)
-- force_unit_fuzzy_goto_behavior = false
-- }
-- },
-- path_finder=
-- {
-- -- defines whether we prefer forward (>1) or backward (<-1) or symmetrical (1) search
-- fwd2bwd_ratio = 1,
-- -- when comparing nodes in open which one to check next
-- -- heuristic value is multiplied by this ratio
-- -- the higher the number the more is the search directed directly towards the goal
-- goal_pressure_ratio = 2,
-- -- How many nodes can be expanded at most per tick.
-- max_steps_worked_per_tick = 1000,
-- -- How much work each patfinding job is allowed to do per tick.
-- max_work_done_per_tick = 8000,
-- -- path cache setings
-- use_path_cache = true,
-- -- number of elements in the cache
-- short_cache_size = 5,
-- long_cache_size = 25,
-- -- minimal distance to goal for path to be searched in short path cache
-- short_cache_min_cacheable_distance = 10,
-- -- minimal number of algorithm steps for path to be inserted into the short path cache
-- short_cache_min_algo_steps_to_cache = 50,
-- -- minimal distance to goal for path to be searched in long path cache
-- long_cache_min_cacheable_distance = 30,
-- -- when searching for connection to path cache path, search at most for this number of steps times the initial estimate
-- cache_max_connect_to_cache_steps_multiplier = 100,
-- -- when looking for path from cache make sure it doesn't start too far from requested start in relative distance terms
-- cache_accept_path_start_distance_ratio = 0.2,
-- -- when looking for path from cache make sure it doesn't end too far from requested end
-- -- this is typically higher than accept value for the start because the end target can be moving
-- cache_accept_path_end_distance_ratio = 0.15,
-- -- Same as cache_accept_path_start_distance_ratio, but used for negative cache queries
-- negative_cache_accept_path_start_distance_ratio = 0.3,
-- -- Same as cache_accept_path_end_distance_ratio, but used for negative cache queries
-- negative_cache_accept_path_end_distance_ratio = 0.3,
-- -- when assigning rating to the best path this * start distances is considered
-- cache_path_start_distance_rating_multiplier = 10,
-- -- when assigning rating to the best path this * end distances is considered
-- -- this is typically higher than value for the start to achieve better path end quality
-- cache_path_end_distance_rating_multiplier = 20,
-- -- somewhere along the path is stuck enemy we need to avoid
-- -- this is mainly to handle situations when units have arrived and are attacking the target
-- -- then units further in the back will use this and run around the target
-- stale_enemy_with_same_destination_collision_penalty = 30,
-- -- if there is a moving unit further than this we don't really care
-- ignore_moving_enemy_collision_distance = 5,
-- -- enemy is not moving/or is too close and has different destination
-- enemy_with_different_destination_collision_penalty = 30,
-- -- simplification for now - collision with everything else is this
-- general_entity_collision_penalty = 10,
-- -- collision penalty for successors of positions that require destroy to reach
-- general_entity_subsequent_collision_penalty = 3,
-- -- Collision penalty for collisions in the extended bounding box but outside the entity's actual bounding box
-- extended_collision_penalty = 3,
-- -- uptil this amount any client will be served by the path finder (no estimate on the path length)
-- max_clients_to_accept_any_new_request = 10,
-- -- from max_clients_to_accept_any_new_request till this one only those that have a short estimate will be served
-- max_clients_to_accept_short_new_request = 100,
-- -- this is the 'threshold' to decide what is short and what is not
-- direct_distance_to_consider_short_request = 100,
-- -- if a short request takes more than this many steps, it will be rescheduled as a long request
-- short_request_max_steps = 1000,
-- -- How many steps will be allocated to short requests each tick, as a ratio of all available steps per tick
-- short_request_ratio = 0.5,
-- -- absolute minimum of steps that will be performed for every path find request no matter what
-- min_steps_to_check_path_find_termination = 2000,
-- -- if the amount of steps is higher than this times estimate of start to goal then path finding is terminated
-- start_to_goal_cost_multiplier_to_terminate_path_find = 2000.0,
-- -- When the number of waiting clients exceeds certain values, the per-tick work limit will be increased by the
-- -- given multiplier. overload_levels gives the threshold values, overload_multipliers gives the multipliers.
-- -- overload_levels and overload_multipliers must be the same length.
-- overload_levels = {0, 100, 500},
-- overload_multipliers = {2, 3, 4}
-- },
-- -- If a behavior fails this many times, the enemy (or enemy group)
-- -- is destroyed.
-- -- This solves biters stuck within their own base.
-- max_failed_behavior_count = 3,
-- -- These can't be changed through game.map_settings - you must use game.difficulty_settings
-- difficulty_settings =
-- {
-- recipe_difficulty = defines.difficulty_settings.recipe_difficulty.normal,
-- technology_difficulty = defines.difficulty_settings.technology_difficulty.normal,
-- technology_price_multiplier = 1,
-- research_queue_setting = 'after-victory'
-- }
-- }
-- })

View File

@ -0,0 +1,285 @@
local Balance = require 'maps.pirates.balance'
local _inspect = require 'utils.inspect'.inspect
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
local Common = require 'maps.pirates.common'
local Utils = require 'maps.pirates.utils_local'
local CoreData = require 'maps.pirates.coredata'
-- local Server = require 'utils.server'
local Public = {}
local enum = {
DECKHAND = 1,
FISHERMAN = 2,
SCOUT = 3,
SAMURAI = 4,
MERCHANT = 5,
SHORESMAN = 6,
BOATSWAIN = 7,
PROSPECTOR = 8,
LUMBERJACK = 9,
MASTER_ANGLER = 10,
WOOD_LORD = 11,
CHIEF_EXCAVATOR = 12,
HATAMOTO = 13,
IRON_LEG = 14,
QUARTERMASTER = 15,
DREDGER = 16,
SMOLDERING = 17,
GOURMET = 18,
}
Public.enum = enum
Public.Class_List = {
enum.DECKHAND,
enum.FISHERMAN,
enum.SCOUT,
enum.SAMURAI,
enum.MERCHANT,
enum.SHORESMAN,
enum.BOATSWAIN,
enum.PROSPECTOR,
enum.LUMBERJACK,
enum.MASTER_ANGLER,
enum.WOOD_LORD,
enum.CHIEF_EXCAVATOR,
enum.HATAMOTO,
enum.IRON_LEG,
enum.QUARTERMASTER,
enum.DREDGER,
enum.SMOLDERING,
enum.GOURMET,
}
Public.display_form = {
[enum.DECKHAND] = 'Deckhand',
[enum.FISHERMAN] = 'Fisherman',
[enum.SCOUT] = 'Scout',
[enum.SAMURAI] = 'Samurai',
[enum.MERCHANT] = 'Merchant',
[enum.SHORESMAN] = 'Shoresman',
[enum.BOATSWAIN] = 'Boatswain',
[enum.PROSPECTOR] = 'Prospector',
[enum.LUMBERJACK] = 'Lumberjack',
[enum.MASTER_ANGLER] = 'Master Angler',
[enum.WOOD_LORD] = 'Lord of the Woods',
[enum.CHIEF_EXCAVATOR] = 'Chief Excavator',
[enum.HATAMOTO] = 'Hatamoto',
[enum.IRON_LEG] = 'Iron Leg',
[enum.QUARTERMASTER] = 'Quartermaster',
[enum.DREDGER] = 'Dredger',
[enum.SMOLDERING] = 'Smoldering',
[enum.GOURMET] = 'Gourmet',
}
Public.explanation = {
[enum.DECKHAND] = 'They move faster and generate ore for the cabin whilst onboard above deck, but move slower offboard.',
[enum.FISHERMAN] = 'They fish at greater distance.',
[enum.SCOUT] = 'They are faster, but frail and deal less damage.',
[enum.SAMURAI] = 'They are tough, and *with no weapon equipped* fight well by melee, but poorly otherwise.',
[enum.MERCHANT] = 'They generate 40 doubloons per league.',
[enum.SHORESMAN] = 'They move slightly faster and generate ore for the cabin whilst offboard, but move slower onboard.',
[enum.BOATSWAIN] = 'They move faster and generate ore for the cabin whilst below deck, but move slower offboard.',
[enum.PROSPECTOR] = 'They find more resources when handmining ore.',
[enum.LUMBERJACK] = 'They find more resources when chopping trees.',
[enum.MASTER_ANGLER] = 'They fish at much greater distance, and catch more.',
[enum.WOOD_LORD] = 'They find many more resources when chopping trees.',
[enum.CHIEF_EXCAVATOR] = 'They find many more resources when handmining ore.',
[enum.HATAMOTO] = 'They are very tough, and *with no weapon equipped* fight well by melee, but poorly otherwise.',
[enum.IRON_LEG] = 'They are very resistant to damage when carrying 3500 iron ore.',
[enum.QUARTERMASTER] = 'They give nearby crewmates extra physical attack, and generate ore for the cabin for each one.',
[enum.DREDGER] = 'They find surprising items when they fish.',
[enum.SMOLDERING] = 'They periodically convert wood into coal, if they have less than 50 coal.',
[enum.GOURMET] = 'They generate ore for the cabin by eating fish in fancy locations.',
}
Public.class_unlocks = {
[enum.FISHERMAN] = {enum.MASTER_ANGLER},
[enum.LUMBERJACK] = {enum.WOOD_LORD},
-- [enum.PROSPECTOR] = {enum.CHIEF_EXCAVATOR}, --breaks the resource pressure in the game too strongly I think
[enum.SAMURAI] = {enum.HATAMOTO},
[enum.MASTER_ANGLER] = {enum.DREDGER},
}
Public.class_purchase_requirement = {
[enum.MASTER_ANGLER] = enum.FISHERMAN,
[enum.WOOD_LORD] = enum.LUMBERJACK,
-- [enum.CHIEF_EXCAVATOR] = enum.PROSPECTOR,
[enum.HATAMOTO] = enum.SAMURAI,
[enum.DREDGER] = enum.MASTER_ANGLER,
}
function Public.initial_class_pool()
return {
enum.DECKHAND,
enum.DECKHAND, --good for afk players
enum.SHORESMAN,
enum.SHORESMAN,
enum.QUARTERMASTER,
enum.FISHERMAN,
enum.SCOUT,
enum.SAMURAI,
-- enum.MERCHANT, --not interesting, breaks coin economy
enum.BOATSWAIN,
enum.PROSPECTOR,
enum.LUMBERJACK,
enum.IRON_LEG,
enum.SMOLDERING,
enum.GOURMET,
}
end
function Public.assign_class(player_index, class, self_assigned)
local memory = Memory.get_crew_memory()
local player = game.players[player_index]
if not memory.classes_table then memory.classes_table = {} end
if memory.classes_table[player_index] == class then
Common.notify_player_error(player, 'Class error: You\'re already a ' .. Public.display_form[class] .. '.')
return false
end
if Utils.contains(memory.spare_classes, class) then -- verify that one is spare
Public.try_renounce_class(player, false)
memory.classes_table[player_index] = class
local force = memory.force
if force and force.valid then
local message
if self_assigned then
message = '%s took the spare class %s. ([font=scenario-message-dialog]%s[/font])'
Common.notify_force_light(force,string.format(message, player.name, Public.display_form[memory.classes_table[player_index]], Public.explanation[memory.classes_table[player_index]]))
else
message = 'A spare %s class was given to %s. [font=scenario-message-dialog](%s)[/font]'
Common.notify_force_light(force,string.format(message, Public.display_form[memory.classes_table[player_index]], player.name, Public.explanation[memory.classes_table[player_index]]))
end
end
memory.spare_classes = Utils.ordered_table_with_single_value_removed(memory.spare_classes, class)
return true
else
Common.notify_player_error(player, 'Class error: No spare class of that type is available.')
return false
end
end
function Public.try_renounce_class(player, whisper_failure_message, override_message)
local memory = Memory.get_crew_memory()
local force = memory.force
if force and force.valid and player and player.index then
if memory.classes_table and memory.classes_table[player.index] then
if force and force.valid then
if override_message then
Common.notify_force_light(force,string.format(override_message, Public.display_form[memory.classes_table[player.index]]))
else
Common.notify_force_light(force,string.format('%s gave up %s.', player.name, Public.display_form[memory.classes_table[player.index]])) --shorter for less spam
-- Common.notify_force_light(force,string.format('%s gave up the class %s.', player.name, Public.display_form[memory.classes_table[player.index]]))
end
end
memory.spare_classes[#memory.spare_classes + 1] = memory.classes_table[player.index]
memory.classes_table[player.index] = nil
elseif whisper_failure_message then
Common.notify_player_error(player, 'Class error: You don\'t have any class to give up.')
end
end
end
function Public.generate_class_for_sale()
local memory = Memory.get_crew_memory()
-- if #memory.available_classes_pool == 0 then
-- -- memory.available_classes_pool = Public.initial_class_pool() --reset to initial state
-- -- turned off as this makes too many classes
-- end
local class
if #memory.available_classes_pool > 0 then
class = memory.available_classes_pool[Math.random(#memory.available_classes_pool)]
end
return class
end
function Public.class_ore_grant(player, how_much, disable_scaling)
local count
if disable_scaling then
count = Math.ceil(how_much)
else
count = Math.ceil(how_much * Balance.class_resource_scale())
end
if Math.random(4) == 1 then
Common.flying_text_small(player.surface, player.position, '[color=0.85,0.58,0.37]+' .. count .. '[/color]')
Common.give_items_to_crew{{name = 'copper-ore', count = count}}
else
Common.flying_text_small(player.surface, player.position, '[color=0.7,0.8,0.8]+' .. count .. '[/color]')
Common.give_items_to_crew{{name = 'iron-ore', count = count}}
end
end
local function class_on_player_used_capsule(event)
local player = game.players[event.player_index]
if not player or not player.valid then
return
end
local player_index = player.index
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
if not (player.character and player.character.valid) then
return
end
local item = event.item
if not (item and item.name and item.name == 'raw-fish') then return end
if memory.classes_table and memory.classes_table[player_index] then
local class = memory.classes_table[player_index]
if class == Public.enum.GOURMET then
local tile = player.surface.get_tile(player.position)
if tile.valid then
local multiplier = 0
if tile.name == CoreData.world_concrete_tile then
multiplier = 1.5
elseif tile.name == 'cyan-refined-concrete' then
multiplier = 1.6
elseif tile.name == CoreData.walkway_tile then
multiplier = 1
elseif tile.name == 'orange-refined-concrete' then
multiplier = 0.8
elseif tile.name == CoreData.enemy_landing_tile then
multiplier = 0.33
elseif tile.name == CoreData.static_boat_floor then
multiplier = 0.1
end
if multiplier > 0 then
local timescale = 60*30 * Math.max((Balance.game_slowness_scale())^(2/3),0.8)
if memory.gourmet_recency_tick then
multiplier = multiplier * Math.max(0.2, Math.min(5, (1/5)^((memory.gourmet_recency_tick - game.tick)/(60*300))))
memory.gourmet_recency_tick = Math.max(memory.gourmet_recency_tick, game.tick - timescale*10) + timescale
else
multiplier = multiplier * 5
memory.gourmet_recency_tick = game.tick - timescale*10 + timescale
end
Public.class_ore_grant(player, 10 * multiplier, true)
end
end
end
end
end
local event = require 'utils.event'
event.add(defines.events.on_player_used_capsule, class_on_player_used_capsule)
return Public

View File

@ -0,0 +1,650 @@
local Session = require 'utils.datastore.session_data'
local Antigrief = require 'utils.antigrief'
-- local Balance = require 'maps.pirates.balance'
local _inspect = require 'utils.inspect'.inspect
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
local Common = require 'maps.pirates.common'
local Utils = require 'maps.pirates.utils_local'
local CoreData = require 'maps.pirates.coredata'
local Server = require 'utils.server'
local Classes = require 'maps.pirates.roles.classes'
local Public = {}
local privilege_levels = {
NORMAL = 1,
OFFICER = 2,
CAPTAIN = 3
}
Public.privilege_levels = privilege_levels
--== Roles — General ==--
function Public.reset_officers()
local memory = Memory.get_crew_memory()
memory.officers_table = {}
end
function Public.make_officer(captain, player)
local memory = Memory.get_crew_memory()
local force = memory.force
if Utils.contains(Common.crew_get_crew_members(), player) then
if (not (captain.index == player.index)) then
if Common.validate_player(player) then
memory.officers_table[player.index] = true
local message = (captain.name .. ' made ' .. player.name .. ' an officer.')
Common.notify_force_light(force, message)
Public.update_privileges(player)
else
Common.notify_player_error(captain, 'Command error: Player is invalid.')
return false
end
else
Common.notify_player_error(captain, 'Command error: Can\'t promote yourself to officer.')
return false
end
else
Common.notify_player_error(captain, 'Command error: Player is not a crewmember.')
return false
end
end
function Public.unmake_officer(captain, player)
local memory = Memory.get_crew_memory()
local force = memory.force
if Utils.contains(Common.crew_get_crew_members(), player) then
if memory.officers_table[player.index] then
memory.officers_table[player.index] = nil
local message = (captain.name .. ' unmade ' .. player.name .. ' an officer.')
Common.notify_force_light(force, message)
Public.update_privileges(player)
return true
else
Common.notify_player_error(captain, 'Command error: Player isn\'t an officer.')
return false
end
else
Common.notify_player_error(captain, 'Command error: Player is not a crewmember.')
return false
end
end
function Public.revoke_class(captain, player)
local memory = Memory.get_crew_memory()
local force = memory.force
if force and force.valid and player.index and memory.classes_table[player.index] then
memory.spare_classes[#memory.spare_classes + 1] = memory.classes_table[player.index]
memory.classes_table[player.index] = nil
Common.notify_force_light(force, string.format('%s revoked %s from %s.', captain.name, Classes.display_form[memory.classes_table[player.index]]), player.name)
end
end
function Public.tag_text(player)
local memory = Memory.get_crew_memory()
local str = ''
local tags = {}
if memory.id ~= 0 and Common.is_captain(player) then
tags[#tags + 1] = 'Cap\'n'
elseif player.controller_type == defines.controllers.spectator then
tags[#tags + 1] = 'Spectating'
elseif memory.officers_table and memory.officers_table[player.index] then
tags[#tags + 1] = 'Officer'
end
local classes_table = memory.classes_table
if classes_table and classes_table[player.index] then
tags[#tags + 1] = Classes.display_form[classes_table[player.index]]
end
for i, t in ipairs(tags) do
if i>1 then str = str .. ', ' end
str = str .. t
end
if (not (str == '')) then str = '[' .. str .. ']' end
return str
end
function Public.update_tags(player)
local str = Public.tag_text(player)
player.tag = str
end
-- function Public.get_classes_print_string()
-- local str = 'Current class Descriptions:'
-- for i, class in ipairs(Classes.Class_List) do
-- str = str .. '\n' .. Classes.display_form[class] .. ': ' .. Classes.explanation[class] .. ''
-- end
-- return str
-- end
function Public.get_class_print_string(class)
for _, class2 in ipairs(Classes.Class_List) do
if Classes.display_form[class2]:lower() == class:lower() then
local str = ''
str = str .. Classes.display_form[class2] .. ': '
if Classes.class_purchase_requirement[class2] then
str = str .. 'An upgrade of ' .. Classes.display_form[Classes.class_purchase_requirement[class2]] .. '. '
end
str = str .. Classes.explanation[class2]
return str
end
end
if class:lower() == 'officer' then
return 'Officer: Assigned by the captain, officers can use the Captain\'s shop and access privileged chests.'
end
if class:lower() == 'captain' then
return 'Captain: Has executive power to undock the ship, purchase items, and various other special actions. When the game assigns a captain, it gives priority to those who have been playing the longest as a non-captain.'
end
return nil
end
function Public.player_privilege_level(player)
local memory = Memory.get_crew_memory()
if memory.id ~= 0 and Common.is_captain(player) then
return Public.privilege_levels.CAPTAIN
elseif memory.officers_table and memory.officers_table[player.index] then
return Public.privilege_levels.OFFICER
else
return Public.privilege_levels.NORMAL
end
end
function Public.make_captain(player)
local global_memory = Memory.get_global_memory()
local memory = Memory.get_crew_memory()
if memory.playerindex_captain then
Public.update_privileges(game.players[memory.playerindex_captain])
end
memory.playerindex_captain = player.index
global_memory.playerindex_to_captainhood_priority[player.index] = nil
memory.captain_acceptance_timer = nil
Public.reset_officers()
Public.update_privileges(player)
end
function Public.player_confirm_captainhood(player)
local memory = Memory.get_crew_memory()
local captain_index = memory.playerindex_captain
if not (player.index == captain_index) then
Common.notify_player_error(player, 'Command error: You\'re not the captain.')
else
if memory.captain_acceptance_timer then
memory.captain_acceptance_timer = nil
local force = player.force
if force and force.valid then
local message = (player.name .. ' accepted the role of captain.')
Common.notify_force(force, message)
Server.to_discord_embed_raw(CoreData.comfy_emojis.derp .. '[' .. memory.name .. '] ' .. message)
end
else
Common.notify_player_expected(player, 'Command error: You\'re not temporary, so you don\'t need to accept.')
end
end
end
function Public.player_left_so_redestribute_roles(player)
-- local memory = Memory.get_crew_memory()
if player and player.index then
if Common.is_captain(player) then
Public.assign_captain_based_on_priorities()
end
-- no need to do this, as long as officers get reset when the captainhood changes hands
-- if memory.officers_table and memory.officers_table[player.index] then
-- memory.officers_table[player.index] = nil
-- end
end
Classes.try_renounce_class(player, false, "A %s class is now spare.")
end
function Public.renounce_captainhood(player)
local global_memory = Memory.get_global_memory()
local memory = Memory.get_crew_memory()
if #Common.crew_get_crew_members() == 1 then
Common.notify_player_error(player, 'Command error: But you\'re the only crew member...')
else
local force = memory.force
global_memory.playerindex_to_captainhood_priority[player.index] = nil
if force and force.valid then
local message = (player.name .. ' renounces their title of captain.')
Common.notify_force(force, message)
Server.to_discord_embed_raw(CoreData.comfy_emojis.ree1 .. '[' .. memory.name .. '] ' .. message)
end
Public.assign_captain_based_on_priorities(player.index)
end
end
function Public.resign_as_officer(player)
local memory = Memory.get_crew_memory()
local force = memory.force
if memory.officers_table and memory.officers_table[player.index] then
memory.officers_table[player.index] = nil
local message = (player.name .. ' resigns as an officer.')
Common.notify_force(force, message)
Server.to_discord_embed_raw(CoreData.comfy_emojis.ree1 .. '[' .. memory.name .. '] ' .. message)
else
log('Error: player tried to resign as officer despite not being one.')
end
end
function Public.confirm_captain_exists(player_to_make_captain_otherwise)
local memory = Memory.get_crew_memory()
-- Currently this catches an issue where a crew drops to zero players, and then someone else joins.
if (memory.id and memory.id > 0 and memory.crewstatus and memory.crewstatus == 'adventuring') and (not (memory.playerindex_captain and game.players[memory.playerindex_captain] and Common.validate_player(game.players[memory.playerindex_captain]))) then --fixme: enum hacked
if player_to_make_captain_otherwise then
Public.make_captain(player_to_make_captain_otherwise)
-- game.print('Auto-reassigning captain.')
else
log('Error: Couldn\'t make a captain.')
end
end
end
function Public.pass_captainhood(player, player_to_pass_to)
-- local global_memory = Memory.get_global_memory()
local memory = Memory.get_crew_memory()
local force = memory.force
if not (force and force.valid) then return end
local message = string.format("%s has passed their captainhood to %s.", player.name, player_to_pass_to.name)
Common.notify_force(force, message)
Server.to_discord_embed_raw(CoreData.comfy_emojis.spurdo .. '[' .. memory.name .. '] ' .. message)
Public.make_captain(player_to_pass_to)
end
function Public.afk_player_tick(player)
-- local global_memory = Memory.get_global_memory()
local memory = Memory.get_crew_memory()
if Common.is_captain(player) and #Common.crew_get_nonafk_crew_members() > 0 then
local force = memory.force
if force and force.valid then
local message = string.format(player.name .. ' was afk.')
Common.notify_force(force, message)
Server.to_discord_embed_raw(CoreData.comfy_emojis.loops .. '[' .. memory.name .. '] ' .. message)
end
if #Common.crew_get_nonafk_crew_members() == 1 then --don't need to bounce it around
Public.make_captain(Common.crew_get_nonafk_crew_members()[1])
else
Public.assign_captain_based_on_priorities()
end
end
end
function Public.assign_captain_based_on_priorities(excluded_player_index)
excluded_player_index = excluded_player_index or nil
local global_memory = Memory.get_global_memory()
local memory = Memory.get_crew_memory()
local crew_members = memory.crewplayerindices
if not (crew_members and #crew_members > 0) then return end
local only_found_afk_players = true
local best_priority_so_far = -1
local captain_index = nil
local captain_name = nil
for _, player_index in pairs(crew_members) do
local player = game.players[player_index]
if Common.validate_player(player) and not (player.index == excluded_player_index) then
local player_active = Utils.contains(Common.crew_get_nonafk_crew_members(), player)
-- prefer non-afk players:
if only_found_afk_players or player_active then
only_found_afk_players = player_active
local player_priority = global_memory.playerindex_to_captainhood_priority[player_index]
if player_priority and player_priority > best_priority_so_far then
best_priority_so_far = player_priority
captain_index = player_index
captain_name = player.name
end
end
end
end
local force = memory.force
if not (force and force.valid) then return end
if not captain_index then
captain_index = crew_members[1]
captain_name = game.players[captain_index].name
Common.notify_force(force,'Looking for a suitable captain...')
end
if captain_index then
local player = game.players[captain_index]
if player and Common.validate_player(player) then
Public.make_captain(player)
-- this sets memory.captain_acceptance_timer = nil so now we must reset that after this function
end
end
if #Common.crew_get_crew_members() > 1 then
local messages = {
"would you like to be captain?",
"would you like to be captain?",
"captain?",
"is it your turn to be captain?",
}
local message = captain_name .. ', ' .. messages[Math.random(#messages)]
Common.notify_force_light(force, message .. ' If yes say /ok')
-- Server.to_discord_embed_raw('[' .. memory.name .. ']' .. CoreData.comfy_emojis.spurdo .. ' ' .. message)
memory.captain_acceptance_timer = 72 --tuned
else
memory.captain_acceptance_timer = nil
end
end
function Public.captain_requisition(captain_index)
local memory = Memory.get_crew_memory()
local any_taken = false
local items_to_req = {'coin', 'uranium-235'}
local item_count_table = {}
for _, i in pairs(items_to_req) do
item_count_table[i] = 0
end
local crew_members = memory.crewplayerindices
local captain = game.players[captain_index]
if not (captain and crew_members) then return end
local captain_inv = captain.get_inventory(defines.inventory.character_main)
if captain_inv and captain_inv.valid then
for _, player_index in pairs(crew_members) do
if player_index ~= captain_index then
local player = game.players[player_index]
if player and player.valid and not (memory.officers_table and memory.officers_table[player.index]) then
local inv = player.get_inventory(defines.inventory.character_main)
if inv and inv.valid then
for _, i in pairs(items_to_req) do
local amount = inv.get_item_count(i)
if amount and amount > 0 then
inv.remove{name=i, count=amount}
captain_inv.insert{name=i, count=amount}
item_count_table[i] = item_count_table[i] + amount
any_taken = true
end
end
end
local cursor_stack = player.cursor_stack
if cursor_stack and cursor_stack.valid_for_read then
for _, i in pairs(items_to_req) do
if cursor_stack.name == i then
local cursor_stack_count = cursor_stack.count
if cursor_stack_count > 0 then
cursor_stack.count = 0
captain_inv.insert{name=i, count = cursor_stack_count}
item_count_table[i] = item_count_table[i] + cursor_stack_count
any_taken = true
end
break
end
end
end
end
end
end
if any_taken then
local str = 'The captain requisitioned '
local j = 1
for i = 1, #items_to_req do
local item = items_to_req[i]
local count = item_count_table[item]
if count > 0 then
if j > 1 then
if i == #items_to_req then
str = str .. ' and '
else
str = str .. ', '
end
end
local display_name = item
if display_name == 'coin' then display_name = 'doubloons' end
if count >= 1000 then
str = str .. Utils.bignumber_abbrevform2(count) .. ' ' .. display_name
else
str = str .. count .. ' ' .. display_name
end
j = j + 1
end
end
str = str .. '.'
Common.notify_force(memory.force, str)
else
Common.notify_player_error(captain, 'No important items found in crewmates\' inventories or cursor stacks.')
end
end
end
function Public.add_player_to_permission_group(player, group_override)
-- local jailed = Jailed.get_jailed_table()
-- local enable_permission_group_disconnect = WPT.get('disconnect_wagon')
local session = Session.get_session_table()
local AG = Antigrief.get()
local gulag = game.permissions.get_group('gulag')
local tbl = gulag and gulag.players
for i = 1, #tbl do
if tbl[i].index == player.index then
return
end
end
-- if player.admin then
-- return
-- end
local playtime = player.online_time
if session and session[player.name] then
playtime = player.online_time + session[player.name]
end
-- if jailed[player.name] then
-- return
-- end
if not game.permissions.get_group('restricted_area') then
local group = game.permissions.create_group('restricted_area')
group.set_allows_action(defines.input_action.edit_permission_group, false)
group.set_allows_action(defines.input_action.import_permissions_string, false)
group.set_allows_action(defines.input_action.delete_permission_group, false)
group.set_allows_action(defines.input_action.add_permission_group, false)
group.set_allows_action(defines.input_action.admin_action, false)
group.set_allows_action(defines.input_action.cancel_craft, false)
group.set_allows_action(defines.input_action.drop_item, false)
group.set_allows_action(defines.input_action.drop_blueprint_record, false)
group.set_allows_action(defines.input_action.build, false)
group.set_allows_action(defines.input_action.build_rail, false)
group.set_allows_action(defines.input_action.build_terrain, false)
group.set_allows_action(defines.input_action.begin_mining, false)
group.set_allows_action(defines.input_action.begin_mining_terrain, false)
-- group.set_allows_action(defines.input_action.deconstruct, false) --pick up dead players
group.set_allows_action(defines.input_action.activate_copy, false)
group.set_allows_action(defines.input_action.activate_cut, false)
group.set_allows_action(defines.input_action.activate_paste, false)
group.set_allows_action(defines.input_action.upgrade, false)
group.set_allows_action(defines.input_action.grab_blueprint_record, false)
if not CoreData.blueprint_library_allowed then
group.set_allows_action(defines.input_action.open_blueprint_library_gui, false)
end
if not CoreData.blueprint_importing_allowed then
group.set_allows_action(defines.input_action.import_blueprint_string, false)
group.set_allows_action(defines.input_action.import_blueprint, false)
end
group.set_allows_action(defines.input_action.open_gui, false)
group.set_allows_action(defines.input_action.fast_entity_transfer, false)
group.set_allows_action(defines.input_action.fast_entity_split, false)
end
if not game.permissions.get_group('restricted_area_privileged') then
local group = game.permissions.create_group('restricted_area_privileged')
group.set_allows_action(defines.input_action.edit_permission_group, false)
group.set_allows_action(defines.input_action.import_permissions_string, false)
group.set_allows_action(defines.input_action.delete_permission_group, false)
group.set_allows_action(defines.input_action.add_permission_group, false)
group.set_allows_action(defines.input_action.admin_action, false)
group.set_allows_action(defines.input_action.cancel_craft, false)
group.set_allows_action(defines.input_action.drop_item, false)
group.set_allows_action(defines.input_action.drop_blueprint_record, false)
group.set_allows_action(defines.input_action.build, false)
group.set_allows_action(defines.input_action.build_rail, false)
group.set_allows_action(defines.input_action.build_terrain, false)
group.set_allows_action(defines.input_action.begin_mining, false)
group.set_allows_action(defines.input_action.begin_mining_terrain, false)
-- group.set_allows_action(defines.input_action.deconstruct, false) --pick up dead players
group.set_allows_action(defines.input_action.activate_copy, false)
group.set_allows_action(defines.input_action.activate_cut, false)
group.set_allows_action(defines.input_action.activate_paste, false)
group.set_allows_action(defines.input_action.upgrade, false)
group.set_allows_action(defines.input_action.grab_blueprint_record, false)
if not CoreData.blueprint_library_allowed then
group.set_allows_action(defines.input_action.open_blueprint_library_gui, false)
end
if not CoreData.blueprint_importing_allowed then
group.set_allows_action(defines.input_action.import_blueprint_string, false)
group.set_allows_action(defines.input_action.import_blueprint, false)
end
end
if not game.permissions.get_group('plebs') then
local plebs_group = game.permissions.create_group('plebs')
if not _DEBUG then
plebs_group.set_allows_action(defines.input_action.edit_permission_group, false)
plebs_group.set_allows_action(defines.input_action.import_permissions_string, false)
plebs_group.set_allows_action(defines.input_action.delete_permission_group, false)
plebs_group.set_allows_action(defines.input_action.add_permission_group, false)
plebs_group.set_allows_action(defines.input_action.admin_action, false)
plebs_group.set_allows_action(defines.input_action.grab_blueprint_record, false)
if not CoreData.blueprint_library_allowed then
plebs_group.set_allows_action(defines.input_action.open_blueprint_library_gui, false)
end
if not CoreData.blueprint_importing_allowed then
plebs_group.set_allows_action(defines.input_action.import_blueprint_string, false)
plebs_group.set_allows_action(defines.input_action.import_blueprint, false)
end
end
end
if not game.permissions.get_group('not_trusted') then
local not_trusted = game.permissions.create_group('not_trusted')
-- not_trusted.set_allows_action(defines.input_action.cancel_craft, false)
not_trusted.set_allows_action(defines.input_action.edit_permission_group, false)
not_trusted.set_allows_action(defines.input_action.import_permissions_string, false)
not_trusted.set_allows_action(defines.input_action.delete_permission_group, false)
not_trusted.set_allows_action(defines.input_action.add_permission_group, false)
not_trusted.set_allows_action(defines.input_action.admin_action, false)
-- not_trusted.set_allows_action(defines.input_action.drop_item, false)
not_trusted.set_allows_action(defines.input_action.disconnect_rolling_stock, false)
not_trusted.set_allows_action(defines.input_action.connect_rolling_stock, false)
not_trusted.set_allows_action(defines.input_action.open_train_gui, false)
not_trusted.set_allows_action(defines.input_action.open_train_station_gui, false)
not_trusted.set_allows_action(defines.input_action.open_trains_gui, false)
not_trusted.set_allows_action(defines.input_action.change_train_stop_station, false)
not_trusted.set_allows_action(defines.input_action.change_train_wait_condition, false)
not_trusted.set_allows_action(defines.input_action.change_train_wait_condition_data, false)
not_trusted.set_allows_action(defines.input_action.drag_train_schedule, false)
not_trusted.set_allows_action(defines.input_action.drag_train_wait_condition, false)
not_trusted.set_allows_action(defines.input_action.go_to_train_station, false)
not_trusted.set_allows_action(defines.input_action.remove_train_station, false)
not_trusted.set_allows_action(defines.input_action.set_trains_limit, false)
not_trusted.set_allows_action(defines.input_action.set_train_stopped, false)
not_trusted.set_allows_action(defines.input_action.grab_blueprint_record, false)
if not CoreData.blueprint_library_allowed then
not_trusted.set_allows_action(defines.input_action.open_blueprint_library_gui, false)
end
if not CoreData.blueprint_importing_allowed then
not_trusted.set_allows_action(defines.input_action.import_blueprint_string, false)
not_trusted.set_allows_action(defines.input_action.import_blueprint, false)
end
end
local group
if group_override then
group = game.permissions.get_group(group_override)
else
if AG.enabled and not player.admin and playtime < 5184000 then -- 24 hours
group = game.permissions.get_group('not_trusted')
else
group = game.permissions.get_group('plebs')
end
end
group.add_player(player)
end
function Public.update_privileges(player)
if not Common.validate_player_and_character(player) then
return
end
if string.sub(player.surface.name, 9, 17) == 'Crowsnest' or string.sub(player.surface.name, 9, 13) == 'Cabin' then
if Public.player_privilege_level(player) >= Public.privilege_levels.OFFICER then
return Public.add_player_to_permission_group(player, 'restricted_area_privileged')
else
return Public.add_player_to_permission_group(player, 'restricted_area')
end
else
return Public.add_player_to_permission_group(player)
end
end
return Public

View File

@ -0,0 +1,333 @@
local Memory = require 'maps.pirates.memory'
-- local Roles = require 'maps.pirates.roles.roles'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
-- local Loot = require 'maps.pirates.loot'
local _inspect = require 'utils.inspect'.inspect
local Upgrades = require 'maps.pirates.boat_upgrades'
-- local Hold = require 'maps.pirates.surfaces.hold'
local Crew = require 'maps.pirates.crew'
local Boats = require 'maps.pirates.structures.boats.boats'
local Dock = require 'maps.pirates.surfaces.dock'
local CustomEvents = require 'maps.pirates.custom_events'
local Public = {}
--== Warning: If something only costs fuel, then we need to check the player can't buy it whilst they're dead
Public.main_shop_data_1 = {
repair_cannons = {
tooltip = 'Repair the cannons.',
what_you_get_sprite_buttons = {['item/artillery-turret'] = false},
base_cost = {coins = 1000},
},
new_boat_cutter = {
tooltip = 'Purchase a cutter.',
what_you_get_sprite_buttons = {['utility/spawn_flag'] = false},
base_cost = {fuel = 3000},
},
new_boat_sloop_with_hold = {
tooltip = 'Purchase a sloop (with hold).',
what_you_get_sprite_buttons = {['utility/spawn_flag'] = false},
base_cost = {fuel = 3500},
},
new_boat_cutter_with_hold = {
tooltip = 'Purchase a cutter (with hold).',
what_you_get_sprite_buttons = {['utility/spawn_flag'] = false},
base_cost = {fuel = 5000},
},
-- buy_iron = {
-- tooltip = 'Purchase 250 iron plates for 300 stored fuel.',
-- what_you_get_sprite_buttons = {['item/iron-plate'] = 250},
-- base_cost = {fuel = 300},
-- },
-- buy_copper = {
-- tooltip = 'Purchase 250 copper plates for 300 stored fuel.',
-- what_you_get_sprite_buttons = {['item/copper-plate'] = 250},
-- base_cost = {fuel = 300},
-- },
-- sell_iron = {
-- tooltip = 'Purchase 200 stored fuel for 2000 iron plates.',
-- what_you_get_sprite_buttons = {['item/sulfur'] = 200},
-- base_cost = {iron_plates = 2000},
-- },
-- sell_copper = {
-- tooltip = 'Purchase 100 stored fuel for 2500 copper plates',
-- what_you_get_sprite_buttons = {['item/sulfur'] = 100},
-- base_cost = {copper_plates = 2500},
-- },
-- as as initial pass let's try making the fuel values half of the old gold values...
[Upgrades.enum.MORE_POWER] = {
tooltip = 'Upgrade the ship\'s power.',
what_you_get_sprite_buttons = {['utility/status_working'] = false},
base_cost = {coins = 5000, fuel = 500},
},
[Upgrades.enum.EXTRA_HOLD] = {
tooltip = 'Upgrade the ship\'s hold.',
what_you_get_sprite_buttons = {['item/steel-chest'] = false},
base_cost = {coins = 10000, fuel = 2000},
},
[Upgrades.enum.UNLOCK_MERCHANTS] = {
tooltip = 'Unlock merchant ships.',
what_you_get_sprite_buttons = {['entity/market'] = false},
base_cost = {coins = 10000, fuel = 2000},
},
[Upgrades.enum.ROCKETS_FOR_SALE] = {
tooltip = 'Unlock rockets for sale at covered-up markets.',
what_you_get_sprite_buttons = {['item/rocket-launcher'] = false},
base_cost = {coins = 15000, fuel = 2000},
},
}
Public.main_shop_data_2 = {
rail_signal = {
tooltip = "100 signals, used to steer the boat one space in the Crow's Nest View.",
what_you_get_sprite_buttons = {['item/rail-signal'] = 100},
base_cost = {coins = 600, fuel = 50},
},
artillery_shell = {
tooltip = '8 cannon shells.',
what_you_get_sprite_buttons = {['item/artillery-shell'] = 8},
base_cost = {coins = 800, fuel = 30},
},
artillery_remote = {
tooltip = 'An artillery targeting remote.',
what_you_get_sprite_buttons = {['item/artillery-targeting-remote'] = 1},
base_cost = {coins = 12000, fuel = 2500},
},
-- buy_fast_loader = {
-- tooltip = 'A fast loader for 500 stored fuel.',
-- what_you_get_sprite_buttons = {['item/fast-loader'] = 1},
-- base_cost = {fuel = 500},
-- },
uranium_ore = {
tooltip = '10 green rocks of unknown origin.',
what_you_get_sprite_buttons = {['item/uranium-238'] = 10},
base_cost = {coins = 1000, fuel = 100},
},
extra_time = {
tooltip = 'Relax at sea for an extra minute. (Increases the next destination\'s loading time.)',
what_you_get_sprite_buttons = {['utility/time_editor_icon'] = 60},
base_cost = {coins = 10, fuel = 1},
},
}
function Public.initialise_captains_shop()
local memory = Memory.get_crew_memory()
memory.mainshop_availability_bools = {
uranium_ore = true,
rail_signal = true,
artillery_shell = true,
artillery_remote = false, --good way to get trolled by crew and remove skill
extra_time = true,
new_boat_sloop_with_hold = false,
new_boat_cutter_with_hold = false,
new_boat_cutter = false,
buy_iron = false,
repair_cannons = false,
-- sell_iron = false,
-- buy_fast_loader = true,
-- sell_copper = false,
}
script.raise_event(CustomEvents.enum['update_crew_fuel_gui'], {})
end
function Public.main_shop_try_purchase(player, purchase_name)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local shop_data_1 = Public.main_shop_data_1
local shop_data_2 = Public.main_shop_data_2
local trade_data = shop_data_1[purchase_name] or shop_data_2[purchase_name]
if not trade_data then return end
local stored_fuel = memory.stored_fuel
if not stored_fuel then return end
-- local captain_index = memory.playerindex_captain
-- if not (stored_fuel and captain_index) then return end
-- local captain = game.players[captain_index]
if not Common.validate_player_and_character(player) then return end
local inv = player.get_inventory(defines.inventory.character_main)
if not (inv and inv.valid) then return end
local multiplier = Balance.main_shop_cost_multiplier()
-- local rate_limit_ok = not (memory.mainshop_rate_limit_ticker and memory.mainshop_rate_limit_ticker > 0)
local rate_limit_ok = true
local enough_fuel = true
local enough_iron_plates = true
local enough_coins = true
local enough_copper_plates = true
local coins_got
local iron_plates_got
local copper_plates_got
-- local able_to_buy_boats = memory.boat.state == Boats.enum_state.DOCKED --disabled for now
local able_to_buy_boats = false
-- local able_to_buy_boats = (memory.boat.state == Boats.enum_state.DOCKED or memory.boat.state == Boats.enum_state.APPROACHING) --problem with this if you buy whilst approaching, the original one no longer moves
for k, v in pairs(trade_data.base_cost) do
if k == 'fuel' then
enough_fuel = (stored_fuel >= v * multiplier)
elseif k == 'coins' then
coins_got = inv.get_item_count('coin')
enough_coins = coins_got >= v * multiplier
elseif k == 'iron_plates' then
iron_plates_got = inv.get_item_count('iron-plate')
enough_iron_plates = iron_plates_got >= v * multiplier
elseif k == 'copper_plates' then
copper_plates_got = inv.get_item_count('copper-plate')
enough_copper_plates = copper_plates_got >= v * multiplier
end
end
local can_buy = rate_limit_ok and enough_coins and enough_fuel and enough_iron_plates and enough_copper_plates
if purchase_name == 'new_boat_sloop_with_hold' or purchase_name == 'new_boat_cutter_with_hold' or purchase_name == 'new_boat_cutter' then can_buy = can_buy and able_to_buy_boats end
-- @TODO: prevent people from buying things whilst marooned
if can_buy then
for k, v in pairs(trade_data.base_cost) do
if k == 'fuel' then
memory.stored_fuel = memory.stored_fuel - v * multiplier
elseif k == 'coins' then
inv.remove{name="coin", count=v * multiplier}
elseif k == 'iron_plates' then
inv.remove{name="iron-plate", count=v * multiplier}
elseif k == 'copper_plates' then
inv.remove{name="copper-plate", count=v * multiplier}
end
end
local force = memory.force
if not (force and force.valid) then return end
local gotamount
if purchase_name == 'uranium_ore' then
gotamount = trade_data.what_you_get_sprite_buttons['item/uranium-238']
Common.give(player, {{name = 'uranium-238', count = gotamount}})
Common.notify_force_light(force,string.format('%s is buying green rocks...', player.name))
elseif purchase_name == 'extra_time' then
local success = Crew.try_add_extra_time_at_sea(60 * 60)
if success then
Common.notify_force_light(force,string.format('%s is buying extra time at sea...', player.name))
else
Common.notify_player_error(player, string.format('Purchase error: Can\'t buy more time than this.', player.name))
-- refund:
memory.stored_fuel = memory.stored_fuel + trade_data.base_cost.fuel * multiplier
end
elseif purchase_name == 'rail_signal' then
gotamount = trade_data.what_you_get_sprite_buttons['item/rail-signal']
Common.give(player, {{name = 'rail-signal', count = gotamount}})
Common.notify_force_light(force,string.format('%s is buying signals...', player.name))
elseif purchase_name == 'artillery_shell' then
gotamount = trade_data.what_you_get_sprite_buttons['item/artillery-shell']
Common.give(player, {{name = 'artillery-shell', count = gotamount}})
Common.notify_force_light(force,string.format('%s is buying cannon shells...', player.name))
elseif purchase_name == 'artillery_remote' then
gotamount = trade_data.what_you_get_sprite_buttons['item/artillery-targeting-remote']
Common.give(player, {{name = 'artillery-targeting-remote', count = gotamount}})
Common.notify_force_light(force,string.format('%s is buying an artillery targeting remote...', player.name))
elseif purchase_name == 'new_boat_cutter' or purchase_name == 'new_boat_cutter_with_hold' or purchase_name == 'new_boat_sloop_with_hold' then
Dock.execute_boat_purchase()
Common.notify_force(force,string.format('[font=heading-1]%s bought a %s.[/font]', player.name, Boats[Common.current_destination().static_params.boat_for_sale_type].Data.display_name))
elseif purchase_name == 'repair_cannons' then
-- heal all cannons:
local cannons = game.surfaces[destination.surface_name].find_entities_filtered({type = 'artillery-turret'})
for _, c in pairs(cannons) do
c.health = c.prototype.max_health
end
Common.notify_force(force,string.format('[font=heading-1]%s repaired the cannons.[/font]', player.name))
memory.mainshop_availability_bools[purchase_name] = false
elseif purchase_name == Upgrades.enum.MORE_POWER then
Upgrades.execute_upgade(Upgrades.enum.MORE_POWER)
Common.notify_force(force,string.format('[font=heading-1]%s upgraded the ship\'s power.[/font]', player.name))
memory.mainshop_availability_bools[purchase_name] = false
elseif purchase_name == Upgrades.enum.EXTRA_HOLD then
Upgrades.execute_upgade(Upgrades.enum.EXTRA_HOLD)
Common.notify_force(force,string.format('[font=heading-1]%s upgraded the ship\'s hold.[/font]', player.name))
memory.mainshop_availability_bools[purchase_name] = false
elseif purchase_name == Upgrades.enum.UNLOCK_MERCHANTS then
Upgrades.execute_upgade(Upgrades.enum.UNLOCK_MERCHANTS)
Common.notify_force(force,string.format('[font=heading-1]%s unlocked merchant ships.[/font]', player.name))
memory.mainshop_availability_bools[purchase_name] = false
elseif purchase_name == Upgrades.enum.ROCKETS_FOR_SALE then
Upgrades.execute_upgade(Upgrades.enum.ROCKETS_FOR_SALE)
Common.notify_force(force,string.format('[font=heading-1]%s unlocked the sale of rockets at covered-up markets.[/font]', player.name))
memory.mainshop_availability_bools[purchase_name] = false
elseif purchase_name == 'sell_iron' then
gotamount = trade_data.what_you_get_sprite_buttons['item/coal']
Common.give(player, {{name = 'fuel', count = gotamount}})
Common.notify_force_light(force,string.format('%s is selling iron...', player.name))
elseif purchase_name == 'buy_iron' then
gotamount = trade_data.what_you_get_sprite_buttons['item/iron-plate']
Common.give_items_to_crew{{name = 'iron-plate', count = gotamount}}
Common.notify_force_light(force,string.format('%s is buying iron...', player.name))
elseif purchase_name == 'buy_copper' then
gotamount = trade_data.what_you_get_sprite_buttons['item/copper-plate']
Common.give_items_to_crew{{name = 'copper-plate', count = gotamount}}
Common.notify_force_light(force,string.format('%s is buying copper...', player.name))
-- elseif name == 'buy_fast_loader' then
-- gotamount = trade_data.what_you_get_sprite_buttons['item/fast-loader']
-- Common.give(player, {{name = 'fast-loader', count = gotamount}})
-- Common.notify_force_light(force,string.format('%s bought a fast loader...', player.name))
elseif purchase_name == 'sell_copper' then
gotamount = trade_data.what_you_get_sprite_buttons['item/coal']
Common.give(player, {{name = 'fuel', count = gotamount}})
Common.notify_force_light(force,string.format('%s is selling copper...', player.name))
end
script.raise_event(CustomEvents.enum['update_crew_fuel_gui'], {})
-- memory.mainshop_rate_limit_ticker = Common.mainshop_rate_limit_ticks
else
-- play sound?
if rate_limit_ok == false then
Common.notify_player_error(player, 'Purchase error: Shop rate limit exceeded.')
end
if enough_fuel == false then
Common.notify_player_error(player, 'Purchase error: Not enough stored fuel.')
end
if enough_coins == false then
Common.notify_player_error(player, 'Purchase error: Not enough doubloons.')
end
if enough_iron_plates == false then
Common.notify_player_error(player, 'Purchase error: Not enough iron plates.')
end
if enough_copper_plates == false then
Common.notify_player_error(player, 'Purchase error: Not enough copper plates.')
end
if (purchase_name == 'new_boat_cutter' or purchase_name == 'new_boat_sloop_with_hold' or purchase_name == 'new_boat_cutter_with_hold') and (not able_to_buy_boats) then
Common.notify_player_error(player, 'Purchase error: Not able to purchase ships right now.')
end
end
end
return Public

View File

@ -0,0 +1,99 @@
local Memory = require 'maps.pirates.memory'
-- local Roles = require 'maps.pirates.roles.roles'
-- local Classes = require 'maps.pirates.roles.classes'
-- local Crew = require 'maps.pirates.crew'
-- local Boats = require 'maps.pirates.structures.boats.boats'
-- local Dock = require 'maps.pirates.surfaces.dock'
-- local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local Utils = require 'maps.pirates.utils_local'
local Math = require 'maps.pirates.math'
local _inspect = require 'utils.inspect'.inspect
-- local Upgrades = require 'maps.pirates.boat_upgrades'
local Public = {}
local enum = {
TIME = 'Time',
}
Public.enum = enum
Public.offers_loaders = {
{price = {{'coin', 1500}}, offer = {type = 'give-item', item = 'loader', count = 1}},
{price = {{'coin', 2500}}, offer = {type = 'give-item', item = 'fast-loader', count = 1}},
{price = {{'coin', 3500}}, offer = {type = 'give-item', item = 'express-loader', count = 1}},
}
Public.offers_rockets = {
{price = {{'coin', 200}, {'electronic-circuit', 80}}, offer = {type = 'give-item', item = 'rocket-launcher', count = 1}},
{price = {{'coin', 1000}, {'explosives', 20}, {'electronic-circuit', 20}}, offer = {type = 'give-item', item = 'rocket', count = 20}},
}
Public.offers_default = {
{price = {{'coin', 600}}, offer = {type = 'give-item', item = 'copper-plate', count = 150}},
{price = {{'coin', 600}}, offer = {type = 'give-item', item = 'iron-plate', count = 150}},
{price = {{'coin', 450}}, offer = {type = 'give-item', item = 'piercing-rounds-magazine', count = 14}},
{price = {{'coin', 700}}, offer = {type = 'give-item', item = 'heavy-armor', count = 1}},
{price = {{'coin', 400}}, offer = {type = 'give-item', item = 'grenade', count = 10}},
{price = {{'coin', 300}}, offer = {type = 'give-item', item = 'defender-capsule', count = 3}},
{price = {{'coin', 400}}, offer = {type = 'give-item', item = 'distractor-capsule', count = 3}},
{price = {{'coin', 500}}, offer = {type = 'give-item', item = 'slowdown-capsule', count = 5}},
{price = {{'coin', 500}}, offer = {type = 'give-item', item = 'poison-capsule', count = 5}},
{price = {{'coin', 500}}, offer = {type = 'give-item', item = 'gate', count = 10}},
{price = {{'coin', 100}}, offer = {type = 'give-item', item = 'storage-tank', count = 4}},
{price = {{'coin', 200}}, offer = {type = 'give-item', item = 'big-electric-pole', count = 8}},
{price = {{'coin', 200}}, offer = {type = 'give-item', item = 'steel-furnace', count = 4}},
{price = {{'coin', 300}}, offer = {type = 'give-item', item = 'stack-inserter', count = 3}},
{price = {{'coin', 750}}, offer = {type = 'give-item', item = 'piercing-shotgun-shell', count = 9}},
{price = {{'coin', 800}}, offer = {type = 'give-item', item = 'flamethrower', count = 1}},
{price = {{'coin', 1500}}, offer = {type = 'give-item', item = 'flamethrower-ammo', count = 4}},
{price = {{'coin', 1500}}, offer = {type = 'give-item', item = 'flying-robot-frame', count = 1}},
}
function Public.market_generate_coin_offers(how_many)
local memory = Memory.get_crew_memory()
local ret = {}
local offerscopy = Utils.deepcopy(Public.offers_default)
local loaderoffers = Public.offers_loaders
local game_completion_progress = Common.game_completion_progress()
if game_completion_progress < 0.2 then
ret[#ret + 1] = loaderoffers[1]
elseif game_completion_progress < 0.6 then
ret[#ret + 1] = loaderoffers[2]
else
ret[#ret + 1] = loaderoffers[3]
end
local toaddcount = how_many
while toaddcount>0 and #offerscopy > 0 do
local index = Math.random(#offerscopy)
local toadd = offerscopy[index]
ret[#ret + 1] = toadd
for i = index, #offerscopy - 1 do
offerscopy[i] = offerscopy[i+1]
end
offerscopy[#offerscopy] = nil
toaddcount = toaddcount - 1
end
if memory.rockets_for_sale then
for _, o in ipairs(Public.offers_rockets) do
ret[#ret + 1] = o
end
end
return ret
end
return Public

190
maps/pirates/shop/dock.lua Normal file
View File

@ -0,0 +1,190 @@
-- local Memory = require 'maps.pirates.memory'
-- local Roles = require 'maps.pirates.roles.roles'
local CoreData = require 'maps.pirates.coredata'
local Classes = require 'maps.pirates.roles.classes'
-- local Crew = require 'maps.pirates.crew'
-- local Boats = require 'maps.pirates.structures.boats.boats'
-- local Dock = require 'maps.pirates.surfaces.dock'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local Utils = require 'maps.pirates.utils_local'
local Math = require 'maps.pirates.math'
local _inspect = require 'utils.inspect'.inspect
-- local Upgrades = require 'maps.pirates.boat_upgrades'
local Public = {}
Public.market_barters = {
{price = {{'iron-plate', 300}}, offer = {type = 'give-item', item = 'copper-plate', count = 500}},
{price = {{'copper-plate', 300}}, offer = {type = 'give-item', item = 'iron-plate', count = 500}},
--repeating these:
{price = {{'iron-plate', 300}}, offer = {type = 'give-item', item = 'copper-plate', count = 500}},
{price = {{'copper-plate', 300}}, offer = {type = 'give-item', item = 'iron-plate', count = 500}},
{price = {{'steel-plate', 40}}, offer = {type = 'give-item', item = 'copper-plate', count = 500}},
{price = {{'steel-plate', 40}}, offer = {type = 'give-item', item = 'iron-plate', count = 500}},
{price = {{'raw-fish', 50}}, offer = {type = 'give-item', item = 'coal', count = 500}},
{price = {{'raw-fish', 50}}, offer = {type = 'give-item', item = 'iron-plate', count = 750}},
{price = {{'raw-fish', 50}}, offer = {type = 'give-item', item = 'copper-plate', count = 750}},
{price = {{'raw-fish', 50}}, offer = {type = 'give-item', item = 'steel-plate', count = 125}},
{price = {{'wood', 200}}, offer = {type = 'give-item', item = 'coin', count = 250}},
--TODO: add more complex trades
}
Public.market_permanent_offers = {
{price = {{'coin', 2400}}, offer = {type = 'give-item', item = 'iron-ore', count = 800}},
{price = {{'coin', 2400}}, offer = {type = 'give-item', item = 'copper-ore', count = 800}},
{price = {{'coin', 3000}}, offer = {type = 'give-item', item = 'crude-oil-barrel', count = 100}},
{price = {{'coin', 2500}}, offer = {type = 'give-item', item = 'fast-loader', count = 1}},
{price = {{'coin', 5000}}, offer = {type = 'give-item', item = 'beacon', count = 2}},
{price = {{'coin', 2800}}, offer = {type = 'give-item', item = 'speed-module-2', count = 2}},
}
-- cheap but one-off
Public.market_sales = {
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'coal', count = 900}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'piercing-rounds-magazine', count = 75}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'uranium-rounds-magazine', count = 30}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'piercing-shotgun-shell', count = 50}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'raw-fish', count = 300}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'laser-turret', count = 1}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'vehicle-machine-gun', count = 3}},
{price = {{'coin', 3000}}, offer = {type = 'give-item', item = 'modular-armor', count = 1}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'distractor-capsule', count = 20}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'poison-capsule', count = 20}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'slowdown-capsule', count = 20}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'coin', count = 4000}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'roboport', count = 1}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'construction-robot', count = 10}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'logistic-chest-passive-provider', count = 2}},
{price = {{'coin', 2000}}, offer = {type = 'give-item', item = 'logistic-robot', count = 2}},
}
-- function Public.dock_generate_offers(how_many_barters, how_many_sales)
-- local ret = {}
-- local toaddcount
-- local barterscopy = Utils.deepcopy(Public.market_barters)
-- toaddcount = how_many_barters
-- while toaddcount>0 and #barterscopy > 0 do
-- local index = Math.random(#barterscopy)
-- local toadd = barterscopy[index]
-- ret[#ret + 1] = toadd
-- for i = index, #barterscopy - 1 do
-- barterscopy[i] = barterscopy[i+1]
-- end
-- barterscopy[#barterscopy] = nil
-- toaddcount = toaddcount - 1
-- end
-- for _, offer in pairs(Public.market_permanent_offers) do
-- ret[#ret + 1] = offer
-- end
-- local salescopy = Utils.deepcopy(Public.market_sales)
-- toaddcount = how_many_sales
-- while toaddcount>0 and #salescopy > 0 do
-- local index = Math.random(#salescopy)
-- local toadd = salescopy[index]
-- ret[#ret + 1] = toadd
-- for i = index, #salescopy - 1 do
-- salescopy[i] = salescopy[i+1]
-- end
-- salescopy[#salescopy] = nil
-- toaddcount = toaddcount - 1
-- end
-- return ret
-- end
function Public.create_dock_markets(surface, p)
-- local memory = Memory.get_crew_memory()
if not (surface and p) then return end
local e
e = surface.create_entity{name = 'market', position = {x = p.x - 7, y = p.y}}
if e and e.valid then
e.minable = false
e.rotatable = false
e.destructible = false
for _, offer in pairs(Public.market_permanent_offers) do
e.add_market_item(offer)
end
end
e = surface.create_entity{name = 'market', position = {x = p.x, y = p.y - 1}}
if e and e.valid then
e.minable = false
e.rotatable = false
e.destructible = false
local toaddcount
local salescopy = Utils.deepcopy(Public.market_sales)
toaddcount = 3
while toaddcount>0 and #salescopy > 0 do
local index = Math.random(#salescopy)
local offer = salescopy[index]
e.add_market_item(offer)
for i = index, #salescopy - 1 do
salescopy[i] = salescopy[i+1]
end
salescopy[#salescopy] = nil
toaddcount = toaddcount - 1
end
-- new class offerings:
local destination = Common.current_destination()
if destination.static_params.class_for_sale then
e.add_market_item{price={{'coin', Balance.class_cost()}}, offer={type="nothing"}}
destination.dynamic_data.market_class_offer_rendering = rendering.draw_text{
text = 'Class available: ' .. Classes.display_form[destination.static_params.class_for_sale],
surface = surface,
target = Utils.psum{e.position, {x = 0, y = -4}},
color = CoreData.colors.renderingtext_green,
scale = 2.5,
font = 'default-game',
alignment = 'center'
}
end
end
e = surface.create_entity{name = 'market', position = {x = p.x + 7, y = p.y}}
if e and e.valid then
e.minable = false
e.rotatable = false
e.destructible = false
local toaddcount
local barterscopy = Utils.deepcopy(Public.market_barters)
toaddcount = 2
while toaddcount>0 and #barterscopy>0 do
local index = Math.random(#barterscopy)
local offer = barterscopy[index]
e.add_market_item(offer)
for i = index, #barterscopy - 1 do
barterscopy[i] = barterscopy[i+1]
end
barterscopy[#barterscopy] = nil
toaddcount = toaddcount - 1
end
end
end
return Public

View File

@ -0,0 +1,60 @@
-- local Memory = require 'maps.pirates.memory'
-- local Roles = require 'maps.pirates.roles.roles'
-- local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
-- local Loot = require 'maps.pirates.loot'
local _inspect = require 'utils.inspect'.inspect
-- local Hold = require 'maps.pirates.surfaces.hold'
local Public = {}
function Public.generate_merchant_trades(market)
-- local memory = Memory.get_crew_memory()
if market and market.valid then
local game_completion_progress = Common.game_completion_progress()
if game_completion_progress <= 1 then
market.add_market_item{price = {{'coin', 8000}, {'raw-fish', 100}}, offer = {type = 'give-item', item = 'modular-armor', count = 1}}
market.add_market_item{price = {{'coin', 5000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'battery-equipment', count = 1}}
market.add_market_item{price = {{'coin', 2000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'solar-panel-equipment', count = 5}}
market.add_market_item{price = {{'coin', 1000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'night-vision-equipment', count = 1}}
market.add_market_item{price = {{'coin', 2000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'energy-shield-equipment', count = 1}}
market.add_market_item{price = {{'coin', 1000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'personal-roboport-equipment', count = 1}}
else
market.add_market_item{price = {{'coin', 8000}, {'raw-fish', 100}}, offer = {type = 'give-item', item = 'modular-armor', count = 1}}
market.add_market_item{price = {{'coin', 12000}, {'raw-fish', 100}}, offer = {type = 'give-item', item = 'power-armor', count = 1}}
market.add_market_item{price = {{'coin', 5000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'battery-equipment', count = 1}}
market.add_market_item{price = {{'coin', 2000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'energy-shield-equipment', count = 1}}
market.add_market_item{price = {{'coin', 1000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'personal-roboport-equipment', count = 1}}
market.add_market_item{price = {{'coin', 8000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'battery-mk2-equipment', count = 1}}
market.add_market_item{price = {{'coin', 2000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'solar-panel-equipment', count = 5}}
market.add_market_item{price = {{'coin', 6000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'fusion-reactor-equipment', count = 1}}
market.add_market_item{price = {{'coin', 1000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'night-vision-equipment', count = 1}}
market.add_market_item{price = {{'coin', 5000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'energy-shield-mk2-equipment', count = 1}}
market.add_market_item{price = {{'coin', 4000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'personal-roboport-mk2-equipment', count = 1}}
market.add_market_item{price = {{'coin', 8000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'exoskeleton-equipment', count = 1}}
market.add_market_item{price = {{'coin', 10000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'personal-laser-defense', count = 1}}
-- else
-- market.add_market_item{price = {{'coin', 18000}, {'raw-fish', 100}}, offer = {type = 'give-item', item = 'power-armor', count = 1}}
-- market.add_market_item{price = {{'coin', 24000}, {'raw-fish', 100}}, offer = {type = 'give-item', item = 'power-armor-mk2', count = 1}}
-- market.add_market_item{price = {{'coin', 10000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'battery-mk2-equipment', count = 1}}
-- market.add_market_item{price = {{'coin', 400}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'solar-panel-equipment', count = 1}}
-- market.add_market_item{price = {{'coin', 6000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'fusion-reactor-equipment', count = 1}}
-- market.add_market_item{price = {{'coin', 1000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'night-vision-equipment', count = 1}}
-- market.add_market_item{price = {{'coin', 5000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'energy-shield-mk2-equipment', count = 1}}
-- market.add_market_item{price = {{'coin', 4000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'personal-roboport-mk2-equipment', count = 1}}
-- market.add_market_item{price = {{'coin', 8000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'exoskeleton-equipment', count = 1}}
-- market.add_market_item{price = {{'coin', 10000}, {'raw-fish', 10}}, offer = {type = 'give-item', item = 'personal-laser-defense', count = 1}}
end
end
end
return Public

201
maps/pirates/shop/shop.lua Normal file
View File

@ -0,0 +1,201 @@
local Memory = require 'maps.pirates.memory'
-- local Roles = require 'maps.pirates.roles.roles'
local Classes = require 'maps.pirates.roles.classes'
-- local Crew = require 'maps.pirates.crew'
-- local Boats = require 'maps.pirates.structures.boats.boats'
-- local Dock = require 'maps.pirates.surfaces.dock'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local Utils = require 'maps.pirates.utils_local'
local Math = require 'maps.pirates.math'
local _inspect = require 'utils.inspect'.inspect
local SurfacesCommon = require 'maps.pirates.surfaces.common'
-- local Upgrades = require 'maps.pirates.boat_upgrades'
local Public = {}
Public.Captains = require 'maps.pirates.shop.captains'
Public.Covered = require 'maps.pirates.shop.covered'
Public.Merchants = require 'maps.pirates.shop.merchants'
Public.Minimarket = require 'maps.pirates.shop.dock'
function Public.event_on_market_item_purchased(event)
local player_index, market, offer_index, trade_count = event.player_index, event.market, event.offer_index, event.count
local player = game.players[player_index]
if not (market and market.valid and offer_index and Common.validate_player(player)) then return end
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local alloffers = market.get_market_items()
local this_offer = alloffers[offer_index]
local price = this_offer.price
local offer_type = this_offer.offer.type
local offer_giveitem_name, offer_giveitem_count
if offer_type == 'give-item' then
offer_giveitem_name = this_offer.offer.item
offer_giveitem_count = this_offer.offer.count
end
local inv = player.get_inventory(defines.inventory.character_main)
-- check for BARTER vs STATIC vs ONE-OFF
-- One-off becomes unavailable after purchase, such as class purchase
-- Static doesn't decay
-- Barter decays
local decay_type
local dock_bool = destination.type == SurfacesCommon.enum.DOCK
local island_bool = destination.type == SurfacesCommon.enum.ISLAND
local purchase_bool = (price and price[1] and price[1].name and (price[1].name == 'coin'))
local simple_efficiency_trade_bool = (price and price[1] and price[1].name and (price[1].name == 'pistol' or price[1].name == 'burner-mining-drill'))
local special_purchase_bool = (offer_giveitem_name == 'rocket-launcher')
-- local special_purchase_bool = (offer_giveitem_name and (offer_giveitem_name == 'loader' or offer_giveitem_name == 'fast-loader' or offer_giveitem_name == 'express-loader' or offer_giveitem_name == 'rocket-launcher'))
if offer_type == 'nothing' then
decay_type = 'one-off'
elseif dock_bool and purchase_bool and (offer_giveitem_name) and (offer_giveitem_name == 'stone' or offer_giveitem_name == 'iron-ore' or offer_giveitem_name == 'copper-ore' or offer_giveitem_name == 'crude-oil-barrel') then
decay_type = 'fast_decay'
elseif dock_bool and purchase_bool and (offer_giveitem_name) then
decay_type = 'one-off'
elseif simple_efficiency_trade_bool or special_purchase_bool then
decay_type = 'static'
elseif island_bool and (not (offer_giveitem_name and offer_giveitem_name == 'rocket')) then
decay_type = 'one-off'
else
decay_type = 'decay'
end
-- For everything but static, we want to disallow multi-purchases in this game, so refund any additional purchases:
if decay_type ~= 'static' and player and trade_count and trade_count > 1 then
inv = player.get_inventory(defines.inventory.character_main)
if not inv then return end
for _, p in pairs(price) do
inv.insert{name = p.name, count = p.amount * (trade_count - 1)}
end
if offer_type == 'give-item' then
inv.remove{name = offer_giveitem_name, count = offer_giveitem_count * (trade_count - 1)}
end
end
if decay_type == 'one-off' then
local force = player.force
if offer_type == 'nothing' and destination.static_params.class_for_sale then
local class_for_sale = destination.static_params.class_for_sale
-- if not class_for_sale then return end
local required_class = Classes.class_purchase_requirement[class_for_sale]
local ok = true
-- check if they have the required class to buy it
if required_class then
if not (memory.classes_table and memory.classes_table[player.index] and memory.classes_table[player.index] == required_class) then
ok = false
Common.notify_force_error(force, string.format('Class purchase error: You need to be a %s to buy this.', Classes.display_form[required_class]))
end
end
if ok then
if required_class then
if force and force.valid then
Common.notify_force_light(force,string.format('%s upgraded their class from %s to %s. ([font=scenario-message-dialog]%s[/font])', player.name, Classes.display_form[required_class], Classes.display_form[class_for_sale], Classes.explanation[class_for_sale]))
end
else
-- check if they have a role already - renounce it if so
if memory.classes_table and memory.classes_table[player.index] then
Classes.try_renounce_class(player, false)
end
if force and force.valid then
Common.notify_force_light(force,string.format('%s bought the class %s. ([font=scenario-message-dialog]%s[/font])', player.name, Classes.display_form[class_for_sale], Classes.explanation[class_for_sale]))
end
end
memory.classes_table[player.index] = class_for_sale
memory.available_classes_pool = Utils.ordered_table_with_single_value_removed(memory.available_classes_pool, class_for_sale)
if destination.dynamic_data and destination.dynamic_data.market_class_offer_rendering then
rendering.destroy(destination.dynamic_data.market_class_offer_rendering)
end
market.remove_market_item(offer_index)
if Classes.class_unlocks[class_for_sale] then
for _, upgrade in pairs(Classes.class_unlocks[class_for_sale]) do
memory.available_classes_pool[#memory.available_classes_pool + 1] = upgrade
end
end
else
--refund
inv = player.get_inventory(defines.inventory.character_main)
if not inv then return end
for _, p in pairs(price) do
inv.insert{name = p.name, count = p.amount}
end
end
else
Common.notify_force_light(player.force, player.name .. ' bought ' .. this_offer.offer.count .. ' ' .. this_offer.offer.item .. ' for ' .. price[1].amount .. ' ' .. price[1].name .. '.')
market.remove_market_item(offer_index)
end
else
-- print:
if (price and price[1]) then
if not (price[1].name and price[1].name == 'burner-mining-drill') then --this one is too boring to announce
if price[2] then
local fish = price[2].name
if fish == 'raw-fish' then fish = 'fish' end
Common.notify_force_light(player.force, player.name .. ' traded away ' .. price[1].amount .. ' ' .. price[1].name .. ' and ' .. fish .. ' for ' .. this_offer.offer.count .. ' ' .. this_offer.offer.item .. '.')
else
if price[1].name == 'coin' then
Common.notify_force_light(player.force, player.name .. ' bought ' ..this_offer.offer.count .. ' ' .. this_offer.offer.item .. ' for ' .. price[1].amount .. ' ' .. price[1].name .. '.')
elseif this_offer.offer.item == 'coin' then
local sold_amount = price[1].amount
if sold_amount == 1 then sold_amount = 'a' end
Common.notify_force_light(player.force, player.name .. ' sold ' .. sold_amount .. ' ' .. price[1].name .. ' for ' .. this_offer.offer.count .. ' ' .. this_offer.offer.item .. '.')
else
Common.notify_force_light(player.force, player.name .. ' traded away ' .. price[1].amount .. ' ' .. price[1].name .. ' for ' .. this_offer.offer.count .. ' ' .. this_offer.offer.item .. '.')
end
end
end
end
if decay_type == 'static' then
if not inv then return end
local flying_text_color = {r = 255, g = 255, b = 255}
local text1 = '[color=1,1,1]+' .. this_offer.offer.count .. '[/color] [item=' .. alloffers[offer_index].offer.item .. ']'
local text2 = '[color=' .. flying_text_color.r .. ',' .. flying_text_color.g .. ',' .. flying_text_color.b .. '](' .. inv.get_item_count(alloffers[offer_index].offer.item) .. ')[/color]'
Common.flying_text(player.surface, player.position, text1 .. ' [font=count-font]' .. text2 .. '[/font]')
else
local decay_param = Balance.barter_decay_parameter()
if decay_type == 'fast_decay' then decay_param = Balance.barter_decay_parameter()^3 end
if not inv then return end
local flying_text_color = {r = 255, g = 255, b = 255}
local text1 = '[color=1,1,1]+' .. this_offer.offer.count .. '[/color] [item=' .. alloffers[offer_index].offer.item .. ']'
local text2 = '[color=' .. flying_text_color.r .. ',' .. flying_text_color.g .. ',' .. flying_text_color.b .. '](' .. inv.get_item_count(this_offer.offer.item) .. ')[/color]'
Common.flying_text(player.surface, player.position, text1 .. ' [font=count-font]' .. text2 .. '[/font]')
--update market trades:
alloffers[offer_index].offer.count = Math.max(Math.floor(alloffers[offer_index].offer.count * decay_param),1)
market.clear_market_items()
for _, offer in pairs(alloffers) do
market.add_market_item(offer)
end
end
end
end
return Public

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
local Public = {}
Public.display_name = 'merchant_1'
Public.tile_areas = {
{{-30,-6},{-5,5}},
{{-5,-5},{-4,4}},
{{-4,-4},{-3,3}},
{{-3,-3},{-2,2}},
{{-2,-2},{-1,1}},
{{-1,-1},{0,0}},
}
Public.width = 30
Public.height = 11
Public.entities = {
static = {
pos = { x = -17, y = -0.5},
bp_str = [[0eNqdmd1uozAUhN/F16TCNrYhr7KqVvmxskgEIkK2G1V594W0aquGwWd6iQpfx0dzju3Jq9o2l3jq63ZQ61d1bjen1dCtDn29n57/qbVxmbqqtda3TNW7rj2r9a/xxfrQbprpleF6imqt6iEeVabazXF6eum6fWxXuz/xPKjpw3YfR5a+PWcqtkM91PGNc3+4/m4vx23sxxfmCZk6defxo659F6Wf3lU9udste8CYD8y2PqxiE3dDX+9Wp66JjyxzJ9lRZRvrw59td+knbVVmTOb98wzeClXmyyoLEmPmMU6IsctqvBBjljFBiHHLmFKIKZYx1QfmfNw0TcoK7kulv9nBZLqc84LOhUrDslIttb5PcIyQUyU4UpOXCY7U5TrR01rqc51oOy11uk50jJZ6XSd6RpecTT94Mz6dxtasTyt21WDcGKnhdaLFjXjYJ5rcUONeu/l5PzV4Fuxc7cxnL5yHGBsoNCRKJ+6FxLAw4l5ITAvjv6ysa+PqZbTfAgYtLAgLVCU4pZBTJjhisyemoBWbPTEGrdTsJjEGrXS+m8QYtNIBbxJj0EpdbRJj0EpdbRIDwXryZGUBJ5CcAnBKdvYiQZWsXd2yniKXYYoERnPDA2EMhwG1KT79vOmHumlif10Nl76PC4dYf99A93U/bhn3v/o5MntQd0ChIzkecDx55ER6AslBekryyIn0VCQH6HE5eXRFHE1ywLqcEe5qLsGxJAetqxByioQeR3KQHi/k+IQedlwHwClJTgk4FdlfQI/Pyb5AHNbPiGO4XQiUx1tuF0IY1s1oVY7bhpAcz2dPOn+8jPgKXEY86/IKCGVdPt2rZ0E/DFnuvG/rHqsX8rlVh/yHV+TZ/4JqGzR7UAPFDYbOGEB1g/3B5fbRUGP9xtrORwOhIK+3UCvZQqh45KUUyglsmowyTbpVEKhis1oUj+ZsWotAms1rEciwiS0CWTZQRaCCTVQRyLGRKgJ5NlNFoECHqohU0qkqItEBIyJVOZ2rIpKms0pEMnRYiUiWDgERqaBTQERydF6GSJ4OzBAp0IkZIpV0ZIZIFZ2Zocg/z+nUDKI0nZvdUc/Z26+26y+/AWfqb+zP909MqYtQmVBYX1SmvN3+A/EBuBE=]],
},}
Public.market_pos = {x = -8.5, y = 0.5}
Public.landingtrack = {
offset = {x = -24, y = -10.5},
bp = [[0eNqV3M1u20YYRuF74VoB8g7J+dGtFFm4tpAKcCTDVn+CwPdeu2aLblrwWQo4psg5M5vz0fox/fz46+np+Xy5Tccf08vl7unT7frp6/P54f3zH9NxLofp+3QseT1M5/vr5WU6/vQGnr9e7h7fkdv3p9N0nM6307fpMF3uvr1/+v16fThdPt3/cnq5Te9/eHk4vV0rr18O0+38ePq4yNP15Xw7Xy/bN3180efXf67y9m33z6fb6e0K/wFH4CLwLPBC91yJbkR3ogct304zs2icReMsGmfROIvGmTTOpHEmjTNpnEnjIhoX0biIxkU0LqJxIY0LaVxI40IaF9K4isZVNK6icRWNq2hcSeNKGlfSuJLGlTRW0VhFYxWNVTRW0VhJYyWNlTRW0lhJYxONTTQ20dhEYxONjTQ20thIYyONjTR20dhFYxeNXTR20dhJYyeNnTR20thJ4xCNQzQO0ThE4xCNgzQO0jhI4yCNgzTms3jc6BBdiJ6JXuy+q+HN8G74sFXcqygkNCQ0JDQkNCQ0JjQmNCY0JjQmlNJOqO2E4k6o7oTyTqzvxAJPrPDEEk+s8YQiT6jyhDJPqPOEQk+s9MRST6z1xGJPrPaEck+o94SCT6j4hJJPrPnEok+s+sSyT6z7hMJPqPyE0k+o/YTiT6z+xPJPrP/EAlCsAIUSUKgBhSJQqAKFMlCsA8VCUKwExVJQrAWFYlCoBoVyUKgHhYJQrAjFklCsCcWiUKwKhbJQqAuFwlCoDIXS0Eav9pSIV8Ob4d3wYYr2+qf6FMpPof4UClChArXRK9HV1sRuJXYvwZtphnfDh/nf+y4FBbRCAa1QQCsU0AoFtI1eia5EN1tBe8zYc8YeNPakwUfthg/bXHt3LnXFQl2xUFcs1BULdcWNXomuRDeiu623LWFsDWOLGFvF2DLG1jG4kMN27t5jYa/p2Xt69qKevalnr+oVOkSFDlGhQ1ToEH3Qw+yYnpifmKCYoZiimKOYpJilmKbdJ5TifKE4XyjOF4rzheL8Rq9EV6Ib0Z3oYXZQptmM6Yz5jAmNGY0pjTmNSY1Z3X2gadxSaNxSaNxSaNyy0SvRlehGdCd6mB2UaTZjOmM+Y0JjRmNKY05jUncfUZpxFZpxFZpxbfRKdCW6Ed2JtvXefYpWO0WrnaLVTtFqp2i1U2QzzmIzzmIzzkKzv0Kzv41eia5EN6JtTQbRuzd6tY1ebaNX2+jVNnq1jW6z32Kz30Jjy41eia5E2313ogfRu/dis73YbC8224vN9mKzvWhj60JD0Y22azeiO9GD6N3bpdt26bZdum2XbtvF5taFBpcb3YjuRA+idxsdZnSY0WFGbfw704huozvRg+i9i/43HsOL4Xv/UZpGOhs9iN69LrF1sfHSTCF9o3ffunX3+f8i4JfDx28vHP/1Sw6H6bfT88tfFyg9SxulLXNdRumvr38Cd8bxwA==]],
}
return Public

View File

@ -0,0 +1,9 @@
-- local Data = require 'maps.pirates.structures.boats.merchant_1.data'
local Public = {}
Public.Data = require 'maps.pirates.structures.boats.merchant_1.data'
return Public

View File

@ -0,0 +1,36 @@
local Public = {}
Public.display_name = 'raft'
Public.capacity = 11
Public.tile_areas = {
{{-9,-4},{0,5}},
}
Public.width = 9
Public.height = 9
Public.spawn_point = {x = -3, y = 0}
Public.areas_infront = {
{{0,-4},{1,5}},
}
Public.areas_behind = {
{{-10,-4},{-9,5}},
}
Public.areas_offright = {
{{-9,5},{0,6}},
}
Public.areas_offleft = {
{{-9,-5},{0,-4}},
}
Public.entities = {
inaccessible = { --this 'left wall' stops biters from being deleted by water
pos = { x = -8.5, y = 0},
bp_str = [[0eNqV08sOwiAQBdB/mTU1ltIXv2KMqXZSSdqhKfhoDP9uQRcmsmF5k7mHWQwvOI83nBdFFuQLDHVzZnU2LKr3+QkyZ7CCbB0DddFkQB62MTVQN/oBu84IEpTFCRhQN/n00LpHyi5XNBZ8kXr0kjsyQLLKKvw4Iawnuk1nXMJTMYHBrM1W0vRdab8rw1L5rnSO/TE8kdnHmSKRKeKMSGR4nCkTmTLOVImMiDN1IlPHmSaRqeJMm8g0ntluMVyt/PkBDO64mFDgTS7qlteiqETLG+fekGkHZg==]],
},
}
Public.landingtrack = {
offset = {x = 3.5, y = -7},
bp = [[0eNqV2M1qwkAYheF7mXWE+eZ/civFhdVBBjQJSSwVyb3XVBddtMV3JYHjBJ85m+NNvZ8uZRhrN6v2pqZuN2zmfnMc62F9/lStmEZd7x9+aVTd992k2rd7sB673WmNzNehqFbVuZxVo7rdeX265/ZjmYtav9QdynrOsm3UXE/lccDQT3Wuffd8y+b5Gr38dsafcWFxA0+neQvz7tW8MBxhOMJwBOIIxBGIoxmOZjia4WiIoyGOZjgZ2WREk5FMZjCZuWTGkhBLQiwJsSTGkhhLYiwRsUTEEhFLZCyRsUTGEhBLQCwBsQTGEhhLYCwesXjE4hGLZyyesXjG4hCLQywOsTjG4hiLYywWsVjEYhHLI21Rmv1Kj9IBpSNKJ5TO7HbgZbLbFHadwu7z5dqyWcJWCRslBtXWoNoaVFuDamtQbQ2qrUG1Nay2htUWbkY4GeliZJuITSK2iFBtBdVWUG0F1VZQbQXVVlBthdVWWG3pmod79Z/abpvHv0btj/+gGvVRxun7AJPExWyiDc5pa5blC6gg570=]],
}
return Public

View File

@ -0,0 +1,9 @@
-- local Data = require 'maps.pirates.structures.boats.raft.data'
local Public = {}
Public.Data = require 'maps.pirates.structures.boats.raft.data'
return Public

View File

@ -0,0 +1,36 @@
local Public = {}
Public.display_name = 'raft_large'
Public.capacity = 38
Public.tile_areas = {
{{-18,-4},{0,5}},
}
Public.width = 18
Public.height = 9
Public.spawn_point = {x = -6, y = 0}
Public.areas_infront = {
{{0,-4},{1,5}},
}
Public.areas_behind = {
{{-19,-4},{-18,5}},
}
Public.areas_offright = {
{{-18,5},{0,6}},
}
Public.areas_offleft = {
{{-18,-5},{0,-4}},
}
Public.entities = {
inaccessible = { --this 'left wall' stops biters from being deleted by water
pos = { x = -17.5, y = 0},
bp_str = [[0eNqV08sOwiAQBdB/mTU1ltIXv2KMqXZSSdqhKfhoDP9uQRcmsmF5k7mHWQwvOI83nBdFFuQLDHVzZnU2LKr3+QkyZ7CCbB0DddFkQB62MTVQN/oBu84IEpTFCRhQN/n00LpHyi5XNBZ8kXr0kjsyQLLKKvw4Iawnuk1nXMJTMYHBrM1W0vRdab8rw1L5rnSO/TE8kdnHmSKRKeKMSGR4nCkTmTLOVImMiDN1IlPHmSaRqeJMm8g0ntluMVyt/PkBDO64mFDgTS7qlteiqETLG+fekGkHZg==]],
},
}
Public.landingtrack = {
offset = {x = 4.5, y = -7},
bp = [[0eNqV2M1qwkAYheF7mXWE+eZ/civFhdVBBjQJSSwVyb3XVBddtMV3JYHjBJ85m+NNvZ8uZRhrN6v2pqZuN2zmfnMc62F9/lStmEZd7x9+aVTd992k2rd7sB673WmNzNehqFbVuZxVo7rdeX265/ZjmYtav9QdynrOsm3UXE/lccDQT3Wuffd8y+b5Gr38dsafcWFxA0+neQvz7tW8MBxhOMJwBOIIxBGIoxmOZjia4WiIoyGOZjgZ2WREk5FMZjCZuWTGkhBLQiwJsSTGkhhLYiwRsUTEEhFLZCyRsUTGEhBLQCwBsQTGEhhLYCwesXjE4hGLZyyesXjG4hCLQywOsTjG4hiLYywWsVjEYhHLI21Rmv1Kj9IBpSNKJ5TO7HbgZbLbFHadwu7z5dqyWcJWCRslBtXWoNoaVFuDamtQbQ2qrUG1Nay2htUWbkY4GeliZJuITSK2iFBtBdVWUG0F1VZQbQXVVlBthdVWWG3pmod79Z/abpvHv0btj/+gGvVRxun7AJPExWyiDc5pa5blC6gg570=]],
}
return Public

View File

@ -0,0 +1,9 @@
-- local Data = require 'maps.pirates.structures.boats.raft_large.data'
local Public = {}
Public.Data = require 'maps.pirates.structures.boats.raft_large.data'
return Public

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
-- local Data = require 'maps.pirates.structures.boats.sloop.data'
local Public = {}
Public.Data = require 'maps.pirates.structures.boats.sloop.data'
return Public

View File

@ -0,0 +1,19 @@
-- local Memory = require 'maps.pirates.memory'
-- local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
local Public = {}
local enum = {
MATTISSO = 'MATTISSO',
ROC = 'ROC',
}
Public[enum.MATTISSO] = require 'maps.pirates.structures.island_structures.mattisso.mattisso'
Public[enum.ROC] = require 'maps.pirates.structures.island_structures.roc.roc'
Public.enum = enum
return Public

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,43 @@
local Data = require 'maps.pirates.structures.island_structures.mattisso.data'
local Public = {}
Public.refuel_station = {}
Public.refuel_station.Data = Data.refuel_station
Public.small_crashed_ship = {}
Public.small_crashed_ship.Data = Data.small_crashed_ship
Public.small_basic_factory = {}
Public.small_basic_factory.Data = Data.small_basic_factory
Public.small_early_enemy_outpost = {}
Public.small_early_enemy_outpost.Data = Data.small_early_enemy_outpost
Public.small_nuclear_powerplant = {}
Public.small_nuclear_powerplant.Data = Data.small_nuclear_powerplant
Public.small_market_outpost = {}
Public.small_market_outpost.Data = Data.small_market_outpost
-- second lot:
Public.small_abandoned_refinery = {}
Public.small_abandoned_refinery.Data = Data.small_abandoned_refinery
Public.small_roboport_base = {}
Public.small_roboport_base.Data = Data.small_roboport_base
Public.small_solar_base = {}
Public.small_solar_base.Data = Data.small_solar_base
Public.small_mining_base = {}
Public.small_mining_base.Data = Data.small_mining_base
Public.small_primitive_mining_base = {}
Public.small_primitive_mining_base.Data = Data.small_primitive_mining_base
Public.small_oilrig_base = {}
Public.small_oilrig_base.Data = Data.small_oilrig_base
-- third lot:
Public.small_radioactive_lab = {}
Public.small_radioactive_lab.Data = Data.small_radioactive_lab
Public.small_radioactive_centrifuge = {}
Public.small_radioactive_centrifuge.Data = Data.small_radioactive_centrifuge
Public.small_radioactive_reactor = {}
Public.small_radioactive_reactor.Data = Data.small_radioactive_reactor
Public.uranium_miners = {}
Public.uranium_miners.Data = Data.uranium_miners
return Public

View File

@ -0,0 +1,355 @@
local Public = {}
local CoreData = require 'maps.pirates.coredata'
Public.shelter1 = {
name = 'shelter1',
width = 18,
height = 18,
components = {
{
type = 'static',
force = 'environment',
offset = {x = 0, y = 0},
bp_string = [[0eNqV1t1qhDAQBeB3metYzL/6KqUUtxuWgEbRbFtZfPca24tCOyRztSskHzlh4OQBl+Hu5sWHCN0D1tDPVZyq2+Kv6fsTOm4YbOlnZ+DfprBC93ws9LfQD2lJ3GYHHfjoRmAQ+jF9rXEKrvrohwHStnB1SdpfGLgQffTuWzk/ttdwHy9uORb8t5/BPK3Hlin8HKiyT/o8Uvqz7+wPI8oYk2FkGaMyjCpjdIbRZYzMMKaIySm2SMlFaoqU3P22RUpuZnhdxORmhlNHWCOOIDoGcSTtdjBG0RgslSamkohjiI5CHEuLhTENjcFStbRUyGlETWOQ0whOCoUpgqRgkSQtEjLFQtEYZIqFJmXCFENSsEiW1nVYYza0m8GYllaZWPHWtMrEGE6rTIwRpMrEFEmqTExRpMrEFNoAY4ohNeapHO+/853Y/Xp1Mnh3y3puEA1XthVWWqt5rfb9C+jHZns=]],
},
{
type = 'tiles',
tile_name = CoreData.world_concrete_tile,
offset = {x = 0, y = 0},
bp_string = [[0eNqV2t1qGkEYBuB7mWOF+Z/UWyk5sMkSFswquv0JwXtvTErpSUufoyCMa9CHj+H93tfw5fB1Op3nZQ2713BZ9qftetw+nefH2+sfYZf6Jrzc/lw3YX44Lpew+/x2cH5a9ofbkfXlNIVdmNfpOWzCsn++vbqsx2Xaft8fDuH2tuVxuj3per8J63yYPh5xOl7mdT4uvz5ne/f+Odu76++nvH3aw3lap7dn/PX4sOPdjjc7Xu14sePZjic6Hum0Pdv+cftW7Cu339Ow/K/EYc6HOR/mfJjzYc6HOR/mfJjzQc4HOR/kfJDzQc4HOR/kfJDzbs67Oe/mvJvzbs67Oe/mvJvzTs47Oe/kvJPzTs47Oe/kvJPzZs6bOW/mvJnzZs6bOW/mvJnzRs4bOW/kvJHzRs4bOW/kvJHzas6rOa/mvJrzas6rOa/mvJrzSs4rOa/kvJLzSs4rOa/kvJLzYs6LOS/mvJjzYs6LOS/mvJjzQs4LOS/kvJDzQs4LOS/kvJDzbM6zOc/mPJvzbM6zOc/mPJvzTM4zOc/kPJPzTM4zOc/kPJPzZM6TOU/mPJnzZM6TOU/mPJnzRM4TOU/kPJHzRM4TOU/kPInzSMwjKY+EPJLxSMQjCY8EPJLvKLyj6I6CO4rtKLSjyI4CO4prG982vW142+y20W2T2wa3zW0a2zS1aWjTzKaRTRObBjbNa7t+2+3bLt9297art9287eJt9266dtOtmy7ddOemKzfduOnCTfdti08sPbHwxLITi04sObHgxHITik0oNaHQhDITikwoMaHAhPISi78t/bbw27Jvi74t+bbg23Jvir0p9abQmzJvirwp8abAm/JuW1/a9tKWl7a7tNWlbS5tcWl7S1pb0taSlpa0s6SVJW0saWFJ+0qrn1j7xMon1j2x6ok1T6x4Yr0Tqp1Q64RKJ9Q5ocoJNU6ocEJ9E6sPWnvQyoPWHbTqoDUHrThovUGqDVJrkEqD1BmkyiA1Bqkw+I++4P3mozG++6N/vgnfpvPl/e35LtXxKY8yRkuxXq8/AdUS1bk=]],
},
},
}
Public.shelter2 = {
name = 'shelter2',
width = 44,
height = 44,
components = {
{
type = 'tiles',
tile_name = 'red-desert-2',
offset = {x = 0, y = 0},
bp_string = [[0eNqd3dFu4zYahuF78XEC+JVkSZxbWfRg2hqFgUwSJOnuFkXufZPW07Mu/PSomIL0aERJPwjye/j74ceHX8/PL5fHt8OX3w+vj1+f79+e7n95ufz8+ef/Hr4s093ht8//vN8dLj89Pb4evvzro+Hll8evD59N3n57Ph++HC5v52+Hu8Pj12+ff3p9e3o83//n68PD4bPb48/nj1/q/Ye7w9vl4fznTzw/vV7eLk+P17/nfuqPv+j+9P7Xz3z8dT+9nN/OHz/y9+0XbD9j+wnbZ+2P1hx/HS8e783Nt/74563Zsf2G7Vdsf8L2+u+dsf2E7bP2R2uOv44Xj/cGbz2OLD44tz6Xjes4HbHDwPY7ttd/wIrtT9h+wfYztp90wKw9Di/+Ol483hu89Tiy+ODgc4mP/c1v1X59CibtkHY4Ygf9J+zYfsP2K7Y/YfsF28/YnkfY2uPw4q/jxeO9wVuPI4sPDj6X+NjjW6Vv7e2fhe36lM3aYdIOfElH7DCw/Y7tN2y/YvsTtl+wvQ4xj7C1x+HFX8eLx3uDtx5HFh8cfC7xsce3St9a/Szc/t1Zr0/loh1m7cCXlHY4YoeB7Xdsv2H7FdufsL2OsQ4xj7C1x+HFX8eLx3uDtx5HFh8cfC7xsce3St9a/Szod+f2D9vp+hSftMOiHfiSJu2Qdjhih4Htd2y/YfsV2+sg6xjrEPMIW3scXvx1vHi8N3jrcWTxwcHnEh97fKv0rdXPgn539MN2+5dzuT71q3Y4aQe+pFk7TNoh7XDEDgPb79h+w/Y6yjrIOsY6xDzC1h6HF38dLx7vDd56HFl8cPC5xMce3yp9a/WzoN8d/bDpl/P2T/N8fUs27bBqB76kRTvM2mHSDmmHI3YY2H7H9jrMOso6yDrGOsQ8wtYehxd/HS8e7w3eehxZfHDwucTHHt8qfWv1s6DfHf2w6ZdTP823f/un61u1a4dNO/AlnbTDoh1m7TBph7TDETsMbK/jrMOso6yDrGOsQ8wjbO1xePHX8eLx3uCtx5HFBwefS3zs8a3St1Y/C/rd0Q+bfjn106zf/tuLS1q+0vKVlq+0fKXlKy1faflKy1davsLyFZavsHyF5QtjJGGMJIyRhDGSMEaSxUiyGEkWI8liJFmM5NocRxYfHHwu8bHHt0rfWv0s6HdHP2z65dRPs377by8u3xM2Qzvs2oEvadUOJ+2waIdZO0zaIe1wxA460DrOOsw6yjrIOsY6xDzC1h6HF38dLx7vDd56HFl8cPC5xMce3yp9a/WzoN8d/bDpl1M/zfrt1+Jyc/UaWB4HVseBxXFgbRxYGgdWxoGFcWBdHFgWNYiKOVSMoWIKFUOomEHFCComUDGAivlTi59a+tTCp5Y9teipJU8teGq5U4udWup0WAUcVgCH1b9h5W9Y9RtW/IbVvmGlb1jl+x4cPVr7myvljpVyx0q5Y6XcsVLuWCl3rJSazdZotiazMZiNuWyMZWMqG0PZmMnGSDYmsjGQbXlsi2NbGtvC2JbFtii2JbEtiG05bIthYwobQ9i7VcrdKuVulXK3SrlbpdytUu7/pFLeXPg2LKwbFtYNC+uGhXXDwrphYd2wsKoloJSASgIKCaAjgIwAKgKICKAhgIQACgIICKAfYHyA6QGGB5gdYHSAyQEGB5gbYGyAqQGIBqAZgGTAZoV1s8K6WWHdrLBuVlg3K6ybFdYVC+uKhXXFwrpiYV2xsK5YWJWmUJlCYQp1KZSlQJUCUQo0KZCkQJECQQr0KJCjQI3CMAqzKIyiMInCIApzKIyhMIXCEAozKJCgQIECAQr0J1YrrKsV1tUK62qFdbXCulph/U4khO2P1l6v5+Z/7gkL8QkLscIl6pYoW6JqiaIlapYgWYJiCYIl6JUgV4JaCWIlaJUgVWJSiUEl5pQYU2JKiSElZpQYUWJCiQEl6JMgT4I6CeIkaJOcrBCfrBCfrBCfrBCf/kkhvrmuLli3F6zbC9btBev2gnVbkRo1apSoUaFGgRr1aZSnQZ0GcRq0aZCmQZkGYRp0aZClQZXGUBozaYykMZHGQBrzaIyjMY3GMBqzaJCiQYkGIRp0aJChQYVmsbq9WN1erG4vVrcXq9sz1u0Z6/aMdXvGuq1ekHJBqgUpFqRWkFJBKgUpFIROEDJBqAQhEoRGEBJBKAQhEIQ+kPFApgMZDmQ2kNFAJgMZDGQukLFApgIhCoQmEJJAKAIhCIQeEHJAs9Xt2er2bHV7tro9Yd2esG5PWLdVVlJYSV0lZZVUVVJUSU0lJZVUVEJQCT0l5JRQU0JMCS0lpJRQUkJIyRwlY5RMUTJEyQwlI5RMUDJAyfwk45NQT0I8Ce0kpJNQTkI4Cd0kZJMmq9uT1e3J6nZYt8O6nRobSmyosKHAhvoaymuorqG4htoaSmugrIGwBroayGqgqoGoBpoaSGqgqGGghnkaxmmYpmGYhlkaRmmYpGGQhjkayGigooGIBhoaSGigoIGABvoZVrezuh3V7aOV7aNVbYSxkMVCFAtJLASxkMNCDAspLISwkMEyBMsILAOwjL8y/MroK4OvjL0y9IrIKwKviLsi7IqoK4KuiLki5IqIKwKujLcy3MpoK4OtjLUy1MpIKwOtjLM6Ujk+UjXGSTTOoXEKjTNonEDj/Bmnzzh7xskzzp1x6owzZ5s427zZps02a7ZJs82ZbcpsM2abMNN8mabLNFumyTLNlWmqTDNlmijTPJmmyTZLtkmyzZFtimwzZJsg2/zYpsc2O7bJsc2NcSkaV6JxIRrXoXEZGlehcREa16BxCRpXoHEBGtefbfnZVp9t8dnWnm3p2VaebeHZ1p1t2ZlWnWnRmdacacmZVpxpwZnWm2m5mVababHZ1pptqdlWmm2h2daZbZnZVpltkdnWmG2J2VaYcUM37ufG7dy4mxs3c+NebtzKjTu5cSM37uPGbdy4i9s2cdsebtvCbTu4bQO37d+27du2e9s2b9Pebdq6TTu3aeM27dumbdu0a5s2bdOebdqybTu2bcO27de27dq2W9s2a9tebduqbTu1baO27dPGWDSmojEUjZlojERjIhoD0ZiHxjg0pqExDI1ZaItCWxLagtCWg7YYtKWgLQRtGWiLQFMCmgLQlH+m+DOlnyn8TNlnij5T8pmCz5Z7ttizpZ4t9GyZZ4s8W+LZAs+Wd7a4s6WdEQtDKwypMJTCEApDJwyZMFTCEAlDIwyJMBPCDAgzH8x4MNPBDAczG8xoMJPBCAYjF4xYMFLBCAUjE4xIMBLBCAQjD8w4MNPADAMzC8woMJPADAIzB8wYMFPAkLlG5RqRazSukbhG4RqBa/StkbdG3Rpxa7OtjbY22dpga3OtjbU21dpQazOtibQm0ZpAa/KsibMmzZowa7KsibImydoga3OsjbE2xdoQazOsjbA2wdoAa/Or8cAlPG8Jj1vC05bwsCU8awmPWsKTlvCgJTxnCY9ZslOW7JAlO2PJjliyE5bsgCU7X8mOV7LTlehwJTpbiY5WopOV6GAlOleJjlWiU5XoUCU6U8mOVLITlexAJTtPyY5TstOU7DAlO0vJjlKyk5TwKF88yRcP8sVzfPEYXzzFFw/xxTN88QhfPMHXDvC183vt+F47vdcO77Wze+3oXju51w7upXN76dheOrWXDu2lM3vpyF46sZcO7KXzeum4Xjut1w7rtbN67aheO6nXDuq1c3rtmF47pXdYTRtW04bVtGE1bVhNG1bThtW0YTVtWE0bVtMG1bRBNW1QTRtU0wbVtEE1bVBNG1TTBtW0ITVtSE0bUtOG1LQhNW1ITRtS04bUtCE1bUhNG1TTBtW0QTVtUE0bVNMG1bRBNW1QTRsWIEV0KFSHQnYodIdCeCiUh0J6KLSHQnwo04cyfijzhzKAKBOIMoIoM4gyhChTiCKGKHKIIogokogiiiiyiCKMKNKIIo4o8ogykCgTiTKSKDOJMpQoU4kylihziVKBRwkeNXgU4VGFRxkedXgU4lGJByketHgQ40GNBzke9HgQ5EGRB0keM3kM5TGVx1gec3kM5jGZx2ges3kM50GdB3ke9HkQ6EGhB4keNHoQ6UFfJgRmQmEmJGZCYyZEZkJlJmRmMmcmg2YyaSajZjJrJsNmMm0m42YybyYCZyJxJiJnInMmQmcidSZiZyJ3JoJnInkmo2cyeybDZzJ9JuNnMn8mA2hC3iT0TULgJBROQuIkNE5C5CRTTjLmJHNOMugkk04y6iSzTjLsJNNOIu4k8k4i8CQSTyLyJDJPIvQkUk8i9iRyTzL4JJNPMvoks08y/CTTT0L+IvQvQgAjFDBCAiM0MDIEI1MwMgYjczAyCCOTMDIKI7MwMgwj0jAiDiPyMCIQIxIxIhIjMjEiFCNSMSIWI3MxMhgjkzEyGiOzMULuIPQOQvAgFA9C8iAzDzL0IFMPMvYgcw8y+CCTDzL6ILMPIvwg0g8i/iDyDyIAIRIQIgIhMhAiBCFSEDIGIXMQMgghkxDCtHoYVw/z6mFgPUusZ5H1LLOehdaz1HoWW89y61lwPUuuR9H1KLsehdej9HoUX4/y61GAPUqwRxH2KMOehdizFHsWYw/TwGEcOMwDZ4HgLBGcRYKzTHAWCs5SwVksOMsFZ8HgKBkcRYOjbHAUDo7SwVE8OMoHRwHhKCEcRYSzjHAWEg7jkFkeMgtEZonILBKZZSKzUGSWisxikVkuMgpGRsnIKBoZZSOjcGSUjozikVE+MgpIRgnJLICVJbCyCFaWwcpCWFkKK4thZTmsKIgVJbGiKFaUxYrCWFEaK4pjRXmsybISk2UlJstKTJaVmCwrMVFWYqKsxERZiYmyEtP/yUr8cHe4vJ2/ffy/Hx9+PT+/XB7fDneHf59fXv/oP+0t25i2edtOHZf39/8BhLbmiQ==]]
},
{
type = 'tiles',
tile_name = CoreData.world_concrete_tile,
offset = {x = 0, y = 0},
bp_string = [[0eNqd3c1uG0cQhdF3mTUJVDX7b/QqgReyNTEIUJRA0UkMQ+8eypGzCxCdlWHYTYKl4adC9b23fiyfT9+258vxfF3ufiwv5/vn/fVp//VyfHj7+1/LXS275fvbH6+75fjl6fyy3P12+4/Hr+f709t/uX5/3pa75XjdHpfdcr5/fPvby/XpvO3/vD+dlrdj54ft9kr5+mm3XI+n7Z+XeH56OV6PT+f399mX/PlG+8Prvy9z2X4/nreH/e1tv1y263Z7sf8+V/Bc2rmwY/huH/5wgcUMLGZgMcOKGVbMoGLmasX8dU7fL+1c2DF8tw9/uInFnFjMicWcVsxpxZxWzIHFHFjMgcUcVsxhxRxWzI7F7FjMjsXsVsxuxexWzIbFbFjMhsVsVsxmxWxWzIrFrFjMisWsVsxqxaxWzAMW84DFPGAxD1bMgxXzYMUs7x+OD6YeDDy44rmJ5wae63iu4bmK5w54jp8YO4ePC74bfjisJf7o8EnBBxO/B/i1w2+5UuXjGEsFZyo4U8GZCM5EcCaCMxGcieBMBCcOuhIHXYmDrrRBV9qgK23Q9X4Ma4k/OnxS8MHE7wF+7fBbrlT5OMZCwRkKzlBwBoIzEJyB4AwEZyA4A8GJQ+3EoXbiUDttqJ021E4caoeBMwycYeAMA2cYOMPAGQbOQHCGgXNFbq6IzRWpuRo0V2PmashcjZirAXM1XuK1Fd5a4aWV3VnZlZXdWK0EypU4uRImV6LkSpBciZErIXI1Qq4GyImAnAjIiYCcBshpgJwGyGmAnAbIaYDEq2i8icaLaLuHtmtou4WeBMhJgJwEyEmAnATISYCcBMhpgJwGyIGAHAjIgYAcBshhgBwGyGGAHAbIYYBEeQmqS1BcYtoSk5aYsmQQIAcBchAgBwFyECAHAXIQIIcBchggOwKyIyA7ArIbILsBshsguwGyGyC7ARIlY6gYQ8GY6cVMLmZqsU6A7ATIToDsBMhOgOwEyE6A7AbIboBsCMiGgGwIyGaAbAbIZoBsBshmgGwGSJSBogoURaCmATUJqClAGwGyESAbAbIRIBsBshEgGwGyGSCbAbIiICsCsiIgqwGyGiCrAbIaIKsBshogUdqNym4Udpuu22TdpuquBMhKgKwEyEqArATISoCsBMhqgKwGyF8ifD0Xdi5XPDfx3MBzHc81PFfx3AHPFTyHz0vi84KPCz4t+LDgs4KPCj4p+KDoc0LH7CGx97IPZlW0H5k9H/Yw2pNvXzP7TiNBEFjIR8Qx0h9/2eDvNvxVir+5sVH4cF9SsA8q2AcV7IMK9kEF+6CCfVDBPqhgH1SwD1K/pdot1W2JZkv0WqLVEp2WaLREnyXaLNFliSZL81iaxdIclmawNH+l2SvNXWnmSvNWmrUSnZVorCzWBxXrg4r1QcX6oGJ9ULE+qFgfVKwPKtYHJfZBiX1QYh+U2Acl9kGJfVBiH5TYByX2QWyfVhOgegDRAogOQDQAov8P7X/o/kPzH3r/0Ppnzj8z/pnvz2x/5voz0595/szyZ44/M/yh3w990tYHpfVBaX1QWh+U1gel9UFpfVBaH5TUB4W1QWFdUFgTFNYDhbVAYR1QWAMU1v+EtT+YgYAJCJh/YOkHln1gyQeWe2CpB5Z5YIkHlndgaQeUdUBJB5RzQCkHlHFACQeUb0DpBpRtQMkGlmtgqQZBXU5QkxPU4wS1OEEdTlCDE9TfBLU3Qd0NDnlwxoMjHpzw4IAH5zs43sHpDg53cLaDox2c7Nhgx+Y6NtaxqY4NdWymYyMdm+jYQIfmOTTOoWkODXNolkOjHJrk0CCH5jg0xrEpjg1xbIZjIxyb4NgAx+Y3Nr6x6Y0Nb2x2g1IeVPKgkAd1PCjjQRUPinhQw4MSHlTwoIAH9Tsm3zH1jol3TLtj0h1T7phwx3Q7Jtsh1Q6JdkizQ5IdUuyQYIf0OiTXIbUOiXVMq2NSHVPqmFDHdDom0zGVjol0TKNjEh1T6KBxB307aNsx146ZdsyzY5Ydc+yYYcf8OmbXMbcOmXXIq0NWHXLqkFGHfDpk0yGXDpl0yKNjFh1z6KC1H539aOw3X7/Z+s3Vb6Z+8/Sbpd8c/WboNz8/2fnJzU9mfvLyk5WfnPxk5CcfP9n4ycVvJn7z8GMIFGZAYQSUJUBZAJTlP1n8k6U/WfiTZT9Z9JMlP1HwE+U+UewTpT5R6BNlPlHkEyU+UeAT5T1Z3JOlPWFcKKaFYlioZYVaVKglhVpQqOWEWkyopYRaSKhlhFJEKCWEUkAo5YNSPCilg1I4KGWDUjQoJYNaMKjlgmKwPObKY6y8pcpbqLxlylukvCXKW6C85clbnLylyVOYPGXJU5Q8JclTkDzlyFOMPKXIU4g8ZchbhLwlyOMKItxAhAuIbP+QrR+y7UO2fMh2D9nqIds8ZIuHbO8QrR2irUO0dIh2DtHKIdo4RAuHaN8QrRuibUO2bMh2DeGyStxViasqbVOlLaq0PZW2ptK2VNqSSttRaSsqbUMlLaik/ZS0npK2U9JyStpNSaspaTMlLaakvZS2ltK2Uuq6c912rsvOcdc5rjrHTee46Bz3nOOac9xyjkvOcce5rTi3Dee24Nz2m9t6c9tubsvNbbe5rTa3zea42Bz3mqe6BdUuqH5BNAyiYxAtg+gZRNMgugbRNoi+QTQOmnPQrIPmHTTzoLkHzT5o/kEzEJqD0CyE6CFEE6H5UNKMKGlOlCQrSpIXJcmMkqZvThM4pymckyTOSRrnJJFzmjoyTR6Zpo9MEkgmKSSTJJJp2qo0cVWauipJXpWkr0oSWKUpM9KkGWnajCRxRpI6I0mekXavm3axm3azm3S1m3S3m3S5m3YrlHYtlHYvlHQxlHQzlHQ1lDZTThsqp02Vk8bKSXPlpMFysTlUsTlUsTlUoTlUoTlU+R9zqE+75XjdHm//9vn0bXu+HM/XZbf8sV1efr5OmVnHWsZhjJZRX1//Bgu6MZo=]],
},
{
type = 'static',
force = 'environment',
offset = {x = 0, y = 0},
bp_string = [[0eNqVmtFq20AQRf9ln+Wg1e5qtf6VEorTiGJwZBM7bUPwv9dOSlqoLzPnKQisw155dD2ZO2/hYfcyH563yyms38Jx2RxWp/3q+/P28Xr9K6zz0IXX659zF7bf9ssxrL9cPrj9vmx214+cXg9zWIftaX4KXVg2T9er42m/zKufm90uXG9bHucLKZ7vuzAvp+1pO39Q3i9evy4vTw/z8+UDt+7vwmF/vNyyX/4caDXclfcjrYb+rpzP3X+cAXJiu81JLo6JyQyjVBWqarrNGSmn3uZU+HQEZoIYoapRVeNtTuwpqAhQhMIUZ4AcJSxRYVmAMgUlASpQmeKMkKOE+Sp6FftPUhSkyUmKJslZ15MFGpx13UyQ06VHE+S06WqCnJWdTZCzsosJwl49CBA2a3UiZ20nE0T9WnEa5IgnlHx1bepKvrI2v/rkq2qzFpOvqM2XI/lq2nxbk6+kTftIvoo2/Sz5Ctp22OQraNv0U8OmLzq93FOS6jwjfEiKM9CHrZQlqky0ezlTkGg/c4HSFGeEHCWsUmGi38u4BxENaG5QmeCUHnKEsBKpMNHvlYGCRANaElSmOBlylDBv+/HpaeLHtTjbjzhZIG9VNwvkrerRAnnduhqg0WvW2QJ567pYIG9dJwvkdevBAmG3ViDq1orjdGvzCTm7EPMBOZsQ86t3erVVi9Xp1dbLUZ0NiPW2VmcDYvlQdTq1ZUPVOdOzDLZSp1YcatSKQ31acSbororToN0LztRDb1WcCM1ecahFKw51aMWhBq040J8VZmS2qjCVubzCTMxUFaYxjxeY1jNLVZjIHF5hoDErDPRlhYG2rDAFvg2in28j5Ih+vsFZh8JMDKNU0TmHCjZ6OuaQkQ0cc0gOHHNIYXTKURWI2vKkQNCXJQdOOaQwOuVoCkSnHGoyFXs45pCgCOccSlqkcw6ZIcUBBlualFiKpEGZ5VoaVFiKpEEjy7U0qLIUSYMmlmtpEAzHJWiA4bgKbaI3REzmiVg6rg/Edj30eTKKozSnoBhJc0YUa2lORTGS5kwo1tKchmIkyXHGh6afOeND22ET7EE0CO57yO2KBPc95HpFYvsemsP2PbSwCoWpfs8ZIf4FyRUdFopLjjNANIVluJGndrxihit5assrZujTksOW8rQwuOmhdgRjhpseakswZraXpzls0eND2H33sQi6/mettAs/5ufj+x3DdDleG2qqtVz+uTmffwPQ9nqN]],
},
},
}
Public.lonely_storage_tank = {
name = 'lonely_storage_tank',
width = 4,
height = 6,
components = {
{
type = 'static_destructible',
force = 'ancient-friendly',
offset = {x = 0, y = 0},
bp_string = [[0eNqFkVsKwjAQRfcy34m0aWs1WxGRqEMJ2klIUrFI9m5axQdW/QoD9xxuZi6wPXZonaYA8gKelOXB8Mbp/TCfQRYMepDzyEDvDHmQqxTTDanjEAi9RZCgA7bAgFQ7TD4YpxrkQdEBBpD2mEx5XDNACjpovHnGod9Q127RpcDDYLvWJp81PoUN3avwfFaNbbiIkX3gYrrApya7a9I75SmeNXT63fcaYpov//HZb776w7/haaXj8uXLIRmc0PkREIu8rJeiLuq6yrMyxiu0uZ7v]],
},
},
}
Public.swamp_lonely_storage_tank = {
name = 'swamp_lonely_storage_tank',
width = 3,
height = 3,
components = {
{
type = 'static_destructible',
force = 'ancient-friendly',
offset = {x = 0, y = 0},
bp_string = [[0eNptj80KwjAQhN9lz4nYH9uaVxGR1C4l2G5KshVLybubRA8ePC0DM9/O7NBPKy7OEIPawZNeJFs5OjMk/QJVCdhANUGAuVvyoC7RZkbSUzLwtiAoMIwzCCA9J+XZOj2iZE0PSEEaMJKKcBWAxIYNfjhZbDda5x5dNPwnCFisjyFL30ryeDjlVvGGBM3v1c8UAU90PifKrqjbc9nWVVOfyy6EN3scTtM=]],
},
},
}
Public.covered1 = {
name = 'covered1',
width = 17,
height = 15,
components = {
{
type = 'tiles',
tile_name = 'orange-refined-concrete',
offset = {x = -9, y = -8},
bp_string = [[0eNqVmMGKwjAURf8l6wreJG2S/srgwtEgAW1L7Qwj0n8fqy5m4YBnJcKxHrxc7sOr+Tx+5WEs3WTaqzl322E19avDWPbL+x/TKlXmcnsJc2XKru/Opv24geXQbY8LMl2GbFpTpnwylem2p+XdjduNecpm+VC3z8tz5k1lpnLMjwcM/blMpe+e37K+f8l6fvWEf2AR2BLYEdgTuCZwQ+BA4EjghEJhEaIMhUIUSlEoRqEc9WaQIh0Q6YBIB0Q6INIBkQ6IdECkAyIdEOmAUAeEOiDUAaEOCHVAqANCHbCkA5Z0wCJri6wdsXbE2iFrh6w9sfbE2iNrj6xrYl0T6xpZ18i6IdYNsW6QdYOsA7EOxDog64CsI7GOxDoi64isE7FOxDoh68RuFnS4C13uYseW4LXFzi12b8GJZBspNJJCKyk2k2I7KTSUQkspNpViWyk0lkJrKTaXYnspNJhCiyk2mWKbKTSaQqspNptiuyk0nELL+aQtoh2iPaJrRLNfMCA6IjqxdGCYLE2xOMXyFAtULNG3S4HuMqHD7ElbRDtEe0TXiGa/YEB0RHRi6cAwWZpicYrlKRaoWKKvS7GpHv+Tt3/+da/Mdx7P9wfYKB+SDd41Ptk4z7+pVIVy]],
},
{
type = 'tiles',
tile_name = 'green-refined-concrete',
offset = {x = -1, y = 2},
bp_string = [[0eNqV0tsKwjAMBuB3yXWF9bCDfRXxYm5lFLa2tFUco+9uO70QVDCXgS9/CMkGl/mqnNcmgtwgmN4doj1MXo+lvoNsCawg60RAD9YEkKfM9GT6uYC4OgUSdFQLEDD9UqrsBq+igtJkRpVTaDoTiHpWzwBng47amteMap8h0reED0wxmGEw33GFwRSDGQZzDP5zQYHBNQY3v3E+/P4e8u3VCNyUD3s766hoj6wVjPOublJ6AP6T06c=]],
},
{
type = 'tiles',
tile_name = 'out-of-map',
offset = {x = -7, y = -6},
bp_string = [[0eNqd2s1Kw0AUhuF7mXWEfnPO/OVWxEXVIIGaljaKIrl3G3XhQqWvqxA4nUDOs3qbt3C7exoOx3GaQ/8WTtP2cDXvrx6O4/16/xJ6pS68ni+2dGG820+n0F+fB8eHabtbR+bXwxD6MM7DY+jCtH1c785zd8dhHsL6o+l+WM9Zbrowj7vh84DD/jTO4376esrm4yGb5acTfhkWGY5k2Miwk+FEhjMZLmS4kuGGlsJWiHaoC5coYknEkoglEUsilkQsiVgSsSRiScSSkCUhS0KWIrEUiaVILEViKRJLkViKxFIkliKxFImliCxFZCkiS0YsGbFkxJIRS0YsGbFkxJIRS0YsGbFkyJIhS4YsObHkxJITS04sObHkxJITS04sObHkxJIjS44sObKUiKVELCViKRFLiVhKxFIilhKxlIilRCwlZCkhSwlZysRSJpYysZSJpUwsZWIpE0uZWMrEUiaWMrKUkaVCdBSioxAdhegoREchOgrRUYiOQnQUoqMgHQXpqERHJToq0VGJjkp0VKKjEh2V6KhERyU6KtJRkY5GdDSioxEdjehoREcjOhrR0VDVQSlWqMUKxVihGiuUY4V6rFCQ1eYf77ui6ca2A5d56TZZaGWllaVW1lpZbGW1leVW1ltZcGXFFSZX2FxRRhXqqEIhVaikCqVUoZYqFFOFaqpQThXqqWJBVayoCkVSoUoqlEmFOqlQKBUqpUKpVKiVCsVSoVoqlktl/5Fy8Z+CqJgKJVOhZioUTYWqqVA2FeqmQuFUqJwKpVOxdioWT/VXPb3pPr906L99N9GF5+F4+jggVnlpsbhlb7Euyzuk5qn3]],
},
{
type = 'static',
force = 'environment',
offset = {x = 0, y = 0},
bp_string = [[0eNqlmNtuozAQht/F11Ax+IR5lVW1Io0VIRGDwOw2qnj3hbKHSGVghr2KUODz2Hwe+PkQl2b0XV+HKMoPMYSqS2Ob3vr6uhy/ixJsIh7zj54SUb+1YRDlt/nE+haqZjklPjovSlFHfxeJCNV9ORpiG3z6s2oasVwWrn4hTa+J8CHWsfYr5fPg8T2M94vv5xO2rk9E1w7zJW34XVD2oteKXvQ0JV8gOQuSbUMkCQL7EEWC5PsQTYLIfYghQdQ+xJIgeh9SkCBmH+JIELsPgYxEKQ4oNGfdAeWftP696/0wpOO8Z/pb386/6cU3ccO+Z4eTP/uwHWM3RrE1CFHqA6uBpjUceA00seHAbKCpDQduA01uOLAbaHqD2W9h4HgYpJo8Y7VCjVCAVwyG4fVli1Aki2IQiuJNCStG8zBYNYY1J4dQLItSIJTifxqRe25EdUD6UM60G5mwzHgYZMaS+dKBbFnJ0xuw9w75Zf2btppvwM6qA1C6v2Q6j06UKT06U571y+NkE2NPCPsXKj+Fvda9f1v/zo/1lWe2iCWMuHPvHNkKd35iKqO7lyPDGMIwcKbFSHQBDWEBVc6UFrFNyTO1G0Lt+HIp3j5RSOWalWhQjGFlGhRjWakGxRSsXINiHCvZYBidsbINigFWukExOSveoBjJyjcohvj0OdJYa16GQTmGF2JQjuWlGJRT8GIMynG8HINxDPPlauW8JutnoPLpo1Iifvh+WJ9BBSjrcqukUS4vpukXJhD2Zw==]],
},
},
}
Public.covered1.red_chest = {x = 2, y = 5}
Public.covered1.blue_chest = {x = 2, y = 6}
Public.covered1.walls = {
{x = -8, y = -5},
{x = -8, y = -4},
{x = -8, y = -3},
{x = 8, y = -5},
{x = 8, y = -4},
{x = 8, y = -3},
}
Public.covered1b = {
name = 'covered1b',
width = 17,
height = 15,
components = {
{
type = 'tiles',
tile_name = 'orange-refined-concrete',
offset = {x = -7, y = -6},
bp_string = [[0eNqVmM1qg0AURt9l1gby3ev/q5QsbDMUwaiobROC797YlpJFaXtWInwzzmHO4rtew2P3Esep7ZdQX8PcN+NuGXbPU3vc3s+hVpaEy+3haxLap6GfQ/1wC7bPfdNtkeUyxlCHdomnkIS+OW1v8zL0cffWdF3YlvXHuO20Jn8ujOdxivO864bmGKe7xbYekrC0Xfz8/jjM7dIO/dch9x9n3K/f+9wO+jTFJYbtmz+GRcJGwk7CKQlnJJyTcEHCJQlX6FLYFaI71D8vUcQlEZdEXBJxScQlEZdEXBJxScQlEZeEXBJyScglIy4ZccmIS0ZcMuKSEZeMuGTEJSMuGXHJkEuGXDLkkhOXnLjkxCUnLjlxyYlLTlxy4pITl5y45MglRy45ciklLqXEpZS4lBKXUuJSRgAzApgRwIwAZgQwJ4A5AcwJYE4AcwJYEMCCABYEsCCABQEsCWBJAEsCWBLAkgBWBLAigBUBrAhghYo0GjuF5k6hwVNo8hQaPcUGIjYRsZGIzURsKEJVXairC5V1obYuVNeFSqRQixSqkUI9UqhICtUboX4jVHCEGo5+qTiH5PNPWX33wy4Jr3GaP9ZbqbSorEjNvczydX0HDBNakQ==]],
},
{
type = 'static',
force = 'ancient-friendly',
offset = {x = 0, y = 0},
bp_string = [[0eNqV1e9qwyAQAPB3uc+mRBOjyauMMdJFipBqiHZrKL77YsqgjPnvUzi4+0W543zAeb6JZZXKwvAAo8alsrq6rHLy8R0GTBFs+6dxCOSnVgaGtz1RXtQ4+xS7LQIGkFZcAYEarz4yVitRfY/zDL5MTcJLDiULxX1ZhTHVrMdJrC/FxL0jEMpKK8XzCEewfajb9bxnDvi/nyNYtNlLtPq9zel5nfpEnT/OH4RkIXUcabKQJo60WQiJIzQLYXGky0L6OMKyEB5HeF6LEz3u85REk3GdxyQ6hDPHNjEtOG9wcZtgmqL5bwNKW6R0AYUWKTSgdEUKDyisSGEBhRcpONSkvojpA5uuLjsMCTC4jMGe2Tf5sfWHl8cHwZdYzVFBOG5ZT1hLmobTzrkfNM8jsg==]],
},
},
}
Public.covered1b.market = {x = -2, y = -5}
Public.covered1b.steel_chest = {x = 1, y = -5}
Public.covered1b.wooden_chests = {
{x = -5, y = 1},
{x = -4, y = 3},
{x = -5, y = 5},
}
Public.maze_defended_camp = {
name = 'maze_defended_camp',
width = 20,
height = 20,
doNotDestroyExistingEntities = true,
components = {
{
type = 'static_destructible',
force = 'ancient-hostile',
offset = {x = 0, y = 0},
bp_string = [[0eNqVkVEKgzAQRO+y3xEaa7TNVUopWhcJ6CbEpFQkd2+iFAq1UL+WgZnHMDtD03s0VpEDOcNItcmczjqr2qSfIHnJYEonMFB3TSPISzSqjuo+WdxkECQohwMwoHpIqvOUOW8tOkgxajGRwpUBklNO4UpZxHQjPzRoo2Erz8DoMUY0vQutfUJgX4D8P4D4TTjuqSC2CMWuDhERR1nGkx+vYPBAOy72/MSL6pxXggt+LA8hvAD1/I5u]],
},
{
type = 'entities_randomlyplaced_border',
name = 'land-mine',
force = 'ancient-hostile',
offset = {x = 0, y = 0},
count = 20,
large_r = 10,
small_r = 7,
},
{
type = 'entities_randomlyplaced',
name = 'wooden-chest',
force = 'ancient-friendly',
offset = {x = 0, y = 0},
count = 13,
r = 4,
},
},
}
Public.maze_undefended_camp = {
name = 'maze_undefended_camp',
width = 20,
height = 20,
doNotDestroyExistingEntities = true,
components = {
{
type = 'entities_randomlyplaced_border',
name = 'land-mine',
force = 'ancient-hostile',
offset = {x = 0, y = 0},
count = 10,
large_r = 10,
small_r = 7,
},
{
type = 'entities_randomlyplaced',
name = 'wooden-chest',
force = 'ancient-friendly',
offset = {x = 0, y = 0},
count = 7,
r = 4,
},
},
}
Public.maze_mines = {
name = 'maze_mines',
width = 24,
height = 24,
doNotDestroyExistingEntities = true,
components = {
{
type = 'entities_randomlyplaced',
name = 'land-mine',
force = 'ancient-hostile',
offset = {x = 0, y = 0},
count = 10,
r = 12,
},
},
}
Public.maze_labs = {
name = 'maze_labs',
width = 12,
height = 12,
doNotDestroyExistingEntities = true,
components = {
{
type = 'entities_randomlyplaced',
name = 'lab',
force = 'ancient-friendly',
offset = {x = 0, y = 0},
count = 4,
r = 6,
},
},
}
Public.maze_worms = {
name = 'maze_worms',
width = 20,
height = 20,
doNotDestroyExistingEntities = true,
components = {
{
type = 'entities_randomlyplaced',
name = 'random-worm',
force = 'enemy',
offset = {x = 0, y = 0},
count = 20,
r = 10,
},
},
}
Public.maze_belts_1 = {
name = 'maze_belts_1',
width = 23,
height = 23,
doNotDestroyExistingEntities = true,
components = {
{
type = 'entities_grid',
force = 'ancient-friendly',
name = 'express-transport-belt',
direction = defines.direction.east,
offset = {x = 0, y = 0},
width = 23,
height = 23,
},
},
}
Public.maze_belts_2 = {
name = 'maze_belts_2',
width = 23,
height = 23,
doNotDestroyExistingEntities = true,
components = {
{
type = 'entities_grid',
force = 'ancient-friendly',
name = 'express-transport-belt',
direction = defines.direction.west,
offset = {x = 0, y = 0},
width = 23,
height = 23,
},
},
}
Public.maze_belts_3 = {
name = 'maze_belts_3',
width = 23,
height = 23,
doNotDestroyExistingEntities = true,
components = {
{
type = 'entities_grid',
force = 'ancient-friendly',
name = 'express-transport-belt',
direction = defines.direction.south,
offset = {x = 0, y = 0},
width = 23,
height = 23,
},
},
}
Public.maze_belts_4 = {
name = 'maze_belts_4',
width = 23,
height = 23,
doNotDestroyExistingEntities = true,
components = {
{
type = 'entities_grid',
force = 'ancient-friendly',
name = 'express-transport-belt',
direction = defines.direction.north,
offset = {x = 0, y = 0},
width = 23,
height = 23,
},
},
}
Public.maze_treasure = {
name = 'maze_treasure',
width = 3,
height = 3,
components = {
{
type = 'tiles',
tile_name = 'cyan-refined-concrete',
offset = {x = -1, y = -1},
bp_string = [[0eNqVksEKgzAMht8l5wqmzrn1VcYOToMEtC21GxPpu691O+wwYb0EAl/+fJCscBvvZB1rD2qFWbe28KYYHPepf4KqBCyxBgHcGT2DukSMB92OCfCLJVDAniYQoNspdZHrHHmCNKR7iikYrgI8j/QOsGZmz0Z/dpTbjjL8StiBMQeW/8GYo4E5GpijIXM0ZI6G3NeI59mOqL4eQsCD3LyNyxMemrNsaqyxOpYhvAAnDLaS]],
},
{
type = 'entities',
name = 'steel-chest',
force = 'ancient-friendly',
offset = {x = 0, y = 0},
instances = {
{position = {x = 0, y = 0}},
}
}
},
}
return Public

View File

@ -0,0 +1,40 @@
local Data = require 'maps.pirates.structures.island_structures.roc.data'
local Public = {}
Public.shelter1 = {}
Public.shelter1.Data = Data.shelter1
Public.shelter2 = {}
Public.shelter2.Data = Data.shelter2
Public.covered1 = {}
Public.covered1.Data = Data.covered1
Public.covered1b = {}
Public.covered1b.Data = Data.covered1b
Public.lonely_storage_tank = {}
Public.lonely_storage_tank.Data = Data.lonely_storage_tank
Public.swamp_lonely_storage_tank = {}
Public.swamp_lonely_storage_tank.Data = Data.swamp_lonely_storage_tank
Public.maze_labs = {}
Public.maze_labs.Data = Data.maze_labs
Public.maze_defended_camp = {}
Public.maze_defended_camp.Data = Data.maze_defended_camp
Public.maze_undefended_camp = {}
Public.maze_undefended_camp.Data = Data.maze_undefended_camp
Public.maze_mines = {}
Public.maze_mines.Data = Data.maze_mines
Public.maze_worms = {}
Public.maze_worms.Data = Data.maze_worms
Public.maze_belts_1 = {}
Public.maze_belts_1.Data = Data.maze_belts_1
Public.maze_belts_2 = {}
Public.maze_belts_2.Data = Data.maze_belts_2
Public.maze_belts_3 = {}
Public.maze_belts_3.Data = Data.maze_belts_3
Public.maze_belts_4 = {}
Public.maze_belts_4.Data = Data.maze_belts_4
Public.maze_treasure = {}
Public.maze_treasure.Data = Data.maze_treasure
return Public

View File

@ -0,0 +1,232 @@
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
-- local Common = require 'maps.pirates.common'
local Loot = require 'maps.pirates.loot'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
local Public = {}
local enum = {
BOATS = 'Boats',
ISLANDSTRUCTURES = 'IslandStructures',
}
Public.enum = enum
Public[enum.BOATS] = require 'maps.pirates.structures.boats.boats'
Public[enum.ISLANDSTRUCTURES] = require 'maps.pirates.structures.island_structures.island_structures'
function Public.post_creation_process(special_name, components)
local memory = Memory.get_crew_memory()
for _, c in pairs(components) do
local type = c.type
local force_name = c.force_name
-- local force
-- if force_name then force = game.forces[force_name] end
if type == 'static' then
for _, e in pairs(c.built_entities) do
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
end
end
elseif type == 'static_destructible' then
for _, e in pairs(c.built_entities) do
if e and e.valid then
e.minable = false
e.rotatable = false
end
end
-- elseif type == 'plain' then
-- for _, e in pairs(c.built_entities) do
-- --
-- end
elseif type == 'static_inoperable' then
for _, e in pairs(c.built_entities) do
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
e.operable = false
end
end
elseif type == 'entities' or type == 'entities_grid' then
for _, e in pairs(c.built_entities) do
if e and e.valid then
e.minable = false
e.rotatable = false
e.destructible = false
end
end
elseif type == 'entities_randomlyplaced' or type == 'entities_randomlyplaced_border' then
for _, e in pairs(c.built_entities) do
if e and e.valid then
e.minable = false
e.rotatable = false
end
end
-- elseif type == 'entities_minable' then
-- for _, e in pairs(c.built_entities) do
-- --
-- end
end
for _, e in pairs(c.built_entities) do
if e and e.valid then
e.update_connections()
if e.name == 'iron-chest' then
local inv = e.get_inventory(defines.inventory.chest)
local loot = Loot.iron_chest_loot()
for i = 1, #loot do
local l = loot[i]
inv.insert(l)
end
elseif e.name == 'stone-furnace' then
local inv = e.get_inventory(defines.inventory.fuel)
local loot = Loot.stone_furnace_loot()
for i = 1, #loot do
local l = loot[i]
inv.insert(l)
end
elseif e.name == 'roboport' then
local inv = e.get_inventory(defines.inventory.roboport_robot)
local loot = Loot.roboport_bots_loot()
for i = 1, #loot do
local l = loot[i]
inv.insert(l)
end
elseif e.name == 'centrifuge' then
local inv = e.get_inventory(defines.inventory.assembling_machine_input)
e.set_recipe('kovarex-enrichment-process')
inv.insert{name = 'uranium-235', count = 20}
elseif e.name == 'gun-turret' and special_name == 'small_radioactive_centrifuge' then
e.force = memory.force
elseif e.name == 'fast-splitter' and special_name == 'small_radioactive_centrifuge' then
e.splitter_output_priority = 'left'
e.splitter_filter = 'uranium-235'
elseif e.name == 'storage-tank' and special_name == 'swamp_lonely_storage_tank' then
e.insert_fluid(Loot.swamp_storage_tank_fluid_loot())
elseif e.name == 'storage-tank' and special_name == 'small_oilrig_base' then
e.insert_fluid(Loot.storage_tank_fluid_loot('crude-oil'))
elseif e.name == 'storage-tank' and special_name == 'small_abandoned_refinery' then
e.insert_fluid(Loot.storage_tank_fluid_loot('petroleum-gas'))
elseif e.name == 'storage-tank' and (not special_name == 'small_radioactive_reactor') then
e.insert_fluid(Loot.storage_tank_fluid_loot())
elseif (special_name == 'maze_labs') and e.name == 'lab' then
local inv = e.get_inventory(defines.inventory.lab_input)
local loot = Loot.maze_lab_loot()
for i = 1, #loot do
local l = loot[i]
inv.insert(l)
end
elseif (special_name == 'maze_treasure') and e.name == 'steel-chest' then
local inv = e.get_inventory(defines.inventory.chest)
local loot = Loot.maze_treasure_loot()
for i = 1, #loot do
local l = loot[i]
inv.insert(l)
end
elseif (special_name == 'maze_defended_camp' or special_name == 'maze_undefended_camp') and e.name == 'wooden-chest' then
local inv = e.get_inventory(defines.inventory.chest)
local loot = Loot.maze_camp_loot()
for i = 1, #loot do
local l = loot[i]
inv.insert(l)
end
end
if force_name and string.sub(force_name, 1, 15) and string.sub(force_name, 1, 15) == 'ancient-hostile' then
if e.name == 'gun-turret' then
if memory.overworldx < 800 then
e.insert({name = "piercing-rounds-magazine", count = 64})
else
e.insert({name = "uranium-rounds-magazine", count = 64})
end
end
elseif force_name and string.sub(force_name, 1, 16) and string.sub(force_name, 1, 16) == 'ancient-friendly' then
if e.name == 'oil-refinery' then
e.set_recipe('advanced-oil-processing')
end
end
end
end
end
end
function Public.try_place(structureScope, specialsTable, left_top, areawidth, areaheight, placeability_function)
local structureData = structureScope.Data
local attempts = 6
local succeeded = false
while attempts > 0 and (not succeeded) do
attempts = attempts - 1
local structure_topleft = {
x = left_top.x + Math.random(areawidth + 1 - structureData.width) - 1,
y = left_top.y + Math.random(areaheight + 1 - structureData.height) - 1,
}
local structure_center = {
x = structure_topleft.x + structureData.width/2,
y = structure_topleft.y + structureData.height/2,
}
local structure_topright = {
x = structure_topleft.x + structureData.width,
y = structure_topleft.y,
}
local structure_bottomleft = {
x = structure_topleft.x,
y = structure_topleft.y + structureData.height,
}
local structure_bottomright = {
x = structure_topleft.x + structureData.width,
y = structure_topleft.y + structureData.height,
}
if placeability_function(structure_topleft) and placeability_function(structure_topright) and placeability_function(structure_bottomleft) and placeability_function(structure_bottomright) and placeability_function(structure_center) then
specialsTable[#specialsTable + 1] = {
position = structure_center,
components = structureData.components,
width = structureData.width,
height = structureData.height,
name = structureData.name,
}
succeeded = true
if _DEBUG then
log('structure_yes: ' .. structureData.name .. ' at ' .. structure_center.x .. ', ' .. structure_center.y)
end
-- else
-- -- if _DEBUG then
-- -- log('structure_no: ' .. structureData.name .. ' at ' .. structure_center.x .. ', ' .. structure_center.y)
-- -- end
end
end
end
function Public.tryAddStructureByName(specialsTable, name, p)
local structureScope = Public[enum.ISLANDSTRUCTURES][Public[enum.ISLANDSTRUCTURES].enum.ROC][name] or Public[enum.ISLANDSTRUCTURES][Public[enum.ISLANDSTRUCTURES].enum.MATTISSO][name]
if not structureScope then
log('Couldn\'t find structure data for ' .. name)
return {}
else
local structureData = structureScope.Data
specialsTable[#specialsTable + 1] = {
position = p,
components = structureData.components,
width = structureData.width,
height = structureData.height,
name = structureData.name,
}
end
end
return Public

View File

@ -0,0 +1,267 @@
local Memory = require 'maps.pirates.memory'
-- local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
local SurfacesCommon = require 'maps.pirates.surfaces.common'
local Public = {}
local enum = {
DEFAULT = 'Default',
}
Public.enum = enum
Public.Data = {}
Public.Data.width = 16
Public.Data.height = 24
Public.Data.cabin_whitebelts_lrtp_order = {
{x = -5.5, y = -10.5, direction = defines.direction.north, type = 'input'},
{x = -4.5, y = -10.5, direction = defines.direction.north, type = 'input'},
{x = -3.5, y = -10.5, direction = defines.direction.north, type = 'input'},
{x = -5.5, y = 10.5, direction = defines.direction.south, type = 'output'},
{x = -4.5, y = 10.5, direction = defines.direction.south, type = 'output'},
{x = -3.5, y = 10.5, direction = defines.direction.south, type = 'output'},
}
Public.Data.car_pos = {x = 7, y = 0}
Public.Data.static_entities_bp = [[0eNqlmt1u4jAQhd/F16HKOP4Lr1JVK1qsbiSaoCTstqp49yWwW9EtJ/FxLyPIx9g+4zP28K4ed4e475t2VOt3NbSb/WrsVs99s52eX9VaXKHe1FqbY6Gap64d1Pr+9MXmud3spq+Mb/uo1qoZ44sqVLt5mZ5+d902tqunn3EY1fRiu40T61gsvhpf930chtXYb9ph3/Xj6jHuriH6+FCo2I7N2MRLMOeHtx/t4eUx9qdfuR1GofbdcHqpa/+N7M6eh1be2eMU2H8YnYgp5zFVIqaax5hEjJ7H2ESMnce4RIyZx/hEjJ/HhESMm8fUiZh6HiNlIicscJJ1vCBkSVWyLEhZUrUsC2KWVDXLgpwlVc+yoERxZLYL4HiSowEnLG2IMNcQsWanCoB0yYLAXGkh56oCHHanNoBT0XOuF4gmm4jGamlitRCj+0LcdZvt6aMb+8MHqFDbpo9Plw9PhL9W3h3G/WEy668/41nVoHgDC0JTWZOyscDgS5LjAEeyxYKIOpuIxlplyw8R+RSxV6P+pEN9i2+zN1IUscuOGBF9cgp6MPKPDGxakIAV7yj+KuzPCX+LX9N8x/BNye5TNmefMsJuLyD5jGZBQB2mIvcXDziG5ATAsdm7CiLmZ1RI2QMMm18hI78M7UtoNuhCDSy4ZY2pBhy2ThN0+Mz3I4issqVzRi5qxxpSOzewy+KxyecYuzQhjq7OEYmu2JB+AqsfcGCwbMUm4AjjynwhIqTkC1GnCNFpVog6Q4iONRsB1bVj3UZAve/y7QYi8/1GTNJasYZzA5uwVoHeNNBi0ZaDptbzlwMgJk+7DqifPHs9IKCi8/n3AxCZf/qRpOOPt6wSc84WnjcdNCG06cBlp10HlFGedh1Q2IVvuA5CfsN1kkrnQLtOTu0cKnrV0YQYmgSWPVh22UH5E9irZY3aHD5bPxBJH1ogibcQMF81e2zRoFysWQfRoLardf5lPIqNVjwMjlY8jMmSjVMNrLumFY9AnmyeQlAg26cQVJMNVASSsiR7qJgkZBsVkzTZScWkimymYpIh26mYZNmGKkY5tqWKUZ5tqmJUYNuqGMV3C8+oh+Lyd4711f9KCvUr9sOlFghifK29FSuVK4/HPzs+Pbo=]]
Public.Data.operable_entities_bp = [[0eNqV1NtqxCAQBuB3mWtTVnP2VUopyWbYCskY1G0bFt+9MUthKW4a7xzw/xyN5gb9eMXZKHIgb2CpmzOns4tRQ6i/QfKKwQJSFJ6BOmuyIF/XiepC3RimuGVGkKAcTsCAuilUX1oPSNn5A62DEKQBg+XZv1FnOrKzNi7rcXwMC//GAMkpp/DexFYs73SdejSrHl+ewaztGtL0u6PTS7ntKQx8aOkPJFKhNu7kRx2+7xRHHbHvlEedfN+pkg+ax6E6GRJxqEmGijjUJkN5HOKn1I/2rCXOU6/RU0mkXoC7tL677Y3Kh58Fg080douIhhd1K+pC5HlTVt7/AARAZgM=]]
Public.Data.cabin_splitters = {
{x = -5, y = 9.5, direction = defines.direction.north, type = 0},
{x = -4, y = 8.5, direction = defines.direction.north, type = 0},
{x = -3, y = 7.5, direction = defines.direction.north, type = 0},
{x = -5, y = 7.5, direction = defines.direction.north, type = 1},
{x = -4, y = 6.5, direction = defines.direction.north, type = 1},
{x = -3, y = 5.5, direction = defines.direction.north, type = 1},
{x = -5, y = 5.5, direction = defines.direction.north, type = 2},
{x = -4, y = 4.5, direction = defines.direction.north, type = 2},
{x = -3, y = 3.5, direction = defines.direction.north, type = 2},
{x = -5, y = 3.5, direction = defines.direction.north, type = 3},
{x = -4, y = 2.5, direction = defines.direction.north, type = 3},
{x = -3, y = 1.5, direction = defines.direction.north, type = 3},
{x = -5, y = 1.5, direction = defines.direction.north, type = 4},
{x = -4, y = 0.5, direction = defines.direction.north, type = 4},
{x = -3, y = -0.5, direction = defines.direction.north, type = 4},
{x = -5, y = -0.5, direction = defines.direction.north, type = 5},
{x = -4, y = -1.5, direction = defines.direction.north, type = 5},
{x = -3, y = -2.5, direction = defines.direction.north, type = 5},
{x = -5, y = -2.5, direction = defines.direction.north, type = 6},
{x = -4, y = -3.5, direction = defines.direction.north, type = 6},
{x = -3, y = -4.5, direction = defines.direction.north, type = 6},
{x = -5, y = -4.5, direction = defines.direction.north, type = 7},
{x = -4, y = -5.5, direction = defines.direction.north, type = 7},
{x = -4, y = -8.5, direction = defines.direction.north, type = 7},
{x = -5, y = -9.5, direction = defines.direction.north, type = 7},
{x = -3, y = -7.5, direction = defines.direction.north, type = 8},
{x = 0.5, y = -7, direction = defines.direction.west, type = 8},
}
Public.Data.output_chest = {x = 3.5, y = -6.5}
Public.Data.backup_output_chest = {x = 3.5, y = -7.5}
Public.Data.input_chests = {
{x = 0.5, y = 6.5},
{x = 0.5, y = 4.5},
{x = 0.5, y = 2.5},
{x = 0.5, y = 0.5},
{x = 0.5, y = -1.5},
{x = 0.5, y = -3.5},
{x = 0.5, y = -5.5},
}
Public.Data.surfacename_rendering_pos = {x = -0.5, y = -15}
function Public.get_cabin_surface_name()
local memory = Memory.get_crew_memory()
return SurfacesCommon.encode_surface_name(memory.id, 1, SurfacesCommon.enum.CABIN, enum.DEFAULT)
end
function Public.get_cabin_surface()
local name = Public.get_cabin_surface_name()
if name and game.surfaces[name] and game.surfaces[name].valid then return game.surfaces[Public.get_cabin_surface_name()] end
end
function Public.create_cabin_surface()
local memory = Memory.get_crew_memory()
local boat = memory.boat
if not Public.get_cabin_surface() then
local width = Public.Data.width
local height = Public.Data.height
local map_gen_settings = Common.default_map_gen_settings(width, height)
map_gen_settings.autoplace_settings.decorative.treat_missing_as_default = false
local cabinname = Public.get_cabin_surface_name()
local surface = game.create_surface(cabinname, map_gen_settings)
surface.freeze_daytime = true
surface.daytime = 0
surface.show_clouds = false
-- more here
Common.ensure_chunks_at(surface, {x = 0, y = 0}, 3)
boat.cabin_whitebelts = {}
for _, b in ipairs(Public.Data.cabin_whitebelts_lrtp_order) do
local p = {x = b.x, y = b.y}
local e = surface.create_entity({name = 'linked-belt', position = p, force = boat.force_name, create_build_effect_smoke = false, direction = b.direction})
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
e.operable = false
e.linked_belt_type = b.type
boat.cabin_whitebelts[#boat.cabin_whitebelts + 1] = e
end
end
boat.cabin_splitters = {}
for i, splitter in ipairs(Public.Data.cabin_splitters) do
local name = 'express-splitter'
local p = {x = splitter.x, y = splitter.y}
local priority, filter
if splitter.type == 0 then
priority = 'right'
filter = 'coal'
elseif splitter.type <= 6 then
priority = 'right'
filter = game.item_prototypes[CoreData.cost_items[splitter.type].name]
elseif splitter.type == 7 then
priority = 'left'
elseif splitter.type == 8 then
priority = 'right'
filter = 'landfill'
end
local e = surface.create_entity({name = name, position = p, force = boat.force_name, create_build_effect_smoke = false, direction = splitter.direction})
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
e.operable = false
if filter then e.splitter_filter = filter end
if priority then e.splitter_output_priority = priority end
boat.cabin_splitters[#boat.cabin_splitters + 1] = e
end
end
boat.input_chests = {}
for i, b in ipairs(Public.Data.input_chests) do
local p = {x = b.x, y = b.y}
local e = surface.create_entity({name = 'blue-chest', position = p, force = boat.force_name, create_build_effect_smoke = false})
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
-- e.operable = false
boat.input_chests[#boat.input_chests + 1] = e
end
end
local p = {x = Public.Data.output_chest.x, y = Public.Data.output_chest.y}
local e = surface.create_entity({name = 'red-chest', position = p, force = boat.force_name, create_build_effect_smoke = false})
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
-- e.operable = false
boat.output_chest = e
end
p = {x = Public.Data.backup_output_chest.x, y = Public.Data.backup_output_chest.y}
e = surface.create_entity({name = 'red-chest', position = p, force = boat.force_name, create_build_effect_smoke = false})
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
-- e.operable = false
boat.backup_output_chest = e
end
local es = Common.build_from_blueprint(Public.Data.static_entities_bp, surface, {x=0, y=0}, boat.force_name)
for _, e2 in pairs(es) do
if e2 and e2.valid then
e2.destructible = false
e2.minable = false
e2.rotatable = false
e2.operable = false
end
end
local es2 = Common.build_from_blueprint(Public.Data.operable_entities_bp, surface, {x=4, y=0}, boat.force_name)
for _, e2 in pairs(es2) do
if e2 and e2.valid then
e2.destructible = false
e2.minable = false
e2.rotatable = false
end
end
e = surface.create_entity({name = 'car', position = Public.Data.car_pos, force = boat.force_name, create_build_effect_smoke = false})
if e and e.valid then
e.get_inventory(defines.inventory.fuel).insert({name = 'wood', count = 16})
e.color = {148, 106, 52}
e.destructible = false
e.minable = false
e.rotatable = false
e.operable = false
end
rendering.draw_text{
text = 'Captain\'s Cabin',
surface = surface,
target = Public.Data.surfacename_rendering_pos,
color = CoreData.colors.renderingtext_yellow,
scale = 3.5,
font = 'default-game',
alignment = 'center'
}
end
end
function Public.connect_up_linked_belts_to_deck() --assumes both are in standard lrtd order
local memory = Memory.get_crew_memory()
local boat = memory.boat
if boat and boat.deck_whitebelts and #boat.deck_whitebelts > 0 and boat.cabin_whitebelts and #boat.cabin_whitebelts > 0 then
local connections = {
{1,7},
{2,8},
{3,9},
{4,10},
{5,11},
{6,12},
}
for _, c in pairs(connections) do
local b1 = boat.cabin_whitebelts[c[1]]
local b2 = boat.deck_whitebelts[c[2]]
b1.connect_linked_belts(b2)
end
end
end
function Public.terrain(args)
if args.p.x > Public.Data.width/2-1 and (args.p.y > 2 or args.p.y < -2) then
args.tiles[#args.tiles + 1] = {name = 'out-of-map', position = args.p}
else
args.tiles[#args.tiles + 1] = {name = CoreData.static_boat_floor, position = args.p}
end
return nil
end
function Public.chunk_structures()
return nil
end
return Public

View File

@ -0,0 +1,33 @@
-- local Memory = require 'maps.pirates.memory'
-- local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
-- local Structures = require 'maps.pirates.structures.structures'
-- local Boats = require 'maps.pirates.structures.boats.boats'
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
local Public = {}
Public.Data = require 'maps.pirates.surfaces.channel.data'
Public.info = {
display_name = 'Channel'
}
function Public.terrain(args)
if (args.p.y>30 or args.p.y<-20) and args.p.x>-80 and args.p.x<80 then
args.tiles[#args.tiles + 1] = {name = 'sand-1', position = args.p}
else
args.tiles[#args.tiles + 1] = {name = 'deepwater', position = args.p}
end
end
function Public.chunk_structures()
return nil
end
return Public

View File

@ -0,0 +1,16 @@
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
local Public = {}
Public.width = 384
Public.height = 384
Public.extra_water_on_left = 96
Public.noiseparams = {
}
return Public

View File

@ -0,0 +1,67 @@
-- local Memory = require 'maps.pirates.memory'
-- local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
-- local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
local Public = {}
local enum = {
SEA = 'Sea',
ISLAND = 'Island',
CROWSNEST = 'Crowsnest',
LOBBY = 'Lobby',
HOLD = 'Hold',
CABIN = 'Cabin',
CHANNEL = 'Channel',
DOCK = 'Dock',
}
Public.enum = enum
function Public.encode_surface_name(crewid, destination_index, type, subtype) -- crewid=0 is shared surfaces
local str
if subtype then
str = string.format('%03d-%03d-%s-%s', crewid, destination_index, type, subtype) --uses the fact that type and subtype resolve to strings
else
str = string.format('%03d-%03d-%s', crewid, destination_index, type)
end
return str
end
function Public.decode_surface_name(name)
local crewid = tonumber(string.sub(name, 1, 3))
local destination_index = tonumber(string.sub(name, 5, 7))
local type = nil
local subtype = nil
local substring = string.sub(name, 9, -1)
local pull = {}
for a, b in string.gmatch(substring, "(%w+)-(%w+)") do
pull[1] = a
pull[2] = b
end
if #pull == 0 then
type = substring
elseif #pull == 2 then
type = pull[1]
subtype = pull[2]
end
return {crewid = crewid, destination_index = destination_index, type = type, subtype = subtype}
end
function Public.fetch_iconized_map(destination)
local type = destination.type
if type == Public.enum.LOBBY then
return CoreData.Lobby_iconized_map()
elseif type == Public.enum.DOCK then
return CoreData.Dock_iconized_map()
else
return destination.iconized_map
end
end
return Public

View File

@ -0,0 +1,429 @@
local Memory = require 'maps.pirates.memory'
-- local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local CustomEvents = require 'maps.pirates.custom_events'
local _inspect = require 'utils.inspect'.inspect
local Token = require 'utils.token'
local Task = require 'utils.task'
local SurfacesCommon = require 'maps.pirates.surfaces.common'
-- This file is logically a bit of a mess, because we changed from islands moving to the crowsnest platform moving.
local Public = {}
local enum = {
DEFAULT = 'Default',
}
Public.enum = enum
Public.Data = {}
Public.Data.chartingdistance = 130
Public.Data.visibilitywidth = 400
Public.Data.width = 10000 --minimap won't chart beyond this point
Public.Data.height = 72
Public.platformwidth = 7
Public.platformheight = 7
Public.platformrightmostedge = 4
Public.Data.chestspos = {
{x = -2.5, y = -3.5},
{x = -1.5, y = -3.5},
{x = -0.5, y = -3.5},
{x = 1.5, y = -3.5},
{x = 2.5, y = -3.5},
{x = 3.5, y = -3.5},
{x = -2.5, y = 3.5},
{x = -1.5, y = 3.5},
{x = -0.5, y = 3.5},
{x = 1.5, y = 3.5},
{x = 2.5, y = 3.5},
{x = 3.5, y = 3.5},
{x = -2.5, y = -2.5},
{x = -2.5, y = -1.5},
{x = -2.5, y = 1.5},
{x = -2.5, y = 2.5},
{x = 3.5, y = -2.5},
{x = 3.5, y = -1.5},
{x = 3.5, y = 1.5},
{x = 3.5, y = 2.5},
}
Public.Data.surfacename_rendering_pos = {x = 0.5, y = -6.1}
function Public.crowsnest_surface_name()
local memory = Memory.get_crew_memory()
return SurfacesCommon.encode_surface_name(memory.id, 0, SurfacesCommon.enum.CROWSNEST, nil)
end
function Public.get_crowsnest_surface()
-- local memory = Memory.get_crew_memory()
return game.surfaces[Public.crowsnest_surface_name()]
end
function Public.move_crowsnest(vectorx, vectory)
local memory = Memory.get_crew_memory()
local surface = game.surfaces[Public.crowsnest_surface_name()]
local old_area = {{memory.overworldx - 2.5, memory.overworldy - 3.5},{memory.overworldx + 3.5, memory.overworldy + 3.5}}
memory.overworldx = memory.overworldx + vectorx
memory.overworldy = memory.overworldy + vectory
local new_area = {{memory.overworldx - 2.5, memory.overworldy - 3.5},{memory.overworldx + 3.5, memory.overworldy + 3.5}}
local new_floor_positions = {}
local tiles1 = {}
for y = new_area[1][2], new_area[2][2], 1 do
for x = new_area[1][1], new_area[2][1], 1 do
if not new_floor_positions[x] then new_floor_positions[x] = {} end
new_floor_positions[x][y] = true
tiles1[#tiles1 + 1] = {name = CoreData.static_boat_floor, position = {x = x, y = y}}
end
end
surface.set_tiles(tiles1, true, true, true)
local entities_to_teleport = surface.find_entities_filtered{area = old_area}
for _, e in pairs(entities_to_teleport) do
e.teleport(vectorx, vectory)
end
local tiles2 = {}
for y = old_area[1][2], old_area[2][2], 1 do
for x = old_area[1][1], old_area[2][1], 1 do
if not (new_floor_positions[x] and new_floor_positions[x][y]) then
tiles2[#tiles2 + 1] = {name = 'deepwater', position = {x = x, y = y}}
end
end
end
surface.set_tiles(tiles2, true, true, true)
if memory.crowsnest_surfacename_rendering then
local p = rendering.get_target(memory.crowsnest_surfacename_rendering).position
rendering.set_target(memory.crowsnest_surfacename_rendering, {x = p.x + vectorx, y = p.y + vectory})
end
if vectorx ~= 0 then
local crew_force = memory.force
local area = {{memory.overworldx,-Public.Data.height/2},{memory.overworldx+Public.Data.chartingdistance,Public.Data.height/2}}
-- crew_force.clear_chart(surface)
crew_force.chart(surface, area)
end
script.raise_event(CustomEvents.enum['update_crew_progress_gui'], {})
end
function Public.update_destination_renderings()
local memory = Memory.get_crew_memory()
for _, dest in pairs(memory.destinations) do
if dest.dynamic_data.crowsnest_renderings then
if dest.overworld_position.x <= memory.overworldx+Public.Data.chartingdistance and dest.overworld_position.x >= memory.overworldx-Public.Data.chartingdistance then
for _, r in pairs(dest.dynamic_data.crowsnest_renderings) do
if type(r) == 'table' then
if rendering.is_valid(r.text_rendering) then
rendering.set_visible(r.text_rendering, true)
end
if rendering.is_valid(r.sprite_rendering) then
rendering.set_visible(r.sprite_rendering, true)
end
else
if rendering.is_valid(r) then
rendering.set_visible(r, true)
end
end
end
else
for _, r in pairs(dest.dynamic_data.crowsnest_renderings) do
if type(r) == 'table' then
if rendering.is_valid(r.text_rendering) then
rendering.set_visible(r.text_rendering, false)
end
if rendering.is_valid(r.sprite_rendering) then
rendering.set_visible(r.sprite_rendering, false)
end
else
if rendering.is_valid(r) then
rendering.set_visible(r, false)
end
end
end
end
end
end
end
function Public.draw_kraken(p)
-- local memory = Memory.get_crew_memory()
local surface = game.surfaces[Public.crowsnest_surface_name()]
surface.set_tiles({{name = CoreData.kraken_tile, position = {x = Public.platformrightmostedge + p.x, y = p.y}}}, true, true, true)
end
function Public.draw_destination(destination)
-- local memory = Memory.get_crew_memory()
local surface = game.surfaces[Public.crowsnest_surface_name()]
local tiles = {}
-- local entities = {}
-- local renderings = {}
local iconized_map = SurfacesCommon.fetch_iconized_map(destination)
if not iconized_map then iconized_map = destination.iconized_map end
local x = Public.platformrightmostedge + destination.overworld_position.x
local y = destination.overworld_position.y
for _, t in pairs(iconized_map.tiles) do
local t2 = Utils.deepcopy(t)
t2.position = {x = x + t.position.x, y = y + t.position.y}
tiles[#tiles+1] = t2
end
surface.set_tiles(tiles, true, true, true)
for _, e in pairs(iconized_map.entities) do
local e2 = Utils.deepcopy(e)
e2.position = {x = x + e.position.x, y = y + e.position.y}
if e2.source then e2.source = {x = e2.source.x + x, y = e2.source.y + y} end
if e2.target then e2.target = {x = e2.target.x + x, y = e2.target.y + y} end
surface.create_entity(e2)
end
-- Now we can destroy the iconized_map... right?
destination.iconized_map = nil
end
function Public.draw_extra_bits()
Public.draw_destination{
type = 'finish line',
seed = 0,
overworld_position = {x = CoreData.victory_x, y = 0},
static_params = {},
dynamic_data = {},
iconized_map = {
tiles = {},
entities = {
{name = 'electric-beam', position = {x = 0, y = 0}, source = {x = 0, y = -37}, target = {x = 0, y = 37}},
{name = 'electric-beam', position = {x = 0, y = 0}, source = {x = 0, y = -37}, target = {x = 0, y = 37}},
},
},
iconized_map_width = 2,
iconized_map_height = 2,
}
Public.draw_destination{
type = 'Lobby',
overworld_position = {x = -14, y = 0},
}
end
function Public.create_crowsnest_surface()
-- if not game.surfaces[crowsnest_surface_name()] then
local memory = Memory.get_crew_memory()
local map_gen_settings = Common.default_map_gen_settings(Public.Data.width, Public.Data.height)
game.create_surface(Public.crowsnest_surface_name(), map_gen_settings)
local surface = game.surfaces[Public.crowsnest_surface_name()]
surface.freeze_daytime = true
surface.daytime = 0
Common.ensure_chunks_at(surface, {x = 0, y = 0}, 15)
Public.paint_crowsnest_background_tiles()
-- end
memory.crowsnest_surfacename_rendering = rendering.draw_text{
text = 'Crow\'s Nest',
surface = surface,
target = Public.Data.surfacename_rendering_pos,
color = CoreData.colors.renderingtext_yellow,
scale = 2.5,
font = 'default-game',
alignment = 'center'
}
end
function Public.paint_water_between_overworld_positions(overworldx1, overworldx2)
-- local memory = Memory.get_crew_memory()
local surface = game.surfaces[Public.crowsnest_surface_name()]
Common.ensure_chunks_at(surface, {x = overworldx2, y = 0}, 10)
local tiles = {}
for y = -(Public.Data.height+32 - 1)/2, (Public.Data.height+32 - 1)/2, 1 do
for x = Public.platformrightmostedge + overworldx1, Public.platformrightmostedge + overworldx2 do
if y>= -(Public.Data.height - 1)/2 and y <= (Public.Data.height - 1)/2 then
tiles[#tiles + 1] = {name = 'deepwater', position = {x = x, y = y}}
else
tiles[#tiles + 1] = {name = 'out-of-map', position = {x = x, y = y}}
end
end
end
surface.set_tiles(tiles)
end
function Public.paint_crowsnest_background_tiles()
-- local memory = Memory.get_crew_memory()
local surface = game.surfaces[Public.crowsnest_surface_name()]
local tiles = {}
for y = -(Public.Data.height+32 - 1)/2, (Public.Data.height+32 - 1)/2, 1 do
for x = -(Public.Data.visibilitywidth+32 - 1)/2, (Public.Data.visibilitywidth+32 - 1)/2, 1 do
if x <= 3.5 and x >= -2.5 and y <= 3.5 and y >= -3.5 then
tiles[#tiles + 1] = {name = CoreData.static_boat_floor, position = {x = x, y = y}}
elseif x>= -(Public.Data.visibilitywidth - 1)/2 and x <= (Public.Data.visibilitywidth - 1)/2 and y>= -(Public.Data.height - 1)/2 and y <= (Public.Data.height - 1)/2 then
tiles[#tiles + 1] = {name = 'deepwater', position = {x = x, y = y}}
else
tiles[#tiles + 1] = {name = 'out-of-map', position = {x = x, y = y}}
end
end
end
surface.set_tiles(tiles)
end
function Public.upgrade_chests(new_chest) --the fast replace doesn't work well on the '/go' tick, but that's okay
local memory = Memory.get_crew_memory()
local boat = memory.boat
local surface = Public.get_crowsnest_surface()
local ps = Public.Data.chestspos
for _, p in pairs(ps) do
local p2 = {x = p.x + memory.overworldx, y = p.y + memory.overworldy}
local es = surface.find_entities_filtered{name = 'wooden-chest', position = p2, radius = 0.05}
if es and #es == 1 then
es[1].minable = true
es[1].destructible = true
es[1].rotatable = true
-- es[1].operable = true
local e2 = surface.create_entity{name = new_chest, position = es[1].position, fast_replace = true, spill = false, force = boat.force_name}
e2.minable = false
e2.destructible = false
e2.rotatable = false
-- e2.operable = false
end
end
end
-- just for debug purposes, might need to fire this again
local crowsnest_delayed = Token.register(
function(data)
Memory.set_working_id(data.crew_id)
Public.crowsnest_surface_delayed_init()
end
)
function Public.crowsnest_surface_delayed_init()
local memory = Memory.get_crew_memory()
local surface = game.surfaces[Public.crowsnest_surface_name()]
local force = memory.force
if _DEBUG and (not (surface and surface.valid)) then
game.print('/go issue: crowsnest_surface_delayed_init called when crowsnest surface wasn\'t valid. This happens due to a difficult-to-handle race condition in concurrent delayed events in the /go shortcut. Firing event again...')
Task.set_timeout_in_ticks(5, crowsnest_delayed, {crew_id = memory.id})
return
end
surface.destroy_decoratives{area = {{-3, -4},{4, 4}}}
local chestspos = Public.Data.chestspos
local steerchestspos = {
{x = 0.5, y = -3.5},
{x = 0.5, y = 3.5},
}
local carspos = {
{x = 3.3, y = 0},
{x = -2.3, y = 0},
}
for _, p in pairs(chestspos) do
local e = surface.create_entity({name = 'wooden-chest', position = p, force = force, create_build_effect_smoke = false})
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
end
end
for _, p in pairs(steerchestspos) do
local e = surface.create_entity({name = 'blue-chest', position = p, force = force, create_build_effect_smoke = false})
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
if not memory.boat.crowsneststeeringchests then
memory.boat.crowsneststeeringchests = {}
end
if p.y < 0 then
memory.boat.crowsneststeeringchests.left = e
else
memory.boat.crowsneststeeringchests.right = e
end
end
end
for _, p in pairs(carspos) do
local e = surface.create_entity({name = 'car', position = p, force = force, create_build_effect_smoke = false})
if e and e.valid then
e.get_inventory(defines.inventory.fuel).insert({name = 'wood', count = 16})
e.destructible = false
e.minable = false
e.rotatable = false
e.operable = false
end
end
end
function Public.paint_around_destination(id, tile_name)
local memory = Memory.get_crew_memory()
local destination_data = memory.destinations[id]
local surface = game.surfaces[Public.crowsnest_surface_name()]
-- local static_params = destination_data.static_params
local type = destination_data.type
local tiles = {}
if type == SurfacesCommon.enum.ISLAND then
for x = memory.overworldx + Public.platformrightmostedge + 0.5, memory.overworldx + Public.platformrightmostedge + destination_data.iconized_map_width - 0.5 do
tiles[#tiles+1] = {name = tile_name, position = {x = x, y = memory.overworldy + destination_data.iconized_map_height/2 - 0.5}}
tiles[#tiles+1] = {name = tile_name, position = {x = x, y = memory.overworldy - destination_data.iconized_map_height/2 + 0.5}}
end
for y = memory.overworldy + -destination_data.iconized_map_height/2 + 1.5, memory.overworldy + destination_data.iconized_map_height/2 - 1.5 do
tiles[#tiles+1] = {name = tile_name, position = {x = memory.overworldx + Public.platformrightmostedge + 0.5, y = y}}
tiles[#tiles+1] = {name = tile_name, position = {x = memory.overworldx + Public.platformrightmostedge + destination_data.iconized_map_width - 0.5, y = y}}
end
end
surface.set_tiles(tiles, true, true, true)
end
function Public.terrain() --blank since we do this manually
--
end
function Public.chunk_structures()
--
end
return Public

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,375 @@
local Memory = require 'maps.pirates.memory'
-- local Math = require 'maps.pirates.math'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
local SurfacesCommon = require 'maps.pirates.surfaces.common'
local Public = {}
local enum = {
INITIAL = 'Initial',
SECONDARY = 'Secondary',
}
Public.enum = enum
Public.Data = {}
-- local enum_boat = Boats.enum
-- Public.enum_boat = enum_boat
Public.Data.width = 92
Public.Data.height = 46
Public.Data.loco_offset = {x = -2, y = 0}
-- Public.Data.loco_offset = {x = 18, y = 0}
-- Public.Data.display_name = 'Ship\'s Hold'
Public.Data.downstairs_pole_positions = {
{x = -1, y = -5},
{x = -1, y = 5},
}
Public[enum.INITIAL] = {}
Public[enum.INITIAL].Data = {}
Public[enum.INITIAL].Data.hold_whitebelts_lrtp_order = {
{x = -19.5, y = -21.5, direction = defines.direction.north, type = 'output'},
{x = -18.5, y = -21.5, direction = defines.direction.north, type = 'output'},
{x = -17.5, y = -21.5, direction = defines.direction.north, type = 'output'},
{x = 17.5, y = -21.5, direction = defines.direction.north, type = 'output'},
{x = 18.5, y = -21.5, direction = defines.direction.north, type = 'output'},
{x = 19.5, y = -21.5, direction = defines.direction.north, type = 'output'},
{x = -44.5, y = -3.5, direction = defines.direction.west, type = 'output'},
{x = 44.5, y = -3.5, direction = defines.direction.east, type = 'output'},
{x = -44.5, y = -2.5, direction = defines.direction.west, type = 'output'},
{x = 44.5, y = -2.5, direction = defines.direction.east, type = 'output'},
{x = -44.5, y = 2.5, direction = defines.direction.west, type = 'input'},
{x = 44.5, y = 2.5, direction = defines.direction.east, type = 'input'},
{x = -44.5, y = 3.5, direction = defines.direction.west, type = 'input'},
{x = 44.5, y = 3.5, direction = defines.direction.east, type = 'input'},
{x = -19.5, y = 21.5, direction = defines.direction.south, type = 'input'},
{x = -18.5, y = 21.5, direction = defines.direction.south, type = 'input'},
{x = -17.5, y = 21.5, direction = defines.direction.south, type = 'input'},
{x = 17.5, y = 21.5, direction = defines.direction.south, type = 'input'},
{x = 18.5, y = 21.5, direction = defines.direction.south, type = 'input'},
{x = 19.5, y = 21.5, direction = defines.direction.south, type = 'input'},
}
Public[enum.SECONDARY] = {}
Public[enum.SECONDARY].Data = {}
Public[enum.SECONDARY].Data.hold_whitebelts_lrtp_order = {
{x = -44.5, y = -3.5, direction = defines.direction.west, type = 'output'},
{x = 44.5, y = -3.5, direction = defines.direction.east, type = 'output'},
{x = -44.5, y = -2.5, direction = defines.direction.west, type = 'output'},
{x = 44.5, y = -2.5, direction = defines.direction.east, type = 'output'},
{x = -44.5, y = 2.5, direction = defines.direction.west, type = 'input'},
{x = 44.5, y = 2.5, direction = defines.direction.east, type = 'input'},
{x = -44.5, y = 3.5, direction = defines.direction.west, type = 'input'},
{x = 44.5, y = 3.5, direction = defines.direction.east, type = 'input'},
}
Public.Data.boxes_bp = [[0eNqV3OtqI0cUBOB3md/you5z+jJ6lbAE71psBGvJWNokZtG7x5cQAkmpqn4a7GJafDrTmlL75/Ll+4/90/PheFl2P5fz8f7p7nK6+/Z8eHj7+c9lt9bN8rLssl83y+Hr6Xhedr+8/uLh2/H++9uvXF6e9stuOVz2j8tmOd4/vv30x+n0sD/eff1tf74sb394fNi/ZpXr582yP14Ol8P+I+f9h5dfjz8ev+yfX3/h/xM2y9Pp/PpHp+PfF1U+tfer2n5q1+vmPzFVjNnejgkxJm7HpBhTb8c0MSZvx3Qxpt2OGWJMvx0zxZhxO2YVY+btmLIVc1aSIzsmAosquZB3RFEtF6KwqJoLeVcU1XMhoIsquhDSRTVdCOqiqi6EdVFdFwK7qrALkV1V2ZXIrqrsyma9KrsS2VWe00R2VWVXIruqsiuRXVXZlciuquxKZFdVdiWyQ5VdiexQZQfbNaiyg8gOef9BZIcqO4jsUGUHkR2q7CCyQ5UdRHaosoPIDlV2ENmpyg4iO1XZSWSnKjuJ7FRlJ5Gdquxke3R5d01kpyo7iexUZSeRnarsJLJTlZ1EdlNlJ5HdVNmNyG6q7EZkN1V2Y5/2VNmNyG6q7EZkN/mTI5HdVNmNyG6q7EZkN1V2I7K7KrsR2V2V3YnsrsruRHZXZXciu6uyO5HdVdmdPRJRZXciu8tPRYjsrsruRHZXZXcie6iyO5E9VNmDyB6q7EFkD1X2ILKHKnsQ2UOVPYjsocoe7HGfKnsQ2UN+4kdkD1X2ILKnKnsQ2VOVPYnsqcqeRPZUZU8ie6qyJ5E9VdmTyJ6q7ElkT1X2ZI+yVdmTyJ7y02wie1VlTyJ7VWWvRPaqyl6J7DXMiqeCnDRzCshp7sLQBXU3CF3RMFeWIGeaOQFyVndhiUqarZsUKKmYa+soyG0cGwoKd23wktJNgtfUzMVNFNTNoIGChrs2mDTdJLi41Z0ksDzcmkkrCiru4mCSPbfx6tzJXSpKsmd3QUn29MZR/vyG63MneEETs7gzvKCJWewpDqOqPcbh+qo7xwuamtUd5AVNzWpPchxlj3K8PneWFzTvqjvMC5rB1Z7mOMoe53h97jyv8KsX7jwvaAzLHeU/5QKOqm7hgaN86TDKlo5fdVd6RXM4XOkV3RzClo6jbOl4fbZ0NIfTlV7RzSHtrQuOsvcueH32p040h9Pdu1R0c9Cby0YvqrszAUf50uECfenwqmzp6O7QbOnoltV86TDKlw7X50oP+DU/Wzq6OTT/GQuMsnfpeH3uLj3QHG72kxZ0c2j2Lh1Gdf9hC1pfd3fpgeZwd3fpgW4O3d674Ch774LX5+5dAk287u5dAo3hbk90HGVPdLw+d6IHmnjDneiBxvCwJzq+KHui46tyJ3rCr0m7Ez3QGB72RMcX5U90eFX2k3M08YY70RON4eE/PUdR057ocH3TneiJJt50J3qiMTztiY6j7ImO19fMk02Jhud0JzpOGub5Jpw0zSNOOGk1jyfBJLn8TJpUzENKOKmap6ZwUpgnlXBSmmencFJzDyvhqO4eoMJRwz2whKOme4gKR63uoSUUVeU6tCSNKu7BJRxV3cNUOCrcw0s4Kt0DVTiquQeYcFR3D1XhqOEeYsJR0z1YhaNW9yATjJLL0Uq1y/VopdrlfrRS7XI/Wql2uSCtVLvckAbVLjekQbXLDWlQ7XJFGlS73JEG1S53pEG1yx1pUO1ySRpUu9ySBtUut6RBtcstaVLtck2aVLvckybVLvekSbXLPWlS7XJRmlS73pRS7XpTSrXLTWlS7XJTmlS73JQ2ql2uShvVLneljWqXu9JGtctdaaPa5bK0Ue1yW9qodrktbVS73JY2ql2uSxvVLvelnWqX+9JOtct9aafa5b60U+1yX9qpdrkw7VS73Jh2ql1uTDvVLjemnWqXK9NOtcud6aDa5c50UO1yZzqodrk0HVS73JoOql1uTQfVLremgxKVa9NB3zhybzooUbk3HfSNI/emkxKVi9NJ3zhyczr5yz7dEz04yj5lBKPk6nSlL7vfnX5Efd58/Ae63b/+n91m+X3/fH7/kzpLjrWOjJ5rndfrX67iTLU=]]
Public.Data.boxes_bp_offset = {x = 0, y = 0}
Public.Data.surfacename_rendering_pos = {x = Public.Data.loco_offset.x, y = -Public.Data.height/2 - 5}
function Public.get_hold_surface_name(nth)
nth = nth or 1
local memory = Memory.get_crew_memory()
local subtype = (nth == 1) and enum.INITIAL or enum.SECONDARY
return SurfacesCommon.encode_surface_name(memory.id, nth, SurfacesCommon.enum.HOLD, subtype)
end
function Public.get_hold_surface(nth)
nth = nth or 1
local name = Public.get_hold_surface_name(nth)
if name and game.surfaces[name] and game.surfaces[name].valid then return game.surfaces[name] end
end
function Public.create_hold_surface(nth)
local memory = Memory.get_crew_memory()
local boat = memory.boat
local width = Public.Data.width
local height = Public.Data.height
local map_gen_settings = Common.default_map_gen_settings(width, height)
map_gen_settings.autoplace_settings.decorative.treat_missing_as_default = false
local holdname = Public.get_hold_surface_name(nth)
if not holdname then log(_inspect{'holdname is nil? here some stuff:', memory.id, nth, SurfacesCommon.enum.HOLD}) end
local surface = game.create_surface(holdname, map_gen_settings)
surface.freeze_daytime = true
surface.daytime = 0
surface.show_clouds = false
surface.solar_power_multiplier = 0
-- more here
Common.ensure_chunks_at(surface, {x = 0, y = 0}, 5)
local subtype = nth == 1 and enum.INITIAL or enum.SECONDARY
local whitebelts_table, whitebelts_data
if (not boat.hold_whitebelts) then boat.hold_whitebelts = {} end
boat.hold_whitebelts[nth] = {}
whitebelts_table = boat.hold_whitebelts[nth]
if subtype == enum.INITIAL then
whitebelts_data = Public[enum.INITIAL].Data.hold_whitebelts_lrtp_order
elseif subtype == enum.SECONDARY then
whitebelts_data = Public[enum.SECONDARY].Data.hold_whitebelts_lrtp_order
end
for _, b in ipairs(whitebelts_data) do
local p = {x = b.x, y = b.y}
local e = surface.create_entity({name = 'linked-belt', position = p, force = boat.force_name, create_build_effect_smoke = false, direction = b.direction})
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
e.operable = false
local type = b.type
if nth % 2 == 0 then
if type == 'input' then type = 'output' else type = 'input' end
end
e.linked_belt_type = type
whitebelts_table[#whitebelts_table + 1] = e
end
end
local boxes = Common.build_from_blueprint(Public.Data.boxes_bp, surface, Public.Data.boxes_bp_offset, boat.force_name)
for _, e in pairs(boxes) do
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
end
end
Common.build_small_loco(surface, Public.Data.loco_offset, memory.force, {255, 106, 52})
local items = subtype == enum.INITIAL and Balance.starting_items_crew_downstairs() or {}
Common.surface_place_random_obstacle_boxes(Public.get_hold_surface(nth), {x=0,y=0}, Public.Data.width, Public.Data.height, 'rocket-silo', {[1] = 0, [2] = 5, [3] = 4, [4] = 2}, items)
-- Public.hold_place_random_obstacle_boxes(nth, {[1] = 0, [2] = 9, [3] = 3, [4] = 1}, items)
if not boat.downstairs_poles then boat.downstairs_poles = {} end
boat.downstairs_poles[nth] = {}
for i = 1, 2 do
local e = surface.create_entity({name = 'substation', position = Public.Data.downstairs_pole_positions[i], force = boat.force_name, create_build_effect_smoke = false})
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
boat.downstairs_poles[nth][i] = e
end
end
if nth >= 2 then
if boat.downstairs_poles[nth][1] and boat.downstairs_poles[nth][1].valid and boat.downstairs_poles[nth-1][2] and boat.downstairs_poles[nth-1][2].valid then
boat.downstairs_poles[nth][1].connect_neighbour(boat.downstairs_poles[nth-1][2])
end
end
if subtype == enum.SECONDARY then
local difficulty_name = CoreData.get_difficulty_name_from_value(Common.difficulty())
if difficulty_name == CoreData.difficulty_options[#CoreData.difficulty_options].text then
Public.upgrade_chests(nth, 'steel-chest')
elseif difficulty_name ~= CoreData.difficulty_options[1].text then
Public.upgrade_chests(nth, 'iron-chest')
end
Public.nth_hold_connect_linked_belts(nth)
end
if nth==1 then
memory.shiphold_rendering_1 = rendering.draw_text{
text = 'Ship\'s Hold',
surface = surface,
target = Public.Data.surfacename_rendering_pos,
color = CoreData.colors.renderingtext_yellow,
scale = 6,
font = 'default-game',
alignment = 'center'
}
else
if nth==2 then
if memory.shiphold_rendering_1 then
rendering.set_text(memory.shiphold_rendering_1, 'Ship\'s Hold: -1')
end
end
rendering.draw_text{
text = 'Ship\'s Hold: -' .. nth,
surface = surface,
target = Public.Data.surfacename_rendering_pos,
color = CoreData.colors.renderingtext_yellow,
scale = 6,
font = 'default-game',
alignment = 'center'
}
end
end
function Public.add_another_hold_surface()
local memory = Memory.get_crew_memory()
memory.hold_surface_count = memory.hold_surface_count + 1
Public.create_hold_surface(memory.hold_surface_count)
return memory.hold_surface_count
end
function Public.upgrade_chests(nth, new_chest)
local memory = Memory.get_crew_memory()
local boat = memory.boat
local surface = Public.get_hold_surface(nth)
local ps = Common.entity_positions_from_blueprint(Public.Data.boxes_bp, {x = -Public.Data.width/2 ,y = -Public.Data.height/2})
for _, p in pairs(ps) do
local es = surface.find_entities_filtered{name = 'wooden-chest', position = p, radius = 0.05}
if es and #es == 1 then
es[1].minable = true
es[1].destructible = true
es[1].rotatable = true
end
local e2 = surface.create_entity{name = new_chest, position = p, fast_replace = true, spill = false, force = boat.force_name}
e2.minable = false
e2.destructible = false
e2.rotatable = false
end
end
function Public.connect_up_linked_belts_to_deck() --assumes both are in standard lrtd order
local memory = Memory.get_crew_memory()
local boat = memory.boat
if boat and boat.deck_whitebelts and #boat.deck_whitebelts > 0 and boat.hold_whitebelts and boat.hold_whitebelts[1] and #boat.hold_whitebelts[1] > 0 then
local connections = {
{1,1},
{2,2},
{3,3},
{4,4},
{5,5},
{6,6},
{15,13},
{16,14},
{17,15},
{18,16},
{19,17},
{20,18},
}
for _, c in pairs(connections) do
local b1 = boat.hold_whitebelts[1][c[1]]
local b2 = boat.deck_whitebelts[c[2]]
b1.connect_linked_belts(b2)
end
end
end
function Public.nth_hold_connect_linked_belts(nth) --assumes both are in standard lrtd order
local memory = Memory.get_crew_memory()
local boat = memory.boat
if boat.hold_whitebelts and boat.hold_whitebelts[nth-1] and #boat.hold_whitebelts[nth-1] > 0 and boat.hold_whitebelts[nth] and #boat.hold_whitebelts[nth] > 0 then
local connections
if nth % 2 == 0 then
if nth == 2 then
connections = {
{5,11},
{6,12},
{7,13},
{8,14},
}
for _, c in pairs(connections) do
local b1 = boat.hold_whitebelts[nth][c[1]]
local b2 = boat.hold_whitebelts[nth-1][c[2]]
b1.connect_linked_belts(b2)
end
else
connections = {
{5,5},
{6,6},
{7,7},
{8,8},
}
for _, c in pairs(connections) do
local b1 = boat.hold_whitebelts[nth][c[1]]
local b2 = boat.hold_whitebelts[nth-1][c[2]]
b1.connect_linked_belts(b2)
end
end
connections = {
{1,7},
{2,8},
{3,9},
{4,10},
}
for _, c in pairs(connections) do
local b1 = boat.hold_whitebelts[nth][c[1]]
local b2 = boat.hold_whitebelts[1][c[2]]
b1.connect_linked_belts(b2)
end
else
connections = {
{1,1},
{2,2},
{3,3},
{4,4},
}
for _, c in pairs(connections) do
local b1 = boat.hold_whitebelts[nth][c[1]]
local b2 = boat.hold_whitebelts[nth-1][c[2]]
b1.connect_linked_belts(b2)
end
connections = {
{5,7},
{6,8},
{7,9},
{8,10},
}
for _, c in pairs(connections) do
local b1 = boat.hold_whitebelts[nth][c[1]]
local b2 = boat.hold_whitebelts[1][c[2]]
b1.connect_linked_belts(b2)
end
end
end
end
function Public.terrain(args)
if args.p.x < Public.Data.width/2-5 and args.p.x > Public.Data.width/2-10 and args.p.y > Public.Data.height/2 - 2 then
args.tiles[#args.tiles + 1] = {name = 'water', position = args.p}
else
args.tiles[#args.tiles + 1] = {name = CoreData.static_boat_floor, position = args.p}
end
return nil
end
function Public.chunk_structures()
return nil
end
return Public

View File

@ -0,0 +1,391 @@
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
local Structures = require 'maps.pirates.structures.structures'
local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Ores = require 'maps.pirates.ores'
local _inspect = require 'utils.inspect'.inspect
local Public = {}
local enum = {
STANDARD = '1',
FIRST = '2',
WALKWAYS = '3',
RED_DESERT = '4',
RADIOACTIVE = '5',
STANDARD_VARIANT = '6',
HORSESHOE = '7',
SWAMP = '8',
MAZE = '9',
}
Public.enum = enum
function Public.place_water_tile(args)
if args.static_params and args.static_params.deepwater_terraingenframe_xposition and args.p.x <= args.static_params.deepwater_terraingenframe_xposition - 0.5
then
args.tiles[#args.tiles + 1] = {name = 'deepwater', position = args.p}
local fishrng = Math.random(350)
if fishrng == 350 then
args.entities[#args.entities + 1] = {name = 'fish', position = args.p}
end
return true
end
if not args.noise_generator['height'] then return end
local height_noise = args.noise_generator['height'](args.p)
if height_noise < 0 then
args.tiles[#args.tiles + 1] = {name = 'water', position = args.p}
local fishrng = Math.random(350)
if fishrng == 350 then
args.entities[#args.entities + 1] = {name = 'fish', position = args.p}
end
return true
end
return false
end
function Public.island_height_1(args)
local noise_name = 'height'
if not args.noise_generator[noise_name] then
args.noise_generator:addNoise(noise_name,
function(p)
local r2 = (p.x)^2 + (p.y)^2
local r = Math.sqrt(r2)
-- 'noise testing suite':
-- local height_noise
-- if args.noise_generator.forest then
-- height_noise = args.noise_generator.forest(p)
-- else return 0 end
-- local height_noise = args.noise_generator.height_background(p)
-- local height_noise = (
-- 1 - r/(args.noise_generator.radius{x = p.x/r, y = p.y/r})
-- )
local height_noise = (
1 - r/(args.noise_generator.radius{x = p.x/r, y = p.y/r})
) + args.noise_generator.height_background(p)
return height_noise
end)
end
return args.noise_generator[noise_name]
end
function Public.island_height_mostly_circular(args)
local noise_name = 'height'
if not args.noise_generator[noise_name] then
args.noise_generator:addNoise(noise_name,
function(p)
local r2 = (p.x)^2 + (p.y)^2
local r = Math.sqrt(r2)
-- 'noise testing suite':
-- local height_noise
-- if args.noise_generator.forest then
-- height_noise = args.noise_generator.forest(p)
-- else return 0 end
-- local height_noise = args.noise_generator.height_background(p)
-- local height_noise = (
-- 1 - r/(args.noise_generator.radius{x = p.x/r, y = p.y/r})
-- )
local height_noise = (
1 - r/(args.noise_generator.radius{x = p.x/r, y = p.y/r})
)
return height_noise
end)
end
return args.noise_generator[noise_name]
end
function Public.island_height_horseshoe(args)
local noise_name = 'height'
if not args.noise_generator[noise_name] then
args.noise_generator:addNoise(noise_name,
function(p)
local r12 = (p.x)^2 + (p.y)^2
local r1 = Math.sqrt(r12)
local offsetp = {x = p.x + 80, y = p.y}
local r22 = (offsetp.x)^2 + (offsetp.y)^2
local r2 = Math.sqrt(r22)
-- 'noise testing suite':
-- local height_noise
-- if args.noise_generator.forest then
-- height_noise = args.noise_generator.forest(p)
-- else return 0 end
-- local height_noise = args.noise_generator.height_background(p)
-- local height_noise = (
-- 1 - r/(args.noise_generator.radius{x = p.x/r, y = p.y/r})
-- )
local height_noise = (
1 - r1/(args.noise_generator.radius1{x = p.x/r1, y = p.y/r1})
) - Math.max(0,
1 - r2/(args.noise_generator.radius2{x = offsetp.x/r2, y = offsetp.y/r2})
) + args.noise_generator.height_background(p)
return height_noise
end)
end
return args.noise_generator[noise_name]
end
function Public.island_farness_1(args)
--on a scale from 0 to 1, how 'far' the point is from the boat dropoff point
if not args.static_params.width and args.static_params.islandcenter_position and args.static_params.terraingen_coordinates_offset then return end -- can only call after detailed static_params are generated
local noise_name = 'farness' --might as well remember as a noise just to memoize
if not args.noise_generator[noise_name] then
args.noise_generator:addNoise(noise_name,
function(p)
local island_width = args.static_params.width - 2*Math.abs(args.static_params.islandcenter_position.x)
local nexus_of_boredom = {x = args.static_params.terraingen_coordinates_offset.x + args.static_params.islandcenter_position.x - 2/5*island_width, y = args.static_params.terraingen_coordinates_offset.y + args.static_params.islandcenter_position.y}
local relativeradius2 = Math.distance(p, nexus_of_boredom)
local farness = Math.slopefromto(relativeradius2, island_width/12, island_width)
if p.x < nexus_of_boredom.x then
local num = Math.abs(nexus_of_boredom.y - p.y)
local denom = Math.abs(nexus_of_boredom.x - p.x)
if denom < 1 then denom = 1 end
farness = farness * Math.slopefromto(num/denom, 1, 5)
end
return farness
end)
end
return args.noise_generator[noise_name]
end
function Public.island_farness_horseshoe(args)
--on a scale from 0 to 1, how 'far' the point is from the boat dropoff point
--compared to first farness function this one is much more simply just distance from boat
if not args.static_params.width and args.static_params.islandcenter_position and args.static_params.terraingen_coordinates_offset then return end -- can only call after detailed static_params are generated
local noise_name = 'farness' --might as well remember as a noise just to memoize
if not args.noise_generator[noise_name] then
args.noise_generator:addNoise(noise_name,
function(p)
local island_width = args.static_params.width - 2*Math.abs(args.static_params.islandcenter_position.x)
local nexus_of_boredom = {x = args.static_params.terraingen_coordinates_offset.x + args.static_params.islandcenter_position.x - 1/6*island_width, y = args.static_params.terraingen_coordinates_offset.y + args.static_params.islandcenter_position.y}
local relativeradius2 = Math.distance(p, nexus_of_boredom)
local farness = Math.slopefromto(relativeradius2, island_width/12, 62/100*island_width)
return farness
end)
end
return args.noise_generator[noise_name]
end
function Public.enemies_1(args, spec, no_worms, worm_evo_bonus)
worm_evo_bonus = worm_evo_bonus or 0
for x = args.left_top.x, args.left_top.x + 31 do
for y = args.left_top.y, args.left_top.y + 31 do
local p = {x = x, y = y}
local spec2 = spec(p)
if spec2.placeable and Math.random() < spec2.density_perchunk/(32*32) then
local memory = Memory.get_crew_memory()
local enemy_force_name = memory.enemy_force_name
local rng = Math.random(10)
if rng >= 4 then
args.entities[#args.entities + 1] = {name = 'biter-spawner', position = p, force = enemy_force_name, indestructible = spec2.spawners_indestructible or false}
elseif rng >= 3 then
args.entities[#args.entities + 1] = {name = 'spitter-spawner', position = p, force = enemy_force_name, indestructible = spec2.spawners_indestructible or false}
elseif not no_worms then
local evolution = memory.evolution_factor + worm_evo_bonus
args.entities[#args.entities + 1] = {name = Common.get_random_worm_type(evolution + 0.05), position = p, force = enemy_force_name}
end
end
end
end
end
function Public.enemies_specworms_separate(args, spec)
for x = args.left_top.x, args.left_top.x + 31 do
for y = args.left_top.y, args.left_top.y + 31 do
local p = {x = x, y = y}
local spec2 = spec(p)
if spec2.placeable and Math.random() < spec2.spawners_density_perchunk/(32*32) then
local memory = Memory.get_crew_memory()
local enemy_force_name = memory.enemy_force_name
local rng = Math.random(10)
if rng >=8 then
args.entities[#args.entities + 1] = {name = 'spitter-spawner', position = p, force = enemy_force_name, indestructible = spec2.spawners_indestructible or false}
else
args.entities[#args.entities + 1] = {name = 'biter-spawner', position = p, force = enemy_force_name, indestructible = spec2.spawners_indestructible or false}
end
elseif spec2.placeable and Math.random() < spec2.worms_density_perchunk/(32*32) then
local memory = Memory.get_crew_memory()
local enemy_force_name = memory.enemy_force_name
local evolution = game.forces[enemy_force_name].evolution_factor
args.entities[#args.entities + 1] = {name = Common.get_random_worm_type(evolution + 0.05), position = p, force = enemy_force_name}
end
end
end
end
function Public.assorted_structures_1(args, spec)
local memory = Memory.get_crew_memory()
local overworldx = memory.overworldx or 0
local rng = Math.random()
local left_top = args.left_top
-- initial attempt, to avoid placing two structures too close to each other, is to divide up the map into 2x2 chonks, and spawn once in each
local bool1, bool2 = left_top.x % 64 < 32, left_top.y % 64 < 32
local all_four_chunks = {
{x = left_top.x, y = left_top.y},
{x = left_top.x + (bool1 and 32 or -32), y = left_top.y},
{x = left_top.x, y = left_top.y + (bool2 and 32 or -32)},
{x = left_top.x + (bool1 and 32 or -32), y = left_top.y + (bool2 and 32 or -32)},
}
if not args.other_map_generation_data.chunks_loaded then args.other_map_generation_data.chunks_loaded = {} end
local chunks_loaded = args.other_map_generation_data.chunks_loaded
if not chunks_loaded[args.left_top.x] then chunks_loaded[args.left_top.x] = {} end
chunks_loaded[args.left_top.x][args.left_top.y] = true
local nearby_chunks_generated_count = 0
for i=1,4 do
if chunks_loaded[all_four_chunks[i].x] and chunks_loaded[all_four_chunks[i].x][all_four_chunks[i].y] then
nearby_chunks_generated_count = nearby_chunks_generated_count + 1
end
end
if nearby_chunks_generated_count == 4 then --should trigger only once per 4 chunks
local avgleft_top = {
x = (all_four_chunks[1].x + all_four_chunks[4].x)/2,
y = (all_four_chunks[1].y + all_four_chunks[4].y)/2,
}
local leftmost_topmost = {
x = avgleft_top.x - 16,
y = avgleft_top.y - 16,
}
local spec2 = spec{x = avgleft_top.x + 16, y = avgleft_top.y + 16}
if rng < spec2.chanceper4chunks then
local rng2 = Math.random()
local struct
if overworldx <= 120 then
if rng2 < 20/100 then
struct = Structures.IslandStructures.ROC.lonely_storage_tank
elseif rng2 < 40/100 then
struct = Structures.IslandStructures.MATTISSO.small_crashed_ship
elseif rng2 < 50/100 then
struct = Structures.IslandStructures.MATTISSO.small_oilrig_base
elseif rng2 < 60/100 then
struct = Structures.IslandStructures.MATTISSO.small_abandoned_refinery
elseif rng2 < 70/100 then
struct = Structures.IslandStructures.MATTISSO.small_mining_base
else
struct = Structures.IslandStructures.MATTISSO.small_primitive_mining_base
end
elseif overworldx <= 240 then
if rng2 < 30/100 then
struct = Structures.IslandStructures.ROC.lonely_storage_tank
elseif rng2 < 40/100 then
struct = Structures.IslandStructures.MATTISSO.small_crashed_ship
elseif rng2 < 50/100 then
struct = Structures.IslandStructures.MATTISSO.small_oilrig_base
elseif rng2 < 70/100 then
struct = Structures.IslandStructures.MATTISSO.small_abandoned_refinery
elseif rng2 < 80/100 then
struct = Structures.IslandStructures.MATTISSO.small_mining_base
else
struct = Structures.IslandStructures.MATTISSO.small_solar_base
end
else
if rng2 < 10/100 then
struct = Structures.IslandStructures.ROC.lonely_storage_tank
elseif rng2 < 20/100 then
struct = Structures.IslandStructures.MATTISSO.small_crashed_ship
elseif rng2 < 40/100 then
struct = Structures.IslandStructures.MATTISSO.small_oilrig_base
elseif rng2 < 50/100 then
struct = Structures.IslandStructures.MATTISSO.small_abandoned_refinery
elseif rng2 < 60/100 then
struct = Structures.IslandStructures.MATTISSO.small_mining_base
elseif rng2 < 80/100 then
struct = Structures.IslandStructures.MATTISSO.small_solar_base
else
struct = Structures.IslandStructures.MATTISSO.small_roboport_base
end
end
if struct then
Structures.try_place(struct, args.specials, leftmost_topmost, 64, 64, function(p) return spec(p).placeable end)
end
end
end
end
function Public.random_rock_1(p)
local rock_raffle = {'sand-rock-big','sand-rock-big','rock-big','rock-big','rock-big','rock-big','rock-huge','rock-huge'}
local s_rock_raffle = #rock_raffle
return {name = rock_raffle[Math.random(1, s_rock_raffle)], position = p}
end
function Public.random_tree_1(p)
local tree_raffle = {
'tree-01',
'tree-02',
'tree-02-red',
'tree-03',
'tree-04',
'tree-05',
'tree-06',
'tree-06-brown',
'tree-07',
'tree-08',
'tree-08-brown',
'tree-08-red',
'tree-09',
'tree-09-brown',
'tree-09-red'
}
local s_tree_raffle = #tree_raffle
return {name = tree_raffle[Math.random(1, s_tree_raffle)], position = p}
end
return Public

View File

@ -0,0 +1,71 @@
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local Public = {}
Public.display_names = {'Fledgling Vale'}
Public.terraingen_frame_width = 325
Public.terraingen_frame_height = 325
Public.static_params_default = {
starting_time_of_day = 0,
daynightcycletype = 1,
boat_extra_distance_from_shore = 0,
-- boat_extra_distance_from_shore = 0.1 * Common.boat_default_starting_distance_from_shore,
default_decoratives = true,
base_starting_treasure = 2000,
base_starting_rock_material = 800,
base_starting_wood = 2400,
}
function Public.base_ores()
return {
['copper-ore'] = 1.9,
['iron-ore'] = 4.2,
['coal'] = 3.1,
['stone'] = 0.4,
}
end
local rscale = 125
Public.noiseparams = {
radius = {
type = 'simplex_2d',
normalised = false,
params = {
{wavelength = 0, amplitude = rscale * 1},
-- {wavelength = 2.5, amplitude = rscale * 0.1},
},
},
height_background = {
type = 'island1',
normalised = false,
params = {
-- {upperscale = 1000, amplitude = hscale * 200},
{upperscale = 600, amplitude = 0.15},
},
},
forest = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 70, amplitude = 1},
},
},
rock = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 120, amplitude = 1, seedfactor = 2},
},
},
}
return Public

View File

@ -0,0 +1,116 @@
-- local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
-- local Structures = require 'maps.pirates.structures.structures'
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
local Ores = require 'maps.pirates.ores'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
local Hunt = require 'maps.pirates.surfaces.islands.hunt'
local Public = {}
Public.Data = require 'maps.pirates.surfaces.islands.first.data'
function Public.noises(args)
local ret = {}
ret.height = IslandsCommon.island_height_1(args)
ret.forest = args.noise_generator.forest
ret.forest_abs = function (p) return Math.abs(ret.forest(p)) end
ret.forest_abs_suppressed = function (p) return ret.forest_abs(p) - 1 * Math.slopefromto(ret.height(p), 0.35, 0.1) end
ret.rock = args.noise_generator.rock
ret.rock_abs = function (p) return Math.abs(ret.rock(p)) end
ret.farness = IslandsCommon.island_farness_1(args) --isn't available on the iconized pass, only on actual generation; check args.iconized_generation before you use this
return ret
end
function Public.terrain(args)
local noises = Public.noises(args)
local p = args.p
if IslandsCommon.place_water_tile(args) then return end
if noises.height(p) < 0 then
args.tiles[#args.tiles + 1] = {name = 'water', position = args.p}
return
end
if noises.height(p) < 0.1 then
args.tiles[#args.tiles + 1] = {name = 'sand-1', position = args.p}
-- if args.specials and noises.farness(p) > 0.0001 and noises.farness(p) < 0.6 and Math.random(150) == 1 then
-- args.specials[#args.specials + 1] = {name = 'buried-treasure', position = args.p}
-- end
elseif noises.height(p) < 0.16 then
args.tiles[#args.tiles + 1] = {name = 'grass-4', position = args.p}
else
if noises.forest_abs_suppressed(p) > 0.5 and noises.rock(p) < 0.3 then
args.tiles[#args.tiles + 1] = {name = 'grass-3', position = args.p}
elseif noises.forest_abs_suppressed(p) > 0.2 and noises.rock(p) < 0.3 then
args.tiles[#args.tiles + 1] = {name = 'grass-2', position = args.p}
else
args.tiles[#args.tiles + 1] = {name = 'grass-1', position = args.p}
end
end
if noises.height(p) > 0.2 then
if noises.forest_abs(p) > 0.65 then
local treedensity = 0.4 * Math.slopefromto(noises.forest_abs_suppressed(p), 0.6, 0.85)
if noises.forest(p) > 0.87 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-01', position = args.p, visible_on_overworld = true} end
elseif noises.forest(p) < -1.4 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-03', position = args.p, visible_on_overworld = true} end
else
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-02', position = args.p, visible_on_overworld = true} end
end
end
end
if noises.forest_abs_suppressed(p) < 0.6 then
if noises.height(p) > 0.12 then
local rockdensity = 0.0018 * Math.slopefromto(noises.rock_abs(p), -0.15, 0.3)
local rockrng = Math.random()
if rockrng < rockdensity then
args.entities[#args.entities + 1] = IslandsCommon.random_rock_1(args.p)
elseif rockrng < rockdensity * 1.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-medium', position = args.p}
elseif rockrng < rockdensity * 2 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-small', position = args.p}
elseif rockrng < rockdensity * 2.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-tiny', position = args.p}
end
end
end
end
function Public.chunk_structures(args)
local spec = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.farness(p) > 0.4,
density_perchunk = 28 * Math.slopefromto(noises.farness(p), 0.4, 1)^2,
}
end
IslandsCommon.enemies_1(args, spec, false, 0.4)
end
function Public.break_rock(surface, p, entity_name)
return Ores.try_ore_spawn(surface, p, entity_name, 8)
end
function Public.generate_silo_setup_position()
return Hunt.silo_setup_position()
end
return Public

View File

@ -0,0 +1,92 @@
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local Public = {}
Public.display_names = {'Shark Keys', 'Little Keys', 'Little Keys'}
Public.terraingen_frame_width = 896
Public.terraingen_frame_height = 896
Public.static_params_default = {
starting_time_of_day = 0,
daynightcycletype = 1,
default_decoratives = true,
base_starting_treasure = 1000,
base_starting_rock_material = 800,
base_starting_wood = 1200,
base_starting_treasure_maps = 0,
}
function Public.base_ores()
return {
['copper-ore'] = 2.4,
['iron-ore'] = 3.9,
['coal'] = 3.6,
['stone'] = 0.8,
}
end
local rscale1 = 240
local rscale2 = 210
local hscale = 0.1
Public.noiseparams = {
radius1 = {
type = 'simplex_2d',
normalised = false,
params = {
{wavelength = 0, amplitude = rscale1 * 1},
{wavelength = 1.6, amplitude = rscale1 * 0.2},
},
},
radius2 = {
type = 'simplex_2d',
normalised = false,
params = {
{wavelength = 0, amplitude = rscale2 * 1},
{wavelength = 1.6, amplitude = rscale2 * 0.2},
},
},
height_background = {
type = 'island1',
normalised = false,
params = {
-- {upperscale = 1000, amplitude = hscale * 200},
{upperscale = 1600, amplitude = hscale * 1},
{upperscale = 80, amplitude = hscale * 0.1},
},
},
forest = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 90, amplitude = 1},
-- {upperscale = 0, amplitude = 0.15},
},
},
rock = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 110, amplitude = 1, seedfactor = 2},
},
},
mood = {
type = 'simplex_2d',
normalised = true,
params = {
{wavelength = 250, amplitude = 70},
{wavelength = 50, amplitude = 20},
},
},
}
return Public

View File

@ -0,0 +1,148 @@
-- local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
-- local Structures = require 'maps.pirates.structures.structures'
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
local Ores = require 'maps.pirates.ores'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
local Hunt = require 'maps.pirates.surfaces.islands.hunt'
local Public = {}
Public.Data = require 'maps.pirates.surfaces.islands.horseshoe.data'
function Public.noises(args)
local ret = {}
ret.height = IslandsCommon.island_height_horseshoe(args)
ret.forest = args.noise_generator.forest
ret.forest_abs = function (p) return Math.abs(ret.forest(p)) end
ret.forest_abs_suppressed = function (p) return ret.forest_abs(p) - 2 * Math.slopefromto(ret.height(p), 0.2, 0.12) end
ret.rock = args.noise_generator.rock
ret.rock_abs = function (p) return Math.abs(ret.rock(p)) end
ret.mood = args.noise_generator.mood
ret.farness = IslandsCommon.island_farness_horseshoe(args) --isn't available on the iconized pass, only on actual generation; check args.iconized_generation before you use this
return ret
end
function Public.terrain(args)
local noises = Public.noises(args)
local p = args.p
if IslandsCommon.place_water_tile(args) then return end
if noises.height(p) < 0 then
args.tiles[#args.tiles + 1] = {name = 'water', position = args.p}
return
end
if noises.height(p) < 0.05 then
args.tiles[#args.tiles + 1] = {name = 'sand-1', position = args.p}
if (not args.iconized_generation) and noises.farness(p) > 0.02 and noises.farness(p) < 0.6 and Math.random(500) == 1 then
args.specials[#args.specials + 1] = {name = 'buried-treasure', position = args.p}
end
elseif noises.height(p) < 0.12 then
args.tiles[#args.tiles + 1] = {name = 'sand-2', position = args.p}
else
if noises.forest_abs_suppressed(p) > 0.3 and noises.rock(p) < -0.1 then
args.tiles[#args.tiles + 1] = {name = 'dirt-1', position = args.p}
else
if noises.mood(p) > 0.66 then
args.tiles[#args.tiles + 1] = {name = 'water', position = args.p}
if Math.random(100) == 1 then
args.entities[#args.entities + 1] = {name = 'fish', position = args.p}
end
else
args.tiles[#args.tiles + 1] = {name = 'sand-3', position = args.p}
end
end
end
if (not args.iconized_generation) and noises.height(p) > 0 and Math.random(6000) == 1 then --but has lots of chests due to spawning anywhere
args.specials[#args.specials + 1] = {name = 'chest', position = args.p}
elseif noises.height(p) > 0.02 then
if noises.forest_abs_suppressed(p) > 0.58 then
local forest_noise = noises.forest(p)
local treedensity
if forest_noise > 0 then
treedensity = 0.5 * Math.slopefromto(noises.forest_abs_suppressed(p), 0.58, 0.75)
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-06', position = args.p, visible_on_overworld = true} end
elseif noises.forest_abs_suppressed(p) > 0.68 then
treedensity = 0.5 * Math.slopefromto(forest_noise, -0.7, -0.75)
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-08-brown', position = args.p, visible_on_overworld = true} end
end
end
end
if noises.forest_abs_suppressed(p) < 0.45 then
if noises.height(p) > 0.05 then
if noises.rock_abs(p) > 0.15 then
local rockdensity = 1/500 * Math.slopefromto(noises.rock_abs(p), 0.15, 0.5)
if noises.height(p) < 0.12 then rockdensity = rockdensity * 3 end
local rockrng = Math.random()
if rockrng < rockdensity then
args.entities[#args.entities + 1] = IslandsCommon.random_rock_1(args.p)
elseif rockrng < rockdensity * 1.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-medium', position = args.p}
elseif rockrng < rockdensity * 2 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-small', position = args.p}
elseif rockrng < rockdensity * 2.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-tiny', position = args.p}
end
end
end
end
if noises.height(p) > 0.18 and noises.mood(p) > 0.2 then
if noises.forest_abs(p) < 0.2 and noises.rock_abs(p) > 1.8 then
args.entities[#args.entities + 1] = {name = 'coal', position = args.p, amount = 8}
end
end
end
function Public.chunk_structures(args)
local spec = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.farness(p) > 0.35,
spawners_indestructible = false,
-- spawners_indestructible = noises.farness(p) > 0.7,
density_perchunk = 10 * Math.slopefromto(noises.mood(p), 0.12, -0.18) * Math.slopefromto(noises.farness(p), 0.35, 1) * args.biter_base_density_scale,
}
end
IslandsCommon.enemies_1(args, spec)
-- local spec2 = function(p)
-- local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
-- return {
-- placeable = noises.height(p) >= 0 and noises.forest_abs_suppressed(p) < 0.3 + Math.max(0, 0.2 - noises.height(p)),
-- chanceper4chunks = 0.5 * Math.slopefromto(noises.farness(p), 0.1, 0.4) * Math.slopefromto(noises.mood(p), 0, 0.25),
-- }
-- end
-- IslandsCommon.assorted_structures_1(args, spec2)
end
function Public.break_rock(surface, p, entity_name)
return Ores.try_ore_spawn(surface, p, entity_name)
end
function Public.generate_silo_setup_position()
return Hunt.silo_setup_position(0, 30)
end
return Public

View File

@ -0,0 +1,338 @@
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local Utils = require 'maps.pirates.utils_local'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
local _inspect = require 'utils.inspect'.inspect
local Public = {}
-- two things to 'hunt':
-- treasure map for X
-- quest treasure
function Public.silo_setup_position(x_fractional_offset, x_absolute_offset)
x_absolute_offset = x_absolute_offset or 0
x_fractional_offset = x_fractional_offset or 0
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
local boatposition = memory.boat.position
local island_center = destination.static_params.islandcenter_position
local difficulty_offset = (1 - Common.difficulty()) * 20 or 0
local silo_count = Balance.silo_count()
local p = {
x = Math.min(
Math.floor(boatposition.x + difficulty_offset + (island_center.x - boatposition.x) * 3/5) - 0.5,
Math.floor(boatposition.x + 175) - 0.5
) + (island_center.x - boatposition.x) * x_fractional_offset + x_absolute_offset,
y = Math.floor(boatposition.y + (island_center.y - boatposition.y) * 3/5) - 0.5
}
local tries = 0
local p_ret = nil
local p2
while p_ret == nil and tries < 200 do
p2 = {x = p.x + Math.random(-30, 0), y = p.y + Math.random(-70, 70)}
if p2.x >= boatposition.x+5 and Common.can_place_silo_setup(surface, p2, silo_count) then p_ret = p2 end
tries = tries + 1
end
while p_ret == nil and tries < 400 do
p2 = {x = p.x + Math.random(-60, 10), y = p.y + Math.random(-90, 90)}
if p2.x >= boatposition.x+5 and Common.can_place_silo_setup(surface, p2, silo_count, true) then p_ret = p2 end
tries = tries + 1
end
while p_ret == nil and tries < 1200 do
p2 = {x = p.x + Math.random(-90, 20), y = p.y + Math.random(-130, 130)}
if p2.x >= boatposition.x+5 and Common.can_place_silo_setup(surface, p2, silo_count, true) then p_ret = p2 end
tries = tries + 1
end
-- if _DEBUG then
if p_ret == nil then
log("No good position found after 1200 tries")
p_ret = p
else
log(string.format("Silo position generated after %f tries: %f, %f", tries, p_ret.x, p_ret.y))
end
-- end
Common.ensure_chunks_at(surface, p_ret, 1)
return p_ret
end
function Public.mid_farness_position_1(args, points_to_avoid)
points_to_avoid = points_to_avoid or {}
-- local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
local island_center = destination.static_params.islandcenter_position
local width = destination.static_params.width
local height = destination.static_params.height
local tries = 0
local p_ret = nil
local p2
while p_ret == nil and tries < 400 do
p2 = {x = island_center.x + Math.random(Math.ceil(-width/2), Math.ceil(width/2)), y = island_center.y + Math.random(Math.ceil(-height/2), Math.ceil(height/2))}
Common.ensure_chunks_at(surface, p2, 0.01)
local tile = surface.get_tile(p2)
if tile and tile.valid and tile.name then
if (not Utils.contains(CoreData.tiles_that_conflict_with_resource_layer, tile.name)) and (not Utils.contains(CoreData.edgemost_tile_names, tile.name)) then
local p3 = {x = p2.x + args.static_params.terraingen_coordinates_offset.x, y = p2.y + args.static_params.terraingen_coordinates_offset.y}
if IslandsCommon.island_farness_1(args)(p3) > 0.1 and IslandsCommon.island_farness_1(args)(p3) < 0.8 then
local allowed = true
for _, pa in pairs(points_to_avoid) do
if Math.distance({x = pa.x, y = pa.y}, p2) < pa.r then
allowed = false
end
end
if allowed then
p_ret = p2
end
end
end
end
tries = tries + 1
end
if _DEBUG then
if p_ret == nil then
log("No good mid_farness_position_1 position found after 500 tries")
-- p_ret = {x = 0, y = 0}
else
log(string.format("mid_farness_position_1 Position found after %f tries: %f, %f", tries, p_ret.x, p_ret.y))
end
end
return p_ret
end
function Public.close_position_try_avoiding_entities(args, points_to_avoid, farness_boost_low, farness_boost_high)
farness_boost_low = farness_boost_low or 0
farness_boost_high = farness_boost_high or 0
points_to_avoid = points_to_avoid or {}
-- local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
local island_center = destination.static_params.islandcenter_position
local width = destination.static_params.width
local height = destination.static_params.height
local tries = 0
local p_ret = nil
local p2
while p_ret == nil and tries < 700 do
p2 = {x = island_center.x + Math.random(Math.ceil(-width/2), 0), y = island_center.y + Math.random(Math.ceil(-height/3), Math.ceil(height/3))}
Common.ensure_chunks_at(surface, p2, 0.01)
local tile = surface.get_tile(p2)
if tile and tile.valid and tile.name then
if (not Utils.contains(CoreData.tiles_that_conflict_with_resource_layer, tile.name)) and (not Utils.contains(CoreData.edgemost_tile_names, tile.name)) then
local p3 = {x = p2.x + args.static_params.terraingen_coordinates_offset.x, y = p2.y + args.static_params.terraingen_coordinates_offset.y}
if IslandsCommon.island_farness_1(args)(p3) > 0.06 + farness_boost_low and IslandsCommon.island_farness_1(args)(p3) < 0.19 + farness_boost_high then
local allowed = true
if tries < 40 and #surface.find_entities({{p2.x - 8, p2.y - 8}, {p2.x + 8, p2.y + 8}}) > 0 then
allowed = false
end
if tries >= 40 and tries < 100 and #surface.find_entities({{p2.x - 6, p2.y - 6}, {p2.x + 6, p2.y + 6}}) > 0 then
allowed = false
end
if tries >= 100 and tries < 200 and #surface.find_entities({{p2.x - 3, p2.y - 3}, {p2.x + 3, p2.y + 3}}) > 0 then
allowed = false
end
for _, pa in pairs(points_to_avoid) do
if allowed and Math.distance({x = pa.x, y = pa.y}, p2) < pa.r then
allowed = false
end
end
if allowed then
p_ret = p2
end
end
end
end
tries = tries + 1
end
if _DEBUG then
if p_ret == nil then
log("No good close_position_try_avoiding_entities found after 500 tries")
-- p_ret = {x = 0, y = 0}
else
log(string.format("close_position_try_avoiding_entities found after %f tries: %f, %f", tries, p_ret.x, p_ret.y))
end
end
return p_ret
end
function Public.position_away_from_players_1(_, radius)
radius = radius or 60
-- local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
local island_center = destination.static_params.islandcenter_position
local width = destination.static_params.width
local height = destination.static_params.height
local tries = 0
local p_ret = nil
local p2
while p_ret == nil and tries < 500 do
p2 = {x = island_center.x + Math.random(Math.ceil(-width/2), Math.ceil(width/2)), y = island_center.y + Math.random(Math.ceil(-height/2), Math.ceil(height/2))}
Common.ensure_chunks_at(surface, p2, 0.01)
-- local p3 = {x = p2.x + args.static_params.terraingen_coordinates_offset.x, y = p2.y + args.static_params.terraingen_coordinates_offset.y}
local tile = surface.get_tile(p2)
if tile and tile.valid and tile.name then
if not Utils.contains(CoreData.tiles_that_conflict_with_resource_layer, tile.name) then
local nearby_characters = surface.find_entities_filtered{position = p2, radius = radius, name = 'character'}
if (not nearby_characters) or (#nearby_characters == 0) then
p_ret = p2
end
end
end
tries = tries + 1
end
if _DEBUG then
if p_ret == nil then
log("No good position found after 500 tries")
-- p_ret = {x = 0, y = 0}
else
log(string.format("Position found after %f tries: %f, %f", tries, p_ret.x, p_ret.y))
end
end
return p_ret
end
function Public.merchant_ship_position()
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
local island_center = destination.static_params.islandcenter_position
local width = destination.static_params.width
local height = destination.static_params.height
local right_boundary = island_center.x + width/2
local to_try = {}
for i = -height/2, height/2, 10 do
to_try[#to_try + 1] = i
end
Math.shuffle(to_try)
local last_reasonable_position
local p_ret
for _, h in ipairs(to_try) do
local right_boundary_p = {x = right_boundary, y = h}
Common.ensure_chunks_at(surface, right_boundary_p, 10)
local i = 0
while i < 300 and (not p_ret) do
i = i + 1
local p2 = {x = right_boundary - i, y = h}
local tile = surface.get_tile(p2)
if i < 32 then
if not Utils.contains(CoreData.tiles_that_conflict_with_resource_layer, tile.name) then
break
end
else
if not Utils.contains(CoreData.tiles_that_conflict_with_resource_layer, tile.name) then
local area = {{p2.x - 40, p2.y - 11},{p2.x + 4, p2.y + 11}}
local spawners = surface.find_entities_filtered({type = 'unit-spawner', force = memory.enemy_force_name, area = area})
local worms = surface.find_entities_filtered({type = 'turret', force = memory.enemy_force_name, area = area})
if #spawners == 0 and #worms == 0 then
p_ret = p2
else
last_reasonable_position = p2
end
break
end
end
end
end
if _DEBUG then
if p_ret == nil then
log("No good position found for merchant ship")
-- p_ret = {x = 0, y = 0}
else
log(string.format("Merchant ship position found: %f, %f", p_ret.x, p_ret.y))
end
end
return p_ret or last_reasonable_position
end
return Public

View File

@ -0,0 +1,409 @@
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
local Balance = require 'maps.pirates.balance'
local Structures = require 'maps.pirates.structures.structures'
local Boats = require 'maps.pirates.structures.boats.boats'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local Utils = require 'maps.pirates.utils_local'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
local Hunt = require 'maps.pirates.surfaces.islands.hunt'
local Ores = require 'maps.pirates.ores'
local Quest = require 'maps.pirates.quest'
local _inspect = require 'utils.inspect'.inspect
local Token = require 'utils.token'
local Task = require 'utils.task'
local Public = {}
local enum = IslandsCommon.enum
Public.enum = enum
Public[enum.FIRST] = require 'maps.pirates.surfaces.islands.first.first'
Public[enum.STANDARD] = require 'maps.pirates.surfaces.islands.standard.standard'
Public[enum.STANDARD_VARIANT] = require 'maps.pirates.surfaces.islands.standard_variant.standard_variant'
Public[enum.WALKWAYS] = require 'maps.pirates.surfaces.islands.walkways.walkways'
Public[enum.RADIOACTIVE] = require 'maps.pirates.surfaces.islands.radioactive.radioactive'
Public[enum.RED_DESERT] = require 'maps.pirates.surfaces.islands.red_desert.red_desert'
Public[enum.HORSESHOE] = require 'maps.pirates.surfaces.islands.horseshoe.horseshoe'
Public[enum.SWAMP] = require 'maps.pirates.surfaces.islands.swamp.swamp'
Public[enum.MAZE] = require 'maps.pirates.surfaces.islands.maze.maze'
Public['IslandsCommon'] = require 'maps.pirates.surfaces.islands.common'
local function render_silo_hp()
-- local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
if not (destination.dynamic_data.rocketsilos and destination.dynamic_data.rocketsilos[1] and destination.dynamic_data.rocketsilos[1].valid) then return end
destination.dynamic_data.rocketsilohptext = rendering.draw_text{
text = 'HP: ' .. destination.dynamic_data.rocketsilohp .. ' / ' .. destination.dynamic_data.rocketsilomaxhp,
surface = surface,
target = destination.dynamic_data.rocketsilos[1],
target_offset = {0, 4.5},
color = {0, 255, 0},
scale = 1.20,
font = 'default-game',
alignment = 'center',
scale_with_zoom = true
}
end
function Public.spawn_treasure_maps(destination, points_to_avoid)
points_to_avoid = points_to_avoid or {}
-- local memory = Memory.get_crew_memory()
local surface = game.surfaces[destination.surface_name]
if not surface and surface.valid then return end
local num = destination.static_params.starting_treasure_maps
if not destination.dynamic_data.treasure_maps then destination.dynamic_data.treasure_maps = {} end
local args = {
static_params = destination.static_params,
noise_generator = Utils.noise_generator({}, 0),
}
for i = 1, num do
local map = {}
local p = Hunt.mid_farness_position_1(args, points_to_avoid)
map.position = p
map.mapobject_rendering = rendering.draw_sprite{
surface = surface,
target = p,
sprite = 'utility/gps_map_icon',
render_layer = '125',
x_scale = 2.4,
y_scale = 2.4,
}
map.state = 'on_ground'
map.x_renderings = nil
map.buried_treasure_position = nil
destination.dynamic_data.treasure_maps[#destination.dynamic_data.treasure_maps + 1] = map
end
end
function Public.spawn_ghosts(destination, points_to_avoid)
points_to_avoid = points_to_avoid or {}
-- local memory = Memory.get_crew_memory()
local surface = game.surfaces[destination.surface_name]
if not surface and surface.valid then return end
if not (destination.dynamic_data and destination.dynamic_data.quest_type and destination.dynamic_data.quest_type == Quest.enum.FIND) then return end
local num = destination.dynamic_data.quest_progressneeded
if not destination.dynamic_data.ghosts then destination.dynamic_data.ghosts = {} end
local args = {
static_params = destination.static_params,
noise_generator = Utils.noise_generator({}, 0),
}
for i = 1, num do
local ghost = {}
local p = Hunt.mid_farness_position_1(args, points_to_avoid)
ghost.position = p
ghost.ghostobject_rendering = rendering.draw_sprite{
surface = surface,
target = p,
sprite = 'utility/ghost_time_to_live_modifier_icon',
render_layer = '125',
x_scale = 1,
y_scale = 1,
}
ghost.state = 'on_ground'
destination.dynamic_data.ghosts[#destination.dynamic_data.ghosts + 1] = ghost
end
end
function Public.spawn_covered(destination, points_to_avoid)
points_to_avoid = points_to_avoid or {}
-- local memory = Memory.get_crew_memory()
local surface = game.surfaces[destination.surface_name]
if not surface and surface.valid then return end
local args = {
static_params = destination.static_params,
noise_generator = Utils.noise_generator({}, 0),
}
local p
for i = 1, 1 do
p = Hunt.mid_farness_position_1(args, points_to_avoid)
local structureData = Structures.IslandStructures.ROC.covered1.Data
local special = {
position = p,
components = structureData.components,
width = structureData.width,
height = structureData.height,
name = structureData.name,
}
if not destination.dynamic_data.structures_waiting_to_be_placed then
destination.dynamic_data.structures_waiting_to_be_placed = {}
end
destination.dynamic_data.structures_waiting_to_be_placed[#destination.dynamic_data.structures_waiting_to_be_placed + 1] = {data = special, tick = game.tick}
local requirement = destination.dynamic_data.covered1_requirement.price
local rendering1 = rendering.draw_text{
surface = surface,
target = {x = p.x + 4, y = p.y + 6.85},
color = CoreData.colors.renderingtext_green,
scale = 1.5,
font = 'default-game',
alignment = 'right',
}
local rendering2 = rendering.draw_sprite{
sprite = 'item/' .. requirement.name,
surface = surface,
target = {x = p.x + 4.85, y = p.y + 7.5},
x_scale = 1.5,
y_scale = 1.5
}
destination.dynamic_data.covered_data = {
position = p,
state = 'covered',
requirement = requirement,
rendering1 = rendering1,
rendering2 = rendering2,
}
log('covered market position: ' .. p.x .. ', ' .. p.y)
end
return p
end
function Public.spawn_ores_on_arrival(destination, points_to_avoid)
points_to_avoid = points_to_avoid or {}
-- local memory = Memory.get_crew_memory()
local surface = game.surfaces[destination.surface_name]
if not surface and surface.valid then return end
if (destination.subtype and (destination.subtype == enum.STANDARD or destination.subtype == enum.STANDARD_VARIANT or destination.subtype == enum.MAZE)) then
local ores = {'iron-ore', 'copper-ore', 'stone', 'coal', 'crude-oil'}
local args = {
static_params = destination.static_params,
noise_generator = Utils.noise_generator({}, 0),
}
local farness_boost_low, farness_boost_high = 0, 0
if destination.subtype == enum.MAZE then
farness_boost_low = 0.08
farness_boost_high = 0.25
end
for _, ore in pairs(ores) do
if destination.static_params.abstract_ore_amounts[ore] then
local p = Hunt.close_position_try_avoiding_entities(args, points_to_avoid, farness_boost_low, farness_boost_high)
if p then points_to_avoid[#points_to_avoid + 1] = {x=p.x, y=p.y, r=11} end
if ore == 'crude-oil' then
local count = Math.max(1, Math.ceil((destination.static_params.abstract_ore_amounts[ore]/3)^(1/2)))
local amount = Common.oil_abstract_to_real(destination.static_params.abstract_ore_amounts[ore]) / count
for i = 1, count do
local p2 = {p.x + Math.random(-7, 7), p.y + Math.random(-7, 7)}
local whilesafety = 0
while (not surface.can_place_entity{name = 'crude-oil', position = p2}) and whilesafety < 30 do
p2 = {p.x + Math.random(-7, 7), p.y + Math.random(-7, 7)}
whilesafety = whilesafety + 1
end
surface.create_entity{name = 'crude-oil', position = p2, amount = amount}
end
destination.dynamic_data.ore_types_spawned[ore] = true
else
local amount = Common.ore_abstract_to_real(destination.static_params.abstract_ore_amounts[ore])
local placed = Ores.draw_noisy_ore_patch(surface, p, ore, amount, 10000, 30, true, true)
if placed > 0 then
destination.dynamic_data.ore_types_spawned[ore] = true
end
end
end
end
end
end
function Public.spawn_merchant_ship(destination)
-- local memory = Memory.get_crew_memory()
local surface = game.surfaces[destination.surface_name]
if not surface and surface.valid then return end
-- local args = {
-- static_params = destination.static_params,
-- noise_generator = Utils.noise_generator({}, 0),
-- }
-- local p = Hunt.merchant_ship_position(args)
local p = Hunt.merchant_ship_position()
if p then
local boat = {
state = Boats.enum_state.LANDED,
type = Boats.enum.MERCHANT,
position = p,
force_name = 'environment',
surface_name = surface.name,
market = nil,
}
Boats.place_landingtrack(boat, CoreData.landing_tile, true)
Boats.place_boat(boat, CoreData.static_boat_floor, true, true, true)
destination.dynamic_data.merchant_market = boat.market
return boat.market
end
end
local silo_chart_tag = Token.register(
function(data)
local p_silo = data.p_silo
local surface_name = data.surface_name
local surface = game.surfaces[surface_name]
if not surface and surface.valid then return end
Memory.set_working_id(data.crew_id)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
local destination = Common.current_destination()
local force = memory.force
destination.dynamic_data.silo_chart_tag = force.add_chart_tag(surface, {icon = {type = 'item', name = 'rocket-silo'}, position = p_silo})
end
)
function Public.spawn_silo_setup()
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
local subtype = destination.subtype
local force = memory.force
local p_silo = Public[subtype].generate_silo_setup_position()
if not p_silo then return end
-- log(string.format("placing silo at x=%f, y = %f", p_silo.x, p_silo.y))
local silo_count = Balance.silo_count()
if not (silo_count and silo_count >= 1) then return end
if _DEBUG then
if silo_count >= 2 then game.print('debug - silo count: ' .. silo_count) end
end
for i=1,silo_count do
local silo = surface.create_entity({name = 'rocket-silo', position = {p_silo.x + 9*(i-1), p_silo.y}, force = force, create_build_effect_smoke = false})
if silo and silo.valid then
if not destination.dynamic_data.rocketsilos then destination.dynamic_data.rocketsilos = {} end
destination.dynamic_data.rocketsilos[#destination.dynamic_data.rocketsilos + 1]= silo
silo.minable = false
silo.rotatable = false
silo.operable = false
if i == 1 then
silo.auto_launch = true
else
silo.destructible = false
end
local modulesinv = silo.get_module_inventory()
modulesinv.insert{name = 'productivity-module-3', count = 4}
end
end
-- local substation = surface.create_entity({name = 'substation', position = {x = p_silo.x - 8.5, y = p_silo.y - 0.5}, force = force, create_build_effect_smoke = false})
-- if substation and substation.valid then
-- substation.destructible = false
-- substation.minable = false
-- substation.rotatable = false
-- end
-- local eei = surface.create_entity({name = 'electric-energy-interface', position = {x = p_silo.x - 8.5, y = p_silo.y + 1.5}, force = force, create_build_effect_smoke = false})
-- if eei and eei.valid then
-- memory.islandeei = eei
-- eei.destructible = false
-- eei.minable = false
-- eei.rotatable = false
-- eei.operable = false
-- eei.electric_buffer_size = memory.islandeeijoulesperrocket / 100
-- eei.power_production = 0
-- eei.power_usage = 0
-- end
if CoreData.rocket_silo_death_causes_loss or (destination.static_params and destination.static_params.base_cost_to_undock and destination.static_params.base_cost_to_undock['launch_rocket'] and destination.static_params.base_cost_to_undock['launch_rocket'] == true) then
-- we need to know where it is
force.chart(surface, {{p_silo.x - 4, p_silo.y - 4},{p_silo.x + 4, p_silo.y + 4}})
Task.set_timeout_in_ticks(2, silo_chart_tag, {p_silo = p_silo, surface_name = destination.surface_name, crew_id = memory.id})
end
render_silo_hp()
return p_silo
end
function Public.spawn_enemy_boat(type)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
local offsets = {50, -50, 63, -63}
local enemyboats = memory.enemyboats
if enemyboats then
local boat = {
state = Boats.enum_state.APPROACHING,
type = type,
speed = 4.5,
position = {x = - surface.map_gen_settings.width/2 + 17.5, y = (memory.boat.dockedposition or memory.boat.position).y + offsets[Math.random(4)]},
force_name = memory.enemy_force_name,
surface_name = surface.name,
unit_group = nil,
spawner = nil,
}
enemyboats[#enemyboats + 1] = boat
Boats.place_boat(boat, CoreData.static_boat_floor, true, true)
local e = surface.create_entity({name = 'biter-spawner', force = boat.force_name, position = {boat.position.x + Boats.get_scope(boat).Data.spawn_point.x, boat.position.y + Boats.get_scope(boat).Data.spawn_point.y}})
if e and e.valid then
-- e.destructible = false
boat.spawner = e
Common.new_healthbar(false, e, 350, nil, 350, 0.3)
end
return enemyboats[#enemyboats]
end
end
return Public

View File

@ -0,0 +1,57 @@
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local Public = {}
Public.display_names = {'Bewildering Maze'}
Public.terraingen_frame_width = 896
Public.terraingen_frame_height = 896
Public.static_params_default = {
starting_time_of_day = 0,
daynightcycletype = 1,
default_decoratives = true,
base_starting_treasure = 1000,
base_starting_rock_material = 800,
base_starting_wood = 1200,
base_starting_treasure_maps = 3,
}
function Public.base_ores()
return {
['copper-ore'] = 3.5,
['iron-ore'] = 6.5,
['coal'] = 4.0,
['stone'] = 2.0,
['crude-oil'] = 25,
}
end
local rscale = 240
-- local hscale = 0.1
Public.noiseparams = {
radius = {
type = 'simplex_2d',
normalised = false,
params = {
{wavelength = 0, amplitude = rscale * 1},
{wavelength = 1.6, amplitude = rscale * 0.15},
},
},
maze = {
type = 'simplex_2d',
normalised = true,
params = {
{wavelength = 250, amplitude = 70},
{wavelength = 50, amplitude = 20},
},
},
}
return Public

View File

@ -0,0 +1,264 @@
-- local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
local Structures = require 'maps.pirates.structures.structures'
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
-- local Ores = require 'maps.pirates.ores'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
local Hunt = require 'maps.pirates.surfaces.islands.hunt'
local Public = {}
Public.Data = require 'maps.pirates.surfaces.islands.maze.data'
function Public.noises(args)
local ret = {}
ret.height = IslandsCommon.island_height_mostly_circular(args)
ret.maze = args.noise_generator.maze
ret.farness = IslandsCommon.island_farness_1(args) --isn't available on the iconized pass, only on actual generation; check args.iconized_generation before you use this
return ret
end
local function maze_wall(args)
args.tiles[#args.tiles + 1] = {name = 'grass-2', position = args.p}
if Math.random(1, 2) == 1 then
args.entities[#args.entities + 1] = IslandsCommon.random_rock_1(args.p)
else
local e = IslandsCommon.random_tree_1(args.p)
e.visible_on_overworld = true
args.entities[#args.entities + 1] = e
end
end
local maze_scale = 24
local steps_orthogonal = {
{x = 0, y = -maze_scale},
{x = -maze_scale, y = 0},
{x = maze_scale, y = 0},
{x = 0, y = maze_scale}
}
local steps_diagonal = {
{diagonal = {x = -maze_scale, y = maze_scale}, connection_1 = {x = -maze_scale, y = 0}, connection_2 = {x = 0, y = maze_scale}},
{diagonal = {x = maze_scale, y = -maze_scale}, connection_1 = {x = maze_scale, y = 0}, connection_2 = {x = 0, y = -maze_scale}},
{diagonal = {x = maze_scale, y = maze_scale}, connection_1 = {x = maze_scale, y = 0}, connection_2 = {x = 0, y = maze_scale}},
{diagonal = {x = -maze_scale, y = -maze_scale}, connection_1 = {x = -maze_scale, y = 0}, connection_2 = {x = 0, y = -maze_scale}}
}
local function get_path_connections_count(lab_cells, p)
local connections = 0
for _, m in pairs(steps_orthogonal) do
if lab_cells[tostring(p.x + m.x) .. '_' .. tostring(p.y + m.y)] then
connections = connections + 1
end
end
return connections
end
local function labyrinth_determine_walkable_cell(args)
-- local noises = Public.noises(args)
-- local mazenoise = noises.maze()
local reduced_p = {x = args.true_p.x - (args.true_p.x % maze_scale), y = args.true_p.y - (args.true_p.y % maze_scale)}
if not args.other_map_generation_data.labyrinth_cells then
args.other_map_generation_data.labyrinth_cells = {}
end
local lab_cells = args.other_map_generation_data.labyrinth_cells
if lab_cells[tostring(reduced_p.x) .. '_' .. tostring(reduced_p.y)] == true then
return true
elseif lab_cells[tostring(reduced_p.x) .. '_' .. tostring(reduced_p.y)] == false then
return false
else
-- presumptive
lab_cells[tostring(reduced_p.x) .. '_' .. tostring(reduced_p.y)] = false
for _, modifier in pairs(steps_diagonal) do
if lab_cells[tostring(reduced_p.x + modifier.diagonal.x) .. '_' .. tostring(reduced_p.y + modifier.diagonal.y)] then
local connection_1 = lab_cells[tostring(reduced_p.x + modifier.connection_1.x) .. '_' .. tostring(reduced_p.y + modifier.connection_1.y)]
local connection_2 = lab_cells[tostring(reduced_p.x + modifier.connection_2.x) .. '_' .. tostring(reduced_p.y + modifier.connection_2.y)]
if not connection_1 and not connection_2 then
return false --sensible corners
end
end
end
local max_connections = 2
if Math.random(4) == 1 then max_connections = 3 end
for _, m in pairs(steps_orthogonal) do
if get_path_connections_count(lab_cells, {x = reduced_p.x + m.x, y = reduced_p.y + m.y}) >= max_connections then
return false
end
end
if get_path_connections_count(lab_cells, reduced_p) >= max_connections then
return false
end
-- for _, m in pairs(steps_orthogonal) do
-- if get_path_connections_count(lab_cells, {x = reduced_p.x + m.x, y = reduced_p.y + m.y}) >= Math.random(2, 3) then
-- return false
-- end
-- end
-- if get_path_connections_count(lab_cells, reduced_p) >= Math.random(2, 3) then
-- return false
-- end
-- if Math.random(80) == 1 then --dead ends and such
-- log(reduced_p.x .. '_' .. reduced_p.y .. ' is dead end')
-- return false
-- end
lab_cells[tostring(reduced_p.x) .. '_' .. tostring(reduced_p.y)] = true
return true
end
end
-- local function terrain_entity_at_relative_position(args, entity)
-- local relative_p = {x = args.true_p.x % maze_scale, y = args.true_p.y % maze_scale}
-- if relative_p.x >= entity.rel_p.x and relative_p.x < entity.rel_p.x+1 and relative_p.y >= entity.rel_p.y and relative_p.y < entity.rel_p.y+1 then
-- entity.rel_p = nil
-- entity.position = args.p
-- args.entities[#args.entities + 1] = entity
-- end
-- end
local free_labyrinth_cell_raffle = {
empty = 16.5,
maze_labs = 0.6,
maze_defended_camp = 0.85,
maze_undefended_camp = 0.25,
maze_worms = 0.8,
small_abandoned_refinery = 0.05,
small_roboport_base = 0.05,
maze_belts_1 = 0.2,
maze_belts_2 = 0.2,
maze_belts_3 = 0.2,
maze_belts_4 = 0.2,
maze_mines = 0.1,
maze_treasure = 0.7,
}
local function free_labyrinth_cell_type(args)
local reduced_p = {x = args.true_p.x - (args.true_p.x % maze_scale), y = args.true_p.y - (args.true_p.y % maze_scale)}
if not args.other_map_generation_data.free_labyrinth_cell_types then
args.other_map_generation_data.free_labyrinth_cell_types = {}
end
local cell_types = args.other_map_generation_data.free_labyrinth_cell_types
local type
if cell_types[tostring(reduced_p.x) .. '_' .. tostring(reduced_p.y)] then
type = cell_types[tostring(reduced_p.x) .. '_' .. tostring(reduced_p.y)]
end
if not type then
type = Math.raffle2(free_labyrinth_cell_raffle)
cell_types[tostring(reduced_p.x) .. '_' .. tostring(reduced_p.y)] = type
end
return type
end
local function free_labyrinth_cell_contents(args)
-- local memory = Memory.get_crew_memory()
-- local noises = Public.noises(args)
-- local mazenoise = noises.maze()
local relative_p = {x = args.true_p.x % maze_scale, y = args.true_p.y % maze_scale}
-- local reduced_p = {x = args.true_p.x - relative_p.x, y = args.true_p.y - relative_p.y}
local type = free_labyrinth_cell_type(args)
if relative_p.x >= maze_scale/2-0.5 and relative_p.x < maze_scale/2+0.5 and relative_p.y >= maze_scale/2-0.5 and relative_p.y < maze_scale/2+0.5 then --should fire just once, and only if the center is included
-- terrain_entity_at_relative_position(args, {name = 'lab', rel_p = {x = 15, y = 15}, force = memory.ancient_friendly_force})
if type == 'empty' then
return nil
else
Structures.tryAddStructureByName(args.specials, type, args.p)
end
end
end
function Public.terrain(args)
local noises = Public.noises(args)
local p = args.p
if IslandsCommon.place_water_tile(args) then return end
if noises.height(p) < 0 then
args.tiles[#args.tiles + 1] = {name = 'water', position = args.p}
return
end
if args.iconized_generation then
maze_wall(args)
else
if noises.height(p) < 0 + Math.max(0, 0.3 - 2*noises.farness(p)) then
if args.true_p.x < 0 and Math.abs(args.true_p.y) < 3 then
args.tiles[#args.tiles + 1] = {name = 'stone-path', position = args.p}
else
args.tiles[#args.tiles + 1] = {name = 'sand-1', position = args.p}
end
if Math.random(500) == 1 then
args.specials[#args.specials + 1] = {name = 'buried-treasure', position = args.p}
end
elseif noises.height(p) < 0.1 + Math.max(0, 0.3 - 2*noises.farness(p)/2) then
if args.true_p.x < 0 and Math.abs(args.true_p.y) < 3 then
args.tiles[#args.tiles + 1] = {name = 'stone-path', position = args.p}
else
maze_wall(args)
end
else -- maze itself
args.tiles[#args.tiles + 1] = {name = 'grass-1', position = args.p}
if labyrinth_determine_walkable_cell(args) then
free_labyrinth_cell_contents(args)
else
maze_wall(args)
end
end
end
end
function Public.chunk_structures(args)
local spec = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.farness(p) > 0.66,
spawners_indestructible = noises.farness(p) > 0.7,
-- spawners_indestructible = true,
density_perchunk = 150 * Math.slopefromto(noises.farness(p), 0.3, 1)^2 * args.biter_base_density_scale,
}
end
IslandsCommon.enemies_1(args, spec)
end
-- function Public.break_rock(surface, p, entity_name)
-- -- return Ores.try_ore_spawn(surface, p, entity_name)
-- end
function Public.generate_silo_setup_position()
return Hunt.silo_setup_position(0.5)
end
return Public

View File

@ -0,0 +1,83 @@
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local Public = {}
Public.display_names = {'Abandoned Labs'}
Public.terraingen_frame_width = 700
Public.terraingen_frame_height = 700
Public.static_params_default = {
starting_time_of_day = 0.45,
brightness_visual_weights = {0.8, 0.8, 0.8},
daynightcycletype = 4,
min_brightness = 0.05,
base_starting_treasure = 1000,
base_starting_rock_material = 1200,
base_starting_wood = 800,
base_starting_treasure_maps = 1,
default_decoratives = false,
}
function Public.base_ores() --here, just for the gui:
return {
['copper-ore'] = 1,
['coal'] = 1,
['uranium-ore'] = 7,
['stone'] = 5,
}
end
local rscale = 200
local hscale = 0.12
Public.noiseparams = {
radius = {
type = 'simplex_2d',
normalised = false,
params = {
{wavelength = 0, amplitude = rscale * 1},
{wavelength = 2.5, amplitude = rscale * 0.12},
},
},
height_background = {
type = 'island1',
normalised = false,
params = {
-- {upperscale = 1000, amplitude = hscale * 200},
{upperscale = 1600, amplitude = hscale * 1},
{upperscale = 80, amplitude = hscale * 0.1},
},
},
forest = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 180, amplitude = 1},
-- {upperscale = 0, amplitude = 0.15},
},
},
rock = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 110, amplitude = 1, seedfactor = 2},
},
},
ore = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 40, amplitude = 1, seedfactor = 3},
},
},
}
return Public

View File

@ -0,0 +1,310 @@
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
local CoreData = require 'maps.pirates.coredata'
local Balance = require 'maps.pirates.balance'
local Structures = require 'maps.pirates.structures.structures'
local Common = require 'maps.pirates.common'
-- local Effects = require 'maps.pirates.effects'
local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
-- local Ores = require 'maps.pirates.ores'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
-- local Hunt = require 'maps.pirates.surfaces.islands.hunt'
local Public = {}
Public.Data = require 'maps.pirates.surfaces.islands.radioactive.data'
function Public.noises(args)
local ret = {}
ret.height = IslandsCommon.island_height_1(args)
ret.height_background = args.noise_generator.height_background
ret.forest = args.noise_generator.forest
ret.forest_abs = function (p) return Math.abs(ret.forest(p)) end
ret.forest_abs_suppressed = function (p) return ret.forest_abs(p) - 1 * Math.slopefromto(ret.height(p), 0.17, 0.11) end
ret.rock = args.noise_generator.rock
ret.ore = args.noise_generator.ore
ret.rock_abs = function (p) return Math.abs(ret.rock(p)) end
ret.farness = IslandsCommon.island_farness_1(args) --isn't available on the iconized pass, only on actual generation; check args.iconized_generation before you use this
return ret
end
function Public.terrain(args)
local noises = Public.noises(args)
local p = args.p
if IslandsCommon.place_water_tile(args) then return end
if noises.height(p) < 0 then
args.tiles[#args.tiles + 1] = {name = 'water', position = args.p}
return
end
if noises.height(p) < 0.05 then
args.tiles[#args.tiles + 1] = {name = 'sand-1', position = args.p}
if (not args.iconized_generation) and noises.farness(p) > 0.02 and noises.farness(p) < 0.6 and Math.random(500) == 1 then
args.specials[#args.specials + 1] = {name = 'buried-treasure', position = args.p}
end
else
if Math.random() < Math.min(0.4,noises.height(p)) then
args.decoratives[#args.decoratives + 1] = {name = 'white-desert-bush', position = p, amount = 1}
elseif Math.random() > Math.max(0.8,1.2-noises.height(p)) then
args.decoratives[#args.decoratives + 1] = {name = 'green-bush-mini', position = p, amount = 1}
end
if noises.height(p) < 0.33 then
args.tiles[#args.tiles + 1] = {name = 'sand-2', position = args.p}
elseif noises.height(p) < 0.35 then
args.tiles[#args.tiles + 1] = {name = 'dirt-5', position = args.p}
else
if noises.height_background(p) > 0.4 then
args.tiles[#args.tiles + 1] = {name = 'nuclear-ground', position = args.p}
else
args.tiles[#args.tiles + 1] = {name = 'dirt-4', position = args.p}
end
end
end
if noises.forest_abs_suppressed(p) > 1 then
local treedensity = 0.02 * Math.slopefromto(noises.forest_abs_suppressed(p), 1, 1.1)
if noises.forest(p) > 1.4 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'dead-grey-trunk', position = args.p, visible_on_overworld = true} end
elseif noises.forest(p) < -0.95 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'dry-tree', position = args.p, visible_on_overworld = true} end
end
end
if noises.forest_abs_suppressed(p) < 0.65 then
if noises.height(p) > 0.12 then
if noises.rock_abs(p) > 0.25 then
local rockdensity = 1/200 * Math.slopefromto(noises.rock_abs(p), 0.25, 0.6)
local rockrng = Math.random()
if rockrng < rockdensity then
args.entities[#args.entities + 1] = IslandsCommon.random_rock_1(args.p)
elseif rockrng < rockdensity * 1.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-medium', position = args.p}
elseif rockrng < rockdensity * 2 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-small', position = args.p}
elseif rockrng < rockdensity * 2.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-tiny', position = args.p}
end
end
end
end
if noises.forest_abs_suppressed(p) < 0.8 and noises.height(p) > 0.35 then
if noises.ore(p) > 1 then
args.entities[#args.entities + 1] = {name = 'uranium-ore', position = args.p, amount = 2000}
end
end
if noises.forest_abs_suppressed(p) < 0.8 and noises.height(p) < 0.35 and noises.height(p) > 0.05 then
if noises.ore(p) < -1.5 then
args.entities[#args.entities + 1] = {name = 'stone', position = args.p, amount = 1000}
elseif noises.ore(p) < 0.005 and noises.ore(p) > -0.005 then
if noises.ore(p) > 0 then
args.entities[#args.entities + 1] = {name = 'coal', position = args.p, amount = 10}
else
args.entities[#args.entities + 1] = {name = 'copper-ore', position = args.p, amount = 100}
end
end
end
end
function Public.chunk_structures(args)
local spec = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.farness(p) > 0.3,
-- we need some indestructible spawners, because otherwise you can clear, stay here forever, make infinite resources...
spawners_indestructible = noises.farness(p) > 0.63,
-- spawners_indestructible = false,
density_perchunk = 25 * Math.slopefromto(noises.farness(p), 0.3, 1)^2 * args.biter_base_density_scale,
}
end
IslandsCommon.enemies_1(args, spec, true)
end
function Public.spawn_structures()
-- local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
-- local surface = game.surfaces[destination.surface_name]
-- local subtype = destination.subtype
-- local force = memory.force
-- local ancient_force = string.format('ancient-friendly-%03d', memory.id)
local ps = Public.structure_positions()
if not destination.dynamic_data.structures_waiting_to_be_placed then
destination.dynamic_data.structures_waiting_to_be_placed = {}
end
for i = 1, #ps do
local p = ps[i]
local structureData
if i == 1 then
structureData = Structures.IslandStructures.MATTISSO.small_radioactive_reactor.Data
elseif i==2 then
structureData = Structures.IslandStructures.MATTISSO.uranium_miners.Data
elseif i>2 and i<7 then
structureData = Structures.IslandStructures.MATTISSO.small_radioactive_centrifuge.Data
else
structureData = Structures.IslandStructures.MATTISSO.small_radioactive_lab.Data
end
local special = {
position = p,
components = structureData.components,
width = structureData.width,
height = structureData.height,
name = structureData.name,
}
destination.dynamic_data.structures_waiting_to_be_placed[#destination.dynamic_data.structures_waiting_to_be_placed + 1] = {data = special, tick = game.tick}
end
end
function Public.structure_positions()
-- local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
local island_center = destination.static_params.islandcenter_position
local width = destination.static_params.width
local height = destination.static_params.height
local tries = 0
local ret = {{x = 0, y = 0}}
local max_exclusion_distance = 45
local maxtries = 2400
local args = {
static_params = destination.static_params,
noise_generator = Utils.noise_generator({}, 0),
}
while #ret < 8 and tries < maxtries do
local p2
if #ret == 1 then
p2 = {x = island_center.x + Math.random(-35, 10), y = island_center.y + Math.random(-40, 40)}
else
p2 = {x = island_center.x + Math.random(Math.ceil(-width/2), Math.ceil(width/2)), y = island_center.y + Math.random(Math.ceil(-height/2), Math.ceil(height/2))}
end
Common.ensure_chunks_at(surface, p2, 0.01)
local tile = surface.get_tile(p2)
if tile and tile.valid and tile.name then
if tile.name ~= 'sand-1' and tile.name ~= 'water' and tile.name ~= 'deepwater' then
local okay = true
local p3 = {x = p2.x + args.static_params.terraingen_coordinates_offset.x, y = p2.y + args.static_params.terraingen_coordinates_offset.y}
local farness = IslandsCommon.island_farness_1(args)(p3)
if (not okay) or (not (farness > 0.05 and farness < 0.55)) then
okay = false
end
local exclusion_distance = max_exclusion_distance * (maxtries - tries) / maxtries
if #ret == 1 then exclusion_distance = 15 * (maxtries - tries) / maxtries end
for _, p in pairs(ret) do
if (not okay) or Math.distance(p, p2) < exclusion_distance then
okay = false
end
end
if okay then
ret[#ret + 1] = p2
end
end
end
tries = tries + 1
end
if _DEBUG then
log('radioactive world locations took ' .. tries .. ' tries.')
end
if #ret < 8 then log('couldn\'t find four positions after 2400 tries') end
return ret
end
-- function Public.break_rock(surface, p, entity_name)
-- -- return Ores.try_ore_spawn(surface, p, entity_name)
-- end
local function radioactive_tick()
for _, id in pairs(Memory.get_global_memory().crew_active_ids) do
Memory.set_working_id(id)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local tickinterval = 60
if destination.subtype == IslandsCommon.enum.RADIOACTIVE then
-- faster evo (doesn't need difficulty scaling as higher difficulties have higher base evo):
local extra_evo = 0.22 * tickinterval/60 / Balance.expected_time_on_island()
Common.increment_evo(extra_evo)
if (not destination.dynamic_data.evolution_accrued_time) then
destination.dynamic_data.evolution_accrued_time = 0
end
destination.dynamic_data.evolution_accrued_time = destination.dynamic_data.evolution_accrued_time + extra_evo
if not memory.floating_pollution then memory.floating_pollution = 0 end
-- faster pollute:
local pollution = 0
local timer = destination.dynamic_data.timer
if timer and timer > 15 then
pollution = 4.9 * (6 * Common.difficulty()^(1.1) * (memory.overworldx/40)^(14/10) * (Balance.crew_scale())^(0.6)) / 3600 * tickinterval * (1 + (Common.difficulty()-1)*0.2 + 0.001 * timer)
end
if pollution > 0 then
memory.floating_pollution = memory.floating_pollution + pollution
game.pollution_statistics.on_flow('uranium-ore', pollution)
end
local surface = game.surfaces[destination.surface_name]
if surface and surface.valid and (not surface.freeze_daytime) and destination.dynamic_data.timer and destination.dynamic_data.timer >= CoreData.daynightcycle_types[Public.Data.static_params_default.daynightcycletype].ticksperday/60/2 then --once daytime, never go back to night
surface.freeze_daytime = true
end
end
end
end
local event = require 'utils.event'
event.on_nth_tick(60, radioactive_tick)
return Public

View File

@ -0,0 +1,89 @@
-- local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local Public = {}
Public.display_names = {'Sandworm Caldera'}
Public.discord_emoji = CoreData.comfy_emojis.mjau
Public.terraingen_frame_width = 700
Public.terraingen_frame_height = 700
Public.static_params_default = {
starting_time_of_day = 0,
daynightcycletype = 1,
default_decoratives = true,
base_starting_treasure = 0,
base_starting_rock_material = 8600,
base_starting_wood = 600,
}
function Public.base_ores() --here, just for the visualisation:
return {
['copper-ore'] = 5,
['iron-ore'] = 5,
['coal'] = 5,
}
end
local rscale = 175
local hscale = 0.16
Public.noiseparams = {
radius = {
type = 'simplex_2d',
normalised = false,
params = {
{wavelength = 0, amplitude = rscale * 1},
},
},
height_background = {
type = 'island1',
normalised = false,
params = {
-- {upperscale = 1000, amplitude = hscale * 200},
{upperscale = 1600, amplitude = hscale * 1},
{upperscale = 60, amplitude = hscale * 0.15},
},
},
forest = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 100, amplitude = 1},
},
},
ore = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 40, amplitude = 1, seedfactor = 2},
},
},
rock = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 120, amplitude = 1, seedfactor = 3},
},
},
mood = {
type = 'simplex_2d',
normalised = true,
params = {
{wavelength = 200, amplitude = 70},
},
},
}
return Public

View File

@ -0,0 +1,435 @@
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
local Balance = require 'maps.pirates.balance'
local Structures = require 'maps.pirates.structures.structures'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local Effects = require 'maps.pirates.effects'
local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
-- local Ores = require 'maps.pirates.ores'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
local Hunt = require 'maps.pirates.surfaces.islands.hunt'
local Public = {}
Public.Data = require 'maps.pirates.surfaces.islands.red_desert.data'
function Public.noises(args)
local ret = {}
ret.height = IslandsCommon.island_height_1(args)
ret.height_background = args.noise_generator.height_background
ret.forest = args.noise_generator.forest
ret.forest_abs = function (p) return Math.abs(ret.forest(p)) end
ret.forest_abs_suppressed = function (p) return ret.forest_abs(p) - 1 * Math.slopefromto(ret.height(p), 0.17, 0.11) end
ret.rock = args.noise_generator.rock
ret.ore = args.noise_generator.ore
ret.rock_abs = function (p) return Math.abs(ret.rock(p)) end
ret.mood = args.noise_generator.mood
ret.farness = IslandsCommon.island_farness_1(args) --isn't available on the iconized pass, only on actual generation; check args.iconized_generation before you use this
return ret
end
function Public.terrain(args)
local noises = Public.noises(args)
local p = args.p
if IslandsCommon.place_water_tile(args) then return end
if noises.height(p) < 0 then
args.tiles[#args.tiles + 1] = {name = 'water', position = args.p}
return
end
if noises.height(p) < 0.19 then
args.tiles[#args.tiles + 1] = {name = 'sand-1', position = args.p}
elseif noises.height(p) < 0.22 then
args.tiles[#args.tiles + 1] = {name = 'red-desert-3', position = args.p}
else
if noises.height_background(p) > 0.4 then
args.tiles[#args.tiles + 1] = {name = 'red-desert-2', position = args.p}
elseif noises.height_background(p) > -0.15 then
args.tiles[#args.tiles + 1] = {name = 'red-desert-1', position = args.p}
else
args.tiles[#args.tiles + 1] = {name = 'red-desert-0', position = args.p}
end
end
if noises.height(p) > 0.32 then
if noises.rock_abs(p) > 0.25 then
if noises.mood(p) < -0.5 then
local density = 0.003 * Math.slopefromto(noises.rock_abs(p), 0.25, 0.4)
local rng = Math.random()
if rng < density then
args.decoratives[#args.decoratives + 1] = {name = 'worms-decal', position = args.p}
end
end
if noises.mood(p) < 0.1 then
local rng = Math.random()
if rng < 0.0004 then
args.entities[#args.entities + 1] = {name = 'medium-remnants', position = args.p}
elseif rng < 0.0007 then
args.entities[#args.entities + 1] = {name = 'spidertron-remnants', position = args.p}
elseif rng < 0.001 then
args.entities[#args.entities + 1] = {name = 'medium-ship-wreck', position = args.p}
elseif rng < 0.0013 then
args.entities[#args.entities + 1] = {name = 'big-ship-wreck-2', position = args.p}
elseif rng < 0.0014 then
args.entities[#args.entities + 1] = {name = 'big-ship-wreck-1', position = args.p}
end
end
end
end
if noises.forest_abs_suppressed(p) > 0.85 then
local treedensity = 0.3 * Math.slopefromto(noises.forest_abs_suppressed(p), 0.85, 0.9)
if noises.forest(p) > 1.6 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'dry-hairy-tree', position = args.p, visible_on_overworld = true} end
elseif noises.forest(p) < -0.95 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'dead-tree-desert', position = args.p, visible_on_overworld = true} end
end
end
if noises.forest_abs_suppressed(p) < 0.65 then
if noises.height(p) > 0.15 then
if noises.rock_abs(p) > 0.25 then
local rockdensity = 1/200 * Math.slopefromto(noises.rock_abs(p), 0.25, 0.6)
local rockrng = Math.random()
if rockrng < rockdensity then
args.entities[#args.entities + 1] = IslandsCommon.random_rock_1(args.p)
elseif rockrng < rockdensity * 1.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-medium', position = args.p}
elseif rockrng < rockdensity * 2 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-small', position = args.p}
elseif rockrng < rockdensity * 2.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-tiny', position = args.p}
end
end
end
end
if noises.forest_abs_suppressed(p) < 0.8 and noises.mood(p) > -0.3 then
if noises.height(p) > 0.27 then
if noises.ore(p) > 1.55 then
local name = 'iron-ore'
if (args.p.x + args.p.y) % 2 < 1 then
name = 'copper-ore'
end
args.entities[#args.entities + 1] = {name = name, position = args.p, amount = 24}
elseif noises.ore(p) < -1.6 then
args.entities[#args.entities + 1] = {name = 'coal', position = args.p, amount = 24}
elseif noises.ore(p) < 0.041 and noises.ore(p) > -0.041 then
args.entities[#args.entities + 1] = {name = 'stone', position = args.p, amount = 10}
end
elseif noises.height(p) < 0.19 then
if noises.ore(p) > 2.1 then
args.entities[#args.entities + 1] = {name = 'copper-ore', position = args.p, amount = 10}
elseif noises.ore(p) < -2.1 then
args.entities[#args.entities + 1] = {name = 'iron-ore', position = args.p, amount = 10}
-- elseif noises.ore(p) < 0.010 and noises.ore(p) > -0.010 then
-- args.entities[#args.entities + 1] = {name = 'coal', position = args.p, amount = 5}
end
end
end
end
function Public.chunk_structures(args)
local rng = Math.random()
local left_top = args.left_top
local spec = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.height(p) > 0.05 and noises.mood(p) > -0.6 and noises.farness(p) > 0.1,
chanceper4chunks = 0.4,
}
end
-- initial attempt, to avoid placing two structures too close to each other, is to divide up the map into 2x2 chonks, and spawn once in each
local bool1, bool2 = left_top.x % 64 < 32, left_top.y % 64 < 32
local all_four_chunks = {
{x = left_top.x, y = left_top.y},
{x = left_top.x + (bool1 and 32 or -32), y = left_top.y},
{x = left_top.x, y = left_top.y + (bool2 and 32 or -32)},
{x = left_top.x + (bool1 and 32 or -32), y = left_top.y + (bool2 and 32 or -32)},
}
if not args.other_map_generation_data.chunks_loaded then args.other_map_generation_data.chunks_loaded = {} end
local chunks_loaded = args.other_map_generation_data.chunks_loaded
if not chunks_loaded[args.left_top.x] then chunks_loaded[args.left_top.x] = {} end
chunks_loaded[args.left_top.x][args.left_top.y] = true
local nearby_chunks_generated_count = 0
for i=1,4 do
if chunks_loaded[all_four_chunks[i].x] and chunks_loaded[all_four_chunks[i].x][all_four_chunks[i].y] then
nearby_chunks_generated_count = nearby_chunks_generated_count + 1
end
end
if nearby_chunks_generated_count == 4 then --should trigger only once per 4 chunks
local avgleft_top = {
x = (all_four_chunks[1].x + all_four_chunks[4].x)/2,
y = (all_four_chunks[1].y + all_four_chunks[4].y)/2,
}
local leftmost_topmost = {
x = avgleft_top.x - 32,
y = avgleft_top.y - 32,
}
local spec2 = spec{x = avgleft_top.x + 16, y = avgleft_top.y + 16}
if rng < spec2.chanceper4chunks then
local rng2 = Math.random()
local struct
if rng2 < 28/100 then
struct = Structures.IslandStructures.ROC.shelter2
else
struct = Structures.IslandStructures.ROC.shelter1
end
if struct then
Structures.try_place(struct, args.specials, leftmost_topmost, 64, 64, function(p) return spec(p).placeable end)
end
end
end
end
-- function Public.break_rock(surface, p, entity_name)
-- -- return Ores.try_ore_spawn(surface, p, entity_name)
-- end
function Public.generate_silo_setup_position()
return Hunt.silo_setup_position()
end
local function red_desert_tick()
for _, id in pairs(Memory.get_global_memory().crew_active_ids) do
Memory.set_working_id(id)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
if destination.subtype == IslandsCommon.enum.RED_DESERT then
if memory.boat and memory.boat.surface_name and memory.boat.surface_name == destination.surface_name then
Public.underground_worms_ai()
if game.tick % 360 == 0 and destination.dynamic_data.timer and destination.dynamic_data.timer > 60 then
Public.custom_biter_ai()
end
end
end
end
end
local event = require 'utils.event'
event.on_nth_tick(30, red_desert_tick)
function Public.underground_worms_ai()
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
local player_force = memory.force
local enemy_force_name = memory.enemy_force_name
local evolution = memory.evolution_factor
if not destination.dynamic_data.worms_table then destination.dynamic_data.worms_table = {} end
local worms = destination.dynamic_data.worms_table
local indices_to_remove = {}
for i = #worms, 1, -1 do
local w = worms[i]
w.age = w.age + 1
-- despawn
if w.age > w.max_age then
indices_to_remove[#indices_to_remove + 1] = i
else
-- move
w.position = {x = w.position.x + Balance.sandworm_speed() * 30/60 * w.direction.x, y = w.position.y + Balance.sandworm_speed() * 30/60 * w.direction.y}
if w.chart_tag then w.chart_tag.destroy() end
local tile = surface.get_tile(w.position.x, w.position.y)
local on_land = tile and tile.valid and (not Utils.contains(CoreData.tiles_that_conflict_with_resource_layer, tile.name)) and (not Utils.contains(CoreData.noworm_tile_names, tile.name))
if on_land then
local solid_ground = (tile and tile.valid and Utils.contains(CoreData.worm_solid_tile_names, tile.name))
-- stomp
local big_bool = (w.age % 4 == 0)
Effects.worm_movement_effect(surface, w.position, solid_ground, big_bool)
w.chart_tag = player_force.add_chart_tag(surface, {icon = {type = 'virtual', name = 'signal-red'}, position = w.position})
if not solid_ground then
local nearby_characters = surface.find_entities_filtered{position = w.position, radius = 7, name = 'character'}
local character_outside = false
for j = 1, #nearby_characters do
local c = nearby_characters[j]
local t = surface.get_tile(c.position.x, c.position.y)
if not (t and t.valid and Utils.contains(CoreData.worm_solid_tile_names, t.name))
then
character_outside = true
break
end
end
if character_outside then
local type = Common.get_random_worm_type(evolution)
local emerge_position = surface.find_non_colliding_position(type, w.position, 3, 0.5)
if emerge_position then
local emerge_position_tile = surface.get_tile(emerge_position.x, emerge_position.y)
local can_emerge = (not solid_ground) and (not (tile and tile.valid and Utils.contains(CoreData.worm_solid_tile_names, emerge_position_tile.name)))
if can_emerge then
surface.create_entity{name = type, position = emerge_position, force = enemy_force_name}
Effects.worm_emerge_effect(surface, emerge_position)
indices_to_remove[#indices_to_remove + 1] = i
if w.chart_tag then w.chart_tag.destroy() end
local extra_evo = Balance.sandworm_evo_increase_per_spawn()
Common.increment_evo(extra_evo)
if destination.dynamic_data then
destination.dynamic_data.evolution_accrued_sandwurms = destination.dynamic_data.evolution_accrued_sandwurms + extra_evo
end
end
end
end
end
end
end
end
for i = 1, #indices_to_remove do
local index = indices_to_remove[i]
for j = index, #worms-1 do
worms[j] = worms[j+1]
end
worms[#worms] = nil
end
local max_worms = Math.ceil(45 * Math.sloped(Common.difficulty(), 1/2))
-- spawn worms
if game.tick % 90 == 0 then
if #worms < max_worms then
local island_center = destination.static_params.islandcenter_position
local r = Math.max(destination.static_params.width, destination.static_params.height)/2
local theta = Math.random()*5.75 - Math.pi/2+0.25
local p = {x = island_center.x + r*Math.sin(theta), y = island_center.y + r*Math.cos(theta)}
local theta2 = Math.random()*1.4-0.7
local d = {x = -Math.sin(theta+theta2), y = -Math.cos(theta+theta2)}
worms[#worms + 1] = {position = p, direction = d, age = 0, max_age = 2*r/(Balance.sandworm_speed() * 30/60) * Math.cos(theta2/2)}
end
end
end
function Public.custom_biter_ai()
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
-- local difficulty = memory.difficulty
local enemy_force_name = memory.enemy_force_name
local evolution = memory.evolution_factor
local fraction_of_floating_pollution = 1/2
local minimum_avg_units = 30
local maximum_units = 256
local pollution_available = memory.floating_pollution
local budget = fraction_of_floating_pollution * pollution_available
if budget >= minimum_avg_units * Common.averageUnitPollutionCost(evolution) then
local initialbudget = budget
local position = Hunt.position_away_from_players_1({static_params = destination.static_params}, 50)
local units_created_count = 0
local units_created = {}
local name = Common.get_random_unit_type(evolution)
local unittype_pollutioncost = CoreData.biterPollutionValues[name] * 1.1 * Balance.scripted_biters_pollution_cost_multiplier()
local function spawn(name2)
units_created_count = units_created_count + 1
local p = surface.find_non_colliding_position(name2, position, 50, 2)
if not p then return end
local biter = surface.create_entity({name = name2, force = enemy_force_name, position = p})
units_created[#units_created + 1] = biter
memory.scripted_biters[biter.unit_number] = {entity = biter, created_at = game.tick}
return biter.unit_number
end
local whilesafety = 1000
while units_created_count < maximum_units and budget >= unittype_pollutioncost and #memory.scripted_biters < CoreData.total_max_biters and whilesafety > 0 do
whilesafety = whilesafety - 1
pollution_available = pollution_available - unittype_pollutioncost
budget = budget - unittype_pollutioncost
spawn(name)
end
game.pollution_statistics.on_flow(name, budget - initialbudget)
memory.floating_pollution = pollution_available
if (not units_created) or (not #units_created) or (#units_created == 0) then return end
Effects.biters_emerge(surface, position)
local position2 = surface.find_non_colliding_position('rocket-silo', position, 256, 2) or position
local unit_group = surface.create_unit_group({position = position2, force = enemy_force_name})
for _, unit in pairs(units_created) do
unit_group.add_member(unit)
end
memory.scripted_unit_groups[unit_group.group_number] = {ref = unit_group, script_type = 'burrowed'}
local target = {valid = true, position = {x = memory.boat.position.x - 60, y = memory.boat.position.y} or nil, name = 'boatarea'}
unit_group.set_command{
type = defines.command.attack_area,
destination = target.position,
radius = 30,
distraction = defines.distraction.by_anything
}
end
end
return Public

View File

@ -0,0 +1,85 @@
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local Public = {}
Public.display_names = {'Isle of Buried Treasure'}
Public.terraingen_frame_width = 896
Public.terraingen_frame_height = 896
Public.static_params_default = {
default_decoratives = true,
base_starting_treasure = 1000,
base_starting_rock_material = 800,
base_starting_wood = 1200,
base_starting_treasure_maps = 6,
starting_time_of_day = 0,
daynightcycletype = 3,
brightness_visual_weights = {0.12, 0.12, 0.12}, --light night, but still workable without lights
min_brightness = 0.2,
}
function Public.base_ores()
return {
['copper-ore'] = 0.9,
['iron-ore'] = 5.7,
['coal'] = 4,
['stone'] = 1.0,
}
end
local rscale = 250
local hscale = 0.1
Public.noiseparams = {
radius = {
type = 'simplex_2d',
normalised = false,
params = {
{wavelength = 0, amplitude = rscale * 1},
{wavelength = 1.6, amplitude = rscale * 0.3},
},
},
height_background = {
type = 'island1',
normalised = false,
params = {
-- {upperscale = 1000, amplitude = hscale * 200},
{upperscale = 1600, amplitude = hscale * 1},
{upperscale = 80, amplitude = hscale * 0.1},
},
},
forest = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 180, amplitude = 1},
-- {upperscale = 0, amplitude = 0.15},
},
},
rock = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 110, amplitude = 1, seedfactor = 2},
},
},
mood = {
type = 'simplex_2d',
normalised = true,
params = {
{wavelength = 250, amplitude = 70},
{wavelength = 50, amplitude = 20},
},
},
}
return Public

View File

@ -0,0 +1,147 @@
-- local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
-- local Structures = require 'maps.pirates.structures.structures'
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
-- local Ores = require 'maps.pirates.ores'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
local Hunt = require 'maps.pirates.surfaces.islands.hunt'
local Public = {}
Public.Data = require 'maps.pirates.surfaces.islands.standard.data'
function Public.noises(args)
local ret = {}
ret.height = IslandsCommon.island_height_1(args)
ret.forest = args.noise_generator.forest
ret.forest_abs = function (p) return Math.abs(ret.forest(p)) end
ret.forest_abs_suppressed = function (p) return ret.forest_abs(p) - 1 * Math.slopefromto(ret.height(p), 0.17, 0.11) end
ret.rock = args.noise_generator.rock
ret.rock_abs = function (p) return Math.abs(ret.rock(p)) end
ret.mood = args.noise_generator.mood
ret.farness = IslandsCommon.island_farness_1(args) --isn't available on the iconized pass, only on actual generation; check args.iconized_generation before you use this
return ret
end
function Public.terrain(args)
local noises = Public.noises(args)
local p = args.p
if IslandsCommon.place_water_tile(args) then return end
if noises.height(p) < 0 then
args.tiles[#args.tiles + 1] = {name = 'water', position = args.p}
return
end
if noises.height(p) < 0.05 then
args.tiles[#args.tiles + 1] = {name = 'sand-1', position = args.p}
if (not args.iconized_generation) and noises.farness(p) > 0.02 and noises.farness(p) < 0.6 and Math.random(500) == 1 then
args.specials[#args.specials + 1] = {name = 'buried-treasure', position = args.p}
end
elseif noises.height(p) < 0.12 then
args.tiles[#args.tiles + 1] = {name = 'sand-2', position = args.p}
else
if noises.forest_abs_suppressed(p) > 0.3 and noises.rock(p) < 0.3 then
args.tiles[#args.tiles + 1] = {name = 'dry-dirt', position = args.p}
elseif noises.forest_abs_suppressed(p) > 0.15 and noises.rock(p) < 0.3 then
args.tiles[#args.tiles + 1] = {name = 'dirt-3', position = args.p}
else
args.tiles[#args.tiles + 1] = {name = 'dirt-2', position = args.p}
end
end
if noises.height(p) > 0.06 then
if noises.forest_abs_suppressed(p) > 0.5 then
if (not args.iconized_generation) and noises.forest_abs_suppressed(p) < 0.75 and Math.random(2000) == 1 then
args.specials[#args.specials + 1] = {name = 'chest', position = args.p}
else
local forest_noise = noises.forest(p)
local treedensity = 0.7 * Math.slopefromto(noises.forest_abs_suppressed(p), 0.5, 0.7)
if forest_noise > 0 then
if noises.rock(p) > 0 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-06', position = args.p, visible_on_overworld = true} end
else
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-06-brown', position = args.p} end
end
elseif forest_noise < -1 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-03', position = args.p, visible_on_overworld = true} end
else
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-07', position = args.p} end
end
end
end
end
if noises.forest_abs_suppressed(p) < 0.45 then
if noises.height(p) > 0.12 then
if noises.rock_abs(p) > 0.22 then
local rockdensity = 1/600 * Math.slopefromto(noises.rock_abs(p), 0.22, 0.6) + 1/5 * Math.slopefromto(noises.rock_abs(p), 2.4, 2.6)
local rockrng = Math.random()
if rockrng < rockdensity then
args.entities[#args.entities + 1] = IslandsCommon.random_rock_1(args.p)
elseif rockrng < rockdensity * 1.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-medium', position = args.p}
elseif rockrng < rockdensity * 2 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-small', position = args.p}
elseif rockrng < rockdensity * 2.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-tiny', position = args.p}
end
end
end
end
if noises.height(p) > 0.18 and noises.mood(p) > 0.6 then
if noises.forest_abs(p) < 0.2 and noises.rock_abs(p) > 1.5 then
args.entities[#args.entities + 1] = {name = 'coal', position = args.p, amount = 7}
end
end
end
function Public.chunk_structures(args)
local spec = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.farness(p) > 0.3,
-- spawners_indestructible = noises.farness(p) > 0.7,
spawners_indestructible = false,
density_perchunk = 30 * Math.slopefromto(noises.mood(p), 0.14, -0.1) * Math.slopefromto(noises.farness(p), 0.3, 1)^2 * args.biter_base_density_scale,
}
end
IslandsCommon.enemies_1(args, spec)
local spec2 = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.height(p) >= 0 and noises.forest_abs_suppressed(p) < 0.3 + Math.max(0, 0.2 - noises.height(p)),
chanceper4chunks = 0.5 * Math.slopefromto(noises.farness(p), 0.1, 0.4) * Math.slopefromto(noises.mood(p), 0, 0.25),
}
end
IslandsCommon.assorted_structures_1(args, spec2)
end
-- function Public.break_rock(surface, p, entity_name)
-- -- return Ores.try_ore_spawn(surface, p, entity_name)
-- end
function Public.generate_silo_setup_position()
return Hunt.silo_setup_position()
end
return Public

View File

@ -0,0 +1,85 @@
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local Public = {}
Public.display_names = {'Secluded Dells'}
Public.terraingen_frame_width = 896
Public.terraingen_frame_height = 896
Public.static_params_default = {
default_decoratives = true,
base_starting_treasure = 1000,
base_starting_rock_material = 800,
base_starting_wood = 1200,
base_starting_treasure_maps = 1,
starting_time_of_day = 0,
daynightcycletype = 3,
brightness_visual_weights = {0.12, 0.12, 0.12}, --light night, but still workable without lights
min_brightness = 0.2,
}
function Public.base_ores()
return {
['copper-ore'] = 4.4,
['iron-ore'] = 4.9,
['coal'] = 4,
['stone'] = 1.2,
}
end
local rscale = 220
local hscale = 0.1
Public.noiseparams = {
radius = {
type = 'simplex_2d',
normalised = false,
params = {
{wavelength = 0, amplitude = rscale * 1},
{wavelength = 1.6, amplitude = rscale * 0.25},
},
},
height_background = {
type = 'island1',
normalised = false,
params = {
-- {upperscale = 1000, amplitude = hscale * 200},
{upperscale = 1600, amplitude = hscale * 1},
{upperscale = 80, amplitude = hscale * 0.1},
},
},
forest = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 75, amplitude = 1},
-- {upperscale = 0, amplitude = 0.15},
},
},
rock = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 110, amplitude = 1, seedfactor = 2},
},
},
mood = {
type = 'simplex_2d',
normalised = true,
params = {
{wavelength = 250, amplitude = 70},
{wavelength = 50, amplitude = 20},
},
},
}
return Public

View File

@ -0,0 +1,151 @@
-- local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
-- local Structures = require 'maps.pirates.structures.structures'
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
-- local Ores = require 'maps.pirates.ores'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
local Hunt = require 'maps.pirates.surfaces.islands.hunt'
local Public = {}
Public.Data = require 'maps.pirates.surfaces.islands.standard_variant.data'
function Public.noises(args)
local ret = {}
ret.height = IslandsCommon.island_height_1(args)
ret.forest = args.noise_generator.forest
ret.forest_abs = function (p) return Math.abs(ret.forest(p)) end
ret.forest_abs_suppressed = function (p) return ret.forest_abs(p) - 1 * Math.slopefromto(ret.height(p), 0.17, 0.11) end
ret.rock = args.noise_generator.rock
ret.rock_abs = function (p) return Math.abs(ret.rock(p)) end
ret.mood = args.noise_generator.mood
ret.farness = IslandsCommon.island_farness_1(args) --isn't available on the iconized pass, only on actual generation; check args.iconized_generation before you use this
return ret
end
function Public.terrain(args)
local noises = Public.noises(args)
local p = args.p
if IslandsCommon.place_water_tile(args) then return end
if noises.height(p) < 0 then
args.tiles[#args.tiles + 1] = {name = 'water', position = args.p}
return
end
if noises.height(p) < 0.04 then
args.tiles[#args.tiles + 1] = {name = 'sand-1', position = args.p}
if (not args.iconized_generation) and noises.farness(p) > 0.02 and noises.farness(p) < 0.6 and Math.random(500) == 1 then
args.specials[#args.specials + 1] = {name = 'buried-treasure', position = args.p}
end
elseif noises.height(p) < 0.05 then
args.tiles[#args.tiles + 1] = {name = 'sand-2', position = args.p}
elseif noises.height(p) < 0.11 then
args.tiles[#args.tiles + 1] = {name = 'dry-dirt', position = args.p}
elseif noises.height(p) < 0.12 then
args.tiles[#args.tiles + 1] = {name = 'grass-4', position = args.p}
else
if noises.forest_abs_suppressed(p) > 0.3 and noises.rock(p) < 0.3 then
args.tiles[#args.tiles + 1] = {name = 'dirt-7', position = args.p}
elseif noises.forest_abs_suppressed(p) > 0.15 and noises.rock(p) < 0.3 then
args.tiles[#args.tiles + 1] = {name = 'grass-4', position = args.p}
else
args.tiles[#args.tiles + 1] = {name = 'grass-3', position = args.p}
end
end
if noises.height(p) > 0.11 then
if noises.forest_abs_suppressed(p) > 0.7 then
if (not args.iconized_generation) and noises.forest_abs_suppressed(p) < 1 and Math.random(750) == 1 then -- high amounts of this
args.specials[#args.specials + 1] = {name = 'chest', position = args.p}
else
local forest_noise = noises.forest(p)
local treedensity = 0.7 * Math.slopefromto(noises.forest_abs_suppressed(p), 0.61, 0.76)
if forest_noise > 0 then
if noises.rock(p) > 0.05 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-08-red', position = args.p, visible_on_overworld = true} end
elseif noises.rock(p) < -0.05 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-09-brown', position = args.p} end
end
elseif forest_noise < -1.2 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-09', position = args.p, visible_on_overworld = true} end
else
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-02-red', position = args.p} end
end
end
end
end
if noises.forest_abs_suppressed(p) < 0.45 then
if noises.height(p) > 0.12 then
if noises.rock_abs(p) > 0.25 then
local rockdensity = 1/600 * Math.slopefromto(noises.rock_abs(p), 0.25, 0.6) + 1/5 * Math.slopefromto(noises.rock_abs(p), 2.4, 2.6)
local rockrng = Math.random()
if rockrng < rockdensity then
args.entities[#args.entities + 1] = IslandsCommon.random_rock_1(args.p)
elseif rockrng < rockdensity * 1.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-medium', position = args.p}
elseif rockrng < rockdensity * 2 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-small', position = args.p}
elseif rockrng < rockdensity * 2.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-tiny', position = args.p}
end
end
end
end
if noises.height(p) > 0.18 and noises.mood(p) > 0.3 then
if noises.forest_abs(p) < 0.2 and noises.rock_abs(p) > 1.5 then
args.entities[#args.entities + 1] = {name = 'coal', position = args.p, amount = 7}
end
end
end
function Public.chunk_structures(args)
local spec = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.farness(p) > 0.3,
spawners_indestructible = false,
-- spawners_indestructible = noises.farness(p) > 0.7,
density_perchunk = 25 * Math.slopefromto(noises.mood(p), 0.16, -0.1) * Math.slopefromto(noises.farness(p), 0.3, 1)^2 * args.biter_base_density_scale,
}
end
IslandsCommon.enemies_1(args, spec)
local spec2 = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.height(p) >= 0 and noises.forest_abs_suppressed(p) < 0.3 + Math.max(0, 0.2 - noises.height(p)),
chanceper4chunks = 0.5 * Math.slopefromto(noises.farness(p), 0.1, 0.4) * Math.slopefromto(noises.mood(p), 0, 0.25),
}
end
IslandsCommon.assorted_structures_1(args, spec2)
end
-- function Public.break_rock(surface, p, entity_name)
-- -- return Ores.try_ore_spawn(surface, p, entity_name)
-- end
function Public.generate_silo_setup_position()
return Hunt.silo_setup_position()
end
return Public

View File

@ -0,0 +1,89 @@
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local Public = {}
Public.display_names = {'Poisonous Fen'}
Public.terraingen_frame_width = 325
Public.terraingen_frame_height = 325
Public.static_params_default = {
starting_time_of_day = 0.35,
daynightcycletype = 1,
brightness_visual_weights = {0, 0, 0},
default_decoratives = true,
base_starting_treasure = 1000,
base_starting_rock_material = 800,
base_starting_wood = 1200,
base_starting_treasure_maps = 0,
}
function Public.base_ores()
return {
['copper-ore'] = 2.9,
['iron-ore'] = 3.6,
['coal'] = 4.8,
['stone'] = 0.5,
['crude-oil'] = 80,
}
end
local rscale = 170
Public.noiseparams = {
radius = {
type = 'simplex_2d',
normalised = false,
params = {
{wavelength = 0, amplitude = rscale * 1},
{wavelength = 2, amplitude = rscale * 0.2},
},
},
height_background = {
type = 'island1',
normalised = false,
params = {
-- {upperscale = 1000, amplitude = hscale * 200},
{upperscale = 600, amplitude = 0.1},
},
},
forest = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 70, amplitude = 1},
},
},
rock = {
type = 'forest1',
normalised = true,
params = {
{upperscale = 120, amplitude = 1, seedfactor = 2},
},
},
mood = {
type = 'simplex_2d',
normalised = true,
params = {
{wavelength = 250, amplitude = 70},
{wavelength = 50, amplitude = 20},
},
},
terrain = {
type = 'simplex_2d',
normalised = true,
params = {
{wavelength = 48, amplitude = 1},
},
},
}
return Public

View File

@ -0,0 +1,231 @@
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
local Structures = require 'maps.pirates.structures.structures'
local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
local Ores = require 'maps.pirates.ores'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
local Hunt = require 'maps.pirates.surfaces.islands.hunt'
local Public = {}
Public.Data = require 'maps.pirates.surfaces.islands.swamp.data'
function Public.noises(args)
local ret = {}
ret.height = IslandsCommon.island_height_1(args)
ret.forest = args.noise_generator.forest
ret.forest_abs = function (p) return Math.abs(ret.forest(p)) end
ret.forest_abs_suppressed = function (p) return ret.forest_abs(p) - 1 * Math.slopefromto(ret.height(p), 0.35, 0.1) end
ret.terrain = args.noise_generator.terrain
ret.terrain_abs = function (p) return Math.abs(ret.terrain(p)) end
ret.rock = args.noise_generator.rock
ret.rock_abs = function (p) return Math.abs(ret.rock(p)) end
ret.mood = args.noise_generator.mood
ret.farness = IslandsCommon.island_farness_1(args) --isn't available on the iconized pass, only on actual generation; check args.iconized_generation before you use this
return ret
end
function Public.terrain(args)
local noises = Public.noises(args)
local p = args.p
if IslandsCommon.place_water_tile(args) then return end
if noises.height(p) < 0 then
args.tiles[#args.tiles + 1] = {name = 'water', position = args.p}
return
end
local land = true
if noises.height(p) < 0.03 then
args.tiles[#args.tiles + 1] = {name = 'water-shallow', position = args.p}
land = false
-- elseif noises.height(p) < 0.07 then
-- args.tiles[#args.tiles + 1] = {name = 'grass-4', position = args.p}
else
if noises.terrain(p) < 0.44 then
args.tiles[#args.tiles + 1] = {name = 'grass-1', position = args.p}
elseif noises.terrain(p) < 0.59 then
args.tiles[#args.tiles + 1] = {name = 'grass-2', position = args.p}
else
args.tiles[#args.tiles + 1] = {name = 'water-mud', position = args.p}
land = false
end
end
if land then
if (not args.iconized_generation) and Math.random(2500) == 1 then
args.specials[#args.specials + 1] = {name = 'chest', position = args.p}
else
if noises.forest_abs(p) > 0.15 then
local treedensity = 0.08 * Math.slopefromto(noises.forest_abs_suppressed(p), 0.3, 0.6) + 0.3 * Math.slopefromto(noises.forest_abs_suppressed(p), 0.65, 1.0)
if noises.forest(p) > 1.3 then
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-09-brown', position = args.p} end
else
if Math.random(1,100) < treedensity*100 then args.entities[#args.entities + 1] = {name = 'tree-08', position = args.p, visible_on_overworld = true} end
end
if noises.forest_abs_suppressed(p) < 0.7 then
if noises.height(p) > 0.12 then
if noises.rock_abs(p) > -0.15 then
local rockdensity = 1/600 * Math.slopefromto(noises.rock_abs(p), 0.22, 0.6) + 1/5 * Math.slopefromto(noises.rock_abs(p), 1.6, 1.8)
local rockrng = Math.random()
if rockrng < rockdensity then
args.entities[#args.entities + 1] = IslandsCommon.random_rock_1(args.p)
elseif rockrng < rockdensity * 1.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-medium', position = args.p}
elseif rockrng < rockdensity * 2 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-small', position = args.p}
elseif rockrng < rockdensity * 2.5 then
args.decoratives[#args.decoratives + 1] = {name = 'rock-tiny', position = args.p}
end
end
end
end
end
if noises.mood(p) > 0.3 then
local density = 0.001 * Math.slopefromto(noises.rock_abs(p), 0.25, 0.4)
local rng = Math.random()
if rng < density then
args.decoratives[#args.decoratives + 1] = {name = 'shroom-decal', position = args.p}
end
end
if noises.mood(p) < -0.3 then
local rng = Math.random()
if rng < 0.0015 then
args.decoratives[#args.decoratives + 1] = {name = 'lichen-decal', position = args.p}
end
end
local rng = Math.random()
if rng < 0.004 then
args.decoratives[#args.decoratives + 1] = {name = 'green-asterisk', position = args.p}
end
end
end
end
function Public.chunk_structures(args)
local spec = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.farness(p) > 0.3,
-- spawners_indestructible = noises.farness(p) > 0.75,
spawners_indestructible = false,
spawners_density_perchunk = 100 * Math.slopefromto(noises.mood(p), 0.7, 0.5) * Math.slopefromto(noises.farness(p), 0.35, 1)^2 * args.biter_base_density_scale,
worms_density_perchunk = 60 * Math.slopefromto(noises.mood(p), 0.7, 0.5) * Math.slopefromto(noises.farness(p), 0.25, 1)^2 * args.biter_base_density_scale,
}
end
IslandsCommon.enemies_specworms_separate(args, spec)
local spec2 = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.height(p) > 0.05,
chanceperchunk = 0.25 * Math.slopefromto(noises.farness(p), 0.05, 0.15),
}
end
Public.swamp_structures(args, spec2)
end
function Public.swamp_structures(args, spec)
-- local memory = Memory.get_crew_memory()
-- local overworldx = memory.overworldx or 0
local rng = Math.random()
local left_top = args.left_top
local spec2 = spec{x = left_top.x + 16, y = left_top.y + 16}
if rng < spec2.chanceperchunk then
local struct
struct = Structures.IslandStructures.ROC.swamp_lonely_storage_tank
if struct then
Structures.try_place(struct, args.specials, left_top, 64, 64, function(p) return spec(p).placeable end)
end
end
end
function Public.break_rock(surface, p, entity_name)
return Ores.try_ore_spawn(surface, p, entity_name)
end
function Public.generate_silo_setup_position()
return Hunt.silo_setup_position()
end
local function swamp_tick()
for _, id in pairs(Memory.get_global_memory().crew_active_ids) do
Memory.set_working_id(id)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
if destination.subtype and destination.subtype == IslandsCommon.enum.SWAMP then
if memory.boat and memory.boat.surface_name and memory.boat.surface_name == destination.surface_name then
local surface = game.surfaces[destination.surface_name]
if not (surface and surface.valid) then return end
local island_center = destination.static_params.islandcenter_position
local width = destination.static_params.width
local height = destination.static_params.height
local area = width*height
local period = 5 * Math.ceil(7 / (area/(330*330)))
if game.tick % period == 0 then
local random_x = Math.random(island_center.x - width/2, island_center.x + width/2)
local random_y = Math.random(island_center.y - height/2, island_center.y + height/2)
local random_p = {x = random_x, y = random_y}
local tile = surface.get_tile(random_x, random_y)
if not (tile and tile.valid) then return end
if tile.name == 'water-mud' then
local nearby_characters = surface.find_entities_filtered{position = random_p, radius = 66, name = 'character'}
local nearby_characters_count = #nearby_characters
if nearby_characters_count >= 1 then
Common.create_poison_clouds(surface, random_p)
if Math.random(1, 3) == 1 then
local random_angles = {Math.rad(Math.random(359))}
Common.create_poison_clouds(surface, {x = random_x + 24 * Math.cos(random_angles[1]), y = random_y + 24 * Math.sin(random_angles[1])})
end
end
end
end
end
end
end
end
local event = require 'utils.event'
event.on_nth_tick(5, swamp_tick)
return Public

View File

@ -0,0 +1,92 @@
-- local Common = require 'maps.pirates.common'
-- local Utils = require 'maps.pirates.utils_local'
-- local Math = require 'maps.pirates.math'
local Public = {}
Public.display_names = {'Frozen Pools'}
Public.terraingen_frame_width = 896
Public.terraingen_frame_height = 896
Public.static_params_default = {
starting_time_of_day = 0,
daynightcycletype = 4,
min_brightness = 0.05,
brightness_visual_weights = {1, 1, 1}, --almost pitch black
default_decoratives = false,
base_starting_rock_material = 800,
}
function Public.base_ores()
return {
['copper-ore'] = 2.6,
['iron-ore'] = 2.8,
['coal'] = 4,
['crude-oil'] = 120,
}
end
local rscale = 135
local hscale = 1/100
Public.noiseparams = {
radius = {
type = 'simplex_2d',
normalised = false,
params = {
{wavelength = 0, amplitude = rscale * 1.0},
{wavelength = 2.5, amplitude = rscale * 0.23},
},
},
height_background = {
type = 'simplex_2d',
normalised = false,
params = {
-- shape:
{wavelength = 1600, amplitude = hscale * 22},
{wavelength = 800, amplitude = hscale * 18},
{wavelength = 400, amplitude = hscale * 15},
{wavelength = 300, amplitude = hscale * 11},
{wavelength = 200, amplitude = hscale * 8},
{wavelength = 140, amplitude = hscale * 6},
{wavelength = 100, amplitude = hscale * 4},
-- edges:
{wavelength = 60, amplitude = hscale * 2.5},
{wavelength = 30, amplitude = hscale * 1.5},
{wavelength = 9, amplitude = hscale * 0.5}
},
},
walkways = {
type = 'simplex_2d',
normalised = false,
params = {
{wavelength = 300, amplitude = 15/100},
{wavelength = 65, amplitude = 90/100},
{wavelength = 7, amplitude = 5/100},
},
},
rock = {
type = 'simplex_2d',
normalised = true,
params = {
{wavelength = 100, amplitude = 80},
{wavelength = 50, amplitude = 20},
{wavelength = 6, amplitude = 20},
},
},
mood = {
type = 'simplex_2d',
normalised = true,
params = {
{wavelength = 250, amplitude = 70},
{wavelength = 50, amplitude = 20},
},
},
}
return Public

View File

@ -0,0 +1,158 @@
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
-- local Structures = require 'maps.pirates.structures.structures'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
-- local Data = require 'maps.pirates.surfaces.islands.walkways.data'
local Ores = require 'maps.pirates.ores'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
local Hunt = require 'maps.pirates.surfaces.islands.hunt'
local Public = {}
Public.Data = require 'maps.pirates.surfaces.islands.walkways.data'
function Public.noises(args)
local ret = {}
ret.height = IslandsCommon.island_height_1(args)
ret.walkways = function (p) return Math.abs(args.noise_generator.walkways(p)) end
ret.rock = args.noise_generator.rock
ret.rock_abs = function (p) return Math.abs(ret.rock(p)) end
ret.mood = args.noise_generator.mood
ret.farness = IslandsCommon.island_farness_1(args) --isn't available on the iconized pass, only on actual generation; check args.iconized_generation before you use this
return ret
end
function Public.terrain(args)
-- local memory = Memory.get_crew_memory()
local noises = Public.noises(args)
local p = args.p
if IslandsCommon.place_water_tile(args) then return end
if noises.height(p) < 0.05 then
args.tiles[#args.tiles + 1] = {name = 'water-mud', position = p}
elseif noises.height(p) < 0.1 then
args.tiles[#args.tiles + 1] = {name = 'landfill', position = p}
if Math.random() < 1/50 then
args.decoratives[#args.decoratives + 1] = {name = 'brown-asterisk', position = p, amount = 1}
end
else
if noises.walkways(p) < 0.34 then
args.tiles[#args.tiles + 1] = {name = 'landfill', position = p}
if noises.walkways(p) <= 0.01 then
if Math.random(40) == 1 then
args.entities[#args.entities + 1] = {name = 'big-scorchmark-tintable', position = p}
end
elseif noises.walkways(p) <= 0.02 then
if Math.random(40) == 1 then
args.entities[#args.entities + 1] = {name = 'medium-scorchmark-tintable', position = p}
end
end
if Math.abs(noises.rock(p)) < 0.3 then
if Math.random() < 1/20 then
args.decoratives[#args.decoratives + 1] = {name = 'red-pita', position = p, amount = 1}
end
end
if noises.rock(p) > 0.2 then
if Math.random() < (0.25 - noises.walkways(p))/8 then
args.entities[#args.entities + 1] = IslandsCommon.random_rock_1(p)
end
elseif Math.random() < -(noises.rock(p) + 0.3)/2 then
args.decoratives[#args.decoratives + 1] = {name = 'red-croton', position = p, amount = 1}
end
if noises.height(p) > 0.12 and noises.walkways(p) < 0.1 and noises.rock_abs(p) < 0.07 then
args.entities[#args.entities + 1] = {name = 'coal', position = args.p, amount = 14}
end
else
args.tiles[#args.tiles + 1] = {name = 'water-shallow', position = p}
end
end
end
function Public.chunk_structures(args)
local spec = function(p)
local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
return {
placeable = noises.walkways(p) < 0.30,
density_perchunk = 20 * (noises.farness(p) - 0.1)^3 * args.biter_base_density_scale,
spawners_indestructible = true,
}
end
IslandsCommon.enemies_1(args, spec)
-- local spec2 = function(p)
-- local noises = Public.noises{p = p, noise_generator = args.noise_generator, static_params = args.static_params, seed = args.seed}
-- return {
-- placeable = noises.height(p) > 0.1 and noises.walkways(p) < 0.3,
-- chanceper4chunks = 1/2,
-- }
-- end
-- IslandsCommon.assorted_structures_1(args, spec2)
end
function Public.generate_silo_setup_position()
-- local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface = game.surfaces[destination.surface_name]
local p_silo = Hunt.silo_setup_position(0.2)
local tiles = {}
for x = -6.5, 6.5, 1 do
for y = -6.5, 6.5, 1 do
tiles[#tiles + 1] = {name = CoreData.world_concrete_tile, position = {x = p_silo.x + x, y = p_silo.y + y}}
end
end
Common.ensure_chunks_at(surface, p_silo, 1)
surface.set_tiles(tiles, true)
return p_silo
end
function Public.break_rock(surface, p, entity_name)
return Ores.try_ore_spawn(surface, p, entity_name)
end
local function walkways_tick()
for _, id in pairs(Memory.get_global_memory().crew_active_ids) do
Memory.set_working_id(id)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
if destination.subtype and destination.subtype == IslandsCommon.enum.WALKWAYS then
for _, player in pairs(game.connected_players) do
if player.force.name == memory.force_name and player.surface == game.surfaces[destination.surface_name] and player.character and player.character.valid and game.surfaces[destination.surface_name].get_tile(player.position).name == 'water-shallow' then
player.character.damage(12, game.forces['environment'], 'fire')
if not (player.character and player.character.valid) then
Common.notify_force(player.force, player.name .. ' froze to death.')
end
end
end
end
end
end
local event = require 'utils.event'
event.on_nth_tick(20, walkways_tick)
return Public

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,297 @@
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
-- local Structures = require 'maps.pirates.structures.structures'
local Token = require 'utils.token'
local Task = require 'utils.task'
-- local SurfacesCommon = require 'maps.pirates.surfaces.common'
local Effects = require 'maps.pirates.effects'
local Public = {}
-- local kraken_width = 31 --not old code, just commented for luacheck
-- local kraken_height = 31 --not old code, just commented for luacheck
local kraken_bps = {
[[0eNqd2tuOmzAQgOF38TWR8Nj4wKtUe5EmaIuUhSihh9Uq714IdtWLVpp/r1aRvh1mxgQYnA/z9fJ9uN7GaTH9h7lPx+thmQ+vt/G8ff5leuca8779eTRmPM3T3fRfVji+TsfLRpb362B6My7Dm2nMdHzbPv2c5/MwHU7fhvtitn+czsMayz5eGrOMl2EPcp3v4zLOUzmSfR7IhsefMOvhTrdhGdYQ/9HO6rTssSPSCelMtCDtWqSVPdkXVlqkE9LKKv2uEW6Rjkgri+xI2jt2BGeCbYs0iq1tSCA17tgTHAlGOWeCta0u2iKNMpGANOqfdtHjU3cEB4IjwVaQZrFRQwR1RLuQac/EIR2JFhRblN/fvGfCdCRaBGlllbYlJ2zREemEtLaFlbNctHfVypWXNmvJNaLogHRGWnsFr5xlrm66ZU0X1EVBd5/KA+OR8Yw4bIywUrUXjMod49rvqSOPQUVr73KVO8ZhMh3jgfHIeGI8I64+IR16BLAenQOerZJnq+TZKnm2Sp6tkmerxEbDylmpwkrVju4WzYdFe6QtC25h9I7xwHhkPDGeERe2psKSEZaM+gQr8yXSgrRjmbDgFkb3jHeMB8Yj44lx7QkT2RMhm6grd4x7xjvGA+ORce0yJTZzJLZMiTUysUYm1kj2xqFybSMza2RmubO3DtKSubNobeqVB8a1qaORWdi+jNjP5KLea7Fss4VN2CLkjUzRGWl1G9lMK2ymFYcKdahQNucVrh2vK1de7sSjQj0qlI1KhWuHQmHTiaAtI2F7RsIewgtXp452gorukGaZZKTVTWTPghI+00TtdCpoI0bQTkzRCemMtGWpqLvCnnYK1853lf+z1Jdm/wlF/9cPMhrzY7jdnwEkWR+zRO+Cz+sBH78BXJmyLg==]],
[[0eNqdmt2O2jAQRt/F19lVPPFfeJVqVbFgbSNBQJBti1Z59ybEqaoWtXN6hSKdDJ/HY/sbw4d5Pbzn86XrB7P5MNd+e34aTk9vl24/P383m6apzG3+GCvT7U791Ww+TWD31m8PMzLcztlsTDfko6lMvz3OT99Op33un3Zf8nUw84v9Pk+x7PhSmdwP3dDlJc794fa5fz++5ssEPI5QmfPpOr106osoG579XVb97Mex+iOOKOPUJcwc71GcRhmnkX8EcnRgc8RxTtfQHUqufkeXLxx/Rp4mZ3fJQzbz9z+EhcANgZ0OFg57HbxUaSCwVYZ2JPQCW4doJMQyJRHRSUd7kpIFjgRGMphoQUJEGTsQ2QHNTECDDEh2JLIjKr+IBrnQ4hGNlAhTosxJIglMaLUnlO6FFkF0g2jlntaSlLQoJQstNaK1J2RNdBdaK7zgWuV2OVRbRKu1FFwbXZCWhbY1wz3DA8O12ostsAwXhjcMdwz3DA8MjwxPDNdOk0ML1bG0M/9mmYGzzMFZZuEs83ArztKuPWFWnOVdWN6F5V1Y3rWOwXrSqFjknS0yzxa550K3iFbv7J5tpp4tU8+WqWfL1LNl6tkyZV3LirNpEjZNwqZJvQn4/1lIwjIj2swEcvlQaI9otRcIrNoDq/bAqj2wamfdq2Xt64qzOVWXQEQlENkWFlkNRFYDkdVAZDXAenvLmvsVTwxnk6qugYRqIKEzMrEzMrECS6wE2HWDZfcNKx4ZnhjO8t5o894iU4UuNAqtTiO70ii4dqBSE69Z6Ihoy4JrjcaKC8LVebHkcC90QLRluLZPWnHHcOU6EvSTi7DbmIKrpQuT3iDp7P6j4GrpDZPukHR2oSHsDkFYH77i2pGiTllYTyisJxTWtRVcmBh1HlG3IawfEObYC67eeiOSHtFeykyvMBcrzMVKQtITOkyZtRNm7QT5HfmL33mplj96bH7520hlvubL9f6+JOtiK9E1wbVTGz/+ALfE6PI=]],
[[0eNqdmctu4kAQRf+l10aiyv3kV0ZZMGBlLBEbgecRRfz7GOgezSZSnawipEP51u1HbuEP9/30czhfxmlxuw93nfbnzTJvXi/j8f75j9v1fefe739unRsP83R1u28rOL5O+9MdWd7Pg9u5cRneXOem/dv90+95Pg7T5vBjuC7u/sXpOKy15PbSuWU8Dc8i5/k6LuM8tSc9HiT+9q/M+rjDZViGtcRndEB0tNH+QXsCBwIjGUy0IiHKaidEZxsdiNmBmB2Qf5VOhNYe0ahJRV1a3Y7EwEh2a0QHMqK1icjtiNxODzoR2Npk+oIQ67LnB50JXAgsSIdVdSFCChJS0B4paGlkS3RX2iq84ioMVyMuTzFbhkeEK6tublWfYiCuDI8MTwwvCDcb2bNWWbYSFq6EpauGJ4Znhlt99+x4eLYjK94z3DM8MDwyPDE8I1yZM+b7+hmKekQXRJuPXmBbILAtENgWYJFYWCZueGY4812Z78p8V2akMiOVGanMSLUaGcnEJWhiEDQyVDohOiOaeWI+pZGdUjYbCRuOGs5sFOajMCPNOSayQBjZHRDZKU1sVdkw2HAoJjE8M9y6qpkdj8yMZBNnwyPDE8OtRhYWCAsLD4W1WtCq6hZl34YLwwPDrdrZWNvwwHAmRpkY66WnylpV1qp+RYz1Bm648eLQnvzTrrTZGDbVKptqK26dUBpuFeNJ9ql0QbS5UTYXKnsvoYFEwkpnRAsrbk0PiqKvouirLBEqS4QVt04nmshEUOmIaGHFhVWHjVrHMM1krlIWepSFHi3kBxBlqaThsLpneGC40Zi+xgxEK6KNtvSClMjnSl665+v33X8v8zv3a7hcH9/XLD4VTb6Pvqyb+fYX8FohZA==]],
[[0eNqV2dtq4zAQgOF30bUD0ehk+1WWXqSJ6BpSO8TeQyl599qxtOzFLsx/VQJf5NF0LI2UT/N6/ZFv92FcTP9p5vF0OyzT4e0+XLbPv03vXGM+tj+PxgznaZxN/22Fw9t4um5k+bhl05thye+mMePpffv0a5oueTycv+d5MdsXx0tex7KPl8YswzXvg9ymeViGaaxPej7IxsefYdbHne95yesQ/9ESdNo/NcKR4ESwRZpNUVDYoowkPLUnGI3cEtwRbFEc2lxHEkhEgexaPNLKsNMeyRHpRLQ27hZF0qKVYdfikFbG3e2RWKQj0SJIK2dpj3sownhEXJRpsZYFY1kwhXeIi7IUrbDYhcUuLHbHgincMx4Yj4wnxpU7hfUsM4U7xj3jgfHIeGIcJrJDXP02ebZweLREWtQvFK3dkyq3jLPQ1QUZWEEGVpCBFSTrvCpvGWf/VWGJFO3okTTGRQekI9IJ6RZpdTFGVoyRFWNkxci678phZli5CEuksEQKm6qwqcI3wx0Z166miS2+iS2+iRVwYgWcWAGzY1jlLeMd4sIyo72TsOxUWLll3DMeGIdTTYy3jHeIC8uM9tLBdqgL61gNsCN25YnxlnE2VW2vLEeSR2En+MoT4tq+vXLl+isWzZRdD1SeENdu2ZUrXyURVO3CbhMqT4hr3+vKtcE4NlV23i9c2/uIRxXmWejshF24tg8TdL0u6H5d2FFM2GFJ0IFGWCcurD2VRA5uRQek1a8d69gK1zbWwhqNwtWjs/20cO3oju1hju1hhcNgtCcUx3alwv89+kuz/7za//VjbWN+5vv8HEBa61Mnybvou3UZeXwB67xzTA==]]
}
Public.kraken_slots = 4
local kraken_positions = {
[1] = {first = {x = -40.5, y = -59.5}, second = {x = -15.5, y = -49.5}, third = {x = -32.5, y = -39.5}, final = {x = -24.5, y = -30.5},},
[2] = {first = {x = 28.5, y = -59.5}, second = {x = 10.5, y = -49.5}, third = {x = 30.5, y = -39.5}, final = {x = 13.5, y = -30.5},},
[3] = {first = {x = -40.5, y = 59.5}, second = {x = -15.5, y = 49.5}, third = {x = -32.5, y = 39.5}, final = {x = -24.5, y = 29.5},},
[4] = {first = {x = 28.5, y = 59.5}, second = {x = 10.5, y = 49.5}, third = {x = 30.5, y = 39.5}, final = {x = 13.5, y = 29.5},},
-- [1] = {first = {x = 96.5, y = 0.5}, second = {x = 81.5, y = 5.5}, third = {x = 66.5, y = -4.5}, final = {x = 51.5, y = 0.5},}
}
local kraken_tick_token =
Token.register(
function(data)
Public.kraken_tick(data.crew_id, data.kraken_id, data.step, data.substep)
end
)
function Public.kraken_tick(crew_id, kraken_id, step, substep)
Memory.set_working_id(crew_id)
local memory = Memory.get_crew_memory()
if not (memory.id and memory.id > 0) then return end --check if crew disbanded
if memory.game_lost then return end
local surface = game.surfaces[memory.sea_name]
local kraken_data = memory.active_sea_enemies.krakens[kraken_id]
if not kraken_data then return end --check if kraken died
local kraken_spawner_entity = kraken_data.spawner_entity
if step == 1 then
if substep == 1 then
Effects.kraken_effect_3(surface, kraken_data.position, 10)
end
if substep < 32 then
Effects.kraken_effect_1(surface, kraken_data.position, substep/32 * 6.283)
Task.set_timeout_in_ticks(1, kraken_tick_token, {crew_id = crew_id, kraken_id = kraken_id, step = 1, substep = substep + 1})
else
Task.set_timeout_in_ticks(1, kraken_tick_token, {crew_id = crew_id, kraken_id = kraken_id, step = 2, substep = 1})
end
elseif step == 2 then
local p1 = kraken_positions[kraken_id].first
local p2 = kraken_positions[kraken_id].second
local p3 = kraken_positions[kraken_id].third
local p4 = kraken_positions[kraken_id].final
if substep <= 30 then
if substep < 5 then
Public.kraken_move(kraken_id, p1, substep % 4 + 1)
elseif substep < 10 then
Public.kraken_move(kraken_id, Utils.interpolate(p1, p2, (substep-5) / 5), substep % 4 + 1)
elseif substep < 15 then
Public.kraken_move(kraken_id, p2, substep % 4 + 1)
elseif substep < 20 then
Public.kraken_move(kraken_id, Utils.interpolate(p2, p3, (substep-15) / 5), substep % 4 + 1)
elseif substep < 25 then
Public.kraken_move(kraken_id, p3, substep % 4 + 1)
elseif substep <= 30 then
Public.kraken_move(kraken_id, Utils.interpolate(p3, p4, (substep-25) / 5), substep % 4 + 1)
end
Task.set_timeout_in_ticks(15, kraken_tick_token, {crew_id = crew_id, kraken_id = kraken_id, step = 2, substep = substep + 1})
else
Task.set_timeout_in_ticks(6, kraken_tick_token, {crew_id = crew_id, kraken_id = kraken_id, step = 3, substep = 1})
end
elseif step == 3 then
Public.kraken_move(kraken_id, kraken_data.position, substep % 4 + 1)
-- regen:
local healthbar = memory.healthbars and memory.healthbars[kraken_spawner_entity.unit_number]
if healthbar then
local new_health = Math.min(healthbar.health + Balance.kraken_regen_scale, kraken_data.max_health)
healthbar.health = new_health
Common.update_healthbar_rendering(healthbar, new_health)
end
if substep % 4 == 0 then
local crewmembers = Common.crew_get_crew_members()
local p_can_fire_at = {}
for _, player in pairs(crewmembers) do
local p = player.position
if player.surface == surface then
-- if player.surface == surface and Public.on_boat(memory.boat, p) then
p_can_fire_at[#p_can_fire_at + 1] = p
end
end
if #p_can_fire_at > 0 then
local p_fire = p_can_fire_at[Math.random(#p_can_fire_at)]
local stream = surface.create_entity{
name = 'acid-stream-spitter-big',
position = kraken_data.position,
force = memory.enemy_force_name,
source = kraken_data.position,
target = p_fire,
max_range = 500,
speed = 0.1
}
memory.kraken_stream_registrations[#memory.kraken_stream_registrations + 1] = {number = script.register_on_entity_destroyed(stream), position = p_fire}
Effects.kraken_effect_4(surface, kraken_data.position)
end
end
if substep % 50 > 40 then
-- if substep % 70 == 69 then
-- Public.kraken_spawn_biters(kraken_id)
-- end
Task.set_timeout_in_ticks(5, kraken_tick_token, {crew_id = crew_id, kraken_id = kraken_id, step = 3, substep = substep + 1})
elseif substep % 50 > 30 then
Task.set_timeout_in_ticks(10, kraken_tick_token, {crew_id = crew_id, kraken_id = kraken_id, step = 3, substep = substep + 1})
else
Task.set_timeout_in_ticks(30, kraken_tick_token, {crew_id = crew_id, kraken_id = kraken_id, step = 3, substep = substep + 1})
end
end
end
local function on_entity_destroyed(event)
local registration_number = event.registration_number
local p
local memory
for i = 1,3 do
Memory.set_working_id(i)
memory = Memory.get_crew_memory()
if memory.kraken_stream_registrations then
for j, r in pairs(memory.kraken_stream_registrations) do
if r.number == registration_number then
p = r.position
memory.kraken_stream_registrations = Utils.ordered_table_with_index_removed(memory.kraken_stream_registrations, j)
break
end
end
end
if p then break end
end
if p then
local surface = game.surfaces[memory.sea_name]
if not surface and surface.valid then return end
local spits_here = surface.find_entities_filtered{position = p, radius = 0.5, name = 'acid-splash-fire-spitter-big'}
if spits_here and #spits_here > 0 then
for _, s in pairs(spits_here) do
if s.valid then s.destroy() end
end
end
local p2 = surface.find_non_colliding_position('medium-biter', p, 10, 0.2)
if not p2 then return end
local name = Common.get_random_unit_type(memory.evolution_factor + Balance.kraken_spawns_base_extra_evo)
surface.create_entity{name = name, position = p2, force = memory.enemy_force_name}
Effects.kraken_effect_2(surface, p2)
local evo_increase = Balance.kraken_evo_increase_per_shot()
if not memory.kraken_evo then memory.kraken_evo = 0 end
memory.kraken_evo = memory.kraken_evo + evo_increase
Common.increment_evo(evo_increase)
end
end
function Public.try_spawn_kraken()
local memory = Memory.get_crew_memory()
local surface = game.surfaces[memory.sea_name]
if not (surface and surface.valid) then return end -- check sea still exists
if not memory.active_sea_enemies then memory.active_sea_enemies = {} end
if not memory.active_sea_enemies.krakens then memory.active_sea_enemies.krakens = {} end
local possible_slots = {}
for i = 1, Public.kraken_slots do
if not memory.active_sea_enemies.krakens[i] then
possible_slots[#possible_slots + 1] = i
end
end
if #possible_slots > 0 then
local kraken_id = possible_slots[Math.random(#possible_slots)]
-- if _DEBUG then game.print('spawning kraken in slot ' .. kraken_id) end
local p = kraken_positions[kraken_id].first
memory.active_sea_enemies.krakens[kraken_id] = {
state = 'submerged',
position = p,
max_health = Balance.kraken_health(),
spawner_entity = nil,
frame = nil,
}
Task.set_timeout_in_ticks(10, kraken_tick_token, {crew_id = memory.id, kraken_id = kraken_id, step = 1, substep = 1})
end
end
function Public.kraken_move(kraken_id, new_p, new_frame)
local memory = Memory.get_crew_memory()
local surface = game.surfaces[memory.sea_name]
if not surface and surface.valid then return end -- check sea still exists
local kraken_data = memory.active_sea_enemies.krakens[kraken_id]
local kraken_tile = CoreData.kraken_tile
local old_p = kraken_data.position
local old_frame = kraken_data.frame
local new_p_2 = {x = Math.ceil(new_p.x), y = Math.ceil(new_p.y)}
local old_p_2 = {x = Math.ceil(old_p.x), y = Math.ceil(old_p.y)}
local new_tile_positions = Common.tile_positions_from_blueprint_arrayform(kraken_bps[new_frame], Utils.psum{new_p_2, {x = -16, y = -16}})
local old_tile_positions = {}
if old_frame then
old_tile_positions = Common.tile_positions_from_blueprint_arrayform(kraken_bps[old_frame], Utils.psum{old_p_2, {x = -16, y = -16}})
end
local new_tile_positions2 = Utils.exclude_position_arrays(new_tile_positions, old_tile_positions)
local tiles1 = {}
for x, xtab in pairs(new_tile_positions2) do
for y, _ in pairs(xtab) do
tiles1[#tiles1 + 1] = {name = kraken_tile, position = {x = x, y = y}}
end
end
surface.set_tiles(tiles1)
if kraken_data.spawner_entity and kraken_data.spawner_entity.valid then
kraken_data.spawner_entity.teleport(new_p_2.x - old_p_2.x, new_p_2.y - old_p_2.y)
else
kraken_data.spawner_entity = surface.create_entity{name = 'biter-spawner', position = new_p_2, force = memory.enemy_force_name}
Common.new_healthbar(true, kraken_data.spawner_entity, kraken_data.max_health, kraken_id)
end
if old_frame then --cleanup old tiles
local old_tile_positions2 = Utils.exclude_position_arrays(old_tile_positions, new_tile_positions)
local tiles2 = {}
for x, xtab in pairs(old_tile_positions2) do
for y, _ in pairs(xtab) do
tiles2[#tiles2 + 1] = {name = 'deepwater', position = {x = x, y = y}}
end
end
surface.set_tiles(tiles2, true, false)
end
kraken_data.position = new_p
kraken_data.frame = new_frame
end
function Public.kraken_die(kraken_id)
local memory = Memory.get_crew_memory()
local surface = game.surfaces[memory.sea_name]
if not surface and surface.valid then return end -- check sea still exists
local kraken_data = memory.active_sea_enemies.krakens[kraken_id]
if kraken_data.spawner_entity and kraken_data.spawner_entity.valid then
kraken_data.spawner_entity.destroy()
end
Effects.kraken_effect_5(surface, kraken_data.position)
local tiles2 = {}
for x = -16, 16 do
for y = -16, 16 do
tiles2[#tiles2 + 1] = {name = 'deepwater', position = Utils.psum{kraken_data.position, {x = x, y = y}}}
end
end
surface.set_tiles(tiles2, true, false)
memory.active_sea_enemies.krakens[kraken_id] = nil
local reward = Balance.kraken_kill_reward()
Common.give_items_to_crew(reward)
local force = memory.force
if not (force and force.valid) then return end
Common.notify_force_light(force,'Granted ' .. reward[1].count .. ' [item=sulfuric-acid-barrel]')
end
local event = require 'utils.event'
event.add(defines.events.on_entity_destroyed, on_entity_destroyed)
return Public

View File

@ -0,0 +1,60 @@
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
-- local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
-- local CoreData = require 'maps.pirates.coredata'
-- local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
-- local Token = require 'utils.token'
-- local Task = require 'utils.task'
-- local Kraken = require 'maps.pirates.surfaces.sea.kraken'
local SurfacesCommon = require 'maps.pirates.surfaces.common'
local Public = {}
local enum = {
DEFAULT = 'Default',
}
Public.enum = enum
Public.Data = {}
Public.Data.width = 384
Public.Data.height = 384
function Public.ensure_sea_surface()
local memory = Memory.get_crew_memory()
local seaname = SurfacesCommon.encode_surface_name(memory.id, 0, SurfacesCommon.enum.SEA, enum.DEFAULT)
if not game.surfaces[seaname] then
local width = Public.Data.width
local height = Public.Data.height
local map_gen_settings = Common.default_map_gen_settings(width, height)
map_gen_settings.autoplace_settings.decorative.treat_missing_as_default = false
local surface = game.create_surface(seaname, map_gen_settings)
surface.freeze_daytime = true
surface.daytime = 0
surface.show_clouds = false
memory.sea_name = seaname
end
end
function Public.terrain(args)
args.tiles[#args.tiles + 1] = {name = 'deepwater', position = args.p}
local fishrng = Math.random(110)
if fishrng == 1 then
args.entities[#args.entities + 1] = {name = 'fish', position = args.p}
end
return nil
end
function Public.chunk_structures()
return nil
end
return Public

View File

@ -0,0 +1,872 @@
local Memory = require 'maps.pirates.memory'
local Math = require 'maps.pirates.math'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local Utils = require 'maps.pirates.utils_local'
local _inspect = require 'utils.inspect'.inspect
local Boats = require 'maps.pirates.structures.boats.boats'
-- local Lobby = require 'maps.pirates.surfaces.lobby'
-- local Dock = require 'maps.pirates.surfaces.dock'
local Hold = require 'maps.pirates.surfaces.hold'
local Cabin = require 'maps.pirates.surfaces.cabin'
-- local Sea = require 'maps.pirates.surfaces.sea.sea'
local Islands = require 'maps.pirates.surfaces.islands.islands'
local Crowsnest = require 'maps.pirates.surfaces.crowsnest'
local Quest = require 'maps.pirates.quest'
local Parrot = require 'maps.pirates.parrot'
local ShopMerchants = require 'maps.pirates.shop.merchants'
local SurfacesCommon = require 'maps.pirates.surfaces.common'
-- local Roles = require 'maps.pirates.roles.roles'
local Classes = require 'maps.pirates.roles.classes'
local Server = require 'utils.server'
local Public = {}
local enum = SurfacesCommon.enum
Public.enum = enum
Public[enum.SEA] = require 'maps.pirates.surfaces.sea.sea'
Public[enum.ISLAND] = require 'maps.pirates.surfaces.islands.islands'
Public[enum.DOCK] = require 'maps.pirates.surfaces.dock'
Public[enum.CROWSNEST] = require 'maps.pirates.surfaces.crowsnest'
Public[enum.LOBBY] = require 'maps.pirates.surfaces.lobby'
Public[enum.HOLD] = require 'maps.pirates.surfaces.hold'
Public[enum.CABIN] = require 'maps.pirates.surfaces.cabin'
Public[enum.CHANNEL] = require 'maps.pirates.surfaces.channel.channel'
Public['SurfacesCommon'] = require 'maps.pirates.surfaces.common'
function Public.initialise_destination(o)
o = o or {}
local memory = Memory.get_crew_memory()
assert(memory.destinations, o.overworld_position)
local scope = Public.get_scope(o)
o.destination_index = #memory.destinations + 1 --assuming none are deleted
memory.destinations[o.destination_index] = o
o.dynamic_data = o.dynamic_data or {}
o.static_params = o.static_params or Utils.deepcopy(Public.get_scope(o).Data.static_params_default)
o.seed = o.seed or Math.random(1, 10000000)
o.iconized_map = o.iconized_map or {}
o.boat_extra_distance_from_shore = o.boat_extra_distance_from_shore or 0
o.surface_name = o.surface_name or SurfacesCommon.encode_surface_name(memory.id, o.destination_index, o.type, o.subtype)
o.dynamic_data.other_map_generation_data = o.dynamic_data.other_map_generation_data or {}
if o.type == enum.ISLAND then
o.init_boat_state = Boats.enum_state.APPROACHING
Public.generate_detailed_island_data(o)
elseif o.type == enum.DOCK then
o.init_boat_state = Boats.enum_state.APPROACHING
o.iconized_map_width = scope.Data.iconized_map_width
o.iconized_map_height = scope.Data.iconized_map_height
end
return o
end
function Public.get_scope(destination)
if destination.type then
if destination.subtype then
return Public[destination.type][destination.subtype]
else
return Public[destination.type]
end
else
return {}
end
end
function Public.on_surface_generation(destination)
-- local memory = Memory.get_crew_memory()
-- game.map_settings.pollution.enemy_attack_pollution_consumption_modifier = Balance.defaultai_attack_pollution_consumption_modifier()
-- Event_functions.flamer_nerfs()
if destination.type == enum.ISLAND then
local subtype = destination.subtype
destination.dynamic_data.rocketsilomaxhp = Balance.silo_max_hp
destination.dynamic_data.rocketsilohp = Balance.silo_max_hp
destination.dynamic_data.rocketsilochargedbools = {}
destination.dynamic_data.rocketsiloenergyconsumed = 0
destination.dynamic_data.rocketsiloenergyconsumedwithinlasthalfsecond = 0
destination.dynamic_data.energychargedinsilosincelastcheck = 0
destination.dynamic_data.silocharged = false
destination.dynamic_data.rocketlaunched = false
if subtype ~= Islands.enum.STANDARD and subtype ~= Islands.enum.STANDARD_VARIANT and subtype ~= Islands.enum.RADIOACTIVE and subtype ~= Islands.enum.RED_DESERT then
destination.dynamic_data.hidden_ore_remaining_abstract = Utils.deepcopy(destination.static_params.abstract_ore_amounts)
end
destination.dynamic_data.wood_remaining = destination.static_params.starting_wood
destination.dynamic_data.rock_material_remaining = destination.static_params.starting_rock_material
destination.dynamic_data.treasure_remaining = destination.static_params.starting_treasure
destination.dynamic_data.ore_types_spawned = {}
destination.dynamic_data.buried_treasure = {}
-- elseif destination.type == enum.DOCK then
end
end
function Public.destination_on_collide(destination)
local memory = Memory.get_crew_memory()
local name = destination.static_params.name and destination.static_params.name or 'NameNotFound'
local message = '[' .. memory.name .. '] Loading destination ' .. (memory.destinationsvisited_indices and (#memory.destinationsvisited_indices + 1) or 0) .. ', ' .. name .. '.'
Common.notify_game(message)
if destination.type ~= Public.enum.DOCK then
local index = destination.destination_index
Crowsnest.paint_around_destination(index, CoreData.overworld_loading_tile)
end
if destination and destination.static_params and destination.static_params.base_cost_to_undock then
local replace = {}
for item, count in pairs(destination.static_params.base_cost_to_undock) do
if item == 'uranium-235' or item == 'launch_rocket' then
replace[item] = count
else
replace[item] = Math.ceil(count * Balance.cost_to_leave_multiplier())
end
end
destination.static_params.base_cost_to_undock = replace
end
if destination.type == Public.enum.ISLAND then
local index = destination.destination_index
Crowsnest.paint_around_destination(index, CoreData.overworld_loading_tile)
if destination.subtype == Islands.enum.RADIOACTIVE then
Parrot.parrot_radioactive_tip_1()
else
local scheduled_raft_raids
-- temporarily placed this back here, as moving it to shorehit broke things:
local playercount = Common.activecrewcount()
local max_evo
local difficulty_name = CoreData.get_difficulty_name_from_value(Common.difficulty())
if difficulty_name == CoreData.difficulty_options[1].text then
if memory.overworldx/40 < 20 then
max_evo = 0.9 - (20 - memory.overworldx/40) * 1/100
else
max_evo = 0.91 + (memory.overworldx/40 - 20) * 0.25/100
end
elseif difficulty_name == CoreData.difficulty_options[2].text then
if memory.overworldx/40 < 15 then
max_evo = 0.9 - (15 - memory.overworldx/40) * 0.5/100
else
max_evo = 0.91 + (memory.overworldx/40 - 15) * 0.25/100
end
else
if memory.overworldx/40 < 12 then
max_evo = 0.9
else
max_evo = 0.91 + (memory.overworldx/40 - 12) * 0.25/100
end
end
if memory.overworldx > 200 then
scheduled_raft_raids = {}
local times = {600, 360, 215, 210, 120, 30, 10, 5}
for i = 1, #times do
local t = times[i]
if Math.random(6) == 1 and #scheduled_raft_raids < 6 then
scheduled_raft_raids[#scheduled_raft_raids + 1] = {timeinseconds = t, max_evo = max_evo}
-- scheduled_raft_raids[#scheduled_raft_raids + 1] = {timeinseconds = t, max_bonus_evolution = 0.52}
end
end
elseif memory.overworldx == 200 or _DEBUG then
local times
if playercount <= 2 then
times = {1, 5, 10, 15, 20}
elseif playercount <= 8 then
times = {1, 5, 10, 15, 20, 25}
elseif playercount <= 15 then
times = {1, 5, 10, 15, 20, 25, 30}
elseif playercount <= 21 then
times = {1, 5, 10, 15, 20, 25, 30, 35}
else
times = {1, 5, 10, 15, 20, 25, 30, 35, 40}
end
scheduled_raft_raids = {}
for _, t in pairs(times) do
-- scheduled_raft_raids[#scheduled_raft_raids + 1] = {timeinseconds = t, max_bonus_evolution = 0.62}
scheduled_raft_raids[#scheduled_raft_raids + 1] = {timeinseconds = t, max_evo = max_evo}
end
end
destination.static_params.scheduled_raft_raids = scheduled_raft_raids
end
end
if memory.overworldx == 40*5 then
Parrot.parrot_boats_warning()
elseif memory.overworldx == 800 then
Parrot.parrot_800_tip()
end
end
function Public.destination_on_arrival(destination)
local memory = Memory.get_crew_memory()
-- game.map_settings.pollution.enemy_attack_pollution_consumption_modifier = Balance.defaultai_attack_pollution_consumption_modifier()
-- Event_functions.flamer_nerfs()
memory.floating_pollution = 0
-- catching a fallthrough error I've observed
if not memory.active_sea_enemies then memory.active_sea_enemies = {} end
memory.active_sea_enemies.krakens = {}
if destination.type == enum.ISLAND then
destination.dynamic_data.rocketsiloenergyneeded = Balance.silo_energy_needed_MJ() * 1000000
destination.dynamic_data.rocketcoalreward = Balance.launch_fuel_reward()
if destination.subtype == Islands.enum.RADIOACTIVE then
destination.dynamic_data.time_remaining = -1
elseif destination.subtype == Islands.enum.MAZE then --more time
destination.dynamic_data.time_remaining = Math.ceil(1.08 * Balance.max_time_on_island())
else
destination.dynamic_data.time_remaining = Math.ceil(Balance.max_time_on_island())
end
if destination.subtype ~= Islands.enum.FIRST and destination.subtype ~= Islands.enum.RADIOACTIVE then
Quest.initialise_random_quest()
-- else
-- if _DEBUG then
-- Quest.initialise_random_quest()
-- end
end
memory.enemy_force.reset_evolution()
local base_evo = Balance.base_evolution_leagues(memory.overworldx)
Common.set_evo(base_evo)
destination.dynamic_data.evolution_accrued_leagues = base_evo
destination.dynamic_data.evolution_accrued_time = 0
if destination.subtype == Islands.enum.RED_DESERT then
destination.dynamic_data.evolution_accrued_sandwurms = 0
else
destination.dynamic_data.evolution_accrued_nests = 0
end
if destination.subtype ~= Islands.enum.RADIOACTIVE then
destination.dynamic_data.evolution_accrued_silo = 0
end
memory.scripted_biters = {}
memory.scripted_unit_groups = {}
memory.floating_pollution = 0
if destination.subtype == Islands.enum.RADIOACTIVE then
Islands[Islands.enum.RADIOACTIVE].spawn_structures()
end
-- -- invulnerable bases on islands 21-25
-- if memory.overworldx >= 21 and memory.overworldx < 25 then
-- local surface = game.surfaces[destination.surface_name]
-- if not (surface and surface.valid) then return end
-- local spawners = surface.find_entities_filtered({type = 'unit-spawner', force = memory.enemy_force_name})
-- for _, s in pairs(spawners) do
-- s.destructible = false
-- end
-- end
elseif destination.type == enum.DOCK then
-- -- kick players out of crow's nest
-- local crowsnestname = SurfacesCommon.encode_surface_name(memory.id, 0, enum.CROWSNEST, nil)
-- for _, player in pairs(game.connected_players) do
-- if player.surface.name == crowsnestname then
-- Public.player_exit_crows_nest(player, {x = 5, y = 0})
-- end
-- end
-- New dock class sales:
local class_for_sale = Classes.generate_class_for_sale()
destination.static_params.class_for_sale = class_for_sale
end
local name = destination.static_params.name and destination.static_params.name or 'NameNotFound'
local message = 'Approaching destination ' .. (memory.destinationsvisited_indices and #memory.destinationsvisited_indices or 0) .. ', ' .. name .. '.'
if not (#memory.destinationsvisited_indices and #memory.destinationsvisited_indices == 1) then --don't need to notify for the first island
Server.to_discord_embed_raw((destination.static_params.discord_emoji or CoreData.comfy_emojis.wut) .. '[' .. memory.name .. '] Approaching ' .. name .. ', ' .. memory.overworldx .. ' leagues.')
end
if destination.static_params.name == 'Dock' then
message = message .. ' ' .. 'New trades are available in the Captain\'s Store.'
end
Common.notify_force(memory.force, message)
if destination.type == enum.ISLAND then
local points_to_avoid = {}
if destination.subtype ~= Islands.enum.RADIOACTIVE then
local silo_position = Islands.spawn_silo_setup()
if silo_position then
points_to_avoid[#points_to_avoid + 1] = {x = silo_position.x, y = silo_position.y, r = 22}
end
end
Islands.spawn_ores_on_arrival(destination, points_to_avoid)
if memory.overworldx >= Balance.covered_first_appears_at and destination.subtype ~= Islands.enum.RADIOACTIVE then
local class_for_sale = Classes.generate_class_for_sale()
destination.static_params.class_for_sale = class_for_sale
local covered = Islands.spawn_covered(destination, points_to_avoid)
points_to_avoid[#points_to_avoid + 1] = {x = covered.x, y = covered.y, r = 25}
end
Islands.spawn_treasure_maps(destination, points_to_avoid)
Islands.spawn_ghosts(destination, points_to_avoid)
if destination.subtype and destination.subtype == Islands.enum.MAZE then
local force = memory.force
force.manual_mining_speed_modifier = 0.8
end
end
end
function Public.destination_on_departure(destination)
local memory = Memory.get_crew_memory()
-- local boat = memory.boat
if memory.overworldx == 40*9 then
Parrot.parrot_kraken_warning()
end
if destination.subtype and destination.subtype == Islands.enum.MAZE then
local force = memory.force
force.manual_mining_speed_modifier = 3 --put back to normal
end
end
function Public.destination_on_crewboat_hits_shore(destination)
local memory = Memory.get_crew_memory()
local boat = memory.boat
destination.dynamic_data.timeratlandingtime = Common.current_destination().dynamic_data.timer
Boats.place_landingtrack(boat, CoreData.landing_tile)
Boats.place_boat(boat, CoreData.static_boat_floor, false, false)
if destination.type == enum.ISLAND then
if memory.overworldx == 0 then
Parrot.parrot_0()
-- elseif memory.overworldx == 80 then
-- Parrot.parrot_80()
end
if destination.subtype == Islands.enum.RADIOACTIVE then
-- replace all miners, so that they sit on uranium properly:
local surface = game.surfaces[destination.surface_name]
local miners = surface.find_entities_filtered({name = 'electric-mining-drill'})
for _, m in pairs(miners) do
local direction = m.direction
local position = m.position
m.destroy()
surface.create_entity{name = 'electric-mining-drill', direction = direction, position = position}
end
Parrot.parrot_radioactive_tip_2()
elseif destination.subtype == Islands.enum.MAZE and memory.overworldx == Common.maze_minimap_jam_league then
Parrot.parrot_maze_tip_1()
end
if memory.merchant_ships_unlocked or _DEBUG then
Islands.spawn_merchant_ship(destination)
ShopMerchants.generate_merchant_trades(destination.dynamic_data.merchant_market)
end
end
if destination and destination.surface_name and game.surfaces[destination.surface_name] and game.surfaces[destination.surface_name].valid and (not (destination.dynamic_data and destination.dynamic_data.initial_spawner_count)) then
--Note: This gives the wrong answer on the first island. Because the terrain hasn't finished generating yet.
destination.dynamic_data.initial_spawner_count = Common.spawner_count(game.surfaces[destination.surface_name])
end
end
function Public.generate_detailed_island_data(destination)
local scope = Public.get_scope(destination)
local frame_width = scope.Data.terraingen_frame_width
local frame_height = scope.Data.terraingen_frame_height
local boat_extra_distance_from_shore = destination.boat_extra_distance_from_shore
-- scale 1:32
local chunks_horizontal = 2 * Math.floor(frame_width/64)
local chunks_vertical = 2 * Math.floor(frame_height/64)
local entities = {}
local entitymap = {}
local tiles = {}
local tiles2 = {}
local leftboundary, rightboundary, topboundary, bottomboundary = chunks_horizontal/2 + 1, -chunks_horizontal/2 - 1, chunks_vertical/2 + 1, -chunks_vertical/2 - 1 -- reversed, because we'll iterate
-- local subtype = destination.subtype
local terrain_fn = scope.terrain
local noise_generator = Utils.noise_generator(scope.Data.noiseparams, destination.seed)
for y = -chunks_vertical/2, chunks_vertical/2 - 1, 1 do
for x = -chunks_horizontal/2, chunks_horizontal/2 - 1, 1 do
local macroposition = {x = x + 0.5, y = y + 0.5}
local chunk_frameposition_topleft = {x = x * 32, y = y * 32}
-- average over the chunk
local modalcounts = {}
for y2 = 5, 27, 11 do
for x2 = 5, 27, 11 do
local p2 = {x = chunk_frameposition_topleft.x + x2, y = chunk_frameposition_topleft.y + y2}
local tiles3, entities3 = {}, {}
terrain_fn{p = p2, noise_generator = noise_generator, static_params = destination.static_params, tiles = tiles3, entities = entities3, decoratives = {}, seed = destination.seed, iconized_generation = true}
local tile = tiles3[1]
if modalcounts[tile.name] then
modalcounts[tile.name] = modalcounts[tile.name] + 1
else
modalcounts[tile.name] = 1
end
if y2 == 16 and x2 == 16 and #entities3 > 0 and entities3[1] and entities3[1].visible_on_overworld then
entitymap[macroposition] = entities3[1].name
end
end
end
local modaltile, max = 'hazard-concrete-left', 0
for k, v in pairs(modalcounts) do
if v > max then
modaltile, max = k, v
end
end
tiles[#tiles + 1] = {name = modaltile, position = macroposition}
if (not Utils.contains(CoreData.water_tile_names, modaltile)) then
leftboundary, rightboundary, topboundary, bottomboundary = Math.min(leftboundary, x), Math.max(rightboundary, x + 1), Math.min(topboundary, y), Math.max(bottomboundary, y + 1)
end
end
end
leftboundary, rightboundary, topboundary, bottomboundary = leftboundary - 1, rightboundary + 1, topboundary - 1, bottomboundary + 1 --push out by one step to get some water
-- construct image, and record where entities can be placed:
local positions_free_to_hold_resources = {}
for _, tile in pairs(tiles) do
local x = tile.position.x
local y = tile.position.y
if tile.name ~= 'water' and x >= leftboundary and x <= rightboundary and y >= topboundary and y <= bottomboundary then --nil represents water
--arrange image so that {0,0} is on the centre of the left edge:
local p = {x = x - leftboundary, y = y - (topboundary + bottomboundary)/2}
if (topboundary + bottomboundary)/2 % 1 ~= 0 then
p.y = p.y + 0.5 --adjust so that tiles land on half-integer positions
end
tiles2[#tiles2 + 1] = {name = tile.name, position = p}
if (not Utils.contains(CoreData.tiles_that_conflict_with_resource_layer, tile.name)) then
local ename = entitymap[tile.position]
if ename then
entities[#entities + 1] = {name = ename, position = p}
else
if (p.x + 2) % 4 <= 2 and (p.y) % 4 <= 2 then --for the ingame minimap, the ore texture checker only colors these squares
local nearby_es = {
entitymap[{x = tile.position.x + 1, y = tile.position.y}],
entitymap[{x = tile.position.x - 1, y = tile.position.y}],
entitymap[{x = tile.position.x, y = tile.position.y + 1}],
entitymap[{x = tile.position.x, y = tile.position.y - 1}],
}
if not (nearby_es[1] or nearby_es[2] or nearby_es[3] or nearby_es[4]) then
positions_free_to_hold_resources[#positions_free_to_hold_resources + 1] = p
-- if destination.destination_index == 3 then
-- game.print(p)
-- end
end
end
end
end
end
end
if #positions_free_to_hold_resources > 0 then
local orestoadd = {}
for k, v in pairs(destination.static_params.abstract_ore_amounts) do
if k == 'crude-oil' then
local count = Math.ceil((v/20)^(1/2)) --assuming the abstract values are about 20 times as big as for other ores
orestoadd[k] = {count = count, sizing_each = Common.oil_abstract_to_real(v)/count}
else
local count = Math.ceil(v^(1/2))
orestoadd[k] = {count = count, sizing_each = Common.ore_abstract_to_real(v)/count}
end
end
for k, v in pairs(orestoadd) do
for i = 1, v.count do
if #positions_free_to_hold_resources > 0 then
local random_index = Math.random(#positions_free_to_hold_resources)
local p = positions_free_to_hold_resources[random_index]
entities[#entities + 1] = {name = k, position = p, amount = v.sizing_each}
for j = random_index, #positions_free_to_hold_resources - 1 do
positions_free_to_hold_resources[j] = positions_free_to_hold_resources[j+1]
end
positions_free_to_hold_resources[#positions_free_to_hold_resources] = nil
end
end
end
end
-- get more precise understanding of left-hand shore
local xcorrection = 0
for ystep = -10, 10, 10 do
for xstep = 0,300,3 do
local x = leftboundary * 32 + 16 + xstep
local y = (topboundary*32 + bottomboundary*32)/2 + ystep
local tiles3 = {}
terrain_fn{p = {x = x, y = y}, noise_generator = noise_generator, static_params = destination.static_params, tiles = tiles3, entities = {}, decoratives = {}, seed = destination.seed, iconized_generation = true}
local tile = tiles3[1]
if (not Utils.contains(CoreData.water_tile_names, tile.name)) then
xcorrection = Math.max(xcorrection, xstep + Math.abs(ystep))
break
end
end
end
-- if xcorrection == 0 then xcorrection = 300 end
local iconwidth, iconheight = rightboundary - leftboundary, bottomboundary - topboundary
iconwidth = iconwidth > 0 and iconwidth or 0 --make them 0 if negative
iconheight = iconheight > 0 and iconheight or 0
local extension_to_left = Math.ceil(Common.boat_default_starting_distance_from_shore + boat_extra_distance_from_shore + Common.mapedge_distance_from_boat_starting_position - xcorrection)
local terraingen_coordinates_offset = {x = (leftboundary*32 + rightboundary*32)/2 - extension_to_left/2, y = (topboundary*32 + bottomboundary*32)/2}
local width = rightboundary*32 - leftboundary*32 + extension_to_left
local height = bottomboundary*32 - topboundary*32
local deepwater_terraingenframe_xposition = leftboundary*32 - Common.deepwater_distance_from_leftmost_shore
local islandcenter_position = {x = extension_to_left/2, y = 0}
local deepwater_xposition = deepwater_terraingenframe_xposition - terraingen_coordinates_offset.x
-- -- must ceil this, because if it's a half integer big things will teleport badly:
-- local boat_starting_xposition = Math.ceil(- width/2 + Common.mapedge_distance_from_boat_starting_position)
-- worse, must make this even due to rails:
local boat_starting_xposition = 2*Math.ceil(
(- width/2 + Common.mapedge_distance_from_boat_starting_position)/2
)
destination.static_params.terraingen_coordinates_offset = terraingen_coordinates_offset
destination.static_params.width = width
destination.static_params.height = height
destination.static_params.islandcenter_position = islandcenter_position
destination.static_params.deepwater_xposition = deepwater_xposition
destination.static_params.deepwater_terraingenframe_xposition = deepwater_terraingenframe_xposition
destination.static_params.boat_starting_xposition = boat_starting_xposition
destination.iconized_map.tiles = tiles2
destination.iconized_map.entities = entities
destination.iconized_map_width = iconwidth
destination.iconized_map_height = iconheight
end
function Public.create_surface(destination)
local surface_name = destination.surface_name
if game.surfaces[surface_name] then return end
-- maybe can set width and height to be 0 here? if so, will need to change references to map_gen_settings.width elsewhere in code
-- local mgs = Utils.deepcopy(Common.default_map_gen_settings(
-- self.static_params.width or 512,
-- self.static_params.height or 512,
-- self.seed or Math.random(1, 1000000)
-- ))
local mgs = Utils.deepcopy(Common.default_map_gen_settings(
Math.max(0,destination.static_params.width) or 128,
Math.max(0,destination.static_params.height) or 128,
destination.seed or Math.random(1, 1000000)
))
--todo: put into static_params
mgs.autoplace_settings.decorative.treat_missing_as_default = destination.static_params.default_decoratives
local surface = game.create_surface(surface_name, mgs)
surface.solar_power_multiplier = destination.static_params.solar_power_multiplier or 1
surface.show_clouds = destination.static_params.clouds or false
surface.min_brightness = destination.static_params.min_brightness or 0.15
surface.brightness_visual_weights = destination.static_params.brightness_visual_weights or {1, 1, 1}
surface.daytime = destination.static_params.starting_time_of_day or 0
local daynightcycletype = destination.static_params.daynightcycletype or 1
local ticksperday = CoreData.daynightcycle_types[daynightcycletype].ticksperday or 0
if ticksperday == 0 then
surface.freeze_daytime = true
ticksperday = ticksperday + 1 -- avoid divide by zero
else
surface.freeze_daytime = false
end
surface.ticks_per_day = ticksperday
Public.on_surface_generation(destination)
end
function Public.clean_up(destination)
local memory = Memory.get_crew_memory()
local oldsurface = game.surfaces[destination.surface_name]
if not (oldsurface and oldsurface.valid) then return end
-- assuming sea is always default subtype:
local seasurface = game.surfaces[memory.sea_name]
Quest.try_resolve_quest()
destination.dynamic_data.quest_type = nil
destination.dynamic_data.quest_reward = nil
destination.dynamic_data.quest_progress = nil
destination.dynamic_data.quest_progressneeded = nil
destination.dynamic_data.quest_complete = nil
-- handle players that were left on the island
-- if there is more than one crew on a surface, this will need to be generalised
for _, player in pairs(game.connected_players) do
if (player.surface == oldsurface) then
if player.character and player.character.valid then player.character.die(memory.force) end
player.teleport(memory.spawnpoint, seasurface)
end
end
destination.dynamic_data = {}
memory.scripted_biters = nil
memory.scripted_unit_groups = nil
memory.floating_pollution = nil
if memory.enemy_force_name then
local ef = memory.enemy_force
if ef and ef.valid then
memory.enemy_force.reset_evolution()
local base_evo = Balance.base_evolution_leagues(memory.overworldx)
Common.set_evo(base_evo)
end
end
game.delete_surface(oldsurface)
end
-- function Public.crowsnest_init_destinations()
-- local memory = Memory.get_crew_memory()
-- local tiles, entities = {}, {}
-- Overworld.try_overworld_move{x = 0, y = 0}
-- -- for _, destination_data in pairs(memory.destinations) do
-- -- local iconized_map = SurfacesCommon.fetch_iconized_map(destination_data.destination_index)
-- -- for _, t in pairs(iconized_map.tiles) do
-- -- local x = Crowsnest.platformrightmostedge + destination_data.overworld_position.x + t.position.x
-- -- local y = destination_data.overworld_position.y + t.position.y
-- -- if Math.abs(x) < Crowsnest.Data.width/2 and Math.abs(y) < Crowsnest.Data.height/2 then
-- -- tiles[#tiles+1] = {name = t.name, position = {x = x, y = y}}
-- -- end
-- -- end
-- -- for _, e in pairs(iconized_map.entities) do
-- -- local x = Crowsnest.platformrightmostedge + destination_data.overworld_position.x + e.position.x
-- -- local y = destination_data.overworld_position.y + e.position.y
-- -- if Math.abs(x) < Crowsnest.Data.width/2 then
-- -- local e2 = Utils.deepcopy(e)
-- -- e2.position = {x = x, y = y}
-- -- entities[#entities+1] = e2
-- -- end
-- -- end
-- -- end
-- -- Crowsnest.update_surface(tiles, entities)
-- end
function Public.player_goto_crows_nest(player, player_relative_pos)
local memory = Memory.get_crew_memory()
local surface = game.surfaces[SurfacesCommon.encode_surface_name(memory.id, 0, enum.CROWSNEST, nil)]
local carpos
if player_relative_pos.x < 0 then
carpos = {x = -2.29687, y = 0}
else
carpos = {x = 3.29687, y = 0}
end
local newpos = {x = memory.overworldx + carpos.x - player_relative_pos.x, y = memory.overworldy + carpos.y + player_relative_pos.y}
local newpos2 = surface.find_non_colliding_position('character', newpos, 5, 0.2) or newpos
if newpos2 then player.teleport(newpos2, surface) end
-- player.minimap_enabled = false
end
function Public.player_exit_crows_nest(player, player_relative_pos)
local memory = Memory.get_crew_memory()
local surface
if memory.boat and (memory.boat.state == Boats.enum_state.ATSEA_SAILING or memory.boat.state == Boats.enum_state.ATSEA_LOADING_MAP) then
surface = game.surfaces[SurfacesCommon.encode_surface_name(memory.id, 0, Public.enum.SEA, Public.Sea.enum.DEFAULT)]
else
surface = game.surfaces[Common.current_destination().surface_name]
end
local carpos
if player_relative_pos.x > 0 then
carpos = Boats.get_scope(memory.boat).Data.entercrowsnest_cars.right
else
carpos = Boats.get_scope(memory.boat).Data.entercrowsnest_cars.left
end
local newpos = {x = memory.boat.position.x + carpos.x - player_relative_pos.x, y = memory.boat.position.y + carpos.y + player_relative_pos.y}
local newpos2 = surface.find_non_colliding_position('character', newpos, 10, 0.2) or newpos
if newpos2 then player.teleport(newpos2, surface) end
-- player.minimap_enabled = true
end
function Public.player_goto_hold(player, relative_pos, nth)
-- local memory = Memory.get_crew_memory()
local surface = Hold.get_hold_surface(nth)
local newpos = {x = Hold.Data.loco_offset.x + 1 + relative_pos.x, y = Hold.Data.loco_offset.y + relative_pos.y}
local newpos2 = surface.find_non_colliding_position('character', newpos, 5, 0.2) or newpos
if newpos2 then player.teleport(newpos2, surface) end
end
function Public.player_exit_hold(player, relative_pos)
local memory = Memory.get_crew_memory()
local surface
if memory.boat and (memory.boat.state == Boats.enum_state.ATSEA_SAILING or memory.boat.state == Boats.enum_state.ATSEA_LOADING_MAP) then
surface = game.surfaces[SurfacesCommon.encode_surface_name(memory.id, 0, Public.enum.SEA, Public.Sea.enum.DEFAULT)]
else
surface = game.surfaces[Common.current_destination().surface_name]
end
local locopos = Boats.get_scope(memory.boat).Data.loco_pos
local newpos = {x = memory.boat.position.x + locopos.x + relative_pos.x, y = memory.boat.position.y + locopos.y + relative_pos.y}
local newpos2 = surface.find_non_colliding_position('character', newpos, 10, 0.2) or newpos
if newpos2 then player.teleport(newpos2, surface) end
end
function Public.player_goto_cabin(player, relative_pos)
-- local memory = Memory.get_crew_memory()
local surface = Cabin.get_cabin_surface()
local newpos = {x = Cabin.Data.car_pos.x - relative_pos.x, y = Cabin.Data.car_pos.y + relative_pos.y}
local newpos2 = surface.find_non_colliding_position('character', newpos, 5, 0.2) or newpos
if newpos2 then player.teleport(newpos2, surface) end
end
function Public.player_exit_cabin(player, relative_pos)
local memory = Memory.get_crew_memory()
local surface
if memory.boat and (memory.boat.state == Boats.enum_state.ATSEA_SAILING or memory.boat.state == Boats.enum_state.ATSEA_LOADING_MAP) then
surface = game.surfaces[SurfacesCommon.encode_surface_name(memory.id, 0, Public.enum.SEA, Public.Sea.enum.DEFAULT)]
else
surface = game.surfaces[Common.current_destination().surface_name]
end
local carpos = Boats.get_scope(memory.boat).Data.cabin_car
local newpos = {x = memory.boat.position.x + carpos.x - relative_pos.x, y = memory.boat.position.y + carpos.y + relative_pos.y}
local newpos2 = surface.find_non_colliding_position('character', newpos, 10, 0.2) or newpos
if newpos2 then player.teleport(newpos2, surface) end
end
return Public

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,231 @@
--luacheck: ignore
--luacheck ignores because tickinterval arguments are a code templating choice...
local Memory = require 'maps.pirates.memory'
local Gui = require 'maps.pirates.gui.gui'
local Ai = require 'maps.pirates.ai'
local Structures = require 'maps.pirates.structures.structures'
local Islands = require 'maps.pirates.surfaces.islands.islands'
local Boats = require 'maps.pirates.structures.boats.boats'
local Surfaces = require 'maps.pirates.surfaces.surfaces'
local Interface = require 'maps.pirates.interface'
local Roles = require 'maps.pirates.roles.roles'
local Classes = require 'maps.pirates.roles.classes'
local Progression = require 'maps.pirates.progression'
local Crowsnest = require 'maps.pirates.surfaces.crowsnest'
local Hold = require 'maps.pirates.surfaces.hold'
local Cabin = require 'maps.pirates.surfaces.cabin'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local Overworld = require 'maps.pirates.overworld'
local Utils = require 'maps.pirates.utils_local'
local Crew = require 'maps.pirates.crew'
local Math = require 'maps.pirates.math'
local _inspect = require 'utils.inspect'.inspect
local Quest = require 'maps.pirates.quest'
local Public = {}
function Public.class_renderings(tickinterval)
local memory = Memory.get_crew_memory()
if not memory.classes_table then return end
local crew = Common.crew_get_crew_members()
if not memory.quartermaster_renderings then
memory.quartermaster_renderings = {}
end
local processed_renderings = {}
for _, player in pairs(crew) do
local player_index = player.index
if memory.classes_table[player_index] == Classes.enum.QUARTERMASTER then
local r = memory.quartermaster_renderings[player_index]
processed_renderings[player_index] = true
if Common.validate_player_and_character(player) then
if r and rendering.is_valid(r) then
rendering.set_target(r, player.character)
else
memory.quartermaster_renderings[player_index] = rendering.draw_circle{
surface = player.surface,
target = player.character,
color = CoreData.colors.quartermaster_rendering,
filled = false,
radius = Common.quartermaster_range,
only_in_alt_mode = true,
draw_on_ground = true,
}
end
else
if r then
rendering.destroy(r)
memory.quartermaster_renderings[player_index] = nil
end
end
end
end
for k, r in pairs(memory.quartermaster_renderings) do
if not processed_renderings[k] then
rendering.destroy(r)
memory.quartermaster_renderings[k] = nil
end
end
end
function Public.update_character_properties(tickinterval)
local memory = Memory.get_crew_memory()
local crew = Common.crew_get_crew_members()
for _, player in pairs(crew) do
if Common.validate_player_and_character(player) then
local player_index = player.index
local character = player.character
if memory.classes_table and memory.classes_table[player_index] then
local max_reach_bonus = 0
-- if memory.classes_table[player_index] == Classes.enum.DECKHAND then
-- max_reach_bonus = Math.max(max_reach_bonus, 6)
-- character.character_build_distance_bonus = 6
-- else
-- character.character_build_distance_bonus = 0
-- end
if memory.classes_table[player_index] == Classes.enum.FISHERMAN then
max_reach_bonus = Math.max(max_reach_bonus, 10)
character.character_resource_reach_distance_bonus = 10
elseif memory.classes_table[player_index] == Classes.enum.MASTER_ANGLER or memory.classes_table[player_index] == Classes.enum.DREDGER then
max_reach_bonus = Math.max(max_reach_bonus, 16)
character.character_resource_reach_distance_bonus = 16
else
character.character_resource_reach_distance_bonus = 0
end
character.character_reach_distance_bonus = max_reach_bonus
end
local health_boost = 0 -- base health is 250
-- moved to damage resistance:
-- if memory.classes_table and memory.classes_table[player_index] then
-- local class = memory.classes_table[player_index]
-- if class == Classes.enum.SAMURAI then
-- health_boost = health_boost + 800
-- elseif class == Classes.enum.HATAMOTO then
-- health_boost = health_boost + 1300
-- end
-- end
if Common.is_captain(player) then
health_boost = health_boost + 50
end
character.character_health_bonus = health_boost
local speed_boost = Balance.base_extra_character_speed
if memory.speed_boost_characters and memory.speed_boost_characters[player_index] then
speed_boost = speed_boost + 0.75
else
if memory.classes_table and memory.classes_table[player_index] then
local class = memory.classes_table[player_index]
if class == Classes.enum.SCOUT then
speed_boost = speed_boost + 0.35
elseif class == Classes.enum.DECKHAND or class == Classes.enum.BOATSWAIN or class == Classes.enum.SHORESMAN then
local surfacedata = Surfaces.SurfacesCommon.decode_surface_name(player.surface.name)
local type = surfacedata.type
local on_ship_bool = type == Surfaces.enum.HOLD or type == Surfaces.enum.CABIN or type == Surfaces.enum.CROWSNEST or (player.surface.name == memory.boat.surface_name and Boats.on_boat(memory.boat, player.position))
local hold_bool = surfacedata.type == Surfaces.enum.HOLD
if class == Classes.enum.DECKHAND then
if on_ship_bool and (not hold_bool) then
speed_boost = speed_boost + 0.25
elseif (not on_ship_bool) then
speed_boost = speed_boost - 0.25
end
elseif class == Classes.enum.BOATSWAIN then
if hold_bool then
speed_boost = speed_boost + 0.25
elseif (not on_ship_bool) then
speed_boost = speed_boost - 0.25
end
elseif class == Classes.enum.SHORESMAN then
if on_ship_bool then
speed_boost = speed_boost - 0.25
else
speed_boost = speed_boost + 0.07
end
end
end
end
end
character.character_running_speed_modifier = speed_boost
end
end
end
function Public.class_rewards_tick(tickinterval)
--assuming tickinterval = 6 seconds for now
local memory = Memory.get_crew_memory()
local crew = Common.crew_get_crew_members()
for _, player in pairs(crew) do
if Common.validate_player_and_character(player) then
local player_index = player.index
if memory.classes_table and memory.classes_table[player_index] then
if game.tick % tickinterval == 0 and Common.validate_player_and_character(player) then
if memory.classes_table[player.index] == Classes.enum.SMOLDERING then
local inv = player.get_inventory(defines.inventory.character_main)
if not (inv and inv.valid) then return end
local max_coal = 50
-- local max_transfer = 1
local wood_count = inv.get_item_count('wood')
local coal_count = inv.get_item_count('coal')
if wood_count >= 1 and coal_count < max_coal then
-- local count = Math.min(wood_count, max_coal-coal_count, max_transfer)
local count = 1
inv.remove({name = 'wood', count = count})
inv.insert({name = 'coal', count = count})
Common.flying_text_small(player.surface, player.position, '[item=coal]')
end
end
end
end
if game.tick % tickinterval == 0 and (not (memory.boat and memory.boat.state and memory.boat.state == Structures.Boats.enum_state.ATSEA_LOADING_MAP)) then --it is possible to spend extra time here, so don't give out freebies
if memory.classes_table and memory.classes_table[player_index] then
local class = memory.classes_table[player_index]
if class == Classes.enum.DECKHAND or class == Classes.enum.SHORESMAN or class == Classes.enum.BOATSWAIN or class == Classes.enum.QUARTERMASTER then --watch out for this line!
local surfacedata = Surfaces.SurfacesCommon.decode_surface_name(player.surface.name)
local type = surfacedata.type
local on_ship_bool = type == Surfaces.enum.HOLD or type == Surfaces.enum.CABIN or type == Surfaces.enum.CROWSNEST or (player.surface.name == memory.boat.surface_name and Boats.on_boat(memory.boat, player.position))
local hold_bool = surfacedata.type == Surfaces.enum.HOLD
if class == Classes.enum.DECKHAND and on_ship_bool and (not hold_bool) then
Classes.class_ore_grant(player, 4)
elseif class == Classes.enum.BOATSWAIN and hold_bool then
Classes.class_ore_grant(player, 6)
elseif class == Classes.enum.SHORESMAN and (not on_ship_bool) then
Classes.class_ore_grant(player, 2)
elseif class == Classes.enum.QUARTERMASTER then
local nearby_players = #player.surface.find_entities_filtered{position = player.position, radius = Common.quartermaster_range, name = 'character'}
if nearby_players > 1 then
Classes.class_ore_grant(player, nearby_players - 1, true)
end
end
end
end
end
end
end
end
return Public

View File

@ -0,0 +1,669 @@
local Math = require 'maps.pirates.math'
-- local _inspect = require 'utils.inspect'.inspect
local simplex_noise = require 'utils.simplex_noise'.d2 --rms ~ 0.1925
-- local perlin_noise = require 'utils.perlin_noise'
-- local Memory = require 'maps.pirates.memory'
-- local CoreData = require 'maps.pirates.coredata'
local NoisePregen = require 'maps.pirates.noise_pregen.noise_pregen'
local Public = {}
-- Lua 5.2 compatibility
-- local unpack = unpack or table.unpack
function Public.rgb_from_hsv(h, s, v)
-- 0 ≤ H < 360, 0 ≤ S ≤ 1 and 0 ≤ V ≤ 1
local r, g, b
local c = v * s
local x = c * (1 - Math.abs(((h/60) % 2) - 1))
local m = v - c
if h < 60 then
r=c+m
g=x+m
b=m
elseif h < 120 then
r=x+m
g=c+m
b=m
elseif h < 180 then
r=m
g=c+m
b=x+m
elseif h < 240 then
r=m
g=x+m
b=c+m
elseif h < 300 then
r=x+m
g=m
b=c+m
else
r=c+m
g=m
b=x+m
end
return {r = 255*r, g = 255*g, b = 255*b}
end
function Public.stable_sort(list, comp) --sorts but preserves ordering of equals
comp = comp or function (a, b) return a < b end
local num = 0
for k, _ in ipairs(list) do
num = num + 1
end
if num <= 1 then
return
end
local sorted = false
local n = num
while not sorted do
sorted = true
for i = 1, n - 1 do
if comp(list[i+1], list[i]) then
local tmp = list[i]
list[i] = list[i+1]
list[i+1] = tmp
sorted = false
end
end
n = n - 1
end
end
function Public.psum(plist)
local totalx, totaly = 0, 0
for i = 1, #plist do
if plist[i][1] then --multiplier
if plist[i][2] and plist[i][2].x and plist[i][2].y then
totalx = totalx + plist[i][1] * plist[i][2].x
totaly = totaly + plist[i][1] * plist[i][2].y
end
elseif plist[i].x and plist[i].y then
totalx = totalx + plist[i].x
totaly = totaly + plist[i].y
end
end
return {x = totalx, y = totaly}
end
function Public.interpolate(vector1, vector2, param)
return {x = vector1.x * (1-param) + vector2.x * param, y = vector1.y * (1-param) + vector2.y * param}
end
function Public.split(s, delimiter)
local result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
return result;
end
function Public.contains(table, element)
if not table then return false end
for _, value in pairs(table) do
if value == element then
return true
end
end
return false
end
function Public.snap_coordinates_for_rails(p)
return {x = p.x + (p.x % 2) - 1, y = p.y + (p.y % 2)}
end
function Public.spritepath_to_richtext(spritepath)
return '[' .. spritepath:gsub("/", "=") .. ']'
end
function Public.nonrepeating_join_dict(t1, t2)
t1 = t1 or {}
t2 = t2 or {}
local t3 = {}
for k, i1 in pairs(t1) do
t3[k] = i1
end
for k, i2 in pairs(t2) do
t3[k] = i2
end
return t3
end
function Public.nonrepeating_join(t1, t2)
t1 = t1 or {}
t2 = t2 or {}
local t3 = {}
for _, i1 in pairs(t1) do
t3[#t3 + 1] = i1
end
for _, i2 in pairs(t2) do
if not Public.contains(t3, i2) then
t3[#t3 + 1] = i2
end
end
return t3
end
function Public.exclude(t, t_exclude)
t_exclude = t_exclude or {}
local t2 = {}
for _, i in pairs(t) do
if not Public.contains(t_exclude, i) then
t2[#t2 + 1] = i
end
end
return t2
end
function Public.exclude_position_arrays(a, b_exclude)
b_exclude = b_exclude or {}
local a2 = {}
for x, xtab in pairs(a) do
for y, _ in pairs(xtab) do
if not (b_exclude[x] and b_exclude[x][y]) then
if not a2[x] then a2[x] = {} end
a2[x][y] = true
end
end
end
return a2
end
function Public.unordered_table_with_values_removed(tbl, val)
local to_keep = {}
for k, v in pairs(tbl) do
if v ~= val then to_keep[k] = v end
end
return to_keep
end
function Public.ordered_table_with_values_removed(tbl, val)
local to_keep = {}
local j = 1
for i = 1, #tbl do
if tbl[i] ~= val then
to_keep[j] = tbl[i]
j = j + 1
end
end
return to_keep
end
function Public.ordered_table_with_single_value_removed(tbl, val)
local to_keep = {}
local j = 1
local taken_one = false
for i = 1, #tbl do
if (tbl[i] ~= val) or taken_one then
to_keep[j] = tbl[i]
j = j + 1
else
taken_one = true
end
end
return to_keep
end
function Public.ordered_table_with_index_removed(tbl, index)
local to_keep = {}
local j = 1
for i = 1, #tbl do
if i ~= index then
to_keep[j] = tbl[i]
j = j + 1
end
end
return to_keep
end
function Public.length(tbl)
local count = 0
for k, _ in pairs(tbl) do
count = count + 1
end
return count
end
function Public.standard_string_form_of_time_in_seconds(time)
local time2, str1
if time >= 0 then
time2 = time
str1 = ''
else
time2 = - time
str1 = '-'
end
local str2
local hours = Math.floor(Math.ceil(time2) / 3600)
local minutes = Math.floor(Math.ceil(time2) / 60)
local seconds = Math.ceil(time2)
if hours >= 1 then
str2 = string.format('%.0fh %.0fm %.0fs', hours, minutes % 60, seconds % 60)
elseif minutes >= 1 then
str2 = string.format('%.0fm %.0fs', minutes, seconds % 60)
else
str2 = string.format('%.0fs', seconds)
end
return str1 .. str2
end
function Public.time_longform(seconds)
local seconds2, str1
if seconds >= 0 then
seconds2 = seconds
str1 = ''
else
seconds2 = - seconds
str1 = '-'
end
local str2
if seconds2 < 60 - 1 then
str2 = string.format('%.0f seconds', Math.ceil(seconds2))
elseif seconds2 < 60 * 60 - 1 then
str2 = string.format('%.0f mins, %.0f seconds', Math.floor(Math.ceil(seconds2) / 60), Math.ceil(seconds2) % 60)
elseif seconds2 < 60 * 60 * 24 - 1 then
str2 = string.format('%.0f hours, %.0f mins, %.0f seconds', Math.floor(Math.ceil(seconds2) / (60*60)), Math.floor(Math.ceil(seconds2) / 60) % 60, Math.ceil(seconds2) % 60)
else
str2 = string.format('%.0f days, %.0f hours, %.0f mins, %.0f seconds', Math.floor(Math.ceil(seconds2) / (24*60*60)), Math.floor(Math.ceil(seconds2) / (60*60)) % 24, Math.floor(Math.ceil(seconds2) / 60) % 60, Math.ceil(seconds2) % 60)
end
return str1 .. str2
end
function Public.time_mediumform(seconds)
local seconds2, str1
if seconds >= 0 then
seconds2 = seconds
str1 = ''
else
seconds2 = - seconds
str1 = '-'
end
local str2
local seconds3 = Math.floor(seconds2)
if seconds3 < 60 - 1 then
str2 = string.format('%.0fs', seconds3)
elseif seconds3 < 60 * 60 - 1 then
str2 = string.format('%.0fm%.0fs', Math.floor(seconds3 / 60), seconds3 % 60)
elseif seconds3 < 60 * 60 * 24 - 1 then
str2 = string.format('%.0fh%.0fm%.0fs', Math.floor(seconds3 / (60*60)), Math.floor(seconds3 / 60) % 60, seconds3 % 60)
else
str2 = string.format('%.0fd%.0fh%.0fm%.0fs', Math.floor(seconds3 / (24*60*60)), Math.floor(seconds3 / (60*60)) % 24, Math.floor(seconds3 / 60) % 60, seconds3 % 60)
end
return str1 .. str2
end
function Public.deepcopy(obj) --doesn't copy metatables
if type(obj) ~= 'table' then return obj end
local res = {}
for k, v in pairs(obj) do res[Public.deepcopy(k)] = Public.deepcopy(v) end
return res
end
function Public.bignumber_abbrevform(number) --e.g. 516, 1.2k, 21.4k, 137k
local str1, str2, number2
if number >= 0 then
number2 = number
str1 = ''
else
number2 = - number
str1 = '-'
end
if number2 == 0 then
str2 = '0'
elseif number2 < 1000 then
str2 = string.format('%.0d', Math.floor(number2))
elseif number2 < 100000 then
str2 = string.format('%.1fk', Math.floor(number2/100)/10)
else
str2 = string.format('%.0dk', Math.floor(number2/1000))
end
return str1 .. str2
end
function Public.bignumber_abbrevform2(number) --e.g. 516, 1.2k, 21k, 137k
local str1, str2, number2
if number >= 0 then
number2 = number
str1 = ''
else
number2 = - number
str1 = '-'
end
if number2 == 0 then
str2 = '0'
elseif number2 < 1000 then
str2 = string.format('%.0d', Math.floor(number2))
elseif number2 < 10000 then
str2 = string.format('%.1fk', Math.floor(number2/100)/10)
else
str2 = string.format('%.0dk', Math.floor(number2/1000))
end
return str1 .. str2
end
function Public.negative_rate_abbrevform(number)
local str1, str2, number2
if number > 0 then
number2 = number
str1 = ''
else
number2 = - number
str1 = '-'
end
if number2 == 0 then
str2 = '0'
elseif number2 < 10 then
str2 = string.format('%.1f', Math.ceil(number2*10)/10)
else
str2 = string.format('%.0d', Math.ceil(number2))
end
return str1 .. str2 .. '/s'
end
function Public.noise_field_simplex_2d(noise_data, seed, normalised)
normalised = normalised or false
local f = function(position)
local noise, _seed, weight_sum = 0, seed, 0
for i = 1, #noise_data do
local n = noise_data[i]
local toadd = n.amplitude
if n.wavelength > 0 then --=0 codes for infinite
toadd = toadd * simplex_noise(position.x / n.wavelength, position.y / n.wavelength, _seed)
_seed = _seed + 12345 --some deficiencies
end
if normalised then weight_sum = weight_sum + n.amplitude end
noise = noise + toadd
end
if normalised then noise = noise / weight_sum end
return noise
end
return f
end
function Public.hardcoded_noise_field_decompress(fieldtype, noise_data, seed, normalised)
normalised = normalised or false
-- local hardcoded_upperscale = NoisePregen[fieldtype].upperscale
-- local hardcoded_boxsize = NoisePregen[fieldtype].boxsize
local hardcoded_wordlength = NoisePregen[fieldtype].wordlength
local factor = NoisePregen[fieldtype].factor
local f = function(position)
local noise, weight_sum, _seed = 0, 0, seed
for i = 1, #noise_data do
local n = noise_data[i]
local toadd = n.amplitude
local seedfactor = n.seedfactor or 1
if n.upperscale > 0 then --=0 codes for infinite
local scale = n.upperscale / 100
local seed2 = seed * seedfactor
local x2 = position.x / scale
local y2 = position.y / scale
local x2remainder = x2%1
local y2remainder = y2%1
local x2floor = x2 - x2remainder
local y2floor = y2 - y2remainder
local topleftnoiseindex = seed2 % (1000*1000)
local relativeindex00 = x2floor + y2floor * 1000
local totalindex00 = (topleftnoiseindex + relativeindex00) % (1000*1000)
local totalindex10 = (1 + topleftnoiseindex + relativeindex00) % (1000*1000)
local totalindex01 = (1000 + topleftnoiseindex + relativeindex00) % (1000*1000)
local strindex00 = 1 + totalindex00 * hardcoded_wordlength
local strindex10 = 1 + totalindex10 * hardcoded_wordlength
local strindex01 = 1 + totalindex01 * hardcoded_wordlength
local str00 = NoisePregen[fieldtype].Data:sub(strindex00, strindex00 + (hardcoded_wordlength - 1))
local str10 = NoisePregen[fieldtype].Data:sub(strindex10, strindex10 + (hardcoded_wordlength - 1))
local str01 = NoisePregen[fieldtype].Data:sub(strindex01, strindex01 + (hardcoded_wordlength - 1))
local noise00, noise10, noise01 = 0, 0, 0
for j = 0, hardcoded_wordlength-1 do
noise00 = noise00 + NoisePregen.dec[str00:sub(hardcoded_wordlength - j, hardcoded_wordlength - j)] * (NoisePregen.encoding_length ^ j)
noise10 = noise10 + NoisePregen.dec[str10:sub(hardcoded_wordlength - j, hardcoded_wordlength - j)] * (NoisePregen.encoding_length ^ j)
noise01 = noise01 + NoisePregen.dec[str01:sub(hardcoded_wordlength - j, hardcoded_wordlength - j)] * (NoisePregen.encoding_length ^ j)
end
if noise00 % 2 == 1 then noise00 = -noise00 end
noise00 = noise00 / (NoisePregen.encoding_length ^ (hardcoded_wordlength-1))
if noise10 % 2 == 1 then noise10 = -noise10 end
noise10 = noise10 / (NoisePregen.encoding_length ^ (hardcoded_wordlength-1))
if noise01 % 2 == 1 then noise01 = -noise01 end
noise01 = noise01 / (NoisePregen.encoding_length ^ (hardcoded_wordlength-1))
-- local hardnoise00 = tonumber(strsub00:sub(2,6))/10000
-- if strsub00:sub(1,1) == '-' then hardnoise00 = -hardnoise00 end
-- local hardnoise10 = tonumber(strsub10:sub(2,6))/10000
-- if strsub10:sub(1,1) == '-' then hardnoise10 = -hardnoise10 end
-- local hardnoise01 = tonumber(strsub01:sub(2,6))/10000
-- if strsub01:sub(1,1) == '-' then hardnoise01 = -hardnoise01 end
-- log(_inspect{topleftnoiseindex, topleftnoiseindex2, relativeindex00, relativeindex10, relativeindex01})
-- log(_inspect{strindex00, strindex10, strindex01, hardnoise1, hardnoise2, hardnoise3})
local interpolatedhardnoise = noise00 + x2remainder*(noise10-noise00) + y2remainder*(noise01-noise00)
toadd = toadd * factor * tonumber(interpolatedhardnoise)
_seed = _seed + 12345
end
if normalised then weight_sum = weight_sum + n.amplitude end
noise = noise + toadd
end
if normalised then noise = noise / weight_sum end
return noise
end
return f
end
function Public.hardcoded_noise_field(fieldtype, noise_data, seed, normalised)
normalised = normalised or false
-- local hardcoded_upperscale = NoisePregen[fieldtype].upperscale --100
-- local hardcoded_boxsize = NoisePregen[fieldtype].boxsize --1000
local hardcoded_wordlength = NoisePregen[fieldtype].wordlength
local factor = NoisePregen[fieldtype].factor
local f = function(position)
local noise, weight_sum, _seed = 0, 0, seed
for i = 1, #noise_data do
local n = noise_data[i]
local toadd = n.amplitude
local seedfactor = n.seedfactor or 1
if n.upperscale > 0 then --=0 codes for infinite
local scale = n.upperscale / 100
local seed2 = seed * seedfactor
local x2 = position.x / scale
local y2 = position.y / scale
local x2remainder = x2%1
local y2remainder = y2%1
local x2floor = x2 - x2remainder
local y2floor = y2 - y2remainder
local seedindex = seed2 % (1000*1000)
local relativeindex00 = x2floor + y2floor * 1000
local noiseindex1 = seedindex + relativeindex00
local totalindex00 = noiseindex1 % (1000*1000)
local totalindex10 = (1 + noiseindex1) % (1000*1000)
local totalindex01 = (1000 + noiseindex1) % (1000*1000)
local strindex00 = 1 + totalindex00 * hardcoded_wordlength
local strindex10 = 1 + totalindex10 * hardcoded_wordlength
local strindex01 = 1 + totalindex01 * hardcoded_wordlength
local str00 = NoisePregen[fieldtype].Data:sub(strindex00, strindex00 + (hardcoded_wordlength - 1))
local str10 = NoisePregen[fieldtype].Data:sub(strindex10, strindex10 + (hardcoded_wordlength - 1))
local str01 = NoisePregen[fieldtype].Data:sub(strindex01, strindex01 + (hardcoded_wordlength - 1))
local noise00 = tonumber(str00:sub(2,6))/10000
if str00:sub(1,1) == '-' then noise00 = -noise00 end
local noise10 = tonumber(str10:sub(2,6))/10000
if str10:sub(1,1) == '-' then noise10 = -noise10 end
local noise01 = tonumber(str01:sub(2,6))/10000
if str01:sub(1,1) == '-' then noise01 = -noise01 end
-- log(_inspect{topleftnoiseindex, topleftnoiseindex2, relativeindex00, relativeindex10, relativeindex01})
-- log(_inspect{strindex00, strindex10, strindex01, hardnoise1, hardnoise2, hardnoise3})
local interpolatedhardnoise = noise00 + x2remainder*(noise10-noise00) + y2remainder*(noise01-noise00)
toadd = toadd * factor * tonumber(interpolatedhardnoise)
_seed = _seed + 12345 --some deficiencies
end
if normalised then weight_sum = weight_sum + n.amplitude end
noise = noise + toadd
end
if normalised then noise = noise / weight_sum end
return noise
end
return f
end
function Public.noise_generator(noiseparams, seed)
--memoizes locally
noiseparams = noiseparams or {}
seed = seed or 0
local ret = {}
for k, v in pairs(noiseparams) do
local fn
if v.type == 'simplex_2d' then
fn = Public.noise_field_simplex_2d(v.params, seed, v.normalised)
elseif v.type == 'perlin_1d_circle' then
fn = Public.noise_field_perlin_1d_circle(v.params, seed, v.normalised)
else
fn = Public.hardcoded_noise_field(v.type, v.params, seed, v.normalised)
end
ret[k] = Public.memoize(fn)
end
function ret:addNoise(key, new_noise_function)
if self[key] then return
else
self[key] = Public.memoize(new_noise_function)
end
end
ret.seed = seed
-- ret.noiseparams = noiseparams
return ret
end
local function cache_get(cache, params)
local node = cache
for i=1, #params do
node = node.children and node.children[params[i]]
if not node then return nil end
end
return node.results
end
local function cache_put(cache, params, results)
local node = cache
local param
for i=1, #params do
param = params[i]
node.children = node.children or {}
node.children[param] = node.children[param] or {}
node = node.children[param]
end
node.results = results
end
-- The following functions were adapted from https://github.com/kikito/memoize.lua/blob/master/memoize.lua, under the MIT License:
-- [[
-- MIT LICENSE
-- Copyright (c) 2018 Enrique García Cota
-- Permission is hereby granted, free of charge, to any person obtaining a
-- copy of this software and associated documentation files (the
-- "Software"), to deal in the Software without restriction, including
-- without limitation the rights to use, copy, modify, merge, publish,
-- distribute, sublicense, and/or sell copies of the Software, and to
-- permit persons to whom the Software is furnished to do so, subject to
-- the following conditions:
-- The above copyright notice and this permission notice shall be included
-- in all copies or substantial portions of the Software.
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-- ]]
local function is_callable(f)
local tf = type(f)
if tf == 'function' then return true end
if tf == 'table' then
local mt = getmetatable(f)
return type(mt) == 'table' and is_callable(mt.__call)
end
return false
end
-- == memoization
-- memoize takes in a function and outputs an auto-memoizing version of the same
-- e.g. local memoized_f = memoize(f, <cache>), explicit cache is optional
function Public.memoize(f, cache)
cache = cache or {}
if not is_callable(f) then
log(string.format(
"Only functions and callable tables are memoizable. Received %s (a %s)",
tostring(f), type(f)))
end
return function (...)
local params = {...}
local results = cache_get(cache, params)
if not results then
results = { f(...) }
cache_put(cache, params, results)
end
return table.unpack(results)
end
end
return Public