diff --git a/control.lua b/control.lua index 6bf3d122..41c13190 100644 --- a/control.lua +++ b/control.lua @@ -13,17 +13,7 @@ require 'utils.utils' require 'utils.pause_game' require 'utils.table' require 'utils.whisper_notice' -require 'utils.datastore.server_ups_data' -require 'utils.datastore.current_time_data' -require 'utils.datastore.color_data' -require 'utils.datastore.session_data' -require 'utils.datastore.jail_data' -require 'utils.datastore.quickbar_data' -require 'utils.datastore.warning_on_join_data' -require 'utils.datastore.message_on_join_data' -require 'utils.datastore.player_tag_data' -require 'utils.datastore.supporters' -require 'utils.datastore.banhandler' +require 'utils.datastore.init' require 'utils.chatbot' require 'utils.commands' require 'utils.antigrief' @@ -35,14 +25,7 @@ require 'modules.inserter_drops_pickup' require 'modules.autostash' require 'modules.blueprint_requesting' -require 'utils.gui' -require 'utils.gui.player_list' -require 'utils.gui.admin' -require 'utils.gui.group' -require 'utils.gui.score' -require 'utils.gui.config' -require 'utils.gui.poll' -require 'utils.gui.server_select' +require 'utils.gui.init' require 'utils.freeplay' require 'utils.remote_chunks' diff --git a/locale/en/mtn_fortress_v3.cfg b/locale/en/mtn_fortress_v3.cfg index 752dd7ad..a4e7ec71 100644 --- a/locale/en/mtn_fortress_v3.cfg +++ b/locale/en/mtn_fortress_v3.cfg @@ -148,9 +148,12 @@ linked=[font=default-bold]Convert chests to linked: [/font] linked_static=[font=default-bold]Purchase linked chests: [/font] mystical_chest=[font=default-bold]Feeding the hungry chest: [/font] enemies_killed=[font=default-bold]Enemies killed: [/font] +enemies_killed_type=[font=default-bold]Enemies killed (__1__): [/font] +crafted_items=[font=default-bold]Handcraft item (__1__): [/font] +cast_spell=[font=default-bold]Cast spell (__1__): [/font] +launch_item=[font=default-bold]Launch item __1__ to orbit: [/font] launch_rockets=[font=default-bold]Rockets launched: [/font] -rocks_mined=[font=default-bold]Rocks mined: [/font] -trees_mined=[font=default-bold]Trees mined: [/font] +minerals_mined=[font=default-bold]Minerals mined: [/font] production=[font=default-bold]Produce the following items: [/font] production_single=[font=default-bold]Produce the following item: [/font] market_spent=[font=default-bold]Spend coins in market: [/font] @@ -158,14 +161,15 @@ research=[font=default-bold]Research __1__: [/font] season_tooltip=Whenever a new season starts, all buffs are reset.\nGerkiz tries to add new content for each season that starts.\nSeason resets in __1__ days. rounds_survived_tooltip=Winning the game increases this number by 1.\nThis number resets as of now every 2 months. -buff_tooltip=Each buff that is given to the players is listed here.\nHover over the icon to the right to view the buffs. +buff_tooltip=Each buff that is given to the players is listed here.\nClick the icon to the right to view the buffs. +buff_tooltip_click=Click the icon view the buffs. zone_tooltip=Complete this objective by breaching/moving forward until you've reached the given zone. wave_tooltip=Complete this objective by surviving until the given wave. linked_tooltip=Complete this objective by converting the given amount of chests to linked chests. linked_static_tooltip=Complete this objective by purchasing the given amount of linked chests. production_tooltip=Complete this objective by producing the given item(s). time_until_attack_tooltip=Time in either minutes or seconds until the biters attack. -generic_tooltip=Complete this and this objective will be marked as complete. +generic_tooltip=Complete this to mark the objective as complete. win_conditions_tooltip=In order to win the game, you must complete all these objectives that are listed below! [img=utility/force_editor_icon] tooltip_failed=You've failed to complete this objective. [img=utility/not_available] tooltip_not_completed=This objective has not been completed. [img=utility/not_available] diff --git a/locale/en/rpg.cfg b/locale/en/rpg.cfg index 2d5fc730..ee938a8a 100644 --- a/locale/en/rpg.cfg +++ b/locale/en/rpg.cfg @@ -147,6 +147,8 @@ distractor=Distractor Capsule defender=Defender Capsule destroyer=Destroyer Capsule warp=Warp Gate +mark_spot=X Marks The Spot +tidal_wave=Tidal Wave pointy_explosives=Detonate Chest repair_aoe=Repair AOE charge=Charge diff --git a/maps/mountain_fortress_v3/biters_yield_coins.lua b/maps/mountain_fortress_v3/biters_yield_coins.lua index 8a5ac83f..260904dd 100644 --- a/maps/mountain_fortress_v3/biters_yield_coins.lua +++ b/maps/mountain_fortress_v3/biters_yield_coins.lua @@ -2,6 +2,7 @@ local Event = require 'utils.event' local Public = require 'maps.mountain_fortress_v3.table' local RPG = require 'modules.rpg.main' local BiterHealthBooster = require 'modules.biter_health_booster_v2' +local StatData = require 'utils.datastore.statistics' local insert = table.insert local floor = math.floor local random = math.random @@ -120,9 +121,15 @@ local function on_entity_died(event) if forest_zone then if random(1, 12) == 1 then player.insert({name = 'coin', count = coin_count}) + if p then + StatData.get_data(p.index):increase('coins', coin_count) + end end else player.insert({name = 'coin', count = coin_count}) + if p then + StatData.get_data(p.index):increase('coins', coin_count) + end end end end diff --git a/maps/mountain_fortress_v3/charging_station.lua b/maps/mountain_fortress_v3/charging_station.lua index 91ee7578..4417c098 100644 --- a/maps/mountain_fortress_v3/charging_station.lua +++ b/maps/mountain_fortress_v3/charging_station.lua @@ -9,14 +9,15 @@ local Color = require 'utils.color_presets' local Public = {} local module_name = '[color=blue][Charging station][/color] ' +local charging_station_name = Gui.uid_name() local function draw_charging_gui(player, activate_custom_buttons) local button = - player.gui.top['charging_station'] or + player.gui.top[charging_station_name] or player.gui.top.add( { type = 'sprite-button', - name = 'charging_station', + name = charging_station_name, sprite = 'item/battery-mk2-equipment', tooltip = { 'modules.charging_station_tooltip' @@ -73,6 +74,12 @@ local function charge(player) if not grid or not grid.valid then return player.print(module_name .. 'No valid armor to charge was found.', Color.warning) end + + local ents = player.surface.find_entities_filtered {name = 'accumulator', force = player.force, position = player.position, radius = 13} + if not ents or not next(ents) then + return player.print(module_name .. 'No accumulators nearby.', Color.warning) + end + local equip = grid.equipment for _, piece in pairs(equip) do if piece.valid and piece.generator_power == 0 then @@ -104,7 +111,7 @@ local function on_player_joined_game(event) BottomFrame.add_inner_frame( { player = player, - element_name = 'charging_station', + element_name = charging_station_name, tooltip = { 'modules.charging_station_tooltip' }, @@ -114,29 +121,24 @@ local function on_player_joined_game(event) end end -local function on_gui_click(event) - if not event then - return - end - if not event.element then - return - end - if not event.element.valid then - return - end - if event.element.name == 'charging_station' then - local player = game.players[event.player_index] +Gui.on_click( + charging_station_name, + function(event) + local player = game.get_player(event.player_index) + if not player or not player.valid then + return + end + local is_spamming = SpamProtection.is_spamming(player, nil, 'Charging Station Gui Click') if is_spamming then return end + charge(player) - return end -end +) Event.add(defines.events.on_player_joined_game, on_player_joined_game) -Event.add(defines.events.on_gui_click, on_gui_click) Event.add( BottomFrame.events.bottom_quickbar_location_changed, diff --git a/maps/mountain_fortress_v3/commands.lua b/maps/mountain_fortress_v3/commands.lua index a055abb1..48ac8451 100644 --- a/maps/mountain_fortress_v3/commands.lua +++ b/maps/mountain_fortress_v3/commands.lua @@ -6,7 +6,7 @@ local Collapse = require 'modules.collapse' local WD = require 'modules.wave_defense.table' local Discord = require 'utils.discord_handler' local mapkeeper = '[color=blue]Mapkeeper:[/color]' -local scenario_name = 'Mtn Fortress' +local scenario_name = Public.scenario_name commands.add_command( 'scenario', diff --git a/maps/mountain_fortress_v3/entities.lua b/maps/mountain_fortress_v3/entities.lua index 423235cf..dec6b5de 100644 --- a/maps/mountain_fortress_v3/entities.lua +++ b/maps/mountain_fortress_v3/entities.lua @@ -14,6 +14,9 @@ local Diff = require 'modules.difficulty_vote_by_amount' local format_number = require 'util'.format_number local RPG_Progression = require 'utils.datastore.rpg_data' local WD = require 'modules.wave_defense.table' +local scenario_name = Public.scenario_name +local StatData = require 'utils.datastore.statistics' +StatData.add_normalize('coins', 'Coins collected'):set_tooltip('The amount of coins the player has collected through mining/killed enemies.') local random = math.random local floor = math.floor @@ -470,8 +473,12 @@ local function give_coin(player) if coin_amount >= 1 then if coin_override then player.insert({name = 'coin', count = coin_override}) + StatData.get_data(player):increase('coins', coin_override) else + ---@diagnostic disable-next-line: param-type-mismatch player.insert({name = 'coin', count = random(1, coin_amount)}) + + StatData.get_data(player):increase('coins', coin_amount) end end end @@ -1230,7 +1237,7 @@ local function show_mvps(player) miners_label_text.style.font_color = {r = 0.33, g = 0.66, b = 0.9} local sent_to_discord = Public.get('sent_to_discord') - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) if not sent_to_discord and server_name_matches then local message = { @@ -1704,7 +1711,7 @@ local on_player_or_robot_built_tile = function(event) if not tiles then return end - for k, v in pairs(tiles) do + for _, v in pairs(tiles) do local old_tile = v.old_tile if old_tile.name == 'black-refined-concrete' then surface.set_tiles({{name = 'black-refined-concrete', position = v.position}}, true) diff --git a/maps/mountain_fortress_v3/functions.lua b/maps/mountain_fortress_v3/functions.lua index 0c772436..3ff24c23 100644 --- a/maps/mountain_fortress_v3/functions.lua +++ b/maps/mountain_fortress_v3/functions.lua @@ -30,6 +30,8 @@ local this = { magic_fluid_crafters = {index = 1}, art_table = {index = 1}, editor_mode = {}, + techs = {}, + limit_types = {}, starting_items = { ['pistol'] = { count = 1 @@ -1342,6 +1344,7 @@ function Public.on_player_joined_game(event) local death_message = ({'main.death_mode_warning'}) Alert.alert_player(player, 15, death_message) end + player.clear_items_inside() for item, data in pairs(this.starting_items) do player.insert({name = item, count = data.count}) end @@ -1793,12 +1796,12 @@ function Public.equip_players(starting_items, recreate) if player.character and player.character.valid then player.character.destroy() end + player.clear_items_inside() if player.connected then if not player.character then player.set_controller({type = defines.controllers.god}) player.create_character() end - player.clear_items_inside() Modifiers.update_player_modifiers(player) if not recreate then starting_items = starting_items or this.starting_items @@ -1821,6 +1824,8 @@ function Public.reset_func_table() this.refill_turrets = {index = 1} this.magic_crafters = {index = 1} this.magic_fluid_crafters = {index = 1} + this.techs = {} + this.limit_types = {} this.starting_items = { ['pistol'] = { count = 1 diff --git a/maps/mountain_fortress_v3/gui.lua b/maps/mountain_fortress_v3/gui.lua index 8a03fddb..05f7e2fd 100644 --- a/maps/mountain_fortress_v3/gui.lua +++ b/maps/mountain_fortress_v3/gui.lua @@ -25,6 +25,22 @@ local function validate_entity(entity) return true end +local function get_top_frame(player) + if Gui.get_mod_gui_top_frame() then + return Gui.get_button_flow(player)[main_frame_name] + else + return player.gui.top[main_frame_name] + end +end + +local function get_top_frame_custom(player, name) + if Gui.get_mod_gui_top_frame() then + return Gui.get_button_flow(player)[name] + else + return player.gui.top[name] + end +end + local function validate_player(player) if not player then return false @@ -45,22 +61,44 @@ local function validate_player(player) end local function create_button(player) - local b = - player.gui.top.add( - { - type = 'sprite-button', - name = main_button_name, - sprite = 'item/dummy-steel-axe', - tooltip = 'Shows statistics!', - style = Gui.button_style - } - ) - b.style.minimal_height = 38 - b.style.maximal_height = 38 + if Gui.get_mod_gui_top_frame() then + local b = + Gui.add_mod_button( + player, + { + type = 'sprite-button', + name = main_button_name, + sprite = 'item/dummy-steel-axe', + tooltip = 'Shows statistics!', + style = Gui.button_style + } + ) + if b then + b.style.font_color = {165, 165, 165} + b.style.font = 'heading-3' + b.style.minimal_height = 36 + b.style.maximal_height = 36 + b.style.minimal_width = 40 + b.style.padding = -2 + end + else + local b = + player.gui.top.add( + { + type = 'sprite-button', + name = main_button_name, + sprite = 'item/dummy-steel-axe', + tooltip = 'Shows statistics!', + style = Gui.button_style + } + ) + b.style.minimal_height = 38 + b.style.maximal_height = 38 + end end local function spectate_button(player) - if player.gui.top[spectate_button_name] then + if get_top_frame_custom(player, spectate_button_name) then return end @@ -68,29 +106,68 @@ local function spectate_button(player) return end - local b = - player.gui.top.add { - type = 'sprite-button', - name = spectate_button_name, - sprite = 'utility/ghost_time_to_live_modifier_icon', - tooltip = 'Spectate!\nThis will kill your character.', - style = Gui.button_style - } + if Gui.get_mod_gui_top_frame() then + local b = + Gui.add_mod_button( + player, + { + type = 'sprite-button', + name = spectate_button_name, + sprite = 'utility/ghost_time_to_live_modifier_icon', + tooltip = 'Spectate!\nThis will kill your character.', + style = Gui.button_style + } + ) + if b then + b.style.font_color = {165, 165, 165} + b.style.font = 'heading-3' + b.style.minimal_height = 36 + b.style.maximal_height = 36 + b.style.minimal_width = 40 + b.style.padding = -2 + end + else + local b = + player.gui.top.add { + type = 'sprite-button', + name = spectate_button_name, + sprite = 'utility/ghost_time_to_live_modifier_icon', + tooltip = 'Spectate!\nThis will kill your character.', + style = Gui.button_style + } - b.style.maximal_height = 38 + b.style.maximal_height = 38 + end end local function create_main_frame(player) local label local line - if player.gui.top['wave_defense'] then - player.gui.top['wave_defense'].visible = true + if get_top_frame_custom(player, 'wave_defense') then + get_top_frame_custom(player, 'wave_defense').visible = true end - local frame = player.gui.top.add({type = 'frame', name = main_frame_name, style = 'finished_game_subheader_frame'}) - frame.location = {x = 1, y = 40} - frame.style.minimal_height = 38 - frame.style.maximal_height = 38 + local frame + + if Gui.get_mod_gui_top_frame() then + frame = + Gui.add_mod_button( + player, + { + type = 'frame', + name = main_frame_name, + style = 'finished_game_subheader_frame' + } + ) + frame.location = {x = 1, y = 38} + frame.style.minimal_height = 36 + frame.style.maximal_height = 36 + else + frame = player.gui.top.add({type = 'frame', name = main_frame_name, style = 'finished_game_subheader_frame'}) + frame.location = {x = 1, y = 40} + frame.style.minimal_height = 38 + frame.style.maximal_height = 38 + end label = frame.add({type = 'label', caption = ' ', name = 'label'}) label.style.font_color = {r = 0.88, g = 0.88, b = 0.88} @@ -166,17 +243,33 @@ local function create_main_frame(player) end local function hide_all_gui(player) - for _, child in pairs(player.gui.top.children) do - if child.name ~= spectate_button_name and child.name ~= 'minimap_button' and child.name ~= 'wave_defense' then - child.visible = false + if Gui.get_mod_gui_top_frame() then + for _, child in pairs(player.gui.top.mod_gui_top_frame.mod_gui_inner_frame.children) do + if child.name ~= spectate_button_name and child.name ~= 'minimap_button' and child.name ~= 'wave_defense' then + child.visible = false + end + end + else + for _, child in pairs(player.gui.top.children) do + if child.name ~= spectate_button_name and child.name ~= 'minimap_button' and child.name ~= 'wave_defense' then + child.visible = false + end end end end local function show_all_gui(player) - for _, child in pairs(player.gui.top.children) do - if child.name ~= spectate_button_name and child.name ~= 'minimap_button' then - child.visible = true + if Gui.get_mod_gui_top_frame() then + for _, child in pairs(player.gui.top.mod_gui_top_frame.mod_gui_inner_frame.children) do + if child.name ~= spectate_button_name and child.name ~= 'minimap_button' then + child.visible = true + end + end + else + for _, child in pairs(player.gui.top.children) do + if child.name ~= spectate_button_name and child.name ~= 'minimap_button' then + child.visible = true + end end end end @@ -187,11 +280,11 @@ local function on_player_joined_game(event) return end - if not player.gui.top[spectate_button_name] then + if not get_top_frame_custom(player, spectate_button_name) then spectate_button(player) end - if not player.gui.top[main_button_name] then + if not get_top_frame_custom(player, main_button_name) then create_button(player) end end @@ -209,19 +302,19 @@ local function changed_surface(player) end local wagon_surface = icw_locomotive.surface - local main_toggle_button = player.gui.top[main_toggle_button_name] - local info = player.gui.top[main_button_name] - local wd = player.gui.top['wave_defense'] - local spectate = player.gui.top[spectate_button_name] - local minimap_button = player.gui.top['minimap_button'] - local rpg_b = player.gui.top[rpg_button] - local poll_b = player.gui.top[poll_button] + local main_toggle_button = get_top_frame_custom(player, main_toggle_button_name) + local info = get_top_frame_custom(player, main_button_name) + local wd = get_top_frame_custom(player, 'wave_defense') + local spectate = get_top_frame_custom(player, spectate_button_name) + local minimap_button = get_top_frame_custom(player, 'minimap_button') + local rpg_b = get_top_frame_custom(player, rpg_button) + local poll_b = get_top_frame_custom(player, poll_button) local rpg_f = player.gui.screen[rpg_frame] local rpg_s = player.gui.screen[rpg_settings] - local diff = player.gui.top[Difficulty.top_button_name] - local charging = player.gui.top['charging_station'] + local diff = get_top_frame_custom(player, Difficulty.top_button_name) + local charging = get_top_frame_custom(player, 'charging_station') local charging_frame = BottomFrame.get_section(player, 'charging_station') - local frame = player.gui.top[main_frame_name] + local frame = get_top_frame(player) local spell_gui_frame_name = RPG.spell_gui_frame_name local spell_cast_buttons = player.gui.screen[spell_gui_frame_name] @@ -326,7 +419,7 @@ local function changed_surface(player) info.sprite = 'utility/map' info.visible = true end - if player.gui.top[main_frame_name] then + if get_top_frame(player) then if frame then frame.visible = false return @@ -391,10 +484,10 @@ local function on_gui_click(event) end return end - if player.gui.top[main_frame_name] then - local info = player.gui.top[main_frame_name] - local wd = player.gui.top['wave_defense'] - local diff = player.gui.top[Difficulty.top_button_name] + if get_top_frame(player) then + local info = get_top_frame(player) + local wd = get_top_frame_custom(player, 'wave_defense') + local diff = get_top_frame_custom(player, Difficulty.top_button_name) if info and info.visible then if wd then @@ -453,14 +546,14 @@ local function enable_guis(event) end local main_toggle_button_name = Gui.main_toggle_button_name - local main_toggle_button = player.gui.top[main_toggle_button_name] + local main_toggle_button = get_top_frame_custom(player, main_toggle_button_name) local rpg_button = RPG.draw_main_frame_name - local info = player.gui.top[main_button_name] - local wd = player.gui.top['wave_defense'] - local spectate = player.gui.top[spectate_button_name] - local rpg_b = player.gui.top[rpg_button] - local diff = player.gui.top[Difficulty.top_button_name] - local charging = player.gui.top['charging_station'] + local info = get_top_frame_custom(player, main_button_name) + local wd = get_top_frame_custom(player, 'wave_defense') + local spectate = get_top_frame_custom(player, spectate_button_name) + local rpg_b = get_top_frame_custom(player, rpg_button) + local diff = get_top_frame_custom(player, Difficulty.top_button_name) + local charging = get_top_frame_custom(player, 'charging_station') local charging_frame = BottomFrame.get_section(player, 'charging_station') IC_Gui.remove_toolbar(player) @@ -511,14 +604,14 @@ function Public.update_gui(player) return end - if not player.gui.top[main_frame_name] then + if not get_top_frame(player) then return end - if not player.gui.top[main_frame_name].visible then + if not get_top_frame(player).visible then return end - local gui = player.gui.top[main_frame_name] + local gui = get_top_frame(player) local rpg_extra = RPG.get('rpg_extra') local mined_scrap = Public.get('mined_scrap') diff --git a/maps/mountain_fortress_v3/ic/functions.lua b/maps/mountain_fortress_v3/ic/functions.lua index 8b1199ea..627e0572 100644 --- a/maps/mountain_fortress_v3/ic/functions.lua +++ b/maps/mountain_fortress_v3/ic/functions.lua @@ -957,22 +957,42 @@ end function Public.kill_car_but_save_surface(entity) if not validate_entity(entity) then - return + return nil end local entity_type = IC.get('entity_type') if not entity_type[entity.type] then - return + return nil + end + + local cars = IC.get('cars') + local car = cars[entity.unit_number] + if not car then + return nil + end + + local surface_index = car.surface + local surface = game.get_surface(surface_index) + if not surface then + return nil + end + + local c = 0 + local entities = surface.find_entities_filtered({area = car.area, force = 'player'}) + if entities and #entities > 0 then + for _, e in pairs(entities) do + if e and e.valid and e.name ~= 'character' then + c = c + 1 + end + end + if c > 0 then + return false, c + end end local surfaces = IC.get('surfaces') local surfaces_deleted_by_button = IC.get('surfaces_deleted_by_button') - local cars = IC.get('cars') - local car = cars[entity.unit_number] - if not car then - return - end kick_players_out_of_vehicles(car) kick_players_from_surface(car) @@ -992,7 +1012,7 @@ function Public.kill_car_but_save_surface(entity) end if not owner then - return + return nil end local renders = IC.get('renders') @@ -1017,9 +1037,6 @@ function Public.kill_car_but_save_surface(entity) misc_settings[owner.index] = nil end - local surface_index = car.surface - local surface = game.surfaces[surface_index] - if not surfaces_deleted_by_button[owner.name] then surfaces_deleted_by_button[owner.name] = {} end @@ -1030,6 +1047,7 @@ function Public.kill_car_but_save_surface(entity) car.entity.force.chart(surface, car.area) surfaces[entity.unit_number] = nil cars[entity.unit_number] = nil + return true end function Public.validate_owner(player, entity) diff --git a/maps/mountain_fortress_v3/ic/gui.lua b/maps/mountain_fortress_v3/ic/gui.lua index 10ea2925..a5ccf0fa 100644 --- a/maps/mountain_fortress_v3/ic/gui.lua +++ b/maps/mountain_fortress_v3/ic/gui.lua @@ -6,6 +6,7 @@ local Tabs = require 'utils.gui' local Event = require 'utils.event' local Task = require 'utils.task_token' local SpamProtection = require 'utils.spam_protection' +local Discord = require 'utils.discord_handler' local Public = {} local insert = table.insert @@ -29,10 +30,19 @@ local allow_anyone_to_enter_name = Gui.uid_name() local auto_upgrade_name = Gui.uid_name() local kick_player_name = Gui.uid_name() local destroy_surface_name = Gui.uid_name() +local scenario_name = 'Mtn Fortress' local add_toolbar local remove_toolbar +local function get_top_frame(player) + if Gui.get_mod_gui_top_frame() then + return Gui.get_button_flow(player)[main_toolbar_name] + else + return player.gui.top[main_toolbar_name] + end +end + local function increment(t, k) t[k] = true end @@ -448,28 +458,50 @@ end add_toolbar = function(player, remove) if remove then - if player.gui.top[main_toolbar_name] then - player.gui.top[main_toolbar_name].destroy() + if get_top_frame(player) then + get_top_frame(player).destroy() return end end - if player.gui.top[main_toolbar_name] then + if get_top_frame(player) then return end local tooltip = ({'ic.control'}) - local button = - player.gui.top.add( - { - type = 'sprite-button', - sprite = 'item/spidertron', - name = main_toolbar_name, - tooltip = tooltip, - style = Gui.button_style - } - ) - button.style.minimal_height = 38 - button.style.maximal_height = 38 + if Gui.get_mod_gui_top_frame() then + local b = + Gui.add_mod_button( + player, + { + type = 'sprite-button', + name = main_toolbar_name, + sprite = 'item/spidertron', + tooltip = tooltip, + style = Gui.button_style + } + ) + if b then + b.style.font_color = {165, 165, 165} + b.style.font = 'heading-3' + b.style.minimal_height = 36 + b.style.maximal_height = 36 + b.style.minimal_width = 40 + b.style.padding = -2 + end + else + local button = + player.gui.top.add( + { + type = 'sprite-button', + sprite = 'item/spidertron', + name = main_toolbar_name, + tooltip = tooltip, + style = Gui.button_style + } + ) + button.style.minimal_height = 38 + button.style.maximal_height = 38 + end end remove_toolbar = function(player) @@ -480,8 +512,8 @@ remove_toolbar = function(player) remove_main_frame(main_frame) end - if player.gui.top[main_toolbar_name] then - player.gui.top[main_toolbar_name].destroy() + if get_top_frame(player) then + get_top_frame(player).destroy() return end end @@ -811,8 +843,14 @@ Gui.on_click( local entity = car.entity if entity and entity.valid then - Functions.kill_car_but_save_surface(entity) - game.print('[IC] ' .. player.name .. ' has destroyed their surface!', Color.warning) + local position = entity.position + local suc, count = Functions.kill_car_but_save_surface(entity) + if suc then + game.print('[IC] ' .. player.name .. ' has destroyed their surface!', Color.warning) + Discord.send_notification_raw(scenario_name, player.name .. ' deleted their vehicle surface at x = ' .. position.x .. ' y = ' .. position.y .. '.') + else + player.print('[IC] Entities are still on the surface. Please remove any entities and retry this operation. Found ' .. count .. ' entities!', Color.warning) + end end remove_main_frame(event.element) diff --git a/maps/mountain_fortress_v3/ic/minimap.lua b/maps/mountain_fortress_v3/ic/minimap.lua index 45a30356..c67e6de8 100644 --- a/maps/mountain_fortress_v3/ic/minimap.lua +++ b/maps/mountain_fortress_v3/ic/minimap.lua @@ -24,28 +24,58 @@ local function validate_player(player) return true end +local function get_top_frame(player) + if CoreGui.get_mod_gui_top_frame() then + return CoreGui.get_button_flow(player)['minimap_button'] + else + return player.gui.top['minimap_button'] + end +end + local function create_button(player) - local button = - player.gui.top['minimap_button'] or - player.gui.top.add( + if CoreGui.get_mod_gui_top_frame() then + local b = + CoreGui.add_mod_button( + player, { type = 'sprite-button', name = 'minimap_button', sprite = 'utility/map', tooltip = 'Open or close minimap.', - style = CoreGui.button_style + style = Gui.button_style } ) - button.style.minimal_height = 38 - button.style.maximal_height = 38 + if b then + b.style.font_color = {165, 165, 165} + b.style.font = 'heading-3' + b.style.minimal_height = 36 + b.style.maximal_height = 36 + b.style.minimal_width = 40 + b.style.padding = -2 + end + else + local button = + player.gui.top['minimap_button'] or + player.gui.top.add( + { + type = 'sprite-button', + name = 'minimap_button', + sprite = 'utility/map', + tooltip = 'Open or close minimap.', + style = CoreGui.button_style + } + ) + button.style.minimal_height = 38 + button.style.maximal_height = 38 + end end function Public.toggle_button(player) - if not player.gui.top['minimap_button'] then + if not get_top_frame(player) then create_button(player) end - local button = player.gui.top['minimap_button'] + local button = get_top_frame(player) if Functions.get_player_surface(player) then create_button(player) else @@ -175,7 +205,7 @@ function Public.minimap(player, surface, position) end function Public.update_minimap() - for k, player in pairs(game.connected_players) do + for _, player in pairs(game.connected_players) do local player_data = get_player_data(player) if Functions.get_player_surface(player) and player.gui.left.minimap_toggle_frame and player_data.auto then kill_frame(player) diff --git a/maps/mountain_fortress_v3/locomotive/market.lua b/maps/mountain_fortress_v3/locomotive/market.lua index 2ca23945..a7004b78 100644 --- a/maps/mountain_fortress_v3/locomotive/market.lua +++ b/maps/mountain_fortress_v3/locomotive/market.lua @@ -429,6 +429,27 @@ local function get_items() upgrade = false, static = false } + if upgrades.burner_generator.bought >= upgrades.burner_generator.limit then + main_market_items['burner-generator'] = { + stack = 1, + value = 'coin', + price = 300, + tooltip = ({'main_market.sold_out'}), + enabled = false, + upgrade = false, + static = true + } + else + main_market_items['burner-generator'] = { + stack = 1, + value = 'coin', + price = 300, + tooltip = {'', {'item-name.burner-generator'}, ' bought: ', upgrades.burner_generator.bought, '/', upgrades.burner_generator.limit}, + upgrade = false, + static = true + } + end + main_market_items['car'] = { stack = 1, value = 'coin', @@ -974,6 +995,31 @@ local function gui_click(event) LinkedChests.set('converted_chests', converted_chests + 1) end + if name == 'burner-generator' then + if this.upgrades.burner_generator.bought >= this.upgrades.burner_generator.limit then + redraw_market_items(data.item_frame, player, data.search_text) + player.print(({'locomotive.limit_reached'}), {r = 0.98, g = 0.66, b = 0.22}) + return + end + player.remove_item({name = item.value, count = item.price}) + + player.insert({name = name, count = item.stack}) + + Event.raise(Public.events.on_market_item_purchased, {cost = item.price}) + + if item.stack > this.upgrades.burner_generator.limit then + item.stack = this.upgrades.burner_generator.limit + end + + this.upgrades.burner_generator.bought = this.upgrades.burner_generator.bought + item.stack + + this.upgrades.train_upgrade_contribution = this.upgrades.train_upgrade_contribution + item.price + redraw_market_items(data.item_frame, player, data.search_text) + redraw_coins_left(data.coins_left, player) + + return + end + if name == 'upgrade_pickaxe' then player.remove_item({name = item.value, count = item.price}) diff --git a/maps/mountain_fortress_v3/locomotive/spawn_locomotive.lua b/maps/mountain_fortress_v3/locomotive/spawn_locomotive.lua index f3906d74..e8228c89 100644 --- a/maps/mountain_fortress_v3/locomotive/spawn_locomotive.lua +++ b/maps/mountain_fortress_v3/locomotive/spawn_locomotive.lua @@ -195,11 +195,17 @@ function Public.locomotive_spawn(surface, position, reversed) ) end + local s = 'entity/compilatron' + + if random(1, 10) == 1 then + s = 'entity/character-corpse' + end + for y = -1, 0, 0.05 do local scale = random(50, 100) * 0.01 rendering.draw_sprite( { - sprite = 'entity/small-biter', + sprite = s, orientation = random(0, 100) * 0.01, x_scale = scale, y_scale = scale, @@ -229,6 +235,8 @@ function Public.locomotive_spawn(surface, position, reversed) surface = locomotive.surface } + log(serpent.block(extra_wagons)) + if extra_wagons and extra_wagons > 0 then local inc = 7 diff --git a/maps/mountain_fortress_v3/main.lua b/maps/mountain_fortress_v3/main.lua index 17739b20..76d8babe 100644 --- a/maps/mountain_fortress_v3/main.lua +++ b/maps/mountain_fortress_v3/main.lua @@ -46,6 +46,7 @@ local Beam = require 'modules.render_beam' -- Use these settings for live local send_ping_to_channel = Discord.channel_names.mtn_channel local role_to_mention = Discord.role_mentions.mtn_fortress +local scenario_name = Public.scenario_name -- Use these settings for testing -- bot-lounge -- local send_ping_to_channel = Discord.channel_names.bot_quarters @@ -107,7 +108,7 @@ end local announce_new_map = Task.register( function() - local server_name = Server.check_server_name('Mtn Fortress') + local server_name = Server.check_server_name(scenario_name) if server_name then Server.to_discord_named_raw(send_ping_to_channel, role_to_mention .. ' ** Mtn Fortress was just reset! **') end @@ -115,9 +116,12 @@ local announce_new_map = ) function Public.reset_map() + log(serpent.block('resetting map')) game.forces.player.reset() Public.reset_main_table() + Difficulty.show_gui(false) + local this = Public.get() local wave_defense_table = WD.get_table() Misc.reset() @@ -199,6 +203,7 @@ function Public.reset_map() local players = game.connected_players for i = 1, #players do local player = players[i] + Difficulty.clear_top_frame(player) Score.init_player_table(player, true) Misc.insert_all_items(player) Modifiers.reset_player_modifiers(player) @@ -211,7 +216,6 @@ function Public.reset_map() Difficulty.reset_difficulty_poll({closing_timeout = game.tick + 36000}) Difficulty.set_gui_width(20) - Difficulty.show_gui(false) Collapse.set_kill_entities(false) Collapse.set_kill_specific_entities(collapse_kill) @@ -224,6 +228,12 @@ function Public.reset_map() Collapse.start_now(false) Collapse.disable_collapse(false) + Public.stateful.enable(true) + Public.stateful.create() + Public.stateful.reset_stateful(true, true) + Public.stateful.increase_enemy_damage_and_health() + Public.stateful.apply_startup_settings() + this.locomotive_health = 10000 this.locomotive_max_health = 10000 @@ -298,11 +308,6 @@ function Public.reset_map() this.game_lost = false RPG.reset_table() - Public.stateful.enable(true) - Public.stateful.create() - Public.stateful.reset_stateful(true, true) - Public.stateful.increase_enemy_damage_and_health() - Public.stateful.apply_startup_settings() RPG.rpg_reset_all_players() RPG.set_surface_name({game.surfaces[this.active_surface_index].name, 'boss_room'}) diff --git a/maps/mountain_fortress_v3/mystical_chest.lua b/maps/mountain_fortress_v3/mystical_chest.lua index 3c1c6f67..acb64500 100644 --- a/maps/mountain_fortress_v3/mystical_chest.lua +++ b/maps/mountain_fortress_v3/mystical_chest.lua @@ -4,6 +4,7 @@ local Public = require 'maps.mountain_fortress_v3.table' local RPG = require 'modules.rpg.main' local Alert = require 'utils.alert' local Task = require 'utils.task_token' +local StatData = require 'utils.datastore.statistics' local shuffle = table.shuffle_table local random = math.random @@ -330,6 +331,7 @@ local mc_random_rewards = { if player and player.valid then if player.can_insert({name = 'coin', count = rng}) then player.insert({name = 'coin', count = rng}) + StatData.get_data(player):increase('coins', rng) end end end diff --git a/maps/mountain_fortress_v3/season_highscore.lua b/maps/mountain_fortress_v3/season_highscore.lua index 90d73a02..6ee651a9 100644 --- a/maps/mountain_fortress_v3/season_highscore.lua +++ b/maps/mountain_fortress_v3/season_highscore.lua @@ -12,6 +12,7 @@ local score_key = 'mtn_v3' local score_key_dev = 'mtn_v3_dev' local set_data = Server.set_data local try_get_data = Server.try_get_data +local scenario_name = Public.scenario_name local insert = table.insert @@ -87,7 +88,7 @@ function Public.get_season_scores() if not secs then return else - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) if server_name_matches then try_get_data(score_dataset, score_key, get_scores) else @@ -102,7 +103,7 @@ function Public.set_season_scores() if not secs then return else - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) if server_name_matches then write_additional_stats(score_key) else diff --git a/maps/mountain_fortress_v3/stateful/gui.lua b/maps/mountain_fortress_v3/stateful/gui.lua index fb3caf52..456f3988 100644 --- a/maps/mountain_fortress_v3/stateful/gui.lua +++ b/maps/mountain_fortress_v3/stateful/gui.lua @@ -19,8 +19,12 @@ local main_button_name = Gui.uid_name() local main_frame_name = Gui.uid_name() local boss_frame_name = Gui.uid_name() local close_button = Gui.uid_name() +local close_buffs_window_name = Gui.uid_name() +local buffs_window_name = Gui.uid_name() +local on_click_buff_name = Gui.uid_name() local random = math.random local floor = math.floor +local scenario_name = Public.scenario_name local main_frame local function create_particles(surface, name, position, amount, cause_position) @@ -68,11 +72,17 @@ local spread_particles_token = end ) +local function pretty_format(input) + local action = string.gsub(input, '-', ' ') + local result = string.upper(string.sub(action, 1, 1)) .. string.sub(action, 2) + return result +end + local function notify_won_to_discord(buff) if not buff then return error('Buff is required when sending message to discord.', 2) end - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) local stateful = Public.get_stateful() @@ -182,18 +192,62 @@ local warn_player_sound_token = ) local function create_button(player) - local b = - player.gui.top.add( - { - type = 'sprite-button', - name = main_button_name, - sprite = 'utility/custom_tag_icon', - tooltip = 'Has information about all objectives that needs to be completed', - style = Gui.button_style - } - ) - b.style.minimal_height = 38 - b.style.maximal_height = 38 + if Gui.get_mod_gui_top_frame() then + local b = + Gui.add_mod_button( + player, + { + type = 'sprite-button', + name = main_button_name, + sprite = 'utility/custom_tag_icon', + tooltip = 'Has information about all objectives that needs to be completed', + style = Gui.button_style + } + ) + if b then + b.style.font_color = {165, 165, 165} + b.style.font = 'heading-3' + b.style.minimal_height = 36 + b.style.maximal_height = 36 + b.style.minimal_width = 40 + b.style.padding = -2 + end + else + local b = + player.gui.top.add( + { + type = 'sprite-button', + name = main_button_name, + sprite = 'utility/custom_tag_icon', + tooltip = 'Has information about all objectives that needs to be completed', + style = Gui.button_style + } + ) + b.style.minimal_height = 38 + b.style.maximal_height = 38 + end +end + +local function create_input_element(frame, type, value, items, index, tooltip, custom_space) + if type == 'slider' then + return frame.add({type = 'slider', value = value, minimum_value = 0, maximum_value = 1}) + end + if type == 'boolean' then + return frame.add({type = 'checkbox', state = value}) + end + if type == 'label' then + local label = frame.add({type = 'label', caption = value}) + label.style.font = 'default-listbox' + label.tooltip = tooltip or '' + if custom_space then + label.style.minimal_height = custom_space + end + return label + end + if type == 'dropdown' then + return frame.add({type = 'drop-down', items = items, selected_index = index}) + end + return frame.add({type = 'text-box', text = value}) end local function play_game_won() @@ -321,6 +375,111 @@ local function objective_frames(stateful, player_frame, objective, data) return end +local function buff_window(player) + local buff_frame_name, inside_table = Gui.add_main_frame_with_toolbar(player, 'center', buffs_window_name, nil, close_buffs_window_name, 'Buffs gathered') + if not buff_frame_name then + return + end + if not inside_table then + return + end + + local stateful = Public.get_stateful() + + local inside_table_style = inside_table.style + inside_table_style.width = 530 + + local info_text = inside_table.add({type = 'label', caption = 'All the buffs that have been gathered throughout the runs!'}) + local info_text_style = info_text.style + info_text_style.font = 'heading-2' + info_text_style.padding = 0 + info_text_style.left_padding = 10 + info_text_style.horizontal_align = 'left' + info_text_style.vertical_align = 'bottom' + info_text_style.font_color = {0.55, 0.55, 0.99} + + local buff_pane = inside_table.add({type = 'scroll-pane'}) + local ns = buff_pane.style + ns.vertically_squashable = true + ns.bottom_padding = 5 + ns.left_padding = 5 + ns.right_padding = 5 + ns.top_padding = 5 + + buff_pane.add({type = 'line'}) + + local starting_items_label = buff_pane.add({type = 'label', caption = 'Starting items'}) + local starting_items_label_style = starting_items_label.style + starting_items_label_style.font = 'heading-3' + starting_items_label_style.padding = 0 + starting_items_label_style.horizontal_align = 'left' + starting_items_label_style.font_color = {0.55, 0.55, 0.99} + + local starting_grid = buff_pane.add({type = 'table', column_count = 8}) + + buff_pane.add({type = 'line'}) + + local force_label = buff_pane.add({type = 'label', caption = 'Force Buffs'}) + local force_label_style = force_label.style + force_label_style.font = 'heading-3' + force_label_style.padding = 0 + force_label_style.horizontal_align = 'left' + force_label_style.font_color = {0.55, 0.55, 0.99} + local force_grid = buff_pane.add({type = 'table', column_count = 2}) + + buff_pane.add({type = 'line'}) + + local custom_label = buff_pane.add({type = 'label', caption = 'Custom Buffs'}) + local custom_label_style = custom_label.style + custom_label_style.font = 'heading-3' + custom_label_style.padding = 0 + custom_label_style.horizontal_align = 'left' + custom_label_style.font_color = {0.55, 0.55, 0.99} + local custom_grid = buff_pane.add({type = 'table', column_count = 2}) + + if stateful.buffs and next(stateful.buffs) then + if stateful.buffs_collected and next(stateful.buffs_collected) then + if stateful.buffs_collected.starting_items then + for item_name, item_data in pairs(stateful.buffs_collected.starting_items) do + -- local text = pretty_format(item_name) .. ': [font=font-bold]' .. item_data.count + local text = '[font=default-large] [item=' .. item_name .. '][/font]' .. ': [font=default-bold]' .. item_data.count .. '[/font]' + create_input_element(starting_grid, 'label', text, nil, nil, item_data.discord, 30) + end + end + + for name, buff_data in pairs(stateful.buffs_collected) do + if type(buff_data.amount) ~= 'table' and buff_data.force then + local c = buff_data.count + local text + if name == 'xp_level' or name == 'xp_bonus' or name == 'character_health_bonus' then + text = '[font=default-bold]' .. Stateful.buff_to_string[name] .. ': ' .. c .. '[/font]' + else + text = '[font=default-bold]' .. Stateful.buff_to_string[name] .. ': ' .. (c * 100) .. '%[/font]' + end + + create_input_element(force_grid, 'label', text, nil, nil, buff_data.discord) + end + + if name ~= 'starting_items' and not buff_data.force then + if buff_data.name then + local text_to_place = buff_data.count or 'Unlocked' + local text = '[font=default-bold]' .. buff_data.name .. ': ' .. text_to_place .. ' [/font]' + create_input_element(custom_grid, 'label', text, nil, nil, buff_data.discord) + else + for _, buff in pairs(buff_data) do + local text_to_place = buff.count or 'Unlocked' + local text = '[font=default-bold]' .. pretty_format(buff.name) .. ': ' .. text_to_place .. ' [/font]' + create_input_element(custom_grid, 'label', text, nil, nil, buff.discord) + end + end + end + end + end + end + + player.opened = buff_frame_name +end + local function boss_frame(player, alert) local main_winning_frame = player.gui.screen[main_frame_name] if main_winning_frame then @@ -480,7 +639,11 @@ main_frame = function(player) local wave_number = WD.get('wave_number') local frame = player.gui.screen.add {type = 'frame', name = main_frame_name, caption = {'stateful.win_conditions'}, direction = 'vertical', tooltip = {'stateful.win_conditions_tooltip'}} - frame.location = {x = 1, y = 45} + if Gui.get_mod_gui_top_frame() then + frame.location = {x = 0, y = 67} + else + frame.location = {x = 1, y = 45} + end frame.style.maximal_height = 700 frame.style.minimal_width = 200 frame.style.maximal_width = 400 @@ -533,31 +696,7 @@ main_frame = function(player) buff_right_flow.style.horizontal_align = 'right' buff_right_flow.style.horizontally_stretchable = true - local buffs = '' - if stateful.buffs_collected and next(stateful.buffs_collected) then - if stateful.buffs_collected.starting_items then - buffs = buffs .. 'Starting items:\n' - for item_name, item_data in pairs(stateful.buffs_collected.starting_items) do - buffs = buffs .. item_name .. ': ' .. item_data.count - buffs = buffs .. '\n' - end - buffs = buffs .. '\n' - end - - buffs = buffs .. 'Force buffs:\n' - for name, buff_data in pairs(stateful.buffs_collected) do - if type(buff_data.amount) ~= 'table' and name ~= 'starting_items' then - if name == 'xp_level' or name == 'xp_bonus' or name == 'character_health_bonus' then - buffs = buffs .. Stateful.buff_to_string[name] .. ': ' .. buff_data.count - else - buffs = buffs .. Stateful.buff_to_string[name] .. ': ' .. (buff_data.count * 100) .. '%' - end - buffs = buffs .. '\n' - end - end - end - - buff_right_flow.add({type = 'label', caption = '[img=utility/center]', tooltip = buffs}) + buff_right_flow.add({name = on_click_buff_name, type = 'label', caption = '[img=utility/center]', tooltip = {'stateful.buff_tooltip_click'}}) local buff_label = buff_left_flow.add({type = 'label', caption = {'stateful.buffs'}, tooltip = {'stateful.buff_tooltip'}}) buff_label.style.single_line = false @@ -874,6 +1013,7 @@ local function update_raw() if breached_wall >= stateful.objectives.randomized_zone then if not stateful.objectives_completed.randomized_zone then stateful.objectives_completed.randomized_zone = true + stateful.objectives_time_spent.randomized_zone = tick play_achievement_unlocked() Alert.alert_all_players(10, 'Objective: **breach zone** has been complete!') Server.to_discord_embed('Objective: **breach zone** has been complete!') @@ -886,6 +1026,8 @@ local function update_raw() if wave_number >= stateful.objectives.randomized_wave then if not stateful.objectives_completed.randomized_wave then stateful.objectives_completed.randomized_wave = true + stateful.objectives_time_spent.randomized_wave = tick + play_achievement_unlocked() Alert.alert_all_players(10, 'Objective: **survive until wave** has been complete!') Server.to_discord_embed('Objective: **survive until wave** has been complete!') @@ -911,6 +1053,7 @@ local function update_raw() if items_done == 3 then if not stateful.objectives_completed.supplies then stateful.objectives_completed.supplies = true + stateful.objectives_time_spent.supplies = tick Alert.alert_all_players(10, 'Objective: **produce 3 items multiple times** has been complete!') Server.to_discord_embed('Objective: **produce 3 items multiple times** has been complete!') play_achievement_unlocked() @@ -937,6 +1080,7 @@ local function update_raw() stateful.objectives.single_item.count = 0 if not stateful.objectives_completed.single_item then stateful.objectives_completed.single_item = true + stateful.objectives_time_spent.single_item = tick play_achievement_unlocked() Alert.alert_all_players(10, 'Objective: **produce an item multiple times** has been completed!') Server.to_discord_embed('Objective: **produce an item multiple times** has been completed!') @@ -1042,6 +1186,7 @@ local function update_raw() local completed, _, _ = callback() if completed and completed == true and not stateful.objectives_completed[objective_name] then stateful.objectives_completed[objective_name] = true + stateful.objectives_time_spent[objective_name] = tick Alert.alert_all_players(10, 'Objective: **' .. objective_name .. '** has been completed!') Server.to_discord_embed('Objective: **' .. objective_name .. '** has been completed!') play_achievement_unlocked() @@ -1189,6 +1334,71 @@ Gui.on_click( end ) +Gui.on_click( + close_buffs_window_name, + function(event) + local is_spamming = SpamProtection.is_spamming(event.player, nil, 'Buff Close Button') + if is_spamming then + return + end + local player = event.player + local center = player.gui.center + if not player or not player.valid or not player.character then + return + end + + local frame_buff = center[buffs_window_name] + if frame_buff and frame_buff.valid then + Gui.remove_data_recursively(frame_buff) + frame_buff.destroy() + end + end +) + +Gui.on_custom_close( + buffs_window_name, + function(event) + local is_spamming = SpamProtection.is_spamming(event.player, nil, 'Buff Custom Close') + if is_spamming then + return + end + local player = event.player + local center = player.gui.center + if not player or not player.valid or not player.character then + return + end + + local frame_buff = center[buffs_window_name] + if frame_buff and frame_buff.valid then + Gui.remove_data_recursively(frame_buff) + frame_buff.destroy() + end + end +) + +Gui.on_click( + on_click_buff_name, + function(event) + local is_spamming = SpamProtection.is_spamming(event.player, nil, 'Buff Open Button') + if is_spamming then + return + end + local player = event.player + local center = player.gui.center + if not player or not player.valid or not player.character then + return + end + + local frame_buff = center[buffs_window_name] + if frame_buff and frame_buff.valid then + Gui.remove_data_recursively(frame_buff) + frame_buff.destroy() + else + buff_window(player) + end + end +) + Event.add(defines.events.on_player_joined_game, on_player_joined_game) Event.on_nth_tick(30, update_data) Event.on_nth_tick(30, update_raw) diff --git a/maps/mountain_fortress_v3/stateful/main.lua b/maps/mountain_fortress_v3/stateful/main.lua index 7bcca4b9..45354248 100644 --- a/maps/mountain_fortress_v3/stateful/main.lua +++ b/maps/mountain_fortress_v3/stateful/main.lua @@ -2,6 +2,7 @@ local Public = require 'maps.mountain_fortress_v3.stateful.table' local Event = require 'utils.event' local WD = require 'modules.wave_defense.table' local Beam = require 'modules.render_beam' +local RPG = require 'modules.rpg.main' Public.stateful_gui = require 'maps.mountain_fortress_v3.stateful.gui' Public.stateful_terrain = require 'maps.mountain_fortress_v3.stateful.terrain' @@ -82,6 +83,123 @@ Event.on_nth_tick( end ) +Event.add( + defines.events.on_player_crafted_item, + function(event) + local player = game.get_player(event.player_index) + if not player or not player.valid then + return + end + + local item = event.item_stack + if not item or not item.valid_for_read then + return + end + + local objectives = Public.get_stateful('objectives') + + local handcrafted_items_any = objectives.handcrafted_items_any + if handcrafted_items_any then + handcrafted_items_any.actual = handcrafted_items_any.actual + item.count + end + + local handcrafted_items = objectives.handcrafted_items + if handcrafted_items then + if item.name ~= handcrafted_items.name then + return + end + + handcrafted_items.actual = handcrafted_items.actual + item.count + end + end +) + +Event.add( + defines.events.on_entity_died, + function(event) + local entity = event.entity + if not entity or not entity.valid then + return + end + + if not Public.valid_enemy_forces[entity.force.name] then + return + end + + local objectives = Public.get_stateful('objectives') + + local damage_type = event.damage_type + if not damage_type then + return + end + local killed_enemies = objectives.killed_enemies_type + if not killed_enemies then + return + end + + if killed_enemies.damage_type ~= damage_type.name then + return + end + + if entity.type == 'unit' then + killed_enemies.actual = killed_enemies.actual + 1 + end + end +) + +Event.add( + defines.events.on_rocket_launched, + function(event) + local rocket_inventory = event.rocket.get_inventory(defines.inventory.rocket) + local slot = rocket_inventory[1] + if slot and slot.valid and slot.valid_for_read then + local objectives = Public.get_stateful('objectives') + + local launch_item = objectives.launch_item + if launch_item then + if slot.name ~= launch_item.name then + return + end + + launch_item.actual = launch_item.actual + slot.count + end + end + end +) + +Event.add( + RPG.events.on_spell_cast_success, + function(event) + local player = game.get_player(event.player_index) + if not player or not player.valid then + return + end + + local spell_name = event.spell_name + local amount = event.amount + + if not player.character or not player.character.valid then + return + end + + local objectives = Public.get_stateful('objectives') + + local cast_spell_any = objectives.cast_spell_any + if cast_spell_any then + cast_spell_any.actual = cast_spell_any.actual + amount + end + + local cast_spell = objectives.cast_spell + if cast_spell then + if spell_name ~= cast_spell.name then + return + end + + cast_spell.actual = cast_spell.actual + amount + end + end +) + Event.on_nth_tick( 14400, function() diff --git a/maps/mountain_fortress_v3/stateful/table.lua b/maps/mountain_fortress_v3/stateful/table.lua index 2e2d045b..2d71dda9 100644 --- a/maps/mountain_fortress_v3/stateful/table.lua +++ b/maps/mountain_fortress_v3/stateful/table.lua @@ -18,6 +18,7 @@ local BiterHealthBooster = require 'modules.biter_health_booster_v2' local Beam = require 'modules.render_beam' local Discord = require 'utils.discord' local Difficulty = require 'modules.difficulty_vote_by_amount' +local scenario_name = Public.scenario_name local this = { enabled = false, @@ -28,6 +29,7 @@ local this = { time_to_reset = 60 } +local random = math.random local round = math.round local floor = math.floor local dataset = 'scenario_settings' @@ -43,6 +45,8 @@ Global.register( end ) +local damage_types = {'physical', 'electric', 'fire', 'poison', 'laser'} + local stateful_spawn_points = { {{x = -205, y = -37}, {x = 195, y = 37}}, {{x = -205, y = -112}, {x = 195, y = 112}}, @@ -88,7 +92,7 @@ local buff_to_string = { } local function notify_season_over_to_discord() - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) local stateful = Public.get_stateful() @@ -140,187 +144,123 @@ local function notify_season_over_to_discord() end end -local function get_random_force_buff(fetch_all) +local function get_random_buff(fetch_all, only_force) local buffs = { { name = 'character_running_speed_modifier', - discord = 'Running speed modifier', + discord = 'Running speed modifier - run faster!', modifier = 'force', + per_force = true, state = 0.05 }, { name = 'manual_mining_speed_modifier', - discord = 'Mining speed modifier', + discord = 'Mining speed modifier - mine faster!', modifier = 'force', + per_force = true, state = 0.15 }, { name = 'laboratory_speed_modifier', - discord = 'Laboratory speed modifier', + discord = 'Laboratory speed modifier - labs work faster!', modifier = 'force', + per_force = true, state = 0.15 }, { name = 'laboratory_productivity_bonus', - discord = 'Productivity bonus', + discord = 'Laboratory productivity bonus - labs dupe things!', modifier = 'force', + per_force = true, state = 0.15 }, { name = 'worker_robots_storage_bonus', - discord = 'Robot storage bonus', + discord = 'Robot storage bonus - robots carry more!', modifier = 'force', + per_force = true, state = 1 }, { name = 'worker_robots_battery_modifier', - discord = 'Robot battery bonus', + discord = 'Robot battery bonus - robots work longer!', modifier = 'force', + per_force = true, state = 1 }, { name = 'worker_robots_speed_modifier', - discord = 'Robot speed modifier', + discord = 'Robot speed modifier - robots move faster!', modifier = 'force', + per_force = true, state = 0.5 }, { name = 'mining_drill_productivity_bonus', - discord = 'Drill productivity bonus', + discord = 'Drill productivity bonus - drills work faster!', modifier = 'force', + per_force = true, state = 0.5 }, { name = 'character_health_bonus', - discord = 'Character health bonus', + discord = 'Character health bonus - more health!', modifier = 'force', + per_force = true, state = 250 }, { name = 'distance', - discord = 'RPG reach distance bonus', + discord = 'RPG reach distance bonus - reach further!', modifier = 'rpg_distance', + per_force = true, modifiers = {'character_resource_reach_distance_bonus', 'character_item_pickup_distance_bonus', 'character_loot_pickup_distance_bonus', 'character_reach_distance_bonus'}, state = 0.05 }, { name = 'manual_crafting_speed_modifier', - discord = 'Crafting speed modifier', + discord = 'Crafting speed modifier - craft faster!', modifier = 'force', + per_force = true, state = 0.12 }, { name = 'xp_bonus', - discord = 'RPG XP point bonus', + discord = 'RPG XP point bonus - more XP points from kills etc.', modifier = 'rpg', + per_force = true, state = 0.12 }, { name = 'xp_level', - discord = 'RPG XP level bonus', + discord = 'RPG XP level bonus - start with more XP levels', modifier = 'rpg', + per_force = true, state = 20 - } - } - - if fetch_all then - return buffs - end - - shuffle(buffs) - shuffle(buffs) - shuffle(buffs) - shuffle(buffs) - shuffle(buffs) - shuffle(buffs) - - return buffs[1] -end - -local function get_random_buff(fetch_all) - local buffs = { - { - name = 'character_running_speed_modifier', - discord = 'Running speed modifier', - modifier = 'force', - state = 0.05 }, { - name = 'manual_mining_speed_modifier', - discord = 'Mining speed modifier', - modifier = 'force', - state = 0.15 + name = 'chemicals_s', + discord = 'Starting items supplies - start with some sulfur', + modifier = 'starting_items', + limit = 200, + add_per_buff = 50, + items = { + {name = 'sulfur', count = 50} + } }, { - name = 'laboratory_speed_modifier', - discord = 'Laboratory speed modifier', - modifier = 'force', - state = 0.15 - }, - { - name = 'laboratory_productivity_bonus', - discord = 'Productivity bonus', - modifier = 'force', - state = 0.15 - }, - { - name = 'worker_robots_storage_bonus', - discord = 'Robot storage bonus', - modifier = 'force', - state = 1 - }, - { - name = 'worker_robots_battery_modifier', - discord = 'Robot battery bonus', - modifier = 'force', - state = 1 - }, - { - name = 'worker_robots_speed_modifier', - discord = 'Robot speed modifier', - modifier = 'force', - state = 0.5 - }, - { - name = 'mining_drill_productivity_bonus', - discord = 'Drill productivity bonus', - modifier = 'force', - state = 0.5 - }, - { - name = 'character_health_bonus', - discord = 'Character health bonus', - modifier = 'force', - state = 250 - }, - { - name = 'distance', - discord = 'RPG reach distance bonus', - modifier = 'rpg_distance', - modifiers = {'character_resource_reach_distance_bonus', 'character_item_pickup_distance_bonus', 'character_loot_pickup_distance_bonus', 'character_reach_distance_bonus'}, - state = 0.05 - }, - { - name = 'manual_crafting_speed_modifier', - discord = 'Crafting speed modifier', - modifier = 'force', - state = 0.12 - }, - { - name = 'xp_bonus', - discord = 'RPG XP point bonus', - modifier = 'rpg', - state = 0.12 - }, - { - name = 'xp_level', - discord = 'RPG XP level bonus', - modifier = 'rpg', - state = 20 + name = 'chemicals_p', + discord = 'Starting items supplies - start with some plastic bar', + modifier = 'starting_items', + limit = 200, + add_per_buff = 50, + items = { + {name = 'plastic-bar', count = 100} + } }, { name = 'supplies', - discord = 'Starting items supplies', + discord = 'Starting items supplies - start with some copper and iron plates', modifier = 'starting_items', limit = 1000, add_per_buff = 100, @@ -331,7 +271,7 @@ local function get_random_buff(fetch_all) }, { name = 'supplies_1', - discord = 'Starting items supplies', + discord = 'Starting items supplies - start with more copper and iron plates', modifier = 'starting_items', limit = 1000, add_per_buff = 200, @@ -342,7 +282,7 @@ local function get_random_buff(fetch_all) }, { name = 'supplies_2', - discord = 'Starting items supplies', + discord = 'Starting items supplies - start with even more copper and iron plates', modifier = 'starting_items', limit = 1000, add_per_buff = 400, @@ -353,7 +293,7 @@ local function get_random_buff(fetch_all) }, { name = 'defense', - discord = 'Defense starting supplies', + discord = 'Defense starting supplies - start with some turrets and ammo', modifier = 'starting_items', limit = 10, add_per_buff = 1, @@ -364,7 +304,7 @@ local function get_random_buff(fetch_all) }, { name = 'defense_3', - discord = 'Defense starting supplies', + discord = 'Defense starting supplies - start with more turrets and ammo', modifier = 'starting_items', limit = 1, add_per_buff = 1, @@ -375,7 +315,7 @@ local function get_random_buff(fetch_all) }, { name = 'armor', - discord = 'Armor starting supplies', + discord = 'Armor starting supplies - start with some armor and solar panels', modifier = 'starting_items', limit = 1, add_per_buff = 1, @@ -386,7 +326,7 @@ local function get_random_buff(fetch_all) }, { name = 'production', - discord = 'Production starting supplies', + discord = 'Production starting supplies - start with some furnaces and coal', modifier = 'starting_items', limit = 2, add_per_buff = 1, @@ -397,7 +337,7 @@ local function get_random_buff(fetch_all) }, { name = 'production_1', - discord = 'Production starting supplies', + discord = 'Production starting supplies - start with some steel furnaces and solid fuel', modifier = 'starting_items', limit = 2, add_per_buff = 1, @@ -408,7 +348,7 @@ local function get_random_buff(fetch_all) }, { name = 'fast_startup', - discord = 'Assembling starting supplies', + discord = 'Assembling starting supplies - start with some assembling machines T1', modifier = 'starting_items', limit = 25, add_per_buff = 2, @@ -418,7 +358,7 @@ local function get_random_buff(fetch_all) }, { name = 'fast_startup_1', - discord = 'Assembling starting supplies', + discord = 'Assembling starting supplies - start with some assembling machines T2', modifier = 'starting_items', limit = 25, add_per_buff = 2, @@ -428,7 +368,7 @@ local function get_random_buff(fetch_all) }, { name = 'fast_startup_2', - discord = 'Assembling starting supplies', + discord = 'Assembling starting supplies - start with some assembling machines T3', modifier = 'starting_items', limit = 25, add_per_buff = 2, @@ -438,7 +378,7 @@ local function get_random_buff(fetch_all) }, { name = 'heal-thy-buildings', - discord = 'Repair starting supplies', + discord = 'Repair starting supplies - start with some repair packs', modifier = 'starting_items', limit = 20, add_per_buff = 2, @@ -451,9 +391,104 @@ local function get_random_buff(fetch_all) discord = 'Extra wagon at start', modifier = 'locomotive', state = 1 + }, + { + name = 'american_oil', + discord = 'Oil tech - start with some crude oil barrels', + modifier = 'starting_items', + limit = 40, + add_per_buff = 20, + items = { + {name = 'crude-oil-barrel', count = 20} + } + }, + { + name = 'steel_plates', + discord = 'Steel tech - start with some steel plates', + modifier = 'starting_items', + limit = 200, + add_per_buff = 100, + items = { + {name = 'steel-plate', count = 100} + } + }, + { + name = 'red_science', + discord = 'Science tech - start with some red science packs', + modifier = 'starting_items', + limit = 200, + add_per_buff = 10, + items = { + {name = 'automation-science-pack', count = 10} + } + }, + { + name = 'roboport_equipement', + discord = 'Equipement tech - start with a personal roboport', + modifier = 'starting_items', + limit = 4, + add_per_buff = 1, + items = { + {name = 'personal-roboport-equipment', count = 1} + } + }, + { + name = 'mk1_tech_unlocked', + discord = 'Equipement tech - start with power armor tech unlocked.', + modifier = 'tech', + limit = 1, + add_per_buff = 1, + techs = { + {name = 'power-armor', count = 1} + } + }, + { + name = 'steel_axe_unlocked', + discord = 'Equipement tech - start with steel axe tech unlocked.', + modifier = 'tech', + limit = 1, + add_per_buff = 1, + techs = { + {name = 'steel-axe', count = 1} + } + }, + { + name = 'military_2_unlocked', + discord = 'Equipement tech - start with military 2 tech unlocked.', + modifier = 'tech', + limit = 1, + add_per_buff = 1, + techs = { + {name = 'military-2', count = 1} + } + }, + { + name = 'all_the_fish', + discord = 'Wagon is full of fish!', + modifier = 'fish', + limit = 1, + add_per_buff = 1 } } + if only_force then + local force_buffs = {} + for _, buff in pairs(buffs) do + if buff.per_force then + force_buffs[#force_buffs + 1] = buff + end + end + + shuffle(force_buffs) + shuffle(force_buffs) + shuffle(force_buffs) + shuffle(force_buffs) + shuffle(force_buffs) + shuffle(force_buffs) + + return force_buffs[1] + end + if fetch_all then return buffs end @@ -608,6 +643,96 @@ local killed_enemies_token = end ) +local killed_enemies_type_token = + Task.register( + function() + local actual = this.objectives.killed_enemies_type.actual + local expected = this.objectives.killed_enemies_type.expected + if actual >= expected then + return true, {'stateful.enemies_killed_type', this.objectives.killed_enemies_type.damage_type}, {'stateful.done', format_number(expected, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_completed'} + end + + return false, {'stateful.enemies_killed_type', this.objectives.killed_enemies_type.damage_type}, {'stateful.not_done', format_number(actual, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, { + 'stateful.tooltip_not_completed' + } + end +) + +local handcrafted_items_token = + Task.register( + function() + local actual = this.objectives.handcrafted_items.actual + local expected = this.objectives.handcrafted_items.expected + if actual >= expected then + return true, {'stateful.crafted_items', this.objectives.handcrafted_items.name}, {'stateful.done', format_number(expected, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_completed'} + end + + return false, {'stateful.crafted_items', this.objectives.handcrafted_items.name}, {'stateful.not_done', format_number(actual, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, { + 'stateful.tooltip_not_completed' + } + end +) + +local handcrafted_items_any_token = + Task.register( + function() + local actual = this.objectives.handcrafted_items_any.actual + local expected = this.objectives.handcrafted_items_any.expected + if actual >= expected then + return true, {'stateful.crafted_items', this.objectives.handcrafted_items_any.name}, {'stateful.done', format_number(expected, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_completed'} + end + + return false, {'stateful.crafted_items', this.objectives.handcrafted_items_any.name}, {'stateful.not_done', format_number(actual, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, { + 'stateful.tooltip_not_completed' + } + end +) + +local launch_item_token = + Task.register( + function() + local actual = this.objectives.launch_item.actual + local expected = this.objectives.launch_item.expected + if actual >= expected then + return true, {'stateful.launch_item', this.objectives.launch_item.name}, {'stateful.done', format_number(expected, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_completed'} + end + + return false, {'stateful.launch_item', this.objectives.launch_item.name}, {'stateful.not_done', format_number(actual, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, { + 'stateful.tooltip_not_completed' + } + end +) + +local cast_spell_token = + Task.register( + function() + local actual = this.objectives.cast_spell.actual + local expected = this.objectives.cast_spell.expected + if actual >= expected then + return true, {'stateful.cast_spell', this.objectives.cast_spell.name}, {'stateful.done', format_number(expected, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_completed'} + end + + return false, {'stateful.cast_spell', this.objectives.cast_spell.name}, {'stateful.not_done', format_number(actual, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, { + 'stateful.tooltip_not_completed' + } + end +) + +local cast_spell_any_token = + Task.register( + function() + local actual = this.objectives.cast_spell_any.actual + local expected = this.objectives.cast_spell_any.expected + if actual >= expected then + return true, {'stateful.cast_spell', this.objectives.cast_spell_any.name}, {'stateful.done', format_number(expected, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_completed'} + end + + return false, {'stateful.cast_spell', this.objectives.cast_spell_any.name}, {'stateful.not_done', format_number(actual, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, { + 'stateful.tooltip_not_completed' + } + end +) + local research_level_selection_token = Task.register( function() @@ -633,27 +758,15 @@ local locomotive_market_coins_spent_token = end ) -local trees_farmed_token = +local minerals_farmed_token = Task.register( function() - local actual = get_entity_mined_count('tree') - local expected = this.objectives.trees_farmed + local actual = get_entity_mined_count('rock') + get_entity_mined_count('tree') + local expected = this.objectives.minerals_farmed if actual >= expected then - return true, {'stateful.trees_mined'}, {'stateful.done', format_number(expected, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_completed'} + return true, {'stateful.minerals_mined'}, {'stateful.done', format_number(expected, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_completed'} end - return false, {'stateful.trees_mined'}, {'stateful.not_done', format_number(actual, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_not_completed'} - end -) - -local rocks_farmed_token = - Task.register( - function() - local actual = get_entity_mined_count('rock') - local expected = this.objectives.rocks_farmed - if actual >= expected then - return true, {'stateful.rocks_mined'}, {'stateful.done', format_number(expected, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_completed'} - end - return false, {'stateful.rocks_mined'}, {'stateful.not_done', format_number(actual, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_not_completed'} + return false, {'stateful.minerals_mined'}, {'stateful.not_done', format_number(actual, true), format_number(expected, true)}, {'stateful.generic_tooltip'}, {'stateful.tooltip_not_completed'} end ) @@ -749,6 +862,102 @@ local function get_random_item() return {name = items[1][1], count = items[1][2]} end +local function get_random_handcrafted_item() + local items = { + {'advanced-circuit', scale(2000, 500000)}, + {'copper-cable', scale(10000, 500000)}, + {'electronic-circuit', scale(5000, 1000000)}, + {'engine-unit', scale(3500, 500000)}, + {'iron-gear-wheel', scale(50000, 1000000)}, + {'iron-stick', scale(75000, 3000000)}, + {'rocket-control-unit', scale(1000, 50000)}, + {'rocket', scale(5000, 1000000)}, + {'explosive-rocket', scale(5000, 1000000)}, + {'slowdown-capsule', scale(2500, 400000)}, + {'laser-turret', scale(1500, 20000)}, + {'stone-wall', scale(5000, 800000)}, + {'accumulator', scale(1000, 200000)}, + {'uranium-rounds-magazine', scale(1000, 60000)}, + {'explosive-uranium-cannon-shell', scale(1000, 10000)}, + {'distractor-capsule', scale(1500, 60000)}, + {'grenade', scale(5000, 200000)}, + {'cluster-grenade', scale(1000, 100000)}, + {'small-lamp', scale(2500, 200000)}, + {'rail', scale(5000, 100000)}, + {'small-electric-pole', scale(5000, 100000)}, + {'medium-electric-pole', scale(3500, 80000)}, + {'big-electric-pole', scale(2000, 50000)}, + {'transport-belt', scale(10000, 100000)}, + {'fast-transport-belt', scale(3000, 50000)}, + {'repair-pack', scale(10000, 100000)}, + {'splitter', scale(10000, 100000)}, + {'fast-splitter', scale(3000, 50000)}, + {'inserter', scale(3000, 50000)}, + {'firearm-magazine', scale(10000, 200000)}, + {'piercing-rounds-magazine', scale(5000, 100000)}, + {'pipe', scale(10000, 100000)}, + {'pipe-to-ground', scale(3000, 50000)}, + {'effectivity-module', scale(100, 50000)}, + {'productivity-module', scale(100, 50000)}, + {'speed-module', scale(100, 50000)} + } + + shuffle(items) + shuffle(items) + shuffle(items) + shuffle(items) + + return {name = items[1][1], count = items[1][2]} +end + +local function get_random_spell() + local items = { + {'stone-wall', scale(1000, 250000)}, + {'wooden-chest', scale(1000, 250000)}, + {'iron-chest', scale(1000, 200000)}, + {'steel-chest', scale(1000, 150000)}, + {'transport-belt', scale(1000, 250000)}, + {'fast-transport-belt', scale(1000, 200000)}, + {'express-transport-belt', scale(1000, 150000)}, + {'underground-belt', scale(1000, 250000)}, + {'fast-underground-belt', scale(1000, 200000)}, + {'express-underground-belt', scale(1000, 150000)}, + {'pipe', scale(1000, 20000)}, + {'pipe-to-ground', scale(1000, 250000)}, + {'tree-05', scale(1000, 200000)}, + {'sand-rock-big', scale(1000, 60000)}, + {'small-biter', scale(1000, 10000)}, + {'small-spitter', scale(1000, 60000)}, + {'medium-biter', scale(1000, 200000)}, + {'medium-spitter', scale(1000, 100000)}, + {'biter-spawner', scale(1000, 200000)}, + {'spitter-spawner', scale(1000, 100000)}, + {'shotgun-shell', scale(1000, 100000)}, + {'grenade', scale(1000, 80000)}, + {'cluster-grenade', scale(1000, 50000)}, + {'cannon-shell', scale(1000, 100000)}, + {'explosive-cannon-shell', scale(1000, 50000)}, + {'uranium-cannon-shell', scale(1000, 100000)}, + {'rocket', scale(1000, 100000)}, + {'repair_aoe', scale(1000, 50000)}, + {'acid-stream-spitter-big', scale(1000, 200000)}, + {'raw-fish', scale(3500, 500000)}, + {'explosives', scale(5000, 100000)}, + {'distractor-capsule', scale(5000, 100000)}, + {'defender-capsule', scale(5000, 100000)}, + {'destroyer-capsule', scale(5000, 100000)}, + {'warp-gate', scale(5000, 500000)}, + {'haste', scale(5000, 500000)} + } + + shuffle(items) + shuffle(items) + shuffle(items) + shuffle(items) + + return {name = items[1][1], count = items[1][2]} +end + local function get_random_research_recipe() -- scale(10, 20) local research_level_list = { @@ -778,6 +987,30 @@ local function get_random_objectives() name = 'killed_enemies', token = killed_enemies_token }, + { + name = 'killed_enemies_type', + token = killed_enemies_type_token + }, + { + name = 'handcrafted_items', + token = handcrafted_items_token + }, + { + name = 'handcrafted_items_any', + token = handcrafted_items_any_token + }, + { + name = 'cast_spell', + token = cast_spell_token + }, + { + name = 'launch_item', + token = launch_item_token + }, + { + name = 'cast_spell_any', + token = cast_spell_any_token + }, { name = 'research_level_selection', token = research_level_selection_token @@ -787,12 +1020,8 @@ local function get_random_objectives() token = locomotive_market_coins_spent_token }, { - name = 'trees_farmed', - token = trees_farmed_token - }, - { - name = 'rocks_farmed', - token = rocks_farmed_token + name = 'minerals_farmed', + token = minerals_farmed_token }, { name = 'rockets_launched', @@ -801,6 +1030,17 @@ local function get_random_objectives() } shuffle(items) + shuffle(items) + shuffle(items) + shuffle(items) + + if _DEBUG then + items[#items + 1] = { + name = 'supplies', + token = empty_token + } + return items + end return { { @@ -857,6 +1097,8 @@ end local function apply_buffs() local starting_items = Public.get_func('starting_items') + local techs = Public.get_func('techs') + local limit_types = Public.get_func('limit_types') if this.buffs and next(this.buffs) then local total_buffs = 0 @@ -880,7 +1122,10 @@ local function apply_buffs() if not this.buffs_collected[buff_name] then this.buffs_collected[buff_name] = { - count = buff.state + name = 'Extra Reach', + count = buff.state, + discord = buff.discord, + force = true } else this.buffs_collected[buff_name].count = this.buffs_collected[buff_name].count + buff.state @@ -892,7 +1137,9 @@ local function apply_buffs() if not this.buffs_collected[buff.name] then this.buffs_collected[buff.name] = { - count = buff.state + count = buff.state, + discord = buff.discord, + force = true } else this.buffs_collected[buff.name].count = this.buffs_collected[buff.name].count + buff.state @@ -906,10 +1153,74 @@ local function apply_buffs() this.extra_wagons = this.extra_wagons + buff.state end + if not this.buffs_collected['locomotive'] then + this.buffs_collected['locomotive'] = { + name = 'Extra Wagons', + count = buff.state, + discord = buff.discord + } + else + if this.extra_wagons > 4 then + this.buffs_collected['locomotive'].count = this.extra_wagons + else + this.buffs_collected['locomotive'].count = this.extra_wagons + buff.state + end + end + if this.extra_wagons > 4 then this.extra_wagons = 4 end end + if buff.modifier == 'fish' then + limit_types[buff.name] = true + Public.set('all_the_fish', true) + if not this.buffs_collected['fish'] then + this.buffs_collected['fish'] = { + name = 'A thousand fishes', + discord = buff.discord + } + end + + local locomotive_cargo = Public.get('locomotive_cargo') + if not locomotive_cargo then + break + end + if not locomotive_cargo.valid then + break + end + locomotive_cargo.get_inventory(defines.inventory.cargo_wagon).insert({name = 'raw-fish', count = 999999}) + end + if buff.modifier == 'tech' then + if not this.buffs_collected['techs'] then + this.buffs_collected['techs'] = {} + end + if type(buff.techs) ~= 'table' then + break + end + + for _, tech in pairs(buff.techs) do + if tech then + if techs[tech.name] then + break + end + + if not techs[tech.name] then + techs[tech.name] = { + name = buff.name + } + end + + if not this.buffs_collected['techs'][tech.name] then + this.buffs_collected['techs'][tech.name] = { + name = tech.name, + buff_type = buff.name, + discord = buff.discord + } + end + force.technologies[tech.name].researched = true + end + end + end if buff.modifier == 'rpg' then local rpg_extra = RPG.get('rpg_extra') if buff.name == 'xp_bonus' then @@ -920,7 +1231,9 @@ local function apply_buffs() end if not this.buffs_collected['xp_bonus'] then this.buffs_collected['xp_bonus'] = { - count = buff.state + name = 'XP Bonus', + count = buff.state, + discord = buff.discord } else this.buffs_collected['xp_bonus'].count = this.buffs_collected['xp_bonus'].count + buff.state @@ -934,7 +1247,9 @@ local function apply_buffs() end if not this.buffs_collected['xp_level'] then this.buffs_collected['xp_level'] = { - count = buff.state + name = 'XP Level Bonus', + count = buff.state, + discord = buff.discord } else this.buffs_collected['xp_level'].count = this.buffs_collected['xp_level'].count + buff.state @@ -973,7 +1288,8 @@ local function apply_buffs() else this.buffs_collected['starting_items'][item.name] = { buff_type = buff.name, - count = item.count + count = item.count, + discord = buff.discord } end end @@ -999,7 +1315,7 @@ local function apply_startup_settings(settings) current_date = round(Utils.convert_date(current_date.year, current_date.month, current_date.day)) - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) settings = settings or {} local stored_date = this.current_date @@ -1052,7 +1368,7 @@ end local apply_settings_token = Task.register( function(data) - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) local settings = data and data.value or nil local current_time = Server.get_current_time() if not current_time then @@ -1091,6 +1407,7 @@ local apply_settings_token = local current_season = Public.get('current_season') if current_season then + ---@diagnostic disable-next-line: param-type-mismatch rendering.set_text(current_season, 'Season: ' .. this.season) end @@ -1173,7 +1490,6 @@ local apply_settings_dev_token = this.rounds_survived = settings.rounds_survived - Public.reset_stateful() Public.increase_enemy_damage_and_health() end ) @@ -1181,6 +1497,8 @@ local apply_settings_dev_token = local function grant_non_limit_reached_buff() local all_buffs = get_random_buff(true) local starting_items = Public.get_func('starting_items') + local techs = Public.get_func('techs') + local limit_types = Public.get_func('limit_types') for index, data in pairs(all_buffs) do for _, item_data in pairs(starting_items) do @@ -1188,6 +1506,18 @@ local function grant_non_limit_reached_buff() all_buffs[index] = nil end end + + for _, tech_data in pairs(techs) do + if tech_data.name == data.name then + all_buffs[index] = nil + end + end + + for limit_name, _ in pairs(limit_types) do + if limit_name == data.name then + all_buffs[index] = nil + end + end end shuffle(all_buffs) @@ -1198,7 +1528,7 @@ local function grant_non_limit_reached_buff() shuffle(all_buffs) if not all_buffs[1] then - return get_random_force_buff() + return get_random_buff(nil, true) end return all_buffs[1] @@ -1209,6 +1539,7 @@ function Public.save_settings() this.buffs[#this.buffs + 1] = granted_buff local settings = { + objectives_time_spent = this.objectives_time_spent, rounds_survived = this.rounds_survived, season = this.season, test_mode = this.test_mode, @@ -1216,7 +1547,7 @@ function Public.save_settings() current_date = this.current_date } - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) if server_name_matches then Server.set_data(dataset, dataset_key, settings) else @@ -1235,7 +1566,7 @@ function Public.save_settings_before_reset() current_date = this.current_date } - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) if server_name_matches then Server.set_data(dataset, dataset_key_previous, settings) else @@ -1254,6 +1585,10 @@ function Public.reset_stateful(refresh_gui, clear_buffs) this.enemies_boosted = false this.tasks_required_to_win = 6 + if not this.previous_objectives_time_spent then + this.previous_objectives_time_spent = {} + end + if this.test_mode then this.objectives = { randomized_zone = 2, @@ -1261,11 +1596,41 @@ function Public.reset_stateful(refresh_gui, clear_buffs) supplies = get_random_items(), single_item = get_random_item(), killed_enemies = 10, + killed_enemies_type = { + actual = 0, + expected = 10, + damage_type = damage_types[random(1, #damage_types)] + }, + handcrafted_items = { + actual = 0, + expected = 10, + name = 'rail' + }, + handcrafted_items_any = { + actual = 0, + expected = 10, + name = 'Any' + }, + cast_spell = { + actual = 0, + expected = 10, + name = 'pipe' + }, + cast_spell_any = { + actual = 0, + expected = 10, + name = 'Any' + }, + launch_item = { + actual = 0, + expected = 10, + name = 'raw-fish' + }, research_level_selection = get_random_research_recipe(), locomotive_market_coins_spent = 0, locomotive_market_coins_spent_required = 1, trees_farmed = 10, - rocks_farmed = 10, + minerals_farmed = 10, rockets_launched = 1 } else @@ -1292,6 +1657,51 @@ function Public.reset_stateful(refresh_gui, clear_buffs) if not this.objectives.killed_enemies or (this.objectives_completed ~= nil and this.objectives_completed.killed_enemies) then this.objectives.killed_enemies = scale(25000, 400000, 1.035) end + if not this.objectives.killed_enemies_type or (this.objectives_completed ~= nil and this.objectives_completed.killed_enemies_type) then + this.objectives.killed_enemies_type = { + actual = 0, + expected = scale(10000, 400000, 1.035), + damage_type = damage_types[random(1, #damage_types)] + } + end + if not this.objectives.handcrafted_items or (this.objectives_completed ~= nil and this.objectives_completed.handcrafted_items) then + local item = get_random_handcrafted_item() + this.objectives.handcrafted_items = { + actual = 0, + expected = item.count, + name = item.name + } + end + if not this.objectives.handcrafted_items_any or (this.objectives_completed ~= nil and this.objectives_completed.handcrafted_items_any) then + this.objectives.handcrafted_items_any = { + actual = 0, + expected = scale(50000, 4000000, 1.035), + name = 'Any' + } + end + if not this.objectives.cast_spell or (this.objectives_completed ~= nil and this.objectives_completed.cast_spell) then + local item = get_random_spell() + this.objectives.cast_spell = { + actual = 0, + expected = item.count, + name = item.name + } + end + if not this.objectives.cast_spell_any or (this.objectives_completed ~= nil and this.objectives_completed.cast_spell_any) then + this.objectives.cast_spell_any = { + actual = 0, + expected = scale(50000, 4000000, 1.035), + name = 'Any' + } + end + if not this.objectives.launch_item or (this.objectives_completed ~= nil and this.objectives_completed.launch_item) then + local item = get_random_handcrafted_item() + this.objectives.launch_item = { + actual = 0, + expected = scale(10, 700), + name = item.name + } + end if not this.objectives.research_level_selection or (this.objectives_completed ~= nil and this.objectives_completed.research_level_selection) then this.objectives.research_level_selection = get_random_research_recipe() end @@ -1301,18 +1711,40 @@ function Public.reset_stateful(refresh_gui, clear_buffs) required = scale(50000) } end - if not this.objectives.trees_farmed or (this.objectives_completed ~= nil and this.objectives_completed.trees_farmed) then - this.objectives.trees_farmed = scale(10000, 200000) - end - if not this.objectives.rocks_farmed or (this.objectives_completed ~= nil and this.objectives_completed.rocks_farmed) then - this.objectives.rocks_farmed = scale(20000, 250000) + if not this.objectives.minerals_farmed or (this.objectives_completed ~= nil and this.objectives_completed.minerals_farmed) then + this.objectives.minerals_farmed = scale(25000, 250000) end if not this.objectives.rockets_launched or (this.objectives_completed ~= nil and this.objectives_completed.rockets_launched) then this.objectives.rockets_launched = scale(10, 700) end end + local supplies = this.objectives.supplies + for _, supply in pairs(supplies) do + if supply and supply.total then + supply.count = supply.total + end + end + + if supplies.single_item and supplies.single_item.total then + supplies.single_item.count = supplies.single_item.total + end + + this.objectives.handcrafted_items.actual = 0 + this.objectives.handcrafted_items_any.actual = 0 + this.objectives.cast_spell.actual = 0 + this.objectives.cast_spell_any.actual = 0 + this.objectives.killed_enemies_type.actual = 0 + this.objectives.launch_item.actual = 0 + this.objectives.research_level_selection.research_count = 0 + this.objectives.locomotive_market_coins_spent.spent = 0 + this.objectives_completed = {} + if this.objectives_time_spent and next(this.objectives_time_spent) then + this.previous_objectives_time_spent[#this.previous_objectives_time_spent + 1] = this.objectives_time_spent + end + + this.objectives_time_spent = {} this.objectives_completed_count = 0 this.collection = { @@ -1320,7 +1752,7 @@ function Public.reset_stateful(refresh_gui, clear_buffs) time_until_attack_timer = nil, survive_for = nil, survive_for_timer = nil, - final_arena_disabled = true + final_arena_disabled = false } this.stateful_locomotive_migrated = false this.force_chunk = true @@ -1352,7 +1784,6 @@ function Public.reset_stateful(refresh_gui, clear_buffs) clear_all_stats() apply_buffs() - if refresh_gui then Public.refresh_frames() end @@ -1568,7 +1999,7 @@ function Public.stateful_on_server_started() return end - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) this.settings_applied = true @@ -1587,7 +2018,7 @@ Event.add( return end - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) this.settings_applied = true @@ -1669,17 +2100,329 @@ if _DEBUG then season = 4, test_mode = false, buffs = { - {name = 'laboratory_productivity_bonus', discord = 'Productivity bonus', modifier = 'force', state = 0.15}, - {name = 'character_running_speed_modifier', discord = 'Running speed modifier', modifier = 'force', state = 0.05}, - {name = 'production_1', discord = 'Production starting supplies', modifier = 'starting_items', limit = 2, add_per_buff = 1, items = {{name = 'steel-furnace', count = 4}, {name = 'solid-fuel', count = 100}}}, - {name = 'manual_crafting_speed_modifier', discord = 'Crafting speed modifier', modifier = 'force', state = 0.12}, - {name = 'manual_mining_speed_modifier', discord = 'Mining speed modifier', modifier = 'force', state = 0.15}, - {name = 'armor', discord = 'Armor starting supplies', modifier = 'starting_items', limit = 1, add_per_buff = 1, items = {{name = 'modular-armor', count = 1}, {name = 'solar-panel-equipment', count = 2}}}, - {name = 'heal-thy-buildings', discord = 'Repair starting supplies', modifier = 'starting_items', limit = 20, add_per_buff = 2, items = {{name = 'repair-pack', count = 5}}}, - {name = 'character_running_speed_modifier', discord = 'Running speed modifier', modifier = 'force', state = 0.05}, - {name = 'defense', discord = 'Defense starting supplies', modifier = 'starting_items', limit = 10, add_per_buff = 1, items = {{name = 'gun-turret', count = 2}, {name = 'firearm-magazine', count = 25}}}, - {name = 'worker_robots_speed_modifier', discord = 'Robot speed modifier', modifier = 'force', state = 0.5}, - {name = 'extra_wagons', discord = 'Extra wagon at start', modifier = 'locomotive', state = 1} + { + name = 'steel_axe_unlocked', + discord = 'Equipement tech - start with steel axe tech unlocked.', + modifier = 'tech', + limit = 1, + add_per_buff = 1, + techs = { + {name = 'steel-axe', count = 1} + } + }, + { + name = 'military_2_unlocked', + discord = 'Equipement tech - start with military 2 tech unlocked.', + modifier = 'tech', + limit = 1, + add_per_buff = 1, + techs = { + {name = 'military-2', count = 1} + } + }, + { + name = 'character_running_speed_modifier', + discord = 'Running speed modifier - run faster!', + modifier = 'force', + per_force = true, + state = 0.05 + }, + { + name = 'manual_mining_speed_modifier', + discord = 'Mining speed modifier - mine faster!', + modifier = 'force', + per_force = true, + state = 0.15 + }, + { + name = 'laboratory_speed_modifier', + discord = 'Laboratory speed modifier - labs work faster!', + modifier = 'force', + per_force = true, + state = 0.15 + }, + { + name = 'laboratory_productivity_bonus', + discord = 'Laboratory productivity bonus - labs dupe things!', + modifier = 'force', + per_force = true, + state = 0.15 + }, + { + name = 'worker_robots_storage_bonus', + discord = 'Robot storage bonus - robots carry more!', + modifier = 'force', + per_force = true, + state = 1 + }, + { + name = 'worker_robots_battery_modifier', + discord = 'Robot battery bonus - robots work longer!', + modifier = 'force', + per_force = true, + state = 1 + }, + { + name = 'worker_robots_speed_modifier', + discord = 'Robot speed modifier - robots move faster!', + modifier = 'force', + per_force = true, + state = 0.5 + }, + { + name = 'mining_drill_productivity_bonus', + discord = 'Drill productivity bonus - drills work faster!', + modifier = 'force', + per_force = true, + state = 0.5 + }, + { + name = 'character_health_bonus', + discord = 'Character health bonus - more health!', + modifier = 'force', + per_force = true, + state = 250 + }, + { + name = 'distance', + discord = 'RPG reach distance bonus - reach further!', + modifier = 'rpg_distance', + per_force = true, + modifiers = {'character_resource_reach_distance_bonus', 'character_item_pickup_distance_bonus', 'character_loot_pickup_distance_bonus', 'character_reach_distance_bonus'}, + state = 0.05 + }, + { + name = 'manual_crafting_speed_modifier', + discord = 'Crafting speed modifier - craft faster!', + modifier = 'force', + per_force = true, + state = 0.12 + }, + { + name = 'xp_bonus', + discord = 'RPG XP point bonus - more XP points from kills etc.', + modifier = 'rpg', + per_force = true, + state = 0.12 + }, + { + name = 'xp_level', + discord = 'RPG XP level bonus - start with more XP levels', + modifier = 'rpg', + per_force = true, + state = 20 + }, + { + name = 'chemicals_s', + discord = 'Starting items supplies - start with some sulfur', + modifier = 'starting_items', + limit = 200, + add_per_buff = 50, + items = { + {name = 'sulfur', count = 50} + } + }, + { + name = 'chemicals_p', + discord = 'Starting items supplies - start with some plastic bar', + modifier = 'starting_items', + limit = 200, + add_per_buff = 50, + items = { + {name = 'plastic-bar', count = 100} + } + }, + { + name = 'supplies', + discord = 'Starting items supplies - start with some copper and iron plates', + modifier = 'starting_items', + limit = 1000, + add_per_buff = 100, + items = { + {name = 'iron-plate', count = 100}, + {name = 'copper-plate', count = 100} + } + }, + { + name = 'supplies_1', + discord = 'Starting items supplies - start with more copper and iron plates', + modifier = 'starting_items', + limit = 1000, + add_per_buff = 200, + items = { + {name = 'iron-plate', count = 200}, + {name = 'copper-plate', count = 200} + } + }, + { + name = 'supplies_2', + discord = 'Starting items supplies - start with even more copper and iron plates', + modifier = 'starting_items', + limit = 1000, + add_per_buff = 400, + items = { + {name = 'iron-plate', count = 400}, + {name = 'copper-plate', count = 400} + } + }, + { + name = 'defense', + discord = 'Defense starting supplies - start with some turrets and ammo', + modifier = 'starting_items', + limit = 10, + add_per_buff = 1, + items = { + {name = 'gun-turret', count = 2}, + {name = 'firearm-magazine', count = 25} + } + }, + { + name = 'defense_3', + discord = 'Defense starting supplies - start with more turrets and ammo', + modifier = 'starting_items', + limit = 1, + add_per_buff = 1, + items = { + {name = 'rocket-launcher', count = 1}, + {name = 'rocket', count = 100} + } + }, + { + name = 'armor', + discord = 'Armor starting supplies - start with some armor and solar panels', + modifier = 'starting_items', + limit = 1, + add_per_buff = 1, + items = { + {name = 'modular-armor', count = 1}, + {name = 'solar-panel-equipment', count = 2} + } + }, + { + name = 'production', + discord = 'Production starting supplies - start with some furnaces and coal', + modifier = 'starting_items', + limit = 2, + add_per_buff = 1, + items = { + {name = 'stone-furnace', count = 4}, + {name = 'coal', count = 100} + } + }, + { + name = 'production_1', + discord = 'Production starting supplies - start with some steel furnaces and solid fuel', + modifier = 'starting_items', + limit = 2, + add_per_buff = 1, + items = { + {name = 'steel-furnace', count = 4}, + {name = 'solid-fuel', count = 100} + } + }, + { + name = 'fast_startup', + discord = 'Assembling starting supplies - start with some assembling machines T1', + modifier = 'starting_items', + limit = 25, + add_per_buff = 2, + items = { + {name = 'assembling-machine-1', count = 2} + } + }, + { + name = 'fast_startup_1', + discord = 'Assembling starting supplies - start with some assembling machines T2', + modifier = 'starting_items', + limit = 25, + add_per_buff = 2, + items = { + {name = 'assembling-machine-2', count = 2} + } + }, + { + name = 'fast_startup_2', + discord = 'Assembling starting supplies - start with some assembling machines T3', + modifier = 'starting_items', + limit = 25, + add_per_buff = 2, + items = { + {name = 'assembling-machine-3', count = 2} + } + }, + { + name = 'heal-thy-buildings', + discord = 'Repair starting supplies - start with some repair packs', + modifier = 'starting_items', + limit = 20, + add_per_buff = 2, + items = { + {name = 'repair-pack', count = 5} + } + }, + { + name = 'extra_wagons', + discord = 'Extra wagon at start', + modifier = 'locomotive', + state = 1 + }, + { + name = 'american_oil', + discord = 'Oil tech - start with some crude oil barrels', + modifier = 'starting_items', + limit = 40, + add_per_buff = 20, + items = { + {name = 'crude-oil-barrel', count = 20} + } + }, + { + name = 'steel_plates', + discord = 'Steel tech - start with some steel plates', + modifier = 'starting_items', + limit = 200, + add_per_buff = 100, + items = { + {name = 'steel-plate', count = 100} + } + }, + { + name = 'red_science', + discord = 'Science tech - start with some red science packs', + modifier = 'starting_items', + limit = 200, + add_per_buff = 10, + items = { + {name = 'automation-science-pack', count = 10} + } + }, + { + name = 'roboport_equipement', + discord = 'Equipement tech - start with a personal roboport', + modifier = 'starting_items', + limit = 4, + add_per_buff = 1, + items = { + {name = 'personal-roboport-equipment', count = 1} + } + }, + { + name = 'mk1_tech_unlocked', + discord = 'Equipement tech - start with power armor tech unlocked.', + modifier = 'tech', + limit = 1, + add_per_buff = 1, + techs = { + {name = 'power-armor', count = 1} + } + }, + { + name = 'all_the_fish', + discord = 'Wagon is full of fish!', + modifier = 'fish', + limit = 1, + add_per_buff = 1 + } }, current_date = 1711187954 } diff --git a/maps/mountain_fortress_v3/table.lua b/maps/mountain_fortress_v3/table.lua index 1f5c47b4..9551d7e5 100644 --- a/maps/mountain_fortress_v3/table.lua +++ b/maps/mountain_fortress_v3/table.lua @@ -30,6 +30,9 @@ Public.events = { on_market_item_purchased = Event.generate_event_name('on_market_item_purchased') } +local scenario_name = 'Mtn Fortress' +Public.scenario_name = scenario_name + Global.register( this, function(tbl) @@ -134,6 +137,7 @@ function Public.reset_main_table() this.locomotive_health = 10000 this.locomotive_max_health = 10000 this.extra_wagons = 0 + this.all_the_fish = false this.gap_between_zones = { set = false, gap = 900, @@ -177,6 +181,10 @@ function Public.reset_main_table() this.robotics_deployed = false this.upgrades = { showed_text = false, + burner_generator = { + limit = 50, + bought = 0 + }, landmine = { limit = 25, bought = 0, @@ -375,7 +383,7 @@ function Public.remove(key, sub_key) end function Public.save_stateful_settings() - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) if server_name_matches then Server.set_data(dataset, dataset_key, stateful_settings) @@ -387,7 +395,7 @@ end local apply_settings_token = Task.register( function(data) - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) local settings = data and data.value or nil if not settings then @@ -412,7 +420,7 @@ Event.add( local start_data = Server.get_start_data() if not start_data.initialized then - local server_name_matches = Server.check_server_name('Mtn Fortress') + local server_name_matches = Server.check_server_name(scenario_name) if server_name_matches then Server.try_get_data(dataset, dataset_key, apply_settings_token) diff --git a/modules/difficulty_vote_by_amount.lua b/modules/difficulty_vote_by_amount.lua index 3536e01e..018e8c96 100644 --- a/modules/difficulty_vote_by_amount.lua +++ b/modules/difficulty_vote_by_amount.lua @@ -79,6 +79,13 @@ local function clear_main_frame(player) end end +local function clear_top_frame(player) + local top = player.gui.top + if top[top_button_name] and top[top_button_name].valid then + top[top_button_name].destroy() + end +end + function Public.difficulty_gui() if not this.show_gui then return @@ -459,5 +466,7 @@ Event.add(defines.events.on_player_joined_game, on_player_joined_game) Event.add(defines.events.on_player_left_game, on_player_left_game) Public.top_button_name = top_button_name +Public.clear_main_frame = clear_main_frame +Public.clear_top_frame = clear_top_frame return Public diff --git a/modules/rpg/functions.lua b/modules/rpg/functions.lua index af25481d..c2c42985 100644 --- a/modules/rpg/functions.lua +++ b/modules/rpg/functions.lua @@ -739,6 +739,114 @@ function Public.aoe_punch(cause, entity, damage, final_damage_amount) end end +function Public.add_tidal_wave(cause, ent_position, shape, length, max_spread) + local rpg_extra = Public.get('rpg_extra') + + if not cause or not cause.valid then + return + end + + local wave = { + cause = cause, + start_position = cause.position, + direction = {ent_position.x - cause.position.x, ent_position.y - cause.position.y}, + length = length or 24, + base_spread = 0.5, + max_spread = max_spread or 5, + shape = shape or false, + tick = 0 + } + local vector_length = math.sqrt(wave.direction[1] ^ 2 + wave.direction[2] ^ 2) + wave.direction = {wave.direction[1] / vector_length, wave.direction[2] / vector_length} + + rpg_extra.tidal_waves = rpg_extra.tidal_waves or {} + rpg_extra.tidal_waves[#rpg_extra.tidal_waves + 1] = wave +end + +--Melee damage modifier +function Public.update_tidal_wave() + local rpg_extra = Public.get('rpg_extra') + + if not rpg_extra.tidal_waves or not next(rpg_extra.tidal_waves) then + return + end + + for id, wave in pairs(rpg_extra.tidal_waves) do + if not wave then + break + end + + local cone = wave.shape and wave.shape == 'cone' or false + + local wave_player = wave.cause + if not wave_player or not wave_player.valid then + rpg_extra.tidal_waves[id] = nil + return + end + + if wave.tick < wave.length then + local surface = wave.cause.surface + local cause_position = wave.start_position + local i = wave.tick + 1 + + local current_spread = wave.base_spread + (wave.max_spread - wave.base_spread) * (i / wave.length) + + if not cone then + for j = -wave.max_spread, wave.max_spread do + local offset_x = cause_position.x + wave.direction[1] * i + j * wave.direction[2] + local offset_y = cause_position.y + wave.direction[2] * i - j * wave.direction[1] + local position = {offset_x, offset_y} + + local next_offset_x = cause_position.x + wave.direction[1] * (i + 1) + j * wave.direction[2] + local next_offset_y = cause_position.y + wave.direction[2] * (i + 1) - j * wave.direction[1] + local next_position = {next_offset_x, next_offset_y} + + surface.create_entity({name = 'water-splash', position = position}) + -- surface.create_trivial_smoke({name = 'poison-capsule-smoke', position = position}) + local sound = 'utility/build_small' + wave_player.play_sound {path = sound, volume_modifier = 1} + + for _, entity in pairs(surface.find_entities({{position[1] - 1, position[2] - 1}, {position[1] + 1, position[2] + 1}})) do + if entity.valid and entity.name ~= 'character' and entity.destructible and entity.type == 'unit' and entity.force.index ~= 3 then + local new_pos = surface.find_non_colliding_position('character', next_position, 3, 0.5) + if new_pos then + entity.teleport(new_pos) + end + end + end + end + else + for j = -current_spread, current_spread, wave.base_spread do + local offset_x = cause_position.x + wave.direction[1] * i + j * wave.direction[2] + local offset_y = cause_position.y + wave.direction[2] * i - j * wave.direction[1] + local position = {offset_x, offset_y} + + local next_offset_x = cause_position.x + wave.direction[1] * (i + 1) + j * wave.direction[2] + local next_offset_y = cause_position.y + wave.direction[2] * (i + 1) - j * wave.direction[1] + local next_position = {next_offset_x, next_offset_y} + -- surface.create_trivial_smoke({name = 'poison-capsule-smoke', position = position}) + surface.create_entity({name = 'water-splash', position = position}) + local sound = 'utility/build_small' + wave_player.play_sound {path = sound, volume_modifier = 1} + + for _, entity in pairs(surface.find_entities({{position[1] - 1, position[2] - 1}, {position[1] + 1, position[2] + 1}})) do + if entity.valid and entity.name ~= 'character' and entity.destructible and entity.type == 'unit' and entity.force.index ~= 3 then + local new_pos = surface.find_non_colliding_position('character', next_position, 3, 0.5) + if new_pos then + entity.teleport(new_pos) + end + end + end + end + end + + wave.tick = wave.tick + 1 + else + rpg_extra.tidal_waves[id] = nil + end + end +end + function Public.level_limit_exceeded(player, value) local rpg_extra = Public.get('rpg_extra') local rpg_t = Public.get_value_from_player(player.index) @@ -1219,6 +1327,7 @@ function Public.rpg_reset_player(player, one_time_reset) dropdown_select_index_3 = 1, dropdown_select_name_3 = Public.all_spells[1].name[1], allocate_index = 1, + amount = 0, explosive_bullets = false, enable_entity_spawn = false, health_bar = rpg_t.health_bar, @@ -1268,6 +1377,7 @@ function Public.rpg_reset_player(player, one_time_reset) dropdown_select_index_3 = 1, dropdown_select_name_3 = Public.all_spells[1].name[1], allocate_index = 1, + amount = 0, explosive_bullets = false, enable_entity_spawn = false, points_left = 0, diff --git a/modules/rpg/gui.lua b/modules/rpg/gui.lua index 43e1898c..29f314e6 100644 --- a/modules/rpg/gui.lua +++ b/modules/rpg/gui.lua @@ -49,11 +49,10 @@ function Public.draw_gui_char_button(player) if b then b.style.font_color = {165, 165, 165} b.style.font = 'heading-3' - b.style.minimal_height = 38 - b.style.maximal_height = 38 - b.style.minimal_width = 50 - b.style.padding = 0 - b.style.margin = 0 + b.style.minimal_height = 36 + b.style.maximal_height = 36 + b.style.minimal_width = 40 + b.style.padding = -2 end else if player.gui.top[draw_main_frame_name] then @@ -218,7 +217,7 @@ local function draw_main_frame(player, location) main_frame.location = location else if ComfyGui.get_mod_gui_top_frame() then - main_frame.location = {x = 1, y = 55} + main_frame.location = {x = 0, y = 67} else main_frame.location = {x = 1, y = 45} end @@ -543,6 +542,7 @@ Gui.on_click( if is_spamming then return end + local player = event.player if not player or not player.valid or not player.character then return diff --git a/modules/rpg/main.lua b/modules/rpg/main.lua index ccfd1e52..71f022b1 100644 --- a/modules/rpg/main.lua +++ b/modules/rpg/main.lua @@ -6,10 +6,12 @@ local AntiGrief = require 'utils.antigrief' local SpamProtection = require 'utils.spam_protection' local BiterHealthBooster = require 'modules.biter_health_booster_v2' local Explosives = require 'modules.explosives' - +local StatData = require 'utils.datastore.statistics' local WD = require 'modules.wave_defense.table' local Math2D = require 'math2d' +StatData.add_normalize('spells', 'Spells casted') + --RPG Settings local enemy_types = Public.enemy_types local die_cause = Public.die_cause @@ -1009,11 +1011,21 @@ local function on_player_used_capsule(event) cast_spell = Public.cast_spell } + rpg_t.amount = 0 + local cast_spell = spell.callback(data, funcs) if not cast_spell then return end + if rpg_t.amount == 0 then + rpg_t.amount = 1 + end + + Event.raise(Public.events.on_spell_cast_success, {player_index = player.index, spell_name = spell.entityName, amount = rpg_t.amount}) + + StatData.get_data(player):increase('spells') + if spell.enforce_cooldown then Public.register_cooldown_for_player(player, spell) end @@ -1078,6 +1090,7 @@ Event.add(defines.events.on_player_used_capsule, on_player_used_capsule) Event.add(defines.events.on_player_changed_surface, on_player_changed_surface) Event.add(defines.events.on_player_removed, on_player_removed) Event.on_nth_tick(10, tick) +Event.on_nth_tick(2, Public.update_tidal_wave) Event.add( defines.events.on_gui_closed, diff --git a/modules/rpg/spells.lua b/modules/rpg/spells.lua index 896fad31..27f8492f 100644 --- a/modules/rpg/spells.lua +++ b/modules/rpg/spells.lua @@ -1,6 +1,5 @@ local Public = require 'modules.rpg.table' -local Token = require 'utils.token' -local Task = require 'utils.task' +local Task = require 'utils.task_token' local Ai = require 'modules.ai' local Modifiers = require 'utils.player_modifiers' @@ -14,9 +13,10 @@ local states = { } local restore_movement_speed_token +local repeat_sound_token local repair_buildings = - Token.register( + Task.register( function(data) local entity = data.entity if entity and entity.valid then @@ -34,6 +34,52 @@ local repair_buildings = end ) +repeat_sound_token = + Task.register( + function(event) + local player_index = event.player_index + local player = game.get_player(player_index) + if not player or not player.valid then + return + end + + local sound = event.sound or 'utility/armor_insert' + + local spell_active = Public.get_value_from_player(player_index, 'has_custom_spell_active') + + if spell_active then + player.play_sound {path = sound, volume_modifier = 1} + if player.character ~= nil then + player.character.surface.create_entity({name = 'water-splash', position = player.position}) + end + Task.set_timeout_in_ticks(30, repeat_sound_token, event) + else + player.play_sound {path = sound, volume_modifier = 1} + return + end + end +) + +local x_marks_the_spot_token = + Task.register( + function(event) + local player_index = event.player_index + local old_surface_index = event.old_surface_index + local player = game.get_player(player_index) + if not player or not player.valid then + return + end + local old_position = event.old_position + if not old_position then + return + end + + player.teleport(old_position, old_surface_index) + Public.set_active_spell_disabled(player_index) + Task.set_timeout_in_ticks(5, repeat_sound_token, {player_index = player.index, sound = 'utility/new_objective'}) + end +) + local function get_area(pos, dist) local area = { left_top = { @@ -48,6 +94,33 @@ local function get_area(pos, dist) return area end +local levels = { + [150] = {length = 26, max_spread = 6}, + [200] = {length = 27, max_spread = 6}, + [250] = {length = 28, max_spread = 7}, + [300] = {length = 29, max_spread = 7}, + [350] = {length = 30, max_spread = 8}, + [400] = {length = 31, max_spread = 8} +} + +local function get_level_data(player_level) + local closest_level = nil + + for level, _ in pairs(levels) do + if player_level >= level then + closest_level = level + else + break + end + end + + if closest_level then + return levels[closest_level] + else + return {length = 26, max_spread = 6} + end +end + local function area_of_effect(player, position, state, radius, callback, find_entities) if not radius then return @@ -87,7 +160,7 @@ local function area_of_effect(player, position, state, radius, callback, find_en end restore_movement_speed_token = - Token.register( + Task.register( function(event) local player_index = event.player_index local rpg_t = event.rpg_t @@ -152,6 +225,7 @@ local function create_projectiles(data) } do_projectile(surface, projectile_types[self.entityName].name, position, force, target_pos, range) Public.remove_mana(player, self.mana_cost) + rpg_t.amount = rpg_t.amount + 1 if self.damage then for _, e in pairs(surface.find_entities_filtered({area = damage_area})) do damage_entity(e) @@ -222,6 +296,7 @@ local function create_entity(data) has_cast = true e.direction = player.character.direction Public.remove_mana(player, self.mana_cost) + rpg_t.amount = rpg_t.amount + 1 end end end @@ -259,6 +334,7 @@ local function insert_onto(data) player.insert({name = self.entityName, count = self.amount}) Public.remove_mana(player, self.mana_cost) + rpg_t.amount = rpg_t.amount + 1 end else player.insert({name = self.entityName, count = self.amount}) @@ -1064,6 +1140,82 @@ spells[#spells + 1] = { return true end } + +spells[#spells + 1] = { + name = {'spells.mark_spot'}, + entityName = 'mark-spot', + target = true, + force = 'player', + level = 60, + type = 'special', + mana_cost = 340, + cooldown = 2000, + enforce_cooldown = true, + check_if_active = true, + enabled = true, + log_spell = true, + sprite = 'virtual-signal/signal-X', + special_sprite = 'virtual-signal=signal-X', + tooltip = 'Warps you back to the locomotive and after a couple of seconds you return to your previous location.', + callback = function(data) + local player = data.player + local surface = data.surface + local old_position = player.position + local rpg_t = data.rpg_t + rpg_t.has_custom_spell_active = true + + local pos = surface.find_non_colliding_position('character', game.forces.player.get_spawn_position(surface), 3, 0, 5) + if pos then + player.teleport(pos, surface) + else + pos = game.forces.player.get_spawn_position(surface) + player.teleport(pos, surface) + end + + Task.set_timeout_in_ticks(5, repeat_sound_token, {player_index = player.index}) + Task.set_timeout_in_ticks(300, x_marks_the_spot_token, {player_index = player.index, old_position = old_position, old_surface_index = surface.index}) + Public.remove_mana(player, 340) + Public.cast_spell(player) + return true + end +} + +spells[#spells + 1] = { + name = {'spells.tidal_wave'}, + entityName = 'tidal-wave', + target = true, + force = 'player', + level = 100, + type = 'special', + mana_cost = 340, + cooldown = 1000, + enforce_cooldown = true, + check_if_active = false, + enabled = true, + log_spell = false, + sprite = 'virtual-signal/signal-T', + special_sprite = 'virtual-signal=signal-T', + tooltip = 'Spawns a tidal wave that pushes the enemies back.', + callback = function(data) + local player = data.player + local rpg_t = data.rpg_t + local cursor_position = data.position + + local shape = 'cone' + + if random(1, 2) == 1 then + shape = 'square' + end + + local level_data = get_level_data(rpg_t.level) + + Public.add_tidal_wave(player, cursor_position, shape, level_data.length, level_data.max_spread) + + Public.remove_mana(player, 340) + Public.cast_spell(player) + return true + end +} spells[#spells + 1] = { name = {'spells.charge'}, entityName = 'haste', diff --git a/modules/rpg/table.lua b/modules/rpg/table.lua index 7109ad27..557dc005 100644 --- a/modules/rpg/table.lua +++ b/modules/rpg/table.lua @@ -34,6 +34,10 @@ Global.register( ) local Public = {} +Public.events = { + on_spell_cast_success = Event.generate_event_name('on_spell_cast_success'), + on_spell_cast_failure = Event.generate_event_name('on_spell_cast_failure') +} Public.points_per_level = 5 @@ -195,6 +199,22 @@ function Public.set_value_to_player(key, value, setter) end end +--- Sets set_active_spell_enabled as enabled. +---@param player_index string +function Public.set_active_spell_enabled(player_index) + if (this.rpg_t[player_index]) then + this.rpg_t[player_index].has_custom_spell_active = true + end +end + +--- Sets set_active_spell_disabled as nil. +---@param player_index string +function Public.set_active_spell_disabled(player_index) + if this.rpg_t[player_index] then + this.rpg_t[player_index].has_custom_spell_active = false + end +end + --- Sets a new table to rpg_t table ---@param key string ---@param tbl table diff --git a/modules/wave_defense/gui.lua b/modules/wave_defense/gui.lua index cb3bd662..b74a1fee 100644 --- a/modules/wave_defense/gui.lua +++ b/modules/wave_defense/gui.lua @@ -1,11 +1,35 @@ local Public = require 'modules.wave_defense.table' +local Gui = require 'utils.gui' local BiterHealthBooster = require 'modules.biter_health_booster_v2' local floor = math.floor +local function get_top_frame_custom(player, name) + if Gui.get_mod_gui_top_frame() then + return Gui.get_button_flow(player)[name] + else + return player.gui.top[name] + end +end + local function create_gui(player) - local frame = player.gui.top.add({type = 'frame', name = 'wave_defense', style = 'finished_game_subheader_frame'}) - frame.style.maximal_height = 38 + local frame + + if Gui.get_mod_gui_top_frame() then + frame = + Gui.add_mod_button( + player, + { + type = 'frame', + name = 'wave_defense', + style = 'finished_game_subheader_frame' + } + ) + frame.style.maximal_height = 36 + else + frame = player.gui.top.add({type = 'frame', name = 'wave_defense', style = 'finished_game_subheader_frame'}) + frame.style.maximal_height = 38 + end local label = frame.add({type = 'label', caption = ' ', name = 'label'}) label.style.font_color = {r = 0.88, g = 0.88, b = 0.88} @@ -19,7 +43,7 @@ local function create_gui(player) wave_number_label.style.font_color = {r = 0.33, g = 0.66, b = 0.9} local progressbar = frame.add({type = 'progressbar', name = 'progressbar', value = 0}) - progressbar.style = 'achievement_progressbar' + progressbar.style = 'achievement_progressbar' ---@class LuaGuiElementStyle progressbar.style.minimal_width = 96 progressbar.style.maximal_width = 96 progressbar.style.padding = -1 @@ -61,16 +85,16 @@ end function Public.update_gui(player) local final_battle = Public.get('final_battle') if final_battle then - if player.gui.top.wave_defense and player.gui.top.wave_defense.valid then - player.gui.top.wave_defense.destroy() + if get_top_frame_custom(player, 'wave_defense') and get_top_frame_custom(player, 'wave_defense').valid then + get_top_frame_custom(player, 'wave_defense').destroy() end return end - if not player.gui.top.wave_defense then + if not get_top_frame_custom(player, 'wave_defense') then create_gui(player) end - local gui = player.gui.top.wave_defense + local gui = get_top_frame_custom(player, 'wave_defense') local biter_health_boost = 1 local biter_health_boosts = BiterHealthBooster.get('biter_health_boost') @@ -93,6 +117,7 @@ function Public.update_gui(player) gui.wave_number.caption = wave_number if wave_number == 0 then gui.label.caption = {'wave_defense.gui_1'} + gui.label.tooltip = 'Next pause will occur in: ' .. floor((Public.get('next_pause_interval') - game.tick) / 60 / 60) + 1 .. ' minute(s)' gui.wave_number.caption = floor((next_wave - game.tick) / 60) + 1 .. 's' end local interval = next_wave - last_wave @@ -105,11 +130,13 @@ function Public.update_gui(player) gui.progressbar.value = value else gui.label.caption = {'wave_defense.gui_4'} + gui.label.tooltip = 'Wave: ' .. wave_number local pause_for = floor((paused_waves_for - game.tick) / 60) + 1 if pause_for < 0 then pause_for = 0 end gui.wave_number.caption = pause_for .. 's' + gui.wave_number.tooltip = 'Wave: ' .. wave_number local interval = paused_waves_for - last_pause local value = 1 - (paused_waves_for - game.tick) / interval @@ -124,6 +151,7 @@ function Public.update_gui(player) gui.threat.caption = {'wave_defense.gui_3'} gui.threat.tooltip = {'wave_defense.tooltip_1', biter_health_boost * 100, max_active_biters} + ---@diagnostic disable-next-line: param-type-mismatch gui.threat_value.caption = floor(threat) gui.threat_value.tooltip = { 'wave_defense.tooltip_1', diff --git a/modules/wave_defense/pause_waves.lua b/modules/wave_defense/pause_waves.lua index ca1fd12d..3c7ed640 100644 --- a/modules/wave_defense/pause_waves.lua +++ b/modules/wave_defense/pause_waves.lua @@ -259,6 +259,8 @@ Event.on_nth_tick( return end + Public.set('next_pause_interval', game.tick + 216000) + local pause_without_votes = Public.get('pause_without_votes') if pause_without_votes then Public.toggle_pause_wave_without_votes() diff --git a/modules/wave_defense/table.lua b/modules/wave_defense/table.lua index ee1c4c45..d7e99c08 100644 --- a/modules/wave_defense/table.lua +++ b/modules/wave_defense/table.lua @@ -70,6 +70,7 @@ function Public.reset_wave_defense() this.paused = false this.pause_without_votes = true this.pause_wave_in_ticks = 18000 -- 5 minutes + this.next_pause_interval = game.tick + 216000 -- 1 hour this.game_lost = false this.get_random_close_spawner_attempts = 5 this.group_size = 2 diff --git a/utils/command_handler.lua b/utils/command_handler.lua index ccefa98b..cd9db51e 100644 --- a/utils/command_handler.lua +++ b/utils/command_handler.lua @@ -10,6 +10,11 @@ local function on_console_command(event) local commands = { ['editor'] = true, + ['open'] = true, + ['cheat'] = true, + ['permissions'] = true, + ['banlist'] = true, + ['config'] = true, ['command'] = true, ['silent-command'] = true, ['sc'] = true, diff --git a/utils/commands/misc.lua b/utils/commands/misc.lua index a9de856e..15039a29 100644 --- a/utils/commands/misc.lua +++ b/utils/commands/misc.lua @@ -7,6 +7,7 @@ local Global = require 'utils.global' local BottomFrame = require 'utils.gui.bottom_frame' local Gui = require 'utils.gui' local SpamProtection = require 'utils.spam_protection' +local Discord = require 'utils.discord_handler' local this = { players = {}, @@ -163,6 +164,62 @@ commands.add_command( end ) +commands.add_command( + 'repair', + 'Revives all ghost entities.', + function(cmd) + local player = game.player + local param = tonumber(cmd.parameter) + + if not (player and player.valid) then + return + end + + local p = player.print + if not player.admin then + p("[ERROR] You're not admin!", Color.fail) + return + end + + if param == nil then + player.print('[ERROR] Must specify radius!', Color.fail) + return + end + if param > 50 then + player.print('[ERROR] Value is too big.', Color.fail) + return + end + + if not this.revive_warning then + this.revive_warning = true + player.print('[WARNING] This command will revive all the ghost entities in the given radius, run this command again if you really want to do this!', Color.yellow) + return + end + + local radius = {{x = (player.position.x + -param), y = (player.position.y + -param)}, {x = (player.position.x + param), y = (player.position.y + param)}} + + local c = 0 + for _, v in pairs(player.surface.find_entities_filtered {type = 'entity-ghost', area = radius}) do + if v and v.valid then + c = c + 1 + v.silent_revive() + end + end + + if c == 0 then + player.print('No entities to repair were found!', Color.warning) + this.revive_warning = nil + return + end + + Discord.send_notification_raw(nil, player.name .. ' repaired ' .. c .. ' entities!') + + player.play_sound {path = 'utility/new_objective', volume_modifier = 1} + player.print('Repaired ' .. c .. ' entities!', Color.success) + this.revive_warning = nil + end +) + commands.add_command( 'dump_layout', 'Dump the current map-layout.', diff --git a/utils/datastore/init.lua b/utils/datastore/init.lua new file mode 100644 index 00000000..8176ec89 --- /dev/null +++ b/utils/datastore/init.lua @@ -0,0 +1,12 @@ +require 'utils.datastore.server_ups_data' +require 'utils.datastore.current_time_data' +require 'utils.datastore.color_data' +require 'utils.datastore.session_data' +require 'utils.datastore.statistics' +require 'utils.datastore.jail_data' +require 'utils.datastore.quickbar_data' +require 'utils.datastore.warning_on_join_data' +require 'utils.datastore.message_on_join_data' +require 'utils.datastore.player_tag_data' +require 'utils.datastore.supporters' +require 'utils.datastore.banhandler' diff --git a/utils/datastore/jail_data.lua b/utils/datastore/jail_data.lua index 86d57bf4..088829f5 100644 --- a/utils/datastore/jail_data.lua +++ b/utils/datastore/jail_data.lua @@ -9,6 +9,9 @@ local Event = require 'utils.event' local Utils = require 'utils.core' local table = require 'utils.table' local Gui = require 'utils.gui' +local StatData = require 'utils.datastore.statistics' + +StatData.add_normalize('jailed', 'Jailed') local module_name = '[Jail handler] ' @@ -72,7 +75,12 @@ Global.register( end ) -local Public = {} +local Public = { + events = { + on_player_jailed = Event.generate_event_name('on_player_jailed'), + on_player_unjailed = Event.generate_event_name('on_player_unjailed') + } +} local function validate_entity(entity) if not (entity and entity.valid) then @@ -641,6 +649,10 @@ local function jail(player, offender, msg, raised, mute) set_data(jailed_data_set, offender, {jailed = true, actor = player, reason = msg, date = date}) end + Event.raise(Public.events.on_player_jailed, {player_index = offender.index}) + + StatData.get_data(player):increase('jailed') + Utils.print_to(nil, message) local data = Server.build_embed_data() data.username = offender @@ -703,6 +715,10 @@ local function jail_temporary(player, offender, msg, mute) votejail[offender.name].jailed = true end + Event.raise(Public.events.on_player_jailed, {player_index = offender.index}) + + StatData.get_data(player):increase('jailed') + offender.clear_console() draw_notice_frame(offender) @@ -728,6 +744,8 @@ local function free(player, offender) set_data(jailed_data_set, offender, nil) + Event.raise(Public.events.on_player_unjailed, {player_index = offender.index}) + Utils.print_to(nil, message) local data = Server.build_embed_data() data.username = offender diff --git a/utils/datastore/quickbar_data.lua b/utils/datastore/quickbar_data.lua index a4bcbc79..bfe431d7 100644 --- a/utils/datastore/quickbar_data.lua +++ b/utils/datastore/quickbar_data.lua @@ -18,6 +18,14 @@ local this = { logistics = {} } +local ignored_items = { + ['blueprint'] = true, + ['blueprint-book'] = true, + ['deconstruction-planner'] = true, + ['spidertron-remote'] = true, + ['upgrade-planner'] = true +} + Global.register( this, function(t) @@ -123,7 +131,7 @@ function Public.save_quickbar(player) for i = 1, 100 do local slot = player.get_quick_bar_slot(i) - if slot ~= nil then + if slot ~= nil and not ignored_items[slot.name] then slots[i] = slot.name end end diff --git a/utils/datastore/session_data.lua b/utils/datastore/session_data.lua index b9ead70c..895a94e7 100644 --- a/utils/datastore/session_data.lua +++ b/utils/datastore/session_data.lua @@ -10,15 +10,15 @@ local Event = require 'utils.event' local table = require 'utils.table' local set_timeout_in_ticks = Task.set_timeout_in_ticks + local session_data_set = 'sessions' local session = {} local online_track = {} local trusted = {} local settings = { - -- local trusted_value = 2592000 -- 12h - trusted_value = 5184000, -- 24h - required_only_time_to_save_time = 36000, -- nearest prime to 10 minutes in ticks - nth_tick = 18000 -- nearest prime to 5 minutes in ticks + trusted_value = 24 * 60 * 3600, -- 24h + required_only_time_to_save_time = 10 * 3600, + nth_tick = 5 * 3600 } local set_data = Server.set_data local try_get_data = Server.try_get_data diff --git a/utils/datastore/statistics.lua b/utils/datastore/statistics.lua new file mode 100644 index 00000000..9ad0cd54 --- /dev/null +++ b/utils/datastore/statistics.lua @@ -0,0 +1,515 @@ +-- created by Gerkiz for ComfyFactorio +local Global = require 'utils.global' +local Token = require 'utils.token' +local Task = require 'utils.task' +local Server = require 'utils.server' +local Event = require 'utils.event' + +local set_timeout_in_ticks = Task.set_timeout_in_ticks +local statistics_dataset = 'statistics' + +local set_data = Server.set_data +local try_get_data = Server.try_get_data +local e = defines.events +local floor = math.floor + +local events = { + map_tags_made = e.on_chart_tag_added, + chat_messages = e.on_console_chat, + commands_used = e.on_console_command, + machines_built = e.on_built_entity, + items_picked_up = e.on_picked_up_item, + tiles_built = e.on_player_built_tile, + join_count = e.on_player_joined_game, + deaths = e.on_player_died, + entities_repaired = e.on_player_repaired_entity, + items_crafted = e.on_player_crafted_item, + capsules_used = e.on_player_used_capsule, + tiles_removed = e.on_player_mined_tile, + deconstructer_planner_used = e.on_player_deconstructed_area +} + +local settings = { + required_only_time_to_save_time = 10 * 3600, + afk_time = 5 * 3600, + nth_tick = 5 * 3600 +} + +local Public = { + events = { + on_player_removed = Event.generate_event_name('on_player_removed') + } +} + +local normalized_names = { + ['map_tags_made'] = {name = 'Map-tags created', tooltip = "Tags that you've created in minimap."}, + ['chat_messages'] = {name = 'Messages', tooltip = 'Messages sent in chat.'}, + ['commands_used'] = {name = 'Commands', tooltip = 'Commands used in console.'}, + ['machines_built'] = {name = 'Entities built', tooltip = 'Entities built by the player.'}, + ['items_picked_up'] = {name = 'Items picked-up', tooltip = 'Items picked-up by the player.'}, + ['tiles_built'] = {name = 'Tiles placed', tooltip = 'Tiles placed by the player.'}, + ['join_count'] = {name = 'Join count', tooltip = 'How many times the player has joined the game.'}, + ['deaths'] = {name = 'Deaths', tooltip = 'How many times the player has died.'}, + ['entities_repaired'] = {name = 'Entities repaired', tooltip = 'How many entities the player has repaired.'}, + ['items_crafted'] = {name = 'Items crafted', tooltip = 'How many items the player has crafted.'}, + ['capsules_used'] = {name = 'Capsules used', tooltip = 'How many capsules the player has used.'}, + ['tiles_removed'] = {name = 'Tiles removed', tooltip = 'How many tiles the player has removed.'}, + ['deconstructer_planner_used'] = {name = 'Decon planner used', tooltip = 'How many times the player has used the deconstruction planner.'}, + ['maps_played'] = {name = 'Maps played', tooltip = 'How many maps the player has played.'}, + ['afk_time'] = {name = 'Total AFK', tooltip = 'How long the player has been AFK.'}, + ['distance_moved'] = {name = 'Distance travelled', tooltip = 'How far the player has travelled.\nIncluding standing still in looped belts.'}, + ['damage_dealt'] = {name = 'Damage dealt', tooltip = 'How much damage the player has dealt.'}, + ['enemies_killed'] = {name = 'Enemies killed', tooltip = 'How many enemies the player has killed.'}, + ['friendly_killed'] = {name = 'Friendlies killed', tooltip = 'How many friendlies the player has killed.\n This includes entities such as buildings etc.'}, + ['rockets_launched'] = {name = 'Rockets launched', tooltip = 'How many rockets the player has launched.'}, + ['research_complete'] = {name = 'Research completed', tooltip = 'How many researches the player has completed.'}, + ['force_mined_machines'] = {name = 'Mined friendly entities', tooltip = 'How many friendly entities the player has mined.'}, + ['trees'] = {name = 'Trees chopped', tooltip = 'How many trees the player has chopped.'}, + ['rocks'] = {name = 'Rocks mined', tooltip = 'How many rocks the player has mined.'}, + ['resources'] = {name = 'Ores mined', tooltip = 'How many ores the player has mined.'}, + ['kicked'] = {name = 'Kicked', tooltip = 'How many times the player has been kicked.'} +} +local statistics = {} + +-- Register the statistics table in the global table +Global.register( + { + statistics = statistics + }, + function(tbl) + statistics = tbl.statistics + + for _, stat in pairs(statistics) do + setmetatable(stat, Public.metatable) + end + end +) + +-- Metatable for the statistics table +Public.metatable = {__index = Public} + +-- Add a normalization entry to the normalized_names table +function Public.add_normalize(name, normalize) + if _LIFECYCLE == _STAGE.runtime then + error('cannot call during runtime', 2) + end + + local mt = setmetatable({name = normalize}, Public.metatable) + + normalized_names[name] = mt + + return mt +end + +-- Set the tooltip for a statistic +function Public:set_tooltip(tooltip) + if _LIFECYCLE == _STAGE.runtime then + error('cannot call during runtime', 2) + end + + self.tooltip = tooltip +end + +--- Returns the player table or false +---@param player LuaPlayer|number +---@return any +local function get_data(player) + local player_index = player and type(player) == 'number' and player or player and player.valid and player.index or false + if not player_index then + log('Invalid player index at get_data') + return false + end + + local data = statistics[player_index] + + if not data then + local p = game.get_player(player_index) + local name = p and p.valid and p.name or nil + local player_data = { + name = name, + tick = 0 + } + + for event, _ in pairs(events) do + player_data[event] = 0 + end + + local mt = setmetatable(player_data, Public.metatable) + + statistics[player_index] = mt + end + + return statistics[player_index] +end + +local try_download_data_token = + Token.register( + function(data) + local player_name = data.key + local player = game.get_player(player_name) + if not player or not player.valid then + return + end + + local stats = data.value + if stats then + local s = setmetatable(stats, Public.metatable) + statistics[player.index] = s + else + get_data(player) + end + end +) + +local try_upload_data_token = + Token.register( + function(data) + local player_name = data.key + if not player_name then + return + end + local stats = data.value + local player = game.get_player(player_name) + if not player or not player.valid then + return + end + + if stats then + -- we don't want to clutter the database with players less than 10 minutes played. + if player.online_time <= settings.required_only_time_to_save_time then + return + end + + set_data(statistics_dataset, player_name, get_data(player)) + else + local d = get_data(player) + if player.online_time >= settings.required_only_time_to_save_time then + set_data(statistics_dataset, player_name, d) + end + end + end +) + +-- Increase a statistic by a delta value +function Public:increase(name, delta) + if not self[name] then + self[name] = 0 + end + + self[name] = self[name] + (delta or 1) + + self.tick = self.tick + 1 + + return self +end + +-- Save the player's statistics +function Public:save() + local player = game.get_player(self.name) + if not player or not player.valid then + return + end + + if player.online_time <= settings.required_only_time_to_save_time then + return + end + + if self.tick < 10 then + return + end + + set_data(statistics_dataset, player.name, self) + return self +end + +-- Clear the player's statistics +function Public:clear(force_clear) + if force_clear then + statistics[self.name] = nil + else + local player = game.get_player(self.name) + if not player or not player.valid then + statistics[self.name] = nil + return + end + local connected = player.connected + + if not connected then + statistics[self.name] = nil + end + end +end + +-- Try to get the player's data from the dataset +function Public:try_get_data() + try_get_data(statistics_dataset, self.name, try_download_data_token) + return self +end + +-- Try to upload the player's data to the dataset +function Public:try_upload_data() + try_get_data(statistics_dataset, self.name, try_upload_data_token) + return self +end + +local nth_tick_token = + Token.register( + function(event) + local player_index = event.player_index + local player = game.get_player(player_index) + if not player or not player.valid then + return + end + + get_data(player):save() + end +) + +--- Uploads each connected players play time to the dataset +local function upload_data() + local players = game.connected_players + local count = 0 + for i = 1, #players do + count = count + 10 + local player = players[i] + set_timeout_in_ticks(count, nth_tick_token, {player_index = player.index}) + end +end + +--- Checks if a player exists within the table +---@param player_index string +---@return boolean +function Public.exists(player_index) + return statistics[player_index] ~= nil +end + +--- Returns the table of statistics +---@param player LuaPlayer +---@return table|boolean +function Public.get_player(player) + return statistics and player and player.valid and statistics[player.index] or false +end + +-- Event handlers + +Event.add( + e.on_player_joined_game, + function(event) + get_data(event.player_index):try_get_data() + end +) + +Event.add( + e.on_player_left_game, + function(event) + get_data(event.player_index):try_upload_data() + end +) + +Event.add( + Public.events.on_player_removed, + function(event) + local player_index = event.player_index + statistics[player_index] = nil + end +) + +Event.add( + e.on_player_removed, + function(event) + local player_index = event.player_index + statistics[player_index] = nil + end +) + +Event.on_nth_tick(settings.nth_tick, upload_data) + +Server.on_data_set_changed( + statistics_dataset, + function(data) + local player = game.get_player(data.key) + if player and player.valid then + local stats = data.value + if stats then + local s = setmetatable(stats, Public.metatable) + statistics[data.key] = s + end + end + end +) + +local function on_marked_for_deconstruction_on_player_mined_entity(event) + if not event.player_index then + return + end + + local player = game.get_player(event.player_index) + if not player.valid or not player.connected then + return + end + local entity = event.entity + if not entity.valid then + return + end + + local data = get_data(event.player_index) + if entity.type == 'resource' then + data:increase('resources') + elseif entity.type == 'tree' then + data:increase('trees') + elseif entity.type == 'simple-entity' then + data:increase('rocks') + elseif entity.force == player.force then + data:increase('force_mined_machines') + end +end + +for stat_name, event_name in pairs(events) do + Event.add( + event_name, + function(event) + if not event.player_index then + return + end + local player = game.get_player(event.player_index) + if not player.valid or not player.connected then + return + end + local data = get_data(event.player_index) + data:increase(stat_name) + end + ) +end + +Event.add( + e.on_research_finished, + function(event) + local research = event.research + if event.by_script or not research or not research.valid then + return + end + local force = research.force + if not force or not force.valid then + return + end + for _, player in pairs(force.connected_players) do + local data = get_data(player) + data:increase('research_complete') + end + end +) + +Event.add( + e.on_rocket_launched, + function(event) + local silo = event.rocket_silo + if not silo or not silo.valid then + return + end + local force = silo.force + if not force or not force.valid then + return + end + for _, player in pairs(force.connected_players) do + local data = get_data(player) + data:increase('rockets_launched') + end + end +) + +Event.add( + e.on_entity_died, + function(event) + local character = event.cause + if not character or not character.valid or character.type ~= 'character' then + return + end + local player = character.player + if not player or not player.valid or not player.connected then + return + end + local entity = event.entity + if not entity.valid or entity.force.name == 'neutral' then + return + end + local data = get_data(player) + if entity.force == player.force then + data:increase('friendly_killed') + return + end + data:increase('enemies_killed') + end +) + +Event.add( + e.on_entity_damaged, + function(event) + local character = event.cause + if not character or not character.valid or character.type ~= 'character' then + return + end + local player = character.player + if not player.valid or not player.connected then + return + end + local entity = event.entity + if not entity.valid or entity.force == player.force or entity.force.name == 'neutral' then + return + end + + local final_damage = event.final_damage_amount + + local data = get_data(player) + data:increase('damage_dealt', floor(final_damage)) + end +) + +Event.add( + e.on_player_changed_position, + function(event) + local player = game.get_player(event.player_index) + if not player.valid or not player.connected or player.afk_time > settings.required_only_time_to_save_time then + return + end + local data = get_data(event.player_index) + data:increase('distance_moved') + end +) + +Event.on_nth_tick( + 3600, + function() + if game.tick == 0 then + return + end + for _, player in pairs(game.connected_players) do + local data = get_data(player) + if player.afk_time > settings.afk_time then + data:increase('afk_time') + end + end + end +) + +Event.add( + e.on_player_created, + function(event) + get_data(event.player_index):increase('maps_played') + end +) + +Event.add( + e.on_player_kicked, + function(event) + get_data(event.player_index):increase('kicked') + end +) + +Event.add(e.on_marked_for_deconstruction, on_marked_for_deconstruction_on_player_mined_entity) +Event.add(e.on_player_mined_entity, on_marked_for_deconstruction_on_player_mined_entity) + +Public.upload_data = upload_data +Public.get_data = get_data +Public.normalized_names = normalized_names + +return Public diff --git a/utils/debug/event_view.lua b/utils/debug/event_view.lua index a81e59f3..3b6eb39b 100644 --- a/utils/debug/event_view.lua +++ b/utils/debug/event_view.lua @@ -19,14 +19,17 @@ local name_lookup = {} -- GUI names local checkbox_name = Gui.uid_name() +local checkbox_all_name = Gui.uid_name() local filter_name = Gui.uid_name() local clear_filter_name = Gui.uid_name() -- global tables local enabled = {} +local enabled_for_all = false local last_events = {} global.debug_event_view = { enabled = enabled, + enabled_for_all = enabled_for_all, last_events = last_events, filter = '' } @@ -83,6 +86,25 @@ local function on_gui_checked_state_changed(event) end end +local function on_gui_checked_state_changed_all(event) + local element = event.element + local state = element.state and true or false + element.state = state + if state then + for name, _ in pairs(events) do + local id = events[name] + enabled[id] = true + end + enabled_for_all = true + else + for name, _ in pairs(events) do + local id = events[name] + enabled[id] = false + end + enabled_for_all = false + end +end + -- GUI -- Create a table with events sorted by their names @@ -116,6 +138,12 @@ function Public.show(container) filter_flow.add({type = 'label', caption = 'filter'}) local filter_textfield = filter_flow.add({type = 'textfield', name = filter_name, text = filter}) local clear_button = filter_flow.add({type = 'button', name = clear_filter_name, caption = 'clear'}) + filter_flow.add({type = 'flow'}).add { + name = checkbox_all_name, + type = 'checkbox', + state = enabled_for_all or false, + caption = 'Toggle all events' + } local scroll_pane = main_frame_flow.add({type = 'scroll-pane'}) local gui_table = scroll_pane.add({type = 'table', column_count = 3, draw_horizontal_lines = true}) @@ -127,6 +155,7 @@ function Public.show(container) end Gui.on_checked_state_changed(checkbox_name, on_gui_checked_state_changed) +Gui.on_checked_state_changed(checkbox_all_name, on_gui_checked_state_changed_all) Gui.on_text_changed( filter_name, diff --git a/utils/discord_handler.lua b/utils/discord_handler.lua index 11d9943d..c2b6fd3d 100644 --- a/utils/discord_handler.lua +++ b/utils/discord_handler.lua @@ -108,7 +108,7 @@ end --- Send a message to the connected channel. --- Requires a title and a description ----@param scenario_name string +---@param scenario_name string|nil ---@param message string function Public.send_notification_raw(scenario_name, message) if not scenario_name then diff --git a/utils/freeplay.lua b/utils/freeplay.lua index cf6040cd..41aeea89 100644 --- a/utils/freeplay.lua +++ b/utils/freeplay.lua @@ -14,7 +14,8 @@ local this = { disable_crashsite = false, crashed_ship_items = {}, crashed_debris_items = {}, - custom_surface_name = nil + custom_surface_name = nil, + clear_mod_gui_top = false } Global.register( @@ -102,6 +103,10 @@ local chart_starting_area = function() end local on_player_joined_game = function(event) + if not this.clear_mod_gui_top then + return + end + Task.set_timeout_in_ticks(5, clear_mod_gui_top_frame_token, event) end diff --git a/utils/gui.lua b/utils/gui.lua index 53d0b93b..6f94ef64 100644 --- a/utils/gui.lua +++ b/utils/gui.lua @@ -27,7 +27,7 @@ local remove_data_recursively local data = {} local element_map = {} local settings = { - mod_gui_top_frame = false, + mod_gui_top_frame = true, disabled_tabs = {}, disable_clear_invalid_data = true } @@ -66,17 +66,17 @@ local main_toggle_button_name = Public.uid_name() local main_button_name = Public.uid_name() local close_button_name = Public.uid_name() -Public.button_style = 'mod_gui_button' - if not Public.mod_gui_button_enabled then Public.button_style = nil end -Public.frame_style = 'non_draggable_frame' - Public.top_main_gui_button = main_button_name Public.main_frame_name = main_frame_name Public.main_toggle_button_name = main_toggle_button_name +Public.frame_style = 'non_draggable_frame' +Public.button_style = 'mod_gui_button' +Public.top_flow_button_enabled_style = 'menu_button_continue' +Public.top_flow_button_disabled_style = Public.button_style --- Verifies if a frame is valid and destroys it. ---@param align userdata @@ -575,7 +575,7 @@ function Public.add_mod_button(player, frame) return Public.get_button_flow(player)[frame.name] end - Public.get_button_flow(player).add(frame) + return Public.get_button_flow(player).add(frame) end ---@param state boolean @@ -764,7 +764,13 @@ end local function top_button(player) if settings.mod_gui_top_frame then - Public.add_mod_button(player, {type = 'sprite-button', name = main_button_name, sprite = 'item/raw-fish', style = Public.button_style}) + local button = Public.add_mod_button(player, {type = 'sprite-button', name = main_button_name, sprite = 'item/raw-fish', style = Public.button_style}) + if button then + button.style.minimal_height = 36 + button.style.maximal_height = 36 + button.style.minimal_width = 40 + button.style.padding = -2 + end else if player.gui.top[main_button_name] then return @@ -782,19 +788,42 @@ local function top_toggle_button(player) return end - local b = - player.gui.top.add( - { - type = 'sprite-button', - name = main_toggle_button_name, - sprite = 'utility/preset', - style = Public.button_style, - tooltip = 'Click to hide top buttons!' - } - ) - b.style.padding = 2 - b.style.width = 20 - b.style.maximal_height = 38 + if Public.get_mod_gui_top_frame() then + local b = + Public.add_mod_button( + player, + { + type = 'sprite-button', + name = main_toggle_button_name, + sprite = 'utility/preset', + tooltip = 'Click to hide top buttons!', + style = Public.button_style + } + ) + if b then + b.style.font_color = {165, 165, 165} + b.style.font = 'heading-3' + b.style.minimal_height = 36 + b.style.maximal_height = 36 + b.style.minimal_width = 15 + b.style.maximal_width = 15 + b.style.padding = -2 + end + else + local b = + player.gui.top.add( + { + type = 'sprite-button', + name = main_toggle_button_name, + sprite = 'utility/preset', + style = Public.button_style, + tooltip = 'Click to hide top buttons!' + } + ) + b.style.padding = 2 + b.style.width = 20 + b.style.maximal_height = 38 + end end local function draw_main_frame(player) @@ -951,6 +980,7 @@ Public.on_click( end local player = event.player local frame = Public.get_parent_frame(player) + if frame then remove_data_recursively(frame) frame.destroy() @@ -999,9 +1029,17 @@ Public.on_click( local top = player.gui.top if button.sprite == 'utility/preset' then - for _, ele in pairs(top.children) do - if ele and ele.valid and ele.name ~= main_toggle_button_name then - ele.visible = false + if Public.get_mod_gui_top_frame() then + for _, ele in pairs(top.mod_gui_top_frame.mod_gui_inner_frame.children) do + if ele and ele.valid and ele.name ~= main_toggle_button_name then + ele.visible = false + end + end + else + for _, ele in pairs(top.children) do + if ele and ele.valid and ele.name ~= main_toggle_button_name then + ele.visible = false + end end end @@ -1015,9 +1053,17 @@ Public.on_click( button.sprite = 'utility/expand_dots_white' button.tooltip = 'Click to show top buttons!' else - for _, ele in pairs(top.children) do - if ele and ele.valid and ele.name ~= main_toggle_button_name then - ele.visible = true + if Public.get_mod_gui_top_frame() then + for _, ele in pairs(top.mod_gui_top_frame.mod_gui_inner_frame.children) do + if ele and ele.valid and ele.name ~= main_toggle_button_name then + ele.visible = true + end + end + else + for _, ele in pairs(top.children) do + if ele and ele.valid and ele.name ~= main_toggle_button_name then + ele.visible = true + end end end diff --git a/utils/gui/init.lua b/utils/gui/init.lua new file mode 100644 index 00000000..c1e0e0ea --- /dev/null +++ b/utils/gui/init.lua @@ -0,0 +1,9 @@ +require 'utils.gui' +require 'utils.gui.player_list' +require 'utils.gui.admin' +require 'utils.gui.group' +require 'utils.gui.score' +require 'utils.gui.statistics' +require 'utils.gui.config' +require 'utils.gui.poll' +require 'utils.gui.server_select' diff --git a/utils/gui/poll.lua b/utils/gui/poll.lua index a872f749..22d76f92 100644 --- a/utils/gui/poll.lua +++ b/utils/gui/poll.lua @@ -767,7 +767,8 @@ local function player_joined(event) end if Gui.get_mod_gui_top_frame() then - Gui.add_mod_button( + local button = + Gui.add_mod_button( player, { type = 'sprite-button', @@ -777,6 +778,14 @@ local function player_joined(event) style = Gui.button_style } ) + if button then + button.style.font_color = {165, 165, 165} + button.style.font = 'heading-3' + button.style.minimal_height = 36 + button.style.maximal_height = 36 + button.style.minimal_width = 40 + button.style.padding = -2 + end else if player.gui.top[main_button_name] ~= nil then local frame = player.gui.top[main_frame_name] diff --git a/utils/gui/server_select.lua b/utils/gui/server_select.lua index 1758dd74..cc5f03d1 100644 --- a/utils/gui/server_select.lua +++ b/utils/gui/server_select.lua @@ -132,8 +132,13 @@ local function create_main_button(event) end local player = game.get_player(event.player_index) + if not player or not player.valid or not player.character then + return + end + if Gui.get_mod_gui_top_frame() then - Gui.add_mod_button( + local b = + Gui.add_mod_button( player, { type = 'sprite-button', @@ -143,6 +148,14 @@ local function create_main_button(event) style = Gui.button_style } ) + if b then + b.style.font_color = {165, 165, 165} + b.style.font = 'heading-3' + b.style.minimal_height = 36 + b.style.maximal_height = 36 + b.style.minimal_width = 40 + b.style.padding = -2 + end else local main_button = player.gui.top[main_button_name] if not main_button or not main_button.valid then diff --git a/utils/gui/statistics.lua b/utils/gui/statistics.lua new file mode 100644 index 00000000..d30d2ed9 --- /dev/null +++ b/utils/gui/statistics.lua @@ -0,0 +1,104 @@ +-- created by Gerkiz for ComfyFactorio + +local Gui = require 'utils.gui' +local Token = require 'utils.token' +local StatData = require 'utils.datastore.statistics' +local format_number = require 'util'.format_number +local Server = require 'utils.server' + +local Public = {} + +local module_name = Gui.uid_name() + +local ignored_stats = { + ['name'] = true, + ['tick'] = true +} + +local normalized_names = StatData.normalized_names + +local function show_score(data) + local player = data.player + local frame = data.frame + frame.clear() + + local t = frame.add {type = 'table', column_count = 2} + + local tooltip = 'Your statistics that are gathered throughout Comfy servers.' + local secs = Server.get_current_time() + if not secs then + tooltip = 'Not currently connected to any backend. Statistics will reset.' + end + + local label = + t.add { + type = 'label', + caption = tooltip + } + + label.style.font = 'heading-2' + label.style.font_color = {r = 0.98, g = 0.66, b = 0.22} + label.style.minimal_width = 125 + label.style.horizontal_align = 'center' + + local line = frame.add {type = 'line'} + line.style.top_margin = 8 + line.style.bottom_margin = 8 + + local stat_data = StatData.get_data(player) + + local scroll_pane = + frame.add( + { + type = 'scroll-pane', + name = 'score_scroll_pane', + direction = 'vertical', + horizontal_scroll_policy = 'never', + vertical_scroll_policy = 'auto' + } + ) + scroll_pane.style.maximal_height = 400 + local column_table = scroll_pane.add {type = 'table', column_count = 4} + + for name, stat in pairs(stat_data) do + if not ignored_stats[name] then + local c = stat + if stat and type(stat) == 'number' then + c = format_number(stat, true) + end + local lines = { + {caption = normalized_names[name].name, tooltip = normalized_names[name].tooltip or ''}, + {caption = c} + } + local default_color = {r = 0.9, g = 0.9, b = 0.9} + + for _, column in ipairs(lines) do + local l = + column_table.add { + type = 'label', + caption = column.caption, + tooltip = column.tooltip, + color = column.color or default_color + } + l.style.font = 'heading-3' + l.style.minimal_width = 175 + l.style.maximal_width = 175 + l.style.horizontal_align = 'left' + end + end + end +end + +local show_stats_token = Token.register(show_score) + +Gui.add_tab_to_gui({name = module_name, caption = 'Statistics', id = show_stats_token, admin = false}) + +Gui.on_click( + module_name, + function(event) + local player = event.player + Gui.reload_active_tab(player) + end +) + +return Public