mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2025-01-04 00:15:45 +02:00
Merge branch 'develop' into develop
This commit is contained in:
commit
c16265fb8c
@ -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_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_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_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_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_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_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_merchant=Merchant
|
||||
# class_merchant_explanation=They generate 50 doubloons per league.
|
||||
class_merchant_explanation_advanced=They generate 50 doubloons per league.
|
||||
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_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_prospector=Prospector
|
||||
# class_prospector_explanation=They find more resources when handmining.
|
||||
class_prospector_explanation_advanced=They find more resources when handmining.
|
||||
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_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_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_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_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_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_quartermaster=Quartermaster
|
||||
# class_quartermaster_explanation=Nearby crewmates get +10% physical attack and generate ore.
|
||||
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_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_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_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_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_chef=Chef
|
||||
class_chef_explanation_advanced=They cook meat of defeated enemies and turn it into something tasty.
|
||||
|
@ -133,6 +133,8 @@ function Public.wave_size_rng() -- random variance in attack sizes
|
||||
local memory = Memory.get_crew_memory()
|
||||
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)
|
||||
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
|
||||
@ -144,17 +146,18 @@ function Public.wave_size_rng() -- random variance in attack sizes
|
||||
|
||||
local wave_size_multiplier = 1
|
||||
local rng1 = Math.random(100)
|
||||
|
||||
if rng1 > wave_percentage_chance then
|
||||
wave_size_multiplier = 0
|
||||
elseif memory.overworldx > 0 then
|
||||
local rng2 = Math.random(1000)
|
||||
if rng2 <= 890 then
|
||||
if rng2 >= 110 * rng_scale then
|
||||
wave_size_multiplier = 1
|
||||
elseif rng2 <= 970 then
|
||||
elseif rng2 >= 30 * rng_scale then
|
||||
wave_size_multiplier = 1.5
|
||||
elseif rng2 <= 985 then
|
||||
elseif rng2 >= 15 * rng_scale then
|
||||
wave_size_multiplier = 2
|
||||
elseif rng2 <= 995 then
|
||||
elseif rng2 >= 5 * rng_scale then
|
||||
wave_size_multiplier = 3
|
||||
else
|
||||
wave_size_multiplier = 4
|
||||
@ -902,7 +905,39 @@ function Public.try_boat_biters_attack()
|
||||
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()
|
||||
@ -921,5 +956,8 @@ end
|
||||
--=== Data
|
||||
|
||||
|
||||
local event = require 'utils.event'
|
||||
event.add(defines.events.on_entity_destroyed, on_entity_destroyed)
|
||||
|
||||
|
||||
return Public
|
||||
|
@ -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 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
|
||||
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
|
||||
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
|
||||
elseif event.entity.name == 'pipe' then
|
||||
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
|
||||
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
|
||||
event.entity.health = event.entity.health - event.final_damage_amount * 0.5
|
||||
event.final_damage_amount = event.final_damage_amount * 1.5
|
||||
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 and eb.state == Structures.Boats.enum_state.APPROACHING then
|
||||
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
|
||||
event.entity.die()
|
||||
@ -228,7 +234,7 @@ local function damage_to_elite_spawners(event)
|
||||
if spawner and spawner.valid and event.entity == spawner then
|
||||
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
|
||||
event.entity.die()
|
||||
@ -253,7 +259,6 @@ local function damage_to_elite_biters(event)
|
||||
local remaining_health = Common.entity_damage_healthbar(event.entity, damage)
|
||||
|
||||
if remaining_health and remaining_health <= 0 then
|
||||
memory.elite_biters[event.entity.unit_number] = nil
|
||||
event.entity.die()
|
||||
end
|
||||
end
|
||||
@ -306,29 +311,27 @@ local function damage_to_krakens(event)
|
||||
if not (surface_name == memory.sea_name) then return end
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
adjusted_damage = adjusted_damage / 1.25
|
||||
event.final_damage_amount = event.final_damage_amount / 1.25
|
||||
end
|
||||
-- and additionally:
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
-- There should be a better way to do it than this...
|
||||
if memory.healthbars and memory.healthbars[unit_number] then
|
||||
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
|
||||
Kraken.kraken_die(kraken_id)
|
||||
end
|
||||
@ -451,7 +454,6 @@ local function other_enemy_damage_bonuses(event)
|
||||
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 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.
|
||||
-- @Future reference: when implementing damage changes for mobs with healthbar, make this check with healthbar health too
|
||||
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
|
||||
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
|
||||
local samurai = class == Classes.enum.SAMURAI
|
||||
local hatamoto = class == Classes.enum.HATAMOTO
|
||||
@ -496,20 +528,28 @@ local function damage_dealt_by_players_changes(event)
|
||||
elseif hatamoto then
|
||||
extra_damage_to_deal = Balance.hatamoto_damage_dealt_with_melee * extra_physical_damage_from_research_multiplier
|
||||
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
|
||||
end
|
||||
else
|
||||
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
|
||||
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
|
||||
|
||||
-- @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 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
|
||||
local surplus = (extra_damage_to_deal - event.entity.health)*0.8
|
||||
event.entity.die(character.force, character)
|
||||
@ -520,30 +560,6 @@ local function damage_dealt_by_players_changes(event)
|
||||
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
|
||||
|
||||
@ -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
|
||||
|
||||
local damage = event.final_damage_amount
|
||||
event.entity.health = event.entity.health + damage
|
||||
event.entity.health = event.entity.health + event.final_damage_amount
|
||||
event.final_damage_amount = 0
|
||||
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
|
||||
|
||||
local damage = event.final_damage_amount
|
||||
|
||||
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
|
||||
-- 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
|
||||
if e2.valid then e2.destroy() end
|
||||
end
|
||||
else
|
||||
if string.sub(event.cause.force.name, 1, 4) == 'crew' then --player damage only
|
||||
event.entity.health = event.entity.health + damage * 0.9
|
||||
if event.cause and event.cause.valid then
|
||||
if string.sub(event.cause.force.name, 1, 4) == 'crew' then --player damage only
|
||||
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
|
||||
@ -627,6 +645,9 @@ 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.
|
||||
-- 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)
|
||||
@ -1314,13 +1335,48 @@ 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()
|
||||
else
|
||||
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.dynamic_data.quest_type == Quest.enum.WORMS and entity.type == 'turret' then
|
||||
destination.dynamic_data.quest_progress = destination.dynamic_data.quest_progress + 1
|
||||
Quest.try_resolve_quest()
|
||||
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
|
||||
destination.dynamic_data.quest_progress = destination.dynamic_data.quest_progress + 1
|
||||
Quest.try_resolve_quest()
|
||||
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)
|
||||
@ -1435,22 +1491,26 @@ local function event_on_player_joined_game(event)
|
||||
|
||||
local crew_to_put_back_in = nil
|
||||
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
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
Roles.make_captain(player)
|
||||
end
|
||||
|
||||
if _DEBUG then log('putting player back in their old crew') end
|
||||
else
|
||||
log('INFO: ' .. player.name .. ' (crew ID: NONE) joined the game')
|
||||
if player.character and player.character.valid then
|
||||
player.character.destroy()
|
||||
end
|
||||
@ -1473,6 +1533,8 @@ local function event_on_player_joined_game(event)
|
||||
|
||||
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.
|
||||
|
||||
-- Auto-join the oldest crew:
|
||||
@ -1503,7 +1565,7 @@ local function event_on_player_joined_game(event)
|
||||
-- end
|
||||
-- )
|
||||
-- if ages[1] then
|
||||
-- Crew.join_crew(player, ages[1].id)
|
||||
-- Crew.join_crew(player)
|
||||
|
||||
-- local memory = global_memory.crew_memories[ages[1].id]
|
||||
-- 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()
|
||||
-- figure out which crew this is about:
|
||||
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
|
||||
if proposal and proposal.endorserindices then
|
||||
@ -2080,7 +2147,7 @@ local function event_on_console_chat(event)
|
||||
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!
|
||||
if player.force.name == 'player' then
|
||||
if player.force.name == Common.lobby_force_name then
|
||||
local other_force_indices = global_memory.crew_active_ids
|
||||
|
||||
for _, index in pairs(other_force_indices) do
|
||||
|
@ -262,33 +262,35 @@ function Public.prune_offline_characters_list(tickinterval)
|
||||
|
||||
if memory.game_lost then return end
|
||||
|
||||
for player_index, tick in pairs(memory.temporarily_logged_off_characters) do
|
||||
if player_index and game.players[player_index] and game.players[player_index].connected then
|
||||
memory.temporarily_logged_off_characters[player_index] = nil
|
||||
if memory.temporarily_logged_off_characters_items[player_index] then
|
||||
memory.temporarily_logged_off_characters_items[player_index].destroy()
|
||||
end
|
||||
memory.temporarily_logged_off_characters_items[player_index] = nil
|
||||
for player_index, data in pairs(memory.temporarily_logged_off_player_data) do
|
||||
if game.players[player_index] and game.players[player_index].connected then
|
||||
-- memory.temporarily_logged_off_characters[player_index] = nil
|
||||
-- if memory.temporarily_logged_off_characters_items[player_index] then
|
||||
-- memory.temporarily_logged_off_characters_items[player_index].destroy()
|
||||
-- end
|
||||
-- memory.temporarily_logged_off_characters_items[player_index] = nil
|
||||
memory.temporarily_logged_off_player_data[player_index] = nil
|
||||
else
|
||||
if player_index and tick < game.tick - 60 * 60 * Common.logged_off_items_preserved_minutes then
|
||||
local any = false
|
||||
local temp_inv = memory.temporarily_logged_off_characters_items[player_index]
|
||||
if temp_inv then
|
||||
for i = 1, #temp_inv, 1 do
|
||||
if temp_inv[i] and temp_inv[i].valid and temp_inv[i].valid_for_read then
|
||||
Common.give_items_to_crew(temp_inv[i])
|
||||
any = true
|
||||
end
|
||||
end
|
||||
if any then
|
||||
Common.notify_force_light(memory.force, {'pirates.recover_offline_player_items'})
|
||||
end
|
||||
local tick = data.tick
|
||||
if tick < game.tick - 60 * 60 * Common.temporarily_logged_off_player_data_preservation_minutes then
|
||||
-- local any = false
|
||||
-- local temp_inv = memory.temporarily_logged_off_characters_items[player_index]
|
||||
-- if temp_inv then
|
||||
-- for i = 1, #temp_inv, 1 do
|
||||
-- if temp_inv[i] and temp_inv[i].valid and temp_inv[i].valid_for_read then
|
||||
-- Common.give_items_to_crew(temp_inv[i])
|
||||
-- any = true
|
||||
-- end
|
||||
-- end
|
||||
|
||||
temp_inv.destroy()
|
||||
end
|
||||
-- if any then
|
||||
-- Common.notify_force_light(memory.force, {'pirates.recover_offline_player_items'})
|
||||
-- end
|
||||
|
||||
memory.temporarily_logged_off_characters[player_index] = nil
|
||||
memory.temporarily_logged_off_characters_items[player_index] = nil
|
||||
-- temp_inv.destroy()
|
||||
-- end
|
||||
|
||||
memory.temporarily_logged_off_player_data[player_index] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1706,19 +1708,14 @@ function Public.update_time_remaining()
|
||||
if memory.boat.state == Boats.enum_state.RETREATING 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)
|
||||
local type = surface_name_decoded.type
|
||||
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
|
||||
if destination.type == Surfaces.enum.ISLAND then
|
||||
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
|
||||
Crew.try_lose({'pirates.loss_resources_were_not_collected_in_time'})
|
||||
else
|
||||
if Balance.need_resources_to_undock(Common.overworldx()) == true then
|
||||
Progression.try_retreat_from_island(false)
|
||||
else
|
||||
Progression.retreat_from_island(false)
|
||||
end
|
||||
Common.consume_undock_cost_resources()
|
||||
Progression.retreat_from_island(false)
|
||||
end
|
||||
elseif type == Surfaces.enum.DOCK then
|
||||
elseif destination.type == Surfaces.enum.DOCK then
|
||||
Progression.undock_from_dock(false)
|
||||
end
|
||||
end
|
||||
|
@ -37,12 +37,12 @@ Public.class_reward_tick_rate_in_seconds = 7
|
||||
Public.poison_damage_multiplier = 1.85
|
||||
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_with_melee = 25
|
||||
Public.hatamoto_damage_taken_multiplier = 0.21
|
||||
Public.samurai_damage_dealt_with_melee = 30
|
||||
Public.hatamoto_damage_taken_multiplier = 0.3
|
||||
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_iron_ore_required = 3000
|
||||
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.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()
|
||||
-- return 3 * Math.sloped(Common.capacity_scale(), 1/2) / 2 --/2 as we have 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()^(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)
|
||||
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
|
||||
-- ) * 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())
|
||||
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
|
||||
function Public.max_time_on_island(island_subtype)
|
||||
local x = Common.overworldx()
|
||||
@ -518,6 +513,9 @@ Public.starting_fuel = 4000
|
||||
Public.silo_max_hp = 5000
|
||||
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
|
||||
|
||||
Public.kraken_static_evo = 0.35
|
||||
@ -579,7 +577,7 @@ function Public.krakens_per_free_slot(overworldx)
|
||||
if rng < 0.0025 * multiplier then
|
||||
return 3
|
||||
elseif rng < 0.075 * multiplier then
|
||||
return 1
|
||||
return 2
|
||||
elseif rng < 0.5 * multiplier then
|
||||
return 1
|
||||
else
|
||||
|
@ -36,6 +36,8 @@ local Classes = require 'maps.pirates.roles.classes'
|
||||
|
||||
local Gui = require 'maps.pirates.gui.gui'
|
||||
|
||||
-- local Session = require 'utils.datastore.session_data'
|
||||
|
||||
|
||||
|
||||
local function cmd_set_memory(cmd)
|
||||
@ -46,7 +48,6 @@ end
|
||||
|
||||
|
||||
local function check_admin(cmd)
|
||||
local Session = require 'utils.datastore.session_data'
|
||||
local player = game.players[cmd.player_index]
|
||||
--local trusted = Session.get_trusted_table()
|
||||
local p
|
||||
@ -415,6 +416,7 @@ function(cmd)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Try undock from an island or dock
|
||||
commands.add_command(
|
||||
'undock',
|
||||
{'pirates.cmd_explain_undock'},
|
||||
@ -435,6 +437,27 @@ function(cmd)
|
||||
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(
|
||||
'tax',
|
||||
{'pirates.cmd_explain_tax'},
|
||||
@ -530,20 +553,6 @@ function(cmd)
|
||||
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)
|
||||
commands.add_command(
|
||||
'jump',
|
||||
|
@ -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_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.lobby_force_name = 'player'
|
||||
|
||||
-- Public.mainshop_rate_limit_ticks = 11
|
||||
|
||||
|
||||
@ -138,7 +140,7 @@ end
|
||||
|
||||
function Public.notify_lobby(message, color_override)
|
||||
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
|
||||
|
||||
function Public.notify_force(force, message, color_override)
|
||||
@ -688,6 +690,16 @@ function Public.spend_stored_resources(to_spend)
|
||||
Public.update_boat_stored_resources()
|
||||
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)
|
||||
health = health or max_health
|
||||
@ -773,11 +785,11 @@ end
|
||||
function Public.entity_damage_healthbar(entity, damage, location_override)
|
||||
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 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
|
||||
healthbar.health = new_health
|
||||
@ -787,11 +799,17 @@ function Public.entity_damage_healthbar(entity, damage, location_override)
|
||||
entity.health = entity.prototype.max_health
|
||||
end
|
||||
|
||||
if healthbar.health > healthbar.max_health then
|
||||
healthbar.health = healthbar.max_health
|
||||
end
|
||||
|
||||
local final_health = healthbar.health
|
||||
|
||||
if healthbar.health <= 0 then
|
||||
location_override.healthbars[unit_number] = nil
|
||||
end
|
||||
|
||||
return healthbar.health
|
||||
return final_health
|
||||
end
|
||||
|
||||
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 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
|
||||
|
||||
@ -1858,7 +1876,7 @@ function Public.try_make_biter_elite(entity)
|
||||
|
||||
local health_multiplier
|
||||
|
||||
if difficulty_index == 3 then
|
||||
if difficulty_index <= 3 then
|
||||
health_multiplier = 5
|
||||
else
|
||||
health_multiplier = 10
|
||||
@ -1881,4 +1899,26 @@ function Public.try_make_biter_elite(entity)
|
||||
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
|
@ -7,7 +7,7 @@ local _inspect = require 'utils.inspect'.inspect
|
||||
local Public = {}
|
||||
|
||||
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_importing_allowed = true
|
||||
|
@ -95,6 +95,8 @@ function Public.update_difficulty()
|
||||
|
||||
memory.difficulty_option = modal_id
|
||||
memory.difficulty = CoreData.difficulty_options[modal_id].value
|
||||
|
||||
Cabin.update_captains_market_offers_based_on_difficulty(memory.difficulty_option)
|
||||
end
|
||||
end
|
||||
|
||||
@ -266,7 +268,7 @@ function Public.join_spectators(player, crewid)
|
||||
player.set_controller{type = defines.controllers.spectator}
|
||||
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)
|
||||
|
||||
@ -331,14 +333,11 @@ function Public.leave_spectators(player, quiet)
|
||||
if _DEBUG then memory.crew_disband_tick = game.tick + 30*60*60 end
|
||||
end
|
||||
|
||||
player.force = 'player'
|
||||
player.force = Common.lobby_force_name
|
||||
end
|
||||
|
||||
|
||||
function Public.join_crew(player, crewid, rejoin)
|
||||
if not crewid then return end
|
||||
|
||||
Memory.set_working_id(crewid)
|
||||
function Public.join_crew(player, rejoin)
|
||||
local memory = Memory.get_crew_memory()
|
||||
|
||||
if not Common.validate_player(player) then return end
|
||||
@ -367,7 +366,7 @@ function Public.join_crew(player, crewid, rejoin)
|
||||
if spectating then
|
||||
local chars = player.get_associated_characters()
|
||||
for _, char in pairs(chars) do
|
||||
char.destroy()
|
||||
char.destroy()
|
||||
end
|
||||
|
||||
player.teleport(surface.find_non_colliding_position('character', memory.spawnpoint, 32, 0.5) or memory.spawnpoint, surface)
|
||||
@ -377,22 +376,51 @@ function Public.join_crew(player, crewid, rejoin)
|
||||
|
||||
memory.spectatorplayerindices = Utils.ordered_table_with_values_removed(memory.spectatorplayerindices, player.index)
|
||||
else
|
||||
if not (player.character and player.character.valid) then
|
||||
player.set_controller{type = defines.controllers.god}
|
||||
player.create_character()
|
||||
end
|
||||
|
||||
Public.player_abandon_endorsements(player)
|
||||
player.force = memory.force
|
||||
player.teleport(surface.find_non_colliding_position('character', memory.spawnpoint, 32, 0.5) or memory.spawnpoint, surface)
|
||||
|
||||
Common.notify_lobby({'pirates.lobby_to_crew_2', player.name, memory.name})
|
||||
end
|
||||
|
||||
Common.give_back_items_to_temporarily_logged_off_player(player)
|
||||
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})
|
||||
-- Server.to_discord_embed_raw(CoreData.comfy_emojis.yum1 .. '[' .. memory.name .. '] ' .. message)
|
||||
|
||||
memory.crewplayerindices[#memory.crewplayerindices + 1] = player.index
|
||||
|
||||
-- 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
|
||||
-- don't give them items if they've been in the crew recently
|
||||
-- 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
|
||||
player.insert({name = item, count = amount})
|
||||
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)
|
||||
-- end
|
||||
|
||||
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
|
||||
|
||||
-- @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 boat_surface_type = SurfacesCommon.decode_surface_name(memory.boat.surface_name).type
|
||||
-- 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
|
||||
|
||||
if player_surface_type == Surfaces.enum.ISLAND and boat_surface_type == Surfaces.enum.SEA then
|
||||
save_items = false
|
||||
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
|
||||
|
||||
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 save_items then
|
||||
Common.temporarily_store_logged_off_character_items(player)
|
||||
end
|
||||
memory.temporarily_logged_off_characters[player.index] = game.tick
|
||||
end
|
||||
char.die(memory.force_name)
|
||||
|
||||
-- else
|
||||
-- if not quiet then
|
||||
@ -475,7 +518,7 @@ function Public.leave_crew(player, to_lobby, quiet)
|
||||
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.force = 'player'
|
||||
player.force = Common.lobby_force_name
|
||||
player.create_character()
|
||||
Event.raise(BottomFrame.events.bottom_quickbar_respawn_raise, {player_index = player.index})
|
||||
end
|
||||
@ -554,7 +597,7 @@ function Public.disband_crew(donotprint)
|
||||
|
||||
for _,player in pairs(players) do
|
||||
if player.controller_type == defines.controllers.editor then player.toggle_map_editor() end
|
||||
player.force = 'player'
|
||||
player.force = Common.lobby_force_name
|
||||
end
|
||||
|
||||
if (not donotprint) then
|
||||
@ -740,12 +783,14 @@ function Public.initialise_crew(accepted_proposal)
|
||||
memory.spectatorplayerindices = {}
|
||||
memory.tempbanned_from_joining_data = {}
|
||||
memory.destinations = {}
|
||||
memory.temporarily_logged_off_characters = {}
|
||||
memory.temporarily_logged_off_characters_items = {}
|
||||
-- memory.temporarily_logged_off_characters = {}
|
||||
-- memory.temporarily_logged_off_characters_items = {}
|
||||
memory.temporarily_logged_off_player_data = {}
|
||||
memory.class_renderings = {}
|
||||
memory.class_auxiliary_data = {}
|
||||
|
||||
memory.elite_biters = {}
|
||||
memory.elite_biters_stream_registrations = {}
|
||||
memory.pet_biters = {}
|
||||
|
||||
memory.hold_surface_count = 1
|
||||
@ -786,8 +831,8 @@ function Public.initialise_crew(accepted_proposal)
|
||||
|
||||
memory.officers_table = {}
|
||||
|
||||
memory.classes_table = {} -- stores all unlocked untaken classes
|
||||
memory.spare_classes = {} -- stores all unlocked taken classes
|
||||
memory.classes_table = {} -- 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.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
|
||||
@ -875,8 +920,8 @@ function Public.reset_crew_and_enemy_force(id)
|
||||
|
||||
|
||||
|
||||
crew_force.set_friend('player', true)
|
||||
game.forces['player'].set_friend(crew_force, true)
|
||||
crew_force.set_friend(Common.lobby_force_name, true)
|
||||
game.forces[Common.lobby_force_name].set_friend(crew_force, true)
|
||||
crew_force.set_friend(ancient_friendly_force, true)
|
||||
ancient_friendly_force.set_friend(crew_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)
|
||||
end
|
||||
|
||||
crew_force.technologies['kovarex-enrichment-process'].researched = true -- needed for radioactive island
|
||||
-- crew_force.technologies['circuit-network'].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['electric-energy-distribution-1'].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['destroyer'].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['research-speed-1'].enabled = false
|
||||
|
@ -114,17 +114,18 @@ function Public.toggle_window(player)
|
||||
flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10}
|
||||
flow3.tooltip = {'pirates.gui_crew_window_buttons_quit_crew_tooltip'}
|
||||
|
||||
flow3 =
|
||||
flow2.add(
|
||||
{
|
||||
name = 'leave_spectators',
|
||||
type = 'button',
|
||||
caption = {'pirates.gui_crew_window_buttons_quit_spectators'}
|
||||
}
|
||||
)
|
||||
flow3.style.minimal_width = 95
|
||||
flow3.style.font = 'default-bold'
|
||||
flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10}
|
||||
-- Runs window already has a button to leave spectators.
|
||||
-- flow3 =
|
||||
-- flow2.add(
|
||||
-- {
|
||||
-- name = 'crewmember_leave_spectators',
|
||||
-- type = 'button',
|
||||
-- caption = {'pirates.gui_crew_window_buttons_quit_spectators'}
|
||||
-- }
|
||||
-- )
|
||||
-- flow3.style.minimal_width = 95
|
||||
-- flow3.style.font = 'default-bold'
|
||||
-- flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10}
|
||||
|
||||
flow3 =
|
||||
flow2.add(
|
||||
@ -138,18 +139,19 @@ function Public.toggle_window(player)
|
||||
flow3.style.font = 'default-bold'
|
||||
flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10}
|
||||
|
||||
flow3 =
|
||||
flow2.add(
|
||||
{
|
||||
name = 'crewmember_join_spectators',
|
||||
type = 'button',
|
||||
caption = {'pirates.gui_crew_window_buttons_join_spectators'}
|
||||
}
|
||||
)
|
||||
flow3.style.minimal_width = 95
|
||||
flow3.style.font = 'default-bold'
|
||||
flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10}
|
||||
flow3.tooltip = {'pirates.gui_crew_window_buttons_join_spectators_tooltip'}
|
||||
-- Disabled spectators for now... might not play well with maze world
|
||||
-- flow3 =
|
||||
-- flow2.add(
|
||||
-- {
|
||||
-- name = 'crewmember_join_spectators',
|
||||
-- type = 'button',
|
||||
-- caption = {'pirates.gui_crew_window_buttons_join_spectators'}
|
||||
-- }
|
||||
-- )
|
||||
-- flow3.style.minimal_width = 95
|
||||
-- flow3.style.font = 'default-bold'
|
||||
-- flow3.style.font_color = {r = 0.10, g = 0.10, b = 0.10}
|
||||
-- flow3.tooltip = {'pirates.gui_crew_window_buttons_join_spectators_tooltip'}
|
||||
|
||||
--*** MEMBERS AND SPECTATORS ***--
|
||||
|
||||
@ -437,8 +439,7 @@ function Public.full_update(player)
|
||||
|
||||
flow.membership_buttons.leave_crew.visible = playercrew_status.adventuring
|
||||
-- flow.membership_buttons.crewmember_join_spectators.visible = playercrew_status.adventuring
|
||||
flow.membership_buttons.crewmember_join_spectators.visible = false --disabled spectators for now... might not play well with maze world
|
||||
flow.membership_buttons.leave_spectators.visible = playercrew_status.spectating
|
||||
-- flow.membership_buttons.leave_spectators.visible = playercrew_status.spectating
|
||||
|
||||
flow.membership_buttons.spectator_join_crew.visible =
|
||||
flow.membership_buttons.spectator_join_crew.visible and (not (memory.tempbanned_from_joining_data[player.index] and game.tick < memory.tempbanned_from_joining_data[player.index] + Common.ban_from_rejoining_crew_ticks))
|
||||
@ -501,18 +502,18 @@ function Public.click(event)
|
||||
|
||||
local memory = Memory.get_crew_memory()
|
||||
|
||||
if eventname == 'crewmember_join_spectators' then
|
||||
Crew.join_spectators(player, memory.id)
|
||||
return
|
||||
end
|
||||
-- if eventname == 'crewmember_join_spectators' then
|
||||
-- Crew.join_spectators(player, memory.id)
|
||||
-- return
|
||||
-- end
|
||||
|
||||
if eventname == 'leave_spectators' then
|
||||
Crew.leave_spectators(player)
|
||||
return
|
||||
end
|
||||
-- if eventname == 'crewmember_leave_spectators' then
|
||||
-- Crew.leave_spectators(player)
|
||||
-- return
|
||||
-- end
|
||||
|
||||
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
|
||||
Common.notify_player_expected(player, {'pirates.player_joins_protected_run_with_no_captain'})
|
||||
|
@ -619,7 +619,7 @@ function Public.process_etaframe_update(player, flow1, bools)
|
||||
-- local caption
|
||||
if bools.atsea_loading_bool then
|
||||
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
|
||||
tooltip = {'pirates.resources_needed_tooltip_4_rocketvariant'}
|
||||
else
|
||||
@ -649,7 +649,7 @@ function Public.process_etaframe_update(player, flow1, bools)
|
||||
tooltip = {'pirates.resources_needed_tooltip_3'}
|
||||
end
|
||||
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
|
||||
|
||||
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)
|
||||
if not event then return end
|
||||
if not event.element then return end
|
||||
|
@ -696,7 +696,11 @@ function Public.click(event)
|
||||
if eventname == 'join_spectators' then
|
||||
local listbox = flow.ongoing_runs.body.ongoing_runs_listbox
|
||||
|
||||
Crew.join_spectators(player, tonumber(listbox.get_item(listbox.selected_index)[2]))
|
||||
-- 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]))
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
@ -707,34 +711,40 @@ function Public.click(event)
|
||||
|
||||
if eventname == 'join_crew' then
|
||||
local listbox = flow.ongoing_runs.body.ongoing_runs_listbox
|
||||
local crewid = tonumber(listbox.get_item(listbox.selected_index)[2])
|
||||
|
||||
local memory = global_memory.crew_memories[crewid]
|
||||
-- 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])
|
||||
|
||||
-- If run is private
|
||||
if global_memory.crew_memories[crewid].run_is_private then
|
||||
if global_memory.crew_memories[crewid].private_run_password == flow.ongoing_runs.body.password_namefield.text then
|
||||
Crew.join_crew(player, crewid)
|
||||
flow.ongoing_runs.body.join_private_crew_info.visible = false
|
||||
flow.ongoing_runs.body.password_namefield.visible = false
|
||||
Memory.set_working_id(crewid)
|
||||
local memory = Memory.get_crew_memory()
|
||||
|
||||
-- If run is private
|
||||
if memory.run_is_private then
|
||||
if memory.private_run_password == flow.ongoing_runs.body.password_namefield.text then
|
||||
Crew.join_crew(player)
|
||||
flow.ongoing_runs.body.join_private_crew_info.visible = false
|
||||
flow.ongoing_runs.body.password_namefield.visible = false
|
||||
|
||||
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.create_new_crew_tip'})
|
||||
end
|
||||
else
|
||||
Common.notify_player_error(player, {'pirates.gui_join_private_run_error_wrong_password'})
|
||||
end
|
||||
else
|
||||
Crew.join_crew(player)
|
||||
|
||||
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.create_new_crew_tip'})
|
||||
end
|
||||
else
|
||||
Common.notify_player_error(player, {'pirates.gui_join_private_run_error_wrong_password'})
|
||||
end
|
||||
else
|
||||
Crew.join_crew(player, crewid)
|
||||
|
||||
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.create_new_crew_tip'})
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if eventname == 'propose_crew' then
|
||||
|
@ -45,7 +45,7 @@ Public.buried_treasure_loot_data_raw = {
|
||||
|
||||
Public.chest_loot_data_raw = {
|
||||
{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},
|
||||
{3, 0, 1, false, 'small-lamp', 4, 16},
|
||||
{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, '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, 'cluster-grenade', 8, 12},
|
||||
|
||||
|
@ -71,7 +71,7 @@ local Server = require 'utils.server'
|
||||
|
||||
local Math = require 'maps.pirates.math'
|
||||
|
||||
require 'utils.profiler'
|
||||
-- require 'utils.profiler'
|
||||
|
||||
local Public = {}
|
||||
|
||||
|
@ -100,16 +100,64 @@ function Public.random_float_in_range(from, to)
|
||||
end
|
||||
|
||||
-- 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)
|
||||
scalar = scalar or 1
|
||||
local random_angle = Public.random_float_in_range(0, 2 * Public.pi)
|
||||
return {
|
||||
x = Public.sin(random_angle) * scalar,
|
||||
y = Public.cos(random_angle) * scalar
|
||||
x = Public.cos(random_angle) * scalar,
|
||||
y = Public.sin(random_angle) * scalar
|
||||
}
|
||||
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)
|
||||
return {x = Public.ceil(point.x), y = Public.ceil(point.x)}
|
||||
end
|
||||
|
@ -86,8 +86,7 @@ function Public.initialise_crew_memory(id) --mostly serves as a dev reference of
|
||||
memory.playerindex_captain = nil
|
||||
memory.captain_accrued_time_data = nil
|
||||
memory.max_players_recorded = nil
|
||||
memory.temporarily_logged_off_characters = nil
|
||||
memory.temporarily_logged_off_characters_items = nil
|
||||
memory.temporarily_logged_off_player_data = nil
|
||||
|
||||
memory.speed_boost_characters = nil
|
||||
|
||||
@ -125,7 +124,7 @@ function Public.fallthrough_crew_memory() --could make this a metatable, but met
|
||||
return {
|
||||
id = 0,
|
||||
difficulty = 1,
|
||||
force_name = 'player',
|
||||
force_name = 'player', -- should match Common.lobby_force_name
|
||||
boat = {},
|
||||
destinations = {},
|
||||
spectatorplayerindices = {},
|
||||
|
@ -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.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
|
||||
end
|
||||
|
||||
@ -495,22 +495,15 @@ function Public.generate_overworld_destination(p)
|
||||
-- end
|
||||
|
||||
if position_candidates then
|
||||
local positions_placed = {}
|
||||
local whilesafety = 0
|
||||
while whilesafety < 10 and (#positions_placed < Math.min(kraken_count, 10)) do
|
||||
local p_chosen, p_kraken
|
||||
local whilesafety2 = 0
|
||||
while whilesafety2 < 50 and ((not p_kraken) or Utils.contains(positions_placed, p_chosen)) do
|
||||
p_chosen = position_candidates[Math.random(#position_candidates)]
|
||||
p_kraken = Utils.psum{p_chosen, p}
|
||||
whilesafety2 = whilesafety2 + 1
|
||||
local random_indices = Math.random_unique_array_indices(#position_candidates, Math.min(kraken_count, 10))
|
||||
if random_indices then
|
||||
for _, idx in ipairs(random_indices) do
|
||||
local p_chosen = position_candidates[idx]
|
||||
local p_kraken = Utils.psum{p_chosen, p}
|
||||
Crowsnest.draw_kraken(p_kraken)
|
||||
memory.overworld_krakens[#memory.overworld_krakens + 1] = p_kraken
|
||||
end
|
||||
Crowsnest.draw_kraken(p_kraken)
|
||||
positions_placed[#positions_placed + 1] = p_kraken
|
||||
memory.overworld_krakens[#memory.overworld_krakens + 1] = p_kraken
|
||||
whilesafety = whilesafety + 1
|
||||
end
|
||||
-- game.print(#positions_placed .. ' krakens placed for' .. macro_p.x .. ', ' .. macro_p.y)
|
||||
end
|
||||
end
|
||||
|
||||
@ -577,16 +570,15 @@ end
|
||||
|
||||
function Public.check_for_kraken_collisions()
|
||||
local memory = Memory.get_crew_memory()
|
||||
local krakens = memory.overworld_krakens
|
||||
|
||||
for i, k in ipairs(krakens) do
|
||||
|
||||
for i = #memory.overworld_krakens, 1, -1 do
|
||||
local k = memory.overworld_krakens[i]
|
||||
local relativex = Crowsnest.platformrightmostedge + k.x - memory.overworldx
|
||||
local relativey = k.y - memory.overworldy
|
||||
|
||||
if (relativex <= 3.5 and relativex >= -3.5 and relativey >= -4 and relativey <= 4) then
|
||||
Kraken.try_spawn_kraken()
|
||||
memory.overworld_krakens = Utils.ordered_table_with_index_removed(krakens, i)
|
||||
memory.overworld_krakens = Utils.ordered_table_with_index_removed(memory.overworld_krakens, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -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'})
|
||||
end
|
||||
else
|
||||
local cost = destination.static_params.base_cost_to_undock
|
||||
|
||||
if cost then
|
||||
local adjusted_cost = Common.time_adjusted_departure_cost(cost)
|
||||
|
||||
Common.spend_stored_resources(adjusted_cost)
|
||||
end
|
||||
Common.consume_undock_cost_resources()
|
||||
Public.retreat_from_island(manual)
|
||||
end
|
||||
else
|
||||
|
@ -1534,4 +1534,13 @@ function Public.force_reconnect_boat_poles()
|
||||
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
|
@ -393,8 +393,13 @@ function Public.tick_quest_structure_entry_price_check()
|
||||
if removed > 0 then
|
||||
local count = 1
|
||||
for k, v in pairs(entry_price.batchRawMaterials) do
|
||||
red_invs[count].insert({name = k, count = v * removed / entry_price.batchSize});
|
||||
count = count + 1
|
||||
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
|
||||
else
|
||||
log('Error (non-critical): item_count is not positive. v: ' .. v .. '; removed: ' .. removed .. '; entry_price.batchSize: ' .. entry_price.batchSize)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -402,8 +407,4 @@ function Public.tick_quest_structure_entry_price_check()
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return Public
|
@ -14,6 +14,7 @@ local Public = {}
|
||||
|
||||
local enum = {
|
||||
DEFAULT = 'Default',
|
||||
SLOT_ARTILLERY_SHELLS = 1,
|
||||
SLOT_EXTRA_HOLD = 5,
|
||||
SLOT_MORE_POWER = 6,
|
||||
SLOT_RANDOM_CLASS = 7,
|
||||
@ -104,11 +105,12 @@ Public.market_price_scale = 300
|
||||
|
||||
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
|
||||
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},
|
||||
},
|
||||
{
|
||||
@ -292,6 +294,8 @@ function Public.create_cabin_surface()
|
||||
end
|
||||
end
|
||||
|
||||
Public.update_captains_market_offers_based_on_difficulty(memory.difficulty_option)
|
||||
|
||||
rendering.draw_text(
|
||||
{
|
||||
color = {60, 255, 124},
|
||||
@ -383,4 +387,29 @@ function Public.get_market_random_price(slot)
|
||||
return nil
|
||||
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
|
@ -20,7 +20,7 @@ Public.Data = require 'maps.pirates.surfaces.islands.cave.data'
|
||||
|
||||
--lab-dark-1 > position has been copied
|
||||
--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)
|
||||
if not source_tile.valid 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
|
||||
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
|
||||
local e = entity.clone({position = entity_position, surface = surface})
|
||||
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.reveal(cave_miner, surface, source_surface, entity_position, 15)
|
||||
Public.reveal(surface, source_surface, entity_position, 15)
|
||||
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 Math.random(20) == 1 then
|
||||
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
|
||||
if elite_spawners then
|
||||
@ -245,7 +245,7 @@ local function on_player_changed_position(event)
|
||||
local cave_miner = destination_data.dynamic_data.cave_miner
|
||||
|
||||
-- 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
|
||||
|
||||
Event.add(defines.events.on_player_changed_position, on_player_changed_position)
|
||||
|
@ -376,7 +376,7 @@ function Public.spawn_enemy_boat(type)
|
||||
boat.spawner = e
|
||||
|
||||
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
|
||||
|
||||
return enemyboats[#enemyboats]
|
||||
|
@ -274,32 +274,36 @@ local function radioactive_tick()
|
||||
local tickinterval = 60
|
||||
|
||||
if destination.subtype == IslandEnum.enum.RADIOACTIVE then
|
||||
-- faster evo (doesn't need difficulty scaling as higher difficulties have higher base evo):
|
||||
local extra_evo = 0.22 * tickinterval/60 / Balance.expected_time_on_island()
|
||||
Common.increment_evo(extra_evo)
|
||||
if (not destination.dynamic_data.evolution_accrued_time) then
|
||||
destination.dynamic_data.evolution_accrued_time = 0
|
||||
end
|
||||
destination.dynamic_data.evolution_accrued_time = destination.dynamic_data.evolution_accrued_time + extra_evo
|
||||
-- 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):
|
||||
local extra_evo = 0.22 * tickinterval/60 / Balance.expected_time_on_island()
|
||||
Common.increment_evo(extra_evo)
|
||||
if (not destination.dynamic_data.evolution_accrued_time) then
|
||||
destination.dynamic_data.evolution_accrued_time = 0
|
||||
end
|
||||
destination.dynamic_data.evolution_accrued_time = destination.dynamic_data.evolution_accrued_time + extra_evo
|
||||
|
||||
if not memory.floating_pollution then memory.floating_pollution = 0 end
|
||||
if not memory.floating_pollution then memory.floating_pollution = 0 end
|
||||
|
||||
-- faster pollute:
|
||||
local pollution = 0
|
||||
local timer = destination.dynamic_data.timer
|
||||
if timer and timer > 15 then
|
||||
pollution = 6 * (Common.difficulty_scale()^(1.1) * (memory.overworldx/40)^(18/10) * (Balance.crew_scale())^(1/5)) / 3600 * tickinterval * (1 + (Common.difficulty_scale()-1)*0.2 + 0.001 * timer)
|
||||
end
|
||||
-- faster pollute:
|
||||
local pollution = 0
|
||||
local timer = destination.dynamic_data.timer
|
||||
if timer and timer > 15 then
|
||||
pollution = 6 * (Common.difficulty_scale()^(1.1) * (memory.overworldx/40)^(18/10) * (Balance.crew_scale())^(1/5)) / 3600 * tickinterval * (1 + (Common.difficulty_scale()-1)*0.2 + 0.001 * timer)
|
||||
end
|
||||
|
||||
if pollution > 0 then
|
||||
memory.floating_pollution = memory.floating_pollution + pollution
|
||||
if pollution > 0 then
|
||||
memory.floating_pollution = memory.floating_pollution + pollution
|
||||
|
||||
game.pollution_statistics.on_flow('uranium-ore', pollution)
|
||||
end
|
||||
game.pollution_statistics.on_flow('uranium-ore', pollution)
|
||||
end
|
||||
|
||||
local surface = game.surfaces[destination.surface_name]
|
||||
if surface and surface.valid and (not surface.freeze_daytime) and destination.dynamic_data.timer and destination.dynamic_data.timer >= CoreData.daynightcycle_types[Public.Data.static_params_default.daynightcycletype].ticksperday/60/2 then --once daytime, never go back to night
|
||||
surface.freeze_daytime = true
|
||||
local surface = game.surfaces[destination.surface_name]
|
||||
if surface and surface.valid and (not surface.freeze_daytime) and destination.dynamic_data.timer and destination.dynamic_data.timer >= CoreData.daynightcycle_types[Public.Data.static_params_default.daynightcycletype].ticksperday/60/2 then --once daytime, never go back to night
|
||||
surface.freeze_daytime = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -161,7 +161,7 @@ local function walkways_tick()
|
||||
if destination.subtype == IslandEnum.enum.WALKWAYS then
|
||||
for _, player in pairs(game.connected_players) do
|
||||
if player.force.name == memory.force_name and player.surface == game.surfaces[destination.surface_name] and player.character and player.character.valid and game.surfaces[destination.surface_name].get_tile(player.position).name == 'water-shallow' then
|
||||
player.character.damage(12, game.forces['environment'], 'fire')
|
||||
player.character.damage(Balance.walkways_frozen_pool_damage, game.forces['environment'], 'fire')
|
||||
if not (player.character and player.character.valid) then
|
||||
Common.notify_force(player.force, {'pirates.death_froze',player.name})
|
||||
end
|
||||
|
@ -488,7 +488,7 @@ function Public.destination_on_crewboat_hits_shore(destination)
|
||||
local spawner = Common.get_random_valid_spawner(surface)
|
||||
if spawner then
|
||||
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
|
||||
if elite_spawners then
|
||||
|
Loading…
Reference in New Issue
Block a user