require "util" require "story" mod_gui = 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 = "sulfur", 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 = "sulfur", 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 = 1} function get_time_left() return global.level_started_at + time_modifier * levels[global.level].time * 60 - game.tick end local accumulate_items = function() local accumulated = global.accumulated 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 item_name, count in pairs(contents) do if accumulated[item_name] then local count_to_consume = math.min(global.required[item_name] - global.accumulated[item_name], count) if count_to_consume ~= 0 then inventory.remove{name = item_name, count = count_to_consume} accumulated[item_name] = accumulated[item_name] + count_to_consume end end end end end end local get_next_level_button = function(player) local flow = mod_gui.get_button_flow(player) return flow.next_level or flow.add{type = "button", name = "next_level", caption={"next-level"}, style = mod_gui.button_style} end local low_time_left_label_color = {r = 1} 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 = {} global.level_started_at = event.tick local level = levels[global.level] for index, item in pairs(level.requirements) do global.accumulated[item.name] = 0 global.required[item.name] = item.count end for k, player in pairs (game.players) do update_gui(player) get_next_level_button(player).enabled = false end end }, { name = "level-progress", update = function(event) if event.tick % 60 ~= 0 then return end accumulate_items() for k, player in pairs(game.players) do update_gui(player) end end, condition = function(event) if event.name == defines.events.on_gui_click and event.element.name == "next_level" then local seconds_left = math.floor(get_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, points_addition}) global.points = global.points + points_addition return true end if event.tick % 60 ~= 0 then return end local result = true local level = levels[global.level] 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 get_next_level_button(player).enabled = true end end if get_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), global.points}) player.gui.top.clear() player.gui.left.clear() 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 get_next_level_button(player).enabled = false 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), points_addition}) global.points = global.points + points_addition if global.level < #levels + 1 then for k, player in pairs (game.players) do get_next_level_button(player).enabled = false 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), 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 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) game.get_player(event.player_index).insert{name = "iron-plate", count = 8} end) function update_gui(player) local flow = mod_gui.get_frame_flow(player) local frame = flow.supply_frame if not frame then frame = flow.add{type = "frame", name = "supply_frame", style = mod_gui.frame_style, direction = "vertical"} end frame.caption = {"level", global.level} frame.clear() local inner = frame.add{type = "frame", direction = "vertical", style = "inside_shallow_frame"} local time_left = get_time_left() local info_table = inner.add{type = "table", column_count = 1, style = "bordered_table"} info_table.style.margin = 4 local time_left_label = info_table.add{type = "label", name = "time_left", caption = {"time-left", util.formattime(time_left)}} if time_left < 60 * 30 then time_left_label.style.font_color = low_time_left_label_color end info_table.add{type = "label", caption = {"points-per-second", points_per_second_start - global.level * points_per_second_level_subtract}} info_table.add{type = "label", caption = {"points", util.format_number(math.floor(global.points))}} local required_items_flow = info_table.add{type = "flow", direction = "vertical"} required_items_flow.add{type = "label", caption = {"required-items"}, style = "caption_label"} local item_prototypes = game.item_prototypes local accumulated = global.accumulated local table = required_items_flow.add{type = "table", column_count = 3} table.style.column_alignments[3] = "right" local level = levels[global.level] for index, item in pairs(level.requirements) do local accumulated = accumulated[item.name] local sprite = table.add{type = "sprite", sprite = "item/"..item.name, style = "small_text_image"} table.add{type = "label", caption = {"", item_prototypes[item.name].localised_name, {"colon"}}} local label = table.add{type = "label", caption = accumulated .. "/" .. item.count} if accumulated == item.count then label.style.font_color = completed_label_color end end local next_level = levels[global.level + 1] if next_level then local next_level_flow = info_table.add{type = "flow", direction = "vertical"} next_level_flow.add{type= "label", caption = {"next-level"}, style = "caption_label"} local next_level_table = next_level_flow.add{type = "table", column_count = 3} next_level_table.style.column_alignments[3] = "right" for index, item in pairs(next_level.requirements) do local sprite = next_level_table.add{type = "sprite", sprite = "item/"..item.name, style = "small_text_image"} next_level_table.add{type = "label", caption = {"", item_prototypes[item.name].localised_name, {"colon"}}} next_level_table.add{type = "label", caption = item.count} end 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