1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2025-01-24 03:47:58 +02:00

Merge branch 'develop' into journey1.9

This commit is contained in:
hanakocz 2023-08-11 18:37:45 +02:00 committed by GitHub
commit 73152ab227
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 776 additions and 8037 deletions

View File

@ -16,7 +16,7 @@ softmod_info_tips_1=Features of the game that are hard to work out alone
softmod_info_tips_2=• The captain can steer the boat from the crow's nest by placing 50 rail signals in one of the blue boxes.\n• Resources granted to the ship appear in the captain's cabin.\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 most of the amount it 'would have' made had it survived.)\n• item-on-ground entities on the deck are moved to the cabin when the boat moves, for performance reasons.\n• Commands: /ccolor gives you a fun color. /classinfo {classname} gives the description of the named class. To manage your class, use /take {classname} or /giveup. softmod_info_tips_2=• The captain can steer the boat from the crow's nest by placing 50 rail signals in one of the blue boxes.\n• Resources granted to the ship appear in the captain's cabin.\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 most of the amount it 'would have' made had it survived.)\n• item-on-ground entities on the deck are moved to the cabin when the boat moves, for performance reasons.\n• Commands: /ccolor gives you a fun color. /classinfo {classname} gives the description of the named class. To manage your class, use /take {classname} or /giveup.
softmod_info_updates_1=Significant recent changes softmod_info_updates_1=Significant recent changes
softmod_info_updates_2=v1.5.1\n• Greatly increased biter wave sizes on harder difficulties.\n• Reduced amount of resources and rewards that can be acquired. However all rewards (such as plates from chests and coin rewards from launching rocket) now scale with leagues.\n• Possible fix for some desync cases.\n• Bugfixes.\n\nv1.5.0\n• Enabled a lot more researches.\n• New elite biters and spawners for hard+ difficulties.\n• Added new classes: Medic, Doctor and Shaman.\n• Disabled old classes: Gourmet, Deckhand, Boatswain.\n• More than one class of same type can be acquired now. Same classes now can be offered again every 5 class purchases.\n• Mining trees now have a chance to spawn smol ore vein or chest with loot.\n• Balance changes, tweaks and more! softmod_info_updates_2=v1.5.2\n• Elite biters now also appear in Easy/Normal difficulties from league 800.\n• Elite biters now also spawn 4 non-elite biters on death.\n• When player rejoins the game within 1 minute, he will spawn where he was when he left, instead of on the boat.\n• "Kovarex-enrichment-process" recipe is now researched by default\n• Bugfixes.\n\nv1.5.1\n• Greatly increased biter wave sizes on harder difficulties.\n• Reduced amount of resources and rewards that can be acquired. However all rewards (such as plates from chests and coin rewards from launching rocket) now scale with leagues.\n• Possible fix for some desync cases.\n• Bugfixes.
softmod_info_credits_1=Credits softmod_info_credits_1=Credits
softmod_info_credits_2=Pirate Ship designed and coded by thesixthroc. Updates from Piratux. Comfy codebase and help from Gerkiz, Hanakocz and Mew @ Comfy Industries (https://getcomfy.eu). Some island structure blueprints contributed by Mattisso.\n\nCome chat with us: 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=Pirate Ship designed and coded by thesixthroc. Updates from Piratux. Comfy codebase and help from Gerkiz, Hanakocz and Mew @ Comfy Industries (https://getcomfy.eu). Some island structure blueprints contributed by Mattisso.\n\nCome chat with us: https://getcomfy.eu/discord\n\n"Those white gloves. I'll never forget them 'till the day I die." - Dr. John
@ -236,58 +236,40 @@ class_unobtainable=Class was disabled and is unobtainable.
class_definition_for=Class definition for class_definition_for=Class definition for
class_deckhand=Deckhand class_deckhand=Deckhand
# class_deckhand_explanation=They move faster and generate ore whilst onboard above deck.
class_deckhand_explanation_advanced=They move __1__% times faster and generate ore (+__2__ every __3__ seconds) whilst onboard above deck.\nNo ore is generated while at sea. class_deckhand_explanation_advanced=They move __1__% times faster and generate ore (+__2__ every __3__ seconds) whilst onboard above deck.\nNo ore is generated while at sea.
class_fisherman=Fisherman class_fisherman=Fisherman
# class_fisherman_explanation=They fish at greater distance.
class_fisherman_explanation_advanced=They fish at greater distance (__1__ extra tile range), and catch more (+__2__ fish). class_fisherman_explanation_advanced=They fish at greater distance (__1__ extra tile range), and catch more (+__2__ fish).
class_scout=Scout class_scout=Scout
# class_scout_explanation=They are faster, but frail and deal less damage.
class_scout_explanation_advanced=They move __1__% times faster, but receive __2__% more damage and deal __3__% less damage. class_scout_explanation_advanced=They move __1__% times faster, but receive __2__% more damage and deal __3__% less damage.
class_samurai=Samurai class_samurai=Samurai
# class_samurai_explanation=They are tough, and *with no weapon equipped* fight well by melee, but poorly otherwise.
class_samurai_explanation_advanced=They receive __1__% less damage, and with no weapon equipped do extra __2__ damage in melee (scales with 'physical projectile damage' research bonuses), but deal __3__% less damage otherwise.\nIf the damage dealt in melee is an overkill, the remaining damage splashes onto nearby enemies. class_samurai_explanation_advanced=They receive __1__% less damage, and with no weapon equipped do extra __2__ damage in melee (scales with 'physical projectile damage' research bonuses), but deal __3__% less damage otherwise.\nIf the damage dealt in melee is an overkill, the remaining damage splashes onto nearby enemies.
class_merchant=Merchant class_merchant=Merchant
# class_merchant_explanation=They generate 50 doubloons per league.
class_merchant_explanation_advanced=They generate 50 doubloons per league. class_merchant_explanation_advanced=They generate 50 doubloons per league.
class_shoresman=Shoresman class_shoresman=Shoresman
# class_shoresman_explanation=They move slightly faster and generate ore whilst offboard.
class_shoresman_explanation_advanced=They move __1__% times faster and generate ore (+__2__ every __3__ seconds) whilst offboard. class_shoresman_explanation_advanced=They move __1__% times faster and generate ore (+__2__ every __3__ seconds) whilst offboard.
class_boatswain=Boatswain class_boatswain=Boatswain
# class_boatswain_explanation=They move faster and generate ore whilst below deck.
class_boatswain_explanation_advanced=They move __1__% times faster and generate ore (+__2__ every __3__ seconds) whilst below deck.\nNo ore is generated while at sea. class_boatswain_explanation_advanced=They move __1__% times faster and generate ore (+__2__ every __3__ seconds) whilst below deck.\nNo ore is generated while at sea.
class_prospector=Prospector class_prospector=Prospector
# class_prospector_explanation=They find more resources when handmining.
class_prospector_explanation_advanced=They find more resources when handmining. class_prospector_explanation_advanced=They find more resources when handmining.
class_lumberjack=Lumberjack class_lumberjack=Lumberjack
# class_lumberjack_explanation=They find more resources when chopping trees.
class_lumberjack_explanation_advanced=They find ores and more coins when chopping trees. class_lumberjack_explanation_advanced=They find ores and more coins when chopping trees.
class_master_angler=Master Angler class_master_angler=Master Angler
# class_master_angler_explanation=They fish at much greater distance, and catch more.
class_master_angler_explanation_advanced=They fish at much greater distance (__1__ extra tile range), and catch more (+__2__ fish and +__3__ coins). class_master_angler_explanation_advanced=They fish at much greater distance (__1__ extra tile range), and catch more (+__2__ fish and +__3__ coins).
class_wood_lord=Lord of the Woods class_wood_lord=Lord of the Woods
# class_wood_lord_explanation=They find many more resources when chopping trees.
class_wood_lord_explanation_advanced=They find many more resources when chopping trees. class_wood_lord_explanation_advanced=They find many more resources when chopping trees.
class_chief_excavator=Chief Excavator class_chief_excavator=Chief Excavator
# class_chief_excavator_explanation=They find many more resources when handmining.
class_chief_excavator_explanation_advanced=They find many more resources when handmining. class_chief_excavator_explanation_advanced=They find many more resources when handmining.
class_hatamoto=Hatamoto class_hatamoto=Hatamoto
# class_hatamoto_explanation=They are very tough, and *with no weapon equipped* fight well by melee, but poorly otherwise.
class_hatamoto_explanation_advanced=They receive __1__% less damage, and with no weapon equipped do extra __2__ damage in melee (scales with 'physical projectile damage' research bonuses), but deal __3__% less damage otherwise.\n\nIf the damage dealt in melee is an overkill, the remaining damage splashes onto nearby enemies. class_hatamoto_explanation_advanced=They receive __1__% less damage, and with no weapon equipped do extra __2__ damage in melee (scales with 'physical projectile damage' research bonuses), but deal __3__% less damage otherwise.\n\nIf the damage dealt in melee is an overkill, the remaining damage splashes onto nearby enemies.
class_iron_leg=Iron Leg class_iron_leg=Iron Leg
# class_iron_leg_explanation=They are very resistant to damage when carrying 3000 iron ore.
class_iron_leg_explanation_advanced=They receive __1__% less damage when carrying at least __2__ iron ore. class_iron_leg_explanation_advanced=They receive __1__% less damage when carrying at least __2__ iron ore.
class_quartermaster=Quartermaster class_quartermaster=Quartermaster
# class_quartermaster_explanation=Nearby crewmates get +10% physical attack and generate ore. class_quartermaster_explanation_advanced= You and your nearby crewmates (at __1__ tile radius) get +__2__% physical attack bonus and generate ore (ore amount depends on nearby crewmate count).\nNo ore is generated while at sea.
class_quartermaster_explanation_advanced=Nearby crewmates (at __1__ tile radius) get +__2__% physical attack bonus and generate ore (ore amount depends on nearby crewmate count).\nNo ore is generated while at sea.
class_dredger=Dredger class_dredger=Dredger
# class_dredger_explanation=They find surprising items when they fish.
class_dredger_explanation_advanced=They can grab fish from an insane distance (__1__ extra tile range), and catch more (+__2__ fish). In addition, they find surprising items when fishing. class_dredger_explanation_advanced=They can grab fish from an insane distance (__1__ extra tile range), and catch more (+__2__ fish). In addition, they find surprising items when fishing.
class_smoldering=Smoldering class_smoldering=Smoldering
# class_smoldering_explanation=They periodically convert wood into coal, if they have less than 50 coal.
class_smoldering_explanation_advanced=They periodically convert wood into coal, if they have less than 50 coal. class_smoldering_explanation_advanced=They periodically convert wood into coal, if they have less than 50 coal.
class_gourmet=Gourmet class_gourmet=Gourmet
# class_gourmet_explanation=They generate ore by eating fish in fancy locations.
class_gourmet_explanation_advanced=They generate ore by eating fish in fancy locations. Does not generate ore while at sea. class_gourmet_explanation_advanced=They generate ore by eating fish in fancy locations. Does not generate ore while at sea.
class_chef=Chef class_chef=Chef
class_chef_explanation_advanced=They cook meat of defeated enemies and turn it into something tasty. class_chef_explanation_advanced=They cook meat of defeated enemies and turn it into something tasty.

View File

@ -959,6 +959,8 @@ local function on_entity_died(event)
return return
end end
local unit_number = entity.unit_number
local cause = event.cause local cause = event.cause
local map_name = 'mtn_v3' local map_name = 'mtn_v3'
@ -971,6 +973,11 @@ local function on_entity_died(event)
entity = entity entity = entity
} }
local enemy_spawners = Public.get('enemy_spawners')
if enemy_spawners.spawners[unit_number] then
enemy_spawners.spawners[unit_number] = nil
end
on_entity_removed(d) on_entity_removed(d)
local player local player
@ -1383,6 +1390,40 @@ function Public.loco_died(invalid_locomotive)
end end
end end
local function on_entity_spawned(event)
local enemy_spawners = Public.get('enemy_spawners')
if not enemy_spawners.enabled then
return
end
local spawner = event.spawner
if not spawner or not spawner.valid then
return
end
local unit_number = spawner.unit_number
local position = spawner.position
if not enemy_spawners.spawners[unit_number] then
enemy_spawners.spawners[unit_number] = {
entity = spawner,
count = 0
}
end
local surface = spawner.surface
enemy_spawners.spawners[unit_number].count = enemy_spawners.spawners[unit_number].count + 1
if enemy_spawners.spawners[unit_number].count >= 100 then
local entity = enemy_spawners.spawners[unit_number].entity
if entity and entity.valid then
surface.create_entity({name = 'explosion', position = position})
entity.destroy()
enemy_spawners.spawners[unit_number] = nil
end
end
end
local function on_built_entity(event) local function on_built_entity(event)
local entity = event.created_entity local entity = event.created_entity
if not entity.valid then if not entity.valid then
@ -1614,6 +1655,7 @@ Public.get_random_weighted = get_random_weighted
Event.add_event_filter(defines.events.on_entity_damaged, {filter = 'final-damage-amount', comparison = '>', value = 0}) Event.add_event_filter(defines.events.on_entity_damaged, {filter = 'final-damage-amount', comparison = '>', value = 0})
Event.add(defines.events.on_entity_damaged, on_entity_damaged) Event.add(defines.events.on_entity_damaged, on_entity_damaged)
Event.add(defines.events.on_entity_spawned, on_entity_spawned)
Event.add(defines.events.on_player_repaired_entity, on_player_repaired_entity) Event.add(defines.events.on_player_repaired_entity, on_player_repaired_entity)
Event.add(defines.events.on_player_mined_entity, on_player_mined_entity) Event.add(defines.events.on_player_mined_entity, on_player_mined_entity)
Event.add(defines.events.on_robot_mined_entity, on_robot_mined_entity) Event.add(defines.events.on_robot_mined_entity, on_robot_mined_entity)

