mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2025-01-10 00:43:27 +02:00
e21483149d
- Damage reduction for players work even with "lethal blows" (except when player is about to be one shotted and regenerates slight amount of health to still get oneshotted, but that's quite rare against biters, although if damage reduction works against uranium grenades, this last_health maybe should be updated every now and then (perhaps every 30 or 60 seconds)) - Moved a lot of class related hard coded constants to Balance.lua - New command: /classinfofull <class> - Samurai and Hatamoto classes melee damage now also scales from 'physical projectile damage' research bonus - Other various code related changes
383 lines
14 KiB
Lua
383 lines
14 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 Balance = require 'maps.pirates.balance'
|
|
local _inspect = require 'utils.inspect'.inspect
|
|
local Memory = require 'maps.pirates.memory'
|
|
local Math = require 'maps.pirates.math'
|
|
local Common = require 'maps.pirates.common'
|
|
local Utils = require 'maps.pirates.utils_local'
|
|
local CoreData = require 'maps.pirates.coredata'
|
|
local SurfacesCommon = require 'maps.pirates.surfaces.common'
|
|
-- local Server = require 'utils.server'
|
|
|
|
local Public = {}
|
|
local enum = {
|
|
DECKHAND = 'deckhand',
|
|
FISHERMAN = 'fisherman',
|
|
SCOUT = 'scout',
|
|
SAMURAI = 'samurai',
|
|
MERCHANT = 'merchant',
|
|
SHORESMAN = 'shoresman',
|
|
BOATSWAIN = 'boatswain',
|
|
PROSPECTOR = 'prospector',
|
|
LUMBERJACK = 'lumberjack',
|
|
MASTER_ANGLER = 'master_angler',
|
|
WOOD_LORD = 'wood_lord',
|
|
CHIEF_EXCAVATOR = 'chief_excavator',
|
|
HATAMOTO = 'hatamoto',
|
|
IRON_LEG = 'iron_leg',
|
|
QUARTERMASTER = 'quartermaster',
|
|
DREDGER = 'dredger',
|
|
SMOLDERING = 'smoldering',
|
|
GOURMET = 'gourmet',
|
|
}
|
|
Public.enum = enum
|
|
|
|
-- function Public.Class_List()
|
|
-- local ret = {}
|
|
-- for _,v in pairs(enum) do
|
|
-- ret[#ret + 1] = v
|
|
-- end
|
|
-- end
|
|
|
|
Public.eng_form = {
|
|
[enum.DECKHAND] = 'Deckhand',
|
|
[enum.FISHERMAN] = 'Fisherman',
|
|
[enum.SCOUT] = 'Scout',
|
|
[enum.SAMURAI] = 'Samurai',
|
|
[enum.MERCHANT] = 'Merchant',
|
|
[enum.SHORESMAN] = 'Shoresman',
|
|
[enum.BOATSWAIN] = 'Boatswain',
|
|
[enum.PROSPECTOR] = 'Prospector',
|
|
[enum.LUMBERJACK] = 'Lumberjack',
|
|
[enum.MASTER_ANGLER] = 'Master Angler',
|
|
[enum.WOOD_LORD] = 'Wood Lord',
|
|
[enum.CHIEF_EXCAVATOR] = 'Chief Excavator',
|
|
[enum.HATAMOTO] = 'Hatamoto',
|
|
[enum.IRON_LEG] = 'Iron Leg',
|
|
[enum.QUARTERMASTER] = 'Quartermaster',
|
|
[enum.DREDGER] = 'Dredger',
|
|
[enum.SMOLDERING] = 'Smoldering',
|
|
[enum.GOURMET] = 'Gourmet',
|
|
}
|
|
|
|
function Public.display_form(class)
|
|
return {'pirates.class_' .. class}
|
|
end
|
|
|
|
function Public.explanation(class)
|
|
return {'pirates.class_' .. class .. '_explanation'}
|
|
end
|
|
|
|
function Public.explanation_advanced(class)
|
|
local explanation = 'pirates.class_' .. class .. '_explanation_advanced'
|
|
local full_explanation = {}
|
|
|
|
if class == enum.DECKHAND then
|
|
local extra_speed = Public.calculate_percentage(Balance.deckhand_extra_speed)
|
|
local ore_amount = Public.ore_grant_amount(Balance.deckhand_ore_grant_multiplier, Balance.deckhand_ore_scaling_enabled)
|
|
local tick_rate = Balance.class_reward_tick_rate_in_seconds
|
|
full_explanation = {'', {explanation, extra_speed, ore_amount, tick_rate}}
|
|
elseif class == enum.BOATSWAIN then
|
|
local extra_speed = Public.calculate_percentage(Balance.boatswain_extra_speed)
|
|
local ore_amount = Public.ore_grant_amount(Balance.boatswain_ore_grant_multiplier, Balance.boatswain_ore_scaling_enabled)
|
|
local tick_rate = Balance.class_reward_tick_rate_in_seconds
|
|
full_explanation = {'', {explanation, extra_speed, ore_amount, tick_rate}}
|
|
elseif class == enum.SHORESMAN then
|
|
local extra_speed = Public.calculate_percentage(Balance.shoresman_extra_speed)
|
|
local ore_amount = Public.ore_grant_amount(Balance.shoresman_ore_grant_multiplier, Balance.shoresman_ore_scaling_enabled)
|
|
local tick_rate = Balance.class_reward_tick_rate_in_seconds
|
|
full_explanation = {'', {explanation, extra_speed, ore_amount, tick_rate}}
|
|
elseif class == enum.QUARTERMASTER then
|
|
local range = Balance.quartermaster_range
|
|
local extra_physical = Public.calculate_percentage(Balance.quartermaster_bonus_physical_damage)
|
|
full_explanation = {'', {explanation, range, extra_physical}}
|
|
elseif class == enum.FISHERMAN then
|
|
local extra_range = Balance.fisherman_reach_bonus
|
|
full_explanation = {'', {explanation, extra_range}}
|
|
elseif class == enum.MASTER_ANGLER then
|
|
local extra_range = Balance.master_angler_reach_bonus
|
|
local extra_fish = Balance.master_angler_fish_bonus
|
|
local extra_coins = Balance.master_angler_coin_bonus
|
|
full_explanation = {'', {explanation, extra_range, extra_fish, extra_coins}}
|
|
elseif class == enum.SCOUT then
|
|
local extra_speed = Public.calculate_percentage(Balance.scout_extra_speed)
|
|
local received_damage = Public.calculate_percentage(Balance.scout_damage_taken_multiplier)
|
|
local dealt_damage = Public.calculate_percentage(Balance.scout_damage_dealt_multiplier)
|
|
full_explanation = {'', {explanation, extra_speed, received_damage, dealt_damage}}
|
|
elseif class == enum.SAMURAI then
|
|
local received_damage = Public.calculate_percentage(Balance.samurai_damage_taken_multiplier)
|
|
local melee_damage = Balance.samurai_damage_dealt_with_melee_multiplier
|
|
local non_melee_damage = Public.calculate_percentage(Balance.samurai_damage_dealt_when_not_melee_multiplier)
|
|
full_explanation = {'', {explanation, received_damage, melee_damage, non_melee_damage}}
|
|
elseif class == enum.HATAMOTO then
|
|
local received_damage = Public.calculate_percentage(Balance.hatamoto_damage_taken_multiplier)
|
|
local melee_damage = Balance.hatamoto_damage_dealt_with_melee_multiplier
|
|
local non_melee_damage = Public.calculate_percentage(Balance.hatamoto_damage_dealt_when_not_melee_multiplier)
|
|
full_explanation = {'', {explanation, received_damage, melee_damage, non_melee_damage}}
|
|
elseif class == enum.IRON_LEG then
|
|
local received_damage = Public.calculate_percentage(Balance.iron_leg_damage_taken_multiplier)
|
|
local iron_ore_required = Balance.iron_leg_iron_ore_required
|
|
full_explanation = {'', {explanation, received_damage, iron_ore_required}}
|
|
else
|
|
full_explanation = {'', {explanation}}
|
|
end
|
|
|
|
full_explanation[#full_explanation + 1] = Public.class_is_obtainable(class) and {'', ' ', {'pirates.class_obtainable'}} or {'', ' ', {'pirates.class_unobtainable'}}
|
|
|
|
return full_explanation
|
|
end
|
|
|
|
-- Public.display_form = {
|
|
-- [enum.DECKHAND] = {'pirates.class_deckhand'},
|
|
-- }
|
|
-- Public.explanation = {
|
|
-- [enum.DECKHAND] = {'pirates.class_deckhand_explanation'},
|
|
-- }
|
|
|
|
|
|
function Public.calculate_percentage(multiplier)
|
|
if(multiplier < 1) then
|
|
return (1 - multiplier) * 100
|
|
else
|
|
return (multiplier - 1) * 100
|
|
end
|
|
end
|
|
|
|
|
|
Public.class_unlocks = {
|
|
[enum.FISHERMAN] = {enum.MASTER_ANGLER},
|
|
-- [enum.LUMBERJACK] = {enum.WOOD_LORD}, --not that interesting
|
|
-- [enum.PROSPECTOR] = {enum.CHIEF_EXCAVATOR}, --breaks the resource pressure in the game too strongly I think
|
|
[enum.SAMURAI] = {enum.HATAMOTO},
|
|
[enum.MASTER_ANGLER] = {enum.DREDGER},
|
|
}
|
|
|
|
Public.class_purchase_requirement = {
|
|
[enum.MASTER_ANGLER] = enum.FISHERMAN,
|
|
[enum.WOOD_LORD] = enum.LUMBERJACK,
|
|
[enum.CHIEF_EXCAVATOR] = enum.PROSPECTOR,
|
|
[enum.HATAMOTO] = enum.SAMURAI,
|
|
[enum.DREDGER] = enum.MASTER_ANGLER,
|
|
}
|
|
|
|
function Public.initial_class_pool()
|
|
return {
|
|
enum.DECKHAND,
|
|
enum.DECKHAND, --good for afk players
|
|
enum.SHORESMAN,
|
|
enum.SHORESMAN,
|
|
enum.QUARTERMASTER,
|
|
enum.FISHERMAN,
|
|
enum.SCOUT,
|
|
enum.SAMURAI,
|
|
-- enum.MERCHANT, --not interesting, breaks coin economy
|
|
enum.BOATSWAIN,
|
|
-- enum.PROSPECTOR, --lumberjack is just more fun
|
|
enum.LUMBERJACK,
|
|
enum.IRON_LEG,
|
|
-- enum.SMOLDERING, --tedious
|
|
enum.GOURMET,
|
|
}
|
|
end
|
|
|
|
function Public.class_is_obtainable(class)
|
|
local obtainable_class_pool = Public.initial_class_pool()
|
|
|
|
for _, unlocked_class_list in pairs(Public.class_unlocks) do
|
|
for __, unlocked_class in ipairs(unlocked_class_list) do
|
|
obtainable_class_pool[#obtainable_class_pool + 1] = unlocked_class
|
|
end
|
|
end
|
|
|
|
for _, unlockable_class in ipairs(obtainable_class_pool) do
|
|
if unlockable_class == class then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
|
|
function Public.assign_class(player_index, class, self_assigned)
|
|
local memory = Memory.get_crew_memory()
|
|
local player = game.players[player_index]
|
|
|
|
if not memory.classes_table then memory.classes_table = {} end
|
|
|
|
if memory.classes_table[player_index] == class then
|
|
Common.notify_player_error(player, {'pirates.error_class_assign_redundant', Public.display_form(class)})
|
|
return false
|
|
end
|
|
|
|
if Utils.contains(memory.spare_classes, class) then -- verify that one is spare
|
|
|
|
Public.try_renounce_class(player, false)
|
|
|
|
memory.classes_table[player_index] = class
|
|
|
|
local force = memory.force
|
|
if force and force.valid then
|
|
if self_assigned then
|
|
Common.notify_force_light(force,{'pirates.class_take_spare', player.name, Public.display_form(memory.classes_table[player_index]), Public.explanation(memory.classes_table[player_index])})
|
|
else
|
|
Common.notify_force_light(force,{'pirates.class_give_spare', Public.display_form(memory.classes_table[player_index]), player.name, Public.explanation(memory.classes_table[player_index])})
|
|
end
|
|
end
|
|
|
|
memory.spare_classes = Utils.ordered_table_with_single_value_removed(memory.spare_classes, class)
|
|
return true
|
|
else
|
|
Common.notify_player_error(player, {'pirates.error_class_assign_unavailable_class'})
|
|
return false
|
|
end
|
|
end
|
|
|
|
function Public.try_renounce_class(player, whisper_failure_message, impersonal_bool)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
local force = memory.force
|
|
if force and force.valid and player and player.index then
|
|
if memory.classes_table and memory.classes_table[player.index] then
|
|
if force and force.valid then
|
|
if impersonal_bool then
|
|
Common.notify_force_light(force,{'pirates.class_becomes_spare', Public.display_form(memory.classes_table[player.index])})
|
|
else
|
|
Common.notify_force_light(force,{'pirates.class_give_up', player.name, Public.display_form(memory.classes_table[player.index])})
|
|
end
|
|
end
|
|
|
|
memory.spare_classes[#memory.spare_classes + 1] = memory.classes_table[player.index]
|
|
memory.classes_table[player.index] = nil
|
|
elseif whisper_failure_message then
|
|
Common.notify_player_error(player, {'pirates.class_give_up_error_no_class'})
|
|
end
|
|
end
|
|
end
|
|
|
|
function Public.generate_class_for_sale()
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
-- if #memory.available_classes_pool == 0 then
|
|
-- -- memory.available_classes_pool = Public.initial_class_pool() --reset to initial state
|
|
-- -- turned off as this makes too many classes
|
|
-- end
|
|
|
|
local class
|
|
if #memory.available_classes_pool > 0 then
|
|
class = memory.available_classes_pool[Math.random(#memory.available_classes_pool)]
|
|
end
|
|
|
|
return class
|
|
end
|
|
|
|
|
|
|
|
function Public.class_ore_grant(player, how_much, enable_scaling)
|
|
local count = ore_grant_amount(how_much, enable_scaling)
|
|
|
|
if Math.random(4) == 1 then
|
|
Common.flying_text_small(player.surface, player.position, '[color=0.85,0.58,0.37]+' .. count .. '[/color]')
|
|
Common.give_items_to_crew{{name = 'copper-ore', count = count}}
|
|
else
|
|
Common.flying_text_small(player.surface, player.position, '[color=0.7,0.8,0.8]+' .. count .. '[/color]')
|
|
Common.give_items_to_crew{{name = 'iron-ore', count = count}}
|
|
end
|
|
end
|
|
|
|
function Public.ore_grant_amount(how_much, enable_scaling)
|
|
if enable_scaling then
|
|
return Math.ceil(how_much * Balance.class_resource_scale())
|
|
else
|
|
return Math.ceil(how_much)
|
|
end
|
|
end
|
|
|
|
local function class_on_player_used_capsule(event)
|
|
|
|
local player = game.players[event.player_index]
|
|
if not player or not player.valid then
|
|
return
|
|
end
|
|
local player_index = player.index
|
|
|
|
local crew_id = tonumber(string.sub(player.force.name, -3, -1)) or nil
|
|
Memory.set_working_id(crew_id)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
if not (player.character and player.character.valid) then
|
|
return
|
|
end
|
|
|
|
local item = event.item
|
|
if not (item and item.name and item.name == 'raw-fish') then return end
|
|
|
|
local global_memory = Memory.get_global_memory()
|
|
global_memory.last_players_health[event.player_index] = player.character.health
|
|
|
|
if memory.classes_table and memory.classes_table[player_index] then
|
|
local class = memory.classes_table[player_index]
|
|
if class == Public.enum.GOURMET then
|
|
local multiplier = 0
|
|
local surfacedata = SurfacesCommon.decode_surface_name(player.surface.name)
|
|
if surfacedata.type == SurfacesCommon.enum.CABIN then
|
|
multiplier = 0.25
|
|
elseif surfacedata.type == SurfacesCommon.enum.CROWSNEST then
|
|
multiplier = 0.15
|
|
else
|
|
local tile = player.surface.get_tile(player.position)
|
|
if tile.valid then
|
|
if tile.name == CoreData.world_concrete_tile then
|
|
multiplier = 1.5
|
|
elseif tile.name == 'cyan-refined-concrete' then
|
|
multiplier = 1.6
|
|
elseif tile.name == CoreData.walkway_tile then
|
|
multiplier = 1
|
|
elseif tile.name == 'orange-refined-concrete' then
|
|
multiplier = 0.5
|
|
elseif tile.name == CoreData.enemy_landing_tile then
|
|
multiplier = 0.3
|
|
elseif tile.name == CoreData.static_boat_floor then
|
|
multiplier = 0.1
|
|
end
|
|
end
|
|
end
|
|
if multiplier > 0 then
|
|
local timescale = 60*30 * Math.max((Balance.game_slowness_scale())^(2/3),0.8)
|
|
if memory.gourmet_recency_tick then
|
|
multiplier = multiplier *Math.clamp(0.2, 5, (1/5)^((memory.gourmet_recency_tick - game.tick)/(60*300)))
|
|
memory.gourmet_recency_tick = Math.max(memory.gourmet_recency_tick, game.tick - timescale*10) + timescale
|
|
else
|
|
multiplier = multiplier * 5
|
|
memory.gourmet_recency_tick = game.tick - timescale*10 + timescale
|
|
end
|
|
Public.class_ore_grant(player, 10 * multiplier, Balance.gourmet_ore_scaling_enabled)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function Public.lumberjack_bonus_items(give_table)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
if Math.random(Balance.every_nth_tree_gives_coins) == 1 then
|
|
local a = 12
|
|
give_table[#give_table + 1] = {name = 'coin', count = a}
|
|
memory.playtesting_stats.coins_gained_by_trees_and_rocks = memory.playtesting_stats.coins_gained_by_trees_and_rocks + a
|
|
elseif Math.random(2) == 1 then
|
|
if Math.random(5) == 1 then
|
|
give_table[#give_table + 1] = {name = 'copper-ore', count = 1}
|
|
else
|
|
give_table[#give_table + 1] = {name = 'iron-ore', count = 1}
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
local event = require 'utils.event'
|
|
event.add(defines.events.on_player_used_capsule, class_on_player_used_capsule)
|
|
|
|
return Public |