Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
454397b
initial mod scaffolding and import feature for online tool
abucnasty Jan 2, 2026
77322bf
include recipe force productivity from research
abucnasty Jan 2, 2026
f8e2a19
ignore zip files
abucnasty Jan 2, 2026
919eef7
properly handle extraction of mining drill data
abucnasty Jan 2, 2026
4f7e6b1
rename to clock generator sidecar
abucnasty Jan 2, 2026
0a7a475
add support for extracting inserter information
abucnasty Jan 2, 2026
29dd858
determine belts for inserters
abucnasty Jan 3, 2026
15f5df2
clear extraction result on closing the window
abucnasty Jan 3, 2026
b957a01
automatically determine belt stack size
abucnasty Jan 3, 2026
d57f5c7
use global stack size research as fallback for output stack inserters
abucnasty Jan 3, 2026
ebaba3a
support extracting the current mining productivity level
abucnasty Jan 3, 2026
71a2485
change reference names to clock-generator-sidecar
abucnasty Jan 3, 2026
fac212b
gotta have custom graphics... right?
abucnasty Jan 3, 2026
7d8b614
cleanup build to not log install instructions when install flag is set
abucnasty Jan 3, 2026
89f6fc7
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 3, 2026
94c773c
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 3, 2026
11273e5
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 3, 2026
881c5c4
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 4, 2026
1899405
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 4, 2026
a264bf2
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 4, 2026
ff94547
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 4, 2026
dfbcc5f
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 4, 2026
f62f884
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 5, 2026
7f634ae
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 6, 2026
6a498ab
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 6, 2026
30dfd3b
Merge branch 'master' into feat/abuc/extract-machine-info-mod
abucnasty Jan 10, 2026
0a295e6
refactor monolithic control.lua
abucnasty Jan 11, 2026
a7f0a54
ability to import chest info
abucnasty Jan 11, 2026
85a77bf
move transport belt 2 to 1 if 1 has no items
abucnasty Jan 11, 2026
5b06e6c
confirmation modal upon pasting from factorio
abucnasty Jan 11, 2026
25dba4e
refactor to move paste and reset actions to secondary drop down option
abucnasty Jan 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clock-generator-sidecar/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.zip
82 changes: 82 additions & 0 deletions clock-generator-sidecar/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/bin/bash
# Build script for Clock Generator Sidecar Factorio mod
# Creates a zip file ready for installation in Factorio's mods folder

set -e

# Get the directory where the script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

# Read mod info from info.json
MOD_NAME=$(grep -o '"name": *"[^"]*"' info.json | cut -d'"' -f4)
MOD_VERSION=$(grep -o '"version": *"[^"]*"' info.json | cut -d'"' -f4)

# Output filename follows Factorio convention: modname_version.zip
OUTPUT_NAME="${MOD_NAME}_${MOD_VERSION}"
OUTPUT_FILE="${OUTPUT_NAME}.zip"

echo "Building ${OUTPUT_FILE}..."

# Clean up any existing build
rm -f "$OUTPUT_FILE"

# Create a temporary directory with the correct structure
# Factorio expects mods to be in a folder named modname_version inside the zip
TEMP_DIR=$(mktemp -d)
MOD_DIR="${TEMP_DIR}/${OUTPUT_NAME}"
mkdir -p "$MOD_DIR"

# Copy mod files
cp info.json "$MOD_DIR/"
cp data.lua "$MOD_DIR/"
cp control.lua "$MOD_DIR/"
cp -r locale "$MOD_DIR/"
cp -r graphics "$MOD_DIR/"
cp -r scripts "$MOD_DIR/"

# Create the zip file
cd "$TEMP_DIR"
zip -r "$SCRIPT_DIR/$OUTPUT_FILE" "$OUTPUT_NAME"

# Clean up
rm -rf "$TEMP_DIR"

echo ""
echo "✅ Built: $OUTPUT_FILE"
echo ""