View File

@ -340,6 +340,24 @@ local function do_beams_away()
end end
end end
local function do_clear_enemy_spawners()
local tick = game.tick
if tick % 1000 ~= 0 then
return
end
local enemy_spawners = Public.get('enemy_spawners')
if not enemy_spawners.enabled then
return
end
for unit_number, spawner in pairs(enemy_spawners.spawners) do
if not spawner.entity or not spawner.entity.valid then
enemy_spawners.spawners[unit_number] = nil
end
end
end
local function do_artillery_turrets_targets() local function do_artillery_turrets_targets()
local art_table = this.art_table local art_table = this.art_table
local index = art_table.index local index = art_table.index
@ -428,6 +446,7 @@ local function tick()
do_magic_fluid_crafters() do_magic_fluid_crafters()
do_artillery_turrets_targets() do_artillery_turrets_targets()
do_beams_away() do_beams_away()
do_clear_enemy_spawners()
end end
Public.deactivate_callback = Public.deactivate_callback =

View File

@ -117,7 +117,7 @@ function Public.reset_main_table()
this.gap_between_locomotive = { this.gap_between_locomotive = {
hinders = {}, hinders = {},
gap = 900, gap = 900,
neg_gap = -2112, neg_gap = -3520, -- earlier 2112 (3 zones, whereas 704 is one zone)
highest_pos = nil highest_pos = nil
} }
this.force_chunk = false this.force_chunk = false
@ -134,6 +134,10 @@ function Public.reset_main_table()
--!grief prevention --!grief prevention
this.enable_arties = 6 -- default to callback 6 this.enable_arties = 6 -- default to callback 6
--!snip --!snip
this.enemy_spawners = {
spawners = {},
enabled = false
}
this.poison_deployed = false this.poison_deployed = false
this.robotics_deployed = false this.robotics_deployed = false
this.upgrades = { this.upgrades = {

View File

@ -146,6 +146,75 @@ local function shuffle(tbl)
return tbl return tbl
end end
--- Returns a function that will generate the next value each time it is called.
--- It is configured by the list of such elements {value = '...', tags = {}, weight = 100}.
--- It ensures that consecutive values do not have the same tags (if such variants exist).
--- The probability of generating the next value is calculated based on the weight parameter.
local function generate_with_tags_and_weights(variants)
local prev_tags = {}
for _, variant in pairs(variants) do
-- Inner weight set to 0 every time we generate the corresponding value.
-- Each iteration it grows up to its originally weight.
variant.inner_weight = variant.weight
end
local function intersect(arr1, arr2)
local result = {}
for _, i1 in pairs(arr1) do
for _, i2 in pairs(arr2) do
if i1 == i2 then
table.insert(result, i1)
end
end
end
return result
end
local function next_value_provider()
local candidates = {}
local total_weight = 0
for _, variant in pairs(variants) do
if #intersect(variant.tags, prev_tags) == 0 then
table.insert(candidates, variant)
total_weight = total_weight + variant.inner_weight
end
end
if #candidates == 0 then
--no candidates found, so getting all the variants to prevent empty result
candidates = variants
end
local chosen = candidates[1]
local selection = math.random(0, total_weight)
for _, candidate in pairs(candidates) do
selection = selection - candidate.inner_weight
if selection <= 0 then
chosen = candidate
goto chosen_found
end
end
::chosen_found::
for _, variant in pairs(variants) do
if variant.inner_weight < variant.weight then
variant.inner_weight = math.min(variant.weight, math.floor(variant.inner_weight + variant.weight / #variants))
end
end
prev_tags = chosen.tags
chosen.inner_weight = 0
return chosen.value
end
return next_value_provider
end
local function is_position_near(area, table_to_check) local function is_position_near(area, table_to_check)
local status = false local status = false
local function inside(pos) local function inside(pos)
@ -2542,22 +2611,22 @@ local function starting_zone(x, y, data, void_or_lab, adjusted_zones)
end end
local zones = { local zones = {
['zone_1'] = zone_1, zone_1 = {fn = zone_1, weight = 100, tags = {'zone_1'}},
['zone_2'] = zone_2, zone_2 = {fn = zone_2, weight = 100, tags = {'zone_2'}},
['zone_3'] = zone_3, zone_3 = {fn = zone_3, weight = 100, tags = {'zone_3'}},
['zone_4'] = zone_4, zone_4 = {fn = zone_4, weight = 100, tags = {'zone_4'}},
['zone_5'] = zone_5, zone_5 = {fn = zone_5, weight = 100, tags = {'zone_5'}},
['zone_forest_1'] = zone_forest_1, zone_forest_1 = {fn = zone_forest_1, weight = 100, tags = {'forest'}},
['zone_forest_2'] = zone_forest_2, zone_forest_2 = {fn = zone_forest_2, weight = 100, tags = {'forest'}},
['zone_scrap_1'] = zone_scrap_1, zone_scrap_1 = {fn = zone_scrap_1, weight = 100, tags = {'scrap'}},
['zone_scrap_2'] = zone_scrap_2, zone_scrap_2 = {fn = zone_scrap_2, weight = 100, tags = {'scrap'}},
['zone_7'] = zone_7, zone_7 = {fn = zone_7, weight = 100, tags = {'zone_7'}},
['zone_9'] = zone_9, zone_9 = {fn = zone_9, weight = 100, tags = {'zone_8'}},
['zone_10'] = zone_10, zone_10 = {fn = zone_10, weight = 100, tags = {'forest'}},
['zone_11'] = zone_11, zone_11 = {fn = zone_11, weight = 100, tags = {'zone_11'}},
['zone_12'] = zone_12, zone_12 = {fn = zone_12, weight = 100, tags = {'zone_12'}},
['zone_13'] = zone_13, zone_13 = {fn = zone_13, weight = 100, tags = {'zone_13'}},
['zone_14'] = zone_14 zone_14 = {fn = zone_14, weight = 100, tags = {'forest'}}
} }
local function shuffle_terrains(adjusted_zones, new_zone) local function shuffle_terrains(adjusted_zones, new_zone)
@ -2584,30 +2653,30 @@ local function init_terrain(adjusted_zones)
return return
end end
local count = 1 local zones_to_generate = {}
local shuffled_zones = {} for zone_name, zone_data in pairs(zones) do
table.insert(
for zone_name, _ in pairs(zones) do zones_to_generate,
shuffled_zones[count] = zone_name {
count = count + 1 value = zone_name,
tags = zone_data.tags,
weight = zone_data.weight
}
)
end end
count = count - 1 local generated_zones = {}
local next_provider = generate_with_tags_and_weights(zones_to_generate)
local shuffle_again = {}
local size = 132 local size = 132
for i = 1, size do
for inc = 1, size do local next = next_provider()
local map = shuffled_zones[random(1, count)] generated_zones[i] = next
if map then -- print(next)
shuffle_again[inc] = map
end end
end
shuffle_again = shuffle(shuffle_again)
adjusted_zones.size = size adjusted_zones.size = size
adjusted_zones.shuffled_zones = shuffle_again adjusted_zones.shuffled_zones = generated_zones
adjusted_zones.init_terrain = true adjusted_zones.init_terrain = true
end end
@ -2622,9 +2691,9 @@ local function process_bits(p, data, adjusted_zones)
if left_top_y >= -zone_settings.zone_depth then if left_top_y >= -zone_settings.zone_depth then
generate_zone = starting_zone generate_zone = starting_zone
else else
generate_zone = zones[adjusted_zones.shuffled_zones[index]] generate_zone = zones[adjusted_zones.shuffled_zones[index]].fn
if not generate_zone then if not generate_zone then
generate_zone = zones[adjusted_zones.shuffled_zones[adjusted_zones.size]] generate_zone = zones[adjusted_zones.shuffled_zones[adjusted_zones.size]].fn
end end
end end

View File

@ -133,6 +133,8 @@ function Public.wave_size_rng() -- random variance in attack sizes
local memory = Memory.get_crew_memory() local memory = Memory.get_crew_memory()
local destination = Common.current_destination() local destination = Common.current_destination()
local rng_scale = Balance.crew_scale()^(1/4) -- slightly dampen wave variance for small crews as they can't handle it
-- prevent situation where when player reveals spawner, he immediately gets surrounded by massive amount of biters (especially late game) -- prevent situation where when player reveals spawner, he immediately gets surrounded by massive amount of biters (especially late game)
if destination and destination.type == SurfacesCommon.enum.ISLAND then if destination and destination.type == SurfacesCommon.enum.ISLAND then
if destination.dynamic_data and destination.dynamic_data.disabled_wave_timer and destination.dynamic_data.disabled_wave_timer > 0 then if destination.dynamic_data and destination.dynamic_data.disabled_wave_timer and destination.dynamic_data.disabled_wave_timer > 0 then
@ -144,17 +146,18 @@ function Public.wave_size_rng() -- random variance in attack sizes
local wave_size_multiplier = 1 local wave_size_multiplier = 1
local rng1 = Math.random(100) local rng1 = Math.random(100)
if rng1 > wave_percentage_chance then if rng1 > wave_percentage_chance then
wave_size_multiplier = 0 wave_size_multiplier = 0
elseif memory.overworldx > 0 then elseif memory.overworldx > 0 then
local rng2 = Math.random(1000) local rng2 = Math.random(1000)
if rng2 <= 890 then if rng2 >= 110 * rng_scale then
wave_size_multiplier = 1 wave_size_multiplier = 1
elseif rng2 <= 970 then elseif rng2 >= 30 * rng_scale then
wave_size_multiplier = 1.5 wave_size_multiplier = 1.5
elseif rng2 <= 985 then elseif rng2 >= 15 * rng_scale then
wave_size_multiplier = 2 wave_size_multiplier = 2
elseif rng2 <= 995 then elseif rng2 >= 5 * rng_scale then
wave_size_multiplier = 3 wave_size_multiplier = 3
else else
wave_size_multiplier = 4 wave_size_multiplier = 4
@ -902,7 +905,39 @@ function Public.try_boat_biters_attack()
end end
local function on_entity_destroyed(event)
local registration_number = event.registration_number
local p
local biter_name
local surface_name
local memory
for i = 1,3 do
Memory.set_working_id(i)
memory = Memory.get_crew_memory()
if memory.elite_biters_stream_registrations then
for j, r in pairs(memory.elite_biters_stream_registrations) do
if r.number == registration_number then
p = r.position
biter_name = r.biter_name
surface_name = r.surface_name
memory.elite_biters_stream_registrations = Utils.ordered_table_with_index_removed(memory.elite_biters_stream_registrations, j)
break
end
end
end
if p then break end
end
if p then
local surface = game.surfaces[surface_name]
if not (surface and surface.valid) then return end
local p2 = surface.find_non_colliding_position('medium-biter', p, 10, 0.2)
if not p2 then return end
surface.create_entity{name = biter_name, position = p2, force = memory.enemy_force_name}
end
end
-- function Public.destroy_inactive_scripted_biters() -- function Public.destroy_inactive_scripted_biters()
@ -921,5 +956,8 @@ end
--=== Data --=== Data
local event = require 'utils.event'
event.add(defines.events.on_entity_destroyed, on_entity_destroyed)
return Public return Public

View File

@ -109,17 +109,23 @@ local function biters_chew_stuff_faster(event)
if not (event.cause and event.cause.valid and event.cause.force and event.cause.force.name and event.entity and event.entity.valid and event.entity.force and event.entity.force.name) then return end if not (event.cause and event.cause.valid and event.cause.force and event.cause.force.name and event.entity and event.entity.valid and event.entity.force and event.entity.force.name) then return end
if event.cause.force.name ~= memory.enemy_force_name then return end --Enemy Forces only if event.cause.force.name ~= memory.enemy_force_name then return end --Enemy Forces only
-- @TODO: "event.entity.force.index == 3" looks suspicious (and probably doesn't work?), investigate it
if (event.entity.force.index == 3 or event.entity.force.name == 'environment') then if (event.entity.force.index == 3 or event.entity.force.name == 'environment') then
event.entity.health = event.entity.health - event.final_damage_amount * 5 event.entity.health = event.entity.health - event.final_damage_amount * 5
event.final_damage_amount = event.final_damage_amount * 6
if destination and destination.type == Surfaces.enum.ISLAND and destination.subtype == IslandEnum.enum.MAZE then if destination and destination.type == Surfaces.enum.ISLAND and destination.subtype == IslandEnum.enum.MAZE then
event.entity.health = event.entity.health - event.final_damage_amount * 10 event.entity.health = event.entity.health - event.final_damage_amount
event.final_damage_amount = event.final_damage_amount * 2
end end
elseif event.entity.name == 'pipe' then elseif event.entity.name == 'pipe' then
event.entity.health = event.entity.health - event.final_damage_amount * 0.5 event.entity.health = event.entity.health - event.final_damage_amount * 0.5
event.final_damage_amount = event.final_damage_amount * 1.5
elseif event.entity.name == 'stone-furnace' then elseif event.entity.name == 'stone-furnace' then
event.entity.health = event.entity.health - event.final_damage_amount * 0.5 event.entity.health = event.entity.health - event.final_damage_amount * 0.5
event.final_damage_amount = event.final_damage_amount * 1.5
elseif event.entity.name == 'wooden-chest' or event.entity.name == 'stone-chest' or event.entity.name == 'steel-chest' then elseif event.entity.name == 'wooden-chest' or event.entity.name == 'stone-chest' or event.entity.name == 'steel-chest' then
event.entity.health = event.entity.health - event.final_damage_amount * 0.5 event.entity.health = event.entity.health - event.final_damage_amount * 0.5
event.final_damage_amount = event.final_damage_amount * 1.5
end end
end end
@ -202,7 +208,7 @@ local function damage_to_enemyboat_spawners(event)
if eb.spawner and eb.spawner.valid and event.entity == eb.spawner then if eb.spawner and eb.spawner.valid and event.entity == eb.spawner then
-- if eb.spawner and eb.spawner.valid and event.entity == eb.spawner and eb.state == Structures.Boats.enum_state.APPROACHING then -- if eb.spawner and eb.spawner.valid and event.entity == eb.spawner and eb.state == Structures.Boats.enum_state.APPROACHING then
local damage = event.final_damage_amount local damage = event.final_damage_amount
local remaining_health = Common.entity_damage_healthbar(event.entity, damage, destination.dynamic_data) local remaining_health = Common.entity_damage_healthbar(event.entity, damage)
if remaining_health and remaining_health <= 0 then if remaining_health and remaining_health <= 0 then
event.entity.die() event.entity.die()
@ -228,7 +234,7 @@ local function damage_to_elite_spawners(event)
if spawner and spawner.valid and event.entity == spawner then if spawner and spawner.valid and event.entity == spawner then
local damage = event.final_damage_amount local damage = event.final_damage_amount
local remaining_health = Common.entity_damage_healthbar(event.entity, damage, destination.dynamic_data) local remaining_health = Common.entity_damage_healthbar(event.entity, damage)
if remaining_health and remaining_health <= 0 then if remaining_health and remaining_health <= 0 then
event.entity.die() event.entity.die()
@ -253,7 +259,6 @@ local function damage_to_elite_biters(event)
local remaining_health = Common.entity_damage_healthbar(event.entity, damage) local remaining_health = Common.entity_damage_healthbar(event.entity, damage)
if remaining_health and remaining_health <= 0 then if remaining_health and remaining_health <= 0 then
memory.elite_biters[event.entity.unit_number] = nil
event.entity.die() event.entity.die()
end end
end end
@ -306,29 +311,27 @@ local function damage_to_krakens(event)
if not (surface_name == memory.sea_name) then return end if not (surface_name == memory.sea_name) then return end
local unit_number = event.entity.unit_number local unit_number = event.entity.unit_number
local damage = event.final_damage_amount
local adjusted_damage = damage
if event.damage_type.name and event.damage_type.name == 'poison' then if event.damage_type.name and event.damage_type.name == 'poison' then
adjusted_damage = adjusted_damage / 1.25 event.final_damage_amount = event.final_damage_amount / 1.25
elseif event.damage_type.name and (event.damage_type.name == 'explosion') then elseif event.damage_type.name and (event.damage_type.name == 'explosion') then
adjusted_damage = adjusted_damage / 1.5 event.final_damage_amount = event.final_damage_amount / 1.5
elseif event.damage_type.name and (event.damage_type.name == 'fire') then elseif event.damage_type.name and (event.damage_type.name == 'fire') then
adjusted_damage = adjusted_damage / 1.25 event.final_damage_amount = event.final_damage_amount / 1.25
end end
-- and additionally: -- and additionally:
if event.cause and event.cause.valid and event.cause.name == 'artillery-turret' then if event.cause and event.cause.valid and event.cause.name == 'artillery-turret' then
adjusted_damage = adjusted_damage / 1.5 event.final_damage_amount = event.final_damage_amount / 1.5
end end
if event.damage_type.name and (event.damage_type.name == 'laser') then if event.damage_type.name and (event.damage_type.name == 'laser') then
adjusted_damage = adjusted_damage / 7 --laser turrets are in range. give some resistance event.final_damage_amount = event.final_damage_amount / 7 --laser turrets are in range. give some resistance
end end
-- There should be a better way to do it than this... -- There should be a better way to do it than this...
if memory.healthbars and memory.healthbars[unit_number] then if memory.healthbars and memory.healthbars[unit_number] then
local kraken_id = memory.healthbars[unit_number].id local kraken_id = memory.healthbars[unit_number].id
local remaining_health = Common.entity_damage_healthbar(event.entity, adjusted_damage) local remaining_health = Common.entity_damage_healthbar(event.entity, event.final_damage_amount)
if remaining_health and remaining_health <= 0 then if remaining_health and remaining_health <= 0 then
Kraken.kraken_die(kraken_id) Kraken.kraken_die(kraken_id)
end end
@ -451,7 +454,6 @@ local function other_enemy_damage_bonuses(event)
end end
-- @TODO: Fix elite biters getting one shotted by Samurai/Hatamoto classes (it doesn't play well with virtual health) as well as check for other inconsistencies/issues when damaging entities with virtual health
local function damage_dealt_by_players_changes(event) local function damage_dealt_by_players_changes(event)
local memory = Memory.get_crew_memory() local memory = Memory.get_crew_memory()
@ -473,8 +475,38 @@ local function damage_dealt_by_players_changes(event)
-- Lethal damage must be unaffected, otherwise enemy will never die. -- Lethal damage must be unaffected, otherwise enemy will never die.
-- @Future reference: when implementing damage changes for mobs with healthbar, make this check with healthbar health too -- @Future reference: when implementing damage changes for mobs with healthbar, make this check with healthbar health too
if event.final_health > 0 then if event.final_health > 0 then
if physical then
-- QUARTERMASTER BUFFS
local nearby_players = player.surface.find_entities_filtered{position = player.position, radius = Balance.quartermaster_range, type = {'character'}}
for _, p2 in pairs(nearby_players) do
if p2.player and p2.player.valid then
local p2_index = p2.player.index
if event.entity.valid and Classes.get_class(p2_index) == Classes.enum.QUARTERMASTER then
-- event.entity.damage((Balance.quartermaster_bonus_physical_damage - 1) * event.final_damage_amount, character.force, 'impact', character) --triggers this function again, but not physical this time
Common.damage_hostile_entity(event.entity, (Balance.quartermaster_bonus_physical_damage - 1) * event.final_damage_amount)
event.final_damage_amount = event.final_damage_amount * Balance.quartermaster_bonus_physical_damage
end
end
end
-- PISTOL BUFFS
if character.shooting_state.state ~= defines.shooting.not_shooting then
local weapon = character.get_inventory(defines.inventory.character_guns)[character.selected_gun_index]
local ammo = character.get_inventory(defines.inventory.character_ammo)[character.selected_gun_index]
if event.entity.valid and weapon.valid_for_read and ammo.valid_for_read and weapon.name == 'pistol' and (ammo.name == 'firearm-magazine' or ammo.name == 'piercing-rounds-magazine' or ammo.name == 'uranium-rounds-magazine') then
-- event.entity.damage((Balance.pistol_damage_multiplier() - 1) * event.final_damage_amount, character.force, 'impact', character) --triggers this function again, but not physical this time
Common.damage_hostile_entity(event.entity, (Balance.pistol_damage_multiplier() - 1) * event.final_damage_amount)
event.final_damage_amount = event.final_damage_amount * Balance.pistol_damage_multiplier()
end
end
end
if class and class == Classes.enum.SCOUT then if class and class == Classes.enum.SCOUT then
event.entity.health = event.entity.health + (1 - Balance.scout_damage_dealt_multiplier) * event.final_damage_amount -- event.entity.health = event.entity.health + (1 - Balance.scout_damage_dealt_multiplier) * event.final_damage_amount
Common.damage_hostile_entity(event.entity, -(1 - Balance.scout_damage_dealt_multiplier) * event.final_damage_amount)
event.final_damage_amount = event.final_damage_amount * Balance.scout_damage_dealt_multiplier
elseif class and (class == Classes.enum.SAMURAI or class == Classes.enum.HATAMOTO) then elseif class and (class == Classes.enum.SAMURAI or class == Classes.enum.HATAMOTO) then
local samurai = class == Classes.enum.SAMURAI local samurai = class == Classes.enum.SAMURAI
local hatamoto = class == Classes.enum.HATAMOTO local hatamoto = class == Classes.enum.HATAMOTO
@ -496,20 +528,28 @@ local function damage_dealt_by_players_changes(event)
elseif hatamoto then elseif hatamoto then
extra_damage_to_deal = Balance.hatamoto_damage_dealt_with_melee * extra_physical_damage_from_research_multiplier extra_damage_to_deal = Balance.hatamoto_damage_dealt_with_melee * extra_physical_damage_from_research_multiplier
end end
elseif acid then --this hacky stuff is to implement repeated spillover splash damage, whilst getting around the fact that if ovekill damage takes something to zero health, we can't tell in that event how much double-overkill damage should be dealt by reading off its HP. This code assumes that characters only deal acid damage via this function. elseif acid then --this hacky stuff is to implement repeated spillover splash damage, whilst getting around the fact that if overkill damage takes something to zero health, we can't tell in that event how much double-overkill damage should be dealt by reading off its HP. This code assumes that characters only deal acid damage via this function.
extra_damage_to_deal = event.original_damage_amount * big_number extra_damage_to_deal = event.original_damage_amount * big_number
end end
else else
if samurai then if samurai then
event.entity.health = event.entity.health + (1 - Balance.samurai_damage_dealt_when_not_melee_multiplier) * event.final_damage_amount -- event.entity.health = event.entity.health + (1 - Balance.samurai_damage_dealt_when_not_melee_multiplier) * event.final_damage_amount
Common.damage_hostile_entity(event.entity, -(1 - Balance.samurai_damage_dealt_when_not_melee_multiplier) * event.final_damage_amount)
event.final_damage_amount = event.final_damage_amount * Balance.samurai_damage_dealt_when_not_melee_multiplier
elseif hatamoto then elseif hatamoto then
event.entity.health = event.entity.health + (1 - Balance.hatamoto_damage_dealt_when_not_melee_multiplier) * event.final_damage_amount -- event.entity.health = event.entity.health + (1 - Balance.hatamoto_damage_dealt_when_not_melee_multiplier) * event.final_damage_amount
Common.damage_hostile_entity(event.entity, -(1 - Balance.hatamoto_damage_dealt_when_not_melee_multiplier) * event.final_damage_amount)
event.final_damage_amount = event.final_damage_amount * Balance.hatamoto_damage_dealt_when_not_melee_multiplier
end end
end end
-- @TODO: This should preferably be reworked, so that "event_on_entity_damaged()" could be simpler by just returning multiplier, although doing AoE is quite fun.
-- @TODO: "event.entity.health >= extra_damage_to_deal" is pointless when enemy has virtual healthbar
if extra_damage_to_deal > 0 then if extra_damage_to_deal > 0 then
if event.entity.health >= extra_damage_to_deal then if event.entity.health >= extra_damage_to_deal then
event.entity.damage(extra_damage_to_deal, character.force, 'impact', character) --using .damage rather than subtracting from health directly plays better with entities which use healthbars -- event.entity.damage(extra_damage_to_deal, character.force, 'impact', character) --using .damage rather than subtracting from health directly plays better with entities which use healthbars
Common.damage_hostile_entity(event.entity, extra_damage_to_deal)
event.final_damage_amount = event.final_damage_amount + extra_damage_to_deal
else else
local surplus = (extra_damage_to_deal - event.entity.health)*0.8 local surplus = (extra_damage_to_deal - event.entity.health)*0.8
event.entity.die(character.force, character) event.entity.die(character.force, character)
@ -520,30 +560,6 @@ local function damage_dealt_by_players_changes(event)
end end
end end
end end
if physical then
-- QUARTERMASTER BUFFS
local nearby_players = player.surface.find_entities_filtered{position = player.position, radius = Balance.quartermaster_range, type = {'character'}}
for _, p2 in pairs(nearby_players) do
if p2.player and p2.player.valid then
local p2_index = p2.player.index
if event.entity.valid and player_index ~= p2_index and Classes.get_class(p2_index) == Classes.enum.QUARTERMASTER then
event.entity.damage((Balance.quartermaster_bonus_physical_damage - 1) * event.final_damage_amount, character.force, 'impact', character) --triggers this function again, but not physical this time
end
end
end
-- PISTOL BUFFS
if character.shooting_state.state ~= defines.shooting.not_shooting then
local weapon = character.get_inventory(defines.inventory.character_guns)[character.selected_gun_index]
local ammo = character.get_inventory(defines.inventory.character_ammo)[character.selected_gun_index]
if event.entity.valid and weapon.valid_for_read and ammo.valid_for_read and weapon.name == 'pistol' and (ammo.name == 'firearm-magazine' or ammo.name == 'piercing-rounds-magazine' or ammo.name == 'uranium-rounds-magazine') then
event.entity.damage(event.final_damage_amount * (Balance.pistol_damage_multiplier() - 1), character.force, 'impact', character) --triggers this function again, but not physical this time
end
end
end
end end
end end
@ -564,8 +580,8 @@ local function swamp_resist_poison(event)
if not ((entity.type and entity.type == 'tree') or (event.entity.force and event.entity.force.name == memory.enemy_force_name)) then return end if not ((entity.type and entity.type == 'tree') or (event.entity.force and event.entity.force.name == memory.enemy_force_name)) then return end
local damage = event.final_damage_amount event.entity.health = event.entity.health + event.final_damage_amount
event.entity.health = event.entity.health + damage event.final_damage_amount = 0
end end
@ -582,18 +598,20 @@ local function maze_walls_resistance(event)
if not ((entity.type and entity.type == 'tree') or entity.name == 'rock-huge' or entity.name == 'rock-big' or entity.name == 'sand-rock-big') then return end if not ((entity.type and entity.type == 'tree') or entity.name == 'rock-huge' or entity.name == 'rock-big' or entity.name == 'sand-rock-big') then return end
local damage = event.final_damage_amount
if (event.damage_type.name and (event.damage_type.name == 'explosion' or event.damage_type.name == 'poison')) then if (event.damage_type.name and (event.damage_type.name == 'explosion' or event.damage_type.name == 'poison')) then
event.entity.health = event.entity.health + damage event.entity.health = event.entity.health + event.final_damage_amount
event.final_damage_amount = 0
elseif event.damage_type.name and event.damage_type.name == 'fire' then elseif event.damage_type.name and event.damage_type.name == 'fire' then
-- put out forest fires: -- put out forest fires:
for _, e2 in pairs(entity.surface.find_entities_filtered({area = {{entity.position.x - 4, entity.position.y - 4},{entity.position.x + 4, entity.position.y + 4}}, name = "fire-flame-on-tree"})) do for _, e2 in pairs(entity.surface.find_entities_filtered({area = {{entity.position.x - 4, entity.position.y - 4},{entity.position.x + 4, entity.position.y + 4}}, name = "fire-flame-on-tree"})) do
if e2.valid then e2.destroy() end if e2.valid then e2.destroy() end
end end
else else
if event.cause and event.cause.valid then
if string.sub(event.cause.force.name, 1, 4) == 'crew' then --player damage only if string.sub(event.cause.force.name, 1, 4) == 'crew' then --player damage only
event.entity.health = event.entity.health + damage * 0.9 event.entity.health = event.entity.health + event.final_damage_amount * 0.9
event.final_damage_amount = event.final_damage_amount * 0.1
end
end end
end end
end end
@ -627,6 +645,9 @@ end
-- -- end -- -- end
-- end -- end
-- Piratux: I feel sorry for whoever needs to lay eyes on this. Bottle of bleach is adviced to have by your side.
-- @TODO: Possible rework solution: "event_on_entity_damaged()" should accumulate final damage dealt multiplier, and "fix" entity health or deal proper damage to virtual healthbar at the very end of the function. Entity deaths should be handled at "event_on_entity_died()" instead, to avoid the mess.
-- NOTE: "event.cause" may not always be provided. -- NOTE: "event.cause" may not always be provided.
-- However, special care needs to be taken when "event.cause" is nil and entity has healthbar (better not ignore such damage or it can cause issues, such as needing to handle their death on "entity_died" functions as opposed to here) -- However, special care needs to be taken when "event.cause" is nil and entity has healthbar (better not ignore such damage or it can cause issues, such as needing to handle their death on "entity_died" functions as opposed to here)
local function event_on_entity_damaged(event) local function event_on_entity_damaged(event)
@ -1314,7 +1335,7 @@ local function event_on_entity_died(event)
-- I think the only reason krakens don't trigger this right now is that they are destroyed rather than .die() -- I think the only reason krakens don't trigger this right now is that they are destroyed rather than .die()
else else
local destination = Common.current_destination() local destination = Common.current_destination()
if not (destination and destination.dynamic_data and destination.dynamic_data.quest_type and (not destination.dynamic_data.quest_complete)) then return end if destination and destination.dynamic_data and destination.dynamic_data.quest_type and (not destination.dynamic_data.quest_complete) then
if destination.dynamic_data.quest_type == Quest.enum.WORMS and entity.type == 'turret' then if destination.dynamic_data.quest_type == Quest.enum.WORMS and entity.type == 'turret' then
destination.dynamic_data.quest_progress = destination.dynamic_data.quest_progress + 1 destination.dynamic_data.quest_progress = destination.dynamic_data.quest_progress + 1
Quest.try_resolve_quest() Quest.try_resolve_quest()
@ -1323,6 +1344,41 @@ local function event_on_entity_died(event)
end end
end end
-- elite biter death
local elite_biters = memory.elite_biters
if elite_biters and entity and entity.valid and entity.force.name == memory.enemy_force_name and elite_biters and elite_biters[entity.unit_number] then
local surface = entity.surface
if surface and surface.valid then
-- Shoot spit around and spawn biters where spit lands
local arc_size = (2 * Math.pi) / Balance.biters_spawned_on_elite_biter_death
for i = 1, Balance.biters_spawned_on_elite_biter_death, 1 do
local offset = Math.random_vec_in_arc(Math.random(4, 8), arc_size * i, arc_size)
local target_pos = Math.vector_sum(entity.position, offset)
local stream = surface.create_entity{
name = 'acid-stream-spitter-big',
position = entity.position,
force = memory.enemy_force_name,
source = entity.position,
target = target_pos,
max_range = 500,
speed = 0.1
}
if not memory.elite_biters_stream_registrations then memory.elite_biters_stream_registrations = {} end
memory.elite_biters_stream_registrations[#memory.elite_biters_stream_registrations + 1] = {
number = script.register_on_entity_destroyed(stream),
position = target_pos,
biter_name = entity.name,
surface_name = surface.name -- surface name is needed to know where biter died
}
end
end
memory.elite_biters[entity.unit_number] = nil
end
end
function Public.research_apply_buffs(event) function Public.research_apply_buffs(event)
local memory = Memory.get_crew_memory() local memory = Memory.get_crew_memory()
local force = memory.force local force = memory.force
@ -1435,22 +1491,26 @@ local function event_on_player_joined_game(event)
local crew_to_put_back_in = nil local crew_to_put_back_in = nil
for _, memory in pairs(global_memory.crew_memories) do for _, memory in pairs(global_memory.crew_memories) do
if Common.is_id_valid(memory.id) and memory.crewstatus == Crew.enum.ADVENTURING and memory.temporarily_logged_off_characters[player.index] then if Common.is_id_valid(memory.id) and memory.crewstatus == Crew.enum.ADVENTURING and memory.temporarily_logged_off_player_data[player.index] then
crew_to_put_back_in = memory.id crew_to_put_back_in = memory.id
break break
end end
end end
if crew_to_put_back_in then if crew_to_put_back_in then
Crew.join_crew(player, crew_to_put_back_in, true) log('INFO: ' .. player.name .. ' (crew ID: ' .. crew_to_put_back_in .. ') joined the game')
local memory = global_memory.crew_memories[crew_to_put_back_in] Memory.set_working_id(crew_to_put_back_in)
Crew.join_crew(player, true)
local memory = Memory.get_crew_memory()
if (not memory.run_is_protected) and #memory.crewplayerindices <= 1 then if (not memory.run_is_protected) and #memory.crewplayerindices <= 1 then
Roles.make_captain(player) Roles.make_captain(player)
end end
if _DEBUG then log('putting player back in their old crew') end if _DEBUG then log('putting player back in their old crew') end
else else
log('INFO: ' .. player.name .. ' (crew ID: NONE) joined the game')
if player.character and player.character.valid then if player.character and player.character.valid then
player.character.destroy() player.character.destroy()
end end
@ -1473,6 +1533,8 @@ local function event_on_player_joined_game(event)
Common.notify_player_expected(player, {'pirates.player_join_game_info'}) Common.notify_player_expected(player, {'pirates.player_join_game_info'})
player.force = Common.lobby_force_name
-- It was suggested to always spawn players in lobby, in hopes that they may want to create their crew increasing the popularity of scenario. -- It was suggested to always spawn players in lobby, in hopes that they may want to create their crew increasing the popularity of scenario.
-- Auto-join the oldest crew: -- Auto-join the oldest crew:
@ -1503,7 +1565,7 @@ local function event_on_player_joined_game(event)
-- end -- end
-- ) -- )
-- if ages[1] then -- if ages[1] then
-- Crew.join_crew(player, ages[1].id) -- Crew.join_crew(player)
-- local memory = global_memory.crew_memories[ages[1].id] -- local memory = global_memory.crew_memories[ages[1].id]
-- if (not memory.run_is_protected) and #memory.crewplayerindices <= 1 then -- if (not memory.run_is_protected) and #memory.crewplayerindices <= 1 then
@ -1563,6 +1625,11 @@ local function event_on_pre_player_left_game(event)
local global_memory = Memory.get_global_memory() local global_memory = Memory.get_global_memory()
-- figure out which crew this is about: -- figure out which crew this is about:
local crew_id = Common.get_id_from_force_name(player.force.name) local crew_id = Common.get_id_from_force_name(player.force.name)
if crew_id then
log('INFO: ' .. player.name .. ' (crew ID: ' .. crew_id .. ') left the game')
else
log('INFO: ' .. player.name .. ' (crew ID: NONE) left the game')
end
for k, proposal in pairs(global_memory.crewproposals) do for k, proposal in pairs(global_memory.crewproposals) do
if proposal and proposal.endorserindices then if proposal and proposal.endorserindices then
@ -2080,7 +2147,7 @@ local function event_on_console_chat(event)
local memory = Memory.get_crew_memory() local memory = Memory.get_crew_memory()
-- NOTE: This check to see if player is in a crew is not reliable and can sometimes cause errors! -- NOTE: This check to see if player is in a crew is not reliable and can sometimes cause errors!
if player.force.name == 'player' then if player.force.name == Common.lobby_force_name then
local other_force_indices = global_memory.crew_active_ids local other_force_indices = global_memory.crew_active_ids
for _, index in pairs(other_force_indices) do for _, index in pairs(other_force_indices) do

View File

@ -262,33 +262,35 @@ function Public.prune_offline_characters_list(tickinterval)
if memory.game_lost then return end if memory.game_lost then return end
for player_index, tick in pairs(memory.temporarily_logged_off_characters) do for player_index, data in pairs(memory.temporarily_logged_off_player_data) do
if player_index and game.players[player_index] and game.players[player_index].connected then if game.players[player_index] and game.players[player_index].connected then
memory.temporarily_logged_off_characters[player_index] = nil -- memory.temporarily_logged_off_characters[player_index] = nil
if memory.temporarily_logged_off_characters_items[player_index] then -- if memory.temporarily_logged_off_characters_items[player_index] then
memory.temporarily_logged_off_characters_items[player_index].destroy() -- memory.temporarily_logged_off_characters_items[player_index].destroy()
end -- end
memory.temporarily_logged_off_characters_items[player_index] = nil -- memory.temporarily_logged_off_characters_items[player_index] = nil
memory.temporarily_logged_off_player_data[player_index] = nil
else else
if player_index and tick < game.tick - 60 * 60 * Common.logged_off_items_preserved_minutes then local tick = data.tick
local any = false if tick < game.tick - 60 * 60 * Common.temporarily_logged_off_player_data_preservation_minutes then
local temp_inv = memory.temporarily_logged_off_characters_items[player_index] -- local any = false
if temp_inv then -- local temp_inv = memory.temporarily_logged_off_characters_items[player_index]
for i = 1, #temp_inv, 1 do -- if temp_inv then
if temp_inv[i] and temp_inv[i].valid and temp_inv[i].valid_for_read then -- for i = 1, #temp_inv, 1 do
Common.give_items_to_crew(temp_inv[i]) -- if temp_inv[i] and temp_inv[i].valid and temp_inv[i].valid_for_read then
any = true -- Common.give_items_to_crew(temp_inv[i])
end -- any = true
end -- end
if any then -- end
Common.notify_force_light(memory.force, {'pirates.recover_offline_player_items'})
end
temp_inv.destroy() -- if any then
end -- Common.notify_force_light(memory.force, {'pirates.recover_offline_player_items'})
-- end
memory.temporarily_logged_off_characters[player_index] = nil -- temp_inv.destroy()
memory.temporarily_logged_off_characters_items[player_index] = nil -- end
memory.temporarily_logged_off_player_data[player_index] = nil
end end
end end
end end
@ -1706,19 +1708,14 @@ function Public.update_time_remaining()
if memory.boat.state == Boats.enum_state.RETREATING then return end if memory.boat.state == Boats.enum_state.RETREATING then return end
if not memory.boat.surface_name then return end if not memory.boat.surface_name then return end
local surface_name_decoded = Surfaces.SurfacesCommon.decode_surface_name(memory.boat.surface_name) if destination.type == Surfaces.enum.ISLAND then
local type = surface_name_decoded.type if destination.static_params and destination.static_params.base_cost_to_undock and Boats.need_resources_to_undock(Common.overworldx(), destination.subtype) and (not Common.query_can_pay_cost_to_leave()) then
if type == Surfaces.enum.ISLAND then
if destination.static_params and destination.static_params.base_cost_to_undock and Balance.need_resources_to_undock(Common.overworldx()) == true and (not Common.query_can_pay_cost_to_leave()) then
Crew.try_lose({'pirates.loss_resources_were_not_collected_in_time'}) Crew.try_lose({'pirates.loss_resources_were_not_collected_in_time'})
else else
if Balance.need_resources_to_undock(Common.overworldx()) == true then Common.consume_undock_cost_resources()
Progression.try_retreat_from_island(false)
else
Progression.retreat_from_island(false) Progression.retreat_from_island(false)
end end
end elseif destination.type == Surfaces.enum.DOCK then
elseif type == Surfaces.enum.DOCK then
Progression.undock_from_dock(false) Progression.undock_from_dock(false)
end end
end end

View File

@ -37,12 +37,12 @@ Public.class_reward_tick_rate_in_seconds = 7
Public.poison_damage_multiplier = 1.85 Public.poison_damage_multiplier = 1.85
Public.every_nth_tree_gives_coins = 10 Public.every_nth_tree_gives_coins = 10
Public.samurai_damage_taken_multiplier = 0.32 Public.samurai_damage_taken_multiplier = 0.45
Public.samurai_damage_dealt_when_not_melee_multiplier = 0.75 Public.samurai_damage_dealt_when_not_melee_multiplier = 0.75
Public.samurai_damage_dealt_with_melee = 25 Public.samurai_damage_dealt_with_melee = 30
Public.hatamoto_damage_taken_multiplier = 0.21 Public.hatamoto_damage_taken_multiplier = 0.3
Public.hatamoto_damage_dealt_when_not_melee_multiplier = 0.75 Public.hatamoto_damage_dealt_when_not_melee_multiplier = 0.75
Public.hatamoto_damage_dealt_with_melee = 45 Public.hatamoto_damage_dealt_with_melee = 50
Public.iron_leg_damage_taken_multiplier = 0.24 Public.iron_leg_damage_taken_multiplier = 0.24
Public.iron_leg_iron_ore_required = 3000 Public.iron_leg_iron_ore_required = 3000
Public.deckhand_extra_speed = 1.25 Public.deckhand_extra_speed = 1.25
@ -93,6 +93,10 @@ Public.min_ore_spawn_distance = 10
Public.biter_boats_start_arrive_x = 40 * 5 Public.biter_boats_start_arrive_x = 40 * 5
Public.need_resources_to_undock_x = 40 * 20 Public.need_resources_to_undock_x = 40 * 20
Public.biters_spawned_on_elite_biter_death = 4
Public.walkways_frozen_pool_damage = 12
function Public.starting_boatEEIpower_production_MW() function Public.starting_boatEEIpower_production_MW()
-- return 3 * Math.sloped(Common.capacity_scale(), 1/2) / 2 --/2 as we have 2 -- return 3 * Math.sloped(Common.capacity_scale(), 1/2) / 2 --/2 as we have 2
return 3/2 return 3/2
@ -167,7 +171,8 @@ function Public.game_slowness_scale()
-- return 1 / Public.crew_scale()^(55/100) / Math.sloped(Common.difficulty_scale(), 1/4) --changed crew_scale factor significantly to help smaller crews -- return 1 / Public.crew_scale()^(55/100) / Math.sloped(Common.difficulty_scale(), 1/4) --changed crew_scale factor significantly to help smaller crews
-- return 1 / (Public.crew_scale()^(50/100) / Math.sloped(Common.difficulty_scale(), 1/4)) --changed crew_scale factor significantly to help smaller crews -- return 1 / (Public.crew_scale()^(50/100) / Math.sloped(Common.difficulty_scale(), 1/4)) --changed crew_scale factor significantly to help smaller crews
local scale = 0.3 + Math.sloped(Common.difficulty_scale(), -0.15) / (Public.crew_scale()^(1/8)) -- local scale = 0.3 + Math.sloped(Common.difficulty_scale(), -0.15) / (Public.crew_scale()^(1/8))
local scale = 2.6 * Math.sloped(Common.difficulty_scale(), -0.2) - Public.crew_scale()^(1/4)
return Math.max(1, scale) return Math.max(1, scale)
end end
@ -179,20 +184,10 @@ function Public.max_time_on_island_formula() --always >0 --tuned
-- (33 + 0.2 * (Common.overworldx()/40)^(1/3)) --based on observing x=2000, lets try killing the extra time -- (33 + 0.2 * (Common.overworldx()/40)^(1/3)) --based on observing x=2000, lets try killing the extra time
-- ) * Public.game_slowness_scale() -- ) * Public.game_slowness_scale()
local minimum_mins_on_island = 40 local minimum_mins_on_island = 30
return Math.ceil(60 * minimum_mins_on_island * Public.game_slowness_scale()) return Math.ceil(60 * minimum_mins_on_island * Public.game_slowness_scale())
end end
-- Returns true if uncollected resources will cause the crew to lose.
function Public.need_resources_to_undock(overworldx)
if overworldx >= Public.need_resources_to_undock_x then
return true
else
return false
end
end
-- In seconds -- In seconds
function Public.max_time_on_island(island_subtype) function Public.max_time_on_island(island_subtype)
local x = Common.overworldx() local x = Common.overworldx()
@ -518,6 +513,9 @@ Public.starting_fuel = 4000
Public.silo_max_hp = 5000 Public.silo_max_hp = 5000
Public.silo_resistance_factor = 7 Public.silo_resistance_factor = 7
-- Pistol shooting speed = 4/s
-- Submachine gun shooting speed = 10/s
-- Pistol damage multiplier shouldn't be >= 2.5, otherwise Submachine gun isn't worth using.
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 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_static_evo = 0.35 Public.kraken_static_evo = 0.35
@ -579,7 +577,7 @@ function Public.krakens_per_free_slot(overworldx)
if rng < 0.0025 * multiplier then if rng < 0.0025 * multiplier then
return 3 return 3
elseif rng < 0.075 * multiplier then elseif rng < 0.075 * multiplier then
return 1 return 2
elseif rng < 0.5 * multiplier then elseif rng < 0.5 * multiplier then
return 1 return 1
else else

View File

@ -36,6 +36,8 @@ local Classes = require 'maps.pirates.roles.classes'
local Gui = require 'maps.pirates.gui.gui' local Gui = require 'maps.pirates.gui.gui'
-- local Session = require 'utils.datastore.session_data'
local function cmd_set_memory(cmd) local function cmd_set_memory(cmd)
@ -46,7 +48,6 @@ end
local function check_admin(cmd) local function check_admin(cmd)
local Session = require 'utils.datastore.session_data'
local player = game.players[cmd.player_index] local player = game.players[cmd.player_index]
--local trusted = Session.get_trusted_table() --local trusted = Session.get_trusted_table()
local p local p
@ -415,6 +416,7 @@ function(cmd)
end end
end) end)
-- Try undock from an island or dock
commands.add_command( commands.add_command(
'undock', 'undock',
{'pirates.cmd_explain_undock'}, {'pirates.cmd_explain_undock'},
@ -435,6 +437,27 @@ function(cmd)
end end
end) end)
-- Force undock from an island or dock
commands.add_command(
'ret',
{'pirates.cmd_explain_dev'},
function(cmd)
cmd_set_memory(cmd)
local memory = Memory.get_crew_memory()
if not Common.is_id_valid(memory.id) then return end
-- local param = tostring(cmd.parameter)
if check_admin(cmd) then
if memory.boat.state == Boats.enum_state.DOCKED then
Progression.undock_from_dock(true)
elseif memory.boat.state == Boats.enum_state.LANDED then
Progression.retreat_from_island(true)
end
end
end)
commands.add_command( commands.add_command(
'tax', 'tax',
{'pirates.cmd_explain_tax'}, {'pirates.cmd_explain_tax'},
@ -530,20 +553,6 @@ function(cmd)
end end
end) end)
-- Undock from an island or dock
commands.add_command(
'ret',
{'pirates.cmd_explain_dev'},
function(cmd)
cmd_set_memory(cmd)
local param = tostring(cmd.parameter)
if check_admin(cmd) then
local player = game.players[cmd.player_index]
Progression.retreat_from_island(true)
end
end)
-- Move overworld boat right by a lot (you can jump over islands that way to skip them) -- Move overworld boat right by a lot (you can jump over islands that way to skip them)
commands.add_command( commands.add_command(
'jump', 'jump',

View File

@ -63,9 +63,11 @@ Public.ban_from_rejoining_crew_ticks = 45 * 60 --to prevent observing map and re
Public.afk_time = 60 * 60 * 5 Public.afk_time = 60 * 60 * 5
Public.afk_warning_time = 60 * 60 * 4.5 Public.afk_warning_time = 60 * 60 * 4.5
Public.logged_off_items_preserved_minutes = 5 Public.temporarily_logged_off_player_data_preservation_minutes = 1
Public.logout_unprotected_items = {'uranium-235', 'uranium-238', 'fluid-wagon', 'coal', 'electric-engine-unit', 'flying-robot-frame', 'advanced-circuit', 'beacon', 'speed-module-3', 'speed-module-2', 'roboport', 'construction-robot'} --internal inventories of these will not be preserved Public.logout_unprotected_items = {'uranium-235', 'uranium-238', 'fluid-wagon', 'coal', 'electric-engine-unit', 'flying-robot-frame', 'advanced-circuit', 'beacon', 'speed-module-3', 'speed-module-2', 'roboport', 'construction-robot'} --internal inventories of these will not be preserved
Public.lobby_force_name = 'player'
-- Public.mainshop_rate_limit_ticks = 11 -- Public.mainshop_rate_limit_ticks = 11
@ -138,7 +140,7 @@ end
function Public.notify_lobby(message, color_override) function Public.notify_lobby(message, color_override)
color_override = color_override or CoreData.colors.notify_lobby color_override = color_override or CoreData.colors.notify_lobby
game.forces['player'].print({"", '>> ', message}, color_override) game.forces[Public.lobby_force_name].print({"", '>> ', message}, color_override)
end end
function Public.notify_force(force, message, color_override) function Public.notify_force(force, message, color_override)
@ -688,6 +690,16 @@ function Public.spend_stored_resources(to_spend)
Public.update_boat_stored_resources() Public.update_boat_stored_resources()
end end
function Public.consume_undock_cost_resources()
local destination = Public.current_destination()
local cost = destination.static_params.base_cost_to_undock
if cost then
local adjusted_cost = Public.time_adjusted_departure_cost(cost)
Public.spend_stored_resources(adjusted_cost)
end
end
function Public.new_healthbar(text, target_entity, max_health, optional_id, health, size, extra_offset, location_override) function Public.new_healthbar(text, target_entity, max_health, optional_id, health, size, extra_offset, location_override)
health = health or max_health health = health or max_health
@ -773,11 +785,11 @@ end
function Public.entity_damage_healthbar(entity, damage, location_override) function Public.entity_damage_healthbar(entity, damage, location_override)
location_override = location_override or Memory.get_crew_memory() location_override = location_override or Memory.get_crew_memory()
if not (location_override.healthbars) then return end if not (location_override.healthbars) then return nil end
local unit_number = entity.unit_number local unit_number = entity.unit_number
local healthbar = location_override.healthbars[unit_number] local healthbar = location_override.healthbars[unit_number]
if not healthbar then return end if not healthbar then return nil end
local new_health = healthbar.health - damage local new_health = healthbar.health - damage
healthbar.health = new_health healthbar.health = new_health
@ -787,11 +799,17 @@ function Public.entity_damage_healthbar(entity, damage, location_override)
entity.health = entity.prototype.max_health entity.health = entity.prototype.max_health
end end
if healthbar.health > healthbar.max_health then
healthbar.health = healthbar.max_health
end
local final_health = healthbar.health
if healthbar.health <= 0 then if healthbar.health <= 0 then
location_override.healthbars[unit_number] = nil location_override.healthbars[unit_number] = nil
end end
return healthbar.health return final_health
end end
function Public.update_healthbar_rendering(new_healthbar, health) function Public.update_healthbar_rendering(new_healthbar, health)
@ -1849,7 +1867,7 @@ function Public.try_make_biter_elite(entity)
local memory = Memory.get_crew_memory() local memory = Memory.get_crew_memory()
local difficulty_index = CoreData.get_difficulty_option_from_value(memory.difficulty) local difficulty_index = CoreData.get_difficulty_option_from_value(memory.difficulty)
if difficulty_index < 3 then return end if difficulty_index < 3 and Public.overworldx() < 800 then return end
if Public.overworldx() == 0 then return end if Public.overworldx() == 0 then return end
@ -1858,7 +1876,7 @@ function Public.try_make_biter_elite(entity)
local health_multiplier local health_multiplier
if difficulty_index == 3 then if difficulty_index <= 3 then
health_multiplier = 5 health_multiplier = 5
else else
health_multiplier = 10 health_multiplier = 10
@ -1881,4 +1899,26 @@ function Public.try_make_biter_elite(entity)
end end
end end
-- This function is meant to handle damage adjustment cases that automatically damages/heals either entity or its virtual health.
-- NOTE: This is only meant for hostile entities (for now at least), as friendly units with healthbars are more difficult to handle
-- NOTE: "damage" can also be negative, which will heal the entity (but not past maximum health)
function Public.damage_hostile_entity(entity, damage)
if not (entity and entity.valid) then return end
local remaining_health = Public.entity_damage_healthbar(entity, damage)
-- Does entity have virtual healthbar
if remaining_health then
if remaining_health <= 0 then
entity.die()
end
else -- Not, so treat it as simple entity
-- Note: According to docs, health is automatically clamped to [0, max_health] so we don't need to do it
entity.health = entity.health - damage
if entity.health <= 0 then
entity.die()
end
end
end
return Public return Public

View File

@ -7,7 +7,7 @@ local _inspect = require 'utils.inspect'.inspect
local Public = {} local Public = {}
Public.scenario_id_name = 'pirates' Public.scenario_id_name = 'pirates'
Public.version_string = '1.5.1' --major.minor.patch versioning, to match factorio mod portal Public.version_string = '1.5.2' --major.minor.patch versioning, to match factorio mod portal
Public.blueprint_library_allowed = true Public.blueprint_library_allowed = true
Public.blueprint_importing_allowed = true Public.blueprint_importing_allowed = true

View File

@ -95,6 +95,8 @@ function Public.update_difficulty()
memory.difficulty_option = modal_id memory.difficulty_option = modal_id
memory.difficulty = CoreData.difficulty_options[modal_id].value memory.difficulty = CoreData.difficulty_options[modal_id].value
Cabin.update_captains_market_offers_based_on_difficulty(memory.difficulty_option)
end end
end end
@ -266,7 +268,7 @@ function Public.join_spectators(player, crewid)
player.set_controller{type = defines.controllers.spectator} player.set_controller{type = defines.controllers.spectator}
end end
local c = surface.create_entity{name = 'character', position = surface.find_non_colliding_position('character', Common.lobby_spawnpoint, 32, 0.5) or Common.lobby_spawnpoint, force = 'player'} local c = surface.create_entity{name = 'character', position = surface.find_non_colliding_position('character', Common.lobby_spawnpoint, 32, 0.5) or Common.lobby_spawnpoint, force = Common.lobby_force_name}
player.associate_character(c) player.associate_character(c)
@ -331,14 +333,11 @@ function Public.leave_spectators(player, quiet)
if _DEBUG then memory.crew_disband_tick = game.tick + 30*60*60 end if _DEBUG then memory.crew_disband_tick = game.tick + 30*60*60 end
end end
player.force = 'player' player.force = Common.lobby_force_name
end end
function Public.join_crew(player, crewid, rejoin) function Public.join_crew(player, rejoin)
if not crewid then return end
Memory.set_working_id(crewid)
local memory = Memory.get_crew_memory() local memory = Memory.get_crew_memory()
if not Common.validate_player(player) then return end if not Common.validate_player(player) then return end
@ -377,22 +376,51 @@ function Public.join_crew(player, crewid, rejoin)
memory.spectatorplayerindices = Utils.ordered_table_with_values_removed(memory.spectatorplayerindices, player.index) memory.spectatorplayerindices = Utils.ordered_table_with_values_removed(memory.spectatorplayerindices, player.index)
else else
Public.player_abandon_endorsements(player) if not (player.character and player.character.valid) then
player.force = memory.force player.set_controller{type = defines.controllers.god}
player.teleport(surface.find_non_colliding_position('character', memory.spawnpoint, 32, 0.5) or memory.spawnpoint, surface) player.create_character()
Common.notify_lobby({'pirates.lobby_to_crew_2', player.name, memory.name})
end end
Common.give_back_items_to_temporarily_logged_off_player(player) Public.player_abandon_endorsements(player)
player.force = memory.force
Common.notify_lobby({'pirates.lobby_to_crew_2', player.name, memory.name})
player.teleport(surface.find_non_colliding_position('character', memory.spawnpoint, 32, 0.5) or memory.spawnpoint, surface)
if rejoin then
if memory.temporarily_logged_off_player_data[player.index] then
local rejoin_data = memory.temporarily_logged_off_player_data[player.index]
local rejoin_surface = game.surfaces[rejoin_data.surface_name]
-- If surface where player left the game still exists, place him there.
if rejoin_surface and rejoin_surface.valid then
-- Edge case: if player left the game while he was on the boat, it could be that boat position
-- changed when he left the game vs when he came back.
if not (rejoin_data.on_boat and rejoin_data.on_island) then
player.teleport(rejoin_surface.find_non_colliding_position('character', rejoin_data.position, 32, 0.5) or memory.spawnpoint, rejoin_surface)
end
end
-- We shouldn't give back items when player was on island, but that island is gone.
-- Left in case we decide to store player inventory in offline character instead of in the corpse.
-- if not (rejoin_data.on_island and (not (rejoin_surface and rejoin_surface.valid))) then
-- Common.give_back_items_to_temporarily_logged_off_player(player)
-- end
memory.temporarily_logged_off_player_data[player.index] = nil
end
end
end
Common.notify_force(player.force, {'pirates.lobby_to_crew', player.name}) Common.notify_force(player.force, {'pirates.lobby_to_crew', player.name})
-- Server.to_discord_embed_raw(CoreData.comfy_emojis.yum1 .. '[' .. memory.name .. '] ' .. message) -- Server.to_discord_embed_raw(CoreData.comfy_emojis.yum1 .. '[' .. memory.name .. '] ' .. message)
memory.crewplayerindices[#memory.crewplayerindices + 1] = player.index memory.crewplayerindices[#memory.crewplayerindices + 1] = player.index
-- don't give them items if they've been in the crew recently: -- don't give them items if they've been in the crew recently
if not (memory.tempbanned_from_joining_data and memory.tempbanned_from_joining_data[player.index]) and (not rejoin) then --just using tempbanned_from_joining_data as a quick proxy for whether the player has ever been in this run before -- just using tempbanned_from_joining_data as a quick proxy for whether the player has ever been in this run before
if not (memory.tempbanned_from_joining_data and memory.tempbanned_from_joining_data[player.index]) and (not rejoin) then
for item, amount in pairs(Balance.starting_items_player_late) do for item, amount in pairs(Balance.starting_items_player_late) do
player.insert({name = item, count = amount}) player.insert({name = item, count = amount})
end end
@ -436,33 +464,48 @@ function Public.leave_crew(player, to_lobby, quiet)
-- -- Server.to_discord_embed_raw(CoreData.comfy_emojis.feel .. '[' .. memory.name .. '] ' .. message) -- -- Server.to_discord_embed_raw(CoreData.comfy_emojis.feel .. '[' .. memory.name .. '] ' .. message)
-- end -- end
-- @TODO: figure out why surface_name can be nil
-- When player remains in island when ship leaves, prevent him from getting items back
local save_items = true
if player.surface and
player.surface.valid and
memory.boat and
memory.boat.surface_name
then
local player_surface_type = SurfacesCommon.decode_surface_name(player.surface.name).type local player_surface_type = SurfacesCommon.decode_surface_name(player.surface.name).type
local boat_surface_type = SurfacesCommon.decode_surface_name(memory.boat.surface_name).type local boat_surface_type = SurfacesCommon.decode_surface_name(memory.boat.surface_name).type
if player_surface_type == Surfaces.enum.ISLAND and boat_surface_type == Surfaces.enum.SEA then -- @TODO: figure out why surface_name can be nil
save_items = false -- When player remains in island when ship leaves, prevent him from getting items back
end -- local save_items = true
-- if player.surface and
-- player.surface.valid and
-- memory.boat and
-- memory.boat.surface_name
-- then
-- if player_surface_type == Surfaces.enum.ISLAND and boat_surface_type == Surfaces.enum.SEA then
-- save_items = false
-- end
-- end
-- Code regarding item saving is left here if we decide it's better to store items in
-- logged off character as opposed to in the character corpse.
-- if to_lobby then
-- if save_items then
-- Common.send_important_items_from_player_to_crew(player, true)
-- end
-- char.die(memory.force_name)
-- else
if not to_lobby then
if not memory.temporarily_logged_off_player_data then memory.temporarily_logged_off_player_data = {} end
memory.temporarily_logged_off_player_data[player.index] = {
on_island = (player_surface_type == Surfaces.enum.ISLAND),
on_boat = (player_surface_type == boat_surface_type) and Boats.on_boat(memory.boat, player.character.position),
surface_name = player.surface.name,
position = player.character.position,
tick = game.tick
}
-- if save_items then
-- Common.temporarily_store_logged_off_character_items(player)
-- end
-- memory.temporarily_logged_off_characters[player.index] = game.tick
end end
if to_lobby then
if save_items then
Common.send_important_items_from_player_to_crew(player, true)
end
char.die(memory.force_name) char.die(memory.force_name)
else
if save_items then
Common.temporarily_store_logged_off_character_items(player)
end
memory.temporarily_logged_off_characters[player.index] = game.tick
end
-- else -- else
-- if not quiet then -- if not quiet then
@ -475,7 +518,7 @@ function Public.leave_crew(player, to_lobby, quiet)
player.set_controller{type = defines.controllers.god} player.set_controller{type = defines.controllers.god}
player.teleport(surface.find_non_colliding_position('character', Common.lobby_spawnpoint, 32, 0.5) or Common.lobby_spawnpoint, surface) player.teleport(surface.find_non_colliding_position('character', Common.lobby_spawnpoint, 32, 0.5) or Common.lobby_spawnpoint, surface)
player.force = 'player' player.force = Common.lobby_force_name
player.create_character() player.create_character()
Event.raise(BottomFrame.events.bottom_quickbar_respawn_raise, {player_index = player.index}) Event.raise(BottomFrame.events.bottom_quickbar_respawn_raise, {player_index = player.index})
end end
@ -554,7 +597,7 @@ function Public.disband_crew(donotprint)
for _,player in pairs(players) do for _,player in pairs(players) do
if player.controller_type == defines.controllers.editor then player.toggle_map_editor() end if player.controller_type == defines.controllers.editor then player.toggle_map_editor() end
player.force = 'player' player.force = Common.lobby_force_name
end end
if (not donotprint) then if (not donotprint) then
@ -740,12 +783,14 @@ function Public.initialise_crew(accepted_proposal)
memory.spectatorplayerindices = {} memory.spectatorplayerindices = {}
memory.tempbanned_from_joining_data = {} memory.tempbanned_from_joining_data = {}
memory.destinations = {} memory.destinations = {}
memory.temporarily_logged_off_characters = {} -- memory.temporarily_logged_off_characters = {}
memory.temporarily_logged_off_characters_items = {} -- memory.temporarily_logged_off_characters_items = {}
memory.temporarily_logged_off_player_data = {}
memory.class_renderings = {} memory.class_renderings = {}
memory.class_auxiliary_data = {} memory.class_auxiliary_data = {}
memory.elite_biters = {} memory.elite_biters = {}
memory.elite_biters_stream_registrations = {}
memory.pet_biters = {} memory.pet_biters = {}
memory.hold_surface_count = 1 memory.hold_surface_count = 1
@ -786,8 +831,8 @@ function Public.initialise_crew(accepted_proposal)
memory.officers_table = {} memory.officers_table = {}
memory.classes_table = {} -- stores all unlocked untaken classes memory.classes_table = {} -- stores all unlocked taken classes
memory.spare_classes = {} -- stores all unlocked taken classes memory.spare_classes = {} -- stores all unlocked untaken classes
memory.recently_purchased_classes = {} -- stores recently unlocked classes to add it back to available class pool list later memory.recently_purchased_classes = {} -- stores recently unlocked classes to add it back to available class pool list later
memory.unlocked_classes = {} -- stores all unlocked classes just for GUI (to have consistent order) memory.unlocked_classes = {} -- stores all unlocked classes just for GUI (to have consistent order)
memory.available_classes_pool = Classes.initial_class_pool() -- stores classes that can be randomly picked for unlocking memory.available_classes_pool = Classes.initial_class_pool() -- stores classes that can be randomly picked for unlocking
@ -875,8 +920,8 @@ function Public.reset_crew_and_enemy_force(id)
crew_force.set_friend('player', true) crew_force.set_friend(Common.lobby_force_name, true)
game.forces['player'].set_friend(crew_force, true) game.forces[Common.lobby_force_name].set_friend(crew_force, true)
crew_force.set_friend(ancient_friendly_force, true) crew_force.set_friend(ancient_friendly_force, true)
ancient_friendly_force.set_friend(crew_force, true) ancient_friendly_force.set_friend(crew_force, true)
enemy_force.set_friend(ancient_friendly_force, true) enemy_force.set_friend(ancient_friendly_force, true)
@ -915,9 +960,9 @@ function Public.reset_crew_and_enemy_force(id)
crew_force.set_turret_attack_modifier(k, v) crew_force.set_turret_attack_modifier(k, v)
end end
crew_force.technologies['kovarex-enrichment-process'].researched = true -- needed for radioactive island
-- crew_force.technologies['circuit-network'].researched = true -- crew_force.technologies['circuit-network'].researched = true
-- crew_force.technologies['uranium-processing'].researched = true -- crew_force.technologies['uranium-processing'].researched = true
-- crew_force.technologies['kovarex-enrichment-process'].researched = true
-- crew_force.technologies['gun-turret'].researched = true -- crew_force.technologies['gun-turret'].researched = true
-- crew_force.technologies['electric-energy-distribution-1'].researched = true -- crew_force.technologies['electric-energy-distribution-1'].researched = true
-- crew_force.technologies['electric-energy-distribution-2'].researched = true -- crew_force.technologies['electric-energy-distribution-2'].researched = true
@ -953,7 +998,7 @@ function Public.reset_crew_and_enemy_force(id)
crew_force.technologies['artillery'].enabled = false crew_force.technologies['artillery'].enabled = false
-- crew_force.technologies['destroyer'].enabled = false -- crew_force.technologies['destroyer'].enabled = false
crew_force.technologies['spidertron'].enabled = false crew_force.technologies['spidertron'].enabled = false
crew_force.technologies['atomic-bomb'].enabled = false -- crew_force.technologies['atomic-bomb'].enabled = false -- experimenting
crew_force.technologies['explosive-rocketry'].enabled = false crew_force.technologies['explosive-rocketry'].enabled = false
-- crew_force.technologies['research-speed-1'].enabled = false -- crew_force.technologies['research-speed-1'].enabled = false

View File

@ -114,17 +114,18 @@ function Public.toggle_window(player)
flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10} flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10}
flow3.tooltip = {'pirates.gui_crew_window_buttons_quit_crew_tooltip'} flow3.tooltip = {'pirates.gui_crew_window_buttons_quit_crew_tooltip'}
flow3 = -- Runs window already has a button to leave spectators.
flow2.add( -- flow3 =
{ -- flow2.add(
name = 'leave_spectators', -- {
type = 'button', -- name = 'crewmember_leave_spectators',
caption = {'pirates.gui_crew_window_buttons_quit_spectators'} -- type = 'button',
} -- caption = {'pirates.gui_crew_window_buttons_quit_spectators'}
) -- }
flow3.style.minimal_width = 95 -- )
flow3.style.font = 'default-bold' -- flow3.style.minimal_width = 95
flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10} -- flow3.style.font = 'default-bold'
-- flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10}
flow3 = flow3 =
flow2.add( flow2.add(
@ -138,18 +139,19 @@ function Public.toggle_window(player)
flow3.style.font = 'default-bold' flow3.style.font = 'default-bold'
flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10} flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10}
flow3 = -- Disabled spectators for now... might not play well with maze world
flow2.add( -- flow3 =
{ -- flow2.add(
name = 'crewmember_join_spectators', -- {
type = 'button', -- name = 'crewmember_join_spectators',
caption = {'pirates.gui_crew_window_buttons_join_spectators'} -- type = 'button',
} -- caption = {'pirates.gui_crew_window_buttons_join_spectators'}
) -- }
flow3.style.minimal_width = 95 -- )
flow3.style.font = 'default-bold' -- flow3.style.minimal_width = 95
flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10} -- flow3.style.font = 'default-bold'
flow3.tooltip = {'pirates.gui_crew_window_buttons_join_spectators_tooltip'} -- flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10}
-- flow3.tooltip = {'pirates.gui_crew_window_buttons_join_spectators_tooltip'}
--*** MEMBERS AND SPECTATORS ***-- --*** MEMBERS AND SPECTATORS ***--
@ -437,8 +439,7 @@ function Public.full_update(player)
flow.membership_buttons.leave_crew.visible = playercrew_status.adventuring 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 = 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.leave_spectators.visible = playercrew_status.spectating
flow.membership_buttons.spectator_join_crew.visible = 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)) 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))
@ -501,18 +502,18 @@ function Public.click(event)
local memory = Memory.get_crew_memory() local memory = Memory.get_crew_memory()
if eventname == 'crewmember_join_spectators' then -- if eventname == 'crewmember_join_spectators' then
Crew.join_spectators(player, memory.id) -- Crew.join_spectators(player, memory.id)
return -- return
end -- end
if eventname == 'leave_spectators' then -- if eventname == 'crewmember_leave_spectators' then
Crew.leave_spectators(player) -- Crew.leave_spectators(player)
return -- return
end -- end
if eventname == 'spectator_join_crew' then if eventname == 'spectator_join_crew' then
Crew.join_crew(player, memory.id) Crew.join_crew(player)
if memory.run_is_protected and (not Roles.captain_exists()) then if memory.run_is_protected and (not Roles.captain_exists()) then
Common.notify_player_expected(player, {'pirates.player_joins_protected_run_with_no_captain'}) Common.notify_player_expected(player, {'pirates.player_joins_protected_run_with_no_captain'})

