1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2025-12-26 00:42:20 +02:00
Files
RedMew/features/gui/experience.lua

721 lines
29 KiB
Lua
Raw Permalink Normal View History

-- This feature adds an experience leveling system based on ForceControl.lua
-- made by Linaori & SimonFlapse
-- modified by Valansch, grilledham, RedRafe
-- ======================================================= --
local config = require 'config'.experience
local Color = require 'resources.color_presets'
local Event = require 'utils.event'
local ForceControl = require 'features.force_control'
local Game = require 'utils.game'
local Global = require 'utils.global'
local Gui = require 'utils.gui'
2024-12-12 00:06:43 +01:00
local math = require 'utils.math'
local Retailer = require 'features.retailer'
2024-12-13 12:33:59 +01:00
local RS = require 'map_gen.shared.redmew_surface'
local ScoreTracker = require 'utils.score_tracker'
2024-12-13 12:51:22 +01:00
local Settings = require 'utils.redmew_settings'
local Toast = require 'features.gui.toast'
2018-12-29 12:39:20 -05:00
local Utils = require 'utils.core'
local Public = {}
local add_experience = ForceControl.add_experience
local add_experience_percentage = ForceControl.add_experience_percentage
local disable_item = Retailer.disable_item
local enable_item = Retailer.enable_item
local get_force_data = ForceControl.get_force_data
local remove_experience_percentage = ForceControl.remove_experience_percentage
local set_item = Retailer.set_item
local floor = math.floor
2024-12-12 23:54:10 +01:00
local insert = table.insert
local max = math.max
2024-12-12 23:54:10 +01:00
local min = math.min
2024-12-12 00:06:43 +01:00
local round_sig = math.round_sig
local gain_xp_color = Color.light_sky_blue
local lose_xp_color = Color.red
2024-12-18 10:51:19 +01:00
local notify_name = 'notify_experience_level_up'
local experience_lost_name = 'experience-lost'
2024-12-12 23:54:10 +01:00
local on_bonuses, off_bonuses = '▼ Bonuses', '▲ Bonuses'
local on_rewards, off_rewards = '▼ Rewards', '▲ Rewards'
2024-12-12 00:06:43 +01:00
local force_sounds = {}
2024-12-12 23:54:10 +01:00
local level_table = {}
local gui_toggled = {}
local mining_efficiency = { active_modifier = 0, research_modifier = 0, level_modifier = 0 }
local inventory_slots = { active_modifier = 0, research_modifier = 0, level_modifier = 0 }
local health_bonus = { active_modifier = 0, research_modifier = 0, level_modifier = 0 }
local character_reach = { active_modifier = 0, research_modifier = 0, level_modifier = 0 }
local crafting_speed = { active_modifier = 0, research_modifier = 0, level_modifier = 0 }
local running_speed = { active_modifier = 0, research_modifier = 0, level_modifier = 0 }
2024-12-12 00:06:43 +01:00
-- Prevents table lookup thousands of times
local rock_big_xp = config.XP['big-rock']
local rock_huge_xp = config.XP['huge-rock']
Global.register(
{
mining_efficiency = mining_efficiency,
inventory_slots = inventory_slots,
2024-12-12 00:06:43 +01:00
health_bonus = health_bonus,
2024-12-12 23:54:10 +01:00
force_sounds = force_sounds,
2024-12-18 10:43:27 +01:00
gui_toggled = gui_toggled,
level_table = level_table,
character_reach = character_reach,
crafting_speed = crafting_speed,
running_speed = running_speed,
},
function(tbl)
mining_efficiency = tbl.mining_efficiency
inventory_slots = tbl.inventory_slots
health_bonus = tbl.health_bonus
2024-12-12 00:06:43 +01:00
force_sounds = tbl.force_sounds
2024-12-12 23:54:10 +01:00
gui_toggled = tbl.gui_toggled
2024-12-18 10:43:27 +01:00
level_table = tbl.level_table
character_reach = tbl.character_reach
crafting_speed = tbl.crafting_speed
running_speed = tbl.running_speed
end
)
2024-12-18 10:51:19 +01:00
Settings.register(notify_name, Settings.types.boolean, true, 'experience.notify_caption_short')
2024-12-18 10:43:27 +01:00
ScoreTracker.register(experience_lost_name, { 'experience.score_experience_lost' }, '[img=item.artillery-targeting-remote]')
2024-12-12 00:06:43 +01:00
local global_to_show = storage.config.score.global_to_show
global_to_show[#global_to_show + 1] = experience_lost_name
-- == HELPERS ==============================================================
2024-12-12 00:06:43 +01:00
--[[
Given the parameters
a: difficulty_scale
b: xp_fine_tune
c: first_lvl_xp
The Level Up formula is defined as:
Experience(L) = aL^3 + bL^2 + (c-a-b)L + 1.15^(0.1L)
]]
---A function to calculate level caps (When to level up)
local level_up_formula = function(level_reached)
local difficulty_scale = floor(config.difficulty_scale)
2024-12-12 00:06:43 +01:00
local fine_tune = floor(config.xp_fine_tune)
local start_value = floor(config.first_lvl_xp)
local precision = floor(config.cost_precision)
local function formula(L)
local L2 = L * L
local L3 = L * L2
return floor(difficulty_scale * L3 + fine_tune * L2 + (start_value - difficulty_scale - fine_tune) * L + 1.15 ^ (0.1 * L))
end
2024-12-12 00:06:43 +01:00
local value = formula(level_reached + 1)
local lower_value = formula(level_reached)
2024-12-12 00:06:43 +01:00
return round_sig(value - lower_value, precision)
end
2024-12-12 23:54:10 +01:00
---Get experience requirement for a given level
---Primarily used for the Experience GUI to display total experience required to unlock a specific item
---@param level number a number specifying the level
---@return number required total experience to reach supplied level
local function calculate_level_xp(level)
if level_table[level] == nil then
local value
if level == 1 then
value = level_up_formula(level - 1)
else
value = level_up_formula(level - 1) + calculate_level_xp(level - 1)
end
insert(level_table, level, value)
end
return level_table[level]
end
---Get a percentage of required experience between a level and the next level
---@param level number a number specifying the current level
---@return number a percentage of the required experience to level up from one level to the other
local function percentage_of_level_req(level, percentage)
return level_up_formula(level) * percentage
end
2024-12-12 23:54:10 +01:00
---@param force LuaForce
local function play_level_up_sound(force)
2024-12-12 00:06:43 +01:00
if not config.sound then
return
end
if not helpers.is_valid_sound_path(config.sound.path or '') then
return
end
if force_sounds[force.index] and game.tick < force_sounds[force.index] then
return
end
force_sounds[force.index] = game.tick + (config.sound.duration or 20 * 60)
2024-12-13 12:51:22 +01:00
for _, player in pairs(force.connected_players) do
2024-12-18 10:51:19 +01:00
if Settings.get(player.index, notify_name) then
2024-12-13 12:51:22 +01:00
player.play_sound(config.sound)
end
end
end
2024-12-12 23:54:10 +01:00
---@param parent LuaGuiElement
---@param caption string
---@param element string|table
2024-12-12 00:06:43 +01:00
local function label_pair(parent, caption, element)
local flow = parent.add { type = 'flow', direction = 'horizontal' }
Gui.set_style(flow, { vertical_align = 'center' })
flow.add { type = 'label', style = 'semibold_caption_label', caption = caption .. ':' }
return (type(element) == 'table') and flow.add(element) or flow.add { type = 'label', caption = element }
end
2024-12-12 23:54:10 +01:00
---@param item table item data
local function get_item_tooltip(item)
return {
'',
'[font=var]Item: [/font]',
{'?', {'entity-name.'..item.name}, {'item-name.'..item.name}, {'equipment-name.'..item.name} },
'\n[font=var]Price: [/font]'..item.price..' [img=item/coin]',
'\n[font=var]Unlocked at: [/font]'..Utils.comma_value(calculate_level_xp(item.level))..' XP'
}
end
---@field buff table of buff config
---@field force LuaForce
---@field formula? function
---@field level_up? number
---@field modifier table of modifiers
---@field property string LuaForce::property to update
local function update_modifier(params)
local buff = params.buff
local force = params.force
local formula = params.formula or function(p) return p.buff.value end
local level_up = params.level_up or 0
local modifier = params.modifier
local property = params.property
if not buff or force[property] >= (buff.max or math.huge) then
return
end
if buff.level_interval and (level_up % buff.level_interval ~= 0) then
return
end
2024-12-12 23:54:10 +01:00
if level_up > 0 then
local value = formula(params)
if buff.double_level and (level_up % buff.double_level) == 0 then
value = 2 * value
end
modifier.level_modifier = modifier.level_modifier + value
end
-- remove the current buff
local old_modifier = force[property] - modifier.active_modifier
old_modifier = old_modifier >= 0 and old_modifier or 0
-- update the active modifier
modifier.active_modifier = modifier.research_modifier + modifier.level_modifier
-- add the new active modifier to the non-buffed modifier
force[property] = old_modifier + modifier.active_modifier
if buff.max then
force[property] = min(buff.max, force[property])
end
end
2024-12-12 00:06:43 +01:00
-- == GUI =====================================================================
2024-12-12 00:06:43 +01:00
local main_frame_name = Gui.uid_name()
local main_button_name = Gui.uid_name()
local bonuses_button_name = Gui.uid_name()
local rewards_list_button_name = Gui.uid_name()
2024-12-12 00:06:43 +01:00
Public.update_main_frame = function(player)
local frame = Gui.get_left_element(player, main_frame_name)
if not frame or not frame.valid then
return
end
2024-12-12 00:06:43 +01:00
local force_data = get_force_data('player')
local data = Gui.get_data(frame)
local current_level = force_data.current_level
data.level.caption = current_level
data.label.caption = Utils.comma_value(force_data.total_experience)
data.label.tooltip = { 'experience.gui_total_xp', Utils.comma_value(force_data.total_experience) }
data.progress.value = force_data.experience_percentage * 0.01
data.progress.tooltip = { 'experience.gui_progress_bar', floor(force_data.experience_percentage * 100) * 0.01 }
2024-12-12 23:54:10 +01:00
data.bonus_mining_speed.caption = '+ '..(player.force.manual_mining_speed_modifier * 100)..'%'
data.bonus_inventory_slot.caption = '+ '..player.force.character_inventory_slots_bonus
data.bonus_health_bonus.caption = '+ '..player.force.character_health_bonus..'%'
data.bonus_character_reach.caption = '+ '..(player.force.character_reach_distance_bonus)
data.bonus_crafting_speed.caption = '+ '..(player.force.manual_crafting_speed_modifier * 100)..'%'
data.bonus_running_speed.caption = '+ '..(player.force.character_running_speed_modifier * 100)..'%'
2024-12-12 00:06:43 +01:00
for _, row in pairs(data.reward_list.children) do
for _, item in pairs(row.children) do
if item.tags and item.tags.level then
item.style = (current_level >= item.tags.level) and 'green_slot' or 'yellow_slot_button'
item.style.size = 32
end
end
end
end
2024-12-12 00:06:43 +01:00
Public.get_main_frame = function(player)
local frame = Gui.get_left_element(player, main_frame_name)
2024-12-12 00:06:43 +01:00
if frame and frame.valid then
return Public.update_main_frame(player)
end
2024-12-12 00:06:43 +01:00
local data = {}
frame = Gui.add_left_element(player, { name = main_frame_name, type = 'frame', direction = 'vertical' })
2024-12-12 00:06:43 +01:00
Gui.set_style(frame, { maximal_width = 360 })
2024-12-12 00:06:43 +01:00
local canvas = frame
.add { type = 'flow', direction = 'vertical', style = 'vertical_flow' }
.add { type = 'frame', direction = 'vertical', style = 'inside_shallow_frame_packed' }
2024-12-12 00:06:43 +01:00
do -- Title
local subheader = canvas.add { type = 'frame', style = 'subheader_frame' }
Gui.set_style(subheader, { horizontally_stretchable = true })
2024-12-12 00:06:43 +01:00
local flow = subheader.add { type = 'flow', direction = 'horizontal' }
local label = flow.add({ type = 'label', caption = 'Experience', style = 'frame_title' })
Gui.set_style(label, { left_margin = 4 })
end
2024-12-12 00:06:43 +01:00
local sp = canvas.add { type = 'scroll-pane', vertical_scroll_policy = 'auto-and-reserve-space' }
Gui.set_style(sp, { padding = 12, maximal_height = 600 })
do -- Level
local content = sp.add { type = 'frame', direction = 'vertical', style = 'deep_frame_in_shallow_frame_for_description' }
content.add { type = 'label', style = 'tooltip_heading_label_category', caption = '★ XP' }
content.add { type = 'line', style = 'tooltip_category_line' }
data.level = label_pair(content, 'Level', '---')
data.label = label_pair(content, 'Total', '---')
data.progress = label_pair(content, 'Progress', { type = 'progressbar' })
end
do -- Bonuses
2024-12-12 23:54:10 +01:00
local toggled = gui_toggled[player.index].bonuses
local label = sp.add { type = 'label', style = 'bold_label', caption = toggled and on_bonuses or off_bonuses, name = bonuses_button_name, tooltip = 'Hide/Show bonuses' }
2024-12-12 00:06:43 +01:00
local deep = sp.add { type = 'frame', direction = 'vertical', style = 'deep_frame_in_shallow_frame_for_description' }
Gui.set_style(deep, { padding = 0, minimal_height = 4 })
local content = deep.add { type = 'flow', direction = 'vertical' }
Gui.set_style(content, { padding = 8 })
2024-12-12 23:54:10 +01:00
content.visible = toggled
2024-12-12 00:06:43 +01:00
Gui.set_data(label, { list = content })
local buffs = config.buffs
content.add { type = 'label', style = 'tooltip_heading_label_category', caption = '✔ Bonuses' }
content.add { type = 'line', style = 'tooltip_category_line' }
data.bonus_mining_speed = label_pair(content, 'Manual mining speed', '---')
data.bonus_inventory_slot = label_pair(content, 'Inventory slots', '---')
data.bonus_health_bonus = label_pair(content, 'Max health', '---')
data.bonus_character_reach = label_pair(content, 'Character reach', '---')
data.bonus_crafting_speed = label_pair(content, 'Crafting speed', '---')
data.bonus_running_speed = label_pair(content, 'Running speed', '---')
2024-12-12 00:06:43 +01:00
data.bonus_mining_speed.tooltip = { 'experience.gui_buff_mining', buffs.mining_speed.value, buffs.mining_speed.max * 100 }
data.bonus_inventory_slot.tooltip = { 'experience.gui_buff_inv', buffs.inventory_slot.value, buffs.inventory_slot.max }
data.bonus_health_bonus.tooltip = { 'experience.gui_buff_health', buffs.health_bonus.value, buffs.health_bonus.max }
data.bonus_character_reach.tooltip = { 'experience.gui_buff_character_reach', buffs.character_reach.value, buffs.character_reach.max }
data.bonus_crafting_speed.tooltip = { 'experience.gui_buff_crafting_speed', buffs.crafting_speed.value * 100, buffs.crafting_speed.max * 100 }
data.bonus_running_speed.tooltip = { 'experience.gui_buff_running_speed', buffs.running_speed.value * 100, buffs.running_speed.max * 100 }
2024-12-12 00:06:43 +01:00
end
do -- Rewards
2024-12-12 23:54:10 +01:00
local toggled = gui_toggled[player.index].rewards
local label = sp.add { type = 'label', style = 'bold_label', caption = toggled and on_rewards or off_rewards, name = rewards_list_button_name, tooltip = 'Hide/Show rewards' }
2024-12-12 00:06:43 +01:00
local deep = sp.add { type = 'frame', style = 'deep_frame_in_shallow_frame_for_description', direction = 'vertical' }
Gui.set_style(deep, { padding = 0, minimal_height = 4 })
local last = {}
local list = deep
.add { type = 'scroll-pane', vertical_scroll_policy = 'dont-show-but-allow-scrolling' }
.add { type = 'table', style = 'table_with_selection', column_count = 2 }
2024-12-12 23:54:10 +01:00
list.visible = toggled
2024-12-12 00:06:43 +01:00
Gui.set_data(label, { list = list })
for _, item in pairs(config.unlockables) do
if item.level ~= last.level then
local row = list.add { type = 'flow', direction = 'horizontal' }
Gui.set_style(row, { vertical_align = 'center' })
local level = row.add { type = 'sprite-button', caption = item.level, style = 'transparent_slot', ignored_by_interaction = true }
Gui.set_style(level, { size = 24, font_color = { 255, 255, 255 } })
Gui.add_pusher(row)
last.row = row
end
local button = last.row.add {
type = 'sprite-button',
sprite = 'item.'..item.name,
number = item.price,
style = 'slot_button',
2024-12-12 23:54:10 +01:00
tooltip = get_item_tooltip(item),
2024-12-12 00:06:43 +01:00
tags = { level = item.level }
}
Gui.set_style(button, { size = 32 })
last.level = item.level
end
data.reward_list = list
end
Gui.set_data(frame, data)
2024-12-12 00:06:43 +01:00
Public.update_main_frame(player)
end
2024-12-12 00:06:43 +01:00
Public.toggle_main_button = function(player)
local frame = Gui.get_left_element(player, main_frame_name)
local button = Gui.get_top_element(player, main_button_name)
if frame then
button.toggled = false
Gui.destroy(frame)
else
button.toggled = true
Public.get_main_frame(player)
end
2024-12-12 00:06:43 +01:00
end
2024-12-12 00:06:43 +01:00
---Toggle the player's experience main window, required for Cutscene controller module
Public.toggle = function(event)
Public.toggle_main_button(event.player)
end
Gui.allow_player_to_toggle_top_element_visibility(main_button_name)
2024-12-12 00:06:43 +01:00
Gui.on_click(main_button_name, Public.toggle)
Gui.on_custom_close(main_frame_name, Public.toggle)
Gui.on_click(bonuses_button_name, function(event)
local list = Gui.get_data(event.element).list
list.visible = not list.visible
2024-12-12 23:54:10 +01:00
event.element.caption = list.visible and on_bonuses or off_bonuses
gui_toggled[event.player_index].bonuses = list.visible
2024-12-12 00:06:43 +01:00
end)
Gui.on_click(rewards_list_button_name, function(event)
local list = Gui.get_data(event.element).list
list.visible = not list.visible
2024-12-12 23:54:10 +01:00
event.element.caption = list.visible and on_rewards or off_rewards
gui_toggled[event.player_index].rewards = list.visible
end)
-- == EVENTS ==================================================================
---Awards experience when a rock has been mined (increases by 1 XP every 5th level)
local function on_player_mined_entity(event)
local entity = event.entity
local name = entity.name
local player_index = event.player_index
2019-05-17 15:57:01 +01:00
local force = game.get_player(player_index).force
local level = get_force_data(force).current_level
local exp = 0
2024-10-26 11:29:52 +02:00
if name == 'big-rock' then
exp = rock_big_xp + floor(level / 5)
elseif name == 'huge-rock' then
2018-11-25 12:53:05 +01:00
exp = rock_huge_xp + floor(level / 5)
end
if exp == 0 then
return
end
2024-12-12 00:06:43 +01:00
local text = { '', '[img=entity/' .. name .. '] ', { 'experience.float_xp_gained_mine', exp } }
local player = game.get_player(player_index)
player.create_local_flying_text { text = text, color = gain_xp_color, position = player.position }
add_experience(force, exp)
end
---Awards experience when a research has finished, based on ingredient cost of research
local function on_research_finished(event)
local research = event.research
local force = research.force
local exp
if research.research_unit_count_formula ~= nil then
local force_data = get_force_data(force)
exp = percentage_of_level_req(force_data.current_level, config.XP['infinity-research'])
else
local award_xp = 0
for _, ingredient in pairs(research.research_unit_ingredients) do
local name = ingredient.name
local reward = config.XP[name]
award_xp = award_xp + reward
end
exp = award_xp * research.research_unit_count
end
2024-12-12 00:06:43 +01:00
local text = { '', '[img=item/automation-science-pack] ', { 'experience.float_xp_gained_research', exp } }
2024-12-13 12:33:59 +01:00
Game.create_local_flying_text { surface = RS.get_surface_name(), text = text, color = gain_xp_color, create_at_cursor = true }
add_experience(force, exp)
local current_modifier = mining_efficiency.research_modifier
local new_modifier = force.mining_drill_productivity_bonus * config.mining_speed_productivity_multiplier * 0.5
if (current_modifier == new_modifier) then
-- something else was researched
return
end
mining_efficiency.research_modifier = new_modifier
inventory_slots.research_modifier = force.mining_drill_productivity_bonus * 50 -- 1 per level
Public.update_inventory_slots(force, 0)
Public.update_mining_speed(force, 0)
Public.update_health_bonus(force, 0)
Public.update_character_reach_bonus(force, 0)
Public.update_crafting_speed_bonus(force, 0)
Public.update_running_speed_bonus(force, 0)
end
---Awards experience when a rocket has been launched based on percentage of total experience
local function on_rocket_launched(event)
local force = event.rocket.force
2024-12-13 12:33:59 +01:00
local silo_surface = event.rocket_silo and event.rocket_silo.surface
local exp = add_experience_percentage(force, config.XP['rocket_launch'], nil, config.XP['rocket_launch_max'])
2024-12-12 00:06:43 +01:00
local text = { '', '[img=item/satellite] ', { 'experience.float_xp_gained_rocket', exp } }
2024-12-13 12:33:59 +01:00
Game.create_local_flying_text { surface = silo_surface and silo_surface.index, text = text, color = gain_xp_color, create_at_cursor = true }
end
---Awards experience when a player kills an enemy, based on type of enemy
2018-12-22 00:37:44 +01:00
local function on_entity_died(event)
local entity = event.entity
local force = event.force
local cause = event.cause
local entity_name = entity.name
-- For bot mining and turrets
2019-05-02 17:02:53 +02:00
if not cause or not cause.valid or cause.type ~= 'character' then
local exp = 0
local floating_text_position
-- stuff killed by the player force, but not the player
if force and force.name == 'player' then
if cause and (cause.name == 'artillery-turret' or cause.name == 'gun-turret' or cause.name == 'laser-turret' or cause.name == 'flamethrower-turret') then
exp = config.XP['enemy_killed'] * (config.alien_experience_modifiers[entity_name] or 1)
floating_text_position = cause.position
else
local level = get_force_data(force).current_level
2024-10-26 11:29:52 +02:00
if entity_name == 'big-rock' then
2018-11-30 20:30:50 +01:00
exp = floor((rock_big_xp + level * 0.2) * 0.5)
elseif entity_name == 'huge-rock' then
2018-11-30 20:30:50 +01:00
exp = floor((rock_huge_xp + level * 0.2) * 0.5)
end
2018-12-01 21:44:01 +01:00
floating_text_position = entity.position
end
end
if exp > 0 then
Game.create_local_flying_text({
surface = entity.surface,
position = floating_text_position,
2024-12-12 00:06:43 +01:00
text = { '', '[img=entity/' .. entity_name .. '] ', { 'experience.float_xp_gained_kill', exp } },
color = gain_xp_color,
})
add_experience(force, exp)
end
2018-11-26 20:23:53 +01:00
return
end
if entity.force.name ~= 'enemy' then
return
end
local exp = config.XP['enemy_killed'] * (config.alien_experience_modifiers[entity.name] or 1)
cause.player.create_local_flying_text {
position = cause.player.position,
2024-12-12 00:06:43 +01:00
text = { '', '[img=entity/' .. entity_name .. '] ', { 'experience.float_xp_gained_kill', exp } },
color = gain_xp_color,
}
add_experience(force, exp)
end
---Deducts experience when a player respawns, based on a percentage of total experience
local function on_player_respawned(event)
2019-05-17 15:57:01 +01:00
local player = game.get_player(event.player_index)
local exp = remove_experience_percentage(player.force, config.XP['death-penalty'], 50)
2024-12-12 00:06:43 +01:00
local text = { '', '[img=entity.character]', { 'experience.float_xp_drain', exp } }
game.print({ 'experience.player_drained_xp', player.name, exp }, { color = lose_xp_color })
2024-12-02 14:28:20 +01:00
Game.create_local_flying_text { surface = player.surface, text = text, color = lose_xp_color, create_at_cursor = true }
ScoreTracker.change_for_global(experience_lost_name, exp)
end
local function on_player_created(event)
local player = game.get_player(event.player_index)
if not (player and player.valid) then
return
end
Gui.add_top_element(player, {
name = main_button_name,
type = 'sprite-button',
sprite = 'entity/market',
2024-12-12 00:06:43 +01:00
tooltip = { 'experience.gui_experience_button_tip' },
})
2024-12-12 23:54:10 +01:00
gui_toggled[player.index] = {
bonuses = true,
rewards = false,
}
end
2024-12-12 00:06:43 +01:00
---Updates the experience progress gui for every player that has it open
local function on_nth_tick()
for _, player in pairs(game.connected_players) do
Public.update_main_frame(player)
end
2024-12-12 00:06:43 +01:00
-- Resets buffs if they have been set to 0
local force = game.forces.player
Public.update_inventory_slots(force, 0)
Public.update_mining_speed(force, 0)
Public.update_health_bonus(force, 0)
Public.update_character_reach_bonus(force, 0)
Public.update_crafting_speed_bonus(force, 0)
Public.update_running_speed_bonus(force, 0)
2024-12-12 00:06:43 +01:00
end
local function on_init()
-- Adds the 'player' force to participate in the force control system.
local force = game.forces.player
ForceControl.register_force(force)
table.sort(config.unlockables, function(a, b) return a.level < b.level end)
local force_name = force.name
for _, prototype in pairs(config.unlockables) do
set_item(force_name, prototype)
end
Public.update_market_contents(force)
end
---A function that will be executed at every level up
local function on_level_reached(level_reached, force)
Toast.toast_force(force, 10, { 'experience.toast_new_level', level_reached })
Public.update_inventory_slots(force, level_reached)
Public.update_mining_speed(force, level_reached)
Public.update_health_bonus(force, level_reached)
Public.update_character_reach_bonus(force, level_reached)
Public.update_crafting_speed_bonus(force, level_reached)
Public.update_running_speed_bonus(force, level_reached)
2024-12-12 00:06:43 +01:00
Public.update_market_contents(force)
2024-12-12 23:54:10 +01:00
play_level_up_sound(force)
2024-12-12 00:06:43 +01:00
end
-- == EXPERIENCE ==============================================================
---Updates the market contents based on the current level.
---@param force LuaForce the force which the unlocking requirement should be based of
Public.update_market_contents = function(force)
local current_level = get_force_data(force).current_level
local force_name = force.name
for _, prototype in pairs(config.unlockables) do
local prototype_level = prototype.level
if current_level < prototype_level then
2024-12-12 00:06:43 +01:00
disable_item(force_name, prototype.name, { 'experience.market_disabled', prototype_level })
else
enable_item(force_name, prototype.name)
end
end
end
---Updates a forces manual mining speed modifier. By removing active modifiers and re-adding
---@param force LuaForce the force of which will be updated
---@param level_up? number a level if updating as part of a level up
Public.update_mining_speed = function(force, level_up)
2024-12-12 23:54:10 +01:00
update_modifier{
buff = config.buffs['mining_speed'],
force = force,
formula = function(params)
local level = get_force_data(params.force).current_level
return 0.01 * floor(max(params.buff.value, 24 * 0.9 ^ (level ^ 0.5)))
end,
level_up = level_up,
modifier = mining_efficiency,
property = 'manual_mining_speed_modifier',
}
end
---Updates a forces inventory slots. By removing active modifiers and re-adding
---@param force LuaForce the force of which will be updated
---@param level_up? number a level if updating as part of a level up
Public.update_inventory_slots = function(force, level_up)
2024-12-12 23:54:10 +01:00
update_modifier{
buff = config.buffs['inventory_slot'],
force = force,
level_up = level_up,
modifier = inventory_slots,
property = 'character_inventory_slots_bonus',
}
end
---Updates a forces health bonus. By removing active modifiers and re-adding
---@param force LuaForce the force of which will be updated
---@param level_up? number a level if updating as part of a level up
Public.update_health_bonus = function(force, level_up)
2024-12-12 23:54:10 +01:00
update_modifier{
buff = config.buffs['health_bonus'],
force = force,
level_up = level_up,
modifier = health_bonus,
property = 'character_health_bonus',
}
end
---Updates a forces reach bonus. By removing active modifiers and re-adding
---@param force LuaForce the force of which will be updated
---@param level_up? number a level if updating as part of a level up
Public.update_character_reach_bonus = function(force, level_up)
update_modifier{
buff = config.buffs['character_reach'],
force = force,
level_up = level_up,
modifier = character_reach,
property = 'character_reach_distance_bonus',
}
for _, reach_property in pairs({
'character_build_distance_bonus',
'character_item_drop_distance_bonus',
'character_reach_distance_bonus',
'character_resource_reach_distance_bonus',
'character_item_pickup_distance_bonus'
}) do
force[reach_property] = force.character_reach_distance_bonus
end
end
---Updates a forces crafting speed bonus. By removing active modifiers and re-adding
---@param force LuaForce the force of which will be updated
---@param level_up? number a level if updating as part of a level up
Public.update_crafting_speed_bonus = function(force, level_up)
update_modifier{
buff = config.buffs['crafting_speed'],
force = force,
level_up = level_up,
modifier = crafting_speed,
property = 'manual_crafting_speed_modifier',
}
end
---Updates a forces running speed bonus. By removing active modifiers and re-adding
---@param force LuaForce the force of which will be updated
---@param level_up? number a level if updating as part of a level up
Public.update_running_speed_bonus = function(force, level_up)
update_modifier{
buff = config.buffs['running_speed'],
force = force,
level_up = level_up,
modifier = running_speed,
property = 'character_running_speed_modifier',
}
end
-- ============================================================================
2024-12-12 00:06:43 +01:00
Event.on_init(on_init)
Event.add(defines.events.on_player_mined_entity, on_player_mined_entity)
Event.add(defines.events.on_research_finished, on_research_finished)
Event.add(defines.events.on_rocket_launched, on_rocket_launched)
Event.add(defines.events.on_player_respawned, on_player_respawned)
Event.add(defines.events.on_entity_died, on_entity_died)
Event.add(defines.events.on_player_created, on_player_created)
Event.on_nth_tick(61, on_nth_tick)
local ForceControlBuilder = ForceControl.register(level_up_formula)
ForceControlBuilder.register_on_every_level(on_level_reached)
return Public