if [[ "$1" != "--install" ]]; then
echo "To install:"
echo " 1. Copy $OUTPUT_FILE to your Factorio mods folder:"
echo " macOS: ~/Library/Application Support/factorio/mods/"
echo " Linux: ~/.factorio/mods/"
echo " Windows: %APPDATA%\\Factorio\\mods\\"
echo ""
echo " 2. Or run: ./build.sh --install"
fi

# Handle --install flag
if [[ "$1" == "--install" ]]; then
# Detect Factorio mods folder
if [[ "$OSTYPE" == "darwin"* ]]; then
MODS_DIR="$HOME/Library/Application Support/factorio/mods"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
MODS_DIR="$HOME/.factorio/mods"
else
echo "❌ Auto-install not supported on this platform. Please copy manually."
exit 1
fi

if [[ -d "$MODS_DIR" ]]; then
# Remove old versions of this mod
rm -f "$MODS_DIR/${MOD_NAME}_"*.zip
cp "$SCRIPT_DIR/$OUTPUT_FILE" "$MODS_DIR/"
echo ""
echo "✅ Installed to: $MODS_DIR/$OUTPUT_FILE"
else
echo "❌ Mods folder not found: $MODS_DIR"
echo " Make sure Factorio has been run at least once."
exit 1
fi
fi
124 changes: 124 additions & 0 deletions clock-generator-sidecar/control.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
-- Clock Generator Sidecar - Main Control Script
-- Entry point for the mod. Handles player storage, event handlers, and
-- event registration. All heavy lifting is delegated to modules in scripts/.

require("scripts.types")
local extraction = require("scripts.extraction")
local export = require("scripts.export")
local gui = require("scripts.gui")

-- Player Storage

---@type table<uint, PlayerData>
storage = storage or {}

---Initialize player data in storage
---@param player_index uint
local function init_player_data(player_index)
storage[player_index] = storage[player_index] or {
machines = {},
gui = nil
}
end

-- Event Handlers

---Handle selection tool area selection
---@param event EventData.on_player_selected_area
local function on_player_selected_area(event)
if event.item ~= "clock-generator-sidecar" then
return
end

local player = game.get_player(event.player_index)
if not player then
return
end

init_player_data(event.player_index)
local player_data = storage[event.player_index]

-- Extract entity data (machines, drills, inserters, belts, and chests)
local result = extraction.extract_all_entities(event.entities, player.force)
player_data.extraction_result = result

-- Show GUI
gui.create(player, player_data, result)

local total = #result.machines + #result.drills + #result.inserters + #result.belts + #result.chests
if total == 0 then
player.print({ "clock-generator-sidecar.no-machines-selected" })
else
player.print({ "clock-generator-sidecar.machines-found", total })
end
end

---Handle GUI button clicks
---@param event EventData.on_gui_click
local function on_gui_click(event)
local element = event.element
if not element or not element.valid then
return
end

local player = game.get_player(event.player_index)
if not player then
return
end

local player_data = storage[event.player_index]

if element.name == "clock_generator_sidecar_copy" then
-- Show copy popup with JSON text
if player_data and player_data.extraction_result then
local result = player_data.extraction_result
if #result.machines > 0 or #result.drills > 0 or #result.inserters > 0 or #result.belts > 0 or #result.chests > 0 then
local json = export.to_json(result)
gui.create_copy_popup(player, json)
else
player.print("[Clock Generator Sidecar] No data found. Please select entities first.")
end
else
player.print("[Clock Generator Sidecar] No data found. Please select entities first.")
end
elseif element.name == "clock_generator_sidecar_close" then
gui.destroy_all(player, player_data)
elseif element.name == "clock_generator_sidecar_copy_close" then
-- Close just the copy popup
local copy_frame = player.gui.screen[gui.COPY_GUI_NAME]
if copy_frame and copy_frame.valid then
copy_frame.destroy()
end
end
end

---Handle GUI close
---@param event EventData.on_gui_closed
local function on_gui_closed(event)
if event.element and event.element.valid and event.element.name == gui.GUI_NAME then
local player = game.get_player(event.player_index)
if player then
local player_data = storage[event.player_index]
gui.destroy(player, player_data, true) -- Clear data when user closes the window
end
end
end

-- Event Registration

