1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2025-03-03 14:53:01 +02:00

merge develop

This commit is contained in:
grilledham 2018-11-25 11:54:06 +00:00
commit 3768a6a3ca
30 changed files with 1284 additions and 804 deletions

View File

@ -56,7 +56,7 @@ local STD_BASE_CONTROL = 'lua52c+factorio+factorio_control+factorio_defines+fact
--[Assume Factorio Control stage as default]--
-------------------------------------------------------------------------------
std = STD_CONTROL
globals = {'math', 'table', '_DEBUG', 'MARKET_ITEM', 'newline'} -- RedMew-specific globals
globals = {'math', 'table', '_DEBUG', '_CHEATS', 'MARKET_ITEM', 'newline'} -- RedMew-specific globals
max_line_length = LINE_LENGTH
not_globals = NOT_GLOBALS

View File

@ -5,15 +5,24 @@ All notable changes to this project will be documented in this file.
## Next version
### Features
- [Core] Change /find-player to /find #417
- [Core] Using `/regular <name>` now defaults to promoting the player. #417
- [Diggy] Added new formula to calculate experience requirement for next level #402
- [Diggy] Added health bonus on level up #402
- [Diggy] Added new level system to replace stone sent to surface #402
- [Diggy] Mining rocks no longer gives stone or coal #402
- [Diggy] Added particles when a biter mines a rock upon spawning #424
- [Diggy] Added bot mining #434
- [Map] Add Tetris map #433
- [Map] Add World Thanksgiving map #433
- [Diggy] Added bot mining levels and rock mining/destruction particles #442
### Bugfixes
- [Diggy] Stones killed by damage no longer spill. #395
- [Core] Fix /kill non-functional if walkabout is disabled. Fix walkabout giving from variable definition. #425
- [Diggy] Improved biter spawning algorithm #408
- [Core] Fix null reference in chat_triggers #431
- [Core] Fix nil ref in train_station_names #441
### Internal
- [Core] Cleanup of code formatting. #413 #415 #414 #412 #411

View File

@ -1,4 +1,5 @@
_DEBUG = false
_CHEATS = false
MARKET_ITEM = 'coin'
global.scenario = {}
@ -18,6 +19,8 @@ global.scenario.config.nuke_control.enable_autokick = true
global.scenario.config.nuke_control.enable_autoban = true
global.scenario.custom_functions = {}
global.scenario.config.nuke_control.nuke_min_time_hours = 3 --how long a player must be on the server to be allowed to use the nuke
global.scenario.config.players_assigned_names = false -- assigns players random names when they first join
global.scenario.config.players_roll_names = false -- allows players to roll random names
global.newline = '\n'
newline = '\n'

View File

@ -8,7 +8,6 @@ require 'map_gen.shared.perlin_noise'
require 'map_layout'
-- Specific to RedMew hosts, can be disabled safely if not hosting on RedMew servers
require 'features.bot'
Server = require 'server'
ServerCommands = require 'server_commands'

View File

