mirror of
https://github.com/Refactorio/RedMew.git
synced 2025-02-11 13:39:17 +02:00
Merge pull request #947 from SimonFlapse/diggy_cutscene
Cutscene Controller
This commit is contained in:
commit
fc1d1af9d8
@ -253,6 +253,12 @@ stds.factorio_control = {
|
||||
'set_forces',
|
||||
'get_players',
|
||||
'set_players',
|
||||
'get_visible',
|
||||
'set_visible',
|
||||
'get_draw_on_ground',
|
||||
'set_draw_on_ground',
|
||||
'get_only_in_alt_mode',
|
||||
'set_only_in_alt_mode',
|
||||
'get_color',
|
||||
'set_color',
|
||||
'get_width',
|
||||
|
@ -169,6 +169,7 @@ global.config = {
|
||||
'Welcome to this map created by the RedMew team. You can join our discord at: redmew.com/discord',
|
||||
'Click the question mark in the top left corner for server information and map details.'
|
||||
},
|
||||
cutscene = false,
|
||||
-- format is a table: {{message, weight}, {message, weight}}, where a higher weight has more chance to be shown
|
||||
random_join_message_set = require 'resources.join_messages',
|
||||
-- applied when cheat_mode is set to true
|
||||
@ -306,6 +307,10 @@ global.config = {
|
||||
rich_text_gui = {
|
||||
enabled = true
|
||||
},
|
||||
-- adds a command to open a gui that can play sounds from a list
|
||||
radio = {
|
||||
enabled = false
|
||||
},
|
||||
-- adds a camera to watch another player
|
||||
camera = {
|
||||
enabled = true
|
||||
|
@ -144,6 +144,9 @@ end
|
||||
if config.rich_text_gui.enabled then
|
||||
require 'features.gui.rich_text'
|
||||
end
|
||||
if config.radio.enabled or _DEBUG then
|
||||
require 'features.gui.radio'
|
||||
end
|
||||
if config.redmew_settings.enabled then
|
||||
require 'features.gui.redmew_settings'
|
||||
end
|
||||
|
529
features/cutscene/cutscene_controller.lua
Normal file
529
features/cutscene/cutscene_controller.lua
Normal file
@ -0,0 +1,529 @@
|
||||
local Event = require 'utils.event'
|
||||
local Token = require 'utils.token'
|
||||
local Task = require 'utils.task'
|
||||
local Global = require 'utils.global'
|
||||
local Command = require 'utils.command'
|
||||
local Debug = require 'utils.debug'
|
||||
local Gui = require 'utils.gui'
|
||||
|
||||
local set_timeout_in_ticks = Task.set_timeout_in_ticks
|
||||
local debug_print = Debug.print
|
||||
|
||||
local skip_btn_name = Gui.uid_name()
|
||||
local backward_btn_name = Gui.uid_name()
|
||||
local forward_btn_name = Gui.uid_name()
|
||||
|
||||
local Public = {}
|
||||
local handler
|
||||
|
||||
local cutscene_functions = {}
|
||||
local running_cutscenes = {}
|
||||
local replay = {
|
||||
identifier = nil,
|
||||
final_transition_time = nil
|
||||
}
|
||||
Global.register(
|
||||
{
|
||||
cutscene_functions = cutscene_functions,
|
||||
running_cutscenes = running_cutscenes,
|
||||
replay = replay
|
||||
},
|
||||
function(tbl)
|
||||
cutscene_functions = tbl.cutscene_functions
|
||||
running_cutscenes = tbl.running_cutscenes
|
||||
replay = tbl.replay
|
||||
end
|
||||
)
|
||||
|
||||
local function valid(entity)
|
||||
return entity and entity.valid
|
||||
end
|
||||
|
||||
local function waypoint_still_active(tick, player_index)
|
||||
local running_cutscene = running_cutscenes[player_index]
|
||||
tick = tick or -1
|
||||
if tick == -1 then
|
||||
debug_print('Tick was nil', 5)
|
||||
end
|
||||
if not running_cutscene or tick < running_cutscene.start_tick then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local toggle_gui_delayed =
|
||||
Token.register(
|
||||
function(params)
|
||||
local player = params.player
|
||||
if not waypoint_still_active(params.tick, player.index) then
|
||||
debug_print('Cutscene is no longer active. Skipping toggle_gui')
|
||||
return
|
||||
end
|
||||
local event = {player = player}
|
||||
local clear = params.clear
|
||||
if clear == 'left' then
|
||||
player.gui.left.clear()
|
||||
elseif clear == 'top' then
|
||||
player.gui.top.clear()
|
||||
elseif clear == 'center' then
|
||||
player.gui.center.clear()
|
||||
end
|
||||
params.gui.toggle(event)
|
||||
end
|
||||
)
|
||||
|
||||
function Public.toggle_gui(tick, player, gui, initial_delay, clear)
|
||||
--[[if type(gui) == 'table' then
|
||||
debug_print('Provided GUI is invalid.')
|
||||
return
|
||||
end]]
|
||||
set_timeout_in_ticks(initial_delay, toggle_gui_delayed, {tick = tick, player = player, gui = gui, clear = clear})
|
||||
end
|
||||
|
||||
local play_sound_delayed =
|
||||
Token.register(
|
||||
function(params)
|
||||
local player = params.player
|
||||
if not waypoint_still_active(params.tick, player.index) then
|
||||
debug_print('Cutscene is no longer active. Skipping play_sound')
|
||||
return
|
||||
end
|
||||
player.play_sound {path = params.path}
|
||||
end
|
||||
)
|
||||
|
||||
function Public.play_sound(tick, player, path, times, delay, initial_delay)
|
||||
if not game.is_valid_sound_path(path) then
|
||||
debug_print('Provided SoundPath is invalid. Try opening /radio and browse for a valid path')
|
||||
return
|
||||
end
|
||||
|
||||
if not waypoint_still_active(tick, player.index) then
|
||||
debug_print('Cutscene is no longer active. Skipping play_sound')
|
||||
return
|
||||
end
|
||||
|
||||
times = times or 1
|
||||
if times == 1 and not delay and initial_delay then
|
||||
delay = initial_delay
|
||||
end
|
||||
if times > 1 or delay then
|
||||
delay = delay or 20
|
||||
initial_delay = initial_delay or 0
|
||||
for i = 1, times, 1 do
|
||||
set_timeout_in_ticks(initial_delay + delay * i, play_sound_delayed, {tick = tick, player = player, path = path})
|
||||
end
|
||||
else
|
||||
player.play_sound {path = path}
|
||||
end
|
||||
end
|
||||
|
||||
local remove_renderings =
|
||||
Token.register(
|
||||
function(renderings)
|
||||
for _, v in pairs(renderings) do
|
||||
if rendering.is_valid(v) then
|
||||
rendering.destroy(v)
|
||||
debug_print('Deleted rendering with id: ' .. v)
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
---Asserts if a given variable is of the expected type using type().
|
||||
---
|
||||
---@param expected_type string
|
||||
---@param given any
|
||||
---@param variable_reference_message string displayed when the expectation is not met
|
||||
local function assert_type(expected_type, given, variable_reference_message, allow_nil)
|
||||
local given_type = type(given)
|
||||
if given_type ~= expected_type and (allow_nil and given_type ~= 'nil') then
|
||||
error('Argument ' .. variable_reference_message .. " must be of type '" .. expected_type .. "', given '" .. given_type .. "'")
|
||||
end
|
||||
end
|
||||
|
||||
function Public.register_cutscene_function(identifier, waypoints, func, terminate_func)
|
||||
assert_type('string', identifier, 'identifier of function cutscene_controller.register_cutscene_function')
|
||||
assert_type('table', waypoints, 'waypoints of function cutscene_controller.register_cutscene_function')
|
||||
assert_type('number', func, 'func of function cutscene_controller.register_cutscene_function')
|
||||
assert_type('number', terminate_func, 'func of function cutscene_controller.register_cutscene_function', true)
|
||||
|
||||
cutscene_functions[identifier] = {func = func, waypoints = waypoints, update = false, terminate_func = terminate_func}
|
||||
end
|
||||
|
||||
function Public.register_running_cutscene(player_index, identifier, final_transition_time)
|
||||
assert_type('number', player_index, 'player_index of function cutscene_controller.register_running_cutscene')
|
||||
assert_type('string', identifier, 'identifier of function cutscene_controller.register_running_cutscene')
|
||||
assert_type('number', final_transition_time, 'identifier of function cutscene_controller.register_running_cutscene', true)
|
||||
|
||||
local player = game.get_player(player_index)
|
||||
if not valid(player) then
|
||||
return
|
||||
end
|
||||
|
||||
local cutscene_function = cutscene_functions[identifier]
|
||||
if not cutscene_function then
|
||||
return
|
||||
end
|
||||
|
||||
local waypoints = cutscene_function.waypoints
|
||||
if not waypoints then
|
||||
return
|
||||
end
|
||||
|
||||
if running_cutscenes[player_index] then
|
||||
player.print({'cutscene_controller.cannot_start_new'})
|
||||
return
|
||||
end
|
||||
|
||||
running_cutscenes[player_index] = {
|
||||
func = cutscene_function.func,
|
||||
waypoints = waypoints,
|
||||
update = cutscene_function.update,
|
||||
final_transition_time = final_transition_time,
|
||||
character = player.character,
|
||||
terminate_func = cutscene_function.terminate_func,
|
||||
rendering = {},
|
||||
current_index = -1,
|
||||
start_tick = 0
|
||||
}
|
||||
local running_cutscene = running_cutscenes[player_index]
|
||||
|
||||
if player.controller_type == defines.controllers.cutscene then
|
||||
debug_print('' .. player.name .. ' was already in another cutscene not controlled by this module. It has been stopped')
|
||||
player.exit_cutscene()
|
||||
end
|
||||
player.set_controller {type = defines.controllers.ghost}
|
||||
|
||||
final_transition_time = final_transition_time >= 0 and final_transition_time or 60
|
||||
running_cutscene.final_transition_time = final_transition_time
|
||||
running_cutscene.identifier = identifier
|
||||
player.set_controller {
|
||||
type = defines.controllers.cutscene,
|
||||
waypoints = waypoints,
|
||||
final_transition_time = final_transition_time
|
||||
}
|
||||
|
||||
local flow = player.gui.top.add {type = 'flow'}
|
||||
running_cutscene.btn = flow
|
||||
|
||||
local btn = flow.add {type = 'sprite-button', name = skip_btn_name, caption = 'Skip cutscene'}
|
||||
btn.style.minimal_height = 28
|
||||
btn.style.minimal_width = 150
|
||||
btn.style.font = 'default-large-bold'
|
||||
btn.style.font_color = {r = 255, g = 215, b = 0}
|
||||
|
||||
local back_btn = flow.add {type = 'sprite-button', name = backward_btn_name, caption = 'Go back'}
|
||||
back_btn.style.minimal_height = 28
|
||||
back_btn.style.minimal_width = 100
|
||||
back_btn.style.font = 'default-large-bold'
|
||||
back_btn.style.font_color = {r = 255, g = 215, b = 0}
|
||||
|
||||
local forward_btn = flow.add {type = 'sprite-button', name = forward_btn_name, caption = 'Go forward'}
|
||||
forward_btn.style.minimal_height = 28
|
||||
forward_btn.style.minimal_width = 100
|
||||
forward_btn.style.font = 'default-large-bold'
|
||||
forward_btn.style.font_color = {r = 255, g = 215, b = 0}
|
||||
|
||||
handler({player_index = player_index, waypoint_index = -1, tick = game.tick})
|
||||
end
|
||||
|
||||
local function restart_cutscene(player_index, waypoints, start_index)
|
||||
local current_running = running_cutscenes[player_index]
|
||||
local final_transition_time = current_running.final_transition_time
|
||||
current_running.update = false
|
||||
local character = current_running.character
|
||||
|
||||
if not character then
|
||||
log('Player index: ' .. player_index .. ' managed to lose their character in a cutscene')
|
||||
end
|
||||
|
||||
local end_waypoint = {
|
||||
-- end waypoint
|
||||
position = character.position,
|
||||
transition_time = final_transition_time,
|
||||
time_to_wait = 1,
|
||||
zoom = 1,
|
||||
terminate = true
|
||||
}
|
||||
|
||||
table.insert(waypoints, end_waypoint)
|
||||
|
||||
running_cutscenes[player_index] = {
|
||||
func = current_running.func,
|
||||
waypoints = waypoints,
|
||||
update = false,
|
||||
final_transition_time = final_transition_time,
|
||||
character = character,
|
||||
terminate_func = current_running.terminate_func,
|
||||
rendering = current_running.rendering,
|
||||
btn = current_running.btn,
|
||||
current_index = current_running.current_index,
|
||||
start_tick = current_running.start_tick
|
||||
}
|
||||
|
||||
debug_print('Updating cutscene for player_index ' .. player_index)
|
||||
debug_print(running_cutscenes[player_index])
|
||||
|
||||
local player = game.get_player(player_index)
|
||||
if not valid(player) then
|
||||
return
|
||||
end
|
||||
|
||||
if player.controller_type == defines.controllers.cutscene then
|
||||
player.exit_cutscene()
|
||||
player.set_controller {type = defines.controllers.ghost}
|
||||
end
|
||||
|
||||
player.set_controller {
|
||||
type = defines.controllers.cutscene,
|
||||
waypoints = waypoints,
|
||||
final_transition_time = final_transition_time
|
||||
}
|
||||
|
||||
if start_index then
|
||||
player.jump_to_cutscene_waypoint(start_index + 1)
|
||||
else
|
||||
start_index = -1
|
||||
end
|
||||
|
||||
handler({player_index = player_index, waypoint_index = start_index, tick = game.tick})
|
||||
end
|
||||
|
||||
function Public.inject_waypoint(player_index, waypoint, waypoint_index, override)
|
||||
local running_cutscene = running_cutscenes[player_index]
|
||||
if not running_cutscene then
|
||||
return
|
||||
end
|
||||
local waypoints = running_cutscene.waypoints
|
||||
if not waypoints then
|
||||
return
|
||||
end
|
||||
local copy_waypoints = {}
|
||||
for i = 1, #waypoints do
|
||||
table.insert(copy_waypoints, waypoints[i])
|
||||
end
|
||||
if override then
|
||||
copy_waypoints[waypoint_index] = waypoint
|
||||
else
|
||||
table.insert(copy_waypoints, waypoint_index, waypoint)
|
||||
end
|
||||
running_cutscene.update = copy_waypoints
|
||||
end
|
||||
|
||||
local callback_function =
|
||||
Token.register(
|
||||
function(params)
|
||||
local player_index = params.player_index
|
||||
local func_params = params.params
|
||||
if waypoint_still_active(func_params.tick, player_index) then
|
||||
Token.get(params.func)(player_index, params.waypoint_index, func_params)
|
||||
else
|
||||
debug_print('Skipping callback function. Cutscene got terminated!')
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
local reconnect_character =
|
||||
Token.register(
|
||||
function(params)
|
||||
local player_index = params.player_index
|
||||
local player = game.get_player(player_index)
|
||||
local running_cutscene = params.running_cutscene
|
||||
local character = running_cutscene.character
|
||||
local func = running_cutscene.terminate_func
|
||||
if valid(player) and valid(character) then
|
||||
player.exit_cutscene()
|
||||
player.set_controller {type = defines.controllers.character, character = character}
|
||||
if func then
|
||||
Token.get(func)(player_index)
|
||||
end
|
||||
Token.get(remove_renderings)(running_cutscene.rendering)
|
||||
running_cutscene.btn.destroy()
|
||||
running_cutscenes[player_index] = nil
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
function Public.terminate_cutscene(player_index, ticks)
|
||||
local running_cutscene = running_cutscenes[player_index]
|
||||
if not running_cutscene then
|
||||
return
|
||||
end
|
||||
ticks = ticks and ticks or 1
|
||||
debug_print('Terminating cutscene in ' .. ticks .. ' Ticks')
|
||||
|
||||
set_timeout_in_ticks(
|
||||
ticks,
|
||||
reconnect_character,
|
||||
{
|
||||
player_index = player_index,
|
||||
running_cutscene = running_cutscene
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function Public.register_rendering_id(player_index, tick, render_id)
|
||||
if type(render_id) ~= 'table' then
|
||||
render_id = {render_id}
|
||||
end
|
||||
local running_cutscene = running_cutscenes[player_index]
|
||||
for _, id in pairs(render_id) do
|
||||
if rendering.is_valid(id) then
|
||||
if not waypoint_still_active(tick, player_index) then
|
||||
debug_print('The rendering with id ' .. id .. ' was not added. Destroying it instead')
|
||||
rendering.destroy(id)
|
||||
else
|
||||
table.insert(running_cutscene.rendering, id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Public.register_replay(identifier, final_transition_time)
|
||||
replay.identifier = identifier
|
||||
replay.final_transition_time = final_transition_time
|
||||
debug_print('Identifier ' .. identifier .. ' registered as replay cutscene')
|
||||
end
|
||||
|
||||
handler = function(event)
|
||||
local player_index = event.player_index
|
||||
local waypoint_index = event.waypoint_index
|
||||
local tick = event.tick
|
||||
|
||||
debug_print('Waypoint_index ' .. waypoint_index .. ' has finished at tick: ' .. tick)
|
||||
|
||||
local running_cutscene = running_cutscenes[player_index]
|
||||
if not running_cutscene then
|
||||
return
|
||||
end
|
||||
running_cutscene.current_index = waypoint_index + 1
|
||||
running_cutscene.start_tick = tick
|
||||
|
||||
local update = running_cutscene.update
|
||||
if update then
|
||||
restart_cutscene(player_index, update, waypoint_index)
|
||||
return
|
||||
end
|
||||
local ticks = running_cutscene.waypoints[waypoint_index + 2]
|
||||
if ticks then
|
||||
ticks = ticks.transition_time
|
||||
else
|
||||
ticks = running_cutscene.final_transition_time
|
||||
end
|
||||
|
||||
local func = running_cutscene.func
|
||||
if not func then
|
||||
return
|
||||
end
|
||||
local current_waypoint = running_cutscene.waypoints[waypoint_index + 2]
|
||||
if not current_waypoint or current_waypoint.terminate then
|
||||
Public.terminate_cutscene(player_index, ticks)
|
||||
return
|
||||
end
|
||||
local params = {
|
||||
position = current_waypoint.position,
|
||||
time_to_wait = current_waypoint.time_to_wait,
|
||||
transition_time = current_waypoint.transition_time,
|
||||
zoom = current_waypoint.zoom,
|
||||
name = current_waypoint.name,
|
||||
tick = tick
|
||||
}
|
||||
|
||||
debug_print('Waypoint_index ' .. waypoint_index + 1 .. ' (waypoint #' .. waypoint_index + 2 .. ') callback in ' .. ticks .. ' ticks')
|
||||
|
||||
set_timeout_in_ticks(ticks, callback_function, {func = running_cutscene.func, player_index = player_index, waypoint_index = waypoint_index, params = params})
|
||||
end
|
||||
|
||||
function Public.goTo(player_index, waypoint_index)
|
||||
local running_cutscene = running_cutscenes[player_index]
|
||||
if waypoint_index < 0 or waypoint_index > #running_cutscene.waypoints - 2 then
|
||||
return false
|
||||
end
|
||||
Token.get(remove_renderings)(running_cutscene.rendering)
|
||||
game.get_player(player_index).jump_to_cutscene_waypoint(waypoint_index)
|
||||
handler({player_index = player_index, waypoint_index = waypoint_index - 1, tick = game.tick})
|
||||
running_cutscene.current_index = waypoint_index
|
||||
return true
|
||||
end
|
||||
|
||||
local function restore(event)
|
||||
Public.terminate_cutscene(event.player_index)
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_cutscene_waypoint_reached, handler)
|
||||
Event.add(defines.events.on_pre_player_left_game, restore)
|
||||
Event.add(defines.events.on_player_joined_game, restore)
|
||||
|
||||
local replay_cutscene =
|
||||
Token.register(
|
||||
function(params)
|
||||
Public.register_running_cutscene(params.event.player_index, replay.identifier, replay.final_transition_time)
|
||||
end
|
||||
)
|
||||
|
||||
local function replay_handler(_, player)
|
||||
if not replay.identifier then
|
||||
player.print({'cutscene_controller.cannot_replay'})
|
||||
return
|
||||
end
|
||||
Token.get(replay_cutscene)({event = {player_index = player.index}})
|
||||
end
|
||||
|
||||
Command.add(
|
||||
'replay',
|
||||
{
|
||||
description = {'cutscene_controller.replay'},
|
||||
capture_excess_arguments = false,
|
||||
allowed_by_server = false
|
||||
},
|
||||
replay_handler
|
||||
)
|
||||
|
||||
local function skip_cutscene(_, player)
|
||||
if not player or not player.valid then
|
||||
return
|
||||
end
|
||||
if player.controller_type == defines.controllers.cutscene then
|
||||
Public.terminate_cutscene(player.index)
|
||||
end
|
||||
end
|
||||
|
||||
Command.add(
|
||||
'skip',
|
||||
{
|
||||
description = {'cutscene_controller.skip'},
|
||||
capture_excess_arguments = false,
|
||||
allowed_by_server = false
|
||||
},
|
||||
skip_cutscene
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
skip_btn_name,
|
||||
function(event)
|
||||
skip_cutscene(nil, game.get_player(event.player_index))
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
backward_btn_name,
|
||||
function(event)
|
||||
local player_index = event.player_index
|
||||
if Public.goTo(player_index, running_cutscenes[player_index].current_index - 1) == false then
|
||||
game.get_player(player_index).print("Cutscene: You're already at the beginning")
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
forward_btn_name,
|
||||
function(event)
|
||||
local player_index = event.player_index
|
||||
if Public.goTo(event.player_index, running_cutscenes[player_index].current_index + 1) == false then
|
||||
game.get_player(player_index).print("Cutscene: You're already at the end")
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
341
features/cutscene/rendering.lua
Normal file
341
features/cutscene/rendering.lua
Normal file
@ -0,0 +1,341 @@
|
||||
local RS = require 'map_gen.shared.redmew_surface'
|
||||
local Debug = require 'utils.debug'
|
||||
local Rendering = require 'utils.rendering'
|
||||
local Vertices = require 'resources.vertices'
|
||||
local insert = table.insert
|
||||
|
||||
local toggle_debug = false --Set to true if you wish to get spammed with debug messages from the rendering module (Requires _DEBUG = true)
|
||||
|
||||
local function debug_print(message, trace_levels)
|
||||
if toggle_debug then
|
||||
Debug.print(message, trace_levels)
|
||||
end
|
||||
end
|
||||
|
||||
local Public = {}
|
||||
|
||||
--At zoom level 1 a tile is 32x32 pixels
|
||||
--tile size is calculated by 32 * zoom level.
|
||||
|
||||
local function text_height_in_tiles(scale, zoom)
|
||||
-- Default (count-font) at scale 10 is 125 pixels for lower letters and 109 for capital letters
|
||||
-- At scale 5 this is 64 or very close to half of the size at scale 10 (62.5)
|
||||
-- Therefore size hsa been determined to be (12.5 * scale) + 1
|
||||
local size = (12.5 * scale) + 1
|
||||
local pixel_per_tile = zoom * 32
|
||||
return size / pixel_per_tile, size
|
||||
end
|
||||
|
||||
local function calculate_percentages(settings, player_resolution)
|
||||
local original_resolution = settings.original_resolution
|
||||
return {
|
||||
height = player_resolution.height / original_resolution.height,
|
||||
width = player_resolution.width / original_resolution.width,
|
||||
tile = (settings.original_zoom * 32) / (settings.player_zoom * 32)
|
||||
}
|
||||
end
|
||||
|
||||
local function text_scale(percentage, scale)
|
||||
return scale * (percentage.height + percentage.width) * 0.5
|
||||
end
|
||||
|
||||
local function fit_to_screen(percentage, coordinates)
|
||||
if not coordinates.fitted then
|
||||
local height = percentage.height
|
||||
local width = percentage.width
|
||||
local tile = percentage.tile
|
||||
for _, pos in pairs(coordinates) do
|
||||
if type(pos) == 'number' then
|
||||
coordinates.x = coordinates.x * width
|
||||
coordinates.y = coordinates.y * height
|
||||
break
|
||||
else
|
||||
pos.x = pos.x * width
|
||||
pos.y = pos.y * height
|
||||
end
|
||||
end
|
||||
for _, pos in pairs(coordinates) do
|
||||
if type(pos) == 'number' then
|
||||
coordinates.x = coordinates.x * tile
|
||||
coordinates.y = coordinates.y * tile
|
||||
break
|
||||
else
|
||||
pos.x = pos.x * tile
|
||||
pos.y = pos.y * tile
|
||||
end
|
||||
end
|
||||
coordinates.fitted = true
|
||||
end
|
||||
return coordinates
|
||||
end
|
||||
|
||||
local function fit_to_screen_edges(settings, player_resolution, coordinates)
|
||||
if not coordinates.fitted then
|
||||
local tile = settings.original_zoom * 32
|
||||
local player_tile = settings.player_zoom * 32
|
||||
local display_scale = player_resolution.scale or 1
|
||||
|
||||
local player_height = (player_resolution.height / player_tile) * 0.5
|
||||
local player_width = (player_resolution.width / player_tile) * 0.5
|
||||
|
||||
for _, pos in pairs(coordinates) do
|
||||
if type(pos) == 'number' then
|
||||
coordinates.y = -player_height + (((coordinates.y * tile) / player_tile) * display_scale)
|
||||
coordinates.x = -player_width + (((coordinates.x * tile) / player_tile) * display_scale)
|
||||
break
|
||||
else
|
||||
pos.y = -player_height + (((pos.y * tile) / player_tile) * display_scale)
|
||||
pos.x = -player_width + (((pos.x * tile) / player_tile) * display_scale)
|
||||
end
|
||||
end
|
||||
coordinates.fitted = true
|
||||
end
|
||||
return coordinates
|
||||
end
|
||||
|
||||
local function create_background_params(params)
|
||||
local background_params = params.background
|
||||
if background_params then
|
||||
for k, v in pairs(params) do
|
||||
if k ~= 'background' then
|
||||
if not background_params[k] then
|
||||
background_params[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
background_params = params
|
||||
end
|
||||
return background_params
|
||||
end
|
||||
|
||||
local function text_background(settings, offset, player, percentages, size, number_text, params)
|
||||
local margin = 0.01 / params.scale
|
||||
local left_top = fit_to_screen(percentages, {x = -40, y = 0})
|
||||
local right_bottom = fit_to_screen(percentages, {x = 40, y = 0})
|
||||
left_top.y = -size * margin * 0.875
|
||||
right_bottom.y = size * (1.5 + (margin * 1.125)) * number_text
|
||||
local background_params = create_background_params(params)
|
||||
return Public.draw_rectangle(settings, offset, left_top, right_bottom, player, background_params)
|
||||
end
|
||||
|
||||
function Public.draw_text(settings, offset, text, player, params, draw_background, fit_to_edge)
|
||||
local ids = {}
|
||||
local player_resolution = player.display_resolution
|
||||
player_resolution.scale = player.display_scale
|
||||
local percentages = calculate_percentages(settings, player_resolution)
|
||||
local scale = params.scale
|
||||
|
||||
if draw_background ~= -1 then
|
||||
scale = text_scale(percentages, scale)
|
||||
local size = text_height_in_tiles(scale, settings.player_zoom)
|
||||
if fit_to_edge then
|
||||
offset = fit_to_screen_edges(settings, player_resolution, offset)
|
||||
else
|
||||
offset = fit_to_screen(percentages, offset)
|
||||
end
|
||||
offset.y = offset.y - size * 0.5
|
||||
end
|
||||
local size = text_height_in_tiles(scale, settings.player_zoom)
|
||||
|
||||
if draw_background == true then
|
||||
insert(ids, text_background(settings, offset, player, percentages, size, 1, params, fit_to_edge))
|
||||
end
|
||||
|
||||
local target = {x = player.position.x + offset.x, y = player.position.y + offset.y}
|
||||
|
||||
local color = params.color
|
||||
color = color and color or {r = 255, g = 255, b = 255}
|
||||
|
||||
local font = params.font
|
||||
|
||||
local surface = params.surface
|
||||
surface = surface or RS.get_surface()
|
||||
|
||||
local ttl = params.time_to_live
|
||||
ttl = ttl and ttl or -1
|
||||
|
||||
local forces = params.forces
|
||||
|
||||
local players = params.players
|
||||
players = players or {}
|
||||
|
||||
table.insert(players, player)
|
||||
|
||||
local visible = params.visible
|
||||
visible = visible or true
|
||||
|
||||
local dog = params.draw_on_ground
|
||||
dog = dog or false
|
||||
|
||||
local orientation = params.orientation
|
||||
orientation = orientation or 0
|
||||
|
||||
local alignment = params.alignment
|
||||
alignment = alignment or 'center'
|
||||
|
||||
--local swz = params.scale_with_zoom
|
||||
local swz = true
|
||||
|
||||
local oiam = params.only_in_alt_mode
|
||||
oiam = oiam or false
|
||||
|
||||
local rendering_params = {
|
||||
text = {'', text},
|
||||
color = color,
|
||||
target = target,
|
||||
scale_with_zoom = swz,
|
||||
surface = surface,
|
||||
time_to_live = ttl,
|
||||
alignment = alignment,
|
||||
players = players,
|
||||
scale = scale,
|
||||
forces = forces,
|
||||
visible = visible,
|
||||
draw_on_ground = dog,
|
||||
only_in_alt_mode = oiam,
|
||||
orientation = orientation,
|
||||
font = font
|
||||
}
|
||||
|
||||
debug_print(rendering_params)
|
||||
|
||||
insert(ids, rendering.draw_text(rendering_params))
|
||||
return ids
|
||||
end
|
||||
|
||||
function Public.draw_multi_line_text(settings, offset, texts, player, params, draw_background, fit_to_edge)
|
||||
local ids = {}
|
||||
local player_resolution = player.display_resolution
|
||||
player_resolution.scale = player.display_scale
|
||||
local percentages = calculate_percentages(settings, player_resolution)
|
||||
local scale = params.scale
|
||||
|
||||
scale = text_scale(percentages, scale)
|
||||
local size = text_height_in_tiles(scale, settings.player_zoom)
|
||||
|
||||
if fit_to_edge then
|
||||
offset = fit_to_screen_edges(settings, player_resolution, offset)
|
||||
else
|
||||
offset = fit_to_screen(percentages, offset)
|
||||
end
|
||||
|
||||
offset.y = offset.y - size * 0.5
|
||||
|
||||
if draw_background then
|
||||
insert(ids, text_background(settings, offset, player, percentages, size, #texts, params, fit_to_edge))
|
||||
draw_background = -1
|
||||
end
|
||||
|
||||
for i = 1, #texts do
|
||||
insert(ids, Public.draw_text(settings, offset, texts[i], player, params, draw_background, fit_to_edge)[1])
|
||||
offset.y = offset.y + (size * 1.5)
|
||||
end
|
||||
return ids
|
||||
end
|
||||
|
||||
function Public.draw_rectangle(settings, offset, left_top, right_bottom, player, params, fit_to_edge)
|
||||
local player_resolution = player.display_resolution
|
||||
player_resolution.scale = player.display_scale
|
||||
local percentages = calculate_percentages(settings, player_resolution)
|
||||
if fit_to_edge then
|
||||
offset = fit_to_screen_edges(settings, player_resolution, offset)
|
||||
left_top = fit_to_screen_edges(settings, player_resolution, left_top)
|
||||
right_bottom = fit_to_screen_edges(settings, player_resolution, right_bottom)
|
||||
else
|
||||
offset = fit_to_screen(percentages, offset)
|
||||
left_top = fit_to_screen(percentages, left_top)
|
||||
right_bottom = fit_to_screen(percentages, right_bottom)
|
||||
end
|
||||
|
||||
local target_left = {x = player.position.x + left_top.x + offset.x, y = player.position.y + left_top.y + offset.y}
|
||||
local target_right = {x = player.position.x + right_bottom.x + offset.x, y = player.position.y + right_bottom.y + offset.y}
|
||||
|
||||
local color = params.color
|
||||
color = color and color or {}
|
||||
|
||||
local width = params.width
|
||||
width = width and width or 0
|
||||
|
||||
local filled = params.filled
|
||||
filled = filled and filled or true
|
||||
|
||||
local surface = params.surface
|
||||
surface = surface or RS.get_surface()
|
||||
|
||||
local ttl = params.time_to_live
|
||||
ttl = ttl and ttl or -1
|
||||
|
||||
local forces = params.forces
|
||||
|
||||
local players = params.players
|
||||
players = players or {}
|
||||
|
||||
table.insert(players, player)
|
||||
|
||||
local visible = params.visible
|
||||
visible = visible or true
|
||||
|
||||
local dog = params.draw_on_ground
|
||||
dog = dog or false
|
||||
|
||||
local oiam = params.only_in_alt_mode
|
||||
oiam = oiam or false
|
||||
|
||||
local rendering_params = {
|
||||
color = color,
|
||||
width = width,
|
||||
filled = filled,
|
||||
left_top = target_left,
|
||||
right_bottom = target_right,
|
||||
surface = surface,
|
||||
time_to_live = ttl,
|
||||
forces = forces,
|
||||
players = players,
|
||||
visible = visible,
|
||||
draw_on_ground = dog,
|
||||
only_in_alt_mode = oiam
|
||||
}
|
||||
|
||||
debug_print(rendering_params)
|
||||
|
||||
return rendering.draw_rectangle(rendering_params)
|
||||
end
|
||||
|
||||
local blackout_settings = {original_resolution = {height = 1440, width = 2560}, original_zoom = 1, player_zoom = 1}
|
||||
function Public.blackout(player, zoom, ttl, color)
|
||||
local left_top = {x = -40, y = -22.5}
|
||||
local right_bottom = {x = 40, y = 22.5}
|
||||
blackout_settings.player_zoom = zoom
|
||||
return Public.draw_rectangle(blackout_settings, {x = 0, y = 0}, left_top, right_bottom, player, {color = color, time_to_live = ttl})
|
||||
end
|
||||
|
||||
function Public.draw_arrow(settings, offset, player, params, fit_to_edge)
|
||||
local player_resolution = player.display_resolution
|
||||
player_resolution.scale = player.display_scale
|
||||
local percentages = calculate_percentages(settings, player_resolution)
|
||||
if fit_to_edge then
|
||||
offset = fit_to_screen_edges(settings, player_resolution, offset)
|
||||
else
|
||||
offset = fit_to_screen(percentages, offset)
|
||||
end
|
||||
|
||||
local vertices = Rendering.scale(Vertices.arrow, percentages.tile, percentages.tile)
|
||||
vertices = Rendering.rotate(vertices, params.rotation)
|
||||
vertices = Rendering.translate(vertices, offset.x, offset.y)
|
||||
|
||||
local color = params.color or {1, 1, 1, 1}
|
||||
params.color = color
|
||||
|
||||
local players = params.players
|
||||
players = players or {}
|
||||
|
||||
table.insert(players, player)
|
||||
params.players = players
|
||||
|
||||
params.surface = RS.get_surface()
|
||||
--Debug.print(vertices)
|
||||
return Rendering.draw_polygon(vertices, params)
|
||||
end
|
||||
|
||||
return Public
|
213
features/gui/radio.lua
Normal file
213
features/gui/radio.lua
Normal file
@ -0,0 +1,213 @@
|
||||
local Gui = require 'utils.gui'
|
||||
local Command = require 'utils.command'
|
||||
local Event = require 'utils.event'
|
||||
|
||||
local main_button_name = Gui.uid_name()
|
||||
local radio_frame = Gui.uid_name()
|
||||
local close_radio = Gui.uid_name()
|
||||
|
||||
local sounds = {
|
||||
['ambient'] = {
|
||||
'after-the-crash',
|
||||
'automation',
|
||||
'resource-deficiency',
|
||||
'are-we-alone',
|
||||
'beyond-factory-outskirts',
|
||||
'censeqs-discrepancy',
|
||||
'efficiency-program',
|
||||
'expansion',
|
||||
'the-search-for-iron',
|
||||
'gathering-horizon',
|
||||
'research-and-minerals',
|
||||
'solar-intervention',
|
||||
'the-oil-industry',
|
||||
'the-right-tools',
|
||||
'pollution',
|
||||
'turbine-dynamics',
|
||||
'sentient',
|
||||
'anomaly',
|
||||
'first-light',
|
||||
'transmit',
|
||||
'swell-pad',
|
||||
'world-ambience-1',
|
||||
'world-ambience-2',
|
||||
'world-ambience-3',
|
||||
'world-ambience-4',
|
||||
'world-ambience-5',
|
||||
'world-ambience-6'
|
||||
},
|
||||
['default'] = {
|
||||
'worm-sends-biters',
|
||||
'mainframe-activated',
|
||||
'car-repaired'
|
||||
},
|
||||
['utility'] = {
|
||||
'achievement_unlocked',
|
||||
'alert_destroyed',
|
||||
'armor_insert',
|
||||
'armor_remove',
|
||||
'axe_fighting',
|
||||
'axe_mining_ore',
|
||||
'build_big',
|
||||
'build_medium',
|
||||
'build_small',
|
||||
'cannot_build',
|
||||
'console_message',
|
||||
'crafting_finished',
|
||||
'deconstruct_big',
|
||||
'deconstruct_medium',
|
||||
'deconstruct_small',
|
||||
'default_manual_repair',
|
||||
'game_lost',
|
||||
'game_won',
|
||||
'gui_click',
|
||||
'inventory_move',
|
||||
'list_box_click',
|
||||
'metal_walking_sound',
|
||||
'mining_wood',
|
||||
'new_objective',
|
||||
'research_completed',
|
||||
'scenario_message',
|
||||
'tutorial_notice',
|
||||
'wire_connect_pole',
|
||||
'wire_disconnect',
|
||||
'wire_pickup'
|
||||
}
|
||||
}
|
||||
|
||||
local function draw_radio(event)
|
||||
local frame_caption
|
||||
|
||||
frame_caption = 'Radio'
|
||||
local player = event.player
|
||||
local center = player.gui.center
|
||||
|
||||
local frame = center[radio_frame]
|
||||
if frame then
|
||||
Gui.remove_data_recursively(frame)
|
||||
frame.destroy()
|
||||
return
|
||||
end
|
||||
|
||||
frame = center.add {type = 'frame', name = radio_frame, caption = frame_caption, direction = 'vertical'}
|
||||
local scroll_pane =
|
||||
frame.add {
|
||||
type = 'scroll-pane',
|
||||
vertical_scroll_policy = 'auto-and-reserve-space',
|
||||
horizontal_scroll_policy = 'never'
|
||||
}
|
||||
Gui.set_data(scroll_pane, frame)
|
||||
|
||||
local main_table = scroll_pane.add {type = 'table', column_count = 4}
|
||||
|
||||
for type, sound in pairs(sounds) do
|
||||
for i = 1, #sound do
|
||||
local name = (type == 'default') and sound[i] or type .. '/' .. sound[i]
|
||||
local textbox = main_table.add {type = 'text-box', text = type .. '/' .. sound[i]}
|
||||
textbox.read_only = true
|
||||
textbox.style.height = 28
|
||||
textbox.style.width = 250
|
||||
local button = main_table.add {type = 'button', name = 'radio_play:' .. name, caption = 'Play'}
|
||||
button.style.width = 54
|
||||
end
|
||||
end
|
||||
|
||||
local information_pane =
|
||||
frame.add {
|
||||
type = 'scroll-pane',
|
||||
vertical_scroll_policy = 'auto-and-reserve-space',
|
||||
horizontal_scroll_policy = 'never'
|
||||
}
|
||||
information_pane.style.horizontally_stretchable = true
|
||||
information_pane.style.horizontal_align = 'center'
|
||||
Gui.set_data(information_pane, frame)
|
||||
|
||||
local text =
|
||||
[[
|
||||
Other types:
|
||||
syntax = <type>/<entity>
|
||||
"tile-walking" - for example "tile-walking/concrete"
|
||||
"tile-build"
|
||||
"tile-mined"
|
||||
"entity-build" - for example "entity-build/wooden-chest"
|
||||
"entity-mined"
|
||||
"entity-vehicle_impact"
|
||||
"entity-open"
|
||||
"entity-close"
|
||||
]]
|
||||
local information = information_pane.add {type = 'text-box', text = text}
|
||||
information.style.horizontally_stretchable = true
|
||||
information.style.vertically_stretchable = true
|
||||
information.style.minimal_height = 200
|
||||
information.style.minimal_width = 400
|
||||
|
||||
local bottom_flow = frame.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_flow = bottom_flow.add {type = 'flow', direction = 'horizontal'}
|
||||
left_flow.style.horizontal_align = 'left'
|
||||
left_flow.style.horizontally_stretchable = true
|
||||
|
||||
local close_button = left_flow.add {type = 'button', name = close_radio, caption = 'Close'}
|
||||
Gui.set_data(close_button, frame)
|
||||
|
||||
player.opened = frame
|
||||
end
|
||||
|
||||
Gui.on_click(main_button_name, draw_radio)
|
||||
|
||||
Gui.on_click(
|
||||
close_radio,
|
||||
function(event)
|
||||
local frame = Gui.get_data(event.element)
|
||||
|
||||
Gui.remove_data_recursively(frame)
|
||||
frame.destroy()
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_custom_close(
|
||||
radio_frame,
|
||||
function(event)
|
||||
local element = event.element
|
||||
Gui.remove_data_recursively(element)
|
||||
element.destroy()
|
||||
end
|
||||
)
|
||||
|
||||
local function radio_command(_, player)
|
||||
if player and player.valid then
|
||||
local event = {player = player}
|
||||
draw_radio(event)
|
||||
end
|
||||
end
|
||||
|
||||
Command.add(
|
||||
'radio',
|
||||
{
|
||||
description = 'Opens radio gui',
|
||||
capture_excess_arguments = false,
|
||||
allowed_by_server = false
|
||||
},
|
||||
radio_command
|
||||
)
|
||||
|
||||
local function handler(event)
|
||||
local element = event.element
|
||||
if not element or not element.valid then
|
||||
return
|
||||
end
|
||||
local name = element.name
|
||||
local subname = string.sub(name, 1, 11)
|
||||
if subname == 'radio_play:' then
|
||||
local path = string.sub(name, 12)
|
||||
local player = game.get_player(event.player_index)
|
||||
if (game.is_valid_sound_path(path)) then
|
||||
player.play_sound {path = path}
|
||||
return
|
||||
else
|
||||
player.print('Unable to play sound: ' .. path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_gui_click, handler)
|
@ -6,25 +6,10 @@ local pairs = pairs
|
||||
|
||||
local get_random_weighted = table.get_random_weighted
|
||||
|
||||
local function player_created(event)
|
||||
local Public = {}
|
||||
|
||||
function Public.show_start_up(player)
|
||||
local config = global.config.player_create
|
||||
local player = game.get_player(event.player_index)
|
||||
|
||||
if not player or not player.valid then
|
||||
return
|
||||
end
|
||||
|
||||
-- ensure the top menu is correctly styled
|
||||
local gui = player.gui
|
||||
gui.top.style = 'slot_table_spacing_horizontal_flow'
|
||||
gui.left.style = 'slot_table_spacing_vertical_flow'
|
||||
|
||||
local player_insert = player.insert
|
||||
|
||||
for _, item in pairs(config.starting_items) do
|
||||
player_insert(item)
|
||||
end
|
||||
|
||||
local p = player.print
|
||||
for _, message in pairs(config.join_messages) do
|
||||
p(message)
|
||||
@ -48,12 +33,29 @@ local function player_created(event)
|
||||
elseif not _DEBUG and not game.is_multiplayer() then
|
||||
player.print('To change your name in single-player, open chat and type the following /c game.player.name = "your_name"')
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove 2019-03-04
|
||||
if player.admin then
|
||||
player.print('## - Regular commands have changed. /regular <player_name> to promote /regular-remove <player_name> to demote.')
|
||||
local function player_created(event)
|
||||
local config = global.config.player_create
|
||||
local player = game.get_player(event.player_index)
|
||||
|
||||
if not player or not player.valid then
|
||||
return
|
||||
end
|
||||
|
||||
-- ensure the top menu is correctly styled
|
||||
local gui = player.gui
|
||||
gui.top.style = 'slot_table_spacing_horizontal_flow'
|
||||
gui.left.style = 'slot_table_spacing_vertical_flow'
|
||||
|
||||
local player_insert = player.insert
|
||||
|
||||
for _, item in pairs(config.starting_items) do
|
||||
player_insert(item)
|
||||
end
|
||||
if not config.cutscene then
|
||||
Public.show_start_up(player)
|
||||
end
|
||||
--End remove
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_created, player_created)
|
||||
@ -125,3 +127,5 @@ if _CHEATS then
|
||||
|
||||
Event.add(defines.events.on_player_created, player_created_cheat_mode)
|
||||
end
|
||||
|
||||
return Public
|
||||
|
@ -20,7 +20,7 @@ show_rail_block=Toggles rail block visualisation.
|
||||
server_time=Prints the server's time.
|
||||
seeds=List the seeds of all surfaces
|
||||
redmew_version=Prints the version of the Redmew scenario
|
||||
whois=provides information about a given player, admins can see the inventory of a player by adding "yes" as a second argument
|
||||
whois=Provides information about a given player, admins can see the inventory of a player by adding "yes" as a second argument
|
||||
meltdown_get=Gets the status of meltdown.
|
||||
meltdown_set=Sets the status of meltdown.
|
||||
redmew_color=Set will save your current color for future maps. Reset will erase your saved color. Random will give you a random color.
|
||||
|
@ -147,6 +147,12 @@ player_distance_walked=Distance walked
|
||||
satellites_launched=Satellites launched
|
||||
unknown_death_cause=Unknown
|
||||
|
||||
[cutscene_controller]
|
||||
cannot_start_new=Can not start cutscene, you need to finish your current one. Try to run /skip before trying again
|
||||
skip=Skips the current cutscene
|
||||
replay=Replays the introduction cutscene
|
||||
cannot_replay=There is no cutscene to replay
|
||||
|
||||
[autofill]
|
||||
insert_item=-__1__ __2__ (__3__)
|
||||
enable=Enable autofill
|
||||
|
@ -43,6 +43,28 @@ score_cave_collapses=Cave collapses
|
||||
score_mine_size=Mine size
|
||||
score_experience_lost=Experience lost
|
||||
|
||||
cutscene_case_line2=Welcome to __1__
|
||||
cutscene_case_line4=This is a custom scenario developed by __1__
|
||||
cutscene_case_line5=Join us at __1__
|
||||
cutscene_case_line6=The following introduction will help you get started!
|
||||
cutscene_case_line7=If you experience issues duing this cutscene let us know
|
||||
cutscene_case0_line1=This is the starting area
|
||||
cutscene_case0_line3=Expanding the mine is dangerous!
|
||||
cutscene_case1_line1=Walls are used to keep the cave roof from crushing us
|
||||
cutscene_case2_line1=The market provides extra supplies in exchange for coins
|
||||
cutscene_case2_line3=You unlock new items when you level up
|
||||
cutscene_case3_line1=Cave ins happens frequently when you don't add supports
|
||||
cutscene_case3_line3=Different types of brick and concrete can reinforce our support pillars!
|
||||
cutscene_case4_line1=This world contains brittle rocks
|
||||
cutscene_case4_line3=Our tools are too powerful to preserve any resources from destroying them
|
||||
cutscene_case5_line1=Most actions give experience!
|
||||
cutscene_case5_line3=The floating text indicates the quantity and cause of the experience
|
||||
cutscene_case6_line1=The native population is lurking in the dark
|
||||
cutscene_case6_line3=Be wary when digging, always bring along some defences
|
||||
cutscene_case7_line1=This concludes the introduction
|
||||
cutscene_case7_line3=Have fun and keep digging!
|
||||
replay_cutscene=To replay the introduction, use the __1__ command
|
||||
|
||||
# locale linked to the quadrants scenario
|
||||
[quadrants]
|
||||
on=ON
|
||||
|
@ -49,6 +49,10 @@ local Config = {
|
||||
starting_items = {}
|
||||
}
|
||||
},
|
||||
-- controls the introduction cutscene
|
||||
cutscene = {
|
||||
enabled = true
|
||||
},
|
||||
-- core feature
|
||||
diggy_hole = {
|
||||
enabled = true,
|
||||
|
305
map_gen/maps/diggy/feature/cutscene.lua
Normal file
305
map_gen/maps/diggy/feature/cutscene.lua
Normal file
@ -0,0 +1,305 @@
|
||||
local Event = require 'utils.event'
|
||||
local Token = require 'utils.token'
|
||||
local Task = require 'utils.task'
|
||||
local Debug = require 'utils.debug'
|
||||
local Cutscene = require 'features.cutscene.cutscene_controller'
|
||||
local CS_Rendering = require 'features.cutscene.rendering'
|
||||
local RS = require 'map_gen.shared.redmew_surface'
|
||||
local Color = require 'resources.color_presets'
|
||||
local PC = require 'features.player_create'
|
||||
local Experience = require 'map_gen.maps.diggy.feature.experience'
|
||||
local register_rendering = Cutscene.register_rendering_id
|
||||
local play_sound = Cutscene.play_sound
|
||||
local draw_text = CS_Rendering.draw_text
|
||||
local draw_multi_line = CS_Rendering.draw_multi_line_text
|
||||
local rad = math.rad
|
||||
local Rendering = require 'utils.rendering'
|
||||
|
||||
local DiggyCutscene = {}
|
||||
|
||||
local delayed_draw_text =
|
||||
Token.register(
|
||||
function(params)
|
||||
local tick = params.tick
|
||||
local player = params.player
|
||||
if params.play_sound > 1 then
|
||||
play_sound(tick, player, 'utility/list_box_click', 1)
|
||||
end
|
||||
register_rendering(player.index, tick, draw_text(params.settings, params.offset, params.text, params.player, params.params, params.draw_background, params.fit_to_edge))
|
||||
end
|
||||
)
|
||||
|
||||
local function draw_text_auto_replacing(tick, settings, offset, texts, player, params, draw_background, fit_to_edge, time, between_time)
|
||||
time = time or 400
|
||||
time = time / #texts
|
||||
between_time = between_time or 30
|
||||
params.time_to_live = time - between_time
|
||||
if params.background then
|
||||
params.background.time_to_live = time - between_time
|
||||
end
|
||||
for i = 1, #texts do
|
||||
if texts[i] ~= '' then
|
||||
Task.set_timeout_in_ticks(time * (i - 1), delayed_draw_text, {tick = tick, settings = settings, offset = offset, text = texts[i], player = player, params = params, draw_background = draw_background, fit_to_edge = fit_to_edge, play_sound = i})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local delayed_draw_arrow =
|
||||
Token.register(
|
||||
function(params)
|
||||
local player = params.player
|
||||
local tick = params.tick
|
||||
params = params.params
|
||||
local rendering_parmas = params.params
|
||||
local id = CS_Rendering.draw_arrow(params.settings, params.offset, player, rendering_parmas, params.fit_to_edge)
|
||||
register_rendering(player.index, tick, id)
|
||||
Rendering.blink(id, 20, rendering_parmas.time_to_live)
|
||||
end
|
||||
)
|
||||
|
||||
local function delayed_function(func, player, tick, params, offset_time)
|
||||
Task.set_timeout_in_ticks(offset_time, func, {player = player, tick = tick, params = params})
|
||||
end
|
||||
|
||||
local delayed_fade_blackout =
|
||||
Token.register(
|
||||
function(params)
|
||||
local render_params = params.params
|
||||
local id = CS_Rendering.blackout(params.player, render_params.zoom, render_params.time_to_live, render_params.color)
|
||||
register_rendering(params.player.index, params.tick, id)
|
||||
Rendering.fade(id, render_params.time_to_live - 1, 10)
|
||||
end
|
||||
)
|
||||
|
||||
local original_resolution = {height = 1440, width = 2560}
|
||||
local original_zoom = 1
|
||||
|
||||
local function cutscene_function(player_index, waypoint_index, params)
|
||||
local cases = {}
|
||||
|
||||
local player = game.players[player_index]
|
||||
local ttw = params.time_to_wait
|
||||
local zoom = params.zoom
|
||||
local tick = params.tick
|
||||
local settings = {original_resolution = original_resolution, original_zoom = original_zoom, player_zoom = zoom}
|
||||
|
||||
if waypoint_index ~= -1 then
|
||||
play_sound(tick, player, 'utility/list_box_click', 1)
|
||||
--play_sound(tick, player, 'utility/inventory_move', 1, 10)
|
||||
end
|
||||
cases[-1] = function()
|
||||
play_sound(tick, player, 'utility/game_won')
|
||||
play_sound(tick, player, 'ambient/first-light', 1, 550)
|
||||
register_rendering(player_index, tick, CS_Rendering.blackout(player, zoom, ttw + 1))
|
||||
register_rendering(player_index, tick, draw_text(settings, {x = 0, y = -16}, 'Diggy', player, {scale = 10, time_to_live = ttw, color = Color.yellow}, false, false))
|
||||
register_rendering(
|
||||
player_index,
|
||||
tick,
|
||||
draw_multi_line(settings, {x = 0, y = -5}, {{'diggy.cutscene_case_line2', 'Diggy'}, '---------------------', {'diggy.cutscene_case_line4', 'Redmew'}, {'diggy.cutscene_case_line5', 'www.redmew.com/discord'}}, player, {scale = 5, time_to_live = ttw}, false)
|
||||
)
|
||||
draw_text_auto_replacing(tick, settings, {x = 0, y = 10}, {'', {'diggy.cutscene_case_line6'}}, player, {scale = 3}, false, false, ttw, 0)
|
||||
draw_text_auto_replacing(tick, settings, {x = 0, y = 16}, {'', '', {'diggy.cutscene_case_line7'}}, player, {scale = 1}, false, false, ttw, 0)
|
||||
end
|
||||
cases[0] = function()
|
||||
register_rendering(player_index, tick, CS_Rendering.blackout(player, zoom, ttw + 1))
|
||||
register_rendering(player_index, tick, draw_text(settings, {x = 0, y = 0}, 'Redmew - Diggy', player, {scale = 10, time_to_live = ttw - 60, color = Color.red}, false, false))
|
||||
register_rendering(player_index, tick, draw_text(settings, {x = 0, y = -5}, 'Introduction', player, {scale = 5, time_to_live = ttw - 60}, false, false))
|
||||
|
||||
delayed_function(delayed_draw_arrow, player, tick, {settings = settings, offset = {x = 7, y = 2.5}, params = {rotation = rad(-45), time_to_live = 275 * 3 - 30}, fit_to_edge = true}, 0)
|
||||
|
||||
draw_text_auto_replacing(tick, settings, {x = 8.5, y = 3}, {'This is our toolbar!'}, player, {scale = 2.5, alignment = 'left'}, false, true, 275)
|
||||
|
||||
draw_text_auto_replacing(tick, settings, {x = 8.5, y = 3}, {'', "Here you'll find a wide range of tools and informations about us!"}, player, {scale = 2.5, alignment = 'left'}, false, true, 275 * 2)
|
||||
|
||||
draw_text_auto_replacing(tick, settings, {x = 8.5, y = 3}, {'', '', 'Hover your mouse over them for more information'}, player, {scale = 2.5, alignment = 'left'}, false, true, 275 * 3)
|
||||
|
||||
delayed_function(delayed_draw_arrow, player, tick, {settings = settings, offset = {x = 1, y = 2.5}, params = {rotation = rad(-45), time_to_live = 275 - 30}, fit_to_edge = true}, 275 * 3)
|
||||
|
||||
draw_text_auto_replacing(tick, settings, {x = 2.5, y = 3}, {'', '', '', 'You can toggle our toolbar with this button'}, player, {scale = 2.5, alignment = 'left'}, false, true, 275 * 4)
|
||||
|
||||
delayed_function(delayed_draw_arrow, player, tick, {settings = settings, offset = {x = 3.5, y = 2.5}, params = {rotation = rad(-45), time_to_live = 275 - 30}, fit_to_edge = true}, 275 * 4.5)
|
||||
|
||||
draw_text_auto_replacing(tick, settings, {x = 5, y = 3}, {'', '', '', '', 'This is the Diggy experience menu'}, player, {scale = 2.5, alignment = 'left'}, false, true, 275 * 5.5)
|
||||
|
||||
delayed_function(delayed_draw_arrow, player, tick, {settings = settings, offset = {x = 15, y = 9}, params = {rotation = rad(-90), time_to_live = 275 - 30}, fit_to_edge = true}, 275 * 5.5)
|
||||
|
||||
Cutscene.toggle_gui(tick, player, Experience, 275 * 5.5, 'left')
|
||||
|
||||
draw_text_auto_replacing(tick, settings, {x = 17, y = 8.7}, {'', '', '', '', '', 'Here you can see the current progress of the mine'}, player, {scale = 2.5, alignment = 'left'}, false, true, 275 * 6.5)
|
||||
|
||||
Cutscene.toggle_gui(tick, player, Experience, 275 * 6.5)
|
||||
|
||||
delayed_function(delayed_fade_blackout, player, tick, {zoom = zoom, time_to_live = 120 + 61, color = {0, 0, 0, 1}}, ttw - 61)
|
||||
end
|
||||
cases[1] = function()
|
||||
draw_text_auto_replacing(tick, settings, {x = 0, y = 18}, {{'diggy.cutscene_case0_line1'}, {'diggy.cutscene_case0_line3'}}, player, {scale = 2.5}, true, false, ttw)
|
||||
local entity = RS.get_surface().find_entities_filtered {position = {0, 0}, radius = 20, name = 'stone-wall', limit = 1}
|
||||
if entity[1] then
|
||||
local position = entity[1].position
|
||||
local waypoint = {
|
||||
-- case 2
|
||||
position = position,
|
||||
transition_time = 120,
|
||||
time_to_wait = 275,
|
||||
zoom = 5
|
||||
}
|
||||
Debug.print_position(position, 'position of wall')
|
||||
Cutscene.inject_waypoint(player_index, waypoint, waypoint_index + 3, true)
|
||||
end
|
||||
end
|
||||
cases[2] = function()
|
||||
--play_sound(tick, player, 'utility/build_small', 1, 25)
|
||||
draw_text_auto_replacing(tick, settings, {x = 0, y = 18}, {{'diggy.cutscene_case1_line1'}}, player, {scale = 2.5}, true, false, ttw)
|
||||
end
|
||||
cases[3] = function()
|
||||
draw_text_auto_replacing(tick, settings, {x = 0, y = 18}, {{'diggy.cutscene_case2_line1'}, {'diggy.cutscene_case2_line3'}}, player, {scale = 2.5}, true, false, ttw)
|
||||
end
|
||||
cases[4] = function()
|
||||
draw_text_auto_replacing(tick, settings, {x = 0, y = 18}, {{'diggy.cutscene_case3_line1'}, {'diggy.cutscene_case3_line3'}}, player, {scale = 2.5}, true, false, ttw)
|
||||
local radius = 10
|
||||
local entity
|
||||
repeat
|
||||
entity = RS.get_surface().find_entities_filtered {position = {0, 0}, radius = radius, name = 'rock-big', limit = 1}
|
||||
if radius <= 10 then
|
||||
radius = 0
|
||||
end
|
||||
radius = radius + 25
|
||||
until entity[1] or radius >= 200
|
||||
local position = {0, 3.5}
|
||||
local way_zoom = 0.4
|
||||
entity = entity[1]
|
||||
if entity then
|
||||
position = entity.position
|
||||
way_zoom = 5
|
||||
Debug.print_position(position, 'position of rock')
|
||||
end
|
||||
local waypoint = {
|
||||
-- case 5
|
||||
position = position,
|
||||
transition_time = 120,
|
||||
time_to_wait = 550,
|
||||
zoom = way_zoom
|
||||
}
|
||||
Cutscene.inject_waypoint(player_index, waypoint, waypoint_index + 3)
|
||||
end
|
||||
cases[5] = function()
|
||||
play_sound(waypoint_index, player, 'utility/axe_mining_ore', 3, 35)
|
||||
draw_text_auto_replacing(tick, settings, {x = 0, y = 18}, {{'diggy.cutscene_case4_line1'}, {'diggy.cutscene_case4_line3'}}, player, {scale = 2.5}, true, false, ttw)
|
||||
end
|
||||
cases[6] = function()
|
||||
play_sound(tick, player, 'utility/research_completed', 1, 5)
|
||||
local exp = 2500
|
||||
local text = {'', '[img=item/automation-science-pack] ', {'diggy.float_xp_gained_research', exp}}
|
||||
player.create_local_flying_text {position = params.position, text = text, color = Color.light_sky_blue, time_to_live = ttw / 3}
|
||||
draw_text_auto_replacing(tick, settings, {x = 0, y = 18}, {{'diggy.cutscene_case5_line1'}, {'diggy.cutscene_case5_line3'}}, player, {scale = 2.5}, true, false, ttw)
|
||||
end
|
||||
cases[7] = function()
|
||||
play_sound(tick, player, 'utility/axe_fighting', 5, 25, 10)
|
||||
play_sound(tick, player, 'worm-sends-biters', 1, 70)
|
||||
draw_text_auto_replacing(tick, settings, {x = 0, y = 18}, {{'diggy.cutscene_case6_line1'}, {'diggy.cutscene_case6_line3'}}, player, {scale = 2.5}, true, false, ttw)
|
||||
end
|
||||
cases[8] = function()
|
||||
draw_text_auto_replacing(tick, settings, {x = 0, y = 18}, {{'diggy.cutscene_case7_line1'}, {'diggy.cutscene_case7_line3'}}, player, {scale = 2.5}, true, false, ttw)
|
||||
--play_sound(tick, player, 'utility/tutorial_notice', 1)
|
||||
end
|
||||
local case = cases[waypoint_index]
|
||||
if case then
|
||||
case()
|
||||
end
|
||||
end
|
||||
|
||||
local waypoints = {
|
||||
{
|
||||
-- case -1
|
||||
position = {x = 0, y = 0},
|
||||
transition_time = 60,
|
||||
time_to_wait = 600,
|
||||
zoom = 0.5
|
||||
},
|
||||
{
|
||||
-- case -1.1
|
||||
position = {x = 0, y = 0},
|
||||
transition_time = 0,
|
||||
time_to_wait = 275 * 7,
|
||||
zoom = 0.5
|
||||
},
|
||||
{
|
||||
-- case 0
|
||||
position = {x = 0, y = 0},
|
||||
transition_time = 120,
|
||||
time_to_wait = 550,
|
||||
zoom = 1
|
||||
},
|
||||
{
|
||||
-- case 1
|
||||
position = {x = 0, y = 0},
|
||||
transition_time = 120,
|
||||
time_to_wait = 275,
|
||||
zoom = 1.5
|
||||
},
|
||||
{
|
||||
-- case 2
|
||||
position = {x = 0.5, y = 3.5},
|
||||
transition_time = 120,
|
||||
time_to_wait = 550,
|
||||
zoom = 5
|
||||
},
|
||||
{
|
||||
-- case 3
|
||||
position = {x = 0, y = 0},
|
||||
transition_time = 120,
|
||||
time_to_wait = 550,
|
||||
zoom = 2
|
||||
},
|
||||
-- inserting case 4
|
||||
{
|
||||
-- case 5
|
||||
position = {x = 0, y = -2},
|
||||
transition_time = 120,
|
||||
time_to_wait = 550,
|
||||
zoom = 1.8
|
||||
},
|
||||
{
|
||||
-- case 6
|
||||
position = {x = 0, y = 0},
|
||||
transition_time = 120,
|
||||
time_to_wait = 550,
|
||||
zoom = 0.3
|
||||
},
|
||||
{
|
||||
-- case 7
|
||||
position = {x = 0, y = 0},
|
||||
transition_time = 120,
|
||||
time_to_wait = 430,
|
||||
zoom = 0.8
|
||||
}
|
||||
}
|
||||
|
||||
local function terminate_function(player_index)
|
||||
local player = game.get_player(player_index)
|
||||
PC.show_start_up(player)
|
||||
player.print({'diggy.replay_cutscene', '/replay'}, Color.yellow)
|
||||
end
|
||||
|
||||
Cutscene.register_cutscene_function('Diggy_Welcome', waypoints, Token.register(cutscene_function), Token.register(terminate_function))
|
||||
Cutscene.register_replay('Diggy_Welcome', 120)
|
||||
|
||||
local start_cutscene =
|
||||
Token.register(
|
||||
function(params)
|
||||
Cutscene.register_running_cutscene(params.event.player_index, 'Diggy_Welcome', 120)
|
||||
end
|
||||
)
|
||||
|
||||
function DiggyCutscene.register()
|
||||
global.config.player_create.cutscene = true
|
||||
|
||||
Event.add(
|
||||
defines.events.on_player_created,
|
||||
function(event)
|
||||
Task.set_timeout_in_ticks(60, start_cutscene, {event = event})
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
return DiggyCutscene
|
@ -489,7 +489,7 @@ local function redraw_buff(data)
|
||||
end
|
||||
end
|
||||
|
||||
local function toggle(event)
|
||||
function Experience.toggle(event)
|
||||
local player = event.player
|
||||
local gui = player.gui
|
||||
local left = gui.left
|
||||
@ -559,7 +559,7 @@ end
|
||||
|
||||
Gui.allow_player_to_toggle_top_element_visibility('Diggy.Experience.Button')
|
||||
|
||||
Gui.on_click('Diggy.Experience.Button', toggle)
|
||||
Gui.on_click('Diggy.Experience.Button', Experience.toggle)
|
||||
Gui.on_custom_close(
|
||||
'Diggy.Experience.Frame',
|
||||
function(event)
|
||||
@ -576,7 +576,7 @@ local function update_gui()
|
||||
|
||||
if frame and frame.valid then
|
||||
local data = {player = p, trigger = 'update_gui'}
|
||||
toggle(data)
|
||||
Experience.toggle(data)
|
||||
end
|
||||
end
|
||||
|
||||
|
16
resources/vertices.lua
Normal file
16
resources/vertices.lua
Normal file
@ -0,0 +1,16 @@
|
||||
local shapes = {
|
||||
arrow_point = {
|
||||
--triangle
|
||||
{1, 1}, -- right edge g
|
||||
{-1, 1}, -- left edge b
|
||||
{0, 0}, -- top (pointer) a
|
||||
--rectangle
|
||||
{0.5, 1}, -- right inner top f
|
||||
{0.5, 2}, -- right inner bottom e
|
||||
{-0.5, 1}, -- left inner top c
|
||||
{-0.5, 2} -- left inner bottom d
|
||||
},
|
||||
arrow = {{1, 0}, {-1, 0}, {0, -1}, {0.5, 0}, {0.5, 1}, {-0.5, 0}, {-0.5, 1}}
|
||||
}
|
||||
|
||||
return shapes
|
114
utils/rendering.lua
Normal file
114
utils/rendering.lua
Normal file
@ -0,0 +1,114 @@
|
||||
local Token = require 'utils.token'
|
||||
local Task = require 'utils.task'
|
||||
|
||||
local Public = {}
|
||||
|
||||
local cos = math.cos
|
||||
local sin = math.sin
|
||||
|
||||
local rendering = rendering
|
||||
local draw_polygon = rendering.draw_polygon
|
||||
|
||||
function Public.draw_polygon(positions, options)
|
||||
local vertices = {}
|
||||
|
||||
for i = 1, #positions do
|
||||
vertices[i] = {target = positions[i]}
|
||||
end
|
||||
|
||||
local args = {vertices = vertices}
|
||||
for k, v in pairs(options) do
|
||||
args[k] = v
|
||||
end
|
||||
|
||||
return draw_polygon(args)
|
||||
end
|
||||
|
||||
function Public.translate(positions, x, y)
|
||||
local result = {}
|
||||
|
||||
for i = 1, #positions do
|
||||
local pos = positions[i]
|
||||
result[i] = {pos[1] + x, pos[2] + y}
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function Public.scale(positions, x, y)
|
||||
local result = {}
|
||||
|
||||
for i = 1, #positions do
|
||||
local pos = positions[i]
|
||||
result[i] = {pos[1] * x, pos[2] * y}
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function Public.rotate(positions, radians)
|
||||
local qx = cos(radians)
|
||||
local qy = sin(radians)
|
||||
|
||||
local result = {}
|
||||
|
||||
for i = 1, #positions do
|
||||
local pos = positions[i]
|
||||
local x, y = pos[1], pos[2]
|
||||
local rot_x = qx * x - qy * y
|
||||
local rot_y = qy * x + qx * y
|
||||
|
||||
result[i] = {rot_x, rot_y}
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
local fade_token =
|
||||
Token.register(
|
||||
function(params)
|
||||
local id = params.id
|
||||
if rendering.is_valid(id) then
|
||||
rendering.set_color(id, params.color)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
function Public.fade(id, time, ticks)
|
||||
ticks = ticks or 20
|
||||
local count = (time - time % ticks) / ticks
|
||||
if rendering.is_valid(id) then
|
||||
local color = rendering.get_color(id)
|
||||
local a = color.a or 1
|
||||
local decrement = a / count
|
||||
for i = 1, count do
|
||||
a = a - decrement
|
||||
a = a >= 0 and a or 0
|
||||
Task.set_timeout_in_ticks(ticks * i, fade_token, {id = id, color = {r = color.r, b = color.b, g = color.g, a = a}})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local blink_token =
|
||||
Token.register(
|
||||
function(params)
|
||||
local id = params.id
|
||||
if rendering.is_valid(id) then
|
||||
rendering.set_visible(id, params.visible)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
function Public.blink(id, rate, time)
|
||||
local count = (time - time % rate) / rate
|
||||
rate = (time / count) * 2
|
||||
if rendering.is_valid(id) then
|
||||
local visible = rendering.get_visible(id)
|
||||
for i = 1, count do
|
||||
visible = not visible
|
||||
Task.set_timeout_in_ticks(rate * i, blink_token, {id = id, visible = visible})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Public
|
Loading…
x
Reference in New Issue
Block a user