script.on_event(defines.events.on_player_selected_area, on_player_selected_area)
script.on_event(defines.events.on_player_alt_selected_area, on_player_selected_area)
script.on_event(defines.events.on_gui_click, on_gui_click)
script.on_event(defines.events.on_gui_closed, on_gui_closed)

-- Initialize storage for new players
script.on_event(defines.events.on_player_created, function(event)
init_player_data(event.player_index)
end)

-- Initialize storage on load
script.on_init(function()
for _, player in pairs(game.players) do
init_player_data(player.index)
end
end)
50 changes: 50 additions & 0 deletions clock-generator-sidecar/data.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-- Clock Generator Sidecar - Data Stage
-- Defines the selection tool prototype for selecting machines

data:extend({
-- Selection tool for extracting crafting speeds
{
type = "selection-tool",
name = "clock-generator-sidecar",
icon = "__clock-generator-sidecar__/graphics/bbq_right.png",
icon_size = 64,
subgroup = "tool",
order = "c[automated-construction]-d[clock-generator-sidecar]",
stack_size = 1,
select = {
border_color = { r = 0, g = 1, b = 0.5 },
cursor_box_type = "copy",
mode = { "buildable-type", "same-force" },
entity_type_filters = { "assembling-machine", "furnace", "mining-drill", "lab", "inserter", "transport-belt", "underground-belt", "splitter", "container", "logistic-container", "infinity-container", "linked-container" }
},
alt_select = {
border_color = { r = 1, g = 0.5, b = 0 },
cursor_box_type = "copy",
mode = { "buildable-type", "same-force" },
entity_type_filters = { "assembling-machine", "furnace", "mining-drill", "lab", "inserter", "transport-belt", "underground-belt", "splitter", "container", "logistic-container", "infinity-container", "linked-container" }
},
flags = { "only-in-cursor", "spawnable" }
},

-- Shortcut button for quick access
{
type = "shortcut",
name = "clock-generator-sidecar-shortcut",
action = "spawn-item",
item_to_spawn = "clock-generator-sidecar",
icon = "__clock-generator-sidecar__/graphics/bbq_right.png",
icon_size = 64,
small_icon = "__clock-generator-sidecar__/graphics/bbq_right.png",
small_icon_size = 64,
associated_control_input = "clock-generator-sidecar-toggle"
},

-- Custom input for keyboard shortcut
{
type = "custom-input",
name = "clock-generator-sidecar-toggle",
key_sequence = "ALT + E",
action = "spawn-item",
item_to_spawn = "clock-generator-sidecar"
}
})
Binary file added clock-generator-sidecar/graphics/bbq_right.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions clock-generator-sidecar/info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "clock-generator-sidecar",
"version": "0.1.0",
"title": "Clock Generator Sidecar",
"author": "abucnasty",
"contact": "",
"homepage": "",
"description": "Select machines to extract crafting speeds and productivity bonuses. Export data for use with the Clock Generator tool.",
"factorio_version": "2.0",
"dependencies": [
"base >= 2.0"
]
}
23 changes: 23 additions & 0 deletions clock-generator-sidecar/locale/en/locale.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[item-name]
clock-generator-sidecar=Clock Generator Sidecar

[item-description]
clock-generator-sidecar=Select machines to extract crafting speeds and productivity bonuses. Use the shortcut (ALT+E) or toolbar button for quick access.

[shortcut-name]
clock-generator-sidecar-shortcut=Clock Generator Sidecar

[clock-generator-sidecar]
gui-title=Clock Generator Sidecar
entity-count=__1__ entities selected
no-machines=No machines with active recipes found in selection.
no-machines-selected=No machines with active recipes found. Make sure machines have recipes set.
machines-found=Found __1__ machines with active recipes.
copy-button=Copy JSON
close-button=Close
copy-popup-title=Copy Machine Data
copy-instructions=Select all (Ctrl+A) and copy (Ctrl+C) the JSON below, then paste into Clock Generator UI:
col-machine=Machine
col-recipe=Recipe
col-speed=Speed
col-productivity=Productivity
Loading