1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2025-01-26 03:52:22 +02:00

First Commit

This commit is contained in:
MewMew 2018-09-19 06:51:25 +02:00
commit 7b1358f154
25 changed files with 3469 additions and 0 deletions

51
control.lua Normal file
View File

@ -0,0 +1,51 @@
require "utils.utils"
require "session_tracker"
require "group"
require "player_list"
require "poll"
require "score"
---- enable maps here ----
require "maps.cave_miner"
-----------------------------
local Event = require 'utils.event'
local function player_created(event)
local player = game.players[event.player_index]
player.gui.top.style = 'slot_table_spacing_horizontal_flow'
player.gui.left.style = 'slot_table_spacing_vertical_flow'
player.print("Join the comfy discord >> comfyplay.net", {r = 110, g = 0, b = 220})
end
function spaghetti()
game.forces["player"].technologies["logistic-system"].enabled = false
game.forces["player"].technologies["construction-robotics"].enabled = false
game.forces["player"].technologies["logistic-robotics"].enabled = false
game.forces["player"].technologies["robotics"].enabled = false
game.forces["player"].technologies["personal-roboport-equipment"].enabled = false
game.forces["player"].technologies["personal-roboport-equipment-2"].enabled = false
game.forces["player"].technologies["character-logistic-trash-slots-1"].enabled = false
game.forces["player"].technologies["character-logistic-trash-slots-2"].enabled = false
game.forces["player"].technologies["auto-character-logistic-trash-slots"].enabled = false
game.forces["player"].technologies["worker-robots-storage-1"].enabled = false
game.forces["player"].technologies["worker-robots-storage-2"].enabled = false
game.forces["player"].technologies["worker-robots-storage-3"].enabled = false
game.forces["player"].technologies["character-logistic-slots-1"].enabled = false
game.forces["player"].technologies["character-logistic-slots-2"].enabled = false
game.forces["player"].technologies["character-logistic-slots-3"].enabled = false
game.forces["player"].technologies["character-logistic-slots-4"].enabled = false
game.forces["player"].technologies["character-logistic-slots-5"].enabled = false
game.forces["player"].technologies["character-logistic-slots-6"].enabled = false
game.forces["player"].technologies["worker-robots-speed-1"].enabled = false
game.forces["player"].technologies["worker-robots-speed-2"].enabled = false
game.forces["player"].technologies["worker-robots-speed-3"].enabled = false
game.forces["player"].technologies["worker-robots-speed-4"].enabled = false
game.forces["player"].technologies["worker-robots-speed-5"].enabled = false
game.forces["player"].technologies["worker-robots-speed-6"].enabled = false
end
Event.add(defines.events.on_player_created, player_created)

260
group.lua Normal file
View File

@ -0,0 +1,260 @@
-- this script adds a group button to create groups for your players --
local Event = require 'utils.event'
local function build_group_gui(player)
local group_name_width = 160
local description_width = 220
local members_width = 90
local member_columns = 3
local actions_width = 60
local total_height = 350
if not player.gui.top["group_button"] then
local b = player.gui.top.add({type = "button", name = "group_button", caption = global.player_group[player.name]})
b.style.font_color = {r = 0.77, g = 0.77, b = 0.77}
b.style.font = "default-bold"
b.style.minimal_height = 38
b.style.minimal_width = 38
b.style.top_padding = 2
b.style.left_padding = 4
b.style.right_padding = 4
b.style.bottom_padding = 2
end
if player.online_time < 1 then return end
if player.gui.left["group_frame"] then player.gui.left["group_frame"].destroy() end
local frame = player.gui.left.add({type = "frame", name = "group_frame", direction = "vertical"})
frame.style.minimal_height = total_height
local t = frame.add({type = "table", column_count = 5})
local headings = {{"Title", group_name_width}, {"Description", description_width}, {"Members", members_width * member_columns}, {"", actions_width*2 - 30}}
for _, h in pairs (headings) do
local l = t.add({ type = "label", caption = h[1]})
l.style.font_color = { r=0.98, g=0.66, b=0.22}
l.style.font = "default-listbox"
l.style.top_padding = 6
l.style.minimal_height = 40
l.style.minimal_width = h[2]
l.style.maximal_width = h[2]
end
local b = t.add {type = "button", caption = "X", name = "close_group_frame", align = "right"}
b.style.font = "default"
b.style.minimal_height = 30
b.style.minimal_width = 30
b.style.top_padding = 2
b.style.left_padding = 4
b.style.right_padding = 4
b.style.bottom_padding = 2
local scroll_pane = frame.add({ type = "scroll-pane", name = "scroll_pane", direction = "vertical", horizontal_scroll_policy = "never", vertical_scroll_policy = "auto"})
scroll_pane.style.maximal_height = total_height - 50
scroll_pane.style.minimal_height = total_height - 50
local t = scroll_pane.add({type = "table", name = "groups_table", column_count = 4})
for _, h in pairs (headings) do
local l = t.add({ type = "label", caption = ""})
l.style.minimal_width = h[2]
l.style.maximal_width = h[2]
end
for _, group in pairs (global.tag_groups) do
local l = t.add({ type = "label", caption = group.name})
l.style.font = "default-bold"
l.style.top_padding = 16
l.style.bottom_padding = 16
l.style.minimal_width = group_name_width
l.style.maximal_width = group_name_width
local color = game.players[group.founder].color
color = {r = color.r * 0.6 + 0.4, g = color.g * 0.6 + 0.4, b = color.b * 0.6 + 0.4, a = 1}
l.style.font_color = color
l.style.single_line = false
local l = t.add({ type = "label", caption = group.description})
l.style.top_padding = 16
l.style.bottom_padding = 16
l.style.minimal_width = description_width
l.style.maximal_width = description_width
l.style.font_color = {r = 0.90, g = 0.90, b = 0.90}
l.style.single_line = false
local tt = t.add({ type = "table", column_count = member_columns})
for _, p in pairs (game.connected_players) do
if group.name == global.player_group[p.name] then
local l = tt.add({ type = "label", caption = p.name})
local color = {r = p.color.r * 0.6 + 0.4, g = p.color.g * 0.6 + 0.4, b = p.color.b * 0.6 + 0.4, a = 1}
l.style.font_color = color
--l.style.minimal_width = members_width
l.style.maximal_width = members_width * 2
end
end
local tt = t.add({ type = "table", name = group.name, column_count = 2})
if player.admin == true or group.founder == player.name then
local b = tt.add({ type = "button", caption = "Delete"})
b.style.font = "default-bold"
b.style.minimal_width = actions_width
b.style.maximal_width = actions_width
else
local l = tt.add({ type = "label", caption = ""})
l.style.minimal_width = actions_width
l.style.maximal_width = actions_width
end
if group.name ~= global.player_group[player.name] then
local b = tt.add({ type = "button", caption = "Join"})
b.style.font = "default-bold"
b.style.minimal_width = actions_width
b.style.maximal_width = actions_width
else
local b = tt.add({ type = "button", caption = "Leave"})
b.style.font = "default-bold"
b.style.minimal_width = actions_width
b.style.maximal_width = actions_width
end
end
local frame2 = frame.add({type = "frame", name = "frame2"})
local t = frame2.add({type = "table", name = "group_table", column_count = 3})
local textfield = t.add({ type = "textfield", name = "new_group_name", text = "Name" })
textfield.style.minimal_width = group_name_width
local textfield = t.add({ type = "textfield", name = "new_group_description", text = "Description" })
textfield.style.minimal_width = description_width + members_width * member_columns
local b = t.add({type = "button", name = "create_new_group", caption = "Create"})
b.style.minimal_width = actions_width*2 - 12
b.style.font = "default-bold"
end
local function refresh_gui()
for _, p in pairs(game.connected_players) do
if p.gui.left["group_frame"] then
local frame = p.gui.left["group_frame"]
local new_group_name = frame.frame2.group_table.new_group_name.text
local new_group_description = frame.frame2.group_table.new_group_description.text
build_group_gui(p)
local frame = p.gui.left["group_frame"]
frame.frame2.group_table.new_group_name.text = new_group_name
frame.frame2.group_table.new_group_description.text = new_group_description
end
end
end
local function on_player_joined_game(event)
local player = game.players[event.player_index]
if not global.player_group then global.player_group = {} end
if not global.player_group[player.name] then global.player_group[player.name] = "[Group]" end
if not global.join_spam_protection then global.join_spam_protection = {} end
if not global.join_spam_protection[player.name] then global.join_spam_protection[player.name] = game.tick end
if not global.tag_groups then global.tag_groups = {} end
if player.online_time < 10 then
build_group_gui(player)
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
local player = game.players[event.element.player_index]
local name = event.element.name
local frame = player.gui.left["group_frame"]
if name == "create_new_group" then
local new_group_name = frame.frame2.group_table.new_group_name.text
local new_group_description = frame.frame2.group_table.new_group_description.text
if new_group_name ~= "" and new_group_name ~= "Name" and new_group_description ~= "Description" then
if string.len(new_group_name) > 32 then
player.print("Group name is too long. 32 characters maximum.", { r=0.90, g=0.0, b=0.0})
return
end
if string.len(new_group_description) > 128 then
player.print("Description is too long. 128 characters maximum.", { r=0.90, g=0.0, b=0.0})
return
end
global.tag_groups[new_group_name] = {name = new_group_name, description = new_group_description, founder = player.name}
local color = {r = player.color.r * 0.7 + 0.3, g = player.color.g * 0.7 + 0.3, b = player.color.b * 0.7 + 0.3, a = 1}
game.print(player.name .. " has founded a new group!", color)
game.print('>> ' .. new_group_name, { r=0.98, g=0.66, b=0.22})
game.print(new_group_description, { r=0.85, g=0.85, b=0.85})
frame.frame2.group_table.new_group_name.text = "Name"
frame.frame2.group_table.new_group_description.text = "Description"
refresh_gui()
end
end
local p = event.element.parent
if p then p = p.parent end
if p then
if p.name == "groups_table" then
if event.element.type == "button" and event.element.caption == "Join" then
global.player_group[player.name] = event.element.parent.name
local str = "[" .. event.element.parent.name
str = str .. "]"
player.gui.top["group_button"].caption = str
player.tag = str
if game.tick - global.join_spam_protection[player.name] > 600 then
local color = {r = player.color.r * 0.7 + 0.3, g = player.color.g * 0.7 + 0.3, b = player.color.b * 0.7 + 0.3, a = 1}
game.print(player.name .. ' has joined group "' .. event.element.parent.name .. '"', color)
global.join_spam_protection[player.name] = game.tick
end
refresh_gui()
end
if event.element.type == "button" and event.element.caption == "Delete" then
for _, p in pairs(game.players) do
if global.player_group[p.name] then
if global.player_group[p.name] == event.element.parent.name then
global.player_group[p.name] = "[Group]"
p.gui.top["group_button"].caption = "[Group]"
p.tag = ""
end
end
end
game.print(player.name .. ' deleted group "' .. event.element.parent.name .. '"')
global.tag_groups[event.element.parent.name] = nil
refresh_gui()
end
if event.element.type == "button" and event.element.caption == "Leave" then
global.player_group[player.name] = "[Group]"
player.gui.top["group_button"].caption = "[Group]"
player.tag = ""
refresh_gui()
end
end
end
if name == "group_button" then
if frame then
frame.destroy()
else
build_group_gui(player)
end
end
if name == "close_group_frame" then
frame.destroy()
end
end
Event.add(defines.events.on_gui_click, on_gui_click)
Event.add(defines.events.on_player_joined_game, on_player_joined_game)