@ -1,59 +0,0 @@
local Event = require 'utils.event'
local Game = require 'utils.game'
Event.add(
defines.events.on_player_died,
function(event)
local player = event.player_index
if Game.get_player_by_index(player).name ~= nil then
print('PLAYER$die,' .. player .. ',' .. Game.get_player_by_index(player).name .. ',' .. Game.get_player_by_index(player).force.name)
end
end
)
Event.add(
defines.events.on_player_respawned,
function(event)
local player = event.player_index
if Game.get_player_by_index(player).name ~= nil then
print('PLAYER$respawn,' .. player .. ',' .. Game.get_player_by_index(player).name .. ',' .. Game.get_player_by_index(player).force.name)
end
end
)
Event.add(
defines.events.on_player_joined_game,
function(event)
local player = event.player_index
if Game.get_player_by_index(player).name ~= nil then
print('PLAYER$join,' .. player .. ',' .. Game.get_player_by_index(player).name .. ',' .. Game.get_player_by_index(player).force.name)
end
end
)
Event.add(
defines.events.on_player_left_game,
function(event)
local player = event.player_index
if Game.get_player_by_index(player).name ~= nil then
print('PLAYER$leave,' .. player .. ',' .. Game.get_player_by_index(player).name .. ',' .. Game.get_player_by_index(player).force.name)
end
end
)
function heartbeat()
--Do nothing, this is just so managepgm can call something as a heartbeat without any errors occurring
end
function playerQuery()
if #game.connected_players == 0 then
print('output$pquery$none')
else
local response = 'output&pquery$'
for _, player in pairs(game.connected_players) do
local playerdata = player.name .. '-' .. player.force.name
response = response .. playerdata .. ','
end
print(response:sub(1, #str - 1))
end
end

View File

@ -0,0 +1,37 @@
local random = math.random
local CreateParticles = {}
---@param create_entity function a reference to a surface.create_entity
---@param particle_count number particle count to spawn
---@param position Position
function CreateParticles.destroy_rock(create_entity, particle_count, position)
for _ = particle_count, 1, -1 do
create_entity({
position = position,
name = 'stone-particle',
movement = {random(-5, 5) * 0.01, random(-5, 5) * 0.01},
frame_speed = 1,
vertical_speed = random(12, 14) * 0.01,
height = random(9, 11) * 0.1,
})
end
end
---@param create_entity function a reference to a surface.create_entity
---@param particle_count number particle count to spawn
---@param position Position
function CreateParticles.mine_rock(create_entity, particle_count, position)
for _ = particle_count, 1, -1 do
create_entity({
position = position,
name = 'stone-particle',
movement = {random(-5, 5) * 0.01, random(-5, 5) * 0.01},
frame_speed = 1,
vertical_speed = random(8, 10) * 0.01,
height = random(5, 8) * 0.1,
})
end
end
return CreateParticles

View File

@ -22,7 +22,6 @@ local function invoke(cmd)
local pos = game.player.surface.find_non_colliding_position('player', game.player.position, 0, 1)
game.players[target].teleport({pos.x, pos.y}, game.player.surface)
game.print(target .. ', get your ass over here!')
Utils.log_command(game.player.name, cmd.name, cmd.parameter)
end
--- Takes a target and teleports player to target. (admin only)
@ -41,7 +40,6 @@ local function teleport_player(cmd)
game.player.teleport(pos, surface)
game.print(target .. "! watcha doin'?!")
game.player.print('You have teleported to ' .. game.players[target].name)
Utils.log_command(game.player.name, cmd.name, cmd.parameter)
end
--- Takes a selected entity and teleports player to entity. (admin only)
@ -56,7 +54,6 @@ local function teleport_location(cmd)
end
local pos = game.player.surface.find_non_colliding_position('player', game.player.selected.position, 0, 1)
game.player.teleport(pos)
Utils.log_command(game.player.name, cmd.name, false)
end
--- Kill a player with fish as the cause of death.
@ -91,8 +88,8 @@ local function kill(cmd)
end
if global.walking then
if global.walking[player.index] == true or global.walking[target.index] == true then
player.print("A player on walkabout cannot be killed by a mere fish, don't waste your efforts.")
if (player and global.walking[player.index]) or global.walking[target.index] then
Game.player_print("A player on walkabout cannot be killed by a mere fish, don't waste your efforts.")
return
end
end
@ -110,7 +107,6 @@ local function kill(cmd)
if not do_fish_kill(target) then
Game.player_print(table.concat {"'Sorry, '", target.name, "' doesn't have a character to kill."})
end
Utils.log_command(game.player.name, cmd.name, param)
else
Game.player_print("Sorry you don't have permission to use the kill command on other players.")
end
@ -132,24 +128,25 @@ local function regular(cmd)
Utils.cant_run(cmd.name)
return
end
if cmd.parameter == nil then
Game.player_print('Command failed. Usage: /regular <promote, demote>, <player>')
return
end
local params = {}
for param in string.gmatch(cmd.parameter, '%S+') do
table.insert(params, param)
end
if params[2] == nil then
Game.player_print('Command failed. Usage: /regular <promote, demote>, <player>')
return
elseif (params[1] == 'promote') then
UserGroups.add_regular(params[2])
Utils.log_command(game.player.name, cmd.name, cmd.parameter)
elseif (params[1] == 'demote') then
UserGroups.remove_regular(params[2])
Utils.log_command(game.player.name, cmd.name, cmd.parameter)
if #params == 2 then
if params[1] == 'promote' then
UserGroups.add_regular(params[2])
elseif params[1] == 'demote' then
UserGroups.remove_regular(params[2])
else
Game.player_print('Command failed. Usage: /regular <promote, demote>, <player>')
end
elseif #params == 1 and params[1] ~= 'promote' and params[1] ~= 'demote' then
UserGroups.add_regular(params[1])
else
Game.player_print('Command failed. Usage: /regular <promote, demote>, <player>')
end
@ -270,11 +267,9 @@ local function toggle_tp_mode(cmd)
if toggled then
global.tp_players[index] = nil
Game.player_print('tp mode is now off')
Utils.log_command(game.player.name, cmd.name, 'off')
else
global.tp_players[index] = true
Game.player_print('tp mode is now on - place a ghost entity to teleport there.')
Utils.log_command(game.player.name, cmd.name, 'on')
end
end
@ -329,7 +324,6 @@ local function tempban(cmd)
local group = get_group()
game.print(Utils.get_actor() .. ' put ' .. params[1] .. ' in timeout for ' .. params[2] .. ' minutes.')
Utils.log_command(game.player.name, cmd.name, cmd.parameter)
if group then
group.add_player(params[1])
if not tonumber(cmd.parameter) then
@ -375,7 +369,6 @@ local function pool(cmd)
end
game.player.surface.set_tiles(t)
game.player.surface.create_entity {name = 'fish', position = {p.x + 0.5, p.y + 5}}
Utils.log_command(game.player.name, cmd.name, false)
end
end
@ -527,7 +520,6 @@ commands.add_command(
function()
if game.player and game.player.admin then
game.player.cheat_mode = not game.player.cheat_mode
Utils.log_command(game.player, 'hax', false)
end
end
)
@ -556,7 +548,7 @@ commands.add_command('tpmode', 'Toggles tp mode. When on place a ghost entity to
commands.add_command('tempban', '<player> <minutes> Temporarily bans a player (Admins only)', tempban)
commands.add_command('zoom', '<number> Sets your zoom.', zoom)
commands.add_command('pool', 'Spawns a pool', pool)
commands.add_command('find-player', '<player> shows an alert on the map where the player is located', find_player)
commands.add_command('find', '<player> shows an alert on the map where the player is located', find_player)
commands.add_command('jail', '<player> disables all actions a player can perform except chatting. (Admins only)', jail_player)
commands.add_command('unjail', '<player> restores ability for a player to perform actions. (Admins only)', Report.unjail_player)
commands.add_command('a', 'Admin chat. Messages all other admins (Admins only)', admin_chat)

View File

@ -2,6 +2,7 @@
local Global = require 'utils.global'
local Event = require 'utils.event'
local raise_event = script.raise_event
local ceil = math.ceil
local max = math.max
-- this, things that can be done run-time
@ -18,19 +19,15 @@ local ForceControlBuilder = {}
-- all force data being monitored
local forces = {}
-- the table holding the function that calculates the experience to next level
local next_level_cap_calculator = {
execute = nil
}
-- the function that calculates the experience to next level
local calculate_next_level_cap = nil
Global.register(
{
forces = forces,
next_level_cap_calculator = next_level_cap_calculator
},
function(tbl)
forces = tbl.forces
next_level_cap_calculator = tbl.next_level_cap_calculator
end
)
@ -147,11 +144,11 @@ end
---Register the config and initialize the feature.
---@param level_up_formula function
function ForceControl.register(level_up_formula)
if next_level_cap_calculator.execute then
if calculate_next_level_cap then
error('Can only register one force control.')
end
next_level_cap_calculator.execute = level_up_formula
calculate_next_level_cap = level_up_formula
return ForceControlBuilder
end
@ -159,7 +156,7 @@ end
---Registers a new force to participate.
---@param lua_force_or_name LuaForce|string
function ForceControl.register_force(lua_force_or_name)
if not next_level_cap_calculator.execute then
if not calculate_next_level_cap then
error('Can only register a force when the config has been initialized via ForceControl.register(config_table).')
end
local force = get_valid_force(lua_force_or_name)
@ -169,14 +166,15 @@ function ForceControl.register_force(lua_force_or_name)
forces[force.name] = {
current_experience = 0,
total_experience = 0,
current_level = 0,
experience_level_up_cap = next_level_cap_calculator.execute(0)
experience_level_up_cap = calculate_next_level_cap(0)
}
end
---Returns the ForceControlBuilder.
function ForceControl.get_force_control_builder()
if not next_level_cap_calculator.execute then
if not calculate_next_level_cap then
error('Can only get the force control builder when the config has been initialized via ForceControl.register(config_table).')
end
@ -185,7 +183,8 @@ end
---Removes experience from a force. Won't cause de-level nor go below 0.
---@param lua_force_or_name LuaForce|string
---@param experience number amount of experience to add
---@param experience number amount of experience to remove
---@return number the experience being removed
function ForceControl.remove_experience(lua_force_or_name, experience)
assert_type('number', experience, 'Argument experience of function ForceControl.remove_experience')
@ -200,14 +199,39 @@ function ForceControl.remove_experience(lua_force_or_name, experience)
if not force_config then
return
end
local backup_current_experience = force_config.current_experience
force_config.current_experience = max(0, force_config.current_experience - experience)
force_config.total_experience = (force_config.current_experience == 0) and force_config.total_experience - backup_current_experience or max(0, force_config.total_experience - experience)
return backup_current_experience - force_config.current_experience
end
---Removes experience from a force, based on a percentage of the total obtained experience
---@param lua_force_or_name LuaForce|string
---@param percentage number percentage of total obtained experience to remove
---@param min_experience number minimum amount of experience to remove (optional)
---@return number the experience being removed
---@see ForceControl.remove_experience
function ForceControl.remove_experience_percentage(lua_force_or_name, percentage, min_experience)
local min_experience = min_experience ~= nil and min_experience or 0
local force = get_valid_force(lua_force_or_name)
if not force then
return
end
local force_config = forces[force.name]
if not force_config then
return
end
local penalty = force_config.total_experience * percentage
penalty = (penalty >= min_experience) and ceil(penalty) or ceil(min_experience)
return ForceControl.remove_experience(lua_force_or_name, penalty)
end
---Adds experience to a force.
---@param lua_force_or_name LuaForce|string
---@param experience number amount of experience to add
function ForceControl.add_experience(lua_force_or_name, experience)
---@param resursive_call boolean whether or not the function is called recursively (optional)
function ForceControl.add_experience(lua_force_or_name, experience, recursive_call)
assert_type('number', experience, 'Argument experience of function ForceControl.add_experience')
if experience < 1 then
@ -224,6 +248,9 @@ function ForceControl.add_experience(lua_force_or_name, experience)
local new_experience = force_config.current_experience + experience
local experience_level_up_cap = force_config.experience_level_up_cap
if not recursive_call then
force_config.total_experience = force_config.total_experience + experience
end
if (new_experience < experience_level_up_cap) then
force_config.current_experience = new_experience
@ -234,11 +261,10 @@ function ForceControl.add_experience(lua_force_or_name, experience)
local new_level = force_config.current_level + 1
force_config.current_level = new_level
force_config.current_experience = 0
force_config.experience_level_up_cap = next_level_cap_calculator.execute(new_level)
force_config.experience_level_up_cap = calculate_next_level_cap(new_level)
raise_event(ForceControl.events.on_level_up, {level_reached = new_level, force = force})
ForceControl.add_experience(force, new_experience - experience_level_up_cap)
ForceControl.add_experience(force, new_experience - experience_level_up_cap, true)
end
---Return the force data as {
@ -261,10 +287,29 @@ function ForceControl.get_force_data(lua_force_or_name)
return {
current_experience = force_config.current_experience,
total_experience = force_config.total_experience,
current_level = force_config.current_level,
experience_level_up_cap = force_config.experience_level_up_cap,
experience_percentage = (force_config.current_experience / force_config.experience_level_up_cap) * 100
}
end
function ForceControl.get_formatted_force_data(lua_force_or_name)
local force_config = ForceControl.get_force_data(lua_force_or_name)
if not force_config then
return
end
local result =
string.format(
'Current experience: %d Total experience: %d Current level: %d Next level at: %d Percentage to level up: %d%%',
force_config.current_experience,
force_config.total_experience,
force_config.current_level,
force_config.experience_level_up_cap,
math.floor(force_config.experience_percentage * 100) / 100
)
return result
end
return ForceControl

View File

@ -86,9 +86,8 @@ local function popup(cmd)
show_popup(p, message)
end
player.print('Popup sent')
Utils.print_admins(player.name .. ' sent a popup to all players', false)
Utils.log_command(game.player.name, cmd.name, cmd.parameter)
Game.player_print('Popup sent')
Utils.print_admins(Utils.get_actor() .. ' sent a popup to all players', false)
end
-- Creates a popup dialog for all players, specifically for the server upgrading factorio versions
@ -105,9 +104,8 @@ local function popup_update(cmd)
show_popup(p, message)
end
player.print('Popup sent')
Utils.print_admins(player.name .. ' sent a popup to all players', false)
Utils.log_command(game.player.name, cmd.name, message)
Game.player_print('Popup sent')
Utils.print_admins(Utils.get_actor() .. ' sent a popup to all players', false)
end
-- Creates a popup dialog for the specifically targetted player
@ -141,8 +139,7 @@ local function popup_player(cmd)
show_popup(target, message)
player.print('Popup sent')
Utils.log_command(game.player.name, cmd.name, cmd.parameter)
Game.player_print('Popup sent')
end
commands.add_command('popup', '<message> - Shows a popup to all connected players (Admins only)', popup)

View File

@ -207,7 +207,7 @@ function Module.jail(target_player, player)
target_player.print('Please respond to inquiries from the admins.', {r = 1, g = 1, b = 0, a = 1})
target_player.print(prefix_e)
Utils.print_admins(target_player.name .. ' has been jailed by ' .. player.name)
Utils.log_command(player, 'jail', target_player.name)
Utils.log_command(Utils.get_actor(), 'jail', target_player.name)
else
-- Let admin know it didn't work.
print('Something went wrong in the jailing of ' .. target_player.name .. '. You can still change their group via /permissions.')
@ -264,7 +264,7 @@ function Module.unjail_player(cmd)
target_player.print('Your ability to perform actions has been restored', {r = 0, g = 1, b = 0, a = 1})
target_player.print(prefix_e)
Utils.print_admins(target_player.name .. ' has been released from jail by ' .. player.name)
Utils.log_command(player, 'unjail', target_player.name)
Utils.log_command(Utils.get_actor(), 'unjail', target_player.name)
else
-- Let admin know it didn't work.
Game.player_print('Something went wrong in the unjailing of ' .. target .. '. You can still change their group via /permissions and inform them.')

View File

@ -0,0 +1,159 @@
require 'utils.list_utils'
local Game = require 'utils.game'
local Event = require 'utils.event'
local naming_words = require 'resources.naming_words'
local Utils = require('utils.utils')
global.actual_name = {}
global.silly_names = {}
global.silly_names.count = 0
local name_combinations = #naming_words.adverbs * #naming_words.adjectives * #naming_words.nouns
--- Creates name by combining elements from the passed table
-- @param1 table including adverbs, adjectives, and nouns
-- @returns name as a string
local function create_name(words_table)
local adverb, adjective, noun
adverb = table.get_random(words_table.adverbs)
adjective = table.get_random(words_table.adjectives)
noun = table.get_random(words_table.nouns)
return adverb .. '_' .. adjective .. '_' .. noun
end
--- Calls create_name until a unique name is returned
-- @param1 table including adverbs, adjectives, and nouns
-- @returns name as a string
local function create_unique_name(words_table)
local silly_names = global.silly_names
local name = create_name(words_table)
while table.contains(silly_names, name) do
name = create_name(words_table)
end
return name
end
--- Assigns a player a name, stores their old and silly names
-- @param1 Takes a LuaPlayer
local function name_player(player)
-- Store a player's original name in case they want it back.
if not global.actual_name[player.index] then
global.actual_name[player.index] = player.name
end
-- Because create_unique_name enters a while loop looking for a unique name, ensure we never get stuck.
local ceiling = math.min(name_combinations * 0.25, 10000)
if global.silly_names.count > ceiling then
global.silly_names = {}
global.silly_names.count = 0
end
local name = create_unique_name(naming_words)
table.insert(global.silly_names, name)
global.silly_names.count = global.silly_names.count + 1
local str = player.name .. ' will now be known as: ' .. name
game.print(str)
Utils.print_admins(str .. ' (ID: ' .. player.index .. ')', false)
player.name = name
end
--- Restores a player's actual name
local function restore_name(cmd)
local player = Game.get_player_by_index(cmd.player_index)
player.name = global.actual_name[player.index]
player.print('Your true name has been restored.')
end
--- Passes _event_ on to name_players
local function name_player_event(event)
local player = Game.get_player_by_index(event.player_index)
name_player(player)
end
--- Passes target or player on to name_players
local function name_player_command(cmd)
local player = game.player
local param = cmd.parameter
local target
if param then
target = game.players[param]
if player and not player.admin then
-- Yes param, yes player, no admin/server = fail, non-admins, non-server cannot use command on others
Game.player_print("Sorry you don't have permission to use the roll-name command on other players.")
return
else
-- Yes param, yes admin/server = check target
if target then
-- Yes param, yes admin/server, yes target = change name
name_player(target)
return
else
-- Yes param, yes admin/server, no target = fail, wrong player name
Game.player_print(table.concat {"Sorry, player '", param, "' was not found."})
return
end
end
else
-- No param = check if server
if not player then
-- No param, no player = server trying to change its name
Game.player_print('The server cannot change its name')
return
end
-- No param, not server = change self name
name_player(player)
return
end
end
--- Prints the original name of the target
local function check_name(cmd)
local current_name = cmd.parameter
if not current_name then
Game.player_print('Usage: /name-check <player>')
return
end
local target = game.players[current_name]
if not target then
Game.player_print('player ' .. current_name .. ' not found')
return
end
local actual_name = global.actual_name[target.index]
Game.player_print(target.name .. ' is actually: ' .. actual_name)
end
--- Prints the index of the target
local function get_player_id(cmd)
local player = game.player
-- Check if the player can run the command
if player and not player.admin then
Utils.cant_run(cmd.name)
return
end
-- Check if the target is valid
local target_name = cmd['parameter']
if not target_name then
Game.player_print('Usage: /get-player-id <player>')
return
end
local target_index = game.players[target_name].index
Game.player_print(target_name .. ' -- ' .. target_index)
end
if global.scenario.config.players_assigned_names == true then
Event.add(defines.events.on_player_created, name_player_event)
end
if global.scenario.config.players_roll_names == true then
commands.add_command('name-roll', 'Assigns you a random, silly name', name_player_command)
end
if global.scenario.config.players_roll_names == true or global.scenario.config.players_assigned_names == true then
commands.add_command('name-restore', 'Removes your fun name and gives you back your actual name', restore_name)
commands.add_command('name-check', '<player> Check the original name of a player', check_name)
commands.add_command('get-player-id', 'Gets the ID of a player (Admin only)', get_player_id)
end

View File

@ -2,12 +2,6 @@
-- this
local Config = {
-- enable debug mode, shows extra messages
debug = false,
-- allow cheats, primarily configured under SetupPlayer
cheats = false,
-- a list of features to register and enable
-- to disable a feature, change the flag
features = {
@ -49,13 +43,18 @@ local Config = {
character_health_bonus = 1000000,
-- unlock all research by default, only useful when testing
unlock_all_research = true,
unlock_all_research = false,
-- adds additional items to the player force when starting in addition to defined in start_items above
starting_items = {
{name = 'power-armor-mk2', count = 1},
{name = 'submachine-gun', count = 1},
{name = 'uranium-rounds-magazine', count = 1000},
{name = 'roboport', count = 2},
{name = 'construction-robot', count = 50},
{name = 'electric-energy-interface', count = 1},
{name = 'medium-electric-pole', count = 50},
{name = 'logistic-chest-storage', count = 50},
},
},
},
@ -70,6 +69,12 @@ local Config = {
-- enables commands like /clear-void
enable_debug_commands = false,
-- initial damage per tick it damages a rock to mine, can be enhanced by robot_damage_per_mining_prod_level
robot_initial_mining_damage = 4,
-- damage added per level of mining productivity level research
robot_damage_per_mining_prod_level = 1,
},
-- adds the ability to collapse caves
@ -295,8 +300,8 @@ local Config = {
cluster_mode = true,
-- location of file to find the cluster definition file
cluster_file_location = 'map_gen.Diggy.Orepattern.Tendrils',
--cluster_file_location = 'map_gen.Diggy.Orepattern.Clusters',
ore_pattern = require 'map_gen.Diggy.Orepattern.Tendrils',
--ore_pattern = require 'map_gen.Diggy.Orepattern.Clusters',
},
@ -343,7 +348,7 @@ local Config = {
-- market config
market_spawn_position = {x = 0, y = 3},
stone_to_surface_amount = 50,
currency_item = 'stone',
currency_item = 'coin',
-- locations where chests will be automatically cleared from currency_item
void_chest_tiles = {
@ -355,52 +360,42 @@ local Config = {
-- add or remove a table entry to add or remove a unlockable item from the mall.
-- format: {unlock_at_level, price, prototype_name},
-- alternative format: {level = 32, price = {{"stone", 2500}, {"coin", 100}}, name = 'power-armor'},
unlockables = require('map_gen.Diggy.FormatMarketItems').initialize_unlockables(
{
{level = 1, price = 50, name = 'iron-axe'},
{level = 2, price = 50, name = 'raw-wood'},
{level = 2, price = 20, name = 'raw-fish'},
{level = 3, price = 50, name = 'stone-brick'},
{level = 5, price = 125, name = 'stone-wall'},
{level = 7, price = 25, name = 'small-lamp'},
{level = 9, price = 50, name = 'firearm-magazine'},
{level = 9, price = 250, name = 'pistol'},
{level = 11, price = 850, name = 'shotgun'},
{level = 11, price = 50, name = 'shotgun-shell'},
{level = 14, price = 500, name = 'light-armor'},
{level = 16, price = 850, name = 'submachine-gun'},
{level = 16, price = 50, name = 'steel-axe'},
{level = 19, price = 100, name = 'piercing-rounds-magazine'},
{level = 19, price = 100, name = 'piercing-shotgun-shell'},
{level = 21, price = 750, name = 'heavy-armor'},
{level = 25, price = 1500, name = 'modular-armor'},
{level = 25, price = 1000, name = 'landfill'},
{level = 28, price = {{"stone", 900}, {"coin", 25}}, name = 'personal-roboport-equipment'},
{level = 28, price = {{"stone", 250}, {"coin", 10}}, name = 'construction-robot'},
{level = 32, price = {{"stone", 2500}, {"coin", 100}}, name = 'power-armor'},
{level = 34, price = {{"stone", 150}, {"coin", 10}}, name = 'battery-equipment'},
{level = 33, price = {{"stone", 2000}, {"coin", 75}}, name = 'fusion-reactor-equipment'},
{level = 36, price = {{"stone", 750}, {"coin", 20}}, name = 'energy-shield-equipment'},
{level = 1, price = 5, name = 'iron-axe'},
{level = 2, price = 5, name = 'raw-wood'},
{level = 2, price = 10, name = 'raw-fish'},
{level = 3, price = 5, name = 'stone-brick'},
{level = 5, price = 12, name = 'stone-wall'},
{level = 7, price = 6, name = 'small-lamp'},
{level = 9, price = 5, name = 'firearm-magazine'},
{level = 9, price = 25, name = 'pistol'},
{level = 11, price = 85, name = 'shotgun'},
{level = 11, price = 5, name = 'shotgun-shell'},
{level = 14, price = 50, name = 'light-armor'},
{level = 16, price = 85, name = 'submachine-gun'},
{level = 16, price = 25, name = 'steel-axe'},
{level = 19, price = 15, name = 'piercing-rounds-magazine'},
{level = 19, price = 15, name = 'piercing-shotgun-shell'},
{level = 21, price = 100, name = 'heavy-armor'},
{level = 25, price = 250, name = 'modular-armor'},
{level = 25, price = 100, name = 'landfill'},
{level = 28, price = 250, name = 'personal-roboport-equipment'},
{level = 28, price = 75, name = 'construction-robot'},
{level = 32, price = 850, name = 'power-armor'},
{level = 34, price = 100, name = 'battery-equipment'},
{level = 33, price = 1000, name = 'fusion-reactor-equipment'},
{level = 36, price = 150, name = 'energy-shield-equipment'},
{level = 42, price = 1000, name = 'combat-shotgun'},
{level = 46, price = 250, name = 'uranium-rounds-magazine'},
{level = 58, price = {{"stone", 1500}, {"coin", 25}}, name = 'rocket-launcher'},
{level = 58, price = {{"stone", 500}, {"coin", 5}}, name = 'rocket'},
{level = 66, price = {{"stone", 1000}, {"coin", 10}}, name = 'explosive-rocket'},
{level = 73, price = {{"stone", 1000}, {"coin", 200}}, name = 'satellite'},
{level = 100, price = {{"stone", 5000}, {"coin", 9999}}, name = 'atomic-bomb'},
{level = 58, price = 250, name = 'rocket-launcher'},
{level = 58, price = 50, name = 'rocket'},
{level = 66, price = 100, name = 'explosive-rocket'},
{level = 73, price = 2000, name = 'satellite'},
{level = 100, price = 1, name = 'iron-stick'},
}
),
buffs = { --Define new buffs here, they are handed out for each level
{prototype = {name = 'mining_speed', value = 5}},
{prototype = {name = 'inventory_slot', value = 1}},
{prototype = {name = 'stone_automation', value = 3}},
},
-- controls the formula for calculating level up costs in stone sent to surface
difficulty_scale = 25, -- Diggy default 25. Higher increases difficulty, lower decreases (Only affects the stone requirement/cost to level up) (Only integers has been tested succesful)
start_stone = 50, -- Diggy default 50. This sets the price for the first level.
cost_precision = 2, -- Diggy default 2. This sets the precision of the stone requirements to level up. E.g. 1234 becomes 1200 with precision 2 and 1230 with precision 3.
},
@ -409,7 +404,40 @@ local Config = {
enabled = true,
autojail = true,
allowed_collapses_first_hour = 4
}
},
Experience = {
enabled = true,
-- controls the formula for calculating level up costs in stone sent to surface
difficulty_scale = 25, -- Diggy default 25. Higher increases experience requirement climb
first_lvl_xp = 600, -- Diggy default 600. This sets the price for the first level.
xp_fine_tune = 200, -- Diggy default 200. This value is used to fine tune the overall requirement climb without affecting the speed
cost_precision = 3, -- Diggy default 3. This sets the precision of the required experience to level up. E.g. 1234 becomes 1200 with precision 2 and 1230 with precision 3.
-- percentage * mining productivity level gets added to mining speed
mining_speed_productivity_multiplier = 5,
XP = {
['sand-rock-big'] = 5,
['rock-huge'] = 10,
['rocket_launch'] = 5000, -- XP reward for a single rocket launch
['science-pack-1'] = 2,
['science-pack-2'] = 4,
['science-pack-3'] = 10,
['military-science-pack'] = 8,
['production-science-pack'] = 25,
['high-tech-science-pack'] = 50,
['space-science-pack'] = 10,
['enemy_killed'] = 10, -- Base XP for killing biters and spitters. This value is multiplied by the alien_coin_modifiers from ArtefactHunting
['death-penalty'] = 0.005, -- XP deduct in percentage of total experience when a player dies (Diggy default: 0.005 which equals 0.5%)
},
buffs = { --Define new buffs here, they are handed out for each level.
['mining_speed'] = {value = 5},
['inventory_slot'] = {value = 1},
['health_bonus'] = {value = 2.5, double_level = 5}, -- double_level is the level interval for receiving a double bonus (Diggy default: 5 which equals every 5th level)
},
},
},
}

View File

@ -53,7 +53,7 @@ function Debug.print_serpent(message)
end
--[[--
Shows the given message if _DEBUG == true for a given position.
Shows the given message if debug is on.
@param x number
@param y number

View File

@ -6,10 +6,11 @@
local Event = require 'utils.event'
local Global = require 'utils.global'
local Token = require 'utils.global_token'
local Task = require 'utils.task'
local Task = require 'utils.Task'
local AlienEvolutionProgress = require 'map_gen.Diggy.AlienEvolutionProgress'
local Debug = require 'map_gen.Diggy.Debug'
local Template = require 'map_gen.Diggy.Template'
local CreateParticles = require 'features.create_particles'
local random = math.random
local floor = math.floor
local ceil = math.ceil
@ -54,16 +55,7 @@ local do_alien_mining = Token.register(function(params)
local particle_count = 16 - ((#rocks - 1) * 5)
for _, rock in pairs(rocks) do
raise_event(defines.events.on_entity_died, {entity = rock})
for _ = particle_count, 1, -1 do
create_entity({
position = rock.position,
name = 'stone-particle',
movement = {random(-5, 5) * 0.01, random(-5, 5) * 0.01},
frame_speed = 1,
vertical_speed = random(12, 14) * 0.01,
height = random(9, 11) * 0.1,
})
end
CreateParticles.destroy_rock(create_entity, particle_count, rock.position)
rock.destroy()
end
end

View File

@ -24,17 +24,13 @@ local function redraw_table(data)
data.frame.caption = 'Scoretable'
local score_keys = ScoreTable.all_keys()
for _, data in pairs(score_keys) do
local val = ScoreTable.get(data)
for name, value in pairs(ScoreTable.all()) do
local table = list.add({type = 'table', column_count = 2})
local key = table.add({type = 'label', name = 'Diggy.ArtefactHunting.Frame.List.Key', caption = data})
local key = table.add({type = 'label', name = 'Diggy.ArtefactHunting.Frame.List.Key', caption = name})
key.style.minimal_width = 175
local val = table.add({type = 'label', name = 'Diggy.ArtefactHunting.Frame.List.Val', caption = utils.comma_value(val)})
local val = table.add({type = 'label', name = 'Diggy.ArtefactHunting.Frame.List.Val', caption = utils.comma_value(value)})
val.style.minimal_width = 225
end
end

View File

@ -6,193 +6,65 @@
-- dependencies
local Event = require 'utils.event'
local Global = require 'utils.global'
local Game = require 'utils.game'
local Scanner = require 'map_gen.Diggy.Scanner'
local Template = require 'map_gen.Diggy.Template'
local ScoreTable = require 'map_gen.Diggy.ScoreTable'
local Debug = require 'map_gen.Diggy.Debug'
local CreateParticles = require 'features.create_particles'
local insert = table.insert
local random = math.random
local raise_event = script.raise_event
-- todo remove this dependency
local ResourceConfig = require 'map_gen.Diggy.Config'.features.ScatteredResources
local Perlin = require 'map_gen.shared.perlin_noise'
local Simplex = require 'map_gen.shared.simplex_noise'
-- this
local DiggyHole = {}
-- keeps track of the amount of times per player when they mined with a full inventory in a row
local full_inventory_mining_cache = {}
-- keeps track of the buffs for the bot mining mining_efficiency
local robot_mining = {
damage = 0,
active_modifier = 0,
research_modifier = 0,
}
Global.register({
full_inventory_mining_cache = full_inventory_mining_cache,
bot_mining_damage = robot_mining,
}, function (tbl)
full_inventory_mining_cache = tbl.full_inventory_mining_cache
robot_mining = tbl.bot_mining_damage
end)
local function reset_player_full_inventory_cache(player)
if not full_inventory_mining_cache[player.index] then
return
end
local function update_robot_mining_damage()
-- remove the current buff
local old_modifier = robot_mining.damage - robot_mining.active_modifier
full_inventory_mining_cache[player.index] = nil
-- update the active modifier
robot_mining.active_modifier = robot_mining.research_modifier
-- add the new active modifier to the non-buffed modifier
robot_mining.damage = old_modifier + robot_mining.active_modifier
ScoreTable.set('Robot mining damage', robot_mining.damage)
end
local full_inventory_message = 'Miner, you have a full inventory!\n\nMake sure to empty it before you continue digging.'
local function trigger_inventory_warning(player)
local player_index = player.index
local count = full_inventory_mining_cache[player_index]
if not count then
full_inventory_mining_cache[player_index] = 1
player.print('## - ' .. full_inventory_message, {r = 1, g = 1, b = 0, a = 1})
player.play_sound{path='utility/new_objective', volume_modifier = 1 }
return
end
full_inventory_mining_cache[player_index] = count + 1
if count % 5 == 0 then
require 'features.gui.popup'.player(player, full_inventory_message)
end
end
--[[--
Triggers a diggy diggy hole for a given sand-rock-big or rock-huge.
Will return true even if the tile behind it is immune.
@param entity LuaEntity
]]
---Triggers a diggy diggy hole for a given sand-rock-big or rock-huge.
---@param entity LuaEntity
local function diggy_hole(entity)
local name = entity.name
if name ~= 'sand-rock-big' and name ~= 'rock-huge' then
return
end
local tiles = {}
local rocks = {}
local surface = entity.surface
local position = entity.position
local x = position.x
local y = position.y
local out_of_map_found = Scanner.scan_around_position(surface, position, 'out-of-map');
local distance = ResourceConfig.distance(x, y)
-- source of noise for resource generation
-- index determines offset
-- '-1' is reserved for cluster mode
-- compound clusters use as many indexes as needed > 1
local base_seed
local function seeded_noise(surface, x, y, index, sources)
base_seed = base_seed or surface.map_gen_settings.seed + surface.index + 4000
local noise = 0
for _, settings in ipairs(sources) do
settings.type = settings.type or 'perlin'
settings.offset = settings.offset or 0
if settings.type == 'zero' then
noise = noise + 0
elseif settings.type == 'one' then
noise = noise + settings.weight * 1
elseif settings.type == 'perlin' then
noise = noise + settings.weight * Perlin.noise(x/settings.variance, y/settings.variance,
base_seed + 2000*index + settings.offset)
elseif settings.type == 'simplex' then
noise = noise + settings.weight * Simplex.d2(x/settings.variance, y/settings.variance,
base_seed + 2000*index + settings.offset)
else
Debug.print('noise type \'' .. settings.type .. '\' not recognized')
end
end
return noise
end
-- global config values
local resource_richness_weights = ResourceConfig.resource_richness_weights
local resource_richness_weights_sum = 0
for _, weight in pairs(resource_richness_weights) do
resource_richness_weights_sum = resource_richness_weights_sum + weight
end
local s_resource_weights = ResourceConfig.scattered_resource_weights
local s_resource_weights_sum = 0
for _, weight in pairs(s_resource_weights) do
s_resource_weights_sum = s_resource_weights_sum + weight
end
-- compound cluster spawning
local c_mode = ResourceConfig.cluster_mode
-- local c_clusters = Config.features.ScatteredResources.clusters
local c_clusters = require(ResourceConfig.cluster_file_location)
if ('table' ~= type(c_clusters)) then
error('cluster_file_location invalid')
end
local c_count = 0
for _, cluster in ipairs(c_clusters) do
c_count = c_count + 1
cluster.weights_sum = 0
for _, weight in pairs(cluster.weights) do
cluster.weights_sum = cluster.weights_sum + weight
end
end
local function spawn_cluster_resource(surface, x, y, cluster_index, cluster)
for name, weight in pairs(cluster.weights) do
if name == 'skip' then return false end
end
return true
end
local huge_rock_inserted = false
for _, position in pairs(out_of_map_found) do
insert(tiles, {name = 'dirt-' .. random(1, 7), position = position})
-- if (random() > 0.50) then
-- insert(rocks, {name = 'rock-huge', position = position})
if c_mode then
for index,cluster in ipairs(c_clusters) do
if distance >= cluster.min_distance and cluster.noise_settings.type ~= 'skip' then
if cluster.noise_settings.type == "connected_tendril" then
local noise = seeded_noise(surface, x, y, index, cluster.noise_settings.sources)
if -1 * cluster.noise_settings.threshold < noise and noise < cluster.noise_settings.threshold then
if spawn_cluster_resource(surface, x, y, index, cluster) then
insert(rocks, {name = 'rock-huge', position = position})
huge_rock_inserted = true
end
end
elseif cluster.noise_settings.type == "fragmented_tendril" then
local noise1 = seeded_noise(surface, x, y, index, cluster.noise_settings.sources)
local noise2 = seeded_noise(surface, x, y, index, cluster.noise_settings.discriminator)
if -1 * cluster.noise_settings.threshold < noise1 and noise1 < cluster.noise_settings.threshold
and -1 * cluster.noise_settings.discriminator_threshold < noise2
and noise2 < cluster.noise_settings.discriminator_threshold then
if spawn_cluster_resource(surface, x, y, index, cluster) then
insert(rocks, {name = 'rock-huge', position = position})
huge_rock_inserted = true
end
end
else
local noise = seeded_noise(surface, x, y, index, cluster.noise_settings.sources)
if noise >= cluster.noise_settings.threshold then
if spawn_cluster_resource(surface, x, y, index, cluster) then
insert(rocks, {name = 'rock-huge', position = position})
huge_rock_inserted = true
end
end
end
end
end
end
if (huge_rock_inserted == false) then
insert(rocks, {name = 'sand-rock-big', position = position})
for _, void_position in ipairs(out_of_map_found) do
insert(tiles, {name = 'dirt-' .. random(1, 7), position = void_position })
if random() < 0.35 then
insert(rocks, {name = 'rock-huge', position = void_position })
else
insert(rocks, {name = 'sand-rock-big', position = void_position })
end
end
@ -226,10 +98,20 @@ end
Registers all event handlers.
]]
function DiggyHole.register(config)
robot_mining.damage = config.robot_initial_mining_damage
ScoreTable.set('Robot mining damage', robot_mining.damage)
ScoreTable.reset('Void removed')
Event.add(defines.events.on_entity_died, function (event)
diggy_hole(event.entity)
local entity = event.entity
local name = entity.name
if name ~= 'sand-rock-big' and name ~= 'rock-huge' then
return
end
diggy_hole(entity)
if event.cause then
CreateParticles.destroy_rock(entity.surface.create_entity, 10, entity.position)
end
end)
Event.add(defines.events.on_entity_damaged, function (event)
@ -257,11 +139,8 @@ function DiggyHole.register(config)
end
local health = entity.health
local remove = event.buffer.remove
health = health - 10
remove({name = 'stone', count = 100})
remove({name = 'coal', count = 100})
health = health - robot_mining.damage
event.buffer.clear()
local graphics_variation = entity.graphics_variation
local create_entity = entity.surface.create_entity
@ -270,12 +149,14 @@ function DiggyHole.register(config)
if health < 1 then
raise_event(defines.events.on_entity_died, {entity = entity, force = force})
CreateParticles.mine_rock(create_entity, 6, position)
entity.destroy()
return
end
entity.destroy()
local rock = create_entity({name = name, position = position})
CreateParticles.mine_rock(create_entity, 1, position)
rock.graphics_variation = graphics_variation
rock.order_deconstruction(force)
rock.health = health
@ -284,24 +165,14 @@ function DiggyHole.register(config)
Event.add(defines.events.on_player_mined_entity, function (event)
local entity = event.entity
local name = entity.name
if name == 'sand-rock-big' or name == 'rock-huge' then
event.buffer.remove({name = 'coal', count = 100})
-- this logic can be replaced once we've fully replaced the stone to surface functionality
if enable_digging_warning then
local player = Game.get_player_by_index(event.player_index)
if player and player.valid then
if player.get_main_inventory().can_insert({name = 'stone'}) then
reset_player_full_inventory_cache(player)
else
trigger_inventory_warning(player)
end
end
end
if name ~= 'sand-rock-big' and name ~= 'rock-huge' then
return
end
event.buffer.clear()
diggy_hole(entity)
CreateParticles.mine_rock(entity.surface.create_entity, 6, entity.position)
end)
Event.add(defines.events.on_robot_mined_tile, function (event)
@ -316,6 +187,18 @@ function DiggyHole.register(config)
ScoreTable.increment('Void removed')
end)
Event.add(defines.events.on_research_finished, function (event)
local new_modifier = event.research.force.mining_drill_productivity_bonus * 50 * config.robot_damage_per_mining_prod_level
if (robot_mining.research_modifier == new_modifier) then
-- something else was researched
return
end
robot_mining.research_modifier = new_modifier
update_robot_mining_damage()
end)
if config.enable_debug_commands then
commands.add_command('clear-void', '<left top x> <left top y> <width> <height> <surface index> triggers Template.insert for the given area.', function(cmd)
local params = {}
@ -351,6 +234,7 @@ end
function DiggyHole.on_init()
game.forces.player.technologies['landfill'].enabled = false
game.forces.player.technologies['atomic-bomb'].enabled = false
end
return DiggyHole

View File

@ -0,0 +1,331 @@
-- dependencies
local Event = require 'utils.event'
local Game = require 'utils.game'
local Global = require 'utils.global'
local ForceControl = require 'features.force_control'
local Debug = require 'map_gen.Diggy.Debug'
-- Will be registered in Experience.register
local ForceControl_builder = {}
-- this
local Experience = {}
local mining_efficiency = {
active_modifier = 0,
research_modifier = 0,
level_modifier = 0,
}
local inventory_slots = {
active_modifier = 0,
research_modifier = 0,
level_modifier = 0,
}
local health_bonus = {
active_modifier = 0,
research_modifier = 0,
level_modifier = 0,
}
Global.register({
mining_efficiency = mining_efficiency,
inventory_slots = inventory_slots,
health_bonus = health_bonus
}, function(tbl)
mining_efficiency = tbl.mining_efficiency
inventory_slots = tbl.inventory_slots
health_bonus = tbl.health_bonus
end)
local config = {}
local string_format = string.format
local alien_coin_modifiers = require 'map_gen.Diggy.Config'.features.ArtefactHunting.alien_coin_modifiers
local floor = math.floor
local level_up_formula = (function (level_reached)
local floor = math.floor
local log = math.log
local Config = require 'map_gen.Diggy.Config'.features.Experience
local difficulty_scale = floor(Config.difficulty_scale)
local level_fine_tune = floor(Config.xp_fine_tune)
local start_value = (floor(Config.first_lvl_xp) * 0.5)
local precision = (floor(Config.cost_precision))
local function formula(level)
return (
difficulty_scale * (level) ^ 3
+ (level_fine_tune + start_value) * (level) ^ 2
+ start_value * (level)
- difficulty_scale * (level)
- level_fine_tune * (level)
)
end
local value = formula(level_reached + 1)
local lower_value = formula(level_reached)
value = value - (value % (10 ^ (floor(log(value,10)) - precision)))
if lower_value == 0 then
return value - lower_value
end
lower_value = lower_value - (lower_value % (10 ^ (floor(log(lower_value, 10)) - precision)))
return value - lower_value
end)
---Updates a forces manual mining speed modifier. By removing active modifiers and re-adding
---@param force LuaForce the force of which will be updated
---@param level_up number a level if updating as part of a level up (optional)
function Experience.update_mining_speed(force, level_up)
level_up = level_up ~= nil and level_up or 0
local buff = config.buffs['mining_speed']
if level_up > 0 and buff ~= nil then
local value = (buff.double_level ~= nil and level_up % buff.double_level == 0) and buff.value * 2 or buff.value
mining_efficiency.level_modifier = mining_efficiency.level_modifier + (value * 0.01)
end
-- remove the current buff
local old_modifier = force.manual_mining_speed_modifier - mining_efficiency.active_modifier
-- update the active modifier
mining_efficiency.active_modifier = mining_efficiency.research_modifier + mining_efficiency.level_modifier
-- add the new active modifier to the non-buffed modifier
force.manual_mining_speed_modifier = old_modifier + mining_efficiency.active_modifier
end
---Updates a forces inventory slots. By removing active modifiers and re-adding
---@param force LuaForce the force of which will be updated
---@param level_up number a level if updating as part of a level up (optional)
function Experience.update_inventory_slots(force, level_up)
level_up = level_up ~= nil and level_up or 0
local buff = config.buffs['inventory_slot']
if level_up > 0 and buff ~= nil then
local value = (buff.double_level ~= nil and level_up % buff.double_level == 0) and buff.value * 2 or buff.value
inventory_slots.level_modifier = inventory_slots.level_modifier + value
end
-- remove the current buff
local old_modifier = force.character_inventory_slots_bonus - inventory_slots.active_modifier
-- update the active modifier
inventory_slots.active_modifier = inventory_slots.research_modifier + inventory_slots.level_modifier
-- add the new active modifier to the non-buffed modifier
force.character_inventory_slots_bonus = old_modifier + inventory_slots.active_modifier
end
---Updates a forces inventory slots. By removing active modifiers and re-adding
---@param force LuaForce the force of which will be updated
---@param level_up number a level if updating as part of a level up (optional)
function Experience.update_health_bonus(force, level_up)
level_up = level_up ~= nil and level_up or 0
local buff = config.buffs['health_bonus']
if level_up > 0 and buff ~= nil then
local value = (buff.double_level ~= nil and level_up%buff.double_level == 0) and buff.value*2 or buff.value
health_bonus.level_modifier = health_bonus.level_modifier + value
end
-- remove the current buff
local old_modifier = force.character_health_bonus - health_bonus.active_modifier
-- update the active modifier
health_bonus.active_modifier = health_bonus.research_modifier + health_bonus.level_modifier
-- add the new active modifier to the non-buffed modifier
force.character_health_bonus = old_modifier + health_bonus.active_modifier
end
-- declaration of variables to prevent table lookups @see Experience.register
local sand_rock_xp
local rock_huge_xp
---Awards experience when a rock has been mined
---@param event LuaEvent
local function on_player_mined_entity(event)
local entity = event.entity
local player_index = event.player_index
local force = Game.get_player_by_index(player_index).force
local exp
if entity.name == 'sand-rock-big' then
exp = sand_rock_xp
elseif entity.name == 'rock-huge' then
exp = rock_huge_xp
else
return
end
local text = string_format('+%d XP', exp)
Game.print_player_floating_text_position(player_index, text, {r = 144, g = 202, b = 249},0, -0.5)
ForceControl.add_experience(force, exp)
end
---Awards experience when a research has finished, based on ingredient cost of research
---@param event LuaEvent
local function on_research_finished(event)
local research = event.research
local force = research.force
local award_xp = 0
for _, ingredient in pairs(research.research_unit_ingredients) do
local name = ingredient.name
local reward = config.XP[name]
award_xp = award_xp + reward
end
local exp = award_xp * research.research_unit_count
local text = string_format('Research completed! +%d XP', exp)
for _, p in pairs(game.connected_players) do
local player_index = p.index
Game.print_player_floating_text_position(player_index, text, {r = 144, g = 202, b = 249}, -1, -0.5)
end
ForceControl.add_experience(force, exp)
local current_modifier = mining_efficiency.research_modifier
local new_modifier = force.mining_drill_productivity_bonus * config.mining_speed_productivity_multiplier * 0.5
if (current_modifier == new_modifier) then
-- something else was researched
return
end
mining_efficiency.research_modifier = new_modifier
inventory_slots.research_modifier = force.mining_drill_productivity_bonus * 50 -- 1 per level
Experience.update_inventory_slots(force, 0)
Experience.update_mining_speed(force, 0)
game.forces.player.technologies['landfill'].enabled = false
end
---Awards experience when a rocket has been launched
---@param event LuaEvent
local function on_rocket_launched(event)
local exp = config.XP['rocket_launch']
local force = event.force
local text = string_format('Rocket launched! +%d XP', exp)
for _, p in pairs(game.connected_players) do
local player_index = p.index
Game.print_player_floating_text_position(player_index, text, {r = 144, g = 202, b = 249},-1, -0.5)
end
ForceControl.add_experience(force, exp)
end
---Awards experience when a player kills an enemy, based on type of enemy
---@param event LuaEvent
local function on_entity_died (event)
local entity = event.entity
local force = event.force
local cause = event.cause
--For bot mining
if not cause or cause.type ~= 'player' or not cause.valid then
local exp
if force.name == 'player' then
if entity.name == 'sand-rock-big' then
exp = floor(sand_rock_xp/2)
elseif entity.name == 'rock-huge' then
exp = floor(rock_huge_xp/2)
else
return
end
elseif cause and (cause.name == 'artillery-turret' or cause.name == 'gun-turret' or cause.name == 'laser-turret' or cause.name == 'flamethrower-turret') then
exp = Config.XP['enemy_killed'] * alien_coin_modifiers[entity.name]
local text = string_format('+ %d XP', exp)
Game.print_floating_text(cause.surface, cause.position, text, {r = 144, g = 202, b = 249})
ForceControl.add_experience(force, exp)
return
else
return
end
local text = string_format('+ %d XP', exp)
Game.print_floating_text(entity.surface, entity.position, text, {r = 144, g = 202, b = 249})
ForceControl.add_experience(force, exp)
return
end
if entity.force.name ~= 'enemy' then
return
end
local exp = config.XP['enemy_killed'] * alien_coin_modifiers[entity.name]
local text = string_format('+ %d XP', exp)
local player_index = cause.player.index
Game.print_player_floating_text_position(player_index, text, {r = 144, g = 202, b = 249},-1, -0.5)
ForceControl.add_experience(force, exp)
end
---Deducts experience when a player respawns, based on a percentage of total experience
---@param event LuaEvent
local function on_player_respawned(event)
local player = Game.get_player_by_index(event.player_index)
local force = player.force
local exp = ForceControl.remove_experience_percentage(force, config.XP['death-penalty'], 50)
local text = string_format('%s died! -%d XP', player.name, exp)
for _, p in pairs(game.connected_players) do
Game.print_player_floating_text_position(p.index, text, {r = 255, g = 0, b = 0},-1, -0.5)
end
end
---Get list of defined buffs
---@return table with the same format as in the Diggy Config
---@see Diggy.Config.features.Experience.Buffs
function Experience.get_buffs()
return config.buffs
end
local level_table = {}
---Get experiment requirement for a given level
---Primarily used for the market GUI to display total experience required to unlock a specific item
---@param level number a number specifying the level
---@return number required total experience to reach supplied level
function Experience.calculate_level_xp(level)
if level_table[level] == nil then
local value
if level == 1 then
value = level_up_formula(level-1)
else
value = level_up_formula(level-1)+Experience.calculate_level_xp(level-1)
end
table.insert(level_table, level, value)
end
return level_table[level]
end
function Experience.register(cfg)
config = cfg
--Adds the function on how to calculate level caps (When to level up)
ForceControl_builder = ForceControl.register(level_up_formula)
--Adds a function that'll be executed at every level up
ForceControl_builder.register_on_every_level(function (level_reached, force)
force.print(string_format('%s Leveled up to %d!', '## - ', level_reached))
force.play_sound{path='utility/new_objective', volume_modifier = 1 }
local Experience = require 'map_gen.Diggy.Feature.Experience'
Experience.update_inventory_slots(force, level_reached)
Experience.update_mining_speed(force, level_reached)
Experience.update_health_bonus(force, level_reached)
local MarketExchange = require 'map_gen.Diggy.Feature.MarketExchange'
local market = MarketExchange.get_market()
MarketExchange.update_market_contents(market, force)
MarketExchange.update_gui()
end)
-- Events
Event.add(defines.events.on_player_mined_entity, on_player_mined_entity)
Event.add(defines.events.on_research_finished, on_research_finished)
Event.add(defines.events.on_rocket_launched, on_rocket_launched)
Event.add(defines.events.on_player_respawned, on_player_respawned)
Event.add(defines.events.on_entity_died, on_entity_died)
-- Prevents table lookup thousands of times
sand_rock_xp = config.XP['sand-rock-big']
rock_huge_xp = config.XP['rock-huge']
end
function Experience.on_init()
--Adds the 'player' force to participate in the force control system.
local force = game.forces.player
ForceControl.register_force(force)
end
return Experience

View File

@ -9,128 +9,33 @@ local Task = require 'utils.Task'
local Gui = require 'utils.gui'
local Debug = require 'map_gen.Diggy.Debug'
local Template = require 'map_gen.Diggy.Template'
local Global = require 'utils.global'
local Game = require 'utils.game'
local MarketUnlockables = require 'map_gen.Diggy.MarketUnlockables'
local calculate_level = MarketUnlockables.calculate_level
local insert = table.insert
local force_control = require 'features.force_control'
local Experience = require 'map_gen.Diggy.Feature.Experience'
local max = math.max
local floor = math.floor
local utils = require 'utils.utils'
local prefix = '## - '
-- this
local MarketExchange = {}
local config = {}
local stone_tracker = {
stone_sent_to_surface = 0,
previous_stone_sent_to_surface = 0,
current_level = 0,
}
local stone_collecting = {
initial_value = 0,
active_modifier = 0,
research_modifier = 0,
market_modifier = 0,
}
local mining_efficiency = {
active_modifier = 0,
research_modifier = 0,
market_modifier = 0,
}
local inventory_slots = {
active_modifier = 0,
research_modifier = 0,
market_modifier = 0,
}
Global.register({
stone_collecting = stone_collecting,
stone_tracker = stone_tracker,
mining_efficiency = mining_efficiency,
inventory_slots = inventory_slots,
}, function(tbl)
stone_collecting = tbl.stone_collecting
stone_tracker = tbl.stone_tracker
mining_efficiency = tbl.mining_efficiency
inventory_slots = tbl.inventory_slots
end)
local function send_stone_to_surface(total)
stone_tracker.previous_stone_sent_to_surface = stone_tracker.stone_sent_to_surface
stone_tracker.stone_sent_to_surface = stone_tracker.stone_sent_to_surface + total
end
local on_market_timeout_finished = Token.register(function(params)
Template.market(params.surface, params.position, params.player_force, {})
local tiles = {}
for _, position in pairs(params.void_chest_tiles) do
insert(tiles, {name = 'tutorial-grid', position = position})
end
params.surface.set_tiles(tiles)
Template.market(params.surface, params.position, params.player_force)
end)
local function update_mining_speed(force)
-- remove the current buff
local old_modifier = force.manual_mining_speed_modifier - mining_efficiency.active_modifier
-- update the active modifier
mining_efficiency.active_modifier = mining_efficiency.research_modifier + mining_efficiency.market_modifier
-- add the new active modifier to the non-buffed modifier
force.manual_mining_speed_modifier = old_modifier + mining_efficiency.active_modifier
end
local function update_inventory_slots(force)
-- remove the current buff
local old_modifier = force.character_inventory_slots_bonus - inventory_slots.active_modifier
-- update the active modifier
inventory_slots.active_modifier = inventory_slots.research_modifier + inventory_slots.market_modifier
-- add the new active modifier to the non-buffed modifier
force.character_inventory_slots_bonus = old_modifier + inventory_slots.active_modifier
end
local function update_stone_collecting()
-- remove the current buff
local old_modifier = stone_collecting.initial_value - stone_collecting.active_modifier
-- update the active modifier
stone_collecting.active_modifier = stone_collecting.research_modifier + stone_collecting.market_modifier
-- add the new active modifier to the non-buffed modifier
stone_collecting.initial_value = old_modifier + stone_collecting.active_modifier
end
--Handles the updating of market items when unlocked, also handles the buffs
local function update_market_contents(market)
if (stone_tracker.previous_stone_sent_to_surface == stone_tracker.stone_sent_to_surface) then
return
end
local should_update_mining_speed = false
local should_update_inventory_slots = false
local should_update_stone_collecting = false
---Updates market content with new items if they are to be unlocked
---Primarily used by the force control system at every level up
---@param market LuaEntity The market to be updated
---@param force LuaForce the force which the unlocking requirement should be based of
function MarketExchange.update_market_contents(market, force)
local add_market_item
local old_level = stone_tracker.current_level
local print = game.print
local item_unlocked = false
if (calculate_level(stone_tracker.current_level+1) <= stone_tracker.stone_sent_to_surface) then
stone_tracker.current_level = stone_tracker.current_level + 1
end
for _, unlockable in pairs(config.unlockables) do
local stone_unlock = calculate_level(unlockable.level)
local is_in_range = stone_unlock > stone_tracker.previous_stone_sent_to_surface and stone_unlock <= stone_tracker.stone_sent_to_surface
local is_in_range = force_control.get_force_data(force).current_level == unlockable.level
-- only add the item to the market if it's between the old and new stone range
if (is_in_range and unlockable.type == 'market') then
@ -153,84 +58,18 @@ local function update_market_contents(market)
end
end
MarketExchange.update_gui()
if (old_level < stone_tracker.current_level) then
if item_unlocked then
print(prefix..'We have reached level ' .. stone_tracker.current_level .. '! New items are available from the market!')
else
print(prefix..'We have reached level ' .. stone_tracker.current_level .. '!')
end
for _, buffs in pairs(config.buffs) do
if (buffs.prototype.name == 'mining_speed') then
local value = buffs.prototype.value
Debug.print('Mining Foreman: Increased mining speed by ' .. value .. '%!')
should_update_mining_speed = true
mining_efficiency.market_modifier = mining_efficiency.market_modifier + (value / 100)
elseif (buffs.prototype.name == 'inventory_slot') then
local value = buffs.prototype.value
Debug.print('Mining Foreman: Increased inventory slots by ' .. value .. '!')
should_update_inventory_slots = true
inventory_slots.market_modifier = inventory_slots.market_modifier + value
elseif (buffs.prototype.name == 'stone_automation') then
local value = buffs.prototype.value
if (stone_tracker.current_level == 1) then
print('Mining Foreman: We can now automatically send stone to the surface from a chest below the market!')
else
Debug.print('Mining Foreman: We can now automatically send ' .. value .. ' more stones!')
end
should_update_stone_collecting = true
stone_collecting.market_modifier = stone_collecting.market_modifier + value
end
end
end
local force
if (should_update_mining_speed) then
force = force or game.forces.player
update_mining_speed(force)
end
if (should_update_inventory_slots) then
force = force or game.forces.player
update_inventory_slots(force)
end
if (should_update_stone_collecting) then
update_stone_collecting()
end
end
local function on_research_finished(event)
local force = game.forces.player
local current_modifier = mining_efficiency.research_modifier
local new_modifier = force.mining_drill_productivity_bonus * config.mining_speed_productivity_multiplier * 0.5
if (current_modifier == new_modifier) then
-- something else was researched
return
end
mining_efficiency.research_modifier = new_modifier
inventory_slots.research_modifier = force.mining_drill_productivity_bonus * 50 -- 1 per level
stone_collecting.research_modifier = force.mining_drill_productivity_bonus * 1250 -- 25 per level
update_inventory_slots(force)
update_mining_speed(force)
update_stone_collecting()
end
local function redraw_title(data)
data.frame.caption = utils.comma_value(stone_tracker.stone_sent_to_surface) .. ' ' .. config.currency_item .. ' sent to the surface'
local force_data = force_control.get_force_data('player')
data.frame.caption = utils.comma_value(force_data.total_experience) .. ' total experience earned!'
end
local function get_data(unlocks, stone, type)
local result = {}
for _, data in pairs(unlocks) do
if calculate_level(data.level) == stone and data.type == type then
if data.level == stone and data.type == type then
insert(result, data)
end
end
@ -259,23 +98,14 @@ local function redraw_heading(data, header)
end
local function redraw_progressbar(data)
local force_data = force_control.get_force_data('player')
local flow = data.market_progressbars
Gui.clear(flow)
-- progress bar for next level
local act_stone = (stone_tracker.current_level ~= 0) and calculate_level(stone_tracker.current_level) or 0
local next_stone = calculate_level(stone_tracker.current_level+1)
local range = next_stone - act_stone
local sent = stone_tracker.stone_sent_to_surface - act_stone
local percentage = (math.floor((sent / range)*1000))*0.001
percentage = (percentage < 0) and (percentage*-1) or percentage
apply_heading_style(flow.add({type = 'label', tooltip = 'Currently at level: ' .. stone_tracker.current_level .. '\nNext level at: ' .. utils.comma_value(next_stone) ..'\nRemaining stone: ' .. utils.comma_value(range - sent), name = 'Diggy.MarketExchange.Frame.Progress.Level', caption = 'Progress to next level:'}).style)
local level_progressbar = flow.add({type = 'progressbar', tooltip = percentage * 100 .. '% stone to next level'})
apply_heading_style(flow.add({type = 'label', tooltip = 'Currently at level: ' .. force_data.current_level .. '\nNext level at: ' .. utils.comma_value((force_data.total_experience - force_data.current_experience) + force_data.experience_level_up_cap) ..' xp\nRemaining xp: ' .. utils.comma_value(force_data.experience_level_up_cap - force_data.current_experience), name = 'Diggy.MarketExchange.Frame.Progress.Level', caption = 'Progress to next level:'}).style)
local level_progressbar = flow.add({type = 'progressbar', tooltip = floor(force_data.experience_percentage*100)*0.01 .. '% xp to next level'})
level_progressbar.style.width = 350
level_progressbar.value = percentage
level_progressbar.value = force_data.experience_percentage * 0.01
end
local function redraw_table(data)
@ -296,10 +126,10 @@ local function redraw_table(data)
-- create table
for i = 1, #config.unlockables do
if calculate_level(config.unlockables[i].level) ~= last_stone then
if config.unlockables[i].level ~= last_stone then
-- get items and buffs for each stone value
items = get_data(config.unlockables, calculate_level(config.unlockables[i].level), 'market')
items = get_data(config.unlockables, config.unlockables[i].level, 'market')
-- get number of rows
number_of_rows = max(#buffs, #items)
@ -311,8 +141,7 @@ local function redraw_table(data)
local level = item.level
-- 1st column
result[6] = calculate_level(level)
result[1] = 'Level ' ..level
result[1] = level
-- 3rd column
if items[j] ~= nil then
result[3] = '+ ' .. item.prototype.name
@ -337,23 +166,23 @@ local function redraw_table(data)
end
-- save lastStone
last_stone = calculate_level(config.unlockables[i].level)
last_stone = config.unlockables[i].level
end
-- print table
for _, unlockable in pairs(row) do
local is_unlocked = unlockable[6] <= stone_tracker.stone_sent_to_surface
local is_unlocked = unlockable[1] <= force_control.get_force_data('player').current_level
local list = market_scroll_pane.add {type = 'table', column_count = 2 }
list.style.horizontal_spacing = 16
local caption = ''
if unlockable[4] ~= true then
caption = unlockable[1]
caption = 'Level ' .. unlockable[1]
else
caption = ''
end
local tag_stone = list.add {type = 'label', name = tag_label_stone, caption = caption}
local tag_stone = list.add {type = 'label', name = tag_label_stone, caption = caption, tooltip = 'XP: ' .. utils.comma_value(Experience.calculate_level_xp(unlockable[1]))}
tag_stone.style.minimal_width = 100
local tag_items = list.add {type = 'label', name = tag_label_item, caption = unlockable[3]}
@ -378,30 +207,27 @@ local function redraw_buff(data) --! Almost equals to the redraw_table() functio
local buff_scroll_pane = data.buff_scroll_pane
Gui.clear(buff_scroll_pane)
local buffs = {}
local number_of_rows = 0
local buffs = Experience.get_buffs()
local row = {}
for i = 1, #config.buffs do
-- get items and buffs for each stone value
buffs = config.buffs
local i = 0
for k, v in pairs(buffs) do
i = i + 1
local result = {}
-- 1st column
result[1] = 'All levels'
-- 2nd column
if buffs[i].prototype.name == 'mining_speed' then
result[2] = '+ '.. buffs[i].prototype.value .. '% mining speed'
elseif buffs[i].prototype.name == 'inventory_slot' then
if buffs[i].prototype.value > 1 then
result[2] = '+ '.. buffs[i].prototype.value .. ' inventory slots'
if k == 'mining_speed' then
result[2] = '+ '.. v.value .. '% mining speed'
elseif k == 'inventory_slot' then
if v.value > 1 then
result[2] = '+ '.. v.value .. ' inventory slots'
else
result[2] = '+ '.. buffs[i].prototype.value .. ' inventory slot'
result[2] = '+ '.. v.value .. ' inventory slot'
end
elseif buffs[i].prototype.name == 'stone_automation' then
result[2] = '+ '.. buffs[i].prototype.value .. ' stones automatically sent'
elseif k == 'health_bonus' then
result[2] = '+ '.. v.value .. ' max health'
else
result[2] = 'Description missing: unknown buff. Please contact admin'
end
@ -437,34 +263,29 @@ local function redraw_buff(data) --! Almost equals to the redraw_table() functio
end
end
local function on_market_item_purchased(event)
if (1 ~= event.offer_index) then
---Interface for force control system to access the primary market
---@return LuaEntity the primary market (The one at spawn)
function MarketExchange.get_market()
local markets = game.surfaces.nauvis.find_entities_filtered({name = 'market', position = config.market_spawn_position, limit = 1})
if (#markets == 0) then
Debug.print_position(config.market_spawn_position, 'Unable to find a market')
return
end
local sum = config.stone_to_surface_amount * event.count
Game.print_player_floating_text(event.player_index, '-' .. sum .. ' stone', {r = 0.6, g = 0.55, b = 0.42})
send_stone_to_surface(sum)
update_market_contents(event.market)
return markets[1]
end
local function on_placed_entity(event)
local market = event.entity
if ('market' ~= market.name) then
if 'market' ~= market.name then
return
end
market.add_market_item({
price = {{config.currency_item, 50}},
offer = {type = 'nothing', effect_description = 'Send ' .. config.stone_to_surface_amount .. ' ' .. config.currency_item .. ' to the surface. To see the overall progress and rewards, click the market button in the menu.'}
})
update_market_contents(market)
end
function MarketExchange.get_extra_map_info(config)
return 'Market Exchange, trade your stone or send it to the surface'
return 'Market Exchange, come make a deal at the foreman\'s shop'
end
local function toggle(event)
@ -530,6 +351,7 @@ Gui.on_custom_close('Diggy.MarketExchange.Frame', function (event)
event.element.destroy()
end)
---Updates the market progress gui for every player that has it open
function MarketExchange.update_gui()
for _, p in ipairs(game.connected_players) do
local frame = p.gui.left['Diggy.MarketExchange.Frame']
@ -537,20 +359,16 @@ function MarketExchange.update_gui()
if frame and frame.valid then
local data = {player = p, trigger = 'update_gui'}
toggle(data)
--toggle(data)
end
end
end
function MarketExchange.on_init()
Task.set_timeout_in_ticks(50, on_market_timeout_finished, {
Task.set_timeout_in_ticks(1, on_market_timeout_finished, {
surface = game.surfaces.nauvis,
position = config.market_spawn_position,
player_force = game.forces.player,
void_chest_tiles = config.void_chest_tiles,
})
update_mining_speed(game.forces.player)
end
--[[--
@ -559,91 +377,10 @@ end
function MarketExchange.register(cfg)
config = cfg
Event.add(defines.events.on_research_finished, on_research_finished)
Event.add(defines.events.on_market_item_purchased, on_market_item_purchased)
--Events
Event.add(Template.events.on_placed_entity, on_placed_entity)
Event.add(defines.events.on_player_created, on_player_created)
local x_min
local y_min
local x_max
local y_max
for _, position in pairs(config.void_chest_tiles) do
local x = position.x
local y = position.y
if (nil == x_min or x < x_min) then
x_min = x
end
if (nil == x_max or x > x_max) then
x_max = x
end
if (nil == y_min or y < y_min) then
y_min = y
end
if (nil == y_max or y > y_max) then
y_max = y
end
end
local area = {{x_min, y_min}, {x_max + 1, y_max + 1}}
local message_x = (x_max + x_min) * 0.5
local message_y = (y_max + y_min) * 0.5
Event.on_nth_tick(config.void_chest_frequency, function ()
local send_to_surface = 0
local surface = game.surfaces.nauvis
local find_entities_filtered = surface.find_entities_filtered
local chests = find_entities_filtered({area = area, type = {'container', 'logistic-container'}})
local to_fetch = stone_collecting.active_modifier
for _, chest in pairs(chests) do
local chest_contents = chest.get_inventory(defines.inventory.chest)
local stone_in_chest = chest_contents.get_item_count(config.currency_item)
local delta = to_fetch
if (stone_in_chest < delta) then
delta = stone_in_chest
end
if (delta > 0) then
chest_contents.remove({name = config.currency_item, count = delta})
send_to_surface = send_to_surface + delta
end
end
if (send_to_surface == 0) then
if (0 == to_fetch) then
return
end
local message = 'Missing chests below market'
if (#chests > 0) then
message = 'No stone in chests found'
end
Game.print_floating_text(surface, {x = message_x, y = message_y}, message, { r = 220, g = 100, b = 50})
return
end
local markets = find_entities_filtered({name = 'market', position = config.market_spawn_position, limit = 1})
if (#markets == 0) then
Debug.print_position(config.market_spawn_position, 'Unable to find a market')
return
end
local message = send_to_surface .. ' stone sent to the surface'
Game.print_floating_text(surface, {x = message_x, y = message_y}, message, { r = 0.6, g = 0.55, b = 0.42})
send_stone_to_surface(send_to_surface)
update_market_contents(markets[1])
end)
Event.on_nth_tick(61, MarketExchange.update_gui)
end
return MarketExchange

View File

@ -96,9 +96,9 @@ function ScatteredResources.register(config)
-- compound cluster spawning
local c_mode = config.cluster_mode
local c_clusters = require(config.cluster_file_location)
if ('table' ~= type(c_clusters)) then
error('cluster_file_location invalid')
local c_clusters = config.ore_pattern
if 'table' ~= type(c_clusters) then
error('ore_pattern invalid')
end
local c_count = 0
for _, cluster in ipairs(c_clusters) do

View File

@ -1,54 +0,0 @@
-- dependencies
local Config = require 'map_gen.Diggy.Config'.features.MarketExchange
-- this
local MarketUnlockables = {}
local floor = math.floor
local ceil = math.ceil
local log10 = math.log10
--- Handles a truncation of numbers to create simple large numbers
-- eg. 1593251 could become 1590000 with precision 3
-- number larger than 10 million will have an precision of +1
-- @param precision number of the precision wanted (number of significant digits)
-- @param precise_number number that needs to be truncated/simplified
--
local function truncate(precision, precise_number)
local number = precise_number
local number_length = floor(log10(number)+1)
precision = (number_length >= 8) and (precision+1) or precision
local exponent = number_length -precision
number = number/10^exponent
number = floor(number)*10^exponent
return number, number_length
end
--- Handles the level requirement to stone sent. Calculates based on a formula one number corresponding to that levels cost
-- You can configure this in Diggy.Config.lua under features.MarketExchange
-- @param level number of a level
-- @returns number of cost corresponding to the level based on a calculation
--
function MarketUnlockables.calculate_level(level) -- all configurable variables must be integers.
local b = floor(Config.difficulty_scale) or 25 -- Default 25 <-- Controls how much stone is needed.
local start_value = floor(Config.start_stone) or 50 -- The start value/the first level cost
local formula = b*(level^3)+(start_value-b)
local precision = floor(Config.cost_precision) or 2 -- Sets the precision
-- Truncates to the precision and prevents duplicates by incrementing with 5 in the third highest place.
-- First evaluates loosely if the previous level requirement would return same number after truncating.
-- If true evaluates down all levels to level 1 for the precise number
-- (In case itself got incremented)
-- Only useful if three or more values turns out to be the same after truncating, thus the loosely evaluation to save an expensive recursive function
local number, number_lenght = truncate(precision, formula)
local prev_number = truncate(precision, b*((level-1)^3)+(start_value-b))
if (level ~= 1 and number == prev_number) then
local prev_number = MarketUnlockables.calculate_level((level-1))
while (prev_number >= number) do
number = (prev_number < number) and number or ceil(number + (5*10^(number_lenght -3)))
end
end
return number
end
return MarketUnlockables

View File

@ -39,7 +39,7 @@ end
--[[--
Register the events required to initialize the scenario.
]]
function Scenario.register(debug)
function Scenario.register(debug, cheats)
if global.diggy_scenario_registered then
error('Cannot register the Diggy scenario multiple times.')
return
@ -50,15 +50,11 @@ function Scenario.register(debug)
global.scenario.config.fish_market.enable = nil
end
if ('boolean' == type(debug)) then
Config.Debug = debug
end
if (Config.debug) then
if debug then
Debug.enable_debug()
end
if (Config.cheats) then
if cheats then
Debug.enable_cheats()
end

View File

@ -12,73 +12,47 @@ Global.register({
scores = tbl.scores
end)
--[[--
Resets the score 0 for the given name
@param name String
]]
---Resets the score 0 for the given name
---@param name string
function ScoreTable.reset(name)
scores[name] = 0
end
--[[--
Adds score.
@param name String
@param value int amount to add
@return int the sum for the score added by name
]]
---Adds score.
---@param name string
---@param value number the sum for the score added by name
---@return number the sum for the score added by the name
function ScoreTable.add(name, value)
local new = (scores[name] or 0) + value
scores[name] = new
return new
end
--[[--
Increments the score by 1 for name.
---Sets score.
---@param name string
---@param value number the sum for the score added by name
function ScoreTable.set(name, value)
scores[name] = value
end
@param name String
@return int the sum for the score incremented by name
]]
---Increments the score by 1 for name.
---@param name string
---@return number the sum for the score incremented by name
function ScoreTable.increment(name)
return ScoreTable.add(name, 1)
end
--[[--
Returns the score for a single key.
@param
]]
---Returns the score for a single key.
---@param name string
---@return number the sum for the score by name
function ScoreTable.get(name)
return scores[name] or 0
end
--[[--
Returns all scores.
@return table {[string] = int}
]]
---Returns all scores.
---@return table {[string] = int}
function ScoreTable.all()
return scores
end
--[[--
Returns all keys of table scores.
@return table {[string] = name of key}
]]
function ScoreTable.all_keys()
local keyset = {}
local n = 0
for k, v in pairs(scores) do
n = n + 1
keyset[n] = k
end
return keyset
end
return ScoreTable

View File

@ -163,15 +163,10 @@ end
@param force LuaForce
@param market_items Table
]]
function Template.market(surface, position, force, market_inventory)
function Template.market(surface, position, force)
local market = surface.create_entity({name = 'market', position = position})
local add_market_item = market.add_market_item
market.destructible = false
for _, item in ipairs(market_inventory) do
add_market_item(item)
end
force.add_chart_tag(surface, {
text = 'Market',
position = position,

View File

@ -1,2 +1,2 @@
-- authors Linaori, valansch
require 'map_gen.Diggy.Scenario'.register(_DEBUG)
require 'map_gen.Diggy.Scenario'.register(_DEBUG, _CHEATS)

View File

@ -0,0 +1,159 @@
require 'utils.list_utils'
local Game = require 'utils.game'
local Event = require 'utils.event'
local naming_words = require 'resources.naming_words'
local Utils = require('utils.utils')
global.actual_name = {}
global.silly_names = {}
global.silly_names.count = 0
local name_combinations = #naming_words.adverbs * #naming_words.adjectives * #naming_words.nouns
--- Creates name by combining elements from the passed table
-- @param1 table including adverbs, adjectives, and nouns
-- @returns name as a string
local function create_name(words_table)
local adverb, adjective, noun
adverb = table.get_random(words_table.adverbs)
adjective = table.get_random(words_table.adjectives)
noun = table.get_random(words_table.nouns)
return adverb .. '_' .. adjective .. '_' .. noun
end
--- Calls create_name until a unique name is returned
-- @param1 table including adverbs, adjectives, and nouns
-- @returns name as a string
local function create_unique_name(words_table)
local silly_names = global.silly_names
local name = create_name(words_table)
while table.contains(silly_names, name) do
name = create_name(words_table)
end
return name
end
--- Assigns a player a name, stores their old and silly names
-- @param1 Takes a LuaPlayer
local function name_player(player)
-- Store a player's original name in case they want it back.
if not global.actual_name[player.index] then
global.actual_name[player.index] = player.name
end
-- Because create_unique_name enters a while loop looking for a unique name, ensure we never get stuck.
local ceiling = math.min(name_combinations * 0.25, 10000)
if global.silly_names.count > ceiling then
global.silly_names = {}
global.silly_names.count = 0
end
local name = create_unique_name(naming_words)
table.insert(global.silly_names, name)
global.silly_names.count = global.silly_names.count + 1
local str = player.name .. ' will now be known as: ' .. name
game.print(str)
Utils.print_admins(str .. ' (ID: ' .. player.index .. ')', false)
player.name = name
end
--- Restores a player's actual name
local function restore_name(cmd)
local player = Game.get_player_by_index(cmd.player_index)
player.name = global.actual_name[player.index]
player.print('Your true name has been restored.')
end
--- Passes _event_ on to name_players
local function name_player_event(event)
local player = Game.get_player_by_index(event.player_index)
name_player(player)
end
--- Passes target or player on to name_players
local function name_player_command(cmd)
local player = game.player
local param = cmd.parameter
local target
if param then
target = game.players[param]
if player and not player.admin then
-- Yes param, yes player, no admin/server = fail, non-admins, non-server cannot use command on others
Game.player_print("Sorry you don't have permission to use the roll-name command on other players.")
return
else
-- Yes param, yes admin/server = check target
if target then
-- Yes param, yes admin/server, yes target = change name
name_player(target)
return
else
-- Yes param, yes admin/server, no target = fail, wrong player name
Game.player_print(table.concat {"Sorry, player '", param, "' was not found."})
return
end
end
else
-- No param = check if server
if not player then
-- No param, no player = server trying to change its name
Game.player_print('The server cannot change its name')
return
end
-- No param, not server = change self name
name_player(player)
return
end
end
--- Prints the original name of the target
local function check_name(cmd)
local current_name = cmd.parameter
if not current_name then
Game.player_print('Usage: /name-check <player>')
return
end
local target = game.players[current_name]
if not target then
Game.player_print('player ' .. current_name .. ' not found')
return
end
local actual_name = global.actual_name[target.index]
Game.player_print(target.name .. ' is actually: ' .. actual_name)
end
--- Prints the index of the target
local function get_player_id(cmd)
local player = game.player
-- Check if the player can run the command
if player and not player.admin then
Utils.cant_run(cmd.name)
return
end
-- Check if the target is valid
local target_name = cmd['parameter']
if not target_name then
Game.player_print('Usage: /get-player-id <player>')
return
end
local target_index = game.players[target_name].index
Game.player_print(target_name .. ' -- ' .. target_index)
end
if global.scenario.config.players_assigned_names == true then
Event.add(defines.events.on_player_created, name_player_event)
end
if global.scenario.config.players_roll_names == true then
commands.add_command('name-roll', 'Assigns you a random, silly name', name_player_command)
end
if global.scenario.config.players_roll_names == true or global.scenario.config.players_assigned_names == true then
commands.add_command('name-restore', 'Removes your fun name and gives you back your actual name', restore_name)
commands.add_command('name-check', '<player> Check the original name of a player', check_name)
commands.add_command('get-player-id', 'Gets the ID of a player (Admin only)', get_player_id)
end

View File

@ -5,6 +5,7 @@
-- Obtain items using silent commands from scenarios\RedMew\map_gen\data\presets\tetris\
-- Place the blueprint on the island south of spawn
-- Teleport to centre of island and run the second command in tetris_theme_items_command.txt
-- Excellent tetris themed music generated from midi files, credit to mgabor of miditorio.com
local b = require 'map_gen.shared.builders'
local math = require 'utils.math'

View File

@ -139,6 +139,7 @@ local terrain_modules = {
--require 'map_gen.misc.nightfall'
--require 'map_gen.misc.creep_spread'
--require 'map_gen.misc.car_body'
--require 'features.silly_player_names'
if #entity_modules > 0 then
shape = shape or b.full_shape

253
resources/naming_words.lua Normal file
View File

@ -0,0 +1,253 @@
-- Currently geared towards the winter holidays but we can always revise if needed.
-- (Ex. a table of standard/common words and tables for season-specific words, then combined for specific purposes/use-cases)
local Module = {}
Module.adverbs = {
'Abnormally',
'Accidentally',
'Adventurously',
'Anxiously',
'Arrogantly',
'Awkwardly',
'Bashfully',
'Beautifully',
'Blindly',
'Blissfully',
'Boastfully',
'Boldly',
'Bravely',
'Calmly',
'Carefully',
'Carelessly',
'Cautiously',
'Certainly',
'Cheerfully',
'Clearly',
'Closely',
'Continually',
'Courageously',
'Cruelly',
'Curiously',
'Dearly',
'Deeply',
'Defiantly',
'Deliberately',
'Delightfully',
'Dimly',
'Doubtfully',
'Dreamily',
'Easily',
'Elegantly',
'Energetically',
'Enormously',
'Enthusiastically',
'Especially',
'Eventually',
'Excitedly',
'Extremely',
'Fairly',
'Faithfully',
'Famously',
'Fatally',
'Ferociously',
'Fervently',
'Fiercely',
'Fondly',
'Foolishly',
'Frantically',
'Frightfully',
'Furiously',
'Generally',
'Generously',
'Gently',
'Gladly',
'Gleefully',
'Gracefully',
'Greatly',
'Happily',
'Heavily',
'Helpfully',
'Helplessly',
'Honestly',
'Hopelessly',
'Hungrily',
'Innocently',
'Inquisitively',
'Intensely',
'Interestingly',
'Irritably',
'Jovially',
'Joyfully',
'Kindheartedly',
'Kindly',
'Knowingly',
'Lazily',
'Lively',
'Longingly',
'Loosely',
'Loudly',
'Lovingly',
'Loyally',
'Madly',
'Majestically',
'Merrily',
'Miserably',
'Mockingly',
'Mostly',
'Mysteriously',
'Naturally',
'Nearly',
'Nicely',
'Noisily',
'Obnoxiously',
'Offensively',
'Optimistically',
'Painfully',
'Patiently',
'Perfectly',
'Playfully',
'Politely',
'Positively',
'Powerfully',
'Questionably',
'Quickly',
'Quietly',
'Randomly',
'Rapidly',
'Readily',
'Reluctantly',
'Righteously',
'Safely',
'Seemingly',
'Selfishly',
'Silently',
'Sleepily',
'Stealthily',
'Successfully',
'Suddenly',
'Surprisingly',
'Suspiciously',
'Sweetly',
'Tenderly',
'Thankfully',
'Unbearably',
'Unexpectedly',
'Unfortunately',
'Unimpressively',
'Unnecessarily',
'Urgently',
'Uselessly',
'Vaguely',
'Warmly',
'Wildly',
'Wisely',
'Wrongly',
'Youthfully',
}
Module.adjectives = {
'Amazing',
'Attractive',
'Awesome',
'Beautiful',
'Big',
'Blissful',
'Brave',
'Breathtaking',
'Careful',
'Caroling',
'Chubby',
'Clever',
'Clumsy',
'Dazzling',
'Delightful',
'Eager',
'Excitable',
'Fabulous',
'Faithful',
'Fancy',
'Fantastic',
'Fantastical',
'Fierce',
'Gentle',
'Glamorous',
'Gorgeous',
'Great',
'Grumpy',
'Happy',
'Helpful',
'Interesting',
'Jolly',
'Kind',
'Lazy',
'Magical',
'Magnificent',
'Merry',
'Muscular',
'Mystical',
'Naughty',
'Nice',
'Obedient',
'Peaceful',
'Plump',
'Polite',
'Quaint',
'Remarkable',
'Roasting',
'Scary',
'Silly',
'Small',
'Tender',
'Thoughtful',
'White',
'Witty',
'Wonderful',
}
Module.nouns = {
'Advent',
'Angel',
'Bear',
'Bell',
'Candy Cane',
'Caroler',
'Chestnut',
'Christmas Stocking',
'Decoration',
'Elf',
'Feast',
'Fruit Cake',
'Garland',
'Gift',
'Grinch',
'Ham',
'Helper',
'Holiday Dinner',
'Holiday Sweater',
'Holly',
'Ice Skate',
'Ivy',
'Menora',
'Miracle',
'Mistletoe',
'Ornament',
'Package',
'Party',
'Present',
'Reindeer',
'Ribbon',
'Scarf',
'Scrooge',
'Sleigh',
'Snowball',
'Snowflake',
'Snowman',
'Sugarplum',
'Toy',
'Tree',
'Turkey',
'Wreath',
}
return Module

View File

@ -68,7 +68,6 @@ end
--[[
Creates a floating text entity at the player location with the specified color in {r, g, b} format.
Example: "+10 iron" or "-10 coins"
@param text String to display
@ -76,14 +75,19 @@ end
@return the created entity
]]
function Game.print_player_floating_text(player_index, text, color)
function Game.print_player_floating_text_position(player_index, text, color, x_offset, y_offset)
local player = Game.get_player_by_index(player_index)
if not player or not player.valid then
return
end
local position = player.position
return Game.print_floating_text(player.surface, {x = position.x, y = position.y - 1.5}, text, color)
return Game.print_floating_text(player.surface, {x = position.x + x_offset, y = position.y + y_offset}, text, color)
end
function Game.print_player_floating_text(player_index, text, color)
Game.print_player_floating_text_position(player_index, text, color, 0, -1.5)
end
return Game

View File

@ -16,7 +16,9 @@ Module.print_except = function(msg, player)
end
end
-- Takes a LuaPlayer or string as source
--- Prints a message to all online admins
--@param1 The message to print, as a string
--@param2 The source of the message, as a string, LuaPlayer, or nil.
Module.print_admins = function(msg, source)
local source_name
local chat_color
@ -41,6 +43,7 @@ Module.print_admins = function(msg, source)
end
end
--- Returns a valid string with the name of the actor of a command.
Module.get_actor = function()
if game.player then
return game.player.name
@ -119,22 +122,20 @@ Module.format_time = function(ticks)
return table.concat(result, ' ')
end
--- Prints a message letting the player know they cannot run a command
-- @param1 name of the command
Module.cant_run = function(name)
Game.player_print("Can't run command (" .. name .. ') - insufficient permission.')
end
Module.log_command = function(user, command, parameters)
local name
-- We can use a LuaPlayer or a string (ex. "Server").
if type(user) == 'string' then
name = user
else
name = user.name
end
local action = table.concat {'[Admin-Command] ', name, ' used: ', command}
--- Logs the use of a command and its user
-- @param1 takes a string with the actor's name (usually acquired by calling get_actor)
-- @param2 the command's name as table element
-- @param3 the command's parameters as a table element (optional)
Module.log_command = function(actor, command, parameters)
local action = table.concat {'[Admin-Command] ', actor, ' used: ', command}
if parameters then
action = table.concat {'[Admin-Command] ', name, ' used: ', command, ' ', parameters}
action = table.concat {action, ' ', parameters}
end
log(action)
end