|
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 = |
|
{ |
|
|
|
{ |
|
requirements = |
|
{ |
|
{name = "stone", count = 15 } |
|
}, |
|
time = 240 |
|
}, |
|
|
|
|
|
{ |
|
requirements = |
|
{ |
|
{name = "iron-plate", count = 30} |
|
}, |
|
time = 300 |
|
}, |
|
|
|
|
|
{ |
|
requirements = |
|
{ |
|
{name = "iron-plate", count = 30}, |
|
{name = "copper-plate", count = 30} |
|
}, |
|
time = 300 |
|
}, |
|
|
|
|
|
{ |
|
requirements = |
|
{ |
|
{name = "iron-plate", count = 30}, |
|
{name = "iron-gear-wheel", count = 30} |
|
}, |
|
time = 300 |
|
}, |
|
|
|
|
|
{ |
|
requirements = |
|
{ |
|
{name = "iron-plate", count = 40}, |
|
{name = "iron-gear-wheel", count = 30}, |
|
{name = "copper-cable", count = 40} |
|
}, |
|
time = 300 |
|
}, |
|
|
|
|
|
{ |
|
requirements = |
|
{ |
|
{name = "iron-plate", count = 40}, |
|
{name = "iron-gear-wheel", count = 30}, |
|
{name = "electronic-circuit", count = 30} |
|
}, |
|
time = 300 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
}, |
|
|
|
|
|
{ |
|
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 |
|
|