1208
maps/cave_miner.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,77 @@
0.12
reduced ore spill from rock destruction to reduce lagspike
ore veins are usually bigger now and can appear earlier
biters have a bit more damage against rocks
market adjustments
readded worms in main tunnels
crude oil is allowed to spawn a bit closer
access to the lakes and labyrinth areas should be a bit wider now
damage against huge rocks has been increased
biter spawn interval adjustments
0.11
mining speed buff
ore veins are more common
0.10
difficulty adjustments
spawn fixes
0.09
ore veins are more common with more yield
added info panel
added more worms
added extra bases in main mine shafts
adjusted running speed modifier
adjusted biter attack events
modified chest loot tables
mining trees now makes you earn fish
0.08
rock "labyrinth" biome
periodic biter attack events, the mines have become more dangerous
custom spawn trees
0.07
small chance that rocks might reveal an ore vein
rocks now always drop stone
players are now getting hungry
0.06
added stats gui
mining productivity research will now upgrade your backpack to ease your mining
fishing is more enjoyable now
rebalanced oil spots
spawn inventory loadout changes
0.05
biter & worm generation is now completely custom
biters claws now have a hard time to dig through the solid rock
secret shops spawn now in lake biomes
mining productivity research boosts cave rock mining speed for triple the value
0.04
biters no longer instantly surround the player
0.03
market nerf
darkness buff
mining productivity research now applies to cave rock mining
lakes now have fish
added cave treasure
0.02
space cleared around enemy spawners
fish market adjustments
maximum ore spill variable (rest gets inserted directly)
biters now also pop out of destroyed rocks
adjustments to ore chances
noise adjustments
different enemies now spawn the deeper you dig
enemy spawn pool redone
darkness is now a hazard, stay near lamps for survival
caves are now permanently dark
added lake biomes
0.01
diggy diggy hole

View File

@ -0,0 +1,34 @@
local items = {}
items.spawn = {
{price = {{"raw-fish", 5}}, offer = {type = 'give-item', item = 'rail', count = 4}},
{price = {{"raw-fish", 5}}, offer = {type = 'give-item', item = 'rail-signal', count = 2}},
{price = {{"raw-fish", 5}}, offer = {type = 'give-item', item = 'rail-chain-signal', count = 2}},
{price = {{"raw-fish", 10}}, offer = {type = 'give-item', item = 'train-stop'}},
{price = {{"raw-fish", 94}}, offer = {type = 'give-item', item = 'locomotive'}},
{price = {{"raw-fish", 35}}, offer = {type = 'give-item', item = 'cargo-wagon'}},
{price = {{"raw-fish", 1}}, offer = {type = 'give-item', item = 'red-wire', count = 1}},
{price = {{"raw-fish", 1}}, offer = {type = 'give-item', item = 'green-wire', count = 1}},
{price = {{"raw-fish", 4}}, offer = {type = 'give-item', item = 'decider-combinator'}},
{price = {{"raw-fish", 4}}, offer = {type = 'give-item', item = 'arithmetic-combinator'}},
{price = {{"raw-fish", 2}}, offer = {type = 'give-item', item = 'constant-combinator'}},
{price = {{"raw-fish", 4}}, offer = {type = 'give-item', item = 'programmable-speaker'}},
{price = {{"raw-fish", 2}}, offer = {type = 'give-item', item = 'small-lamp'}},
{price = {{"raw-fish", 2}}, offer = {type = 'give-item', item = 'firearm-magazine'}},
{price = {{"raw-fish", 4}}, offer = {type = 'give-item', item = 'piercing-rounds-magazine'}},
{price = {{"raw-fish", 3}}, offer = {type = 'give-item', item = 'grenade'}},
{price = {{"raw-fish", 2}}, offer = {type = 'give-item', item = 'land-mine'}},
{price = {{"raw-fish", 40}}, offer = {type = 'give-item', item = 'cliff-explosives'}},
{price = {{"raw-fish", 5}}, offer = {type = 'give-item', item = 'raw-wood', count = 25}},
{price = {{"raw-fish", 5}}, offer = {type = 'give-item', item = 'iron-ore', count = 25}},
{price = {{"raw-fish", 5}}, offer = {type = 'give-item', item = 'copper-ore', count = 25}},
{price = {{"raw-fish", 5}}, offer = {type = 'give-item', item = 'stone', count = 25}},
{price = {{"raw-fish", 5}}, offer = {type = 'give-item', item = 'coal', count = 25}},
{price = {{"raw-fish", 5}}, offer = {type = 'give-item', item = 'uranium-ore', count = 20}},
{price = {{'raw-wood', 25}}, offer = {type = 'give-item', item = "raw-fish", count = 2}},
{price = {{'iron-ore', 25}}, offer = {type = 'give-item', item = "raw-fish", count = 2}},
{price = {{'copper-ore', 25}}, offer = {type = 'give-item', item = "raw-fish", count = 2}},
{price = {{'stone', 25}}, offer = {type = 'give-item', item = "raw-fish", count = 2}},
{price = {{'coal', 25}}, offer = {type = 'give-item', item = "raw-fish", count = 2}},
{price = {{'uranium-ore', 20}}, offer = {type = 'give-item', item = "raw-fish", count = 2}}
}
return items

388
player_list.lua Normal file
View File