View File

@ -619,7 +619,7 @@ function Public.process_etaframe_update(player, flow1, bools)
-- local caption -- local caption
if bools.atsea_loading_bool then if bools.atsea_loading_bool then
flow2.etaframe_label_3.caption = {'pirates.gui_etaframe_next_escape_cost'} flow2.etaframe_label_3.caption = {'pirates.gui_etaframe_next_escape_cost'}
if Balance.need_resources_to_undock(Common.overworldx()) then if Boats.need_resources_to_undock(Common.overworldx(), destination.subtype) then
if bools.cost_includes_rocket_launch_bool then if bools.cost_includes_rocket_launch_bool then
tooltip = {'pirates.resources_needed_tooltip_4_rocketvariant'} tooltip = {'pirates.resources_needed_tooltip_4_rocketvariant'}
else else
@ -649,7 +649,7 @@ function Public.process_etaframe_update(player, flow1, bools)
tooltip = {'pirates.resources_needed_tooltip_3'} tooltip = {'pirates.resources_needed_tooltip_3'}
end end
else -- Shown when at island else -- Shown when at island
if Balance.need_resources_to_undock(Common.overworldx()) == true then if Boats.need_resources_to_undock(Common.overworldx(), destination.subtype) then
flow2.etaframe_label_3.visible = false flow2.etaframe_label_3.visible = false
if bools.cost_includes_rocket_launch_bool then if bools.cost_includes_rocket_launch_bool then
@ -1220,7 +1220,7 @@ end
-- ATTENTION: Giving same names to GUI elements can cause issues, because click events are dispatched to all GUI windows!
local function on_gui_click(event) local function on_gui_click(event)
if not event then return end if not event then return end
if not event.element then return end if not event.element then return end

