From 9da692565a28d69e9e4bfb6ce9bdcb85164e4dc8 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 1 Mar 2025 16:18:45 -0600 Subject: [PATCH 01/44] New Feature: `gym` Code for dwarves to hit the gym when they yearn for the gains. Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training --- gym.lua | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 gym.lua diff --git a/gym.lua b/gym.lua new file mode 100644 index 000000000..f4e99f440 --- /dev/null +++ b/gym.lua @@ -0,0 +1,261 @@ +-- Code for dwarves to hit the gym when they yearn for the gains. +--[====[ +Gym +================= + +Tags: Fort| Needs | BugFix | Units + +Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training. Also Passively builds military skills and physical stats. + +CRITICAL SETUP: +01-Minimum 1 squad with the name "Gym" +02-An assigned squadleader in "Gym" +03-An assigned Barracks for the squad "Gym" +04-Active Training orders for the squad "Gym" + +This should be a new non military use squad. The uniform should be set to "No Uniform" and the squad should be set to "Constant Training" in the military screen. +Set the squad's schedule to full time training with at least 8 or 9 training. +The squad doesn't need months off. The members leave the squad once they have gotten their gains. + +NOTE-Dwarfs with the labor "Fish Dissection" enabled are ignored +Make a Dwarven labour with only the Fish Dissection enabled, set to "Only selected do this" and assign it to a dwarf to ignore them. + +Usage +-------- + + Gym [] + +Examples +-------- + +Gym + Current status of script + +Gym -start + checks to see if you have fullfilled the creation of a training gym + searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. + Once they have fulfilled their need they will be removed from the gym squad to be replaced by the next dwarf in the list. + +Gym -stop + Dwarves currently in the squad ,with the exception of the squadleader, will be unassigned and no new dwarves will be added to the squad. + +Options +------- + -start + Starts the script + If there is no squad named GYM with a squadleader assigned it will not proceed. + + -stop + Stops the script + + -t + Use integer values. (Default 3000) + The negative need threshhold to trigger for each citizen + The greater the number the longer before a dwarf is added to the waiting list. +]====] + +local repeatUtil = require 'repeat-util' +local utils=require('utils') + +validArgs = utils.invert({ + 'start', + 'stop', + 't' +}) + +local args = utils.processArgs({...}, validArgs) +local scriptname = "Gym" +local ignore_flag = 43 -- Fish Dissection labor id +local ignore_count = 0 +local need_id = 14 +local squadname ="Gym" + + +--###### +--Functions +--###### +function getAllCititzen() + local citizen = {} + local my_civ = df.global.world.world_data.active_site[0].entity_links[0].entity_id + for n, unit in ipairs(df.global.world.units.all) do + if unit.civ_id == my_civ and dfhack.units.isCitizen(unit) then + if unit.profession ~= df.profession.BABY and unit.profession ~= df.profession.CHILD then + if ( not unit.status.labors[ignore_flag] ) then + table.insert(citizen, unit) + else + ignore_count = ignore_count +1 + end + end + end + end + return citizen +end + +local citizen = getAllCititzen() + +function findNeed(unit,need_id) + local needs = unit.status.current_soul.personality.needs + local need_index = -1 + for k = #needs-1,0,-1 do + if needs[k].id == need_id then + need_index = k + break + end + end if (need_index ~= -1 ) then + return needs[need_index] + end + return nil +end + +--###### +--Main +--###### + +function getByID(id) + for n, unit in ipairs(citizen) do + if (unit.hist_figure_id == id) then + return unit + end + end + + return nil +end + +-- Find all training squads +-- Abort if no squads found +function checkSquads() + local squads = {} + local count = 0 + for n, mil in ipairs(df.global.world.squads.all) do + if (mil.alias == squadname) then + local leader = mil.positions[0].occupant + if ( leader ~= -1) then + table.insert(squads,mil) + count = count +1 + end + end + end + + if (count == 0) then + dfhack.print(scriptname.." | ") + dfhack.printerr('ERROR: You need a squad with the name ' .. squadname) + dfhack.print(scriptname.." | ") + dfhack.printerr('That has an active Squad Leader') + dfhack.color(-1) + return nil + end + + return squads +end + +function addTraining(squads,unit) + for n, squad in ipairs(squads) do + for i=1,9,1 do + if (unit.hist_figure_id == squad.positions[i].occupant) then + return true + end + + if (unit.military.squad_id ~= -1) then + return false + end + + if ( squad.positions[i].occupant == -1 ) then + squad.positions[i].occupant = unit.hist_figure_id + return true + end + end + end + + return false +end + +function removeTraining(squads,unit) + for n, squad in ipairs(squads) do + for i=1,9,1 do + if ( unit.hist_figure_id == squad.positions[i].occupant ) then + unit.military.squad_id = -1 + unit.military.squad_position = -1 + squad.positions[i].occupant = -1 + return true + end + end + end + return false +end + +function removeAll(squads) + if ( squads == nil) then return end + for n, squad in ipairs(squads) do + for i=1,9,1 do + local dwarf = getByID(squad.positions[i].occupant) + if (dwarf ~= nil) then + dwarf.military.squad_id = -1 + dwarf.military.squad_position = -1 + squad.positions[i].occupant = -1 + end + end + end +end + + +function check() + local squads = checkSquads() + local intraining_count = 0 + local inque_count = 0 + if ( squads == nil)then return end + for n, unit in ipairs(citizen) do + local need = findNeed(unit,need_id) + if ( need ~= nil ) then + if ( need.focus_level < threshold ) then + local bol = addTraining(squads,unit) + if ( bol ) then + intraining_count = intraining_count +1 + else + inque_count = inque_count +1 + end + else + removeTraining(squads,unit) + end + end + end + + dfhack.println(scriptname .. " | IGN: " .. ignore_count .. " TRAIN: " .. intraining_count .. " QUE: " ..inque_count ) +end + + +function start() + threshold = -5000 + dfhack.println(scriptname .. " | START") + + if (args.t) then + threshold = 0-tonumber(args.t) + end + + running = true + repeatUtil.scheduleEvery(scriptname,1000,'ticks',check) +end + +function stop() + repeatUtil.cancel(scriptname) + local squads = checkSquads() + removeAll(squads) + running = false + dfhack.println(scriptname .. " | STOP") +end + +if (args.stop) then + if (running) then stop() end + return +end + +if (args.start) then + if (running) then stop() end + start() + return +end + +if ( running ) then + dfhack.println(scriptname .." | Enabled") +else + dfhack.println(scriptname .." | Disabled") +end From 81127f2d2ab0ae506cecb27ee792c99107455224 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 1 Mar 2025 16:20:31 -0600 Subject: [PATCH 02/44] Fix whitespace --- gym.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gym.lua b/gym.lua index f4e99f440..06afa0c77 100644 --- a/gym.lua +++ b/gym.lua @@ -81,9 +81,9 @@ function getAllCititzen() if unit.civ_id == my_civ and dfhack.units.isCitizen(unit) then if unit.profession ~= df.profession.BABY and unit.profession ~= df.profession.CHILD then if ( not unit.status.labors[ignore_flag] ) then - table.insert(citizen, unit) - else - ignore_count = ignore_count +1 + table.insert(citizen, unit) + else + ignore_count = ignore_count +1 end end end @@ -101,7 +101,7 @@ function findNeed(unit,need_id) need_index = k break end - end if (need_index ~= -1 ) then + end if (need_index ~= -1 ) then return needs[need_index] end return nil @@ -226,9 +226,9 @@ end function start() threshold = -5000 dfhack.println(scriptname .. " | START") - + if (args.t) then - threshold = 0-tonumber(args.t) + threshold = 0-tonumber(args.t) end running = true From 5c08fb6aaa8e7b28a4e4b6208cebbe495043ab6c Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 1 Mar 2025 16:21:02 -0600 Subject: [PATCH 03/44] missed some --- gym.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gym.lua b/gym.lua index 06afa0c77..685a46669 100644 --- a/gym.lua +++ b/gym.lua @@ -128,7 +128,7 @@ function checkSquads() local count = 0 for n, mil in ipairs(df.global.world.squads.all) do if (mil.alias == squadname) then - local leader = mil.positions[0].occupant + local leader = mil.positions[0].occupant if ( leader ~= -1) then table.insert(squads,mil) count = count +1 @@ -160,7 +160,7 @@ function addTraining(squads,unit) end if ( squad.positions[i].occupant == -1 ) then - squad.positions[i].occupant = unit.hist_figure_id + squad.positions[i].occupant = unit.hist_figure_id return true end end From edb3e830b56f78f2468876313617818244a206e1 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 1 Mar 2025 16:26:49 -0600 Subject: [PATCH 04/44] MORE whitespace (and some other cleanup) --- gym.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/gym.lua b/gym.lua index 685a46669..6af96a143 100644 --- a/gym.lua +++ b/gym.lua @@ -1,7 +1,7 @@ -- Code for dwarves to hit the gym when they yearn for the gains. --[====[ Gym -================= +=== Tags: Fort| Needs | BugFix | Units @@ -93,7 +93,7 @@ end local citizen = getAllCititzen() -function findNeed(unit,need_id) +function findNeed(unit,need_id) local needs = unit.status.current_soul.personality.needs local need_index = -1 for k = #needs-1,0,-1 do @@ -111,7 +111,7 @@ end --Main --###### -function getByID(id) +function getByID(id) for n, unit in ipairs(citizen) do if (unit.hist_figure_id == id) then return unit @@ -169,7 +169,7 @@ function addTraining(squads,unit) return false end -function removeTraining(squads,unit) +function removeTraining(squads,unit) for n, squad in ipairs(squads) do for i=1,9,1 do if ( unit.hist_figure_id == squad.positions[i].occupant ) then @@ -183,7 +183,7 @@ function removeTraining(squads,unit) return false end -function removeAll(squads) +function removeAll(squads) if ( squads == nil) then return end for n, squad in ipairs(squads) do for i=1,9,1 do @@ -208,9 +208,9 @@ function check() if ( need ~= nil ) then if ( need.focus_level < threshold ) then local bol = addTraining(squads,unit) - if ( bol ) then + if ( bol ) then intraining_count = intraining_count +1 - else + else inque_count = inque_count +1 end else @@ -223,11 +223,11 @@ function check() end -function start() +function start() threshold = -5000 dfhack.println(scriptname .. " | START") - if (args.t) then + if (args.t) then threshold = 0-tonumber(args.t) end @@ -246,9 +246,9 @@ end if (args.stop) then if (running) then stop() end return -end +end -if (args.start) then +if (args.start) then if (running) then stop() end start() return From 0c4f5b5729cc495a6ed4e24b48821a536a49644b Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 1 Mar 2025 16:29:11 -0600 Subject: [PATCH 05/44] Update gym.lua --- gym.lua | 220 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/gym.lua b/gym.lua index 6af96a143..199ceae37 100644 --- a/gym.lua +++ b/gym.lua @@ -21,7 +21,7 @@ NOTE-Dwarfs with the labor "Fish Dissection" enabled are ignored Make a Dwarven labour with only the Fish Dissection enabled, set to "Only selected do this" and assign it to a dwarf to ignore them. Usage --------- +----- Gym [] @@ -60,7 +60,7 @@ local utils=require('utils') validArgs = utils.invert({ 'start', 'stop', - 't' + 't' }) local args = utils.processArgs({...}, validArgs) @@ -75,20 +75,20 @@ local squadname ="Gym" --Functions --###### function getAllCititzen() - local citizen = {} - local my_civ = df.global.world.world_data.active_site[0].entity_links[0].entity_id - for n, unit in ipairs(df.global.world.units.all) do + local citizen = {} + local my_civ = df.global.world.world_data.active_site[0].entity_links[0].entity_id + for n, unit in ipairs(df.global.world.units.all) do if unit.civ_id == my_civ and dfhack.units.isCitizen(unit) then if unit.profession ~= df.profession.BABY and unit.profession ~= df.profession.CHILD then if ( not unit.status.labors[ignore_flag] ) then - table.insert(citizen, unit) - else - ignore_count = ignore_count +1 - end + table.insert(citizen, unit) + else + ignore_count = ignore_count +1 + end end end - end - return citizen + end + return citizen end local citizen = getAllCititzen() @@ -112,135 +112,135 @@ end --###### function getByID(id) - for n, unit in ipairs(citizen) do - if (unit.hist_figure_id == id) then - return unit - end - end + for n, unit in ipairs(citizen) do + if (unit.hist_figure_id == id) then + return unit + end + end - return nil + return nil end -- Find all training squads -- Abort if no squads found function checkSquads() - local squads = {} - local count = 0 - for n, mil in ipairs(df.global.world.squads.all) do - if (mil.alias == squadname) then - local leader = mil.positions[0].occupant - if ( leader ~= -1) then - table.insert(squads,mil) - count = count +1 - end - end - end - - if (count == 0) then - dfhack.print(scriptname.." | ") + local squads = {} + local count = 0 + for n, mil in ipairs(df.global.world.squads.all) do + if (mil.alias == squadname) then + local leader = mil.positions[0].occupant + if ( leader ~= -1) then + table.insert(squads,mil) + count = count +1 + end + end + end + + if (count == 0) then + dfhack.print(scriptname.." | ") dfhack.printerr('ERROR: You need a squad with the name ' .. squadname) - dfhack.print(scriptname.." | ") - dfhack.printerr('That has an active Squad Leader') - dfhack.color(-1) - return nil - end + dfhack.print(scriptname.." | ") + dfhack.printerr('That has an active Squad Leader') + dfhack.color(-1) + return nil + end - return squads + return squads end function addTraining(squads,unit) - for n, squad in ipairs(squads) do - for i=1,9,1 do - if (unit.hist_figure_id == squad.positions[i].occupant) then - return true - end - - if (unit.military.squad_id ~= -1) then - return false - end - - if ( squad.positions[i].occupant == -1 ) then - squad.positions[i].occupant = unit.hist_figure_id - return true - end - end - end - - return false + for n, squad in ipairs(squads) do + for i=1,9,1 do + if (unit.hist_figure_id == squad.positions[i].occupant) then + return true + end + + if (unit.military.squad_id ~= -1) then + return false + end + + if ( squad.positions[i].occupant == -1 ) then + squad.positions[i].occupant = unit.hist_figure_id + return true + end + end + end + + return false end function removeTraining(squads,unit) - for n, squad in ipairs(squads) do - for i=1,9,1 do - if ( unit.hist_figure_id == squad.positions[i].occupant ) then - unit.military.squad_id = -1 - unit.military.squad_position = -1 - squad.positions[i].occupant = -1 - return true - end - end - end - return false + for n, squad in ipairs(squads) do + for i=1,9,1 do + if ( unit.hist_figure_id == squad.positions[i].occupant ) then + unit.military.squad_id = -1 + unit.military.squad_position = -1 + squad.positions[i].occupant = -1 + return true + end + end + end + return false end function removeAll(squads) - if ( squads == nil) then return end - for n, squad in ipairs(squads) do - for i=1,9,1 do - local dwarf = getByID(squad.positions[i].occupant) - if (dwarf ~= nil) then - dwarf.military.squad_id = -1 - dwarf.military.squad_position = -1 - squad.positions[i].occupant = -1 - end - end - end + if ( squads == nil) then return end + for n, squad in ipairs(squads) do + for i=1,9,1 do + local dwarf = getByID(squad.positions[i].occupant) + if (dwarf ~= nil) then + dwarf.military.squad_id = -1 + dwarf.military.squad_position = -1 + squad.positions[i].occupant = -1 + end + end + end end function check() - local squads = checkSquads() - local intraining_count = 0 - local inque_count = 0 - if ( squads == nil)then return end - for n, unit in ipairs(citizen) do - local need = findNeed(unit,need_id) - if ( need ~= nil ) then - if ( need.focus_level < threshold ) then - local bol = addTraining(squads,unit) - if ( bol ) then - intraining_count = intraining_count +1 - else - inque_count = inque_count +1 - end - else - removeTraining(squads,unit) - end - end - end - - dfhack.println(scriptname .. " | IGN: " .. ignore_count .. " TRAIN: " .. intraining_count .. " QUE: " ..inque_count ) + local squads = checkSquads() + local intraining_count = 0 + local inque_count = 0 + if ( squads == nil)then return end + for n, unit in ipairs(citizen) do + local need = findNeed(unit,need_id) + if ( need ~= nil ) then + if ( need.focus_level < threshold ) then + local bol = addTraining(squads,unit) + if ( bol ) then + intraining_count = intraining_count +1 + else + inque_count = inque_count +1 + end + else + removeTraining(squads,unit) + end + end + end + + dfhack.println(scriptname .. " | IGN: " .. ignore_count .. " TRAIN: " .. intraining_count .. " QUE: " ..inque_count ) end function start() - threshold = -5000 - dfhack.println(scriptname .. " | START") + threshold = -5000 + dfhack.println(scriptname .. " | START") - if (args.t) then - threshold = 0-tonumber(args.t) - end + if (args.t) then + threshold = 0-tonumber(args.t) + end - running = true - repeatUtil.scheduleEvery(scriptname,1000,'ticks',check) + running = true + repeatUtil.scheduleEvery(scriptname,1000,'ticks',check) end function stop() - repeatUtil.cancel(scriptname) - local squads = checkSquads() - removeAll(squads) - running = false - dfhack.println(scriptname .. " | STOP") + repeatUtil.cancel(scriptname) + local squads = checkSquads() + removeAll(squads) + running = false + dfhack.println(scriptname .. " | STOP") end if (args.stop) then From 50d6a963fcd0c4aed2f157961f8d4ea5de5bdf8a Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 1 Mar 2025 22:16:15 -0600 Subject: [PATCH 06/44] Create gym.rst --- docs/gym.rst | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 docs/gym.rst diff --git a/docs/gym.rst b/docs/gym.rst new file mode 100644 index 000000000..e82cc9035 --- /dev/null +++ b/docs/gym.rst @@ -0,0 +1,54 @@ +Gym +=== + +.. dfhack-tool:: + :summary: Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training + :tags: Fort Needs BugFix Units + +Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training. Also Passively builds military skills and physical stats. + +CRITICAL SETUP: +01-Minimum 1 squad with the name "Gym" +02-An assigned squadleader in "Gym" +03-An assigned Barracks for the squad "Gym" +04-Active Training orders for the squad "Gym" + +This should be a new non military use squad. The uniform should be set to "No Uniform" and the squad should be set to "Constant Training" in the military screen. +Set the squad's schedule to full time training with at least 8 or 9 training. +The squad doesn't need months off. The members leave the squad once they have gotten their gains. + +NOTE-Dwarfs with the labor "Fish Dissection" enabled are ignored +Make a Dwarven labour with only the Fish Dissection enabled, set to "Only selected do this" and assign it to a dwarf to ignore them. + +Usage +----- + + ``gym []`` + +Examples +-------- + +``gym`` + Current status of script + +``gym -start`` + checks to see if you have fullfilled the creation of a training gym + searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. + Once they have fulfilled their need they will be removed from the gym squad to be replaced by the next dwarf in the list. + +``gym -stop`` + Dwarves currently in the squad ,with the exception of the squadleader, will be unassigned and no new dwarves will be added to the squad. + +Options +------- + ``-start`` + Starts the script + If there is no squad named GYM with a squadleader assigned it will not proceed. + + ``-stop`` + Stops the script + + ``-t`` + Use integer values. (Default 3000) + The negative need threshhold to trigger for each citizen + The greater the number the longer before a dwarf is added to the waiting list. \ No newline at end of file From deca155bc6a2735dc4307af3194ae50df6190354 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 1 Mar 2025 22:17:03 -0600 Subject: [PATCH 07/44] Fix EOF --- docs/gym.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gym.rst b/docs/gym.rst index e82cc9035..bee3dbbbf 100644 --- a/docs/gym.rst +++ b/docs/gym.rst @@ -51,4 +51,4 @@ Options ``-t`` Use integer values. (Default 3000) The negative need threshhold to trigger for each citizen - The greater the number the longer before a dwarf is added to the waiting list. \ No newline at end of file + The greater the number the longer before a dwarf is added to the waiting list. From 74784f89725c77c9de933a110a828aa345860951 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 1 Mar 2025 22:19:22 -0600 Subject: [PATCH 08/44] Update gym.rst --- docs/gym.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gym.rst b/docs/gym.rst index bee3dbbbf..860a3374b 100644 --- a/docs/gym.rst +++ b/docs/gym.rst @@ -1,4 +1,4 @@ -Gym +gym === .. dfhack-tool:: From f4cf0a06c7c358b9c3f413fff5b0cd52ae5ee591 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 1 Mar 2025 22:22:09 -0600 Subject: [PATCH 09/44] fix key error --- docs/gym.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gym.rst b/docs/gym.rst index 860a3374b..f711bd6bd 100644 --- a/docs/gym.rst +++ b/docs/gym.rst @@ -3,7 +3,7 @@ gym .. dfhack-tool:: :summary: Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training - :tags: Fort Needs BugFix Units + :tags: fort Needs BugFix Units Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training. Also Passively builds military skills and physical stats. From 39f2c9c0d616dd06902933178e9fbcfe58f5a549 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 1 Mar 2025 22:25:12 -0600 Subject: [PATCH 10/44] more key errors --- docs/gym.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gym.rst b/docs/gym.rst index f711bd6bd..f3ccb7d58 100644 --- a/docs/gym.rst +++ b/docs/gym.rst @@ -3,7 +3,7 @@ gym .. dfhack-tool:: :summary: Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training - :tags: fort Needs BugFix Units + :tags: fort auto bugfix units Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training. Also Passively builds military skills and physical stats. From a5d6c0ab875d931bbdb86443c1b18eca63eb8b73 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sun, 2 Mar 2025 19:01:38 -0600 Subject: [PATCH 11/44] Update the documentation --- docs/gym.rst | 26 +++++++++++++------------- gym.lua | 24 ++++++++++++------------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/gym.rst b/docs/gym.rst index f3ccb7d58..f6eac1a12 100644 --- a/docs/gym.rst +++ b/docs/gym.rst @@ -5,20 +5,20 @@ gym :summary: Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training :tags: fort auto bugfix units -Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training. Also Passively builds military skills and physical stats. +Also passively builds military skills and physical stats. -CRITICAL SETUP: -01-Minimum 1 squad with the name "Gym" -02-An assigned squadleader in "Gym" -03-An assigned Barracks for the squad "Gym" -04-Active Training orders for the squad "Gym" +Critical setup: -This should be a new non military use squad. The uniform should be set to "No Uniform" and the squad should be set to "Constant Training" in the military screen. +- Minimum 1 squad with the name "Gym" +- An assigned squadleader in "Gym" +- An assigned Barracks for the squad "Gym" +- Active Training orders for the squad "Gym" + +This should be a new non-military-use squad. The uniform should be set to "No Uniform" and the squad should be set to "Constant Training" in the military screen. Set the squad's schedule to full time training with at least 8 or 9 training. The squad doesn't need months off. The members leave the squad once they have gotten their gains. -NOTE-Dwarfs with the labor "Fish Dissection" enabled are ignored -Make a Dwarven labour with only the Fish Dissection enabled, set to "Only selected do this" and assign it to a dwarf to ignore them. +NOTE: Dwarfs with the labor "Fish Dissection" enabled are ignored. Make a Dwarven labour with only the Fish Dissection enabled, set to "Only selected do this" and assign it to a dwarf to ignore them. Usage ----- @@ -32,18 +32,18 @@ Examples Current status of script ``gym -start`` - checks to see if you have fullfilled the creation of a training gym - searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. + Checks to see if you have fullfilled the creation of a training gym. + Searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. Once they have fulfilled their need they will be removed from the gym squad to be replaced by the next dwarf in the list. ``gym -stop`` - Dwarves currently in the squad ,with the exception of the squadleader, will be unassigned and no new dwarves will be added to the squad. + Dwarves currently in the Gym squad, with the exception of the squad leader, will be unassigned and no new dwarves will be added to the squad. Options ------- ``-start`` Starts the script - If there is no squad named GYM with a squadleader assigned it will not proceed. + If there is no squad named ``Gym`` with a squadleader assigned it will not proceed. ``-stop`` Stops the script diff --git a/gym.lua b/gym.lua index 199ceae37..b423c4a14 100644 --- a/gym.lua +++ b/gym.lua @@ -5,20 +5,20 @@ Gym Tags: Fort| Needs | BugFix | Units -Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training. Also Passively builds military skills and physical stats. +Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training. Also passively builds military skills and physical stats. CRITICAL SETUP: -01-Minimum 1 squad with the name "Gym" -02-An assigned squadleader in "Gym" -03-An assigned Barracks for the squad "Gym" -04-Active Training orders for the squad "Gym" -This should be a new non military use squad. The uniform should be set to "No Uniform" and the squad should be set to "Constant Training" in the military screen. +- Minimum 1 squad with the name "Gym" +- An assigned squadleader in "Gym" +- An assigned Barracks for the squad "Gym" +- Active Training orders for the squad "Gym" + +This should be a new non-military-use squad. The uniform should be set to "No Uniform" and the squad should be set to "Constant Training" in the military screen. Set the squad's schedule to full time training with at least 8 or 9 training. The squad doesn't need months off. The members leave the squad once they have gotten their gains. -NOTE-Dwarfs with the labor "Fish Dissection" enabled are ignored -Make a Dwarven labour with only the Fish Dissection enabled, set to "Only selected do this" and assign it to a dwarf to ignore them. +NOTE: Dwarfs with the labor "Fish Dissection" enabled are ignored. Make a Dwarven labour with only the Fish Dissection enabled, set to "Only selected do this" and assign it to a dwarf to ignore them. Usage ----- @@ -32,18 +32,18 @@ Gym Current status of script Gym -start - checks to see if you have fullfilled the creation of a training gym - searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. + Checks to see if you have fullfilled the creation of a training gym. + Searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. Once they have fulfilled their need they will be removed from the gym squad to be replaced by the next dwarf in the list. Gym -stop - Dwarves currently in the squad ,with the exception of the squadleader, will be unassigned and no new dwarves will be added to the squad. + Dwarves currently in the Gym squad, with the exception of the squad leader, will be unassigned and no new dwarves will be added to the squad. Options ------- -start Starts the script - If there is no squad named GYM with a squadleader assigned it will not proceed. + If there is no squad named `Gym` with a squadleader assigned it will not proceed. -stop Stops the script From 6c532159713c2319e97fa2211a3f1d9d7876f7d9 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Thu, 6 Mar 2025 13:40:56 -0600 Subject: [PATCH 12/44] Use the enable/disable stuff not args to start or stop --- gym.lua | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/gym.lua b/gym.lua index b423c4a14..81d78e43d 100644 --- a/gym.lua +++ b/gym.lua @@ -23,43 +23,42 @@ NOTE: Dwarfs with the labor "Fish Dissection" enabled are ignored. Make a Dwarve Usage ----- - Gym [] + gym [] Examples -------- -Gym +gym Current status of script -Gym -start +enable gym Checks to see if you have fullfilled the creation of a training gym. Searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. Once they have fulfilled their need they will be removed from the gym squad to be replaced by the next dwarf in the list. -Gym -stop +disable gym Dwarves currently in the Gym squad, with the exception of the squad leader, will be unassigned and no new dwarves will be added to the squad. Options ------- - -start - Starts the script - If there is no squad named `Gym` with a squadleader assigned it will not proceed. - - -stop - Stops the script -t Use integer values. (Default 3000) The negative need threshhold to trigger for each citizen The greater the number the longer before a dwarf is added to the waiting list. ]====] +--@ enable = true +--@ module = true + +enabled = enabled or false +function isEnabled() + return enabled +end local repeatUtil = require 'repeat-util' local utils=require('utils') validArgs = utils.invert({ - 'start', - 'stop', 't' }) @@ -222,7 +221,6 @@ function check() dfhack.println(scriptname .. " | IGN: " .. ignore_count .. " TRAIN: " .. intraining_count .. " QUE: " ..inque_count ) end - function start() threshold = -5000 dfhack.println(scriptname .. " | START") @@ -243,19 +241,22 @@ function stop() dfhack.println(scriptname .. " | STOP") end -if (args.stop) then - if (running) then stop() end - return +if dfhack_flags.enable then + if dfhack_flags.enable_state then + start() + enabled = true + else + stop() + enabled = false + end end -if (args.start) then - if (running) then stop() end - start() +if dfhack_flags.module then return end if ( running ) then dfhack.println(scriptname .." | Enabled") else - dfhack.println(scriptname .." | Disabled") + dfhack.println(scriptname .." | Disabled") end From a6761bd2b444ba766a27fb3feb3e136e34721a58 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 12 Mar 2025 15:13:37 -0500 Subject: [PATCH 13/44] Do the documentation in one place --- docs/gym.rst | 14 ++++---------- gym.lua | 49 ------------------------------------------------- 2 files changed, 4 insertions(+), 59 deletions(-) diff --git a/docs/gym.rst b/docs/gym.rst index f6eac1a12..ae3e266b8 100644 --- a/docs/gym.rst +++ b/docs/gym.rst @@ -5,7 +5,7 @@ gym :summary: Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training :tags: fort auto bugfix units -Also passively builds military skills and physical stats. +Code for dwarves to hit the gym when they yearn for the gains. Also passively builds military skills and physical stats. Critical setup: @@ -31,23 +31,17 @@ Examples ``gym`` Current status of script -``gym -start`` +``enable gym`` Checks to see if you have fullfilled the creation of a training gym. + If there is no squad named ``Gym`` with a squadleader assigned it will not proceed. Searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. Once they have fulfilled their need they will be removed from the gym squad to be replaced by the next dwarf in the list. -``gym -stop`` +``disable gym`` Dwarves currently in the Gym squad, with the exception of the squad leader, will be unassigned and no new dwarves will be added to the squad. Options ------- - ``-start`` - Starts the script - If there is no squad named ``Gym`` with a squadleader assigned it will not proceed. - - ``-stop`` - Stops the script - ``-t`` Use integer values. (Default 3000) The negative need threshhold to trigger for each citizen diff --git a/gym.lua b/gym.lua index 81d78e43d..dc4f7e968 100644 --- a/gym.lua +++ b/gym.lua @@ -1,52 +1,3 @@ --- Code for dwarves to hit the gym when they yearn for the gains. ---[====[ -Gym -=== - -Tags: Fort| Needs | BugFix | Units - -Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training. Also passively builds military skills and physical stats. - -CRITICAL SETUP: - -- Minimum 1 squad with the name "Gym" -- An assigned squadleader in "Gym" -- An assigned Barracks for the squad "Gym" -- Active Training orders for the squad "Gym" - -This should be a new non-military-use squad. The uniform should be set to "No Uniform" and the squad should be set to "Constant Training" in the military screen. -Set the squad's schedule to full time training with at least 8 or 9 training. -The squad doesn't need months off. The members leave the squad once they have gotten their gains. - -NOTE: Dwarfs with the labor "Fish Dissection" enabled are ignored. Make a Dwarven labour with only the Fish Dissection enabled, set to "Only selected do this" and assign it to a dwarf to ignore them. - -Usage ------ - - gym [] - -Examples --------- - -gym - Current status of script - -enable gym - Checks to see if you have fullfilled the creation of a training gym. - Searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. - Once they have fulfilled their need they will be removed from the gym squad to be replaced by the next dwarf in the list. - -disable gym - Dwarves currently in the Gym squad, with the exception of the squad leader, will be unassigned and no new dwarves will be added to the squad. - -Options -------- - - -t - Use integer values. (Default 3000) - The negative need threshhold to trigger for each citizen - The greater the number the longer before a dwarf is added to the waiting list. -]====] --@ enable = true --@ module = true From 0acd601deb1decb7bdefa5284a3dc0d887746c27 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 12 Mar 2025 16:02:06 -0500 Subject: [PATCH 14/44] Various fixes - Clean up documentation - Add option to change squad name. - persist the enabled state, the threshold, and the squad name. - fixed findNeed function - renamed script to `autotraining` - made the ignore flag more clear and more changable - fixed 1 sided military link in `addTraining` --- docs/gym.rst | 16 +++++---- gym.lua | 95 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 71 insertions(+), 40 deletions(-) diff --git a/docs/gym.rst b/docs/gym.rst index ae3e266b8..6b681ac08 100644 --- a/docs/gym.rst +++ b/docs/gym.rst @@ -9,10 +9,10 @@ Code for dwarves to hit the gym when they yearn for the gains. Also passively bu Critical setup: -- Minimum 1 squad with the name "Gym" -- An assigned squadleader in "Gym" -- An assigned Barracks for the squad "Gym" -- Active Training orders for the squad "Gym" +- Minimum 1 squad with the correct name (default is "Gym") +- An assigned squad leader in the squad +- An assigned Barracks for the squad +- Active Training orders for the squad This should be a new non-military-use squad. The uniform should be set to "No Uniform" and the squad should be set to "Constant Training" in the military screen. Set the squad's schedule to full time training with at least 8 or 9 training. @@ -33,7 +33,7 @@ Examples ``enable gym`` Checks to see if you have fullfilled the creation of a training gym. - If there is no squad named ``Gym`` with a squadleader assigned it will not proceed. + If there is no squad named ``Gym`` with a squad leader assigned it will not proceed. Searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. Once they have fulfilled their need they will be removed from the gym squad to be replaced by the next dwarf in the list. @@ -43,6 +43,10 @@ Examples Options ------- ``-t`` - Use integer values. (Default 3000) + Use integer values. (Default 5000) The negative need threshhold to trigger for each citizen The greater the number the longer before a dwarf is added to the waiting list. + + ``-n`` + Use a string. (Default 'Gym') + Pick a different name for the squad the script looks for. diff --git a/gym.lua b/gym.lua index dc4f7e968..b0a49d86d 100644 --- a/gym.lua +++ b/gym.lua @@ -1,24 +1,51 @@ --@ enable = true --@ module = true -enabled = enabled or false -function isEnabled() - return enabled -end - local repeatUtil = require 'repeat-util' local utils=require('utils') validArgs = utils.invert({ - 't' + 't', + 'n' }) local args = utils.processArgs({...}, validArgs) -local scriptname = "Gym" -local ignore_flag = 43 -- Fish Dissection labor id +local GLOBAL_KEY = "autotraining" +local ignore_flag = df.unit_labor['DISSECT_FISH'] local ignore_count = 0 local need_id = 14 -local squadname ="Gym" + +local function get_default_state() + return { + enabled=false, + threshold=-5000, + squadname='Gym' + } +end + +state = state or get_default_state() + +function isEnabled() + return state.enabled +end + +dfhack.onStateChange[GLOBAL_KEY] = function(sc) + -- the state changed, is a map loaded and is that map in fort mode? + if sc ~= SC_MAP_LOADED or df.global.gamemode ~= df.game_mode.DWARF then + -- no its isnt, so bail + return + end + -- yes it was so: + -- retrieve state saved in game. merge with default state so config + -- saved from previous versions can pick up newer defaults. + state = get_default_state() + utils.assign(state, dfhack.persistent.getSiteData(GLOBAL_KEY, state)) +end + +-- Save any configurations in the save data +local function persist_state() + dfhack.persistent.saveSiteData(GLOBAL_KEY, state) +end --###### @@ -45,14 +72,10 @@ local citizen = getAllCititzen() function findNeed(unit,need_id) local needs = unit.status.current_soul.personality.needs - local need_index = -1 - for k = #needs-1,0,-1 do - if needs[k].id == need_id then - need_index = k - break + for _, need in ipairs(needs) do + if need.id == need_id then + return need end - end if (need_index ~= -1 ) then - return needs[need_index] end return nil end @@ -77,7 +100,7 @@ function checkSquads() local squads = {} local count = 0 for n, mil in ipairs(df.global.world.squads.all) do - if (mil.alias == squadname) then + if (mil.alias == state.squadname) then local leader = mil.positions[0].occupant if ( leader ~= -1) then table.insert(squads,mil) @@ -87,9 +110,9 @@ function checkSquads() end if (count == 0) then - dfhack.print(scriptname.." | ") - dfhack.printerr('ERROR: You need a squad with the name ' .. squadname) - dfhack.print(scriptname.." | ") + dfhack.print(GLOBAL_KEY .." | ") + dfhack.printerr('ERROR: You need a squad with the name ' .. state.squadname) + dfhack.print(GLOBAL_KEY .." | ") dfhack.printerr('That has an active Squad Leader') dfhack.color(-1) return nil @@ -111,6 +134,8 @@ function addTraining(squads,unit) if ( squad.positions[i].occupant == -1 ) then squad.positions[i].occupant = unit.hist_figure_id + unit.military.squad_id = squad.id + unit.military.squad_position = i return true end end @@ -169,36 +194,38 @@ function check() end end - dfhack.println(scriptname .. " | IGN: " .. ignore_count .. " TRAIN: " .. intraining_count .. " QUE: " ..inque_count ) + dfhack.println(GLOBAL_KEY .. " | IGN: " .. ignore_count .. " TRAIN: " .. intraining_count .. " QUE: " ..inque_count ) end function start() - threshold = -5000 - dfhack.println(scriptname .. " | START") + dfhack.println(GLOBAL_KEY .. " | START") if (args.t) then - threshold = 0-tonumber(args.t) + state.threshold = 0-tonumber(args.t) + end + if (args.n) then + state.squadname = args.n end - running = true - repeatUtil.scheduleEvery(scriptname,1000,'ticks',check) + repeatUtil.scheduleEvery(GLOBAL_KEY, 997, 'ticks', check) -- 997 is the closest prime to 1000 end function stop() - repeatUtil.cancel(scriptname) + repeatUtil.cancel(GLOBAL_KEY) local squads = checkSquads() removeAll(squads) - running = false - dfhack.println(scriptname .. " | STOP") + dfhack.println(GLOBAL_KEY .. " | STOP") end if dfhack_flags.enable then if dfhack_flags.enable_state then start() - enabled = true + state.enabled = true + persist_state() else stop() - enabled = false + state.enabled = false + persist_state() end end @@ -206,8 +233,8 @@ if dfhack_flags.module then return end -if ( running ) then - dfhack.println(scriptname .." | Enabled") +if ( state.enabled ) then + dfhack.println(GLOBAL_KEY .." | Enabled") else - dfhack.println(scriptname .." | Disabled") + dfhack.println(GLOBAL_KEY .." | Disabled") end From 9463d7995900bfc59553314ab26302bf5910faa5 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 12 Mar 2025 16:52:20 -0500 Subject: [PATCH 15/44] More cleanup Also tell the user when data was persisted (mostly for debugging) --- gym.lua | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/gym.lua b/gym.lua index b0a49d86d..b1da10a2e 100644 --- a/gym.lua +++ b/gym.lua @@ -13,7 +13,7 @@ local args = utils.processArgs({...}, validArgs) local GLOBAL_KEY = "autotraining" local ignore_flag = df.unit_labor['DISSECT_FISH'] local ignore_count = 0 -local need_id = 14 +local need_id = df.need_type['MartialTraining'] local function get_default_state() return { @@ -35,11 +35,14 @@ dfhack.onStateChange[GLOBAL_KEY] = function(sc) -- no its isnt, so bail return end - -- yes it was so: + -- yes it was, so: -- retrieve state saved in game. merge with default state so config -- saved from previous versions can pick up newer defaults. state = get_default_state() utils.assign(state, dfhack.persistent.getSiteData(GLOBAL_KEY, state)) + if state.enabled then + dfhack.print(GLOBAL_KEY .." was persisted with the following data:\nThreshold: ".. state.threshold .. ' | Squad name: '..state.squadname ..'.\n') + end end -- Save any configurations in the save data @@ -52,25 +55,21 @@ end --Functions --###### function getAllCititzen() - local citizen = {} - local my_civ = df.global.world.world_data.active_site[0].entity_links[0].entity_id - for n, unit in ipairs(df.global.world.units.all) do - if unit.civ_id == my_civ and dfhack.units.isCitizen(unit) then - if unit.profession ~= df.profession.BABY and unit.profession ~= df.profession.CHILD then - if ( not unit.status.labors[ignore_flag] ) then - table.insert(citizen, unit) - else - ignore_count = ignore_count +1 - end + local ret = {} + local citizen = dfhack.units.getCitizens(true) + for _, unit in ipairs(citizen) do + if unit.profession ~= df.profession.BABY and unit.profession ~= df.profession.CHILD then + if ( not unit.status.labors[ignore_flag] ) then + table.insert(ret, unit) + else + ignore_count = ignore_count +1 end end end - return citizen + return ret end -local citizen = getAllCititzen() - -function findNeed(unit,need_id) +function findNeed(unit) local needs = unit.status.current_soul.personality.needs for _, need in ipairs(needs) do if need.id == need_id then @@ -85,6 +84,7 @@ end --###### function getByID(id) + local citizen = getAllCititzen() for n, unit in ipairs(citizen) do if (unit.hist_figure_id == id) then return unit @@ -178,10 +178,11 @@ function check() local intraining_count = 0 local inque_count = 0 if ( squads == nil)then return end + local citizen = getAllCititzen() for n, unit in ipairs(citizen) do - local need = findNeed(unit,need_id) + local need = findNeed(unit) if ( need ~= nil ) then - if ( need.focus_level < threshold ) then + if ( need.focus_level < state.threshold ) then local bol = addTraining(squads,unit) if ( bol ) then intraining_count = intraining_count +1 @@ -198,7 +199,7 @@ function check() end function start() - dfhack.println(GLOBAL_KEY .. " | START") + dfhack.println(GLOBAL_KEY .. " | START") if (args.t) then state.threshold = 0-tonumber(args.t) @@ -234,7 +235,7 @@ if dfhack_flags.module then end if ( state.enabled ) then - dfhack.println(GLOBAL_KEY .." | Enabled") + dfhack.println(GLOBAL_KEY .." | Enabled") else - dfhack.println(GLOBAL_KEY .." | Disabled") + dfhack.println(GLOBAL_KEY .." | Disabled") end From 7dba7e437a00c80f9862002baaaf2f0b07400d87 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 12 Mar 2025 16:58:50 -0500 Subject: [PATCH 16/44] rename the script itself and update the docs to account. --- gym.lua => autotraining.lua | 0 docs/{gym.rst => autotraining.rst} | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) rename gym.lua => autotraining.lua (100%) rename docs/{gym.rst => autotraining.rst} (72%) diff --git a/gym.lua b/autotraining.lua similarity index 100% rename from gym.lua rename to autotraining.lua diff --git a/docs/gym.rst b/docs/autotraining.rst similarity index 72% rename from docs/gym.rst rename to docs/autotraining.rst index 6b681ac08..52afec63d 100644 --- a/docs/gym.rst +++ b/docs/autotraining.rst @@ -1,15 +1,15 @@ -gym +autotraining === .. dfhack-tool:: - :summary: Assigns Dwarves to a military squad until they have fulfilled their need for Martial Training + :summary: Assigns citizens to a military squad until they have fulfilled their need for Martial Training :tags: fort auto bugfix units -Code for dwarves to hit the gym when they yearn for the gains. Also passively builds military skills and physical stats. +Automation script for citizens to hit the gym when they yearn for the gains. Also passively builds military skills and physical stats. Critical setup: -- Minimum 1 squad with the correct name (default is "Gym") +- Minimum 1 squad with the correct name (default is ``Gym``) - An assigned squad leader in the squad - An assigned Barracks for the squad - Active Training orders for the squad @@ -23,21 +23,21 @@ NOTE: Dwarfs with the labor "Fish Dissection" enabled are ignored. Make a Dwarve Usage ----- - ``gym []`` + ``autotraining []`` Examples -------- -``gym`` +``autotraining`` Current status of script -``enable gym`` +``enable autotraining`` Checks to see if you have fullfilled the creation of a training gym. - If there is no squad named ``Gym`` with a squad leader assigned it will not proceed. + If there is no squad with the correct name (default: ``Gym``) with a squad leader assigned it will not proceed. Searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. Once they have fulfilled their need they will be removed from the gym squad to be replaced by the next dwarf in the list. -``disable gym`` +``disable autotraining`` Dwarves currently in the Gym squad, with the exception of the squad leader, will be unassigned and no new dwarves will be added to the squad. Options @@ -48,5 +48,5 @@ Options The greater the number the longer before a dwarf is added to the waiting list. ``-n`` - Use a string. (Default 'Gym') + Use a string. (Default ``Gym``) Pick a different name for the squad the script looks for. From 86967d7573dd40a2b1612cabdbc23338fc76f8ca Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 12 Mar 2025 17:00:06 -0500 Subject: [PATCH 17/44] fix docs --- docs/autotraining.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/autotraining.rst b/docs/autotraining.rst index 52afec63d..1434ef0bf 100644 --- a/docs/autotraining.rst +++ b/docs/autotraining.rst @@ -1,5 +1,5 @@ autotraining -=== +============ .. dfhack-tool:: :summary: Assigns citizens to a military squad until they have fulfilled their need for Martial Training From eaa1d8629b1b9f81f6e7bbdd899f4df67dd5f255 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 12 Mar 2025 17:10:32 -0500 Subject: [PATCH 18/44] Add credit where credit is due --- autotraining.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/autotraining.lua b/autotraining.lua index b1da10a2e..e04ecc928 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -1,3 +1,4 @@ +-- Based on the original code by RNGStrategist (who also got some help from Uncle Danny) --@ enable = true --@ module = true From 1892f62b4a8dfa59209c4fb07cc5489baca16348 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 12 Mar 2025 18:19:39 -0500 Subject: [PATCH 19/44] add to control panel alert the user if the squad cant be found (since we cant reliably make a squad ourselves... yet) --- autotraining.lua | 47 ++++++++++++++++++----------- internal/control-panel/registry.lua | 2 ++ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/autotraining.lua b/autotraining.lua index e04ecc928..3441217f9 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -2,8 +2,9 @@ --@ enable = true --@ module = true -local repeatUtil = require 'repeat-util' +local repeatUtil = require('repeat-util') local utils=require('utils') +local dlg = require('gui.dialogs') validArgs = utils.invert({ 't', @@ -30,6 +31,11 @@ function isEnabled() return state.enabled end +-- Save any configurations in the save data +local function persist_state() + dfhack.persistent.saveSiteData(GLOBAL_KEY, state) +end + dfhack.onStateChange[GLOBAL_KEY] = function(sc) -- the state changed, is a map loaded and is that map in fort mode? if sc ~= SC_MAP_LOADED or df.global.gamemode ~= df.game_mode.DWARF then @@ -37,18 +43,21 @@ dfhack.onStateChange[GLOBAL_KEY] = function(sc) return end -- yes it was, so: + -- retrieve state saved in game. merge with default state so config -- saved from previous versions can pick up newer defaults. state = get_default_state() utils.assign(state, dfhack.persistent.getSiteData(GLOBAL_KEY, state)) + if ( state.enabled ) then + start() + else + stop() + end + -- start can change the enabled state if the squad cant be found if state.enabled then dfhack.print(GLOBAL_KEY .." was persisted with the following data:\nThreshold: ".. state.threshold .. ' | Squad name: '..state.squadname ..'.\n') end -end - --- Save any configurations in the save data -local function persist_state() - dfhack.persistent.saveSiteData(GLOBAL_KEY, state) + persist_state() end @@ -111,11 +120,11 @@ function checkSquads() end if (count == 0) then - dfhack.print(GLOBAL_KEY .." | ") - dfhack.printerr('ERROR: You need a squad with the name ' .. state.squadname) - dfhack.print(GLOBAL_KEY .." | ") - dfhack.printerr('That has an active Squad Leader') - dfhack.color(-1) + local message = '' + message = message .. (GLOBAL_KEY .." | ") + message = message .. ('ERROR: You need a squad with the name ' .. state.squadname) + message = message .. (' that has an active Squad Leader.') + dlg.showMessage('Could not enable autotraining', message, COLOR_WHITE) return nil end @@ -178,7 +187,11 @@ function check() local squads = checkSquads() local intraining_count = 0 local inque_count = 0 - if ( squads == nil)then return end + if ( squads == nil)then + repeatUtil.cancel(GLOBAL_KEY) + state.enabled = false + dfhack.println(GLOBAL_KEY .. " | STOP") + return end local citizen = getAllCititzen() for n, unit in ipairs(citizen) do local need = findNeed(unit) @@ -214,21 +227,16 @@ end function stop() repeatUtil.cancel(GLOBAL_KEY) - local squads = checkSquads() - removeAll(squads) dfhack.println(GLOBAL_KEY .. " | STOP") end if dfhack_flags.enable then if dfhack_flags.enable_state then - start() state.enabled = true - persist_state() else - stop() state.enabled = false - persist_state() end + persist_state() end if dfhack_flags.module then @@ -236,7 +244,10 @@ if dfhack_flags.module then end if ( state.enabled ) then + start() dfhack.println(GLOBAL_KEY .." | Enabled") else + stop() dfhack.println(GLOBAL_KEY .." | Disabled") end +persist_state() diff --git a/internal/control-panel/registry.lua b/internal/control-panel/registry.lua index 37cd56c4e..6cd4229fc 100644 --- a/internal/control-panel/registry.lua +++ b/internal/control-panel/registry.lua @@ -34,6 +34,8 @@ COMMANDS_BY_IDX = { desc='Automatically shear creatures that are ready for shearing.', params={'--time', '14', '--timeUnits', 'days', '--command', '[', 'workorder', 'ShearCreature', ']'}}, {command='autoslab', group='automation', mode='enable'}, + {command='autotraining', group='automation', mode='enable', + desc='Automation script for citizens to hit the gym when they yearn for the gains.'}, {command='ban-cooking all', group='automation', mode='run'}, {command='buildingplan set boulders false', group='automation', mode='run', desc='Enable if you usually don\'t want to use boulders for construction.'}, From e69aba55b0d9b3605ddaf602dc06031c7f35cbd8 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Thu, 13 Mar 2025 17:24:36 -0500 Subject: [PATCH 20/44] Check the squad's entity_id to make sure we get *our* Gym --- autotraining.lua | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/autotraining.lua b/autotraining.lua index 3441217f9..673955fb0 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -109,12 +109,16 @@ end function checkSquads() local squads = {} local count = 0 - for n, mil in ipairs(df.global.world.squads.all) do - if (mil.alias == state.squadname) then - local leader = mil.positions[0].occupant - if ( leader ~= -1) then - table.insert(squads,mil) - count = count +1 + for _, mil in ipairs(df.global.world.squads.all) do + for _, link in ipairs(dfhack.world.getCurrentSite().entity_links) do + if mil.entity_id == link.entity_id then + if (mil.alias == state.squadname) then + local leader = mil.positions[0].occupant + if ( leader ~= -1) then + table.insert(squads,mil) + count = count +1 + end + end end end end From 0d0ef1e48e9c150d4ad33c19f6308cd2b761a2d6 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Thu, 13 Mar 2025 17:25:02 -0500 Subject: [PATCH 21/44] Update autotraining.lua remove the `.` because it could lead to confusion --- autotraining.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autotraining.lua b/autotraining.lua index 673955fb0..03d57da7d 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -55,7 +55,7 @@ dfhack.onStateChange[GLOBAL_KEY] = function(sc) end -- start can change the enabled state if the squad cant be found if state.enabled then - dfhack.print(GLOBAL_KEY .." was persisted with the following data:\nThreshold: ".. state.threshold .. ' | Squad name: '..state.squadname ..'.\n') + dfhack.print(GLOBAL_KEY .." was persisted with the following data:\nThreshold: ".. state.threshold .. ' | Squad name: '..state.squadname ..'\n') end persist_state() end From c7c73adf4463b1edcc69a92ec73c108e23a83d85 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Thu, 13 Mar 2025 20:20:59 -0500 Subject: [PATCH 22/44] Fix the ignore count never being reset --- autotraining.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autotraining.lua b/autotraining.lua index 03d57da7d..3b423d2c0 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -14,7 +14,6 @@ validArgs = utils.invert({ local args = utils.processArgs({...}, validArgs) local GLOBAL_KEY = "autotraining" local ignore_flag = df.unit_labor['DISSECT_FISH'] -local ignore_count = 0 local need_id = df.need_type['MartialTraining'] local function get_default_state() @@ -67,6 +66,7 @@ end function getAllCititzen() local ret = {} local citizen = dfhack.units.getCitizens(true) + local ignore_count = 0 for _, unit in ipairs(citizen) do if unit.profession ~= df.profession.BABY and unit.profession ~= df.profession.CHILD then if ( not unit.status.labors[ignore_flag] ) then From bceb905b902e2b6fe835b7f0db3d704fab76029c Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Thu, 13 Mar 2025 20:59:13 -0500 Subject: [PATCH 23/44] Fix units that need training but are already doing so being reported as queued --- autotraining.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autotraining.lua b/autotraining.lua index 3b423d2c0..5117bc6e6 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -143,7 +143,7 @@ function addTraining(squads,unit) end if (unit.military.squad_id ~= -1) then - return false + return (squad.alias == state.squadname) end if ( squad.positions[i].occupant == -1 ) then From 8803e82209b08f1adccf460b2a4083093d5b63b5 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Thu, 13 Mar 2025 21:16:19 -0500 Subject: [PATCH 24/44] fix the ignore count (it should be global) --- autotraining.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/autotraining.lua b/autotraining.lua index 5117bc6e6..898f2b4d6 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -15,6 +15,7 @@ local args = utils.processArgs({...}, validArgs) local GLOBAL_KEY = "autotraining" local ignore_flag = df.unit_labor['DISSECT_FISH'] local need_id = df.need_type['MartialTraining'] +local ignore_count = 0 local function get_default_state() return { @@ -66,7 +67,7 @@ end function getAllCititzen() local ret = {} local citizen = dfhack.units.getCitizens(true) - local ignore_count = 0 + ignore_count = 0 for _, unit in ipairs(citizen) do if unit.profession ~= df.profession.BABY and unit.profession ~= df.profession.CHILD then if ( not unit.status.labors[ignore_flag] ) then From 82d3acd0c1d3afde2a225ffb76f19c0e11c9979e Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 15 Mar 2025 15:04:41 -0500 Subject: [PATCH 25/44] Apply suggestions from code review --- autotraining.lua | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/autotraining.lua b/autotraining.lua index 898f2b4d6..11394f028 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -64,12 +64,12 @@ end --###### --Functions --###### -function getAllCititzen() +function getTrainingCandidates() local ret = {} local citizen = dfhack.units.getCitizens(true) ignore_count = 0 for _, unit in ipairs(citizen) do - if unit.profession ~= df.profession.BABY and unit.profession ~= df.profession.CHILD then + if dfhack.units.isAdult(unit) then if ( not unit.status.labors[ignore_flag] ) then table.insert(ret, unit) else @@ -95,8 +95,7 @@ end --###### function getByID(id) - local citizen = getAllCititzen() - for n, unit in ipairs(citizen) do + for n, unit in ipairs(getTrainingCandidates()) do if (unit.hist_figure_id == id) then return unit end @@ -111,14 +110,12 @@ function checkSquads() local squads = {} local count = 0 for _, mil in ipairs(df.global.world.squads.all) do - for _, link in ipairs(dfhack.world.getCurrentSite().entity_links) do - if mil.entity_id == link.entity_id then - if (mil.alias == state.squadname) then - local leader = mil.positions[0].occupant - if ( leader ~= -1) then - table.insert(squads,mil) - count = count +1 - end + if squad.entity_id == df.global.plotinfo.group_id then + if (mil.alias == state.squadname) then + local leader = mil.positions[0].occupant + if ( leader ~= -1) then + table.insert(squads,mil) + count = count +1 end end end @@ -197,8 +194,7 @@ function check() state.enabled = false dfhack.println(GLOBAL_KEY .. " | STOP") return end - local citizen = getAllCititzen() - for n, unit in ipairs(citizen) do + for n, unit in ipairs(getTrainingCandidates()) do local need = findNeed(unit) if ( need ~= nil ) then if ( need.focus_level < state.threshold ) then From 00e883ffefbe47c67b03a152d9aa9dd5c45aa4c8 Mon Sep 17 00:00:00 2001 From: Squid Coder <92821989+realSquidCoder@users.noreply.github.com> Date: Sat, 15 Mar 2025 21:50:11 -0500 Subject: [PATCH 26/44] fix typo --- autotraining.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autotraining.lua b/autotraining.lua index 11394f028..acab778f7 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -110,7 +110,7 @@ function checkSquads() local squads = {} local count = 0 for _, mil in ipairs(df.global.world.squads.all) do - if squad.entity_id == df.global.plotinfo.group_id then + if mil.entity_id == df.global.plotinfo.group_id then if (mil.alias == state.squadname) then local leader = mil.positions[0].occupant if ( leader ~= -1) then From 663412009894d74a302cf1dc2d59e3e1e132659e Mon Sep 17 00:00:00 2001 From: Squid Coder <92821989+realSquidCoder@users.noreply.github.com> Date: Sat, 15 Mar 2025 23:30:22 -0500 Subject: [PATCH 27/44] fix to actually check the unit's squad --- autotraining.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autotraining.lua b/autotraining.lua index acab778f7..114998cd9 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -141,7 +141,7 @@ function addTraining(squads,unit) end if (unit.military.squad_id ~= -1) then - return (squad.alias == state.squadname) + return (df.squad.find(unit.military.squad_id).alias == state.squadname) end if ( squad.positions[i].occupant == -1 ) then From ed76a08fc36bafca9497624fb4f9b4d80fff15a1 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sun, 16 Mar 2025 01:26:45 -0500 Subject: [PATCH 28/44] Update for gui usage --- autotraining.lua | 128 ++++++++++++++++++------------ internal/notify/notifications.lua | 18 +++++ 2 files changed, 97 insertions(+), 49 deletions(-) diff --git a/autotraining.lua b/autotraining.lua index 114998cd9..7ec0fda01 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -7,8 +7,7 @@ local utils=require('utils') local dlg = require('gui.dialogs') validArgs = utils.invert({ - 't', - 'n' + 't' }) local args = utils.processArgs({...}, validArgs) @@ -21,7 +20,8 @@ local function get_default_state() return { enabled=false, threshold=-5000, - squadname='Gym' + ignored={}, + training_squads = {}, } end @@ -31,9 +31,46 @@ function isEnabled() return state.enabled end --- Save any configurations in the save data +-- persisting a table with numeric keys results in a json array with a huge number of null entries +-- therefore, we convert the keys to strings for persistence +-- also, we clear the frame counter values since the frame counter gets reset on load +local function to_persist(persistable) + local persistable_ignored = {} + for thing in pairs(persistable) do + persistable_ignored[tostring(thing)] = -1 + end + return persistable_ignored +end + +-- loads both from the older array format and the new string table format +local function from_persist(persistable) + if not persistable then + return + end + local ret = {} + for thing in pairs(persistable) do + ret[tonumber(thing)] = -1 + end + return ret +end + local function persist_state() - dfhack.persistent.saveSiteData(GLOBAL_KEY, state) + dfhack.persistent.saveSiteData(GLOBAL_KEY, { + enabled=enabled, + threshold=threshold, + ignored=to_persist(state.ignored), + training_squads=to_persist(state.training_squads) + }) +end + +--- Load the saved state of the script +local function load_state() + -- load persistent data + local persisted_data = dfhack.persistent.getSiteData(GLOBAL_KEY, {}) + state.enabled = persisted_data.enabled or false + state.threshold = persisted_data.threshold or -5000 + state.allowed = from_persist(persisted_data.ignored) or {} + state.training_squads = from_persist(persisted_data.training_squads) or {} end dfhack.onStateChange[GLOBAL_KEY] = function(sc) @@ -47,7 +84,7 @@ dfhack.onStateChange[GLOBAL_KEY] = function(sc) -- retrieve state saved in game. merge with default state so config -- saved from previous versions can pick up newer defaults. state = get_default_state() - utils.assign(state, dfhack.persistent.getSiteData(GLOBAL_KEY, state)) + utils.assign(state, load_state()) if ( state.enabled ) then start() else @@ -55,7 +92,7 @@ dfhack.onStateChange[GLOBAL_KEY] = function(sc) end -- start can change the enabled state if the squad cant be found if state.enabled then - dfhack.print(GLOBAL_KEY .." was persisted with the following data:\nThreshold: ".. state.threshold .. ' | Squad name: '..state.squadname ..'\n') + dfhack.print(GLOBAL_KEY .." was persisted with the following data:\nThreshold: ".. state.threshold .. '\n') end persist_state() end @@ -70,7 +107,7 @@ function getTrainingCandidates() ignore_count = 0 for _, unit in ipairs(citizen) do if dfhack.units.isAdult(unit) then - if ( not unit.status.labors[ignore_flag] ) then + if ( not state.ignored[unit.id] ) then table.insert(ret, unit) else ignore_count = ignore_count +1 @@ -90,12 +127,20 @@ function findNeed(unit) return nil end +function ignoreUnit(unit) + state.ignored[unit.id] = true +end + +function unignoreUnit(unit) + state.ignored[unit.id] = false +end + --###### --Main --###### function getByID(id) - for n, unit in ipairs(getTrainingCandidates()) do + for _, unit in ipairs(getTrainingCandidates()) do if (unit.hist_figure_id == id) then return unit end @@ -108,42 +153,35 @@ end -- Abort if no squads found function checkSquads() local squads = {} - local count = 0 - for _, mil in ipairs(df.global.world.squads.all) do + for _, squad_id in ipairs(state.training_squads) do + local mil = df.squad:find(squad_id) if mil.entity_id == df.global.plotinfo.group_id then - if (mil.alias == state.squadname) then - local leader = mil.positions[0].occupant - if ( leader ~= -1) then - table.insert(squads,mil) - count = count +1 - end + local leader = mil.positions[0].occupant + if ( leader ~= -1) then + table.insert(squads,mil) end end end - if (count == 0) then - local message = '' - message = message .. (GLOBAL_KEY .." | ") - message = message .. ('ERROR: You need a squad with the name ' .. state.squadname) - message = message .. (' that has an active Squad Leader.') - dlg.showMessage('Could not enable autotraining', message, COLOR_WHITE) + if (#squads == 0) then return nil end return squads end -function addTraining(squads,unit) - for n, squad in ipairs(squads) do - for i=1,9,1 do - if (unit.hist_figure_id == squad.positions[i].occupant) then - return true - end - - if (unit.military.squad_id ~= -1) then - return (df.squad.find(unit.military.squad_id).alias == state.squadname) +function addTraining(unit) + if (unit.military.squad_id ~= -1) then + local inTraining = false + for _, squad in ipairs(state.training_squads) do + if unit.military.squad_id == squad then + inTraining = true end - + end + return inTraining + end + for _, squad in ipairs(state.training_squads) do + for i=1,9,1 do if ( squad.positions[i].occupant == -1 ) then squad.positions[i].occupant = unit.hist_figure_id unit.military.squad_id = squad.id @@ -156,8 +194,8 @@ function addTraining(squads,unit) return false end -function removeTraining(squads,unit) - for n, squad in ipairs(squads) do +function removeTraining(unit) + for n, squad in ipairs(state.training_squads) do for i=1,9,1 do if ( unit.hist_figure_id == squad.positions[i].occupant ) then unit.military.squad_id = -1 @@ -170,9 +208,9 @@ function removeTraining(squads,unit) return false end -function removeAll(squads) - if ( squads == nil) then return end - for n, squad in ipairs(squads) do +function removeAll() + if ( state.training_squads == nil) then return end + for n, squad in ipairs(state.training_squads) do for i=1,9,1 do local dwarf = getByID(squad.positions[i].occupant) if (dwarf ~= nil) then @@ -189,23 +227,19 @@ function check() local squads = checkSquads() local intraining_count = 0 local inque_count = 0 - if ( squads == nil)then - repeatUtil.cancel(GLOBAL_KEY) - state.enabled = false - dfhack.println(GLOBAL_KEY .. " | STOP") - return end + if ( squads == nil) then return end for n, unit in ipairs(getTrainingCandidates()) do local need = findNeed(unit) if ( need ~= nil ) then if ( need.focus_level < state.threshold ) then - local bol = addTraining(squads,unit) + local bol = addTraining(unit) if ( bol ) then intraining_count = intraining_count +1 else inque_count = inque_count +1 end else - removeTraining(squads,unit) + removeTraining(unit) end end end @@ -219,10 +253,6 @@ function start() if (args.t) then state.threshold = 0-tonumber(args.t) end - if (args.n) then - state.squadname = args.n - end - repeatUtil.scheduleEvery(GLOBAL_KEY, 997, 'ticks', check) -- 997 is the closest prime to 1000 end diff --git a/internal/notify/notifications.lua b/internal/notify/notifications.lua index 8af7c2c18..c0c91c5ca 100644 --- a/internal/notify/notifications.lua +++ b/internal/notify/notifications.lua @@ -366,6 +366,24 @@ NOTIFICATIONS_BY_IDX = { dlg.showMessage('Rescue stuck squads', message, COLOR_WHITE) end, }, + { + name='auto_train', + desc='Notifies when there are no squads set up for training', + default=true, + dwarf_fn=function() + local at = reqscript('autotraining') + if (at.checkSquads() == nil) then + return {{text="autotraining: no squads selected",pen=COLOR_LIGHTRED}} + end + end, + on_click=function() + local message = + "You have no squads selected for training.\n".. + "You should have a squad set up to be constantly training for around 8 months.\n".. + "Then you can select that squad for training in the config.\n\nWould you like to open the config? Alternatively, simply close this popup to go create a squad." + dlg.showYesNoPrompt('Training Squads not configured', message, COLOR_WHITE, function () dfhack.run_command('gui/autotraining') end) + end, + }, { name='traders_ready', desc='Notifies when traders are ready to trade at the depot.', From 92076ba632f3b846d2e9e3cdf725a8cc9f36780e Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sun, 16 Mar 2025 01:38:32 -0500 Subject: [PATCH 29/44] clean up --- autotraining.lua | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/autotraining.lua b/autotraining.lua index 7ec0fda01..4141f4ee9 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -12,7 +12,6 @@ validArgs = utils.invert({ local args = utils.processArgs({...}, validArgs) local GLOBAL_KEY = "autotraining" -local ignore_flag = df.unit_labor['DISSECT_FISH'] local need_id = df.need_type['MartialTraining'] local ignore_count = 0 @@ -56,8 +55,8 @@ end local function persist_state() dfhack.persistent.saveSiteData(GLOBAL_KEY, { - enabled=enabled, - threshold=threshold, + enabled=state.enabled, + threshold=state.threshold, ignored=to_persist(state.ignored), training_squads=to_persist(state.training_squads) }) @@ -67,10 +66,11 @@ end local function load_state() -- load persistent data local persisted_data = dfhack.persistent.getSiteData(GLOBAL_KEY, {}) - state.enabled = persisted_data.enabled or false - state.threshold = persisted_data.threshold or -5000 - state.allowed = from_persist(persisted_data.ignored) or {} - state.training_squads = from_persist(persisted_data.training_squads) or {} + state.enabled = persisted_data.enabled or state.enabled + state.threshold = persisted_data.threshold or state.threshold + state.allowed = from_persist(persisted_data.ignored) or state.allowed + state.training_squads = from_persist(persisted_data.training_squads) or state.training_squads + return state end dfhack.onStateChange[GLOBAL_KEY] = function(sc) @@ -83,8 +83,7 @@ dfhack.onStateChange[GLOBAL_KEY] = function(sc) -- retrieve state saved in game. merge with default state so config -- saved from previous versions can pick up newer defaults. - state = get_default_state() - utils.assign(state, load_state()) + load_state() if ( state.enabled ) then start() else From fc832a3a568b6b395e18e96d51148d5229de50fc Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sun, 16 Mar 2025 09:25:22 -0500 Subject: [PATCH 30/44] initial gui and update from code review --- autotraining.lua | 52 ++++----- gui/autotraining.lua | 173 ++++++++++++++++++++++++++++++ internal/notify/notifications.lua | 2 +- 3 files changed, 202 insertions(+), 25 deletions(-) create mode 100644 gui/autotraining.lua diff --git a/autotraining.lua b/autotraining.lua index 4141f4ee9..018def32c 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -32,11 +32,10 @@ end -- persisting a table with numeric keys results in a json array with a huge number of null entries -- therefore, we convert the keys to strings for persistence --- also, we clear the frame counter values since the frame counter gets reset on load local function to_persist(persistable) local persistable_ignored = {} - for thing in pairs(persistable) do - persistable_ignored[tostring(thing)] = -1 + for k, v in pairs(persistable) do + persistable_ignored[tostring(k)] = v end return persistable_ignored end @@ -47,13 +46,13 @@ local function from_persist(persistable) return end local ret = {} - for thing in pairs(persistable) do - ret[tonumber(thing)] = -1 + for k, v in pairs(persistable) do + ret[tonumber(k)] = v end return ret end -local function persist_state() +function persist_state() dfhack.persistent.saveSiteData(GLOBAL_KEY, { enabled=state.enabled, threshold=state.threshold, @@ -68,7 +67,7 @@ local function load_state() local persisted_data = dfhack.persistent.getSiteData(GLOBAL_KEY, {}) state.enabled = persisted_data.enabled or state.enabled state.threshold = persisted_data.threshold or state.threshold - state.allowed = from_persist(persisted_data.ignored) or state.allowed + state.ignored = from_persist(persisted_data.ignored) or state.ignored state.training_squads = from_persist(persisted_data.training_squads) or state.training_squads return state end @@ -116,6 +115,20 @@ function getTrainingCandidates() return ret end +function getTrainingSquads() + local squads = {} + for squad_id, _ in pairs(state.training_squads) do + local squad = df.squad.find(squad_id) + if squad then + table.insert(squads, squad) + else + -- setting to nil during iteration is permitted by lua + state.training_squads[squad_id] = nil + end + end + return squads +end + function findNeed(unit) local needs = unit.status.current_soul.personality.needs for _, need in ipairs(needs) do @@ -126,14 +139,6 @@ function findNeed(unit) return nil end -function ignoreUnit(unit) - state.ignored[unit.id] = true -end - -function unignoreUnit(unit) - state.ignored[unit.id] = false -end - --###### --Main --###### @@ -152,12 +157,11 @@ end -- Abort if no squads found function checkSquads() local squads = {} - for _, squad_id in ipairs(state.training_squads) do - local mil = df.squad:find(squad_id) - if mil.entity_id == df.global.plotinfo.group_id then - local leader = mil.positions[0].occupant + for _, squad in ipairs(getTrainingSquads()) do + if squad.entity_id == df.global.plotinfo.group_id then + local leader = squad.positions[0].occupant if ( leader ~= -1) then - table.insert(squads,mil) + table.insert(squads,squad) end end end @@ -172,14 +176,14 @@ end function addTraining(unit) if (unit.military.squad_id ~= -1) then local inTraining = false - for _, squad in ipairs(state.training_squads) do + for _, squad in ipairs(getTrainingSquads()) do if unit.military.squad_id == squad then inTraining = true end end return inTraining end - for _, squad in ipairs(state.training_squads) do + for _, squad in ipairs(getTrainingSquads()) do for i=1,9,1 do if ( squad.positions[i].occupant == -1 ) then squad.positions[i].occupant = unit.hist_figure_id @@ -194,7 +198,7 @@ function addTraining(unit) end function removeTraining(unit) - for n, squad in ipairs(state.training_squads) do + for _, squad in ipairs(getTrainingSquads()) do for i=1,9,1 do if ( unit.hist_figure_id == squad.positions[i].occupant ) then unit.military.squad_id = -1 @@ -209,7 +213,7 @@ end function removeAll() if ( state.training_squads == nil) then return end - for n, squad in ipairs(state.training_squads) do + for _, squad in ipairs(getTrainingSquads()) do for i=1,9,1 do local dwarf = getByID(squad.positions[i].occupant) if (dwarf ~= nil) then diff --git a/gui/autotraining.lua b/gui/autotraining.lua new file mode 100644 index 000000000..fa5515df7 --- /dev/null +++ b/gui/autotraining.lua @@ -0,0 +1,173 @@ +---@diagnostic disable: missing-fields + +local gui = require('gui') +local widgets = require('gui.widgets') + +local autotraining = reqscript('autotraining') + +local training_squads = autotraining.state.training_squads +local ignored_units = autotraining.state.ignored + +AutoTrain = defclass(AutoTrain, widgets.Window) +AutoTrain.ATTRS { + frame_title='Training Setup', + frame={w=55, h=45}, + resizable=true, -- if resizing makes sense for your dialog + resize_min={w=55, h=20}, -- try to allow users to shrink your windows +} + +local SELECTED_ICON = dfhack.pen.parse{ch=string.char(251), fg=COLOR_LIGHTGREEN} +function AutoTrain:getSquadIcon(squad_id) + if training_squads[squad_id] then + return SELECTED_ICON + end + return nil +end + +function AutoTrain:getSquads() + local squads = {} + for _, squad in ipairs(df.global.world.squads.all) do + if not (squad.entity_id == df.global.plotinfo.group_id) then + goto continue + end + table.insert(squads, { + text = dfhack.translation.translateName(squad.name, true), + icon = self:callback("getSquadIcon", squad.id ), + id = squad.id + }) + + ::continue:: + end + return squads +end + +function AutoTrain:toggleSquad(_, choice) + training_squads[choice.id] = not training_squads[choice.id] + autotraining.persist_state() + self:updateLayout() +end + +local IGNORED_ICON = dfhack.pen.parse{ch='x', fg=COLOR_RED} +function AutoTrain:getUnitIcon(unit_id) + if ignored_units[unit_id] then + return IGNORED_ICON + end + return nil +end + +function AutoTrain:getUnits() + local unit_choices = {} + for _, unit in ipairs(dfhack.units.getCitizens(true,false)) do + if not dfhack.units.isAdult(unit) then + goto continue + end + + table.insert(unit_choices, { + text = dfhack.units.getReadableName(unit), + icon = self:callback("getUnitIcon", unit.id ), + id = unit.id + }) + ::continue:: + end + return unit_choices +end + +function AutoTrain:toggleUnit(_, choice) + ignored_units[choice.id] = not ignored_units[choice.id] + autotraining.persist_state() + self:updateLayout() +end + +function AutoTrain:init() + + -- TODO: provide actual values, and write to configuration + -- (once the base tool actually supports this) + local position_options = { + { label = "none", val = nil, pen = COLOR_LIGHTCYAN }, + { label = "manager", val = nil, pen = COLOR_LIGHTCYAN }, + { label = "manager and chief medical dwarf", val = nil, pen = COLOR_LIGHTCYAN }, + } + + self:addviews{ + widgets.Label{ + frame={ t = 0 , h = 1 }, + text = "Select squads for automatic training", + }, + widgets.List{ + view_id = "squad_list", + icon_width = 2, + frame = { t = 2, h = 10 }, + choices = self:getSquads(), + on_submit=self:callback("toggleSquad") + }, + widgets.Divider{ frame={t=12, h=1}, frame_style_l = false, frame_style_r = false}, + widgets.Label{ + frame={ t = 13 , h = 1 }, + text = "General options", + }, + widgets.EditField { + view_id = "threshold", + frame={ t = 15 , h = 1 }, + key = "CUSTOM_T", + label_text = "Need threshold for training: ", + text = tostring(-autotraining.state.threshold), + on_char = function (char, _) + return tonumber(char,10) + end, + on_submit = function (text) + -- still necessary, because on_char does not check pasted text + local entered_number = tonumber(text,10) or 5000 + autotraining.state.threshold = -entered_number + autotraining.persist_state() + -- make sure that the auto correction is reflected in the EditField + self.subviews.threshold:setText(tostring(entered_number)) + end + }, + widgets.CycleHotkeyLabel { + view_id = "ignored_positions", + frame={ t = 16 , h = 2 }, + key = "CUSTOM_P", + label = "Positions to keep from training: ", + label_below = true, + options = position_options, + initial_option = 3 + }, + widgets.Divider{ frame={t=19, h=1}, frame_style_l = false, frame_style_r = false}, + widgets.Label{ + frame={ t = 20 , h = 1 }, + text = "Select units to exclude from automatic training" + }, + widgets.FilteredList{ + frame = { t = 22 }, + view_id = "unit_list", + edit_key = "CUSTOM_CTRL_F", + icon_width = 2, + choices = self:getUnits(), + on_submit=self:callback("toggleUnit") + } + } + --self.subviews.unit_list:setChoices(unit_choices) +end + +function AutoTrain:onDismiss() + view = nil +end + +AutoTrainScreen = defclass(AutoTrainScreen, gui.ZScreen) +AutoTrainScreen.ATTRS { + focus_path='autotrain', +} + +function AutoTrainScreen:init() + self:addviews{AutoTrain{}} +end + +function AutoTrainScreen:onDismiss() + view = nil +end + +if not dfhack.world.isFortressMode() or not dfhack.isMapLoaded() then + qerror('gui/autotrain requires a fortress map to be loaded') +end + +view = view and view:raise() or AutoTrainScreen{}:show() diff --git a/internal/notify/notifications.lua b/internal/notify/notifications.lua index c0c91c5ca..303f2f76e 100644 --- a/internal/notify/notifications.lua +++ b/internal/notify/notifications.lua @@ -372,7 +372,7 @@ NOTIFICATIONS_BY_IDX = { default=true, dwarf_fn=function() local at = reqscript('autotraining') - if (at.checkSquads() == nil) then + if (at.isEnabled() and at.checkSquads() == nil) then return {{text="autotraining: no squads selected",pen=COLOR_LIGHTRED}} end end, From 55ddbfe999dc40f917b02f6940855f49acbadac7 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sun, 16 Mar 2025 09:27:10 -0500 Subject: [PATCH 31/44] show alias in gui too --- gui/autotraining.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/autotraining.lua b/gui/autotraining.lua index fa5515df7..f64e667e0 100644 --- a/gui/autotraining.lua +++ b/gui/autotraining.lua @@ -31,7 +31,7 @@ function AutoTrain:getSquads() goto continue end table.insert(squads, { - text = dfhack.translation.translateName(squad.name, true), + text = dfhack.translation.translateName(squad.name, true)..' ('..squad.alias..')', icon = self:callback("getSquadIcon", squad.id ), id = squad.id }) From f1edec292c0d8db2921189c4c3ff3eaf3693c893 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sun, 16 Mar 2025 09:40:24 -0500 Subject: [PATCH 32/44] clean up --- autotraining.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/autotraining.lua b/autotraining.lua index 018def32c..d2faf4c11 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -4,7 +4,6 @@ local repeatUtil = require('repeat-util') local utils=require('utils') -local dlg = require('gui.dialogs') validArgs = utils.invert({ 't' From 3906eb0f730dbb96d4cf5d535f0aa8cb2376612f Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 19 Mar 2025 09:44:46 -0500 Subject: [PATCH 33/44] Create gui docs --- docs/gui/autotraining.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 docs/gui/autotraining.rst diff --git a/docs/gui/autotraining.rst b/docs/gui/autotraining.rst new file mode 100644 index 000000000..a86b28adf --- /dev/null +++ b/docs/gui/autotraining.rst @@ -0,0 +1,15 @@ +gui/autotraining +================ + +.. dfhack-tool:: + :summary: GUI interface for ``autotraining`` + :tags: fort auto interface + +This is an in-game configuration interface for `autotraining`. You can pick squads for training, select ignored units, and set the needs threshold. + +Usage +----- + +:: + + gui/autotraining From 59d53f5fe81d35bca3ec4231212cd7aa7d4a01eb Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 19 Mar 2025 15:44:41 -0500 Subject: [PATCH 34/44] update the docs --- docs/autotraining.rst | 17 +++++------------ internal/notify/notifications.lua | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/docs/autotraining.rst b/docs/autotraining.rst index 1434ef0bf..d1d67f90e 100644 --- a/docs/autotraining.rst +++ b/docs/autotraining.rst @@ -7,18 +7,11 @@ autotraining Automation script for citizens to hit the gym when they yearn for the gains. Also passively builds military skills and physical stats. -Critical setup: - -- Minimum 1 squad with the correct name (default is ``Gym``) -- An assigned squad leader in the squad -- An assigned Barracks for the squad -- Active Training orders for the squad - -This should be a new non-military-use squad. The uniform should be set to "No Uniform" and the squad should be set to "Constant Training" in the military screen. -Set the squad's schedule to full time training with at least 8 or 9 training. +You need to have at least one squad that is set up for training. This should be a new non-military-use squad. The uniform should be +set to "No Uniform" and the squad should be set to "Constant Training" in the military screen. Edit the squad's schedule to full time training with around 8 units training. The squad doesn't need months off. The members leave the squad once they have gotten their gains. -NOTE: Dwarfs with the labor "Fish Dissection" enabled are ignored. Make a Dwarven labour with only the Fish Dissection enabled, set to "Only selected do this" and assign it to a dwarf to ignore them. +Once you have made squads for training use `gui/autotraining` to select the squads and ignored units, as well as the needs threshhold. Usage ----- @@ -33,12 +26,12 @@ Examples ``enable autotraining`` Checks to see if you have fullfilled the creation of a training gym. - If there is no squad with the correct name (default: ``Gym``) with a squad leader assigned it will not proceed. + If there is no squad marked for training use, a clickable notification will appear letting you know to set one up/ Searches your fort for dwarves with a need to go to the gym, and begins assigning them to said gym. Once they have fulfilled their need they will be removed from the gym squad to be replaced by the next dwarf in the list. ``disable autotraining`` - Dwarves currently in the Gym squad, with the exception of the squad leader, will be unassigned and no new dwarves will be added to the squad. + Stops adding new units to the squad. Options ------- diff --git a/internal/notify/notifications.lua b/internal/notify/notifications.lua index 303f2f76e..653d3887d 100644 --- a/internal/notify/notifications.lua +++ b/internal/notify/notifications.lua @@ -379,7 +379,7 @@ NOTIFICATIONS_BY_IDX = { on_click=function() local message = "You have no squads selected for training.\n".. - "You should have a squad set up to be constantly training for around 8 months.\n".. + "You should have a squad set up to be constantly training with about 8 units needed for training.\n".. "Then you can select that squad for training in the config.\n\nWould you like to open the config? Alternatively, simply close this popup to go create a squad." dlg.showYesNoPrompt('Training Squads not configured', message, COLOR_WHITE, function () dfhack.run_command('gui/autotraining') end) end, From 612936be53e5554398e1710bc08cff69bc68c351 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 19 Mar 2025 16:00:19 -0500 Subject: [PATCH 35/44] remove non-existant name args in docs --- docs/autotraining.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/autotraining.rst b/docs/autotraining.rst index d1d67f90e..c64790551 100644 --- a/docs/autotraining.rst +++ b/docs/autotraining.rst @@ -39,7 +39,3 @@ Options Use integer values. (Default 5000) The negative need threshhold to trigger for each citizen The greater the number the longer before a dwarf is added to the waiting list. - - ``-n`` - Use a string. (Default ``Gym``) - Pick a different name for the squad the script looks for. From f59f6de78701ce07e609fd7a2f90f893532c7205 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 19 Mar 2025 18:11:57 -0500 Subject: [PATCH 36/44] fix typo in message --- gui/autotraining.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/autotraining.lua b/gui/autotraining.lua index f64e667e0..3388e980b 100644 --- a/gui/autotraining.lua +++ b/gui/autotraining.lua @@ -167,7 +167,7 @@ function AutoTrainScreen:onDismiss() end if not dfhack.world.isFortressMode() or not dfhack.isMapLoaded() then - qerror('gui/autotrain requires a fortress map to be loaded') + qerror('gui/autotraining requires a fortress map to be loaded') end view = view and view:raise() or AutoTrainScreen{}:show() From 1c427c69d64f6fc40de1e08ed3989007d326b6c3 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 19 Mar 2025 18:26:35 -0500 Subject: [PATCH 37/44] fix trainees being labeled as queued --- autotraining.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/autotraining.lua b/autotraining.lua index d2faf4c11..11d5c2d58 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -174,13 +174,12 @@ end function addTraining(unit) if (unit.military.squad_id ~= -1) then - local inTraining = false for _, squad in ipairs(getTrainingSquads()) do - if unit.military.squad_id == squad then - inTraining = true + if unit.military.squad_id == squad.id then + return true end end - return inTraining + return false end for _, squad in ipairs(getTrainingSquads()) do for i=1,9,1 do From 648ae90333ad04c8d15d39688a307d26d6593f14 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Fri, 21 Mar 2025 14:38:10 -0500 Subject: [PATCH 38/44] add ignore nobles --- autotraining.lua | 25 +++++++-- gui/autotraining.lua | 119 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 118 insertions(+), 26 deletions(-) diff --git a/autotraining.lua b/autotraining.lua index 11d5c2d58..d6ec90968 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -19,6 +19,7 @@ local function get_default_state() enabled=false, threshold=-5000, ignored={}, + ignored_nobles={}, training_squads = {}, } end @@ -56,6 +57,7 @@ function persist_state() enabled=state.enabled, threshold=state.threshold, ignored=to_persist(state.ignored), + ignored_nobles=state.ignored_nobles, training_squads=to_persist(state.training_squads) }) end @@ -67,6 +69,7 @@ local function load_state() state.enabled = persisted_data.enabled or state.enabled state.threshold = persisted_data.threshold or state.threshold state.ignored = from_persist(persisted_data.ignored) or state.ignored + state.ignored_nobles = persisted_data.ignored_nobles or state.ignored_nobles state.training_squads = from_persist(persisted_data.training_squads) or state.training_squads return state end @@ -104,9 +107,25 @@ function getTrainingCandidates() ignore_count = 0 for _, unit in ipairs(citizen) do if dfhack.units.isAdult(unit) then + local noblePos = dfhack.units.getNoblePositions(unit) + local isIgnNoble = false if ( not state.ignored[unit.id] ) then - table.insert(ret, unit) + if noblePos ~=nil then + for _, position in ipairs(noblePos) do + if state.ignored_nobles[position.position.code] then + isIgnNoble = true + break + end + end + end + if not isIgnNoble then + table.insert(ret, unit) + else + removeTraining(unit) + ignore_count = ignore_count +1 + end else + removeTraining(unit) ignore_count = ignore_count +1 end end @@ -229,7 +248,7 @@ function check() local intraining_count = 0 local inque_count = 0 if ( squads == nil) then return end - for n, unit in ipairs(getTrainingCandidates()) do + for _, unit in ipairs(getTrainingCandidates()) do local need = findNeed(unit) if ( need ~= nil ) then if ( need.focus_level < state.threshold ) then @@ -254,7 +273,7 @@ function start() if (args.t) then state.threshold = 0-tonumber(args.t) end - repeatUtil.scheduleEvery(GLOBAL_KEY, 997, 'ticks', check) -- 997 is the closest prime to 1000 + repeatUtil.scheduleEvery(GLOBAL_KEY, 1, 'days', check) -- 997 is the closest prime to 1000 end function stop() diff --git a/gui/autotraining.lua b/gui/autotraining.lua index 3388e980b..bd43d8c17 100644 --- a/gui/autotraining.lua +++ b/gui/autotraining.lua @@ -7,6 +7,7 @@ local autotraining = reqscript('autotraining') local training_squads = autotraining.state.training_squads local ignored_units = autotraining.state.ignored +local ignored_nobles = autotraining.state.ignored_nobles AutoTrain = defclass(AutoTrain, widgets.Window) AutoTrain.ATTRS { @@ -55,6 +56,13 @@ function AutoTrain:getUnitIcon(unit_id) return nil end +function AutoTrain:getNobleIcon(noble_code) + if ignored_nobles[noble_code] then + return IGNORED_ICON + end + return nil +end + function AutoTrain:getUnits() local unit_choices = {} for _, unit in ipairs(dfhack.units.getCitizens(true,false)) do @@ -78,16 +86,78 @@ function AutoTrain:toggleUnit(_, choice) self:updateLayout() end -function AutoTrain:init() +local function to_title_case(str) + return dfhack.capitalizeStringWords(dfhack.lowerCp437(str:gsub('_', ' '))) +end - -- TODO: provide actual values, and write to configuration - -- (once the base tool actually supports this) - local position_options = { - { label = "none", val = nil, pen = COLOR_LIGHTCYAN }, - { label = "manager", val = nil, pen = COLOR_LIGHTCYAN }, - { label = "manager and chief medical dwarf", val = nil, pen = COLOR_LIGHTCYAN }, - } +function toSet(list) + local set = {} + for _, v in ipairs(list) do + set[v] = true + end + return set +end + +local function add_positions(positions, entity) + if not entity then return end + for _,position in pairs(entity.positions.own) do + print(position.code..' '..position.id) + positions[position.id] = { + id=position.id+1, + code=position.code, + } + end +end + +function AutoTrain:getPositions() + local positions = {} + local excludedPositions = toSet({ + 'MILITIA_CAPTAIN', + 'MILITIA_COMMANDER', + 'OUTPOST_LIAISON', + 'CAPTAIN_OF_THE_GUARD', + }) + + add_positions(positions, df.historical_entity.find(df.global.plotinfo.civ_id)) + add_positions(positions, df.historical_entity.find(df.global.plotinfo.group_id)) + + -- Step 1: Extract values into a sortable array + local sortedPositions = {} + for _, val in pairs(positions) do + if val and not excludedPositions[val.code] then + table.insert(sortedPositions, val) + end + end + -- Step 2: Sort the positions (optional, adjust sorting criteria) + table.sort(sortedPositions, function(a, b) + return a.id < b.id -- Sort alphabetically by code + end) + + -- Step 3: Rebuild the table without gaps + positions = {} -- Reset positions table + for i, val in ipairs(sortedPositions) do + positions[i] = { + text = to_title_case(val.code), + value = val.code, + pen = COLOR_LIGHTCYAN, + icon = self:callback("getNobleIcon", val.code), + id = val.id + } + end + + return positions +end + + + +function AutoTrain:toggleNoble(_, choice) + ignored_nobles[choice.value] = not ignored_nobles[choice.value] + autotraining.persist_state() + self:updateLayout() +end + +function AutoTrain:init() self:addviews{ widgets.Label{ frame={ t = 0 , h = 1 }, @@ -96,18 +166,18 @@ function AutoTrain:init() widgets.List{ view_id = "squad_list", icon_width = 2, - frame = { t = 2, h = 10 }, + frame = { t = 2, h = 5 }, choices = self:getSquads(), on_submit=self:callback("toggleSquad") }, - widgets.Divider{ frame={t=12, h=1}, frame_style_l = false, frame_style_r = false}, + widgets.Divider{ frame={t=6, h=1}, frame_style_l = false, frame_style_r = false}, widgets.Label{ - frame={ t = 13 , h = 1 }, + frame={ t = 7 , h = 1 }, text = "General options", }, widgets.EditField { view_id = "threshold", - frame={ t = 15 , h = 1 }, + frame={ t = 8 , h = 1 }, key = "CUSTOM_T", label_text = "Need threshold for training: ", text = tostring(-autotraining.state.threshold), @@ -123,22 +193,25 @@ function AutoTrain:init() self.subviews.threshold:setText(tostring(entered_number)) end }, - widgets.CycleHotkeyLabel { - view_id = "ignored_positions", - frame={ t = 16 , h = 2 }, - key = "CUSTOM_P", - label = "Positions to keep from training: ", - label_below = true, - options = position_options, - initial_option = 3 + widgets.Divider{ frame={t=9, h=1}, frame_style_l = false, frame_style_r = false}, + widgets.Label{ + frame={ t = 10 , h = 1 }, + text = "Ignored noble positions", + }, + widgets.List{ + frame = { t = 11 , h = 11}, + view_id = "nobles_list", + icon_width = 2, + choices = self:getPositions(), + on_submit=self:callback("toggleNoble") }, - widgets.Divider{ frame={t=19, h=1}, frame_style_l = false, frame_style_r = false}, + widgets.Divider{ frame={t=22, h=1}, frame_style_l = false, frame_style_r = false}, widgets.Label{ - frame={ t = 20 , h = 1 }, + frame={ t = 23 , h = 1 }, text = "Select units to exclude from automatic training" }, widgets.FilteredList{ - frame = { t = 22 }, + frame = { t = 24 }, view_id = "unit_list", edit_key = "CUSTOM_CTRL_F", icon_width = 2, From 36125933e9b79de2a7d69b80a08606828d5c1566 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Fri, 21 Mar 2025 14:57:53 -0500 Subject: [PATCH 39/44] Remove more debug code --- gui/autotraining.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/gui/autotraining.lua b/gui/autotraining.lua index bd43d8c17..396edb739 100644 --- a/gui/autotraining.lua +++ b/gui/autotraining.lua @@ -101,7 +101,6 @@ end local function add_positions(positions, entity) if not entity then return end for _,position in pairs(entity.positions.own) do - print(position.code..' '..position.id) positions[position.id] = { id=position.id+1, code=position.code, From a9bf6e6dc34ab7e0c3637353fe9228dbadae1e06 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Fri, 21 Mar 2025 15:45:32 -0500 Subject: [PATCH 40/44] Gui cleanup --- gui/autotraining.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gui/autotraining.lua b/gui/autotraining.lua index 396edb739..03ac18326 100644 --- a/gui/autotraining.lua +++ b/gui/autotraining.lua @@ -160,19 +160,19 @@ function AutoTrain:init() self:addviews{ widgets.Label{ frame={ t = 0 , h = 1 }, - text = "Select squads for automatic training", + text = "Select squads for automatic training:", }, widgets.List{ view_id = "squad_list", icon_width = 2, - frame = { t = 2, h = 5 }, + frame = { t = 1, h = 5 }, choices = self:getSquads(), on_submit=self:callback("toggleSquad") }, widgets.Divider{ frame={t=6, h=1}, frame_style_l = false, frame_style_r = false}, widgets.Label{ frame={ t = 7 , h = 1 }, - text = "General options", + text = "General options:", }, widgets.EditField { view_id = "threshold", @@ -195,7 +195,7 @@ function AutoTrain:init() widgets.Divider{ frame={t=9, h=1}, frame_style_l = false, frame_style_r = false}, widgets.Label{ frame={ t = 10 , h = 1 }, - text = "Ignored noble positions", + text = "Ignored noble positions:", }, widgets.List{ frame = { t = 11 , h = 11}, @@ -207,7 +207,7 @@ function AutoTrain:init() widgets.Divider{ frame={t=22, h=1}, frame_style_l = false, frame_style_r = false}, widgets.Label{ frame={ t = 23 , h = 1 }, - text = "Select units to exclude from automatic training" + text = "Select units to exclude from automatic training:" }, widgets.FilteredList{ frame = { t = 24 }, From f44a37ab6fa163b073b8eefd247364a5c3d33bb1 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Thu, 3 Apr 2025 21:15:15 -0500 Subject: [PATCH 41/44] Update to use the Military Module --- autotraining.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/autotraining.lua b/autotraining.lua index d6ec90968..08ff3af19 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -203,9 +203,10 @@ function addTraining(unit) for _, squad in ipairs(getTrainingSquads()) do for i=1,9,1 do if ( squad.positions[i].occupant == -1 ) then - squad.positions[i].occupant = unit.hist_figure_id - unit.military.squad_id = squad.id - unit.military.squad_position = i + dfhack.military.addToSquad(unit.id,squad.id) + -- squad.positions[i].occupant = unit.hist_figure_id + -- unit.military.squad_id = squad.id + -- unit.military.squad_position = i return true end end @@ -218,9 +219,10 @@ function removeTraining(unit) for _, squad in ipairs(getTrainingSquads()) do for i=1,9,1 do if ( unit.hist_figure_id == squad.positions[i].occupant ) then - unit.military.squad_id = -1 - unit.military.squad_position = -1 - squad.positions[i].occupant = -1 + dfhack.military.removeFromSquad(unit.id) + -- unit.military.squad_id = -1 + -- unit.military.squad_position = -1 + -- squad.positions[i].occupant = -1 return true end end @@ -234,9 +236,7 @@ function removeAll() for i=1,9,1 do local dwarf = getByID(squad.positions[i].occupant) if (dwarf ~= nil) then - dwarf.military.squad_id = -1 - dwarf.military.squad_position = -1 - squad.positions[i].occupant = -1 + removeTraining(dwarf) end end end From 7c186d32149730944780566a94a503b997b3c23a Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Thu, 3 Apr 2025 21:31:08 -0500 Subject: [PATCH 42/44] use the squad position --- autotraining.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autotraining.lua b/autotraining.lua index 08ff3af19..d903d5600 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -203,7 +203,7 @@ function addTraining(unit) for _, squad in ipairs(getTrainingSquads()) do for i=1,9,1 do if ( squad.positions[i].occupant == -1 ) then - dfhack.military.addToSquad(unit.id,squad.id) + dfhack.military.addToSquad(unit.id,squad.id,i) -- squad.positions[i].occupant = unit.hist_figure_id -- unit.military.squad_id = squad.id -- unit.military.squad_position = i From f670df7212a73cec11fe2bb34f53f02a08c452da Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sun, 6 Apr 2025 15:10:59 -0500 Subject: [PATCH 43/44] Remove all training dwarves when you disable --- autotraining.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/autotraining.lua b/autotraining.lua index d903d5600..f509091f7 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -277,6 +277,7 @@ function start() end function stop() + removeAll() repeatUtil.cancel(GLOBAL_KEY) dfhack.println(GLOBAL_KEY .. " | STOP") end From 595a760495ff3dea9b11d6c1ecafb667d1f10a80 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Thu, 10 Apr 2025 16:34:16 -0500 Subject: [PATCH 44/44] disable autotraining on map unload --- autotraining.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/autotraining.lua b/autotraining.lua index f509091f7..2f6b03f64 100644 --- a/autotraining.lua +++ b/autotraining.lua @@ -75,6 +75,10 @@ local function load_state() end dfhack.onStateChange[GLOBAL_KEY] = function(sc) + if sc == SC_MAP_UNLOADED then + state.enabled = false + return + end -- the state changed, is a map loaded and is that map in fort mode? if sc ~= SC_MAP_LOADED or df.global.gamemode ~= df.game_mode.DWARF then -- no its isnt, so bail