mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2025-01-06 00:23:49 +02:00
564428661a
Changes: - Fixed an issue, where crew would lose when ship auto undock timer reached 0, but crew hadn't escaped from island by then, causing it to lose (in cases where resources are a must to escape from island). - Fixed an issue, where boat wouldn't auto undock in radioactive island when auto undock timer reached 0. - Adjusted some dock market prices (for converting one item to another). - Decreased the amount of items needed for a gui island crafting quest for harder difficulties and decreased the scaling of item requirement. This should make these quests completable even in late game. - Slightly decreased the reward for gui island crafting quest to accommodate the change above. - Reduced amount of items needed for island market quests.
450 lines
16 KiB
Lua
450 lines
16 KiB
Lua
-- This file is part of thesixthroc's Pirate Ship softmod, licensed under GPLv3 and stored at https://github.com/danielmartin0/ComfyFactorio-Pirates.
|
|
|
|
local Memory = require 'maps.pirates.memory'
|
|
-- local Roles = require 'maps.pirates.roles.roles'
|
|
local Balance = require 'maps.pirates.balance'
|
|
local Common = require 'maps.pirates.common'
|
|
-- local Utils = require 'maps.pirates.utils_local'
|
|
local Math = require 'maps.pirates.math'
|
|
local Raffle = require 'maps.pirates.raffle'
|
|
-- local Loot = require 'maps.pirates.loot'
|
|
local CoreData = require 'maps.pirates.coredata'
|
|
local IslandEnum = require 'maps.pirates.surfaces.islands.island_enum'
|
|
local _inspect = require 'utils.inspect'.inspect
|
|
|
|
|
|
local Public = {}
|
|
|
|
local enum = {
|
|
TIME = 'Time',
|
|
FIND = 'Find',
|
|
NODAMAGE = 'No_Damage',
|
|
RESOURCEFLOW = 'Resource_Flow',
|
|
RESOURCECOUNT = 'Resource_Count',
|
|
WORMS = 'Worms',
|
|
FISH = 'Fish',
|
|
COMPILATRON = 'Compilatron', -- compilatron is robot that looks like sheep
|
|
}
|
|
Public.enum = enum
|
|
|
|
Public.quest_icons = {
|
|
[enum.TIME] = '[img=utility.time_editor_icon]',
|
|
[enum.NODAMAGE] = '[item=stone-wall]',
|
|
[enum.WORMS] = '[entity=small-worm-turret]',
|
|
[enum.FIND] = '[img=utility.ghost_time_to_live_modifier_icon]',
|
|
[enum.RESOURCEFLOW] = '',
|
|
[enum.RESOURCECOUNT] = '',
|
|
[enum.FISH] = '[item=raw-fish]',
|
|
[enum.COMPILATRON] = '[entity=compilatron]',
|
|
}
|
|
|
|
|
|
|
|
-- @TODO remake into a loot-style table:
|
|
|
|
function Public.quest_reward()
|
|
local ret
|
|
local multiplier = Balance.quest_reward_multiplier()
|
|
local rng = Math.random()
|
|
|
|
if rng <= 0.3 then
|
|
ret = {name = 'iron-plate', count = Math.ceil(2000 * multiplier), display_sprite = '[item=iron-plate]', display_amount = string.format('%.1fk', 2 * multiplier), chat_name = '[item=iron-plate]'}
|
|
elseif rng <= 0.5 then
|
|
ret = {name = 'copper-plate', count = Math.ceil(2000 * multiplier), display_sprite = '[item=copper-plate]', display_amount = string.format('%.1fk', 2 * multiplier), chat_name = '[item=copper-plate]'}
|
|
elseif rng <= 0.6 then
|
|
ret = {name = 'steel-plate', count = Math.ceil(380 * multiplier), display_sprite = '[item=steel-plate]', display_amount = string.format('%.0f', 380 * multiplier), chat_name = '[item=steel-plate]'}
|
|
elseif rng <= 0.7 then
|
|
ret = {name = 'raw-fish', count = Math.ceil(420 * (multiplier^(1/2))), display_sprite = '[item=raw-fish]', display_amount = string.format('%.0f', 420 * (multiplier^(1/2))), chat_name = '[item=raw-fish]'}
|
|
elseif rng <= 0.85 then
|
|
ret = {name = 'piercing-rounds-magazine', count = Math.ceil(250 * multiplier), display_sprite = '[item=piercing-rounds-magazine]', display_amount = string.format('%.0f', Math.ceil(250 * multiplier)), chat_name = '[item=piercing-rounds-magazine]'}
|
|
elseif rng <= 0.94 then
|
|
ret = {name = 'solid-fuel', count = Math.ceil(450 * multiplier), display_sprite = '[item=solid-fuel]', display_amount = string.format('%.0f', Math.ceil(450 * multiplier)), chat_name = '[item=solid-fuel]'}
|
|
else
|
|
ret = {name = 'modular-armor', count = 1, display_sprite = '[item=modular-armor]', display_amount = '1', chat_name = '[item=modular-armor]'}
|
|
end
|
|
|
|
return ret
|
|
end
|
|
|
|
|
|
|
|
|
|
function Public.initialise_random_quest()
|
|
local destination = Common.current_destination()
|
|
|
|
destination.dynamic_data.quest_complete = false
|
|
|
|
local rng = Math.random(100)
|
|
if rng <= 10 then
|
|
Public.initialise_nodamage_quest()
|
|
elseif rng <= 33 then
|
|
Public.initialise_worms_quest()
|
|
elseif rng <= 54 then
|
|
Public.initialise_time_quest()
|
|
elseif rng <= 74 then
|
|
Public.initialise_find_quest()
|
|
elseif rng <= 100 then
|
|
Public.initialise_resourcecount_quest()
|
|
-- Public.initialise_resourceflow_quest()
|
|
end
|
|
|
|
-- Public.initialise_time_quest()
|
|
end
|
|
|
|
function Public.initialise_random_cave_island_quest()
|
|
local rng = Math.random(100)
|
|
if rng <= 50 then
|
|
Public.initialise_fish_quest()
|
|
else
|
|
Public.initialise_compilatron_quest()
|
|
end
|
|
end
|
|
|
|
function Public.initialise_time_quest()
|
|
local destination = Common.current_destination()
|
|
|
|
destination.dynamic_data.quest_type = enum.TIME
|
|
destination.dynamic_data.quest_reward = Public.quest_reward()
|
|
destination.dynamic_data.quest_progress = Balance.time_quest_seconds()
|
|
destination.dynamic_data.quest_progressneeded = 9999999
|
|
|
|
return true
|
|
end
|
|
|
|
function Public.initialise_find_quest()
|
|
local destination = Common.current_destination()
|
|
|
|
if destination.subtype == IslandEnum.enum.STANDARD or destination.subtype == IslandEnum.enum.RADIOACTIVE or destination.subtype == IslandEnum.enum.STANDARD_VARIANT then
|
|
|
|
destination.dynamic_data.quest_type = enum.FIND
|
|
destination.dynamic_data.quest_reward = Public.quest_reward()
|
|
destination.dynamic_data.quest_progress = 0
|
|
if #Common.crew_get_crew_members() > 15 then
|
|
destination.dynamic_data.quest_progressneeded = 2
|
|
else
|
|
destination.dynamic_data.quest_progressneeded = 1
|
|
end
|
|
return true
|
|
else
|
|
log('Find quest not appropriate, rerolling')
|
|
Public.initialise_random_quest() --@FIXME: mild danger of loop
|
|
return false
|
|
end
|
|
end
|
|
|
|
|
|
function Public.initialise_nodamage_quest()
|
|
local destination = Common.current_destination()
|
|
|
|
-- @FIXME: this if check looks ill-formed when destination is nil
|
|
if not destination and destination.dynamic_data and destination.dynamic_data.rocketsilomaxhp then return false end
|
|
|
|
destination.dynamic_data.quest_type = enum.NODAMAGE
|
|
destination.dynamic_data.quest_reward = Public.quest_reward()
|
|
destination.dynamic_data.quest_progress = 0
|
|
destination.dynamic_data.quest_progressneeded = destination.dynamic_data.rocketsilomaxhp
|
|
|
|
return true
|
|
end
|
|
|
|
|
|
-- @UNUSED
|
|
-- function Public.initialise_resourceflow_quest()
|
|
-- local destination = Common.current_destination()
|
|
|
|
-- if not destination and destination.dynamic_data and destination.dynamic_data.rocketsilomaxhp then return false end
|
|
|
|
-- destination.dynamic_data.quest_type = enum.RESOURCEFLOW
|
|
-- destination.dynamic_data.quest_reward = Public.quest_reward()
|
|
-- destination.dynamic_data.quest_progress = 0
|
|
|
|
-- local generated_flow_quest = Public.generate_flow_quest()
|
|
-- if not generated_flow_quest then return false end
|
|
|
|
-- destination.dynamic_data.quest_params = {item = generated_flow_quest.item}
|
|
|
|
-- local progressneeded_before_rounding = generated_flow_quest.base_rate * Balance.resource_quest_multiplier()
|
|
|
|
-- destination.dynamic_data.quest_progressneeded = Math.ceil(progressneeded_before_rounding/10) * 10
|
|
|
|
-- return true
|
|
-- end
|
|
|
|
|
|
function Public.initialise_resourcecount_quest()
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
|
|
if not destination and destination.dynamic_data and destination.dynamic_data.rocketsilomaxhp then return end
|
|
|
|
destination.dynamic_data.quest_type = enum.RESOURCECOUNT
|
|
destination.dynamic_data.quest_reward = Public.quest_reward()
|
|
destination.dynamic_data.quest_progress = 0
|
|
|
|
local generated_production_quest = Public.generate_resourcecount_quest()
|
|
if not generated_production_quest then return false end
|
|
|
|
destination.dynamic_data.quest_params = {item = generated_production_quest.item}
|
|
|
|
local force = memory.force
|
|
if force and force.valid then
|
|
destination.dynamic_data.quest_params.initial_count = force.item_production_statistics.get_flow_count{name = generated_production_quest.item, input = true, precision_index = defines.flow_precision_index.one_thousand_hours, count = true}
|
|
end
|
|
|
|
local progressneeded_before_rounding = generated_production_quest.base_rate * Balance.resource_quest_multiplier()
|
|
|
|
destination.dynamic_data.quest_progressneeded = Math.ceil(progressneeded_before_rounding/10)*10
|
|
|
|
return true
|
|
end
|
|
|
|
|
|
function Public.initialise_worms_quest()
|
|
local destination = Common.current_destination()
|
|
|
|
if not (destination.surface_name and game.surfaces[destination.surface_name]) then return false end
|
|
|
|
local surface = game.surfaces[destination.surface_name]
|
|
|
|
local worms = surface.find_entities_filtered{type = 'turret'}
|
|
|
|
local count = 0
|
|
for i = 1, #worms do
|
|
local w = worms[i]
|
|
if w.destructible then count = count + 1 end
|
|
end
|
|
|
|
local needed = Math.ceil(
|
|
1 + 9 * Math.slopefromto(count, 0, 20) + 10 * Math.slopefromto(count, 20, 70)
|
|
)
|
|
|
|
if Common.difficulty_scale() < 1 then needed = Math.max(1, needed - 3) end
|
|
if Common.difficulty_scale() > 1 then needed = Math.max(1, needed + 2) end
|
|
|
|
local difficulty_name = CoreData.get_difficulty_option_informal_name_from_value(Common.difficulty_scale())
|
|
if difficulty_name == 'easy' then
|
|
needed = Math.max(1, needed - 3)
|
|
elseif difficulty_name ~= 'normal' then
|
|
needed = Math.max(1, needed + 2)
|
|
end
|
|
|
|
if needed >= 5 then
|
|
destination.dynamic_data.quest_type = enum.WORMS
|
|
destination.dynamic_data.quest_reward = Public.quest_reward()
|
|
destination.dynamic_data.quest_progress = 0
|
|
destination.dynamic_data.quest_progressneeded = needed
|
|
return true
|
|
else
|
|
log('Worms quest not appropriate, rerolling')
|
|
Public.initialise_random_quest() --@FIXME: mild danger of loop
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- Catch amount of fish (currently Cave island exclusive, because it's hard to calculate "quest_progressneeded")
|
|
function Public.initialise_fish_quest()
|
|
local destination = Common.current_destination()
|
|
|
|
if not destination and destination.dynamic_data then return false end
|
|
|
|
destination.dynamic_data.quest_type = enum.FISH
|
|
destination.dynamic_data.quest_reward = Public.quest_reward()
|
|
destination.dynamic_data.quest_progress = 0
|
|
destination.dynamic_data.quest_progressneeded = Math.random(350, 500) -- assuming that base caught fish amount is 3
|
|
|
|
return true
|
|
end
|
|
|
|
-- Rescue compilatrons under the heavy rocks (currently Cave island exclusive, because it's hard to calculate "quest_progressneeded")
|
|
function Public.initialise_compilatron_quest()
|
|
local destination = Common.current_destination()
|
|
|
|
if not destination and destination.dynamic_data then return false end
|
|
|
|
destination.dynamic_data.quest_type = enum.COMPILATRON
|
|
destination.dynamic_data.quest_reward = Public.quest_reward()
|
|
destination.dynamic_data.quest_progress = 0
|
|
destination.dynamic_data.quest_progressneeded = Math.random(50, 80) -- assuming that chance to find compilatron is 1/20
|
|
|
|
return true
|
|
end
|
|
|
|
|
|
function Public.try_resolve_quest()
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
|
|
if destination.dynamic_data.quest_type and destination.dynamic_data.quest_progress and destination.dynamic_data.quest_progressneeded and destination.dynamic_data.quest_progress >= destination.dynamic_data.quest_progressneeded and (not destination.dynamic_data.quest_complete) then
|
|
|
|
local force = memory.force
|
|
if not (force and force.valid) then return end
|
|
Common.notify_force_light(force, {'pirates.granted_1', {'pirates.granted_quest_complete'}, destination.dynamic_data.quest_reward.display_amount .. destination.dynamic_data.quest_reward.chat_name})
|
|
|
|
local name = destination.dynamic_data.quest_reward.name
|
|
local count = destination.dynamic_data.quest_reward.count
|
|
|
|
-- destination.dynamic_data.quest_type = nil
|
|
-- destination.dynamic_data.quest_reward = nil
|
|
-- destination.dynamic_data.quest_progress = nil
|
|
-- destination.dynamic_data.quest_progressneeded = nil
|
|
destination.dynamic_data.quest_complete = true
|
|
|
|
local boat = memory.boat
|
|
if not boat then return end
|
|
local surface_name = boat.surface_name
|
|
if not surface_name then return end
|
|
local surface = game.surfaces[surface_name]
|
|
if not (surface and surface.valid) then return end
|
|
local chest = boat.output_chest
|
|
if not chest and chest.valid then return end
|
|
|
|
local inventory = chest.get_inventory(defines.inventory.chest)
|
|
local inserted = inventory.insert{name = name, count = count}
|
|
|
|
if inserted < count then
|
|
local chest2 = boat.backup_output_chest
|
|
if chest2 and chest2.valid then
|
|
local inventory2 = chest2.get_inventory(defines.inventory.chest)
|
|
local inserted2 = inventory2.insert{name = name, count = count - inserted}
|
|
if (inserted + inserted2) < count then
|
|
Common.notify_force(force, {'pirates.error_cabin_full'})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
-- Public.flow_quest_data_raw = {
|
|
-- {0.2, 0, 1, false, 'submachine-gun', 3 * 12},
|
|
-- {1, 0, 1, false, 'electronic-circuit', 3 * 120},
|
|
-- {0.2, 0.1, 1, false, 'big-electric-pole', 1 * 120},
|
|
-- {0.4, 0.2, 1, false, 'engine-unit', 3 * 6},
|
|
-- -- {1, 0.5, 1, false, 'advanced-circuit', 1 * 10},
|
|
-- -- {0.3, 0.8, 1, false, 'electric-engine-unit', 1 * 6},
|
|
-- }
|
|
|
|
-- function Public.flow_quest_data()
|
|
-- local ret = {}
|
|
-- local data = Public.flow_quest_data_raw
|
|
-- for i = 1, #data do
|
|
-- local datum = data[i]
|
|
-- ret[#ret + 1] = {
|
|
-- weight = datum[1],
|
|
-- game_completion_progress_min = datum[2],
|
|
-- game_completion_progress_max = datum[3],
|
|
-- scaling = datum[4],
|
|
-- item = datum[5],
|
|
-- base_rate = datum[6],
|
|
-- }
|
|
-- end
|
|
-- return ret
|
|
-- end
|
|
|
|
-- @UNUSED
|
|
-- function Public.generate_flow_quest()
|
|
-- --@TODO: Ensure this function cannot return nil
|
|
-- --@TODO: This is related to a more general problem with raffles — how they handle game_completion being above 1. As of May '22, we cap game_completion at 1 before passing it to the raffle
|
|
|
|
-- local game_completion_progress = Common.game_completion_progress_capped()
|
|
|
|
-- local data = Public.flow_quest_data()
|
|
-- local v, w = {}, {}
|
|
|
|
-- for i = 1, #data, 1 do
|
|
-- table.insert(v, {item = data[i].item, base_rate = data[i].base_rate})
|
|
|
|
-- local destination = Common.current_destination()
|
|
-- if not (destination and destination.subtype and data[i].map_subtype and data[i].map_subtype == destination.subtype) then
|
|
-- if data[i].scaling then -- scale down weights away from the midpoint 'peak' (without changing the mean)
|
|
-- local midpoint = (data[i].game_completion_progress_max + data[i].game_completion_progress_min) / 2
|
|
-- local difference = (data[i].game_completion_progress_max - data[i].game_completion_progress_min)
|
|
-- table.insert(w, data[i].weight * Math.max(0, 1 - (Math.abs(game_completion_progress - midpoint) / (difference / 2))))
|
|
-- else -- no scaling
|
|
-- if data[i].game_completion_progress_min <= game_completion_progress and data[i].game_completion_progress_max >= game_completion_progress then
|
|
-- table.insert(w, data[i].weight)
|
|
-- else
|
|
-- table.insert(w, 0)
|
|
-- end
|
|
-- end
|
|
-- end
|
|
-- end
|
|
|
|
-- return Raffle.raffle(v, w)
|
|
-- end
|
|
|
|
|
|
|
|
|
|
|
|
Public.resourcecount_quest_data_raw = {
|
|
{1.1, 0, 1, false, 'iron-gear-wheel', 2400},
|
|
{0.5, 0, 1, false, 'electronic-circuit', 1400},
|
|
{1.1, 0, 1, false, 'transport-belt', 1600},
|
|
{0.8, 0, 1, false, 'repair-pack', 350},
|
|
-- {0.1, 0, 1, false, 'red-wire', 500},
|
|
{0.4, 0, 1, false, 'empty-barrel', 200},
|
|
{0.7, 0, 0.5, false, 'underground-belt', 200},
|
|
{0.7, 0, 0.5, false, 'splitter', 150},
|
|
{0.35, 0.2, 1, false, 'fast-splitter', 60},
|
|
{0.35, 0.2, 1, false, 'fast-underground-belt', 75},
|
|
{0.7, 0.3, 1, false, 'big-electric-pole', 100},
|
|
{0.3, 0.61, 1, false, 'advanced-circuit', 350},
|
|
{1, 0, 1, false, 'shotgun-shell', 600},
|
|
{1, 0.9, 1, false, 'processing-unit', 40},
|
|
{0.6, 0.8, 1, false, 'electric-engine-unit', 1 * 6},
|
|
}
|
|
|
|
function Public.resourcecount_quest_data()
|
|
local ret = {}
|
|
local data = Public.resourcecount_quest_data_raw
|
|
for i = 1, #data do
|
|
local datum = data[i]
|
|
ret[#ret + 1] = {
|
|
weight = datum[1],
|
|
game_completion_progress_min = datum[2],
|
|
game_completion_progress_max = datum[3],
|
|
scaling = datum[4],
|
|
item = datum[5],
|
|
base_rate = datum[6],
|
|
}
|
|
end
|
|
return ret
|
|
end
|
|
|
|
function Public.generate_resourcecount_quest()
|
|
local game_completion_progress = Common.game_completion_progress_capped()
|
|
|
|
local data = Public.resourcecount_quest_data()
|
|
local v, w = {}, {}
|
|
|
|
for i = 1, #data, 1 do
|
|
table.insert(v, {item = data[i].item, base_rate = data[i].base_rate})
|
|
|
|
local destination = Common.current_destination()
|
|
if not (destination and destination.subtype and data[i].map_subtype and data[i].map_subtype == destination.subtype) then
|
|
if data[i].scaling then -- scale down weights away from the midpoint 'peak' (without changing the mean)
|
|
local midpoint = (data[i].game_completion_progress_max + data[i].game_completion_progress_min) / 2
|
|
local difference = (data[i].game_completion_progress_max - data[i].game_completion_progress_min)
|
|
table.insert(w, data[i].weight * Math.max(0, 1 - (Math.abs(game_completion_progress - midpoint) / (difference / 2))))
|
|
else -- no scaling
|
|
if data[i].game_completion_progress_min <= game_completion_progress and data[i].game_completion_progress_max >= game_completion_progress then
|
|
table.insert(w, data[i].weight)
|
|
else
|
|
table.insert(w, 0)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return Raffle.raffle(v, w)
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
return Public |