View File

@ -696,7 +696,11 @@ function Public.click(event)
if eventname == 'join_spectators' then if eventname == 'join_spectators' then
local listbox = flow.ongoing_runs.body.ongoing_runs_listbox local listbox = flow.ongoing_runs.body.ongoing_runs_listbox
-- It was observed that "listbox.get_item(listbox.selected_index)" can produce "Index out of range error"
-- This is to prevent that error.
if listbox.selected_index >= 1 and listbox.selected_index <= #listbox.items then
Crew.join_spectators(player, tonumber(listbox.get_item(listbox.selected_index)[2])) Crew.join_spectators(player, tonumber(listbox.get_item(listbox.selected_index)[2]))
end
return return
end end
@ -707,14 +711,19 @@ function Public.click(event)
if eventname == 'join_crew' then if eventname == 'join_crew' then
local listbox = flow.ongoing_runs.body.ongoing_runs_listbox local listbox = flow.ongoing_runs.body.ongoing_runs_listbox
-- It was observed that "listbox.get_item(listbox.selected_index)" can produce "Index out of range error"
-- This is to prevent that error.
if listbox.selected_index >= 1 and listbox.selected_index <= #listbox.items then
local crewid = tonumber(listbox.get_item(listbox.selected_index)[2]) local crewid = tonumber(listbox.get_item(listbox.selected_index)[2])
local memory = global_memory.crew_memories[crewid] Memory.set_working_id(crewid)
local memory = Memory.get_crew_memory()
-- If run is private -- If run is private
if global_memory.crew_memories[crewid].run_is_private then if memory.run_is_private then
if global_memory.crew_memories[crewid].private_run_password == flow.ongoing_runs.body.password_namefield.text then if memory.private_run_password == flow.ongoing_runs.body.password_namefield.text then
Crew.join_crew(player, crewid) Crew.join_crew(player)
flow.ongoing_runs.body.join_private_crew_info.visible = false flow.ongoing_runs.body.join_private_crew_info.visible = false
flow.ongoing_runs.body.password_namefield.visible = false flow.ongoing_runs.body.password_namefield.visible = false
@ -726,7 +735,7 @@ function Public.click(event)
Common.notify_player_error(player, {'pirates.gui_join_private_run_error_wrong_password'}) Common.notify_player_error(player, {'pirates.gui_join_private_run_error_wrong_password'})
end end
else else
Crew.join_crew(player, crewid) Crew.join_crew(player)
if memory.run_is_protected and (not Roles.captain_exists()) then if memory.run_is_protected and (not Roles.captain_exists()) then
Common.notify_player_expected(player, {'pirates.player_joins_protected_run_with_no_captain'}) Common.notify_player_expected(player, {'pirates.player_joins_protected_run_with_no_captain'})
@ -736,6 +745,7 @@ function Public.click(event)
return return
end end
end
if eventname == 'propose_crew' then if eventname == 'propose_crew' then
if #global_memory.crew_active_ids >= global_memory.active_crews_cap then if #global_memory.crew_active_ids >= global_memory.active_crews_cap then

