You've already forked ComfyFactorio
							
							
				mirror of
				https://github.com/ComfyFactory/ComfyFactorio.git
				synced 2025-10-30 23:47:41 +02:00 
			
		
		
		
	Mtn v3 - changes for new season
This commit is contained in:
		
							
								
								
									
										21
									
								
								control.lua
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								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' | ||||
|  | ||||
|   | ||||
| @@ -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] | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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', | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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') | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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}) | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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'}) | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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) | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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', | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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', | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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.', | ||||
|   | ||||
							
								
								
									
										12
									
								
								utils/datastore/init.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								utils/datastore/init.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -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' | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										515
									
								
								utils/datastore/statistics.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										515
									
								
								utils/datastore/statistics.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
							
								
								
									
										9
									
								
								utils/gui/init.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								utils/gui/init.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -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' | ||||
| @@ -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] | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										104
									
								
								utils/gui/statistics.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								utils/gui/statistics.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
		Reference in New Issue
	
	Block a user