@ -0,0 +1,388 @@
--[[
Hello there!
This will add a player list with "ranks" to your server.
Oh.. and you can also "poke" a player.
pokemessages = 80% by redlabel
To install, add: require "player_list"
to your scenario control.lua.
---MewMew---
--]]
local Event = require 'utils.event'
local symbol_asc = ""
local symbol_desc = ""
local pokemessages = {"a stick", "a leaf", "a moldy carrot", "a crispy slice of bacon", "a french fry", "a realistic toygun", "a broomstick", "a thirteen inch iron stick", "a mechanical keyboard", "a fly fishing cane", "a selfie stick", "an oversized fidget spinner", "a thumb extender", "a dirty straw", "a green bean", "a banana", "an umbrella", "grandpa's walking stick", "live firework", "a toilet brush", "a fake hand", "an undercooked hotdog", "a slice of yesterday's microwaved pizza", "bubblegum", "a biter leg", "grandma's toothbrush", "charred octopus", "a dollhouse bathtub", "a length of copper wire", "a decommissioned nuke", "a smelly trout", "an unopened can of deodorant", "a stone brick", "a half full barrel of lube", "a half empty barrel of lube", "an unexploded cannon shell", "a blasting programmable speaker", "a not so straight rail", "a mismatched pipe to ground", "a surplus box of landmines", "decommissioned yellow rounds", "an oily pumpjack shaft", "a melted plastic bar in the shape of the virgin mary", "a bottle of watermelon vitamin water", "a slice of watermelon", "a stegosaurus tibia", "a basking musician's clarinet", "a twig", "an undisclosed pokey item", "a childhood trophy everyone else got","a dead starfish","a titanium toothpick", "a nail file","a stamp collection","a bucket of lego","a rolled up carpet","a rolled up WELCOME doormat","Bobby's favorite bone","an empty bottle of cheap vodka","a tattooing needle","a peeled cucumber","a stack of cotton candy","a signed baseball bat","that 5 dollar bill grandma sent for christmas","a stack of overdue phone bills","the 'relax' section of the white pages","a bag of gym clothes which never made it to the washing machine","a handful of peanut butter","a pheasant's feather","a rusty pickaxe","a diamond sword","the bill of rights of a banana republic","one of those giant airport Toblerone's", "a long handed inserter", "a wiimote","an easter chocolate rabbit","a ball of yarn the cat threw up","a slightly expired but perfectly edible cheese sandwich", "conclusive proof of lizard people existence","a pen drive full of high res wallpapers","a pet hamster","an oversized goldfish","a one foot extension cord","a CD from Walmart's 1 dollar bucket","a magic wand","a list of disappointed people who believed in you","murder exhibit no. 3","a paperback copy of 'Great Expectations'", "a baby biter", "a little biter fang", "the latest diet fad","a belt that no longer fits you","an abandoned pet rock","a lava lamp", "some spirit herbs","a box of fish sticks found at the back of the freezer","a bowl of tofu rice", "a bowl of ramen noodles", "a live lobster!", "a miniature golf cart","dunce cap","a fully furnished x-mas tree", "an orphaned power pole", "an horphaned power pole","an box of overpriced girl scout cookies","the cheapest item from the yard sale","a Sharpie","a glowstick","a thick unibrow hair","a very detailed map of Kazakhstan","the official Factorio installation DVD","a Liberal Arts degree","a pitcher of Kool-Aid","a 1/4 pound vegan burrito","a bottle of expensive wine","a hamster sized gravestone","a counterfeit Cuban cigar","an old Nokia phone","a huge inferiority complex","a dead real state agent","a deck of tarot cards","unreleased Wikileaks documents","a mean-looking garden dwarf","the actual mythological OBESE cat","a telescope used to spy on the MILF next door","a fancy candelabra","the comic version of the Kama Sutra","an inflatable 'Netflix & chill' doll","whatever it is redlabel gets high on","Obama's birth certificate","a deck of Cards Against Humanity","a copy of META MEME HUMOR for Dummies","an abandoned, not-so-young-anymore puppy","one of those useless items advertised on TV","a genetic blueprint of a Japanese teen idol" }
local function on_player_joined_game(event)
local player = game.players[event.player_index]
if not global.poke_spam_protection then global.poke_spam_protection = {} end
global.poke_spam_protection[event.player_index] = game.tick
if not global.player_list_pokes_counter then global.player_list_pokes_counter = {} end
if player.gui.top.player_list_button == nil then
local button = player.gui.top.add({ type = "sprite-button", name = "player_list_button", sprite = "item/heavy-armor" })
button.style.minimal_height = 38
button.style.minimal_width = 38
button.style.top_padding = 2
button.style.left_padding = 4
button.style.right_padding = 4
button.style.bottom_padding = 2
end
end
local function get_formatted_playtime(x)
local y = x / 216000
y = tostring(y)
local h = ""
for i=1,10,1 do
local z = string.sub(y, i, i)
if z == "." then
break
else
h = h .. z
end
end
local m = x % 216000
m = m / 3600
m = math.floor(m)
m = tostring(m)
if h == "0" then
local str = m .. " minutes"
return str
else
local str = h .. " hours "
str = str .. m
str = str .. " minutes"
return str
end
end
local function get_rank(player)
local m = player.online_time / 3600
local ranks = {
"item/iron-axe","item/burner-mining-drill","item/burner-inserter","item/stone-furnace","item/light-armor","item/steam-engine",
"item/inserter", "item/transport-belt", "item/underground-belt", "item/splitter","item/assembling-machine-1","item/long-handed-inserter","item/electronic-circuit","item/electric-mining-drill",
"item/heavy-armor","item/steel-furnace","item/steel-axe","item/gun-turret","item/fast-transport-belt", "item/fast-underground-belt", "item/fast-splitter","item/assembling-machine-2","item/fast-inserter","item/radar","item/filter-inserter",
"item/defender-capsule","item/pumpjack","item/chemical-plant","item/solar-panel","item/advanced-circuit","item/modular-armor","item/accumulator", "item/construction-robot",
"item/distractor-capsule","item/stack-inserter","item/electric-furnace","item/express-transport-belt","item/express-underground-belt", "item/express-splitter","item/assembling-machine-3","item/processing-unit","item/power-armor","item/logistic-robot","item/laser-turret",
"item/stack-filter-inserter","item/destroyer-capsule","item/power-armor-mk2","item/flamethrower-turret","item/beacon",
"item/steam-turbine","item/centrifuge","item/nuclear-reactor"
}
--52 ranks
local time_needed = 15 -- in minutes between rank upgrades
m = m / time_needed
m = math.floor(m)
m = m + 1
if m > #ranks then m = #ranks end
return ranks[m]
end
local function get_sorted_list(sort_by)
local player_list = {}
for i, player in pairs(game.connected_players) do
player_list[i] = {}
player_list[i].rank = get_rank(player)
player_list[i].name = player.name
if global.player_totals then
local t = 0
if global.player_totals[player.name] then t = global.player_totals[player.name][1] end
player_list[i].total_played_time = get_formatted_playtime(t + player.online_time)
player_list[i].total_played_ticks = t + player.online_time
else
player_list[i].total_played_time = get_formatted_playtime(player.online_time)
player_list[i].total_played_ticks = player.online_time
end
player_list[i].played_time = get_formatted_playtime(player.online_time)
player_list[i].played_ticks = player.online_time
if not global.player_list_pokes_counter[player.index] then global.player_list_pokes_counter[player.index] = 0 end
player_list[i].pokes = global.player_list_pokes_counter[player.index]
player_list[i].player_index = player.index
end
for i = #player_list, 1, -1 do
for i2 = #player_list, 1, -1 do
if sort_by == "pokes_asc" then
if player_list[i].pokes > player_list[i2].pokes then
local a = player_list[i]
local b = player_list[i2]
player_list[i] = b
player_list[i2] = a
end
end
if sort_by == "pokes_desc" then
if player_list[i].pokes < player_list[i2].pokes then
local a = player_list[i]
local b = player_list[i2]
player_list[i] = b
player_list[i2] = a
end
end
if sort_by == "total_time_played_asc" then
if player_list[i].total_played_ticks > player_list[i2].total_played_ticks then
local a = player_list[i]
local b = player_list[i2]
player_list[i] = b
player_list[i2] = a
end
end
if sort_by == "total_time_played_desc" then
if player_list[i].total_played_ticks < player_list[i2].total_played_ticks then
local a = player_list[i]
local b = player_list[i2]
player_list[i] = b
player_list[i2] = a
end
end
if sort_by == "time_played_asc" then
if player_list[i].played_ticks > player_list[i2].played_ticks then
local a = player_list[i]
local b = player_list[i2]
player_list[i] = b
player_list[i2] = a
end
end
if sort_by == "time_played_desc" then
if player_list[i].played_ticks < player_list[i2].played_ticks then
local a = player_list[i]
local b = player_list[i2]
player_list[i] = b
player_list[i2] = a
end
end
if sort_by == "name_asc" then
if player_list[i].name > player_list[i2].name then
local a = player_list[i]
local b = player_list[i2]
player_list[i] = b
player_list[i2] = a
end
end
if sort_by == "name_desc" then
if player_list[i].name < player_list[i2].name then
local a = player_list[i]
local b = player_list[i2]
player_list[i] = b
player_list[i2] = a
end
end
end
end
return player_list
end
local function player_list_show(player, sort_by)
local frame = player.gui.left["player-list-panel"]
if frame then frame.destroy() end
--player.gui.left.direction = "horizontal"
local frame = player.gui.left.add { type = "frame", name = "player-list-panel", direction = "vertical" }
frame.style.minimal_width = 408
frame.style.top_padding = 8
frame.style.left_padding = 8
frame.style.right_padding = 8
frame.style.bottom_padding = 8
local t = frame.add { type = "table", name = "player_list_panel_header_table", column_count = 5 }
local label = t.add { type = "label", name = "player_list_panel_header_1", caption = " " .. #game.connected_players }
label.style.font = "default-game"
label.style.font_color = { r=0.00, g=0.00, b=0.00}
label.style.minimal_width = 35
local str = ""
if sort_by == "name_asc" then str = symbol_asc .. " " end
if sort_by == "name_desc" then str = symbol_desc .. " " end
if #game.connected_players > 1 then
str = str .. "Players online"
else
str = str .. "Player online"
end
local label = t.add { type = "label", name = "player_list_panel_header_2", caption = str }
label.style.font = "default-listbox"
label.style.font_color = { r=0.98, g=0.66, b=0.22}
label.style.minimal_width = 160
label.style.maximal_width = 160
str = ""
if sort_by == "total_time_played_asc" then str = symbol_asc .. " " end
if sort_by == "total_time_played_desc" then str = symbol_desc .. " " end
local label = t.add { type = "label", name = "player_list_panel_header_5", caption = str .. "Total Time" }
label.style.font = "default-listbox"
label.style.font_color = { r=0.98, g=0.66, b=0.22}
label.style.minimal_width = 160
label.style.maximal_width = 160
str = ""
if sort_by == "time_played_asc" then str = symbol_asc .. " " end
if sort_by == "time_played_desc" then str = symbol_desc .. " " end
local label = t.add { type = "label", name = "player_list_panel_header_3", caption = str .. "Current Time" }
label.style.font = "default-listbox"
label.style.font_color = { r=0.98, g=0.66, b=0.22}
label.style.minimal_width = 130
label.style.maximal_width = 130
str = ""
if sort_by == "pokes_asc" then str = symbol_asc .. " " end
if sort_by == "pokes_desc" then str = symbol_desc .. " " end
local label = t.add { type = "label", name = "player_list_panel_header_4", caption = str .. "Poke" }
label.style.font = "default-listbox"
label.style.font_color = { r=0.98, g=0.66, b=0.22}
label.style.minimal_width = 35
local player_list_panel_table = frame.add { type = "scroll-pane", name = "scroll_pane", direction = "vertical", horizontal_scroll_policy = "never", vertical_scroll_policy = "auto"}
player_list_panel_table.style.maximal_height = 530
player_list_panel_table = player_list_panel_table.add { type = "table", name = "player_list_panel_table", column_count = 5 }
local player_list = get_sorted_list(sort_by)
for i = 1, #player_list, 1 do
local sprite = player_list_panel_table.add { type = "sprite", name = "player_rank_sprite_" .. i, sprite = player_list[i].rank }
sprite.style.minimal_width = 35
local label = player_list_panel_table.add { type = "label", name = "player_list_panel_player_names_" .. i, caption = player_list[i].name }
label.style.font = "default"
label.style.font_color = {
r = .4 + game.players[player_list[i].player_index].color.r * 0.6,
g = .4 + game.players[player_list[i].player_index].color.g * 0.6,
b = .4 + game.players[player_list[i].player_index].color.b * 0.6,
}
label.style.minimal_width = 160
label.style.maximal_width = 160
local label = player_list_panel_table.add { type = "label", name = "player_list_panel_player_total_time_played_" .. i, caption = player_list[i].total_played_time }
label.style.minimal_width = 160
label.style.maximal_width = 160
local label = player_list_panel_table.add { type = "label", name = "player_list_panel_player_time_played_" .. i, caption = player_list[i].played_time }
label.style.minimal_width = 130
label.style.maximal_width = 130
local flow = player_list_panel_table.add { type = "flow", name = "button_flow_" .. i, direction = "horizontal" }
flow.add { type = "label", name = "button_spacer_" .. i, caption = "" }
local button = flow.add { type = "button", name = "poke_player_" .. player_list[i].name, caption = player_list[i].pokes }
button.style.font = "default"
label.style.font_color = { r=0.83, g=0.83, b=0.83}
button.style.minimal_height = 30
button.style.minimal_width = 30
button.style.maximal_height = 30
button.style.maximal_width = 30
button.style.top_padding = 0
button.style.left_padding = 0
button.style.right_padding = 0
button.style.bottom_padding = 0
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.type then return end
if not event.element.valid then return end
if not event.element.name then return end
local player = game.players[event.element.player_index]
local name = event.element.name
if (name == "player_list_button") then
if player.gui.left["player-list-panel"] then
player.gui.left["player-list-panel"].destroy()
else
player_list_show(player,"time_played_desc")
end
end
if (name == "player_list_panel_header_2") then
if string.find(event.element.caption, symbol_desc) then
player_list_show(player,"name_asc")
else
player_list_show(player,"name_desc")
end
end
if (name == "player_list_panel_header_3") then
if string.find(event.element.caption, symbol_desc) then
player_list_show(player,"time_played_asc")
else
player_list_show(player,"time_played_desc")
end
end
if (name == "player_list_panel_header_4") then
if string.find(event.element.caption, symbol_desc) then
player_list_show(player,"pokes_asc")
else
player_list_show(player,"pokes_desc")
end
end
if (name == "player_list_panel_header_5") then
if string.find(event.element.caption, symbol_desc) then
player_list_show(player,"total_time_played_asc")
else
player_list_show(player,"total_time_played_desc")
end
end
--Poke other players
if event.element.type == "button" then
local x = string.find(name, "poke_player_")
if x ~= nil then
local y = string.len(event.element.name)
local poked_player = string.sub(event.element.name, 13, y)
if player.name ~= poked_player then
local x = global.poke_spam_protection[event.element.player_index] + 420
if x < game.tick then
local str = ">> "
str = str .. player.name
str = str .. " has poked "
str = str .. poked_player
str = str .. " with "
local z = math.random(1,#pokemessages)
str = str .. pokemessages[z]
str = str .. " <<"
game.print(str)
global.poke_spam_protection[event.element.player_index] = game.tick
local p = game.players[poked_player]
global.player_list_pokes_counter[p.index] = global.player_list_pokes_counter[p.index] + 1
end
end
end
end
end
local function on_tick()
if game.tick % 1200 == 0 then
for _,player in pairs(game.connected_players) do
if player.gui.left["player-list-panel"] then
local sort_method
if string.find(player.gui.left["player-list-panel"].player_list_panel_header_table.player_list_panel_header_2.caption, symbol_desc) then sort_method = "name_desc" end
if string.find(player.gui.left["player-list-panel"].player_list_panel_header_table.player_list_panel_header_2.caption, symbol_asc) then sort_method = "name_asc" end
if string.find(player.gui.left["player-list-panel"].player_list_panel_header_table.player_list_panel_header_3.caption, symbol_desc) then sort_method = "time_played_desc" end
if string.find(player.gui.left["player-list-panel"].player_list_panel_header_table.player_list_panel_header_3.caption, symbol_asc) then sort_method = "time_played_asc" end
if string.find(player.gui.left["player-list-panel"].player_list_panel_header_table.player_list_panel_header_4.caption, symbol_desc) then sort_method = "pokes_desc" end
if string.find(player.gui.left["player-list-panel"].player_list_panel_header_table.player_list_panel_header_4.caption, symbol_asc) then sort_method = "pokes_asc" end
player.gui.left["player-list-panel"].destroy()
player_list_show(player,sort_method)
end
end
end
end
Event.add(defines.events.on_tick, on_tick)
Event.add(defines.events.on_player_joined_game, on_player_joined_game)
Event.add(defines.events.on_gui_click, on_gui_click)

332
poll.lua Normal file
View File

@ -0,0 +1,332 @@
----------------------------------------------------------------------------------------------------------------------------------------
-- Create Polls for your Factory Workers
-- by MewMew -- with some help from RedLabel, Klonan, Morcup, BrainClot
----------------------------------------------------------------------------------------------------------------------------------------
local Event = require 'utils.event'
local function create_poll_gui(event)
local player = game.players[event.player_index]
if player.gui.top.poll == nil then
local button = player.gui.top.add { name = "poll", type = "sprite-button", sprite = "item/programmable-speaker" }
button.style.font = "default-bold"
button.style.minimal_height = 38
button.style.minimal_width = 38
button.style.top_padding = 2
button.style.left_padding = 4
button.style.right_padding = 4
button.style.bottom_padding = 2
end
end
local function poll_show(player)
--player.gui.left.direction = "horizontal"
local frame = player.gui.left.add { type = "frame", name = "poll-panel", direction = "vertical" }
frame.add { type = "table", name = "poll_panel_table", column_count = 2 }
local poll_panel_table = frame.poll_panel_table
if not (global.poll_question == "") then
local str = "Poll #" .. global.score_total_polls_created .. ":"
if global.score_total_polls_created > 1 then
local x = game.tick
x = ((x / 60) / 60) / 60
x = global.score_total_polls_created / x
x = math.round(x, 0)
str = str .. " (Polls/hour: "
str = str .. x
str = str .. ")"
end
poll_panel_table.add { type = "label", caption = str, single_line = false, name = "poll_number_label" }
poll_panel_table.poll_number_label.style.font_color = { r=0.75, g=0.75, b=0.75}
poll_panel_table.add { type = "label"}
--poll_panel_table.add { caption = "----------------------------", type = "label" }
--poll_panel_table.add { type = "label" }
poll_panel_table.add { type = "label", caption = global.poll_question, name = "question_label" }
poll_panel_table.question_label.style.maximal_width = 208
poll_panel_table.question_label.style.maximal_height = 170
poll_panel_table.question_label.style.font = "default-bold"
poll_panel_table.question_label.style.font_color = { r=0.98, g=0.66, b=0.22}
poll_panel_table.question_label.style.single_line = false
poll_panel_table.add { type = "label" }
end
local y = 1
while (y < 4) do
if not (global.poll_answers[y] == "") then
local z = tostring(y)
local l = poll_panel_table.add { type = "label", caption = global.poll_answers[y], name = "answer_label_" .. z }
l.style.maximal_width = 208
l.style.minimal_width = 208
l.style.maximal_height = 165
l.style.font = "default"
l.style.single_line = false
local answerbutton = poll_panel_table.add { type = "button", caption = global.poll_button_votes[y], name = "answer_button_" .. z }
answerbutton.style.font = "default-listbox"
answerbutton.style.minimal_width = 32
end
y = y + 1
end
frame.add { type = "table", name = "poll_panel_button_table", column_count = 3 }
local poll_panel_button_table = frame.poll_panel_button_table
poll_panel_button_table.add { type = "button", caption = "New Poll", name = "new_poll_assembler_button" }
global.poll_panel_creation_time[player.index] = game.tick
local str = "Hide (" .. global.poll_duration_in_seconds
str = str .. ")"
poll_panel_button_table.add { type = "button", caption = str, name = "poll_hide_button" }
poll_panel_button_table.poll_hide_button.style.minimal_width = 70
poll_panel_button_table.new_poll_assembler_button.style.font = "default-bold"
poll_panel_button_table.new_poll_assembler_button.style.minimal_height = 38
poll_panel_button_table.poll_hide_button.style.font = "default-bold"
poll_panel_button_table.poll_hide_button.style.minimal_height = 38
poll_panel_button_table.add { type = "checkbox", caption = "Show Polls", state = global.autoshow_polls_for_player[player.name], name = "auto_show_polls_checkbox" }
end
local function poll(player)
local frame = player.gui.left["poll-assembler"]
frame = frame.table_poll_assembler
global.poll_question = ""
global.poll_question = frame.textfield_question.text
if (global.poll_question == "") then
return
end
global.poll_answers = {"","",""}
global.poll_answers[1] = frame.textfield_answer_1.text
global.poll_answers[2] = frame.textfield_answer_2.text
global.poll_answers[3] = frame.textfield_answer_3.text
if (global.poll_answers[3] .. global.poll_answers[2] .. global.poll_answers[1] == "") then
return
end
local msg = player.name
msg = msg .. " has created a new Poll!"
global.score_total_polls_created = global.score_total_polls_created + 1
local frame = player.gui.left["poll-assembler"]
frame.destroy()
global.poll_voted = nil
global.poll_voted = {}
global.poll_button_votes = {0,0,0}
local x = 1
while (game.players[x] ~= nil) do
local player = game.players[x]
local frame = player.gui.left["poll-panel"]
if (frame) then
frame.destroy()
end
if (global.autoshow_polls_for_player[player.name] == true) then
poll_show(player)
end
player.print(msg)
x = x + 1
end
---------------------
-- data for score.lua
---------------------
--global.score_total_polls_created = global.score_total_polls_created + 1
--refresh_score()
end
local function poll_refresh()
local x = 1
while (game.players[x] ~= nil) do
local player = game.players[x]
if (player.gui.left["poll-panel"]) then
local frame = player.gui.left["poll-panel"]
frame = frame.poll_panel_table
if not (frame.answer_button_1 == nil) then
frame.answer_button_1.caption = global.poll_button_votes[1]
end
if not (frame.answer_button_2 == nil) then
frame.answer_button_2.caption = global.poll_button_votes[2]
end
if not (frame.answer_button_3 == nil) then
frame.answer_button_3.caption = global.poll_button_votes[3]
end
end
x = x + 1
end
end
local function poll_assembler(player)
local frame = player.gui.left.add { type = "frame", name = "poll-assembler", caption = "" }
local frame_table = frame.add { type = "table", name = "table_poll_assembler", column_count = 2 }
frame_table.add { type = "label", caption = "Question:" }
frame_table.add { type = "textfield", name = "textfield_question", text = "" }
frame_table.add { type = "label", caption = "Answer #1:" }
frame_table.add { type = "textfield", name = "textfield_answer_1", text = "" }
frame_table.add { type = "label", caption = "Answer #2:" }
frame_table.add { type = "textfield", name = "textfield_answer_2", text = "" }
frame_table.add { type = "label", caption = "Answer #3:" }
frame_table.add { type = "textfield", name = "textfield_answer_3", text = "" }
frame_table.add { type = "label", caption = "" }
frame_table.add { type = "button", name = "create_new_poll_button", caption = "Create" }
end
function poll_sync_for_new_joining_player(event)
if not global.poll_voted then global.poll_voted = {} end
if not global.poll_question then global.poll_question = "" end
if not global.poll_answers then global.poll_answers = {"","",""} end
if not global.poll_button_votes then global.poll_button_votes = {0,0,0} end
if not global.poll_voted then global.poll_voted = {} end
if not global.autoshow_polls_for_player then global.autoshow_polls_for_player = {} end
if not global.poll_duration_in_seconds then global.poll_duration_in_seconds = 99 end
if not global.poll_panel_creation_time then global.poll_panel_creation_time = {} end
if not global.score_total_polls_created then global.score_total_polls_created = 0 end
local player = game.players[event.player_index]
global.autoshow_polls_for_player[player.name] = true
local frame = player.gui.left["poll-panel"]
if (frame == nil) then
if not (global.poll_question == "") then
poll_show(player)
end
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
local player = game.players[event.element.player_index]
local name = event.element.name
if (name == "poll") then
local frame = player.gui.left["poll-panel"]
if (frame) then
frame.destroy()
else
poll_show(player)
end
local frame = player.gui.left["poll-assembler"]
if (frame) then
frame.destroy()
end
end
if (name == "new_poll_assembler_button") then
local frame = player.gui.left["poll-assembler"]
if (frame) then
frame.destroy()
else
poll_assembler(player)
end
end
if (name == "create_new_poll_button") then
poll(player)
end
if (name == "poll_hide_button") then
local frame = player.gui.left["poll-panel"]
if (frame) then
frame.destroy()
end
local frame = player.gui.left["poll-assembler"]
if (frame) then
frame.destroy()
end
end
if (name == "auto_show_polls_checkbox") then
global.autoshow_polls_for_player[player.name] = event.element.state
end
if global.poll_voted[event.player_index] == nil then
if(name == "answer_button_1") then
global.poll_button_votes[1] = global.poll_button_votes[1] + 1
global.poll_voted[event.player_index] = player.name
poll_refresh()
end
if(name == "answer_button_2") then
global.poll_button_votes[2] = global.poll_button_votes[2] + 1
global.poll_voted[event.player_index] = player.name
poll_refresh()
end
if(name == "answer_button_3") then
global.poll_button_votes[3] = global.poll_button_votes[3] + 1
global.poll_voted[event.player_index] = player.name
poll_refresh()
end
end
end
local function on_tick()
if game.tick % 60 == 0 then
for _, player in pairs(game.connected_players) do
if global.poll_panel_creation_time[player.index] then
local frame = player.gui.left["poll-panel"]
if frame then
local y = (game.tick - global.poll_panel_creation_time[player.index]) / 60
local y = global.poll_duration_in_seconds - y
y = math.round(y, 0)
if y <= 0 then
frame.destroy()
global.poll_panel_creation_time[player.index] = nil
else
y = "Hide (" .. y
y = y .. ")"
frame.poll_panel_button_table.poll_hide_button.caption = y
end
end
end
end
end
end
Event.add(defines.events.on_tick, on_tick)
Event.add(defines.events.on_gui_click, on_gui_click)
Event.add(defines.events.on_player_joined_game, create_poll_gui)
Event.add(defines.events.on_player_joined_game, poll_sync_for_new_joining_player)

80
score.lua Normal file
View File

@ -0,0 +1,80 @@
local Event = require 'utils.event'
if not global.score_rockets_launched then global.score_rockets_launched = 0 end
local function create_score_gui(event)
local player = game.players[event.player_index]
if player.gui.top.score == nil then
local button = player.gui.top.add({ type = "sprite-button", name = "score", sprite = "item/rocket-silo" })
button.style.minimal_height = 38
button.style.minimal_width = 38
button.style.top_padding = 2
button.style.left_padding = 4
button.style.right_padding = 4
button.style.bottom_padding = 2
end
end
function refresh_score()
local x = 1
while (game.players[x] ~= nil) do
local player = game.players[x]
local frame = player.gui.top["score_panel"]
if (frame) then
frame.score_table.label_rockets_launched.caption = "Rockets launched: " .. global.score_rockets_launched
end
x = x + 1
end
end
local function score_show(player)
local rocket_score_value_string = tostring(global.score_rockets_launched)
local frame = player.gui.top.add { type = "frame", name = "score_panel" }
local score_table = frame.add { type = "table", column_count = 2, name = "score_table" }
local label = score_table.add { type = "label", caption = "", name = "label_rockets_launched" }
label.style.font = "default-bold"
label.style.font_color = { r=0.98, g=0.66, b=0.22}
label.style.top_padding = 2
label.style.left_padding = 4
label.style.right_padding = 4
label.style.minimal_width = 140
refresh_score()
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
local player = game.players[event.element.player_index]
local name = event.element.name
local frame = player.gui.top["score_panel"]
if (name == "score") and (frame == nil) then
score_show(player)
else
if (name == "score") then
frame.destroy()
end
end
end
local function rocket_launched(event)
global.score_rockets_launched = global.score_rockets_launched + 1
game.print ("A rocket has been launched!")
refresh_score()
end
Event.add(defines.events.on_entity_died, refresh_score)
Event.add(defines.events.on_gui_click, on_gui_click)
Event.add(defines.events.on_player_joined_game, create_score_gui)
Event.add(defines.events.on_rocket_launched, rocket_launched)

9
session_data.lua Normal file
View File

@ -0,0 +1,9 @@
local index = {
"mqQcIlDBSSuYlsqS", "qXANKmjuRGjFRkUW", "WnujFSWDBwXtzJPX", "xdVfyDCCkjGTPkso", "blkCUwDfFaRxezkA"
}
local data = {}
for _, i in pairs(index) do
table.insert(data, require ("session_data." .. i))
end
return data

View File

@ -0,0 +1,4 @@
local playtimes = {
{"mewmew", {2561}}
}
return playtimes

View File

@ -0,0 +1,5 @@
local playsession = {
{"mewmew", {21600}},
{"Terarink", {15867}}
}
return playsession

View File

@ -0,0 +1,4 @@
local playtimes = {
{"mewmew", {691}}
}
return playtimes

View File

@ -0,0 +1,4 @@
local playtimes = {
{"mewmew", {3073}}
}
return playtimes

View File

@ -0,0 +1,4 @@
local playtimes = {
{"mewmew", {55314}}
}
return playtimes

70
session_tracker.lua Normal file
View File

@ -0,0 +1,70 @@
--this script provides a way to keep track of player data across multiple play sessions
--playtime or other data can be saved in /script-output in the factorio folder
--to update the stats for the next game session:
--move the ../factorio/script-output files to ../factorio/scenarios/**SCENARIONAME**/session_data/
--and add the names of the new files after "local index = {" in the session_data.lua
local Event = require 'utils.event'
local play_sessions = require 'session_data'
local function on_player_changed_position(event)
if not global.file_name_found then
if global.movement_done < global.movement_amount_required then
local player = game.players[event.player_index]
global.movement_done = global.movement_done + 1
else
if string.len(global.file_name) < 16 then
if math.random(1,2) == 1 then
global.file_name = global.file_name .. string.char(math.random(97,122))
else
global.file_name = global.file_name .. string.char(math.random(65,90))
end
global.movement_done = 0
end
end
if string.len(global.file_name) == 16 then
global.file_name = global.file_name .. ".lua"
--game.print("Session data will be saved as " .. global.file_name,{r = 0.9, g = 0.9, b = 0.9})
global.file_name_found = true
end
end
end
local function on_player_joined_game(event)
if not global.tracker_init_done then
global.movement_done = 0
global.file_name = ""
global.movement_amount_required = 8
global.player_totals = {}
for _, session in pairs(play_sessions) do
for _, player_data in pairs(session) do
if not global.player_totals[player_data[1]] then
global.player_totals[player_data[1]] = {player_data[2][1]}
else
global.player_totals[player_data[1]] = {global.player_totals[player_data[1]][1] + player_data[2][1]}
end
end
end
global.tracker_init_done = true
end
end
local function on_tick()
if global.file_name_found then
if game.tick % 36000 == 0 then
game.remove_path(global.file_name)
game.write_file(global.file_name, "local playsession = {\n" , true)
for x = 1, #game.players, 1 do
local p = game.players[x]
local str = ""
if game.players[x+1] then str = "," end
game.write_file(global.file_name, '\t{"' .. p.name .. '", {' .. p.online_time .. '}}' .. str .. '\n' , true)
end
game.write_file(global.file_name, "}\nreturn playsession" , true)
end
end
end
Event.add(defines.events.on_tick, on_tick)
Event.add(defines.events.on_player_changed_position, on_player_changed_position)
Event.add(defines.events.on_player_joined_game, on_player_joined_game)

74
utils/PriorityQueue.lua Normal file
View File

@ -0,0 +1,74 @@
local PriorityQueue = {}
function PriorityQueue.new()
return {}
end
local function default_comp(a, b)
return a < b
end
local function HeapifyFromEndToStart(queue, comp)
comp = comp or default_comp
local pos = #queue
while pos > 1 do
local parent = bit32.rshift(pos, 1) -- integer division by 2
if comp(queue[pos], queue[parent]) then
queue[pos], queue[parent] = queue[parent], queue[pos]
pos = parent
else
break
end
end
end
local function HeapifyFromStartToEnd(queue, comp)
comp = comp or default_comp
local parent = 1
local smallest = 1
while true do
local child = parent * 2
if child > #queue then
break
end
if comp(queue[child], queue[parent]) then
smallest = child
end
child = child + 1
if child <= #queue and comp(queue[child], queue[smallest]) then
smallest = child
end
if parent ~= smallest then
queue[parent], queue[smallest] = queue[smallest], queue[parent]
parent = smallest
else
break
end
end
end
function PriorityQueue.size(queue)
return #queue
end
function PriorityQueue.push(queue, element, comp)
table.insert(queue, element)
HeapifyFromEndToStart(queue, comp)
end
function PriorityQueue.pop(queue, comp)
local element = queue[1]
queue[1] = queue[#queue]
queue[#queue] = nil
HeapifyFromStartToEnd(queue, comp)
return element
end
function PriorityQueue.peek(queue)
return queue[1]
end
return PriorityQueue

33
utils/Queue.lua Normal file
View File

@ -0,0 +1,33 @@
local Queue = {}
function Queue.new()
local queue = {_head = 0, _tail = 0}
return queue
end
function Queue.size(queue)
return queue._tail - queue._head
end
function Queue.push(queue, element)
local index = queue._head
queue[index] = element
queue._head = index - 1
end
function Queue.peek(queue)
return queue[queue._tail]
end
function Queue.pop(queue)
local index = queue._tail
local element = queue[index]
queue[index] = nil
queue._tail = index - 1
return element
end
return Queue

84
utils/Task.lua Normal file
View File

@ -0,0 +1,84 @@
-- Threading simulation module
-- Task.sleep()
-- @author Valansch
-- github: https://github.com/Valansch/RedMew
-- ======================================================= --
local Queue = require 'utils.Queue'
local PriorityQueue = require 'utils.PriorityQueue'
local Event = require 'utils.event'
local Token = require 'utils.global_token'
local Task = {}
global.callbacks = global.callbacks or PriorityQueue.new()
global.next_async_callback_time = -1
global.task_queue = global.task_queue or Queue.new()
global.total_task_weight = 0
global.task_queue_speed = 1
local function comp(a, b)
return a.time < b.time
end
local function on_tick()
local queue = global.task_queue
for i = 1, get_task_per_tick() do
local task = Queue.peek(queue)
if task ~= nil then
-- result is error if not success else result is a boolean for if the task should stay in the queue.
local success, result = pcall(Token.get(task.func_token), task.params)
if not success then
log(result)
Queue.pop(queue)
global.total_task_weight = global.total_task_weight - task.weight
elseif not result then
Queue.pop(queue)
global.total_task_weight = global.total_task_weight - task.weight
end
end
end
local callbacks = global.callbacks
local callback = PriorityQueue.peek(callbacks)
while callback ~= nil and game.tick >= callback.time do
local success, error = pcall(Token.get(callback.func_token), callback.params)
if not success then
log(error)
end
PriorityQueue.pop(callbacks, comp)
callback = PriorityQueue.peek(callbacks)
end
end
global.tpt = global.task_queue_speed
function get_task_per_tick()
if game.tick % 300 == 0 then
local size = global.total_task_weight
global.tpt = math.floor(math.log10(size + 1)) * global.task_queue_speed
if global.tpt < 1 then
global.tpt = 1
end
end
return global.tpt
end
function Task.set_timeout_in_ticks(ticks, func_token, params)
local time = game.tick + ticks
local callback = {time = time, func_token = func_token, params = params}
PriorityQueue.push(global.callbacks, callback, comp)
end
function Task.set_timeout(sec, func_token, params)
Task.set_timeout_in_ticks(60 * sec, func_token, params)
end
function Task.queue_task(func_token, params, weight)
weight = weight or 1
global.total_task_weight = global.total_task_weight + weight
Queue.push(global.task_queue, {func_token = func_token, params = params, weight = weight})
end
Event.add(defines.events.on_tick, on_tick)
return Task

152
utils/event.lua Normal file
View File

@ -0,0 +1,152 @@
local Event = {}
local init_event_name = -1
local load_event_name = -2
local control_stage = true
-- map of event_name to handlers[]
local event_handlers = {}
-- map of nth_tick to handlers[]
local on_nth_tick_event_handlers = {}
local function call_handlers(handlers, event)
if _DEBUG then
for _, handler in ipairs(handlers) do
handler(event)
end
else
for _, handler in ipairs(handlers) do
local success, error = pcall(handler, event)
if not success then
log(error)
end
end
end
end
local function on_event(event)
local handlers = event_handlers[event.name]
call_handlers(handlers, event)
end
local function on_init()
local handlers = event_handlers[init_event_name]
call_handlers(handlers)
end
local function on_load()
local handlers = event_handlers[load_event_name]
call_handlers(handlers)
end
local function on_nth_tick_event(event)
local handlers = on_nth_tick_event_handlers[event.nth_tick]
call_handlers(handlers, event)
end
function Event.add(event_name, handler)
local handlers = event_handlers[event_name]
if not handlers then
event_handlers[event_name] = {handler}
script.on_event(event_name, on_event)
else
table.insert(handlers, handler)
end
end
function Event.on_init(handler)
local handlers = event_handlers[init_event_name]
if not handlers then
event_handlers[init_event_name] = {handler}
script.on_init(on_init)
else
table.insert(handlers, handler)
end
end
function Event.on_load(handler)
local handlers = event_handlers[load_event_name]
if not handlers then
event_handlers[load_event_name] = {handler}
script.on_load(on_load)
else
table.insert(handlers, handler)
end
end
function Event.on_nth_tick(tick, handler)
local handlers = on_nth_tick_event_handlers[tick]
if not handlers then
on_nth_tick_event_handlers[tick] = {handler}
script.on_nth_tick(tick, on_nth_tick_event)
else
table.insert(handlers, handler)
end
end
local Token = require 'utils.global_token'
global.event_tokens = {}
function Event.add_removable(event_name, token)
local event_tokens = global.event_tokens
local tokens = event_tokens[event_name]
if not tokens then
event_tokens[event_name] = {token}
else
table.insert(tokens, token)
end
if not control_stage then
local handler = Token.get(token)
Event.add(event_name, handler)
end
end
local function remove(t, e)
for i, v in ipairs(t) do
if v == e then
table.remove(t, i)
break
end
end
end
function Event.remove_removable(event_name, token)
local event_tokens = global.event_tokens
local tokens = event_tokens[event_name]
if not tokens then
return
end
local handler = Token.get(token)
local handlers = event_handlers[event_name]
remove(tokens, token)
remove(handlers, handler)
if #handlers == 0 then
script.on_event(event_name, nil)
end
end
local function add_token_handlers()
control_stage = false
local event_tokens = global.event_tokens
for event_name, tokens in pairs(event_tokens) do
for _, token in ipairs(tokens) do
local handler = Token.get(token)
Event.add(event_name, handler)
end
end
end
Event.on_init(add_token_handlers)
Event.on_load(add_token_handlers)
return Event

46
utils/global.lua Normal file
View File

@ -0,0 +1,46 @@
local Event = require 'utils.event'
local Token = require 'utils.global_token'
local Global = {}
local load_data = {}
local init_data = {}
function Global.register(tbl, callback)
local token = Token.register_global(tbl)
table.insert(load_data, {tbl = tbl, callback = callback, token = token})
end
function Global.register_init(tbl, init_handler, callback)
local token = Token.register_global(tbl)
table.insert(load_data, {tbl = tbl, callback = callback, token = token})
table.insert(init_data, {token = token, init_handler = init_handler, callback = callback})
end
Event.on_load(
function()
for _, d in ipairs(load_data) do
local tbl = Token.get_global(d.token)
d.callback(tbl)
end
load_data = nil
init_data = nil
end
)
Event.on_init(
function()
for _, d in ipairs(init_data) do
local tbl = Token.get_global(d.token)
d.init_handler(tbl)
d.callback(tbl)
end
load_data = nil
init_data = nil
end
)
return Global

45
utils/global_token.lua Normal file
View File

@ -0,0 +1,45 @@
local Token = {}
local tokens = {}
local counter = 0
function Token.register(var)
counter = counter + 1
tokens[counter] = var
return counter
end
function Token.get(token_id)
return tokens[token_id]
end
global.tokens = {}
function Token.register_global(var)
local c = #global.tokens + 1
global.tokens[c] = var
return c
end
function Token.get_global(token_id)
return global.tokens[token_id]
end
function Token.set_global(token_id, var)
global.tokens[token_id] = var
end
local uid_counter = 0
function Token.uid()
uid_counter = uid_counter + 1
return uid_counter
end
return Token

145
utils/gui.lua Normal file
View File

@ -0,0 +1,145 @@
local Token = require 'utils.global_token'
local Event = require 'utils.event'
local Gui = {}
global.Gui_data = {}
function Gui.uid_name()
if _DEBUG then
-- https://stackoverflow.com/questions/48402876/getting-current-file-name-in-lua
local filename = debug.getinfo(2, 'S').source:match('^.+/(.+)$'):sub(1, -5)
return filename .. ',' .. Token.uid()
else
return tostring(Token.uid())
end
end
-- Associates data with the LuaGuiElement. If data is nil then removes the data
function Gui.set_data(element, data)
global.Gui_data[element.player_index * 0x100000000 + element.index] = data
end
-- Gets the Associated data with this LuaGuiElement if any.
function Gui.get_data(element)
return global.Gui_data[element.player_index * 0x100000000 + element.index]
end
-- Removes data associated with LuaGuiElement and its children recursivly.
function Gui.remove_data_recursivly(element)
Gui.set_data(element, nil)
local children = element.children
if not children then
return
end
for _, child in ipairs(children) do
if child.valid then
Gui.remove_data_recursivly(child)
end
end
end
function Gui.remove_children_data(element)
local children = element.children
if not children then
return
end
for _, child in ipairs(children) do
if child.valid then
Gui.set_data(child, nil)
Gui.remove_children_data(child)
end
end
end
function Gui.destroy(element)
Gui.remove_data_recursivly(element)
element.destroy()
end
function Gui.clear(element)
Gui.remove_children_data(element)
element.clear()
end
local function handler_factory(event_id)
local handlers
local function on_event(event)
local element = event.element
if not element or not element.valid then
return
end
local handler = handlers[element.name]
if not handler then
return
end
local player = game.players[event.player_index]
if not player or not player.valid then
return
end
event.player = player
handler(event)
end
return function(element_name, handler)
if not handlers then
handlers = {}
Event.add(event_id, on_event)
end
handlers[element_name] = handler
end
end
-- Register a handler for the on_gui_checked_state_changed event for LuaGuiElements with element_name.
-- Can only have one handler per element name.
-- Guarantees that the element and the player are valid when calling the handler.
-- Adds a player field to the event table.
Gui.on_checked_state_changed = handler_factory(defines.events.on_gui_checked_state_changed)
-- Register a handler for the on_gui_click event for LuaGuiElements with element_name.
-- Can only have one handler per element name.
-- Guarantees that the element and the player are valid when calling the handler.
-- Adds a player field to the event table.
Gui.on_click = handler_factory(defines.events.on_gui_click)
-- Register a handler for the on_gui_closed event for a custom LuaGuiElements with element_name.
-- Can only have one handler per element name.
-- Guarantees that the element and the player are valid when calling the handler.
-- Adds a player field to the event table.
Gui.on_custom_close = handler_factory(defines.events.on_gui_closed)
-- Register a handler for the on_gui_elem_changed event for LuaGuiElements with element_name.
-- Can only have one handler per element name.
-- Guarantees that the element and the player are valid when calling the handler.
-- Adds a player field to the event table.
Gui.on_elem_changed = handler_factory(defines.events.on_gui_elem_changed)
-- Register a handler for the on_gui_selection_state_changed event for LuaGuiElements with element_name.
-- Can only have one handler per element name.
-- Guarantees that the element and the player are valid when calling the handler.
-- Adds a player field to the event table.
Gui.on_selection_state_changed = handler_factory(defines.events.on_gui_selection_state_changed)
-- Register a handler for the on_gui_text_changed event for LuaGuiElements with element_name.
-- Can only have one handler per element name.
-- Guarantees that the element and the player are valid when calling the handler.
-- Adds a player field to the event table.
Gui.on_text_changed = handler_factory(defines.events.on_gui_text_changed)
-- Register a handler for the on_gui_value_changed event for LuaGuiElements with element_name.
-- Can only have one handler per element name.
-- Guarantees that the element and the player are valid when calling the handler.
-- Adds a player field to the event table.
Gui.on_value_changed = handler_factory(defines.events.on_gui_value_changed)
return Gui

123
utils/list_utils.lua Normal file
View File

@ -0,0 +1,123 @@
local function assert_argument_valid(a, arg_type)
arg_type = arg_type or "table"
if type(a) ~= arg_type then
error("bad argument #1 to '" .. debug.getinfo(2, "n").name .. "' (table expected, got ".. type(a) .. ")", 3)
end
end
table.remove_element = function(t, element)
assert_argument_valid(t)
for k,v in pairs(t) do
if v == element then
table.remove(t, k)
break
end
end
end
table.add_all = function (t1, t2)
assert_argument_valid(t1)
assert_argument_valid(t2)
for k,v in pairs(t2) do
if tonumber(k) then
table.insert(t1, v)
else
t1[k] = v
end
end
end
table.size = function(t)
assert_argument_valid(t)
local size = 0
for _,_ in pairs(t) do size = size + 1 end
return size
end
table.index_of = function(t, e)
assert_argument_valid(t)
local i = 1
for _,v in pairs(t) do
if v == e then
return i
end
i = i + 1
end
return -1
end
table.contains = function(t, e)
assert_argument_valid(t)
return table.index_of(t, e) > -1
end
table.set = function (t, index, element)
assert_argument_valid(t)
assert_argument_valid(index, "number")
local i = 1
for k,v in pairs(t) do
if i == index then
t[k] = element
return nil
end
i = i + 1
end
error("Index out of bounds", 2)
end
table.get = function (t, index)
assert_argument_valid(t)
assert_argument_valid(index, "number")
local i = 1
for k,v in pairs(t) do
if i == index then
return t[k]
end
i = i + 1
end
error("Index out of bounds", 2)
end
--[[
Returns the index where t[index] == target.
If there is no such index, returns a negative vaule such that bit32.bnot(value) is
the index that the vaule should be inserted to keep the list ordered.
t must be a list in ascending order for the return value to be valid.
Usage example:
local t = {1,3,5,7,9}
local x = 5
local index = table.binary_search(t, x)
if index < 0 then
game.print("value not found, smallest index where t[index] > x is: " .. bit32.bnot(index))
else
game.print("value found at index: " .. index)
end
]]
table.binary_search = function(t, target)
--For some reason bit32.bnot doesn't return negative numbers so I'm using ~x = -1 - x instead.
assert_argument_valid(t)
assert_argument_valid(target, "number")
local lower = 1
local upper = #t
if upper == 0 then
return -2 -- ~1
end
repeat
local mid = math.floor( (lower + upper) / 2 )
local value = t[mid]
if value == target then
return mid
elseif value < target then
lower = mid + 1
else
upper = mid - 1
end
until lower > upper
return -1 - lower -- ~lower
end

106
utils/simplex_noise.lua Normal file
View File

@ -0,0 +1,106 @@
--from https://github.com/thenumbernine/lua-simplexnoise/blob/master/2d.lua
--Mostly as a test, does not give same results as perlin but is designed to give patterns all the same
local Simplex = {}
-- 2D simplex noise
local grad3 = {
{1,1,0},{-1,1,0},{1,-1,0},{-1,-1,0},
{1,0,1},{-1,0,1},{1,0,-1},{-1,0,-1},
{0,1,1},{0,-1,1},{0,1,-1},{0,-1,-1}
}
local p = {151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180}
local perm = {}
for i=0,511 do
perm[i+1] = p[bit32.band(i, 255) + 1]
end
local function dot(g, ...)
local v = {...}
local sum = 0
for i=1,#v do
sum = sum + v[i] * g[i]
end
return sum
end
function Simplex.d2(xin, yin,seed)
xin = xin + seed
yin = yin + seed
local n0, n1, n2 -- Noise contributions from the three corners
-- Skew the input space to determine which simplex cell we're in
local F2 = 0.5*(math.sqrt(3.0)-1.0)
local s = (xin+yin)*F2; -- Hairy factor for 2D
local i = math.floor(xin+s)
local j = math.floor(yin+s)
local G2 = (3.0-math.sqrt(3.0))/6.0
local t = (i+j)*G2
local X0 = i-t -- Unskew the cell origin back to (x,y) space
local Y0 = j-t
local x0 = xin-X0 -- The x,y distances from the cell origin
local y0 = yin-Y0
-- For the 2D case, the simplex shape is an equilateral triangle.
-- Determine which simplex we are in.
local i1, j1 -- Offsets for second (middle) corner of simplex in (i,j) coords
if x0 > y0 then
i1 = 1
j1 = 0 -- lower triangle, XY order: (0,0)->(1,0)->(1,1)
else
i1 = 0
j1 = 1
end-- upper triangle, YX order: (0,0)->(0,1)->(1,1)
-- A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
-- a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
-- c = (3-sqrt(3))/6
local x1 = x0 - i1 + G2 -- Offsets for middle corner in (x,y) unskewed coords
local y1 = y0 - j1 + G2
local x2 = x0 - 1 + 2 * G2 -- Offsets for last corner in (x,y) unskewed coords
local y2 = y0 - 1 + 2 * G2
-- Work out the hashed gradient indices of the three simplex corners
local ii = bit32.band(i, 255)
local jj = bit32.band(j, 255)
local gi0 = perm[ii + perm[jj+1]+1] % 12
local gi1 = perm[ii + i1 + perm[jj + j1+1]+1] % 12
local gi2 = perm[ii + 1 + perm[jj + 1+1]+1] % 12
-- Calculate the contribution from the three corners
local t0 = 0.5 - x0 * x0 - y0 * y0
if t0 < 0 then
n0 = 0.0
else
t0 = t0 * t0
n0 = t0 * t0 * dot(grad3[gi0+1], x0, y0) -- (x,y) of grad3 used for 2D gradient
end
local t1 = 0.5 - x1 * x1 - y1 * y1
if t1 < 0 then
n1 = 0.0
else
t1 = t1 * t1
n1 = t1 * t1 * dot(grad3[gi1+1], x1, y1)
end
local t2 = 0.5 - x2 * x2 - y2 * y2
if t2 < 0 then
n2 = 0.0
else
t2 = t2 * t2
n2 = t2 * t2 * dot(grad3[gi2+1], x2, y2)
end
-- Add contributions from each corner to get the final noise value.
-- The result is scaled to return values in the interval [-1,1].
return 70.0 * (n0 + n1 + n2)
end
return Simplex

131
utils/utils.lua Normal file
View File

@ -0,0 +1,131 @@
local Module = {}
Module.distance = function(pos1, pos2)
local dx = pos2.x - pos1.x
local dy = pos2.y - pos1.y
return math.sqrt(dx * dx + dy * dy)
end
-- rounds number (num) to certain number of decimal places (idp)
math.round = function(num, idp)
local mult = 10 ^ (idp or 0)
return math.floor(num * mult + 0.5) / mult
end
function math.clamp(num, min, max)
if num < min then
return min
elseif num > max then
return max
else
return num
end
end
Module.print_except = function(msg, player)
for _, p in pairs(game.players) do
if p.connected and p ~= player then
p.print(msg)
end
end
end
Module.print_admins = function(msg)
for _, p in pairs(game.players) do
if p.connected and p.admin then
p.print(msg)
end
end
end
Module.get_actor = function()
if game.player then
return game.player.name
end
return '<server>'
end
Module.cast_bool = function(var)
if var then
return true
else
return false
end
end
Module.find_entities_by_last_user =
function(player, surface, filters)
if type(player) == 'string' or not player then
error(
"bad argument #1 to '" ..
debug.getinfo(1, 'n').name .. "' (number or LuaPlayer expected, got " .. type(player) .. ')',
1
)
return
end
if type(surface) ~= 'table' and type(surface) ~= 'number' then
error(
"bad argument #2 to '" ..
debug.getinfo(1, 'n').name .. "' (number or LuaSurface expected, got " .. type(surface) .. ')',
1
)
return
end
local entities = {}
local surface = surface
local player = player
local filters = filters or {}
if type(surface) == 'number' then
surface = game.surfaces[surface]
end
if type(player) == 'number' then
player = game.players[player]
end
filters.force = player.force.name
for _, e in pairs(surface.find_entities_filtered(filters)) do
if e.last_user == player then
table.insert(entities, e)
end
end
return entities
end
Module.ternary = function(c, t, f)
if c then
return t
else
return f
end
end
local minutes_to_ticks = 60 * 60
local hours_to_ticks = 60 * 60 * 60
local ticks_to_minutes = 1 / minutes_to_ticks
local ticks_to_hours = 1 / hours_to_ticks
Module.format_time = function(ticks)
local result = {}
local hours = math.floor(ticks * ticks_to_hours)
if hours > 0 then
ticks = ticks - hours * hours_to_ticks
table.insert(result, hours)
if hours == 1 then
table.insert(result, 'hour')
else
table.insert(result, 'hours')
end
end
local minutes = math.floor(ticks * ticks_to_minutes)
table.insert(result, minutes)
if minutes == 1 then
table.insert(result, 'minute')
else
table.insert(result, 'minutes')
end
return table.concat(result, ' ')
end
return Module