View File

@ -45,7 +45,7 @@ Public.buried_treasure_loot_data_raw = {
Public.chest_loot_data_raw = { Public.chest_loot_data_raw = {
{80, 0, 1, false, 'coin', 400, 700}, {80, 0, 1, false, 'coin', 400, 700},
{30, 0, 1, false, 'rail-signal', 50, 50}, {10, 0, 1, false, 'rail-signal', 50, 50},
{1, 0.2, 1, false, 'electric-engine-unit', 1, 2}, {1, 0.2, 1, false, 'electric-engine-unit', 1, 2},
{3, 0, 1, false, 'small-lamp', 4, 16}, {3, 0, 1, false, 'small-lamp', 4, 16},
{10, 0, 1, false, 'coal', 60, 100}, {10, 0, 1, false, 'coal', 60, 100},
@ -412,7 +412,7 @@ Public.maze_treasure_data_raw = {
{2, 0, 1, false, 'uranium-rounds-magazine', 15, 25}, {2, 0, 1, false, 'uranium-rounds-magazine', 15, 25},
{2, 0, 1, false, 'artillery-shell', 5, 7}, {2, 0, 1, false, 'artillery-shell', 5, 7},
{2, 0, 1, false, 'rail-signal', 400, 400}, {2, 0, 1, false, 'rail-signal', 250, 250},
{2, 0, 1, false, 'electric-engine-unit', 3, 4}, {2, 0, 1, false, 'electric-engine-unit', 3, 4},
{2, 0, 1, false, 'cluster-grenade', 8, 12}, {2, 0, 1, false, 'cluster-grenade', 8, 12},

View File

@ -71,7 +71,7 @@ local Server = require 'utils.server'
local Math = require 'maps.pirates.math' local Math = require 'maps.pirates.math'
require 'utils.profiler' -- require 'utils.profiler'
local Public = {} local Public = {}

View File

@ -100,16 +100,64 @@ function Public.random_float_in_range(from, to)
end end
-- Returns vector in random direction. -- Returns vector in random direction.
-- scalar: sets returned vector length. If nil, 1 will be chosen -- scalar: returned vector length. If nil, 1 will be chosen.
function Public.random_vec(scalar) function Public.random_vec(scalar)
scalar = scalar or 1 scalar = scalar or 1
local random_angle = Public.random_float_in_range(0, 2 * Public.pi) local random_angle = Public.random_float_in_range(0, 2 * Public.pi)
return { return {
x = Public.sin(random_angle) * scalar, x = Public.cos(random_angle) * scalar,
y = Public.cos(random_angle) * scalar y = Public.sin(random_angle) * scalar
} }
end end
-- Returns vector in random direction in arc: [arc_offset, arc_offset + arc_size], starting at {x=1, y=1} and going anti-clockwise.
-- scalar: returned vector length. If nil, 1 will be chosen.
-- arc_offset: offset of arc in radians.
-- arc_size: size of arc in radians. Result is undefined with negative arc_size
function Public.random_vec_in_arc(scalar, arc_offset, arc_size)
scalar = scalar or 1
local random_angle = Public.random_float_in_range(arc_offset, arc_offset + arc_size)
return {
x = Public.cos(random_angle) * scalar,
y = Public.sin(random_angle) * scalar
}
end
-- Returns unique contiguous array indices.
-- array_size: size of contiguous array.
-- count: amount of unique indices to take from the array (elements are taken in range [1, array_size]).
--
-- Example with {array_size = 5, count = 3}:
-- - Possible return value: {4, 1, 5}
--
-- Idea taken from this big brained stranger https://stackoverflow.com/a/2380705
function Public.random_unique_array_indices(array_size, count)
if count > array_size then
return nil
end
local modified_indices = {}
local result_indices = {}
for i = 0, count - 1 do
local curr_array_size = array_size - i
local num = Public.random(curr_array_size)
if modified_indices[num] == nil then
result_indices[#result_indices + 1] = num
else
result_indices[#result_indices + 1] = modified_indices[num]
end
if modified_indices[curr_array_size] == nil then
modified_indices[num] = curr_array_size
else
modified_indices[num] = modified_indices[curr_array_size]
end
end
return result_indices
end
function Public.snap_to_grid(point) function Public.snap_to_grid(point)
return {x = Public.ceil(point.x), y = Public.ceil(point.x)} return {x = Public.ceil(point.x), y = Public.ceil(point.x)}
end end

View File

@ -86,8 +86,7 @@ function Public.initialise_crew_memory(id) --mostly serves as a dev reference of
memory.playerindex_captain = nil memory.playerindex_captain = nil
memory.captain_accrued_time_data = nil memory.captain_accrued_time_data = nil
memory.max_players_recorded = nil memory.max_players_recorded = nil
memory.temporarily_logged_off_characters = nil memory.temporarily_logged_off_player_data = nil
memory.temporarily_logged_off_characters_items = nil
memory.speed_boost_characters = nil memory.speed_boost_characters = nil
@ -125,7 +124,7 @@ function Public.fallthrough_crew_memory() --could make this a metatable, but met
return { return {
id = 0, id = 0,
difficulty = 1, difficulty = 1,
force_name = 'player', force_name = 'player', -- should match Common.lobby_force_name
boat = {}, boat = {},
destinations = {}, destinations = {},
spectatorplayerindices = {}, spectatorplayerindices = {},

View File

@ -291,7 +291,7 @@ function Public.generate_overworld_destination(p)
static_params.base_cost_to_undock = Public.generate_destination_base_cost_to_undock(p, subtype) -- Multiplication by Balance.cost_to_leave_multiplier() happens later, in destination_on_collide. static_params.base_cost_to_undock = Public.generate_destination_base_cost_to_undock(p, subtype) -- Multiplication by Balance.cost_to_leave_multiplier() happens later, in destination_on_collide.
static_params.undock_cost_decreases = true static_params.undock_cost_decreases = true
if Balance.need_resources_to_undock(p.x) == true or subtype == IslandEnum.enum.RADIOACTIVE then if Boats.need_resources_to_undock(p.x, subtype) then
static_params.undock_cost_decreases = false static_params.undock_cost_decreases = false
end end
@ -495,22 +495,15 @@ function Public.generate_overworld_destination(p)
-- end -- end
if position_candidates then if position_candidates then
local positions_placed = {} local random_indices = Math.random_unique_array_indices(#position_candidates, Math.min(kraken_count, 10))
local whilesafety = 0 if random_indices then
while whilesafety < 10 and (#positions_placed < Math.min(kraken_count, 10)) do for _, idx in ipairs(random_indices) do
local p_chosen, p_kraken local p_chosen = position_candidates[idx]
local whilesafety2 = 0 local p_kraken = Utils.psum{p_chosen, p}
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) Crowsnest.draw_kraken(p_kraken)
positions_placed[#positions_placed + 1] = p_kraken
memory.overworld_krakens[#memory.overworld_krakens + 1] = p_kraken memory.overworld_krakens[#memory.overworld_krakens + 1] = p_kraken
whilesafety = whilesafety + 1
end end
-- game.print(#positions_placed .. ' krakens placed for' .. macro_p.x .. ', ' .. macro_p.y) end
end end
end end
@ -577,16 +570,15 @@ end
function Public.check_for_kraken_collisions() function Public.check_for_kraken_collisions()
local memory = Memory.get_crew_memory() local memory = Memory.get_crew_memory()
local krakens = memory.overworld_krakens
for i, k in ipairs(krakens) do
for i = #memory.overworld_krakens, 1, -1 do
local k = memory.overworld_krakens[i]
local relativex = Crowsnest.platformrightmostedge + k.x - memory.overworldx local relativex = Crowsnest.platformrightmostedge + k.x - memory.overworldx
local relativey = k.y - memory.overworldy local relativey = k.y - memory.overworldy
if (relativex <= 3.5 and relativex >= -3.5 and relativey >= -4 and relativey <= 4) then if (relativex <= 3.5 and relativex >= -3.5 and relativey >= -4 and relativey <= 4) then
Kraken.try_spawn_kraken() Kraken.try_spawn_kraken()
memory.overworld_krakens = Utils.ordered_table_with_index_removed(krakens, i) memory.overworld_krakens = Utils.ordered_table_with_index_removed(memory.overworld_krakens, i)
end end
end end
end end

View File

@ -475,13 +475,7 @@ function Public.try_retreat_from_island(player, manual) -- Assumes the cost can
Common.notify_player_error(player, {'pirates.error_undock_too_early'}) Common.notify_player_error(player, {'pirates.error_undock_too_early'})
end end
else else
local cost = destination.static_params.base_cost_to_undock Common.consume_undock_cost_resources()
if cost then
local adjusted_cost = Common.time_adjusted_departure_cost(cost)
Common.spend_stored_resources(adjusted_cost)
end
Public.retreat_from_island(manual) Public.retreat_from_island(manual)
end end
else else

View File

@ -1534,4 +1534,13 @@ function Public.force_reconnect_boat_poles()
end end
end end
-- Returns true if uncollected resources will cause the crew to lose.
function Public.need_resources_to_undock(overworldx, island_subtype)
if overworldx >= Balance.need_resources_to_undock_x or island_subtype == IslandEnum.enum.RADIOACTIVE then
return true
else
return false
end
end
return Public return Public

View File

@ -393,17 +393,18 @@ function Public.tick_quest_structure_entry_price_check()
if removed > 0 then if removed > 0 then
local count = 1 local count = 1
for k, v in pairs(entry_price.batchRawMaterials) do for k, v in pairs(entry_price.batchRawMaterials) do
red_invs[count].insert({name = k, count = v * removed / entry_price.batchSize}); local item_count = v * removed / entry_price.batchSize
if item_count > 0 then
red_invs[count].insert({name = k, count = item_count});
count = count + 1 count = count + 1
else
log('Error (non-critical): item_count is not positive. v: ' .. v .. '; removed: ' .. removed .. '; entry_price.batchSize: ' .. entry_price.batchSize)
end
end end
end end
end end
end end
end end
return Public return Public

View File

@ -14,6 +14,7 @@ local Public = {}
local enum = { local enum = {
DEFAULT = 'Default', DEFAULT = 'Default',
SLOT_ARTILLERY_SHELLS = 1,
SLOT_EXTRA_HOLD = 5, SLOT_EXTRA_HOLD = 5,
SLOT_MORE_POWER = 6, SLOT_MORE_POWER = 6,
SLOT_RANDOM_CLASS = 7, SLOT_RANDOM_CLASS = 7,
@ -104,11 +105,12 @@ Public.market_price_scale = 300
Public.cabin_shop_data = { Public.cabin_shop_data = {
{ {
-- Note: coin price for this offer changes based on difficulty.
price = {{'coin', 4000}, {'coal', 20}, {'iron-plate', 20}},--should be inefficient on resources to merely buy arty to shoot nests price = {{'coin', 4000}, {'coal', 20}, {'iron-plate', 20}},--should be inefficient on resources to merely buy arty to shoot nests
offer = {type='give-item', item = 'artillery-shell', count = 5}, offer = {type='give-item', item = 'artillery-shell', count = 5},
}, },
{ {
price = {{'coin', 1000}, {'electronic-circuit', 20}}, price = {{'coin', 2000}, {'electronic-circuit', 50}},
offer = {type='give-item', item = 'rail-signal', count = 50}, offer = {type='give-item', item = 'rail-signal', count = 50},
}, },
{ {
@ -292,6 +294,8 @@ function Public.create_cabin_surface()
end end
end end
Public.update_captains_market_offers_based_on_difficulty(memory.difficulty_option)
rendering.draw_text( rendering.draw_text(
{ {
color = {60, 255, 124}, color = {60, 255, 124},
@ -383,4 +387,29 @@ function Public.get_market_random_price(slot)
return nil return nil
end end
function Public.get_captains_market()
local surface = game.surfaces[Public.get_cabin_surface_name()]
return surface.find_entity('market', Public.Data.market_position)
end
function Public.update_captains_market_offers_based_on_difficulty(difficulty_option)
local market = Public.get_captains_market()
if market == nil then return end
local offers = market.get_market_items()
market.clear_market_items()
for i, offer in pairs(offers) do
if i == enum.SLOT_ARTILLERY_SHELLS then
for _, price in pairs(offer.price) do
if price.name == "coin" then
price.amount = difficulty_option * 1000
end
end
end
market.add_market_item(offer)
end
end
return Public return Public

View File

@ -20,7 +20,7 @@ Public.Data = require 'maps.pirates.surfaces.islands.cave.data'
--lab-dark-1 > position has been copied --lab-dark-1 > position has been copied
--lab-dark-2 > position has been visited --lab-dark-2 > position has been visited
function Public.reveal(cave_miner, surface, source_surface, position, brushsize) function Public.reveal(surface, source_surface, position, brushsize)
local source_tile = source_surface.get_tile(position) local source_tile = source_surface.get_tile(position)
if not source_tile.valid then return end if not source_tile.valid then return end
if source_tile.name == 'lab-dark-2' then return end if source_tile.name == 'lab-dark-2' then return end
@ -66,7 +66,7 @@ function Public.reveal(cave_miner, surface, source_surface, position, brushsize)
for _, entity in pairs(source_surface.find_entities_filtered({area = {{position.x - brushsize, position.y - brushsize}, {position.x + brushsize, position.y + brushsize}}})) do for _, entity in pairs(source_surface.find_entities_filtered({area = {{position.x - brushsize, position.y - brushsize}, {position.x + brushsize, position.y + brushsize}}})) do
if entity.valid then if entity.valid then
local entity_position = entity.position local entity_position = {x = entity.position.x, y = entity.position.y}
if (position.x - entity_position.x) ^ 2 + (position.y - entity_position.y) ^ 2 < brushsize_square then if (position.x - entity_position.x) ^ 2 + (position.y - entity_position.y) ^ 2 < brushsize_square then
local e = entity.clone({position = entity_position, surface = surface}) local e = entity.clone({position = entity_position, surface = surface})
if e and e.valid then if e and e.valid then
@ -98,7 +98,7 @@ function Public.reveal(cave_miner, surface, source_surface, position, brushsize)
Public.try_make_spawner_elite(e, destination) Public.try_make_spawner_elite(e, destination)
Public.reveal(cave_miner, surface, source_surface, entity_position, 15) Public.reveal(surface, source_surface, entity_position, 15)
end end
end end
end end
@ -115,7 +115,7 @@ function Public.try_make_spawner_elite(spawner, destination)
if spawner and CoreData.get_difficulty_option_from_value(memory.difficulty) >= 3 then if spawner and CoreData.get_difficulty_option_from_value(memory.difficulty) >= 3 then
if Math.random(20) == 1 then if Math.random(20) == 1 then
local max_health = Balance.elite_spawner_health() local max_health = Balance.elite_spawner_health()
Common.new_healthbar(true, spawner, max_health, nil, max_health, 0.8, nil, destination.dynamic_data) Common.new_healthbar(true, spawner, max_health, nil, max_health, 0.8, nil)
local elite_spawners = destination.dynamic_data.elite_spawners local elite_spawners = destination.dynamic_data.elite_spawners
if elite_spawners then if elite_spawners then
@ -245,7 +245,7 @@ local function on_player_changed_position(event)
local cave_miner = destination_data.dynamic_data.cave_miner local cave_miner = destination_data.dynamic_data.cave_miner
-- TODO: make more reliable way to get island surface -- TODO: make more reliable way to get island surface
Public.reveal(cave_miner, player.surface, cave_miner.cave_surface, {x = Math.floor(player.position.x), y = Math.floor(player.position.y)}, 11) Public.reveal(player.surface, cave_miner.cave_surface, {x = Math.floor(player.position.x), y = Math.floor(player.position.y)}, 11)
end end
Event.add(defines.events.on_player_changed_position, on_player_changed_position) Event.add(defines.events.on_player_changed_position, on_player_changed_position)

View File

@ -376,7 +376,7 @@ function Public.spawn_enemy_boat(type)
boat.spawner = e boat.spawner = e
local max_health = Balance.biter_boat_health() local max_health = Balance.biter_boat_health()
Common.new_healthbar(true, e, max_health, nil, max_health, 0.5, nil, destination.dynamic_data) Common.new_healthbar(true, e, max_health, nil, max_health, 0.5, nil)
end end
return enemyboats[#enemyboats] return enemyboats[#enemyboats]

View File

@ -274,6 +274,9 @@ local function radioactive_tick()
local tickinterval = 60 local tickinterval = 60
if destination.subtype == IslandEnum.enum.RADIOACTIVE then if destination.subtype == IslandEnum.enum.RADIOACTIVE then
-- Stop increasing evo when boat left the island
local surface_name = memory.boat and memory.boat.surface_name
if surface_name ~= memory.sea_name then
-- faster evo (doesn't need difficulty scaling as higher difficulties have higher base evo): -- 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() local extra_evo = 0.22 * tickinterval/60 / Balance.expected_time_on_island()
Common.increment_evo(extra_evo) Common.increment_evo(extra_evo)
@ -304,6 +307,7 @@ local function radioactive_tick()
end end
end end
end end
end
local event = require 'utils.event' local event = require 'utils.event'

View File

@ -161,7 +161,7 @@ local function walkways_tick()
if destination.subtype == IslandEnum.enum.WALKWAYS then if destination.subtype == IslandEnum.enum.WALKWAYS then
for _, player in pairs(game.connected_players) do 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 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') player.character.damage(Balance.walkways_frozen_pool_damage, game.forces['environment'], 'fire')
if not (player.character and player.character.valid) then if not (player.character and player.character.valid) then
Common.notify_force(player.force, {'pirates.death_froze',player.name}) Common.notify_force(player.force, {'pirates.death_froze',player.name})
end end

View File

@ -488,7 +488,7 @@ function Public.destination_on_crewboat_hits_shore(destination)
local spawner = Common.get_random_valid_spawner(surface) local spawner = Common.get_random_valid_spawner(surface)
if spawner then if spawner then
local max_health = Balance.elite_spawner_health() local max_health = Balance.elite_spawner_health()
Common.new_healthbar(true, spawner, max_health, nil, max_health, 0.8, nil, destination.dynamic_data) Common.new_healthbar(true, spawner, max_health, nil, max_health, 0.8, nil)
local elite_spawners = destination.dynamic_data.elite_spawners local elite_spawners = destination.dynamic_data.elite_spawners
if elite_spawners then if elite_spawners then

View File

@ -11,7 +11,9 @@ Have fun and be comfy ^.^]]
local info_adv = local info_adv =
[[ [[
# Changelog (21th October 2022): # Changelog (10th Aug 2023):
- Fixed that the map now has a random seed upon reset
- Fixed automatic server reset
- Fixed player spawn issue - Fixed player spawn issue
- Fixed map random seed - Fixed map random seed
- Made tank and car extremely rare to get via scrap - Made tank and car extremely rare to get via scrap

View File

@ -143,7 +143,7 @@ function Public.initialize()
mgs.water = 0.5 mgs.water = 0.5
mgs.peaceful_mode = false mgs.peaceful_mode = false
mgs.starting_area = 'none' mgs.starting_area = 'none'
mgs.terrain_segmentation = 8 mgs.terrain_segmentation = 3
-- terrain size is 64 x 64 chunks, water size is 80 x 80 -- terrain size is 64 x 64 chunks, water size is 80 x 80
mgs.width = map_width mgs.width = map_width
mgs.height = map_height mgs.height = map_height
@ -158,10 +158,15 @@ function Public.initialize()
-- adjust this value to set how many nests spawn per tile -- adjust this value to set how many nests spawn per tile
['enemy-base-frequency'] = 0.4, ['enemy-base-frequency'] = 0.4,
-- this will make and average base radius around 12 tiles -- this will make and average base radius around 12 tiles
['enemy-base-radius'] = 12 ['enemy-base-radius'] = 12,
['control-setting:aux:bias'] = '-0.300000',
['control-setting:aux:frequency:multiplier'] = '1.333333',
['control-setting:moisture:bias'] = '-0.200000',
['control-setting:moisture:frequency:multiplier'] = '1.333333'
} }
mgs.seed = math_random(10000, 999999) mgs.seed = math_random(100000, 9999999)
log(serpent.block(mgs))
if not this.active_surface_index then if not this.active_surface_index then
this.active_surface_index = game.create_surface('towny', mgs).index this.active_surface_index = game.create_surface('towny', mgs).index
else else
@ -179,6 +184,7 @@ function Public.initialize()
surface.peaceful_mode = false surface.peaceful_mode = false
surface.always_day = false surface.always_day = false
surface.freeze_daytime = false surface.freeze_daytime = false
surface.map_gen_settings.water = 0.5
surface.clear(true) surface.clear(true)
surface.regenerate_entity({'rock-huge', 'rock-big', 'sand-rock-big'}) surface.regenerate_entity({'rock-huge', 'rock-big', 'sand-rock-big'})
surface.regenerate_decorative() surface.regenerate_decorative()

View File

@ -994,12 +994,9 @@ local function setup_enemy_force()
end end
local function reset_forces() local function reset_forces()
local players = game.players
local forces = game.forces local forces = game.forces
for i = 1, #players do for name, force in pairs(forces) do
local player = players[i] if name ~= 'rogue' and name ~= 'player' and name ~= 'enemy' and name ~= 'neutral' then
local force = forces[player.name]
if force then
game.merge_forces(force, 'player') game.merge_forces(force, 'player')
end end
end end

7664
tmp.lua

File diff suppressed because it is too large Load Diff

View File

@ -142,7 +142,7 @@ function Public.save_logistics(player)
local slots = {} local slots = {}
for i = 1, 100 do for i = 1, 400 do
local slot = player.get_personal_logistic_slot(i) local slot = player.get_personal_logistic_slot(i)
if slot and slot.name then if slot and slot.name then
slots[i] = {name = slot.name, min = slot.min, max = slot.max} slots[i] = {name = slot.name, min = slot.min, max = slot.max}
@ -220,6 +220,7 @@ commands.add_command(
return return
end end
local success, _ = pcall(save_logistics, player) local success, _ = pcall(save_logistics, player)
player.print('Notice: only the first 400 slots are saved.', Color.warning)
if not success then if not success then
player.print('An error occured while trying to save your logistics slots.', Color.warning) player.print('An error occured while trying to save your logistics slots.', Color.warning)
end end