require "util" require "story" require "mod-gui" time_modifier = 1.4 points_per_second_start = 5 points_per_second_level_subtract = 0.2 levels = { -- 1 { requirements = { {name = "stone", count = 15 } }, time = 240 }, -- 2 { requirements = { {name = "iron-plate", count = 30} }, time = 300 }, -- 3 { requirements = { {name = "iron-plate", count = 30}, {name = "copper-plate", count = 30} }, time = 300 }, -- 4 { requirements = { {name = "iron-plate", count = 30}, {name = "iron-gear-wheel", count = 30} }, time = 300 }, -- 5 { requirements = { {name = "iron-plate", count = 40}, {name = "iron-gear-wheel", count = 30}, {name = "copper-cable", count = 40} }, time = 300 }, -- 6 { requirements = { {name = "iron-plate", count = 40}, {name = "iron-gear-wheel", count = 30}, {name = "electronic-circuit", count = 30} }, time = 300 }, -- 7 { requirements = { {name = "iron-plate", count = 40}, {name = "iron-gear-wheel", count = 30}, {name = "electronic-circuit", count = 30}, {name = "automation-science-pack", count = 10}, {name = "firearm-magazine", count = 50} }, time = 300 }, -- 8 { requirements = { {name = "iron-plate", count = 40}, {name = "iron-gear-wheel", count = 30}, {name = "electronic-circuit", count = 30}, {name = "automation-science-pack", count = 10}, {name = "transport-belt", count = 20} }, time = 300 }, -- 9 { requirements = { {name = "iron-plate", count = 50}, {name = "iron-gear-wheel", count = 30}, {name = "electronic-circuit", count = 30}, {name = "automation-science-pack", count = 10}, {name = "transport-belt", count = 20}, {name = "inserter", count = 20} }, time = 300 }, -- 10 { requirements = { {name = "iron-plate", count = 50}, {name = "iron-gear-wheel", count = 30}, {name = "electronic-circuit", count = 30}, {name = "automation-science-pack", count = 10}, {name = "logistic-science-pack", count = 10} }, time = 300 }, -- 11 { requirements = { {name = "iron-plate", count = 50}, {name = "iron-gear-wheel", count = 30}, {name = "electronic-circuit", count = 30}, {name = "automation-science-pack", count = 20}, {name = "logistic-science-pack", count = 20}, {name = "fast-transport-belt", count = 10} }, time = 300 }, -- 12 { requirements = { {name = "iron-plate", count = 50}, {name = "iron-gear-wheel", count = 30}, {name = "electronic-circuit", count = 30}, {name = "automation-science-pack", count = 20}, {name = "logistic-science-pack", count = 20}, {name = "steel-plate", count = 10} }, time = 420 }, -- 13 { requirements = { {name = "iron-gear-wheel", count = 30}, {name = "electronic-circuit", count = 30}, {name = "automation-science-pack", count = 20}, {name = "logistic-science-pack", count = 20}, {name = "steel-plate", count = 15}, {name = "piercing-rounds-magazine", count = 50} }, time = 300 }, -- 14 { requirements = { {name = "iron-gear-wheel", count = 30}, {name = "electronic-circuit", count = 30}, {name = "automation-science-pack", count = 20}, {name = "logistic-science-pack", count = 20}, {name = "steel-plate", count = 15}, {name = "grenade", count = 15}, {name = "plastic-bar", count = 5} }, time = 600 }, -- 15 { requirements = { {name = "electronic-circuit", count = 30}, {name = "automation-science-pack", count = 20}, {name = "logistic-science-pack", count = 20}, {name = "steel-plate", count = 15}, {name = "advanced-circuit", count = 10} }, time = 500 }, -- 16 { requirements = { {name = "automation-science-pack", count = 20}, {name = "logistic-science-pack", count = 20}, {name = "steel-plate", count = 15}, {name = "advanced-circuit", count = 10}, {name = "solid-fuel", count = 10} }, time = 500 }, -- 17 { requirements = { {name = "automation-science-pack", count = 20}, {name = "logistic-science-pack", count = 20}, {name = "advanced-circuit", count = 10}, {name = "engine-unit", count = 10}, {name = "solid-fuel", count = 10} }, time = 500 }, -- 18 { requirements = { {name = "automation-science-pack", count = 20}, {name = "logistic-science-pack", count = 20}, {name = "chemical-science-pack", count = 20}, {name = "grenade", count = 20} }, time = 500 }, -- 19 { requirements = { {name = "automation-science-pack", count = 30}, {name = "logistic-science-pack", count = 30}, {name = "chemical-science-pack", count = 30}, {name = "stone-wall", count = 20} }, time = 500 }, -- 20 { requirements = { {name = "automation-science-pack", count = 40}, {name = "logistic-science-pack", count = 40}, {name = "chemical-science-pack", count = 40}, {name = "stone-wall", count = 20}, {name = "grenade", count = 20} }, time = 600 }, -- 21 { requirements = { {name = "automation-science-pack", count = 50}, {name = "logistic-science-pack", count = 50}, {name = "chemical-science-pack", count = 50}, {name = "military-science-pack", count = 50} }, time = 700 } } local completed_label_color = {g = 0.6} function update_info() local level = levels[global.level] local accumulated = global.accumulated for k, player in pairs (game.players) do local frame = mod_gui.get_frame_flow(player).frame local table = frame.table for index, item in pairs(level.requirements) do local accumulated = accumulated[item.name] local label = table[item.name] label.caption = accumulated .. "/" .. item.count if accumulated == item.count then label.style.font_color = completed_label_color end end end end function get_time_left() return global.level_started_at + time_modifier * levels[global.level].time * 60 - game.tick end local low_time_left_label_color = {r = 1} function update_time_left(tick) --If given not given a tick, we update regardless if tick and tick % 60 ~= 0 then return end local time_left = get_time_left() if time_left < 0 then time_left = 0 end local caption = {"time-left", util.formattime(time_left)} local low_time_left = time_left < 60 * 30 for k, player in pairs (game.players) do local label = mod_gui.get_frame_flow(player).frame.time_left label.caption = caption if low_time_left then label.style.font_color = low_time_left_label_color end end end story_table = { { { action = function() if not game.is_multiplayer() then game.show_message_dialog{text = {"welcome"}} game.show_message_dialog{text = {"rules1"}} game.show_message_dialog{text = {"rules2"}} game.show_message_dialog{text = {"rules3"}} game.show_message_dialog{text = {"rules4"}} game.show_message_dialog{text = {"rules5"}} end end }, {}, { name = "level-start", init = function(event) global.accumulated = {} global.required = {} global.labels = {} local level = levels[global.level] for k, player in pairs (game.players) do make_frame(player) end for index, item in pairs(levels[global.level].requirements) do global.accumulated[item.name] = 0 global.required[item.name] = item.count end if global.level < #levels then for k, player in pairs (game.players) do update_frame(player, levels[global.level + 1]) end local item_prototypes = game.item_prototypes for index, item in pairs(levels[global.level + 1].requirements) do local diff if global.required[item.name] ~= nil then diff = item.count - global.required[item.name] else diff = item.count end for k, player in pairs (game.players) do update_table(player, diff, item) end end end global.level_started_at = event.tick update_info() update_time_left() end }, { name = "level-progress", update = function(event) update_time_left(event.tick) local update_info_needed = false local level = levels[global.level] for index, chest in pairs(global.chests) do if chest.valid then local inventory = chest.get_inventory(defines.inventory.chest) local contents = inventory.get_contents() for itemname, count in pairs(contents) do if global.accumulated[itemname] then local counttoconsume = global.required[itemname] - global.accumulated[itemname] if counttoconsume > count then counttoconsume = count end if counttoconsume ~= 0 then inventory.remove{name = itemname, count = counttoconsume} global.accumulated[itemname] = global.accumulated[itemname] + counttoconsume update_info_needed = true end end end end end if update_info_needed then update_info() end end, condition = function(event) local level = levels[global.level] local time_left = get_time_left() if event.name == defines.events.on_gui_click and event.element.name == "next_level" then local seconds_left = math.floor(time_left / 60) local points_addition = math.floor(seconds_left * (points_per_second_start - global.level * points_per_second_level_subtract)) game.print({"time-bonus", util.format_number(points_addition), seconds_left}) global.points = global.points + points_addition for k, player in pairs (game.players) do if mod_gui.get_button_flow(player).next_level ~= nil then mod_gui.get_button_flow(player).next_level.destroy() end end return true end local result = true for index, item in pairs(level.requirements) do local accumulated = global.accumulated[item.name] if accumulated < item.count then result = false end end if result then for k, player in pairs (game.players) do if mod_gui.get_button_flow(player).next_level == nil then mod_gui.get_button_flow(player).add{type = "button", name = "next_level", caption={"next-level"}, style = mod_gui.button_style} end end end if time_left <= 0 then if result == false then for k, player in pairs (game.players) do player.set_ending_screen_data({"points-achieved", util.format_number(global.points)}) end game.set_game_state{game_finished=true, player_won=false} return false else return true end end return false end, action = function(event, story) for k, player in pairs (game.players) do if mod_gui.get_button_flow(player).next_level ~= nil then mod_gui.get_button_flow(player).next_level.destroy() end end global.level = global.level + 1 local points_addition = (global.level - 1) * 10 game.print({"level-completed", global.level - 1, util.format_number(points_addition)}) global.points = global.points + points_addition if global.level < #levels + 1 then for k, player in pairs (game.players) do mod_gui.get_frame_flow(player).frame.destroy() end story_jump_to(story, "level-start") end end }, { action = function() for k, player in pairs (game.players) do player.set_ending_screen_data({"points-achieved", util.format_number(global.points)}) end end } } } story_init_helpers(story_table) script.on_init(function() validate_prototypes() global.story = story_init() game.map_settings.pollution.enabled = false game.forces.enemy.evolution_factor = 0 global.required = {} global.chests = {} for k, chest in pairs (game.surfaces[1].find_entities_filtered{name = "red-chest"}) do chest.minable = false chest.destructible = false global.chests[chest.unit_number] = chest end global.level = 1 global.points = 0 for k, player in pairs (game.players) do on_joined(player) end end) supply_events = { defines.events.on_gui_click, defines.events.on_tick, } script.on_event(supply_events, function(event) story_update(global.story, event, "") end) script.on_event(defines.events.on_player_created, function(event) local player = game.get_player(event.player_index) on_joined(player) end) function on_joined(player) player.insert{name = "iron-plate", count = 8} make_frame(player) if global.level < #levels then update_frame(player, levels[global.level + 1]) for index, item in pairs(levels[global.level + 1].requirements) do local diff if global.required[item.name] ~= nil then diff = item.count - global.required[item.name] else diff = item.count end update_table(player,diff,item) end end end function make_frame(player) local flow = mod_gui.get_frame_flow(player) if flow.frame then flow.frame.destroy() end local frame = flow.add{type = "frame", name = "frame", direction = "vertical", caption = {"level", global.level}} frame.add{type = "label", name = "time_left", caption = {"time-left", "-"}} frame.add{type = "label", caption = {"points-per-second", points_per_second_start - global.level * points_per_second_level_subtract}} frame.add{type = "label", caption = {"points", util.format_number(math.floor(global.points))}} frame.add{type = "label", caption = {"required-items"}, style = "caption_label"} local table = frame.add{type = "table", name = "table", column_count = 2} table.style.column_alignments[2] = "right" for index, item in pairs(levels[global.level].requirements) do table.add{type = "label", caption = {"", game.item_prototypes[item.name].localised_name, {"colon"}}} table.add{type = "label", caption = "0/" .. item.count, name=item.name} end return frame end function update_frame(player, next_level) local frame = mod_gui.get_frame_flow(player).frame if not frame then frame = make_frame(player) end frame.add{type= "label", caption={"next-level"}, style = "caption_label"} local next_level_table = frame.add{type = "table", column_count=2, name = "next_level_table"} next_level_table.style.column_alignments[2] = "right" end function update_table(player, diff, item) local table = mod_gui.get_frame_flow(player).frame.next_level_table if not table then game.print("No table for update_table function") return end if diff ~= 0 then table.add{type = "label", caption = {"", game.item_prototypes[item.name].localised_name, {"colon"}}} end if diff > 0 then table.add{type = "label", caption = "+" .. diff} return end if diff < 0 then table.add{type = "label", caption = diff} return end end function validate_prototypes() local items = game.item_prototypes local is_error = false local bad_items = {} for k, level in pairs (levels) do for k, item in pairs (level.requirements) do if not items[item.name] or item.count <= 0 then is_error = true bad_items[item.name] = item.count end end end if is_error then error("Bad prototypes in supply challenge:\n"..serpent.block(bad_items)) end end function test_fill_chest_requirements() assert(global.chests) local index, chest = next(global.chests) assert(chest.valid) local level = levels[global.level] assert(level) for k, item in pairs (level.requirements) do chest.insert(item) end global.level_started_at = (game.tick + 5) - (level.time * 60 * time_modifier) end