z_mirror/TaskList

git clone https://git.raiguard.me/z_mirror/TaskList

Caleb HeuerAdd icon selector button to textfields155b98f

trunk
6.9 KiB227 linesraw
1--[[
2  DESIGN GOALS:
3  - Extremely simple to-do list
4  - Compact and full versions of the UI
5    - Compact for reference, full for manipulation
6  - Opt-in advanced features
7    - Timed reminders (interval, absolute time)
8    - Notifications for reminders
9    - Task assignment
10    - Maybe projects?
11  - Current task must be easily visible in some way
12    - Consider using the bottom-right of the screen for this
13  - "Private" and "public" tasks
14    - Private is visible only to you, public is visible to your force
15  - An infinite amount of subtask nesting
16  - Import and export TODOs
17
18  DESIGN NOTES:
19  - Each task will have an entirely unique ID
20    - Store the next ID in the root of `storage`
21  - Tasks will be stored in a one-dimensional table keyed by task ID
22  - Tasks can be owned by the force, or by the player
23    - Player tasks will be shown separately from force tasks
24  - A task can have infinite subtasks
25    - Subtasks are just pointers to other tasks, so they support the full feature suite, including more subtasks
26    - Subtasks may have a different assignee from their parent
27  - Import and export will use the standard method
28  - Each owner (force, player, or task) will have a `tasks` array containing the task IDs to show, in order
29    - Subtasks are also stored in the same manner, but are in a `subtasks` table instead
30  - When a task is completed, it is added to its owners `completed_tasks` array at the front
31    - Completed tasks will be listed after active tasks, in the order that they were completed
32    - Store completion time?
33]]
34
35local gui = require("gui")
36
37local active_task_button = require("scripts.gui.active-task-button")
38local migrations = require("scripts.migrations")
39local edit_task_gui = require("scripts.gui.edit-task.index")
40local player_data = require("scripts.player-data")
41local task = require("scripts.task")
42local tasks_gui = require("scripts.gui.tasks.index")
43local util = require("scripts.util")
44
45--- @param force LuaForce
46local function init_force(force)
47  --- @class ForceTable
48  storage.forces[force.index] = {
49    --- @type number[]
50    completed_tasks = {},
51    --- @type number[]
52    tasks = {},
53  }
54end
55
56-- BOOTSTRAP
57
58script.on_init(function()
59  --- @type table<uint, ForceTable>
60  storage.forces = {}
61  storage.next_task_id = 1
62  --- @type table<uint, PlayerTable>
63  storage.players = {}
64  --- @type table<number, Task>
65  storage.tasks = {}
66
67  for _, force in pairs(game.forces) do
68    init_force(force)
69  end
70  for _, player in pairs(game.players) do
71    player_data.init(player)
72  end
73
74  migrations.generic()
75end)
76
77script.on_load(function()
78  for _, Task in pairs(storage.tasks) do
79    task.load(Task)
80  end
81
82  for _, player_table in pairs(storage.players) do
83    tasks_gui.load(player_table.guis.tasks)
84    if player_table.guis.edit_task then
85      edit_task_gui.load(player_table.guis.edit_task)
86    end
87  end
88end)
89
90script.on_configuration_changed(function(e)
91  local old_version = e.mod_changes[script.mod_name] and e.mod_changes[script.mod_name].old_version
92  if not old_version then
93    return
94  end
95
96  migrations.generic()
97
98  for version, migration in pairs(migrations.versions) do
99    if helpers.compare_versions(old_version, version) < 0 then
100      migration()
101    end
102  end
103end)
104
105-- FORCE
106
107script.on_event(defines.events.on_force_created, function(e)
108  init_force(e.force)
109end)
110
111-- INTERACTION
112
113gui.hook_events(function(e)
114  local msg = gui.read_action(e)
115  if msg then
116    local Gui = util.get_gui(e.player_index, msg.gui)
117    if Gui then
118      Gui:dispatch(msg, e)
119    end
120  end
121end)
122
123script.on_event("tlst-linked-confirm-gui", function(e)
124  local player = game.get_player(e.player_index) --[[@as LuaPlayer]]
125  local EditTaskGui = util.get_gui(e.player_index, "edit_task")
126  if EditTaskGui then
127    EditTaskGui:dispatch({ action = "confirm" }, e )
128    player.play_sound({ path = "utility/confirm" })
129  elseif player.mod_settings["tlst-new-task-on-confirm"].value then
130    local TasksGui = util.get_gui(e.player_index, "tasks")
131    if TasksGui and TasksGui.state.visible and not TasksGui.state.pinned then
132      TasksGui:dispatch({ action = "edit_task", confirmed = true }, e )
133    end
134  end
135end)
136
137--- @param player_index uint
138local function toggle_new_task(player_index)
139  local EditTaskGui = util.get_gui(player_index, "edit_task")
140  if EditTaskGui then
141    EditTaskGui:destroy()
142  else
143    local player = game.get_player(player_index) --[[@as LuaPlayer]]
144    local player_table = storage.players[player_index]
145    edit_task_gui.new(player, player_table, { standalone = true })
146  end
147end
148
149script.on_event("tlst-new-task", function(e)
150  toggle_new_task(e.player_index)
151end)
152
153script.on_event({ "tlst-toggle-gui", defines.events.on_lua_shortcut }, function(e)
154  if (e.input_name or e.prototype_name) == "tlst-toggle-gui" then
155    local player_table = storage.players[e.player_index]
156    local Gui = util.get_gui(e.player_index, "tasks")
157    if Gui then
158      Gui:toggle()
159    else
160      local player = game.get_player(e.player_index) --[[@as LuaPlayer]]
161      player_data.refresh(player, player_table)
162    end
163  elseif e.prototype_name == "tlst-new-task" then
164    toggle_new_task(e.player_index)
165  end
166end)
167
168-- PLAYER
169
170script.on_event(defines.events.on_player_created, function(e)
171  local player = game.get_player(e.player_index) --[[@as LuaPlayer]]
172
173  player_data.init(player)
174  player_data.refresh(player, storage.players[e.player_index])
175end)
176
177script.on_event(defines.events.on_player_changed_force, function(e)
178  local player = game.get_player(e.player_index)
179  if not player then
180    return
181  end
182
183  local player_table = storage.players[e.player_index]
184  if not player_table then
185    return
186  end
187
188  player_data.refresh(player, player_table)
189end)
190
191script.on_event(defines.events.on_player_removed, function(e)
192  -- Remove all player tasks
193  local player_table = storage.players[e.player_index]
194  for _, task_ids in pairs({ player_table.completed_tasks, player_table.tasks }) do
195    for _, task_id in pairs(task_ids) do
196      local Task = storage.tasks[task_id]
197      if Task then
198        Task:delete()
199      end
200    end
201  end
202
203  storage.players[e.player_index] = nil
204end)
205
206script.on_event(defines.events.on_runtime_mod_setting_changed, function(e)
207  if e.setting == "tlst-new-task-on-confirm" then
208    local TasksGui = util.get_gui(e.player_index, "tasks")
209    if TasksGui then
210      local player = game.get_player(e.player_index) --[[@as LuaPlayer]]
211      local tooltip = { "gui.tlst-new-task" }
212      if player.mod_settings[e.setting].value then
213        tooltip[1] = tooltip[1] .. "-instruction"
214      end
215      TasksGui.refs.new_task_button.tooltip = tooltip
216    end
217  elseif e.setting == "tlst-show-active-task" or e.setting == "tlst-active-filter-assigned" then
218    local player = game.get_player(e.player_index) --[[@as LuaPlayer]]
219    local player_table = storage.players[e.player_index]
220    local value = player.mod_settings["tlst-show-active-task"].value
221    if value == "off" then
222      active_task_button.destroy(player_table)
223    else
224      active_task_button.build(player, player_table)
225    end
226  end
227end)