diff --git a/config.lua b/config.lua index 2ec69207..1c1ccd32 100644 --- a/config.lua +++ b/config.lua @@ -362,7 +362,9 @@ global.config = { }, -- enables a command which allows for an end-game event apocalypse = { - enabled = true + enabled = true, + -- chance behemoth biters and spitters will double on death. + duplicate_chance = 0.05 }, -- gradually informs players of features such as chat, toasts, etc. player_onboarding = { diff --git a/control.lua b/control.lua index 600b63ad..7b33815e 100644 --- a/control.lua +++ b/control.lua @@ -151,6 +151,8 @@ if config.redmew_settings.enabled then require 'features.gui.redmew_settings' end +require 'features.snake.control' + -- Debug-only modules if _DEBUG then require 'features.scenario_data_manipulation' diff --git a/features/apocalypse.lua b/features/apocalypse.lua index 39e50a62..f7d8dd48 100644 --- a/features/apocalypse.lua +++ b/features/apocalypse.lua @@ -5,15 +5,13 @@ local Global = require 'utils.global' local Command = require 'utils.command' local Toast = require 'features.gui.toast' local RS = require 'map_gen.shared.redmew_surface' -local HailHydra = require 'map_gen.shared.hail_hydra' local Color = require 'resources.color_presets' local Ranks = require 'resources.ranks' +local Event = require 'utils.event' +local random = math.random +local Config = require 'config' --- Constants -local hail_hydra_data = { - ['behemoth-spitter'] = {['behemoth-spitter'] = 0.01}, - ['behemoth-biter'] = {['behemoth-biter'] = 0.01} -} +local duplicate_chance = Config.apocalypse.duplicate_chance -- Local var local Public = {} @@ -35,6 +33,54 @@ Global.register( end ) +local name_map = {['behemoth-biter'] = true, ['behemoth-spitter'] = true} + +local biter_died_token = + Token.register( + function(event) + local entity = event.entity + if not entity.valid then + return + end + + local name = entity.name + if not name_map[name] then + return + end + + local force_name = entity.force.name + if force_name ~= 'enemy' then + return + end + + local surface = entity.surface + local create_entity = surface.create_entity + local position = entity.position + local spawn = {name = entity.name, force = 'enemy', position = position} + + create_entity(spawn) + + if random() > duplicate_chance then + return + end + + local spawn_position = surface.find_non_colliding_position(name, position, 8, 1) + if not spawn_position then + return + end + + spawn.position = spawn_position + create_entity(spawn) + end +) + +local aliens = { + 'behemoth-biter', + 'behemoth-biter', + 'behemoth-spitter', + 'behemoth-spitter' +} + local biter_spawn_token = Token.register( function() @@ -45,9 +91,6 @@ local biter_spawn_token = surface = RS.get_surface() player_force = game.forces.player - HailHydra.set_hydras(hail_hydra_data) - HailHydra.set_evolution_scale(1) - HailHydra.enable_hail_hydra() enemy_force.evolution_factor = 1 local p_spawn = player_force.get_spawn_position(surface) @@ -55,13 +98,6 @@ local biter_spawn_token = local create_entity = surface.create_entity - local aliens = { - 'behemoth-biter', - 'behemoth-biter', - 'behemoth-spitter', - 'behemoth-spitter' - } - for i = 1, #aliens do local spawn_pos = surface.find_non_colliding_position('behemoth-biter', p_spawn, 300, 1) if spawn_pos then @@ -72,6 +108,8 @@ local biter_spawn_token = group.set_command({type = defines.command.attack_area, destination = {0, 0}, radius = 500}) Toast.toast_all_players(500, {'apocalypse.toast_message'}) + + Event.add_removable(defines.events.on_entity_died, biter_died_token) end ) @@ -97,7 +135,7 @@ function Public.begin_apocalypse(_, player) primitives.apocalypse_now = true game.print({'apocalypse.apocalypse_begins'}, Color.pink) - Task.set_timeout(15, biter_spawn_token, {}) + Task.set_timeout(1, biter_spawn_token, {}) end Command.add( diff --git a/features/snake/control.lua b/features/snake/control.lua new file mode 100644 index 00000000..a48420a7 --- /dev/null +++ b/features/snake/control.lua @@ -0,0 +1,28 @@ +local GameGui = require 'features.snake.gui' +local Game = require 'features.snake.game' + +local Public = {} + +--- Starts snake game. +-- Note when players join the game they will lose thier character. +-- @param surface Surface that the board is placed on. +-- @param top_left_position Position where board is placed. Defaults to {x = 1, y = 1}. +-- @param size size of board in board tiles. Note that the actual size of the board will be (2 * size) + 1 in +-- factorio tiles. Defaults to 15. +-- @param update_rate number of ticks between updates. Defaults to 30. +-- @param maximun food on the board. Defaults to 6. +function Public.start_game(surface, top_left_position, size, update_rate, max_food) + Game.start_game(surface, top_left_position, size, update_rate, max_food) + GameGui.show() +end + +--- Ends the snake game. This will clean up any snake and food entities but will not restore the tiles nor +-- give players thier character back. +function Public.end_game() + Game.end_game() + GameGui.destroy() +end + +remote.add_interface('snake', Public) + +return Public diff --git a/features/snake/game.lua b/features/snake/game.lua new file mode 100644 index 00000000..adf8d641 --- /dev/null +++ b/features/snake/game.lua @@ -0,0 +1,411 @@ +local Global = require 'utils.global' +local Event = require 'utils.event' +local Token = require 'utils.token' +local Queue = require 'utils.queue' + +local random = math.random +local queue_new = Queue.new +local push = Queue.push +local push_to_end = Queue.push_to_end +local pop = Queue.pop +local peek = Queue.peek +local peek_start = Queue.peek_start +local peek_index = Queue.peek_index +local queue_size = Queue.size +local queue_pairs = Queue.pairs +local pairs = pairs + +local snakes = {} -- player_index -> snake_data {is_marked_for_destroy:bool, queue :Queue of {entity, cord} } +local board = { + size = 0, + surface = nil, + position = nil, + food_count = 0, + is_running = false, + update_rate = 30, + max_food = 6 +} +local cords_map = {} -- cords -> positions + +Global.register( + {snakes = snakes, board = board, cords_map = cords_map}, + function(tbl) + snakes = tbl.snakes + board = tbl.board + cords_map = tbl.cords_map + end +) + +local vectors = { + [0] = {x = 0, y = -1}, + [1] = {x = 0, y = -1}, + [2] = {x = 1, y = 0}, + [3] = {x = 1, y = 0}, + [4] = {x = 0, y = 1}, + [5] = {x = 0, y = 1}, + [6] = {x = -1, y = 0}, + [7] = {x = -1, y = 0} +} + +local function destroy_snake(index, snake) + for _, element in queue_pairs(snake.queue) do + local e = element.entity + if e and e.valid then + e.destroy() + end + end + snakes[index] = nil + + local player = game.get_player(index) + if not player or not player.valid then + return + end + + game.print({'snake.snake_destroyed', player.name, queue_size(snake.queue)}) +end + +local function destroy_dead_snakes() + for index, snake in pairs(snakes) do + if snake.is_marked_for_destroy then + destroy_snake(index, snake) + end + end +end + +local function spawn_food() + local size = board.size + local center = math.ceil(size / 2) + local surface = board.surface + local find_entity = surface.find_entity + + local food_count = board.food_count + local max_food = board.max_food + local tries = max_food - food_count + 10 + + while food_count < max_food and tries > 0 do + while tries > 0 do + tries = tries - 1 + local x, y = random(size), random(size) + + if x == center and y == center then + goto continue + end + + local pos = cords_map[x][y] + + local entity = find_entity('character', pos) or find_entity('compilatron', pos) + if entity then + goto continue + end + + entity = + surface.create_entity({name = 'compilatron', position = pos, force = 'neutral', direction = random(7)}) + entity.active = false + food_count = food_count + 1 + + break + + ::continue:: + end + end + + board.food_count = food_count +end + +local function destroy_food() + local position = board.position + local size = board.size + local food = + board.surface.find_entities_filtered( + { + name = 'compilatron', + area = {left_top = position, right_bottom = {position.x + size * 2, position.y + size * 2}} + } + ) + + for i = 1, #food do + local e = food[i] + if e.valid then + e.destroy() + end + end + + board.food_count = 0 +end + +local function get_new_head_cord(head_cord, direction) + local vector = vectors[direction] + local vec_x, vec_y = vector.x, vector.y + local x, y = head_cord.x + vec_x, head_cord.y + vec_y + + return x, y +end + +local function tick_snake(index, snake) + local player = game.get_player(index) + + if not player or not player.valid then + snake.is_marked_for_destroy = true + return + end + + local character = player.character + if not character or not character.valid then + snake.is_marked_for_destroy = true + return + end + + local surface = board.surface + local find_entity = surface.find_entity + local snake_queue = snake.queue + local snake_size = queue_size(snake_queue) + local head = peek_start(snake_queue) + local tail = peek(snake_queue) + local head_cord = head.cord + local tail_entity = tail.entity + local tail_cord = tail.cord + local size = board.size + + local walking_state = character.walking_state + walking_state.walking = true + local direction = walking_state.direction + local x, y = get_new_head_cord(head_cord, direction) + + if x <= 0 or x > size or y <= 0 or y > size then + snake.is_marked_for_destroy = true + tail_entity.destroy() + return + end + + local new_head_position = cords_map[x][y] + + if snake_size > 1 and find_entity('character', new_head_position) == peek_index(snake_queue, snake_size - 1).entity then + direction = (direction + 4) % 8 + walking_state.direction = direction + x, y = get_new_head_cord(head_cord, direction) + end + + if x <= 0 or x > size or y <= 0 or y > size then + snake.is_marked_for_destroy = true + tail_entity.destroy() + return + end + + new_head_position = cords_map[x][y] + + tail_entity.teleport(new_head_position) + tail.cord = {x = x, y = y} + + pop(snake_queue) + push(snake_queue, tail) + + player.character = nil + player.character = tail_entity + tail_entity.walking_state = walking_state + head.entity.active = false + tail_entity.active = true + + local entity = find_entity('compilatron', new_head_position) + if entity and entity.valid then + entity.destroy() + + entity = + surface.create_entity {name = 'character', position = cords_map[tail_cord.x][tail_cord.y], force = 'player'} + entity.character_running_speed_modifier = -1 + entity.color = player.color + entity.active = false + push_to_end(snake_queue, {entity = entity, cord = tail_cord}) + + board.food_count = board.food_count - 1 + end +end + +local function tick_snakes() + for index, snake in pairs(snakes) do + tick_snake(index, snake) + end +end + +local function check_snakes_for_collisions() + local count_entities_filtered = board.surface.count_entities_filtered + for index, snake in pairs(snakes) do + if snake.is_marked_for_destroy then + goto continue + end + + if count_entities_filtered({name = 'character', position = peek_start(snake.queue).entity.position}) > 1 then + snake.is_marked_for_destroy = true + end + + ::continue:: + end +end + +local tick = + Token.register( + function() + tick_snakes() + check_snakes_for_collisions() + destroy_dead_snakes() + spawn_food() + end +) + +local function make_board() + local size = board.size + local position = board.position + local surface = board.surface + + local pos_x, pos_y = position.x, position.y + + for x = 1, size do + local col = {} + cords_map[x] = col + for y = 1, size do + col[y] = {pos_x + 2 * x - 0.5, pos_y + 2 * y - 0.5} + end + end + + size = size * 2 + local tiles = {} + + for x = 0, size do + for y = 0, size do + local pos = {pos_x + x, pos_y + y} + local tile_name + + if x == 0 or x == size or y == 0 or y == size then + tile_name = 'deepwater' + elseif x % 2 == 1 and y % 2 == 1 then + tile_name = 'grass-1' + else + tile_name = 'water' + end + + tiles[#tiles + 1] = {position = pos, name = tile_name} + end + end + + surface.set_tiles(tiles) +end + +local function find_new_snake_position() + local size = board.size + local find_entity = board.surface.find_entity + + local min = math.min(4, size) + local max = math.max(1, size - 4) + + if min > max then + min, max = max, min + elseif min == max then + min = 1 + max = size + end + + local tries = 10 + + while tries > 0 do + tries = tries - 1 + + local x, y = random(min, max), random(min, max) + local pos = cords_map[x][y] + + local entity = find_entity('character', pos) or find_entity('compilatron', pos) + if not entity then + return {x = x, y = y}, pos + end + end +end + +local function new_snake(player) + if not board.is_running then + return + end + + if not player or not player.valid then + return + end + + if snakes[player.index] then + return + end + + local character = player.character + if character and character.valid then + character.destroy() + end + + local cord, pos = find_new_snake_position() + + if not cord then + player.print({'snake.spawn_snake_fail'}) + return + end + + player.teleport(pos, board.surface) + player.create_character() + character = player.character + character.character_running_speed_modifier = -1 + + local queue = queue_new() + push(queue, {entity = character, cord = cord}) + local snake = {queue = queue} + + snakes[player.index] = snake +end + +local function new_game(surface, position, size, update_rate, max_food) + board.size = size or 15 + board.surface = surface + position = position or {x = 1, y = 1} + position.x = position.x or position[1] + position.y = position.y or position[2] + board.position = position + board.update_rate = update_rate or 30 + board.max_food = max_food or 6 + + make_board() + destroy_food() + spawn_food() + + board.is_running = true + + Event.add_removable_nth_tick(board.update_rate, tick) +end + +local Public = {} + +function Public.start_game(surface, top_left_position, size, update_rate, max_food) + if board.is_running then + error('Snake game is already running you must end the game first.', 2) + end + + if not surface then + error('Surface must be set.', 2) + end + + new_game(surface, top_left_position, size, update_rate or board.update_rate, max_food or board.max_food) +end + +function Public.end_game() + for index, snake in pairs(snakes) do + destroy_snake(index, snake) + end + + destroy_food() + + Event.remove_removable_nth_tick(board.update_rate, tick) + + board.is_running = false +end + +function Public.new_snake(player) + new_snake(player) +end + +function Public.is_running() + return board.is_running +end + +return Public diff --git a/features/snake/gui.lua b/features/snake/gui.lua new file mode 100644 index 00000000..b222ccde --- /dev/null +++ b/features/snake/gui.lua @@ -0,0 +1,51 @@ +local Game = require 'features.snake.game' +local Gui = require 'utils.gui' +local Event = require 'utils.event' + +local Public = {} + +local main_button_name = Gui.uid_name() + +local function show_gui_for_player(player) + if not player or not player.valid then + return + end + + local top = player.gui.top + if not top[main_button_name] then + top.add {type = 'button', name = main_button_name, caption = {'snake.name'}} + end +end + +local function player_created(event) + if Game.is_running() then + local player = game.get_player(event.player_index) + show_gui_for_player(player) + end +end + +Event.add(defines.events.on_player_created, player_created) + +function Public.show() + for _, player in pairs(game.players) do + show_gui_for_player(player) + end +end + +function Public.destroy() + for _, player in pairs(game.players) do + local button = player.gui.top[main_button_name] + if button and button.valid then + button.destroy() + end + end +end + +Gui.on_click( + main_button_name, + function(event) + Game.new_snake(event.player) + end +) + +return Public diff --git a/locale/en/redmew_features.cfg b/locale/en/redmew_features.cfg index 780addb5..33a1786f 100644 --- a/locale/en/redmew_features.cfg +++ b/locale/en/redmew_features.cfg @@ -161,3 +161,8 @@ ammo_count=Autofill ammo count invalid_ammo_count=ammo count must be a positive integer main_button_tooltip=Autofill settings frame_name=Autofill + +[snake] +name=Snake +spawn_snake_fail=Unable to spawn snake, please try again. +snake_destroyed=__1__ has been destroyed with a score of __2__. diff --git a/utils/priority_queue.lua b/utils/priority_queue.lua index bccd2358..18ab9e8b 100644 --- a/utils/priority_queue.lua +++ b/utils/priority_queue.lua @@ -1,20 +1,46 @@ +local Debug = require 'utils.debug' +local is_closure = Debug.is_closure +local floor = math.floor + local PriorityQueue = {} -function PriorityQueue.new() - return {} -end - -local function default_comp(a, b) +local function default_comparator(a, b) return a < b end -local function HeapifyFromEndToStart(queue, comp) - comp = comp or default_comp - local pos = #queue +--- Min heap implementation of a priority queue. Smaller elements, as determined by the comparator, +-- have a higher priority. +-- @param comparator the comparator function used to compare elements, if nil the +-- deafult comparator is used. +-- @usage +-- local PriorityQueue = require 'utils.priority_queue' +-- +-- local queue = PriorityQueue.new() +-- PriorityQueue.push(queue, 4) +-- PriorityQueue.push(queue, 7) +-- PriorityQueue.push(queue, 2) +-- +-- game.print(PriorityQueue.pop(queue)) -- 2 +-- game.print(PriorityQueue.pop(queue)) -- 4 +-- game.print(PriorityQueue.pop(queue)) -- 7 +function PriorityQueue.new(comparator) + if comparator == nil then + comparator = default_comparator + elseif is_closure(comparator) then + error('comparator cannot be a closure.', 2) + end + + return {_comparator = comparator} +end + +local function heapify_from_end_to_start(self) + local comparator = self._comparator + local pos = #self while pos > 1 do - local parent = bit32.rshift(pos, 1) -- integer division by 2 - if comp(queue[pos], queue[parent]) then - queue[pos], queue[parent] = queue[parent], queue[pos] + local parent = floor(pos * 0.5) + local a, b = self[pos], self[parent] + if comparator(a, b) then + self[pos], self[parent] = b, a pos = parent else break @@ -22,25 +48,26 @@ local function HeapifyFromEndToStart(queue, comp) end end -local function HeapifyFromStartToEnd(queue, comp) - comp = comp or default_comp +local function heapify_from_start_to_end(self) + local comparator = self._comparator local parent = 1 local smallest = 1 + local count = #self while true do local child = parent * 2 - if child > #queue then + if child > count then break end - if comp(queue[child], queue[parent]) then + if comparator(self[child], self[parent]) then smallest = child end child = child + 1 - if child <= #queue and comp(queue[child], queue[smallest]) then + if child <= count and comparator(self[child], self[smallest]) then smallest = child end if parent ~= smallest then - queue[parent], queue[smallest] = queue[smallest], queue[parent] + self[parent], self[smallest] = self[smallest], self[parent] parent = smallest else break @@ -48,27 +75,33 @@ local function HeapifyFromStartToEnd(queue, comp) end end -function PriorityQueue.size(queue) - return #queue +--- Returns the number of the number of elements in the priority queue. +function PriorityQueue.size(self) + return #self end -function PriorityQueue.push(queue, element, comp) - table.insert(queue, element) - HeapifyFromEndToStart(queue, comp) +-- Inserts an element into the priority queue. +function PriorityQueue.push(self, element) + self[#self + 1] = element + heapify_from_end_to_start(self) end -function PriorityQueue.pop(queue, comp) - local element = queue[1] +-- Removes and returns the highest priority element from the priority queue. +-- If the priority queue is empty returns nil. +function PriorityQueue.pop(self) + local element = self[1] - queue[1] = queue[#queue] - queue[#queue] = nil - HeapifyFromStartToEnd(queue, comp) + self[1] = self[#self] + self[#self] = nil + heapify_from_start_to_end(self) return element end -function PriorityQueue.peek(queue) - return queue[1] +-- Returns, without removing, the highest priority element from the priority queue. +-- If the priority queue is empty returns nil. +function PriorityQueue.peek(self) + return self[1] end return PriorityQueue diff --git a/utils/queue.lua b/utils/queue.lua index 1907c30b..881cbd23 100644 --- a/utils/queue.lua +++ b/utils/queue.lua @@ -1,24 +1,39 @@ local Queue = {} function Queue.new() - local queue = {_head = 0, _tail = 0} + local queue = {_head = 1, _tail = 1} return queue end function Queue.size(queue) - return queue._tail - queue._head + return queue._head - queue._tail end function Queue.push(queue, element) local index = queue._head queue[index] = element - queue._head = index - 1 + queue._head = index + 1 +end + +--- Pushes the element such that it would be the next element pop'ed. +function Queue.push_to_end(queue, element) + local index = queue._tail - 1 + queue[index] = element + queue._tail = index end function Queue.peek(queue) return queue[queue._tail] end +function Queue.peek_start(queue) + return queue[queue._head - 1] +end + +function Queue.peek_index(queue, index) + return queue[queue._tail + index - 1] +end + function Queue.pop(queue) local index = queue._tail @@ -26,9 +41,37 @@ function Queue.pop(queue) queue[index] = nil if element then - queue._tail = index - 1 + queue._tail = index + 1 end + return element end +function Queue.to_array(queue) + local n = 1 + local res = {} + + for i = queue._tail, queue._head - 1 do + res[n] = queue[i] + n = n + 1 + end + + return res +end + +function Queue.pairs(queue) + local index = queue._tail + return function() + local element = queue[index] + + if element then + local old = index + index = index + 1 + return old, element + else + return nil + end + end +end + return Queue diff --git a/utils/task.lua b/utils/task.lua index a1d30ab9..ded5cd66 100644 --- a/utils/task.lua +++ b/utils/task.lua @@ -9,38 +9,65 @@ local PriorityQueue = require 'utils.priority_queue' local Event = require 'utils.event' local Token = require 'utils.token' local ErrorLogging = require 'utils.error_logging' +local Global = require 'utils.global' + +local floor = math.floor +local log10 = math.log10 +local Token_get = Token.get +local pcall = pcall +local Queue_peek = Queue.peek +local Queue_pop = Queue.pop +local Queue_push = Queue.push +local PriorityQueue_peek = PriorityQueue.peek +local PriorityQueue_pop = PriorityQueue.pop +local PriorityQueue_push = PriorityQueue.push local Task = {} -global.callbacks = global.callbacks or PriorityQueue.new() -global.next_async_callback_time = -1 -global.task_queue = global.task_queue or Queue.new() -global.total_task_weight = 0 -global.task_queue_speed = 1 - -local function comp(a, b) +local function comparator(a, b) return a.time < b.time end -global.tpt = global.task_queue_speed -local function get_task_per_tick() - if game.tick % 300 == 0 then - local size = global.total_task_weight - global.tpt = math.floor(math.log10(size + 1)) * global.task_queue_speed - if global.tpt < 1 then - global.tpt = 1 - end +local callbacks = PriorityQueue.new(comparator) +local task_queue = Queue.new() +local primitives = { + next_async_callback_time = -1, + total_task_weight = 0, + task_queue_speed = 1, + task_per_tick = 1 +} + +Global.register( + {callbacks = callbacks, task_queue = task_queue, primitives = primitives}, + function(tbl) + callbacks = tbl.callbacks + task_queue = tbl.task_queue + primitives = tbl.primitives end - return global.tpt +) + +local function get_task_per_tick(tick) + if tick % 300 == 0 then + local size = primitives.total_task_weight + local task_per_tick = floor(log10(size + 1)) * primitives.task_queue_speed + if task_per_tick < 1 then + task_per_tick = 1 + end + + primitives.task_per_tick = task_per_tick + return task_per_tick + end + return primitives.task_per_tick end local function on_tick() - local queue = global.task_queue - for i = 1, get_task_per_tick() do - local task = Queue.peek(queue) + local tick = game.tick + + for i = 1, get_task_per_tick(tick) do + local task = Queue_peek(task_queue) if task ~= nil then -- result is error if not success else result is a boolean for if the task should stay in the queue. - local success, result = pcall(Token.get(task.func_token), task.params) + local success, result = pcall(Token_get(task.func_token), task.params) if not success then if _DEBUG then error(result) @@ -48,19 +75,18 @@ local function on_tick() log(result) ErrorLogging.generate_error_report(result) end - Queue.pop(queue) - global.total_task_weight = global.total_task_weight - task.weight + Queue_pop(task_queue) + primitives.total_task_weight = primitives.total_task_weight - task.weight elseif not result then - Queue.pop(queue) - global.total_task_weight = global.total_task_weight - task.weight + Queue_pop(task_queue) + primitives.total_task_weight = primitives.total_task_weight - task.weight end end end - local callbacks = global.callbacks - local callback = PriorityQueue.peek(callbacks) - while callback ~= nil and game.tick >= callback.time do - local success, result = pcall(Token.get(callback.func_token), callback.params) + local callback = PriorityQueue_peek(callbacks) + while callback ~= nil and tick >= callback.time do + local success, result = pcall(Token_get(callback.func_token), callback.params) if not success then if _DEBUG then error(result) @@ -69,8 +95,8 @@ local function on_tick() ErrorLogging.generate_error_report(result) end end - PriorityQueue.pop(callbacks, comp) - callback = PriorityQueue.peek(callbacks) + PriorityQueue_pop(callbacks) + callback = PriorityQueue_peek(callbacks) end end @@ -85,7 +111,7 @@ function Task.set_timeout_in_ticks(ticks, func_token, params) end local time = game.tick + ticks local callback = {time = time, func_token = func_token, params = params} - PriorityQueue.push(global.callbacks, callback, comp) + PriorityQueue_push(callbacks, callback) end --- Allows you to set a timer (in seconds) after which the tokened function will be run with params given as an argument @@ -109,8 +135,21 @@ end -- Ex. if the task is expected to repeat multiple times (ie. the function returns true and loops several ticks) function Task.queue_task(func_token, params, weight) weight = weight or 1 - global.total_task_weight = global.total_task_weight + weight - Queue.push(global.task_queue, {func_token = func_token, params = params, weight = weight}) + primitives.total_task_weight = primitives.total_task_weight + weight + Queue_push(task_queue, {func_token = func_token, params = params, weight = weight}) +end + +function Task.get_queue_speed() + return primitives.task_queue_speed +end + +function Task.set_queue_speed(value) + value = value or 1 + if value < 0 then + value = 0 + end + + primitives.task_queue_speed = value end Event.add(defines.events.on_tick, on_tick)