1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2025-01-08 00:39:30 +02:00

Merge pull request #8 from M3wM3w/master

update from main
This commit is contained in:
hanakocz 2020-05-22 17:36:31 +02:00 committed by GitHub
commit 3b5a9e27b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 8083 additions and 113 deletions

View File

@ -39,31 +39,40 @@ local function on_player_demoted(event)
if player.gui.left["admin_panel"] then player.gui.left["admin_panel"].destroy() end
end
]]
local function on_marked_for_deconstruction(event)
local tracker = session.get_session_table()
local trusted = session.get_trusted_table()
if not event.player_index then return end
local player = game.players[event.player_index]
if player.admin == true then return end
if trusted[player.name] == true then return end
local tracker = session.get_session_table()
local trusted = session.get_trusted_table()
if not event.player_index then
return
end
local player = game.players[event.player_index]
if player.admin == true then
return
end
if trusted[player.name] == true then
return
end
local playtime = player.online_time
if tracker[player.name] then
playtime = player.online_time + tracker[player.name]
end
if playtime < 2592000 then
event.entity.cancel_deconstruction(game.players[event.player_index].force.name)
player.print("You have not grown accustomed to this technology yet.", { r=0.22, g=0.99, b=0.99})
end
local playtime = player.online_time
if tracker[player.name] then
playtime = player.online_time + tracker[player.name]
end
if playtime < 2592000 then
event.entity.cancel_deconstruction(game.players[event.player_index].force.name)
player.print('You have not grown accustomed to this technology yet.', {r = 0.22, g = 0.99, b = 0.99})
end
end
local function on_player_ammo_inventory_changed(event)
local tracker = session.get_session_table()
local trusted = session.get_trusted_table()
local player = game.players[event.player_index]
if player.admin == true then return end
if trusted[player.name] == true then return end
local tracker = session.get_session_table()
local trusted = session.get_trusted_table()
local player = game.players[event.player_index]
if player.admin == true then
return
end
if trusted[player.name] == true then
return
end
local playtime = player.online_time
if tracker[player.name] then
@ -75,19 +84,25 @@ local function on_player_ammo_inventory_changed(event)
player.surface.spill_item_stack(player.position, {name = "atomic-bomb", count = nukes}, false)
player.print("You have not grown accustomed to this technology yet.", {r=0.22, g=0.99, b=0.99})
Server.to_discord_bold(table.concat{'[Nuke] ' .. player.name .. ' tried to equip nukes but was not trusted.'})
game.print('[Nuke] ' .. player.name .. ' tried to equip nukes but was not trusted.', {r=0.22, g=0.99, b=0.99})
player.character.health = 0
end
end
end
local function on_player_built_tile(event)
local tracker = session.get_session_table()
local trusted = session.get_trusted_table()
local placed_tiles = event.tiles
if placed_tiles[1].old_tile.name ~= "deepwater" and placed_tiles[1].old_tile.name ~= "water" and placed_tiles[1].old_tile.name ~= "water-green" then return end
local player = game.players[event.player_index]
local tracker = session.get_session_table()
local trusted = session.get_trusted_table()
local placed_tiles = event.tiles
if
placed_tiles[1].old_tile.name ~= 'deepwater' and placed_tiles[1].old_tile.name ~= 'water' and
placed_tiles[1].old_tile.name ~= 'water-green'
then
return
end
local player = game.players[event.player_index]
--[[
--[[
if not player.admin and not trusted[player.name] then
local playtime = player.online_time
if tracker[player.name] then
@ -107,7 +122,9 @@ local function on_player_built_tile(event)
--landfill history--
if not global.landfill_history then global.landfill_history = {} end
if #global.landfill_history > 999 then global.landfill_history = {} end
local str = player.name .. " at X:"
local t = math.abs(math.floor((game.tick) / 3600))
local str = "[" .. t .. "] "
str = str .. player.name .. " at X:"
str = str .. placed_tiles[1].position.x
str = str .. " Y:"
str = str .. placed_tiles[1].position.y
@ -115,26 +132,32 @@ local function on_player_built_tile(event)
end
local function on_built_entity(event)
local tracker = session.get_session_table()
local trusted = session.get_trusted_table()
if game.tick < 1296000 then return end
if event.created_entity.type == "entity-ghost" then
local player = game.players[event.player_index]
if player.admin == true then return end
if trusted[player.name] == true then return end
local playtime = player.online_time
if tracker[player.name] then
playtime = player.online_time + tracker[player.name]
end
if playtime < 432000 then
event.created_entity.destroy()
player.print("You have not grown accustomed to this technology yet.", { r=0.22, g=0.99, b=0.99})
end
end
local tracker = session.get_session_table()
local trusted = session.get_trusted_table()
if game.tick < 1296000 then
return
end
if event.created_entity.type == 'entity-ghost' then
local player = game.players[event.player_index]
if player.admin == true then
return
end
if trusted[player.name] == true then
return
end
local playtime = player.online_time
if tracker[player.name] then
playtime = player.online_time + tracker[player.name]
end
if playtime < 432000 then
event.created_entity.destroy()
player.print('You have not grown accustomed to this technology yet.', {r = 0.22, g = 0.99, b = 0.99})
end
end
end
--Artillery History and Antigrief
@ -162,7 +185,9 @@ local function on_player_used_capsule(event)
if not global.artillery_history then global.artillery_history = {} end
if #global.artillery_history > 999 then global.artillery_history = {} end
local str = player.name .. " at X:"
local t = math.abs(math.floor((game.tick) / 3600))
local str = "[" .. t .. "] "
str = str .. player.name .. " at X:"
str = str .. math.floor(position.x)
str = str .. " Y:"
str = str .. math.floor(position.y)
@ -170,15 +195,15 @@ local function on_player_used_capsule(event)
end
local blacklisted_types = {
["transport-belt"] = true,
["wall"] = true,
["underground-belt"] = true,
["inserter"] = true,
["land-mine"] = true,
["gate"] = true,
["lamp"] = true,
["mining-drill"] = true,
["splitter"] = true
['transport-belt'] = true,
['wall'] = true,
['underground-belt'] = true,
['inserter'] = true,
['land-mine'] = true,
['gate'] = true,
['lamp'] = true,
['mining-drill'] = true,
['splitter'] = true
}
--Friendly Fire History
@ -190,8 +215,11 @@ local function on_entity_died(event)
local player = event.cause.player
if not global.friendly_fire_history then global.friendly_fire_history = {} end
if #global.friendly_fire_history > 999 then global.friendly_fire_history = {} end
if not player then return end
local str = player.name .. " destroyed "
if not player then return end
local t = math.abs(math.floor((game.tick) / 3600))
local str = "[" .. t .. "] "
str = str .. player.name .. " destroyed "
str = str .. event.entity.name
str = str .. " at X:"
str = str .. math.floor(event.entity.position.x)
@ -201,30 +229,6 @@ local function on_entity_died(event)
global.friendly_fire_history[#global.friendly_fire_history + 1] = str
end
local chest_types = {
["container"] = true,
["logistic-container"] = true
}
--Bad Fire History
local function on_entity_died_do_actual_usefull_logging(event)
if not event.cause then return end
if event.cause.name ~= "character" then return end
local player = event.cause.player
if not player then return end
if not chest_types[event.entity.type] then return end
if event.entity.get_item_count("explosives") < 100 then return end
if not global.bad_fire_history then global.bad_fire_history = {} end
if #global.bad_fire_history > 999 then global.bad_fire_history = {} end
local str = player.name .. " destroyed container with explosives amount: " .. event.entity.get_item_count("explosives") .. ", at location: "
str = str .. " at X:" .. event.entity.position.x .. " Y:" .. event.entity.position.y
global.bad_fire_history[#global.bad_fire_history + 1] = str
end
--Mining Thieves History
local function on_player_mined_entity(event)
if not event.entity.last_user then return end
@ -236,7 +240,9 @@ local function on_player_mined_entity(event)
if not global.mining_history then global.mining_history = {} end
if #global.mining_history > 999 then global.mining_history = {} end
local str = player.name .. " mined "
local t = math.abs(math.floor((game.tick) / 3600))
local str = "[" .. t .. "] "
str = str .. player.name .. " mined "
str = str .. event.entity.name
str = str .. " at X:"
str = str .. math.floor(event.entity.position.x)
@ -247,28 +253,54 @@ local function on_player_mined_entity(event)
end
local function on_gui_opened(event)
if not event.entity then return end
if event.entity.name ~= "character-corpse" then return end
local player = game.players[event.player_index]
local corpse_owner = game.players[event.entity.character_corpse_player_index]
if not corpse_owner then return end
if corpse_owner.force.name ~= player.force.name then return end
if player.name ~= corpse_owner.name then
game.print(player.name .. " is looting " .. corpse_owner.name .. "´s body.", { r=0.85, g=0.85, b=0.85})
Server.to_discord_bold(table.concat{'[Corpse] ' .. player.name .. " is looting " .. corpse_owner.name .. "´s body."})
end
if not event.entity then
return
end
if event.entity.name ~= 'character-corpse' then
return
end
local player = game.players[event.player_index]
local corpse_owner = game.players[event.entity.character_corpse_player_index]
if not corpse_owner then
return
end
if corpse_owner.force.name ~= player.force.name then
return
end
if player.name ~= corpse_owner.name then
game.print(player.name .. ' is looting ' .. corpse_owner.name .. '´s body.', {r = 0.85, g = 0.85, b = 0.85})
Server.to_discord_bold(
table.concat {'[Corpse] ' .. player.name .. ' is looting ' .. corpse_owner.name .. '´s body.'}
)
end
end
local function on_pre_player_mined_item(event)
if event.entity.name ~= "character-corpse" then return end
local player = game.players[event.player_index]
local corpse_owner = game.players[event.entity.character_corpse_player_index]
if not corpse_owner then return end
if corpse_owner.force.name ~= player.force.name then return end
if player.name ~= corpse_owner.name then
game.print(player.name .. " has looted " .. corpse_owner.name .. "´s body.", { r=0.85, g=0.85, b=0.85})
Server.to_discord_bold(table.concat{'[Corpse] ' .. player.name .. " has looted " .. corpse_owner.name .. "´s body."})
end
if event.entity.name ~= 'character-corpse' then
return
end
local player = game.players[event.player_index]
local corpse_owner = game.players[event.entity.character_corpse_player_index]
if not corpse_owner then
return
end
local entity = event.entity
if not entity then
return
end
local corpse_content = #entity.get_inventory(defines.inventory.character_corpse)
if corpse_content <= 0 then
return
end
if corpse_owner.force.name ~= player.force.name then
return
end
if player.name ~= corpse_owner.name then
game.print(player.name .. ' has looted ' .. corpse_owner.name .. '´s body.', {r = 0.85, g = 0.85, b = 0.85})
Server.to_discord_bold(
table.concat {'[Corpse] ' .. player.name .. ' has looted ' .. corpse_owner.name .. '´s body.'}
)
end
end
event.add(defines.events.on_player_mined_entity, on_player_mined_entity)

View File

@ -70,6 +70,7 @@ require 'modules.autostash'
--require 'maps.chronosphere.main'
--require 'maps.fish_defender.main'
--require 'maps.biter_battles_v2.main'
--require 'maps.mountain_fortress_v3.main'
--require 'maps.mountain_fortress_v2.main'
--require 'maps.lumberjack.main'
--require 'maps.dungeons.main'

View File

@ -18,7 +18,7 @@ local function reset_forces(new_surface, old_surface)
end
for _, tech in pairs(game.forces.player.technologies) do
tech.researched = false
game.player.force.set_saved_technology_progress(tech, 0)
game.forces.player.set_saved_technology_progress(tech, 0)
end
end

View File

@ -0,0 +1,70 @@
local Event = require 'utils.event'
local Difficulty = require 'modules.difficulty_vote'
local Public = {}
function Public.init_enemy_weapon_damage()
local data = {
['artillery-shell'] = 0,
['biological'] = 0.1,
['bullet'] = 2,
['cannon-shell'] = 0,
['capsule'] = 0,
['combat-robot-beam'] = 0,
['combat-robot-laser'] = 0,
['electric'] = 0,
['flamethrower'] = 0,
['grenade'] = 0,
['landmine'] = 0,
['laser-turret'] = 0,
['melee'] = 0.5,
['railgun'] = 0,
['rocket'] = 0,
['shotgun-shell'] = 0
}
local e = game.forces.enemy
for k, v in pairs(data) do
e.set_ammo_damage_modifier(k, v)
end
end
local function enemy_weapon_damage()
local Diff = Difficulty.get()
if game.tick < 100 then
goto rtn
end
local e = game.forces.enemy
local data = {
['artillery-shell'] = 0.1,
['biological'] = 0.1,
['bullet'] = 0.1,
['capsule'] = 0.1,
['combat-robot-beam'] = 0.1,
['combat-robot-laser'] = 0.1,
['electric'] = 0.1,
['flamethrower'] = 0.1,
--['grenade'] = 0.1,
--['landmine'] = 0.1,
['laser-turret'] = 0.1,
['melee'] = 0.1
--['railgun'] = 0.1,
--['rocket'] = 0.1,
--['shotgun-shell'] = 0.1
}
for k, v in pairs(data) do
local new = Diff.difficulty_vote_value * v
local e_old = e.get_ammo_damage_modifier(k)
e.set_ammo_damage_modifier(k, new + e_old)
end
::rtn::
end
Event.on_nth_tick(54000, enemy_weapon_damage)
return Public

View File

@ -0,0 +1,201 @@
local WPT = require 'maps.mountain_fortress_v3.table'
local math_random = math.random
local nom_msg = {'munch', 'munch', 'yum'}
local Public = {}
local function feed_floaty_text(unit)
unit.surface.create_entity(
{
name = 'flying-text',
position = unit.position,
text = nom_msg[math_random(1, #nom_msg)],
color = {math_random(50, 100), 0, 255}
}
)
end
local function floaty_hearts(entity, c)
local position = {x = entity.position.x - 0.75, y = entity.position.y - 1}
local b = 1.35
for a = 1, c, 1 do
local p = {
(position.x + 0.4) + (b * -1 + math_random(0, b * 20) * 0.1),
position.y + (b * -1 + math_random(0, b * 20) * 0.1)
}
entity.surface.create_entity(
{name = 'flying-text', position = p, text = '', color = {math_random(150, 255), 0, 255}}
)
end
end
local function tame_unit_effects(player, entity)
floaty_hearts(entity, 7)
rendering.draw_text {
text = '~' .. player.name .. "'s pet~",
surface = player.surface,
target = entity,
target_offset = {0, -2.6},
color = {
r = player.color.r * 0.6 + 0.25,
g = player.color.g * 0.6 + 0.25,
b = player.color.b * 0.6 + 0.25,
a = 1
},
scale = 1.05,
font = 'default-large-semibold',
alignment = 'center',
scale_with_zoom = false
}
end
local function find_unit(player, entity)
local units =
player.surface.find_entities_filtered(
{
type = 'unit',
area = {{entity.position.x - 1, entity.position.y - 1}, {entity.position.x + 1, entity.position.y + 1}},
limit = 1
}
)
return units[1]
end
local function feed_pet(unit)
if unit.prototype.max_health == unit.health then
return
end
unit.health = unit.health + 8 + math.floor(unit.prototype.max_health * 0.05)
feed_floaty_text(unit)
floaty_hearts(unit, math_random(1, 2))
return true
end
local function is_valid_player(player, unit)
if not player.character then
return
end
if not player.character.valid then
return
end
if player.surface.index ~= unit.surface.index then
return
end
return true
end
function Public.biter_pets_tame_unit(player, unit, forced)
local this = WPT.get()
if this.biter_pets[player.index] then
return false
end
if not forced then
if math_random(1, math.floor(unit.prototype.max_health * 0.01) + 1) ~= 1 then
feed_floaty_text(unit)
return true
end
end
if unit.force.index == player.force.index then
return false
end
unit.ai_settings.allow_destroy_when_commands_fail = false
unit.ai_settings.allow_try_return_to_spawner = false
unit.force = player.force
unit.set_command({type = defines.command.wander, distraction = defines.distraction.by_enemy})
this.biter_pets[player.index] = {last_command = 0, entity = unit}
tame_unit_effects(player, unit)
return true
end
function Public.tame_unit_for_closest_player(unit)
local valid_players = {}
for _, player in pairs(game.connected_players) do
if is_valid_player(player, unit) then
table.insert(valid_players, player)
end
end
local nearest_player = valid_players[1]
if not nearest_player then
return
end
Public.biter_pets_tame_unit(nearest_player, unit, true)
end
local function command_unit(entity, player)
if entity.surface ~= player.surface then
return
end
local square_distance = (player.position.x - entity.position.x) ^ 2 + (player.position.y - entity.position.y) ^ 2
--Pet will follow, if the player is between a distance of 8 to 160 tiles away from it.
if square_distance < 64 or square_distance > 25600 then
entity.set_command({type = defines.command.wander, distraction = defines.distraction.by_enemy})
else
entity.set_command(
{
type = defines.command.go_to_location,
destination_entity = player.character,
radius = 4,
distraction = defines.distraction.by_damage
}
)
end
end
local function on_player_changed_position(event)
local this = WPT.get()
if math_random(1, 100) ~= 1 then
return
end
local player = game.players[event.player_index]
if not this.biter_pets[player.index] then
return
end
if not this.biter_pets[player.index].entity then
this.biter_pets[player.index] = nil
return
end
if not this.biter_pets[player.index].entity.valid then
this.biter_pets[player.index] = nil
return
end
if not player.character then
return
end
if this.biter_pets[player.index].last_command + 600 > game.tick then
return
end
this.biter_pets[player.index].last_command = game.tick
command_unit(this.biter_pets[player.index].entity, player)
end
local function on_player_dropped_item(event)
local player = game.players[event.player_index]
if event.entity.stack.name ~= 'raw-fish' then
return
end
local unit = find_unit(player, event.entity)
if not unit then
return
end
if Public.biter_pets_tame_unit(player, unit, false) then
event.entity.destroy()
return
end
if unit.force.index == player.force.index then
feed_pet(unit)
end
end
local event = require 'utils.event'
event.add(defines.events.on_player_dropped_item, on_player_dropped_item)
event.add(defines.events.on_player_changed_position, on_player_changed_position)
return Public

View File

@ -0,0 +1,116 @@
local Color = require 'utils.color_presets'
local Task = require 'utils.task'
local WPT = require 'maps.mountain_fortress_v3.table'
local mapkeeper = '[color=blue]Mapkeeper:[/color]'
commands.add_command(
'reset_game',
'Usable only for admins - resets the game!',
function()
local p
local player = game.player
local reset_map = require 'maps.mountain_fortress_v3.main'.reset_map
local this = WPT.get()
if player then
if player ~= nil then
p = player.print
if not player.admin then
p("[ERROR] You're not admin!", Color.fail)
return
end
else
p = log
end
end
if not this.reset_are_you_sure then
this.reset_are_you_sure = true
player.print(
'[WARNING] This command will reset the current game, run this command again if you really want to do this!',
Color.yellow
)
return
end
game.print(mapkeeper .. ' ' .. player.name .. ', has reset the game!', {r = 0.98, g = 0.66, b = 0.22})
this.reset_are_you_sure = nil
reset_map()
end
)
commands.add_command(
'disable_reset_game',
'Usable only for admins - disables the auto-reset of map!',
function(cmd)
local p
local player = game.player
local this = WPT.get()
local param = tostring(cmd.parameter)
if player then
if player ~= nil then
p = player.print
if not player.admin then
p("[ERROR] You're not admin!", Color.fail)
return
end
else
p = log
end
end
if not param then
p('[ERROR] Arguments are true/false', Color.yellow)
return
end
if not this.reset_are_you_sure then
this.reset_are_you_sure = true
player.print(
'[WARNING] This command will disable the auto-reset feature, run this command again if you really want to do this!',
Color.yellow
)
return
end
if param == 'true' then
if this.disable_reset then
this.reset_are_you_sure = nil
return p('[WARNING] Reset is already disabled!', Color.fail)
end
this.disable_reset = true
p('[SUCCESS] Auto-reset is disabled!', Color.success)
this.reset_are_you_sure = nil
elseif param == 'false' then
if not this.disable_reset then
this.reset_are_you_sure = nil
return p('[WARNING] Reset is already enabled!', Color.fail)
end
this.disable_reset = false
p('[SUCCESS] Auto-reset is enabled!', Color.success)
this.reset_are_you_sure = nil
end
end
)
if _DEBUG then
commands.add_command(
'get_queue_speed',
'Debug only, return the current task queue speed!',
function()
local player = game.player
if player then
if player ~= nil then
if not player.admin then
return
end
end
end
game.print(Task.get_queue_speed())
end
)
end

View File

@ -0,0 +1,466 @@
require 'on_tick_schedule'
require 'modules.rocks_broken_paint_tiles'
local Event = require 'utils.event'
local Map_score = require 'comfy_panel.map_score'
local BiterRolls = require 'modules.wave_defense.biter_rolls'
local Loot = require 'maps.mountain_fortress_v3.loot'
local Pets = require 'maps.mountain_fortress_v3.biter_pets'
local tick_tack_trap = require 'maps.mountain_fortress_v3.tick_tack_trap'
local RPG = require 'maps.mountain_fortress_v3.rpg'
local Mining = require 'maps.mountain_fortress_v3.mining'
local Terrain = require 'maps.mountain_fortress_v3.terrain'
local BiterHealthBooster = require 'modules.biter_health_booster'
-- tables
local WPT = require 'maps.mountain_fortress_v3.table'
local WD = require 'modules.wave_defense.table'
-- module
local Public = {}
local math_random = math.random
local math_floor = math.floor
local math_abs = math.abs
local mapkeeper = '[color=blue]Mapkeeper:[/color]'
local treasure_chest_messages = {
"You notice an old crate within the rubble. It's filled with treasure!",
"You find a chest underneath the broken rocks. It's filled with goodies!",
'We has found the precious!'
}
local rare_treasure_chest_messages = {
'Your magic improves. You have found a chest that is filled with rare treasures!',
"Oh how wonderful. You found a chest underneath the broken rocks. It's filled with rare goodies!",
"You're a wizard! We have found the rare precious!"
}
local disabled_threats = {
['entity-ghost'] = true,
['raw-fish'] = true
}
local defeated_messages = {
"Oh no, the biters nom'ed the train away!",
"I'm not 100% sure, but - apparently the train was chewed away.",
'You had one objective - defend the train *-*',
"Looks like we're resetting cause you did not defend the train ._."
}
local entity_type = {
['unit'] = true,
['unit-spawner'] = true,
['simple-entity'] = true,
['tree'] = true
}
local function set_objective_health(entity, final_damage_amount)
local this = WPT.get()
if final_damage_amount == 0 then
return
end
this.locomotive_health = math_floor(this.locomotive_health - final_damage_amount)
this.cargo_health = math_floor(this.cargo_health - final_damage_amount)
if this.locomotive_health > this.locomotive_max_health then
this.locomotive_health = this.locomotive_max_health
end
if this.cargo_health > this.cargo_max_health then
this.cargo_health = this.cargo_max_health
end
if this.locomotive_health <= 0 then
Public.loco_died()
end
local m
if entity == this.locomotive then
m = this.locomotive_health / this.locomotive_max_health
entity.health = 1000 * m
elseif entity == this.locomotive_cargo then
m = this.cargo_health / this.cargo_max_health
entity.health = 600 * m
end
rendering.set_text(this.health_text, 'HP: ' .. this.locomotive_health .. ' / ' .. this.locomotive_max_health)
end
local function is_protected(entity)
local this = WPT.get()
local map_name = 'mountain_fortress_v3'
if string.sub(entity.surface.name, 0, #map_name) ~= map_name then
return true
end
local protected = {this.locomotive, this.locomotive_cargo}
for i = 1, #protected do
if protected[i] == entity then
return true
end
end
return false
end
local function protect_train(event)
local this = WPT.get()
if event.entity.force.index ~= 1 then
return
end --Player Force
if is_protected(event.entity) then
if event.entity == this.locomotive_cargo or event.entity == this.locomotive then
if event.cause then
if event.cause.force.index == 2 then
if this.locomotive_health <= 0 then
goto continue
end
set_objective_health(event.entity, event.final_damage_amount)
end
end
::continue::
end
if not event.entity.valid then
return
end
event.entity.health = event.entity.health + event.final_damage_amount
end
end
local function hidden_biter(entity)
local surface = entity.surface
local h = math_floor(math_abs(entity.position.y))
local m = 1 / Terrain.level_depth
local count = math_floor(math_random(0, h + Terrain.level_depth) * m) + 1
local position = surface.find_non_colliding_position('small-biter', entity.position, 16, 0.5)
if not position then
position = entity.position
end
BiterRolls.wave_defense_set_unit_raffle(h * 0.20)
for _ = 1, count, 1 do
local unit
if math_random(1, 3) == 1 then
unit = surface.create_entity({name = BiterRolls.wave_defense_roll_spitter_name(), position = position})
else
unit = surface.create_entity({name = BiterRolls.wave_defense_roll_biter_name(), position = position})
end
if math_random(1, 64) == 1 then
BiterHealthBooster.add_boss_unit(unit, m * h * 5 + 1, 0.38)
end
end
end
local function hidden_worm(entity)
BiterRolls.wave_defense_set_worm_raffle(math.sqrt(entity.position.x ^ 2 + entity.position.y ^ 2) * 0.20)
entity.surface.create_entity({name = BiterRolls.wave_defense_roll_worm_name(), position = entity.position})
end
local function hidden_biter_pet(event)
if math_random(1, 2048) ~= 1 then
return
end
BiterRolls.wave_defense_set_unit_raffle(math.sqrt(event.entity.position.x ^ 2 + event.entity.position.y ^ 2) * 0.25)
local unit
if math_random(1, 3) == 1 then
unit =
event.entity.surface.create_entity(
{name = BiterRolls.wave_defense_roll_spitter_name(), position = event.entity.position}
)
else
unit =
event.entity.surface.create_entity(
{name = BiterRolls.wave_defense_roll_biter_name(), position = event.entity.position}
)
end
Pets.biter_pets_tame_unit(game.players[event.player_index], unit, true)
end
local function hidden_treasure(event)
local player = game.players[event.player_index]
local rpg_t = RPG.get_table()
local magic = rpg_t[player.index].magic
if math.random(1, 320) ~= 1 then
return
end
if magic > 50 then
player.print(
rare_treasure_chest_messages[math.random(1, #rare_treasure_chest_messages)],
{r = 0.98, g = 0.66, b = 0.22}
)
Loot.add_rare(event.entity.surface, event.entity.position, 'wooden-chest', magic)
return
end
player.print(treasure_chest_messages[math.random(1, #treasure_chest_messages)], {r = 0.98, g = 0.66, b = 0.22})
Loot.add(event.entity.surface, event.entity.position, 'wooden-chest')
end
local function biters_chew_rocks_faster(event)
if event.entity.force.index ~= 3 then
return
end --Neutral Force
if not event.cause then
return
end
if not event.cause.valid then
return
end
if event.cause.force.index ~= 2 then
return
end --Enemy Force
event.entity.health = event.entity.health - event.final_damage_amount * 2.5
end
local projectiles = {'grenade', 'explosive-rocket', 'grenade', 'explosive-rocket', 'explosive-cannon-projectile'}
local function angry_tree(entity, cause)
if entity.type ~= 'tree' then
return
end
if math.abs(entity.position.y) < Terrain.level_depth then
return
end
if math_random(1, 4) == 1 then
hidden_biter(entity)
end
if math_random(1, 8) == 1 then
hidden_worm(entity)
end
if math_random(1, 16) ~= 1 then
return
end
local position = false
if cause then
if cause.valid then
position = cause.position
end
end
if not position then
position = {entity.position.x + (-20 + math_random(0, 40)), entity.position.y + (-20 + math_random(0, 40))}
end
entity.surface.create_entity(
{
name = projectiles[math_random(1, 5)],
position = entity.position,
force = 'neutral',
source = entity.position,
target = position,
max_range = 16,
speed = 0.01
}
)
end
local function give_coin(player)
player.insert({name = 'coin', count = 1})
end
local function on_player_mined_entity(event)
local this = WPT.get()
local entity = event.entity
local player = game.players[event.player_index]
if not player.valid then
return
end
if not entity.valid then
return
end
local map_name = 'mountain_fortress_v3'
if string.sub(entity.surface.name, 0, #map_name) ~= map_name then
return
end
if disabled_threats[entity.name] then
return
end
if entity.type == 'simple-entity' or entity.type == 'tree' then
this.mined_scrap = this.mined_scrap + 1
Mining.on_player_mined_entity(event)
give_coin(player)
if math.random(1, 32) == 1 then
hidden_biter(event.entity)
entity.destroy()
return
end
if math.random(1, 512) == 1 then
hidden_worm(event.entity)
entity.destroy()
return
end
if math_random(1, 512) == 1 then
tick_tack_trap(entity.surface, entity.position)
return
end
hidden_biter_pet(event)
hidden_treasure(event)
angry_tree(event.entity, game.players[event.player_index].character)
entity.destroy()
end
end
local function on_entity_damaged(event)
if not event.entity then
return
end
if not event.entity.valid then
return
end
if not event.entity.health then
return
end
protect_train(event)
biters_chew_rocks_faster(event)
end
local function on_player_repaired_entity(event)
local this = WPT.get()
if not event.entity then
return
end
if not event.entity.valid then
return
end
if not event.entity.health then
return
end
local entity = event.entity
if entity == this.locomotive_cargo or entity == this.locomotive then
set_objective_health(entity, -1)
end
end
local function on_entity_died(event)
local this = WPT.get()
local entity = event.entity
if not entity.valid then
return
end
local rng = math.random(1, 32) == 1
if event.cause then
if event.cause.valid then
if event.cause.force.index == 2 or event.cause.force.index == 3 then
entity.destroy()
return
end
end
end
local map_name = 'mountain_fortress_v3'
if string.sub(entity.surface.name, 0, #map_name) ~= map_name then
return
end
if disabled_threats[entity.name] then
return
end
local data = {
entity = entity,
surface = entity.surface
}
if entity_type[entity.type] then
if entity.type == 'unit' or entity_type == 'unit-spawner' then
this.biters_killed = this.biters_killed + 1
end
if math.random(1, 32) == 1 then
hidden_biter(event.entity)
return
end
if math_random(1, 512) == 1 then
tick_tack_trap(entity.surface, entity.position)
return
end
end
if entity.type == 'tree' then
angry_tree(event.entity, event.cause)
return
end
if entity.type == 'simple-entity' then
if rng then
hidden_biter(event.entity)
end
Mining.entity_died_randomness(data)
entity.destroy()
return
end
end
function Public.set_scores()
local this = WPT.get()
local wagon = this.locomotive_cargo
if not wagon then
return
end
if not wagon.valid then
return
end
local score = math_floor(wagon.position.y * -1)
for _, player in pairs(game.connected_players) do
if score > Map_score.get_score(player) then
Map_score.set_score(player, score)
end
end
end
function Public.loco_died()
local this = WPT.get()
local surface = game.surfaces[this.active_surface_index]
local wave_defense_table = WD.get_table()
Public.set_scores()
if not this.locomotive.valid then
wave_defense_table.game_lost = true
wave_defense_table.target = nil
game.print(mapkeeper .. ' ' .. defeated_messages[math.random(1, #defeated_messages)], {r = 1, g = 0.5, b = 0.1})
game.print(mapkeeper .. ' Better luck next time.', {r = 1, g = 0.5, b = 0.1})
Public.reset_map()
return
end
this.locomotive_health = 0
this.locomotive.color = {0.49, 0, 255, 1}
rendering.set_text(this.health_text, 'HP: ' .. this.locomotive_health .. ' / ' .. this.locomotive_max_health)
wave_defense_table.game_lost = true
wave_defense_table.target = nil
game.print(mapkeeper .. ' ' .. defeated_messages[math.random(1, #defeated_messages)], {r = 1, g = 0.5, b = 0.1})
game.print(mapkeeper .. ' Better luck next time.', {r = 1, g = 0.5, b = 0.1})
game.print(mapkeeper .. ' Game will soft-reset shortly.', {r = 1, g = 0.5, b = 0.1})
game.forces.enemy.set_friend('player', true)
game.forces.player.set_friend('enemy', true)
local fake_shooter =
surface.create_entity({name = 'character', position = this.locomotive.position, force = 'enemy'})
surface.create_entity(
{
name = 'atomic-rocket',
position = this.locomotive.position,
force = 'enemy',
speed = 1,
max_range = 1200,
target = this.locomotive,
source = fake_shooter
}
)
surface.spill_item_stack(this.locomotive.position, {name = 'coin', count = 512}, false)
surface.spill_item_stack(this.locomotive_cargo.position, {name = 'coin', count = 512}, false)
this.game_reset_tick = game.tick + 1000
for _, player in pairs(game.connected_players) do
player.play_sound {path = 'utility/game_lost', volume_modifier = 0.75}
end
end
Event.add(defines.events.on_entity_damaged, on_entity_damaged)
Event.add(defines.events.on_player_repaired_entity, on_player_repaired_entity)
Event.add(defines.events.on_player_mined_entity, on_player_mined_entity)
Event.add(defines.events.on_entity_died, on_entity_died)
return Public

View File

@ -0,0 +1,20 @@
--Flamethrower Turret Nerf
local function on_research_finished(event)
local research = event.research
local force_name = research.force.name
if research.name == "military" then
if not global.flamethrower_damage then global.flamethrower_damage = {} end
global.flamethrower_damage[force_name] = -0.65
game.forces[force_name].set_turret_attack_modifier("flamethrower-turret", global.flamethrower_damage[force_name])
game.forces[force_name].set_ammo_damage_modifier("flamethrower", global.flamethrower_damage[force_name])
end
if string.sub(research.name, 0, 18) == "refined-flammables" then
global.flamethrower_damage[force_name] = global.flamethrower_damage[force_name] + 0.10
game.forces[force_name].set_turret_attack_modifier("flamethrower-turret", global.flamethrower_damage[force_name])
game.forces[force_name].set_ammo_damage_modifier("flamethrower", global.flamethrower_damage[force_name])
end
end
local event = require 'utils.event'
event.add(defines.events.on_research_finished, on_research_finished)

View File

@ -0,0 +1,449 @@
local Market = require 'functions.basic_markets'
local Loot = require 'maps.mountain_fortress_v3.loot'
local Task = require 'utils.task'
local Token = require 'utils.token'
local Event = require 'utils.event'
local Terrain = require 'maps.mountain_fortress_v3.terrain'.heavy_functions
local insert = table.insert
local tiles_per_call = 16 --how many tiles are inserted with each call of insert_action
local total_calls = math.ceil(1024 / tiles_per_call)
local regen_decoratives = false
local force_chunk = false
local Public = {}
-- Set to false by modules that want to control the on_chunk_generated event themselves.
Public.enable_register_events = true
local function do_tile_inner(tiles, tile, pos)
if type(tile) == 'string' then
insert(tiles, {name = tile, position = pos})
end
end
local function do_tile(y, x, data, shape)
local pos = {x, y}
-- local coords need to be 'centered' to allow for correct rotation and scaling.
local tile = shape(x + 0.5, y + 0.5, data)
if not data.surface.valid then
return
end
if type(tile) == 'table' then
do_tile_inner(data.tiles, tile.tile, pos)
local hidden_tile = tile.hidden_tile
if hidden_tile then
insert(data.hidden_tiles, {tile = hidden_tile, position = pos})
end
local entities = tile.entities
if entities then
for _, entity in ipairs(entities) do
if not entity.position then
entity.position = pos
end
insert(data.entities, entity)
end
end
local decoratives = tile.decoratives
if decoratives then
for _, decorative in ipairs(decoratives) do
insert(data.decoratives, decorative)
end
end
local markets = tile.markets
if markets then
for _, t in ipairs(markets) do
if not t.position then
t.position = pos
end
insert(data.markets, t)
end
end
local treasure = tile.treasure
if treasure then
for _, t in ipairs(treasure) do
if not t.position then
t.position = pos
end
insert(data.treasure, t)
end
end
else
do_tile_inner(data.tiles, tile, pos)
end
end
local function do_row(row, data, shape)
local y = data.top_y + row
local top_x = data.top_x
local tiles = data.tiles
if not data.surface.valid then
return
end
data.y = y
for x = top_x, top_x + 31 do
data.x = x
local pos = {data.x, data.y}
-- local coords need to be 'centered' to allow for correct rotation and scaling.
local tile = shape(x + 0.5, y + 0.5, data)
if type(tile) == 'table' then
do_tile_inner(tiles, tile.tile, pos)
local hidden_tile = tile.hidden_tile
if hidden_tile then
insert(data.hidden_tiles, {tile = hidden_tile, position = pos})
end
local entities = tile.entities
if entities then
for _, entity in ipairs(entities) do
if not entity.position then
entity.position = pos
end
insert(data.entities, entity)
end
end
local decoratives = tile.decoratives
if decoratives then
for _, decorative in ipairs(decoratives) do
if not decorative.position then
decorative.position = pos
end
insert(data.decoratives, decorative)
end
end
local markets = tile.markets
if markets then
for _, t in ipairs(markets) do
if not t.position then
t.position = pos
end
insert(data.markets, t)
end
end
local treasure = tile.treasure
if treasure then
for _, t in ipairs(treasure) do
if not t.position then
t.position = pos
end
insert(data.treasure, t)
end
end
else
do_tile_inner(tiles, tile, pos)
end
end
end
local function do_place_treasure(data)
if not data.surface.valid then
return
end
local surface = data.surface
local treasure = data.treasure
local rnd = math.random
if #treasure == 0 then
return
end
for _, e in ipairs(data.treasure) do
if rnd(1, 6) == 1 then
e.chest = 'iron-chest'
end
Loot.add(surface, e.position, e.chest)
end
end
local function do_place_markets(data)
if not data.surface.valid then
return
end
local markets = data.markets
local surface = data.surface
local rnd = math.random
local abs = math.abs
if #markets == 0 then
return
end
local pos = markets[rnd(1, #markets)]
if
surface.count_entities_filtered {
area = {{pos.x - 96, pos.y - 96}, {pos.x + 96, pos.y + 96}},
name = 'market',
limit = 1
} == 0
then
local market = Market.mountain_market(surface, pos, abs(pos.y) * 0.004)
market.destructible = false
end
end
local function do_place_tiles(data)
if not data.surface.valid then
return
end
data.surface.set_tiles(data.tiles, true)
end
local function do_place_hidden_tiles(data)
if not data.surface.valid then
return
end
local surface = data.surface
for _, t in ipairs(data.hidden_tiles) do
surface.set_hidden_tile(t.position, t.tile)
end
end
local function do_place_decoratives(data)
if not data.surface.valid then
return
end
if regen_decoratives then
data.surface.regenerate_decorative(nil, {{data.top_x / 32, data.top_y / 32}})
end
local dec = data.decoratives
if #dec > 0 then
data.surface.create_decoratives({check_collision = true, decoratives = dec})
end
end
local function do_place_entities(data)
if not data.surface.valid then
return
end
local surface = data.surface
local entity
for _, e in ipairs(data.entities) do
if e.force then
entity = surface.create_entity({name = e.name, position = e.position, force = e.force})
else
entity = surface.create_entity(e)
end
if entity and e.callback then
local callback = Token.get(e.callback)
callback(entity, e.data)
end
end
end
local function run_chart_update(data)
if not data.surface.valid then
return
end
local x = data.top_x / 32
local y = data.top_y / 32
if game.forces.player.is_chunk_charted(data.surface, {x, y}) then
-- Don't use full area, otherwise adjacent chunks get charted
game.forces.player.chart(
data.surface,
{
{data.top_x, data.top_y},
{data.top_x + 1, data.top_y + 1}
}
)
end
end
local function map_gen_action(data)
local state = data.y
if state < 32 then
if not data.surface.valid then
return
end
local shape = Terrain
if shape == nil then
return false
end
local count = total_calls
local y = state + data.top_y
local x = data.x
local max_x = data.top_x + 32
data.y = y
repeat
count = count - 1
do_tile(y, x, data, shape)
x = x + 1
if x == max_x then
y = y + 1
if y == data.top_y + 32 then
break
end
x = data.top_x
data.y = y
end
data.x = x
until count == 0
data.y = y - data.top_y
return true
elseif state == 32 then
do_place_tiles(data)
data.y = 33
return true
elseif state == 33 then
do_place_hidden_tiles(data)
data.y = 34
return true
elseif state == 34 then
do_place_entities(data)
data.y = 35
return true
elseif state == 35 then
do_place_markets(data)
data.y = 36
return true
elseif state == 36 then
do_place_treasure(data)
data.y = 37
return true
elseif state == 37 then
do_place_decoratives(data)
data.y = 38
return true
elseif state == 38 then
run_chart_update(data)
return false
end
end
local map_gen_action_token = Token.register(map_gen_action)
--- Adds generation of a Chunk of the map to the queue
-- @param event <table> the event table from on_chunk_generated
function Public.schedule_chunk(event)
local surface = event.surface
local shape = Terrain
if not surface.valid then
return
end
if not shape then
return
end
local area = event.area
local data = {
y = 0,
x = area.left_top.x,
area = area,
top_x = area.left_top.x,
top_y = area.left_top.y,
surface = surface,
tiles = {},
hidden_tiles = {},
entities = {},
decoratives = {},
markets = {},
treasure = {}
}
if not data.surface.valid then
return
end
Task.queue_task(map_gen_action_token, data, total_calls)
end
--- Generates a Chunk of map when called
-- @param event <table> the event table from on_chunk_generated
function Public.do_chunk(event)
local surface = event.surface
local shape = Terrain
if not surface.valid then
return
end
if not shape then
return
end
local area = event.area
local data = {
area = area,
top_x = area.left_top.x,
top_y = area.left_top.y,
surface = surface,
tiles = {},
hidden_tiles = {},
entities = {},
decoratives = {},
markets = {},
treasure = {}
}
if not data.surface.valid then
return
end
for row = 0, 31 do
do_row(row, data, shape)
end
do_place_tiles(data)
do_place_hidden_tiles(data)
do_place_entities(data)
do_place_decoratives(data)
do_place_markets(data)
do_place_treasure(data)
end
local do_chunk = Public.do_chunk
local schedule_chunk = Public.schedule_chunk
local function on_chunk(event)
if force_chunk then
do_chunk(event)
elseif event.tick == 0 then
--do_chunk(event)
else
schedule_chunk(event)
end
end
Event.add(defines.events.on_chunk_generated, on_chunk)
return Public

View File

@ -0,0 +1,83 @@
local RPG = require 'maps.mountain_fortress_v3.rpg'
local WPT = require 'maps.mountain_fortress_v3.table'
local floor = math.floor
local format_number = require 'util'.format_number
local function create_gui(player)
local label
local line
local frame = player.gui.top.add({type = 'frame', name = 'mountain_fortress_v3'})
frame.style.minimal_height = 38
frame.style.maximal_height = 38
label = frame.add({type = 'label', caption = ' ', name = 'label'})
label.style.font_color = {r = 0.88, g = 0.88, b = 0.88}
label.style.font = 'default-bold'
label.style.font_color = {r = 0.33, g = 0.66, b = 0.9}
label = frame.add({type = 'label', caption = ' ', name = 'global_pool'})
label.style.font_color = {r = 0.88, g = 0.88, b = 0.88}
label.style.font = 'default-bold'
label.style.right_padding = 4
label.style.font_color = {r = 0.33, g = 0.66, b = 0.9}
line = frame.add({type = 'line', direction = 'vertical'})
line.style.left_padding = 4
line.style.right_padding = 4
label = frame.add({type = 'label', caption = ' ', name = 'scrap_mined'})
label.style.font_color = {r = 0.88, g = 0.88, b = 0.88}
label.style.font = 'default-bold'
label.style.right_padding = 4
label.style.font_color = {r = 0.33, g = 0.66, b = 0.9}
line = frame.add({type = 'line', direction = 'vertical'})
line.style.left_padding = 4
line.style.right_padding = 4
label = frame.add({type = 'label', caption = ' ', name = 'biters_killed'})
label.style.font_color = {r = 0.88, g = 0.88, b = 0.88}
label.style.font = 'default-bold'
label.style.right_padding = 4
label.style.font_color = {r = 0.33, g = 0.66, b = 0.9}
line = frame.add({type = 'line', direction = 'vertical'})
line.style.left_padding = 4
line.style.right_padding = 4
label = frame.add({type = 'label', caption = ' ', name = 'train_upgrades'})
label.style.font_color = {r = 0.88, g = 0.88, b = 0.88}
label.style.font = 'default-bold'
label.style.right_padding = 4
label.style.font_color = {r = 0.33, g = 0.66, b = 0.9}
end
local function update_gui(player)
local rpg = RPG.get_table()
local st = WPT.get()
if not player.gui.top.mountain_fortress_v3 then
create_gui(player)
end
local gui = player.gui.top.mountain_fortress_v3
if rpg.global_pool == 0 then
gui.global_pool.caption = 'XP: 0'
gui.global_pool.tooltip = 'Dig, handcraft or run to increase the pool!'
elseif rpg.global_pool > 0 then
gui.global_pool.caption = 'XP: ' .. format_number(floor(rpg.global_pool), true)
gui.global_pool.tooltip = 'Get this number over 5k to get some of this mad XP! \\o/'
end
gui.scrap_mined.caption = ' [img=entity.tree-01][img=entity.rock-huge]: ' .. format_number(st.mined_scrap, true)
gui.scrap_mined.tooltip = 'Amount of trees/rocks harvested.'
gui.biters_killed.caption = ' [img=entity.small-biter]: ' .. format_number(st.biters_killed, true)
gui.biters_killed.tooltip = 'Amount of biters killed.'
gui.train_upgrades.caption = ' [img=entity.locomotive]: ' .. format_number(st.train_upgrades, true)
gui.train_upgrades.tooltip = 'Amount of train upgrades.'
end
return update_gui

View File

@ -0,0 +1,17 @@
local Public = {}
Public.wagon_types = {
["cargo-wagon"] = true,
["artillery-wagon"] = true,
["fluid-wagon"] = true,
["locomotive"] = true,
}
Public.wagon_areas = {
["cargo-wagon"] = {left_top = {x = -20, y = 0}, right_bottom = {x = 20, y = 60}},
["artillery-wagon"] = {left_top = {x = -20, y = 0}, right_bottom = {x = 20, y = 60}},
["fluid-wagon"] = {left_top = {x = -20, y = 0}, right_bottom = {x = 20, y = 60}},
["locomotive"] = {left_top = {x = -20, y = 0}, right_bottom = {x = 20, y = 60}},
}
return Public

View File

@ -0,0 +1,758 @@
local Public = {}
local Constants = require 'maps.mountain_fortress_v3.icw.constants'
local table_insert = table.insert
local table_remove = table.remove
local math_round = math.round
local math_random = math.random
function Public.request_reconstruction(icw)
icw.rebuild_tick = game.tick + 30
end
local function delete_empty_surfaces(icw)
for k, surface in pairs(icw.surfaces) do
if not icw.trains[tonumber(surface.name)] then
game.delete_surface(surface)
table_remove(icw.surfaces, k)
end
end
end
local function kick_players_out_of_vehicles(wagon)
for _, player in pairs(game.connected_players) do
local character = player.character
if character and character.valid and character.driving then
if wagon.surface == player.surface then
character.driving = false
end
end
end
end
local function connect_power_pole(entity, wagon_area_left_top_y)
local surface = entity.surface
local max_wire_distance = entity.prototype.max_wire_distance
local area = {
{entity.position.x - max_wire_distance, entity.position.y - max_wire_distance},
{entity.position.x + max_wire_distance, entity.position.y - 1}
}
for _, pole in pairs(surface.find_entities_filtered({area = area, name = entity.name})) do
if pole.position.y < wagon_area_left_top_y then
entity.connect_neighbour(pole)
return
end
end
end
local function equal_fluid(source_tank, target_tank)
if not source_tank.valid then
return
end
if not target_tank.valid then
return
end
local source_fluid = source_tank.fluidbox[1]
if not source_fluid then
return
end
local target_fluid = target_tank.fluidbox[1]
local source_fluid_amount = source_fluid.amount
local amount
if target_fluid then
amount = source_fluid_amount - ((target_fluid.amount + source_fluid_amount) * 0.5)
else
amount = source_fluid.amount * 0.5
end
if amount <= 0 then
return
end
local inserted_amount =
target_tank.insert_fluid({name = source_fluid.name, amount = amount, temperature = source_fluid.temperature})
if inserted_amount > 0 then
source_tank.remove_fluid({name = source_fluid.name, amount = inserted_amount})
end
end
local function divide_fluid(wagon, storage_tank)
local fluid_wagon = wagon.entity
equal_fluid(fluid_wagon, storage_tank)
equal_fluid(storage_tank, fluid_wagon)
end
local function input_filtered(wagon_inventory, chest, chest_inventory, free_slots)
local request_stacks = {}
local prototypes = game.item_prototypes
for slot_index = 1, 4, 1 do
local stack = chest.get_request_slot(slot_index)
if stack then
request_stacks[stack.name] = 10 * prototypes[stack.name].stack_size
end
end
for i = 1, wagon_inventory.get_bar() - 1, 1 do
if free_slots <= 0 then
return
end
local stack = wagon_inventory[i]
if stack.valid_for_read then
local request_stack = request_stacks[stack.name]
if request_stack and request_stack > chest_inventory.get_item_count(stack.name) then
chest_inventory.insert(stack)
stack.clear()
free_slots = free_slots - 1
end
end
end
end
local function input_cargo(wagon, chest)
if not chest.request_from_buffers then
return
end
local wagon_entity = wagon.entity
if not wagon_entity.valid then
wagon.transfer_entities = nil
return
end
local wagon_inventory = wagon_entity.get_inventory(defines.inventory.cargo_wagon)
if wagon_inventory.is_empty() then
return
end
local chest_inventory = chest.get_inventory(defines.inventory.chest)
local free_slots = 0
for i = 1, chest_inventory.get_bar() - 1, 1 do
if not chest_inventory[i].valid_for_read then
free_slots = free_slots + 1
end
end
if chest.get_request_slot(1) then
input_filtered(wagon_inventory, chest, chest_inventory, free_slots)
return
end
for i = 1, wagon_inventory.get_bar() - 1, 1 do
if free_slots <= 0 then
return
end
if wagon_inventory[i].valid_for_read then
chest_inventory.insert(wagon_inventory[i])
wagon_inventory[i].clear()
free_slots = free_slots - 1
end
end
end
local function output_cargo(wagon, passive_chest)
local passive_chest_inventory = passive_chest.get_inventory(defines.inventory.cargo_wagon)
if passive_chest_inventory.is_empty() then
return
end
local wagon_inventory = wagon.entity.get_inventory(defines.inventory.cargo_wagon)
local free_slots = 0
for i = 1, wagon_inventory.get_bar() - 1, 1 do
if not wagon_inventory[i].valid_for_read and not wagon_inventory.get_filter(i) then
free_slots = free_slots + 1
end
end
for i = 1, passive_chest_inventory.get_bar() - 1, 1 do
if free_slots <= 0 then
return
end
if passive_chest_inventory[i].valid_for_read then
wagon_inventory.insert(passive_chest_inventory[i])
passive_chest_inventory[i].clear()
free_slots = free_slots - 1
end
end
end
local transfer_functions = {
['storage-tank'] = divide_fluid,
['logistic-chest-requester'] = input_cargo,
['logistic-chest-passive-provider'] = output_cargo
}
local function get_wagon_for_entity(icw, entity)
local train = icw.trains[tonumber(entity.surface.name)]
if not train then
return
end
local position = entity.position
for k, unit_number in pairs(train.wagons) do
local wagon = icw.wagons[unit_number]
if wagon then
local left_top = wagon.area.left_top
local right_bottom = wagon.area.right_bottom
if
position.x >= left_top.x and position.y >= left_top.y and position.x <= right_bottom.x and
position.y <= right_bottom.y
then
return wagon
end
end
end
return false
end
local function kill_wagon_doors(icw, wagon)
for k, e in pairs(wagon.doors) do
icw.doors[e.unit_number] = nil
e.destroy()
wagon.doors[k] = nil
end
end
local function construct_wagon_doors(icw, wagon)
local area = wagon.area
local surface = wagon.surface
for _, x in pairs({area.left_top.x - 0.55, area.right_bottom.x + 0.55}) do
local e =
surface.create_entity(
{
name = 'car',
position = {x, area.left_top.y + ((area.right_bottom.y - area.left_top.y) * 0.5)},
force = 'neutral',
create_build_effect_smoke = false
}
)
e.get_inventory(defines.inventory.fuel).insert({name = 'wood', count = 1})
e.destructible = false
e.minable = false
e.operable = false
icw.doors[e.unit_number] = wagon.entity.unit_number
table_insert(wagon.doors, e)
end
end
local function get_player_data(icw, player)
local player_data = icw.players[player.index]
if icw.players[player.index] then
return player_data
end
icw.players[player.index] = {
surface = 1,
fallback_surface = 1,
zoom = 0.30,
map_size = 360
}
return icw.players[player.index]
end
function Public.kill_minimap(player)
local element = player.gui.left.icw_map
if element then
element.destroy()
end
end
function Public.kill_wagon(icw, entity)
if not Constants.wagon_types[entity.type] then
return
end
local wagon = icw.wagons[entity.unit_number]
local surface = wagon.surface
kick_players_out_of_vehicles(wagon)
kill_wagon_doors(icw, wagon)
for _, e in pairs(surface.find_entities_filtered({area = wagon.area})) do
if e.name == 'character' and e.player then
local p = wagon.entity.surface.find_non_colliding_position('character', wagon.entity.position, 128, 0.5)
if p then
e.player.teleport(p, wagon.entity.surface)
else
e.player.teleport(wagon.entity.position, wagon.entity.surface)
end
Public.kill_minimap(e.player)
else
e.die()
end
end
for _, tile in pairs(surface.find_tiles_filtered({area = wagon.area})) do
surface.set_tiles({{name = 'out-of-map', position = tile.position}}, true)
end
wagon.entity.force.chart(surface, wagon.area)
icw.wagons[entity.unit_number] = nil
Public.request_reconstruction(icw)
end
function Public.create_room_surface(icw, unit_number)
if game.surfaces[tostring(unit_number)] then
return game.surfaces[tostring(unit_number)]
end
local map_gen_settings = {
['width'] = 2,
['height'] = 2,
['water'] = 0,
['starting_area'] = 1,
['cliff_settings'] = {cliff_elevation_interval = 0, cliff_elevation_0 = 0},
['default_enable_all_autoplace_controls'] = true,
['autoplace_settings'] = {
['entity'] = {treat_missing_as_default = false},
['tile'] = {treat_missing_as_default = true},
['decorative'] = {treat_missing_as_default = false}
}
}
local surface = game.create_surface(unit_number, map_gen_settings)
surface.freeze_daytime = true
surface.daytime = 0.1
surface.request_to_generate_chunks({16, 16}, 2)
surface.force_generate_chunk_requests()
for _, tile in pairs(surface.find_tiles_filtered({area = {{-2, -2}, {2, 2}}})) do
surface.set_tiles({{name = 'out-of-map', position = tile.position}}, true)
end
table_insert(icw.surfaces, surface)
return surface
end
function Public.create_wagon_room(icw, wagon)
local surface = wagon.surface
local area = wagon.area
local main_tile_name = 'tutorial-grid'
if wagon.entity.type == 'locomotive' then
main_tile_name = 'black-refined-concrete'
end
local tiles = {}
for x = -3, 2, 1 do
table_insert(tiles, {name = 'hazard-concrete-right', position = {x, area.left_top.y}})
table_insert(tiles, {name = 'hazard-concrete-right', position = {x, area.right_bottom.y - 1}})
end
for x = area.left_top.x, area.right_bottom.x - 1, 1 do
for y = area.left_top.y + 2, area.right_bottom.y - 3, 1 do
table_insert(tiles, {name = main_tile_name, position = {x, y}})
end
end
for x = -3, 2, 1 do
for y = 1, 3, 1 do
table_insert(tiles, {name = main_tile_name, position = {x, y}})
end
for y = area.right_bottom.y - 4, area.right_bottom.y - 2, 1 do
table_insert(tiles, {name = main_tile_name, position = {x, y}})
end
end
local fishes = {}
if wagon.entity.type == 'locomotive' then
for x = -3, 2, 1 do
for y = 10, 12, 1 do
table_insert(tiles, {name = 'water', position = {x, y}})
table_insert(fishes, {name = 'fish', position = {x, y}})
end
end
end
surface.set_tiles(tiles, true)
for _, fish in pairs(fishes) do
surface.create_entity(fish)
end
construct_wagon_doors(icw, wagon)
if wagon.entity.type == 'fluid-wagon' then
local height = area.right_bottom.y - area.left_top.y
local positions = {
{area.right_bottom.x, area.left_top.y + height * 0.25},
{area.right_bottom.x, area.left_top.y + height * 0.75},
{area.left_top.x - 1, area.left_top.y + height * 0.25},
{area.left_top.x - 1, area.left_top.y + height * 0.75}
}
table.shuffle_table(positions)
local e =
surface.create_entity(
{
name = 'storage-tank',
position = positions[1],
force = 'neutral',
create_build_effect_smoke = false
}
)
e.destructible = false
e.minable = false
wagon.transfer_entities = {e}
return
end
if wagon.entity.type == 'cargo-wagon' then
local vectors = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}
local v = vectors[math_random(1, 4)]
local position = {
math_random(area.left_top.x + 4, area.right_bottom.x - 4),
math_random(area.left_top.y + 6, area.right_bottom.y - 6)
}
local e =
surface.create_entity(
{
name = 'logistic-chest-requester',
position = position,
force = 'neutral',
create_build_effect_smoke = false
}
)
e.destructible = false
e.minable = false
local e2 =
surface.create_entity(
{
name = 'logistic-chest-passive-provider',
position = {position[1] + v[1], position[2] + v[2]},
force = 'neutral',
create_build_effect_smoke = false
}
)
e2.destructible = false
e2.minable = false
wagon.transfer_entities = {e, e2}
return
end
end
function Public.create_wagon(icw, created_entity, delay_surface)
if not created_entity.unit_number then
return
end
if icw.trains[tonumber(created_entity.surface.name)] or icw.wagons[tonumber(created_entity.surface.name)] then
return
end
if not Constants.wagon_types[created_entity.type] then
return
end
local wagon_area = Constants.wagon_areas[created_entity.type]
icw.wagons[created_entity.unit_number] = {
entity = created_entity,
area = {
left_top = {x = wagon_area.left_top.x, y = wagon_area.left_top.y},
right_bottom = {x = wagon_area.right_bottom.x, y = wagon_area.right_bottom.y}
},
doors = {},
entity_count = 0
}
local wagon = icw.wagons[created_entity.unit_number]
if not delay_surface then
wagon.surface = Public.create_room_surface(icw, created_entity.unit_number)
Public.create_wagon_room(icw, icw.wagons[created_entity.unit_number])
end
Public.request_reconstruction(icw)
return wagon
end
function Public.add_wagon_entity_count(icw, added_entity)
local wagon = get_wagon_for_entity(icw, added_entity)
if not wagon then
return
end
wagon.entity_count = wagon.entity_count + 1
wagon.entity.minable = false
end
function Public.subtract_wagon_entity_count(icw, removed_entity)
local wagon = get_wagon_for_entity(icw, removed_entity)
if not wagon then
return
end
wagon.entity_count = wagon.entity_count - 1
if wagon.entity_count > 0 then
return
end
wagon.entity.minable = true
end
function Public.use_cargo_wagon_door(icw, player, door)
local player_data = get_player_data(icw, player)
if player_data.state then
player_data.state = player_data.state - 1
if player_data.state == 0 then
player_data.state = nil
end
return
end
if not door then
return
end
if not door.valid then
return
end
local doors = icw.doors
local wagons = icw.wagons
local wagon = false
if doors[door.unit_number] then
wagon = wagons[doors[door.unit_number]]
end
if wagons[door.unit_number] then
wagon = wagons[door.unit_number]
end
if not wagon then
return
end
player_data.fallback_surface = wagon.entity.surface.index
player_data.fallback_position = {wagon.entity.position.x, wagon.entity.position.y}
if wagon.entity.surface.name ~= player.surface.name then
local surface = wagon.entity.surface
local x_vector = (door.position.x / math.abs(door.position.x)) * 2
local position = {wagon.entity.position.x + x_vector, wagon.entity.position.y}
local position = surface.find_non_colliding_position('character', position, 128, 0.5)
if not position then
return
end
if wagon.entity.type == 'locomotive' then
player.teleport(position, surface)
player_data.state = 2
player.driving = true
Public.kill_minimap(player)
else
player.teleport(position, surface)
Public.kill_minimap(player)
end
player_data.surface = surface.index
else
local surface = wagon.surface
local area = wagon.area
local x_vector = door.position.x - player.position.x
local position
if x_vector > 0 then
position = {area.left_top.x + 0.5, area.left_top.y + ((area.right_bottom.y - area.left_top.y) * 0.5)}
else
position = {area.right_bottom.x - 0.5, area.left_top.y + ((area.right_bottom.y - area.left_top.y) * 0.5)}
end
local p = surface.find_non_colliding_position('character', position, 128, 0.5)
if p then
player.teleport(p, surface)
else
player.teleport(position, surface)
end
player_data.surface = surface.index
end
end
local function move_room_to_train(icw, train, wagon)
if not wagon then
return
end
table_insert(train.wagons, wagon.entity.unit_number)
local destination_area = {
left_top = {x = wagon.area.left_top.x, y = train.top_y},
right_bottom = {
x = wagon.area.right_bottom.x,
y = train.top_y + (wagon.area.right_bottom.y - wagon.area.left_top.y)
}
}
train.top_y = destination_area.right_bottom.y
if
destination_area.left_top.x == wagon.area.left_top.x and destination_area.left_top.y == wagon.area.left_top.y and
wagon.surface.name == train.surface.name
then
return
end
kick_players_out_of_vehicles(wagon)
local player_positions = {}
for _, e in pairs(wagon.surface.find_entities_filtered({name = 'character', area = wagon.area})) do
local player = e.player
if player then
player_positions[player.index] = {
player.position.x,
player.position.y + (destination_area.left_top.y - wagon.area.left_top.y)
}
player.teleport({0, 0}, game.surfaces.nauvis)
end
end
kill_wagon_doors(icw, wagon)
wagon.surface.clone_area(
{
source_area = wagon.area,
destination_area = destination_area,
destination_surface = train.surface,
clone_tiles = true,
clone_entities = true,
clone_decoratives = true,
clear_destination_entities = true,
clear_destination_decoratives = true,
expand_map = true
}
)
for player_index, position in pairs(player_positions) do
local player = game.players[player_index]
player.teleport(position, train.surface)
end
for _, tile in pairs(wagon.surface.find_tiles_filtered({area = wagon.area})) do
wagon.surface.set_tiles({{name = 'out-of-map', position = tile.position}}, true)
end
wagon.entity.force.chart(wagon.surface, wagon.area)
wagon.surface = train.surface
wagon.area = destination_area
wagon.transfer_entities = {}
construct_wagon_doors(icw, wagon)
local left_top_y = wagon.area.left_top.y
for _, e in pairs(wagon.surface.find_entities_filtered({type = 'electric-pole', area = wagon.area})) do
connect_power_pole(e, left_top_y)
end
for _, e in pairs(wagon.surface.find_entities_filtered({area = wagon.area, force = 'neutral'})) do
if transfer_functions[e.name] then
table_insert(wagon.transfer_entities, e)
end
end
end
function Public.construct_train(icw, carriages)
local unit_number = carriages[1].unit_number
if icw.trains[unit_number] then
return
end
local train = {surface = Public.create_room_surface(icw, unit_number), wagons = {}, top_y = 0}
icw.trains[unit_number] = train
for k, carriage in pairs(carriages) do
move_room_to_train(icw, train, icw.wagons[carriage.unit_number])
end
end
function Public.reconstruct_all_trains(icw)
icw.trains = {}
for unit_number, wagon in pairs(icw.wagons) do
if not wagon.entity or not wagon.entity.valid then
icw.wagons[unit_number] = nil
Public.request_reconstruction(icw)
return
end
if not wagon.surface then
wagon.surface = Public.create_room_surface(icw, unit_number)
Public.create_wagon_room(icw, wagon)
end
local carriages = wagon.entity.train.carriages
Public.construct_train(icw, carriages)
end
delete_empty_surfaces(icw)
end
function Public.item_transfer(icw)
for _, wagon in pairs(icw.wagons) do
if not wagon and not wagon.valid then
return
end
if wagon.transfer_entities then
if not wagon and not wagon.valid then
return
end
for k, e in pairs(wagon.transfer_entities) do
transfer_functions[e.name](wagon, e)
end
end
end
end
function Public.draw_minimap(icw, player, surface, position)
local element = player.gui.left.icw_map
if not element then
local player_data = get_player_data(icw, player)
element =
player.gui.left.add(
{
type = 'camera',
name = 'icw_map',
position = position,
surface_index = surface.index,
zoom = player_data.zoom,
tooltip = 'LMB: Increase zoom level.\nRMB: Decrease zoom level.\nMMB: Toggle camera size.'
}
)
element.style.margin = 1
element.style.minimal_height = player_data.map_size
element.style.minimal_width = player_data.map_size
return
end
element.position = position
end
function Public.update_minimap(icw)
for k, player in pairs(game.connected_players) do
if player.character and player.character.valid then
local wagon = get_wagon_for_entity(icw, player.character)
if wagon then
Public.draw_minimap(icw, player, wagon.entity.surface, wagon.entity.position)
end
end
end
end
function Public.toggle_minimap(icw, event)
local element = event.element
if not element then
return
end
if not element.valid then
return
end
if element.name ~= 'icw_map' then
return
end
local player = game.players[event.player_index]
local player_data = get_player_data(icw, player)
if event.button == defines.mouse_button_type.right then
player_data.zoom = player_data.zoom - 0.07
if player_data.zoom < 0.07 then
player_data.zoom = 0.07
end
element.zoom = player_data.zoom
return
end
if event.button == defines.mouse_button_type.left then
player_data.zoom = player_data.zoom + 0.07
if player_data.zoom > 2 then
player_data.zoom = 2
end
element.zoom = player_data.zoom
return
end
if event.button == defines.mouse_button_type.middle then
player_data.map_size = player_data.map_size + 50
if player_data.map_size > 650 then
player_data.map_size = 250
end
element.style.minimal_height = player_data.map_size
element.style.minimal_width = player_data.map_size
element.style.maximal_height = player_data.map_size
element.style.maximal_width = player_data.map_size
return
end
end
return Public

View File

@ -0,0 +1,192 @@
local Global = require 'utils.global'
local Event = require 'utils.event'
local Functions = require 'maps.mountain_fortress_v3.icw.functions'
local Constants = require 'maps.mountain_fortress_v3.icw.constants'
local Public = {}
local math_round = math.round
local icw = {}
Global.register(
icw,
function(tbl)
icw = tbl
end
)
function Public.reset()
if icw.surfaces then
for k, surface in pairs(icw.surfaces) do
if surface and surface.valid then
game.delete_surface(surface)
end
end
end
for k, v in pairs(icw) do
icw[k] = nil
end
icw.doors = {}
icw.wagons = {}
icw.trains = {}
icw.players = {}
icw.surfaces = {}
end
local function on_entity_died(event)
local entity = event.entity
if not entity and not entity.valid then
return
end
if not Constants.wagon_types[entity.type] then
return
end
Functions.subtract_wagon_entity_count(icw, entity)
Functions.kill_wagon(icw, entity)
end
local function on_player_mined_entity(event)
local entity = event.entity
if not entity and not entity.valid then
return
end
Functions.subtract_wagon_entity_count(icw, entity)
Functions.kill_wagon(icw, entity)
end
local function on_robot_mined_entity(event)
local entity = event.entity
if not entity and not entity.valid then
return
end
Functions.subtract_wagon_entity_count(icw, entity)
Functions.kill_wagon(icw, entity)
end
local function on_built_entity(event)
local created_entity = event.created_entity
Functions.create_wagon(icw, created_entity)
Functions.add_wagon_entity_count(icw, created_entity)
end
local function on_robot_built_entity(event)
local created_entity = event.created_entity
Functions.create_wagon(icw, created_entity)
Functions.add_wagon_entity_count(icw, created_entity)
end
local function on_player_driving_changed_state(event)
local player = game.players[event.player_index]
Functions.use_cargo_wagon_door(icw, player, event.entity)
end
--[[
local function on_player_created(event)
local player = game.players[event.player_index]
player.insert({name = "cargo-wagon", count = 5})
player.insert({name = "artillery-wagon", count = 5})
player.insert({name = "fluid-wagon", count = 5})
player.insert({name = "locomotive", count = 5})
player.insert({name = "rail", count = 100})
end
]]
local function on_gui_closed(event)
local entity = event.entity
if not entity then
return
end
if not entity.valid then
return
end
if not entity.unit_number then
return
end
if not icw.wagons[entity.unit_number] then
return
end
Functions.kill_minimap(game.players[event.player_index])
end
local function on_gui_opened(event)
local entity = event.entity
if not entity then
return
end
if not entity.valid then
return
end
if not entity.unit_number then
return
end
local wagon = icw.wagons[entity.unit_number]
if not wagon then
return
end
Functions.draw_minimap(
icw,
game.players[event.player_index],
wagon.surface,
{
wagon.area.left_top.x + (wagon.area.right_bottom.x - wagon.area.left_top.x) * 0.5,
wagon.area.left_top.y + (wagon.area.right_bottom.y - wagon.area.left_top.y) * 0.5
}
)
end
local function on_player_died(event)
Functions.kill_minimap(game.players[event.player_index])
end
local function on_train_created(event)
Functions.request_reconstruction(icw)
end
local function on_gui_click(event)
Functions.toggle_minimap(icw, event)
end
local function on_tick()
local tick = game.tick
if tick % 60 == 0 then
Functions.item_transfer(icw)
end
if tick % 240 == 0 then
Functions.update_minimap(icw)
end
if not icw.rebuild_tick then
return
end
if icw.rebuild_tick ~= tick then
return
end
Functions.reconstruct_all_trains(icw)
icw.rebuild_tick = nil
end
local function on_init()
Public.reset()
end
function Public.get_table()
return icw
end
function Public.register_wagon(wagon_entity)
return Functions.create_wagon(icw, wagon_entity)
end
Event.on_init(on_init)
Event.add(defines.events.on_tick, on_tick)
Event.add(defines.events.on_player_driving_changed_state, on_player_driving_changed_state)
Event.add(defines.events.on_entity_died, on_entity_died)
Event.add(defines.events.on_built_entity, on_built_entity)
Event.add(defines.events.on_train_created, on_train_created)
Event.add(defines.events.on_robot_built_entity, on_robot_built_entity)
Event.add(defines.events.on_player_died, on_player_died)
--Event.add(defines.events.on_player_created, on_player_created)
Event.add(defines.events.on_gui_click, on_gui_click)
Event.add(defines.events.on_gui_closed, on_gui_closed)
Event.add(defines.events.on_gui_opened, on_gui_opened)
Event.add(defines.events.on_player_mined_entity, on_player_mined_entity)
Event.add(defines.events.on_robot_mined_entity, on_robot_mined_entity)
return Public

View File

@ -0,0 +1,319 @@
local Event = require 'utils.event'
--local Power = require 'maps.mountain_fortress_v3.power'
local ICW = require 'maps.mountain_fortress_v3.icw.main'
local WPT = require 'maps.mountain_fortress_v3.table'
local RPG = require 'maps.mountain_fortress_v3.rpg'
require 'maps.mountain_fortress_v3.locomotive_market'
local Public = {}
local rnd = math.random
local function validate_player(player)
if not player then
return false
end
if not player.valid then
return false
end
if not player.character then
return false
end
if not player.connected then
return false
end
if not game.players[player.index] then
return false
end
return true
end
local function property_boost(data)
local xp_floating_text_color = {r = 0, g = 127, b = 33}
local visuals_delay = 1800
local this = data.this
local locomotive_surface = data.locomotive_surface
local aura = this.locomotive_xp_aura
local rpg = data.rpg
local loco = this.locomotive.position
local area = {
left_top = {x = loco.x - aura, y = loco.y - aura},
right_bottom = {x = loco.x + aura, y = loco.y + aura}
}
for _, player in pairs(game.connected_players) do
if not validate_player(player) then
return
end
if Public.contains_positions(player.position, area) or player.surface.index == locomotive_surface.index then
local pos = player.position
RPG.gain_xp(player, 0.3 * (rpg[player.index].bonus + this.xp_points))
player.create_local_flying_text {
text = '+' .. '',
position = {x = pos.x, y = pos.y - 2},
color = xp_floating_text_color,
time_to_live = 60,
speed = 3
}
rpg[player.index].xp_since_last_floaty_text = 0
rpg[player.index].last_floaty_text = game.tick + visuals_delay
end
end
end
local function fish_tag()
local this = WPT.get()
if not this.locomotive_cargo then
return
end
if not this.locomotive_cargo.valid then
return
end
if not this.locomotive_cargo.surface then
return
end
if not this.locomotive_cargo.surface.valid then
return
end
if this.locomotive_tag then
if this.locomotive_tag.valid then
if
this.locomotive_tag.position.x == this.locomotive_cargo.position.x and
this.locomotive_tag.position.y == this.locomotive_cargo.position.y
then
return
end
this.locomotive_tag.destroy()
end
end
this.locomotive_tag =
this.locomotive_cargo.force.add_chart_tag(
this.locomotive_cargo.surface,
{
icon = {type = 'item', name = 'raw-fish'},
position = this.locomotive_cargo.position,
text = ' '
}
)
end
local function set_player_spawn_and_refill_fish()
local this = WPT.get()
if not this.locomotive_cargo then
return
end
if not this.locomotive_cargo.valid then
return
end
this.locomotive_cargo.get_inventory(defines.inventory.cargo_wagon).insert(
{name = 'raw-fish', count = math.random(2, 5)}
)
local position =
this.locomotive_cargo.surface.find_non_colliding_position(
'stone-furnace',
this.locomotive_cargo.position,
16,
2
)
if not position then
return
end
game.forces.player.set_spawn_position({x = position.x, y = position.y}, this.locomotive_cargo.surface)
end
local function set_locomotive_health()
local this = WPT.get()
if not this.locomotive then
return
end
if not this.locomotive.valid then
return
end
local locomotive_health = WPT.get('locomotive_health')
local locomotive_max_health = WPT.get('locomotive_max_health')
local m = locomotive_health / locomotive_max_health
this.locomotive.health = 1000 * m
rendering.set_text(this.health_text, 'HP: ' .. locomotive_health .. ' / ' .. locomotive_max_health)
end
local function tick()
if game.tick % 120 == 0 then
Public.boost_players_around_train()
end
if game.tick % 30 == 0 then
if game.tick % 1800 == 0 then
set_player_spawn_and_refill_fish()
end
set_locomotive_health()
fish_tag()
end
end
function Public.boost_players_around_train()
local rpg = RPG.get_table()
local this = WPT.get()
if not this.active_surface_index then
return
end
local surface = game.surfaces[this.active_surface_index]
local icw_table = ICW.get_table()
local unit_surface = this.locomotive.unit_number
local locomotive_surface = game.surfaces[icw_table.wagons[unit_surface].surface.index]
if not this.locomotive then
return
end
if not this.locomotive.valid then
return
end
local data = {
this = this,
surface = surface,
locomotive_surface = locomotive_surface,
rpg = rpg
}
property_boost(data)
end
function Public.render_train_hp()
local this = WPT.get()
local surface = game.surfaces[this.active_surface_index]
local names = {
'Hanakocz',
'Redlabel',
'Hanakocz',
'Gerkiz',
'Hanakocz',
'Mewmew',
'Gerkiz',
'Hanakocz',
'Redlabel',
'Gerkiz',
'Hanakocz',
'Redlabel',
'Gerkiz',
'Hanakocz'
}
local size_of_names = #names
local n = names[rnd(1, size_of_names)]
this.health_text =
rendering.draw_text {
text = 'HP: ' .. this.locomotive_health .. ' / ' .. this.locomotive_max_health,
surface = surface,
target = this.locomotive,
target_offset = {0, -2.5},
color = this.locomotive.color,
scale = 1.40,
font = 'default-game',
alignment = 'center',
scale_with_zoom = false
}
this.caption =
rendering.draw_text {
text = n .. 's Comfy Train',
surface = surface,
target = this.locomotive,
target_offset = {0, -4.25},
color = this.locomotive.color,
scale = 1.80,
font = 'default-game',
alignment = 'center',
scale_with_zoom = false
}
this.circle =
rendering.draw_circle {
surface = surface,
target = this.locomotive,
color = this.locomotive.color,
filled = false,
radius = this.locomotive_xp_aura,
only_in_alt_mode = true
}
end
function Public.locomotive_spawn(surface, position)
local this = WPT.get()
for y = -6, 6, 2 do
surface.create_entity(
{name = 'straight-rail', position = {position.x, position.y + y}, force = 'player', direction = 0}
)
end
this.locomotive =
surface.create_entity({name = 'locomotive', position = {position.x, position.y + -3}, force = 'player'})
this.locomotive.get_inventory(defines.inventory.fuel).insert({name = 'wood', count = 100})
this.locomotive_cargo =
surface.create_entity({name = 'cargo-wagon', position = {position.x, position.y + 3}, force = 'player'})
this.locomotive_cargo.get_inventory(defines.inventory.cargo_wagon).insert({name = 'raw-fish', count = 8})
rendering.draw_light(
{
sprite = 'utility/light_medium',
scale = 5.5,
intensity = 1,
minimum_darkness = 0,
oriented = true,
color = {255, 255, 255},
target = this.locomotive,
surface = surface,
visible = true,
only_in_alt_mode = false
}
)
for y = -1, 0, 0.05 do
local scale = math.random(50, 100) * 0.01
rendering.draw_sprite(
{
sprite = 'item/raw-fish',
orientation = math.random(0, 100) * 0.01,
x_scale = scale,
y_scale = scale,
tint = {math.random(60, 255), math.random(60, 255), math.random(60, 255)},
render_layer = 'selection-box',
target = this.locomotive_cargo,
target_offset = {-0.7 + math.random(0, 140) * 0.01, y},
surface = surface
}
)
end
this.locomotive.color = {0, 255, 0}
this.locomotive.minable = false
this.locomotive_cargo.minable = false
this.locomotive_cargo.operable = true
local locomotive = ICW.register_wagon(this.locomotive)
local wagon = ICW.register_wagon(this.locomotive_cargo)
locomotive.entity_count = 999
wagon.entity_count = 999
this.loco_surface = locomotive.surface
this.locomotive_index = locomotive
end
function Public.inside(pos, area)
local lt = area.left_top
local rb = area.right_bottom
return pos.x >= lt.x and pos.y >= lt.y and pos.x <= rb.x and pos.y <= rb.y
end
function Public.contains_positions(pos, area)
if Public.inside(pos, area) then
return true
end
return false
end
Event.on_nth_tick(5, tick)
return Public

View File

@ -0,0 +1,725 @@
local Event = require 'utils.event'
local WPT = require 'maps.mountain_fortress_v3.table'
local WD = require 'modules.wave_defense.table'
local ICW = require 'maps.mountain_fortress_v3.icw.main'
local format_number = require 'util'.format_number
local shopkeeper = '[color=blue]Shopkeeper:[/color]'
local random = math.random
local Public = {}
local function shuffle(tbl)
local size = #tbl
for i = size, 1, -1 do
local rand = random(size)
tbl[i], tbl[rand] = tbl[rand], tbl[i]
end
return tbl
end
function Public.get_items()
local this = WPT.get()
local threat_cost = 100000
local health_cost = 30000 * (1 + this.health_upgrades)
local aura_cost = 30000 * (1 + this.aura_upgrades)
local xp_point_boost_cost = 30000 * (1 + this.xp_points_upgrade)
local items = {}
items['clear_threat_level'] = {
stack = 1,
value = 'coin',
price = threat_cost,
tooltip = '[Wave Defense]:\nReduces the threat level by 50%\nUsable if threat level is too high.\nCan be purchased multiple times.',
sprite = 'item/computer',
enabled = true
}
items['locomotive_max_health'] = {
stack = 1,
value = 'coin',
price = health_cost,
tooltip = '[Locomotive Health]:\nUpgrades the train health.\nCan be purchased multiple times.',
sprite = 'item/computer',
enabled = true
}
items['locomotive_xp_aura'] = {
stack = 1,
value = 'coin',
price = aura_cost,
tooltip = '[XP Aura]:\nUpgrades the aura that is around the train.\nNote! Reaching breach walls gives more XP.',
sprite = 'item/computer',
enabled = true
}
items['xp_points_boost'] = {
stack = 1,
value = 'coin',
price = xp_point_boost_cost,
tooltip = '[XP Points]:\nUpgrades the amount of xp points you get inside the XP aura',
sprite = 'item/computer',
enabled = true
}
items['small-lamp'] = {stack = 1, value = 'coin', price = 5, tooltip = 'Small Sunlight'}
items['wood'] = {stack = 50, value = 'coin', price = 12, tooltip = 'Some fine Wood'}
items['iron-ore'] = {stack = 50, value = 'coin', price = 12, tooltip = 'Some chunky iron'}
items['copper-ore'] = {stack = 50, value = 'coin', price = 12, tooltip = 'Some chunky copper'}
items['stone'] = {stack = 50, value = 'coin', price = 12, tooltip = 'Some chunky stone'}
items['coal'] = {stack = 50, value = 'coin', price = 12, tooltip = 'Some chunky coal'}
items['uranium-ore'] = {stack = 50, value = 'coin', price = 12, tooltip = 'Some chunky uranium'}
items['land-mine'] = {stack = 1, value = 'coin', price = 25, tooltip = 'Land Boom Danger'}
items['raw-fish'] = {stack = 1, value = 'coin', price = 4, tooltip = 'Flappy Fish'}
items['firearm-magazine'] = {stack = 1, value = 'coin', price = 5, tooltip = 'Firearm Pew'}
items['crude-oil-barrel'] = {stack = 1, value = 'coin', price = 8, tooltip = 'Crude Oil Flame'}
--
--[[
items['loader'] = {stack = 1, value = 'coin', price = 150, tooltip = 'Ground Inserter.'}
items['fast-loader'] = {
stack = 1,
value = 'coin',
price = 300,
tooltip = 'Ground Fast Inserter'
}
]] return items
end
local space = {
minimal_height = 10,
top_padding = 0,
bottom_padding = 0
}
local function addStyle(guiIn, styleIn)
for k, v in pairs(styleIn) do
guiIn.style[k] = v
end
end
local function adjustSpace(guiIn)
addStyle(guiIn.add {type = 'line', direction = 'horizontal'}, space)
end
local function validate_player(player)
if not player then
return false
end
if not player.valid then
return false
end
if not player.character then
return false
end
if not player.connected then
return false
end
if not game.players[player.index] then
return false
end
return true
end
local function close_market_gui(player)
local this = WPT.get()
local element = player.gui.center
local data = this.players[player.index].data
if not data then
return
end
if element and element.valid then
element = element['market_gui']
if element and element.valid then
element.destroy()
end
if data.frame and data.frame.valid then
data.frame.destroy()
for k, _ in pairs(data) do
data[k] = nil
end
end
end
end
local function redraw_market_items(gui, player, search_text)
if not validate_player(player) then
return
end
local this = WPT.get()
gui.clear()
shuffle(Public.get_items())
local inventory = player.get_main_inventory()
local player_item_count = inventory.get_item_count('coin')
local items_table = gui.add({type = 'table', column_count = 6})
local slider_value = math.ceil(this.players[player.index].data.slider.slider_value)
for name, opts in pairs(Public.get_items()) do
if not search_text then
goto continue
end
if not search_text.text then
goto continue
end
if not string.lower(name:gsub('-', ' ')):find(search_text.text) then
goto continue
end
local item_count = opts.stack * slider_value
local item_cost = opts.price * slider_value
local flow = items_table.add({type = 'flow'})
flow.style.vertical_align = 'bottom'
local button =
flow.add(
{
type = 'sprite-button',
sprite = opts.sprite or 'item/' .. name,
number = item_count,
name = name,
tooltip = opts.tooltip,
style = 'slot_button',
enabled = opts.enabled
}
)
flow.add(
{
type = 'label',
caption = format_number(item_cost, true) .. ' coins'
}
)
if player_item_count < item_cost then
button.enabled = false
end
::continue::
end
end
local function redraw_coins_left(gui, player)
if not validate_player(player) then
return
end
gui.clear()
local inventory = player.get_main_inventory()
local player_item_count = inventory.get_item_count('coin')
local coinsleft =
gui.add(
{
type = 'label',
caption = 'Coins left: ' .. format_number(player_item_count, true)
}
)
adjustSpace(coinsleft)
end
local function slider_changed(event)
local player = game.players[event.player_index]
local this = WPT.get()
local slider_value
slider_value = this.players
if not slider_value then
return
end
slider_value = slider_value[player.index].data
if not slider_value then
return
end
slider_value = slider_value.slider
if not slider_value then
return
end
slider_value = slider_value.slider_value
if not slider_value then
return
end
slider_value = math.ceil(slider_value)
this.players[player.index].data.text_input.text = slider_value
redraw_market_items(this.players[player.index].data.item_frame, player, this.players[player.index].data.search_text)
end
local function text_changed(event)
local this = WPT.get()
local player = game.players[event.player_index]
local data = this.players[player.index].data
if not data then
return
end
if not data.text_input then
return
end
if not data.text_input.text then
return
end
local value = 0
tonumber(data.text_input.text)
if not value then
return
end
data.slider.slider_value = value
redraw_market_items(data.item_frame, player, data.search_text)
end
local function gui_opened(event)
local this = WPT.get()
if not event.gui_type == defines.gui_type.entity then
return
end
local entity = event.entity
if not entity then
return
end
if entity ~= this.market then
return
end
local player = game.players[event.player_index]
if not validate_player(player) then
return
end
local inventory = player.get_main_inventory()
local player_item_count = inventory.get_item_count('coin')
local data = this.players[player.index].data
if data.frame then
data.frame = nil
end
local frame =
player.gui.screen.add(
{
type = 'frame',
caption = 'Market',
direction = 'vertical',
name = 'market_gui'
}
)
frame.auto_center = true
player.opened = frame
frame.style.minimal_width = 500
frame.style.minimal_height = 250
local search_table = frame.add({type = 'table', column_count = 2})
search_table.add({type = 'label', caption = 'Search: '})
local search_text = search_table.add({type = 'textfield'})
adjustSpace(frame)
local pane =
frame.add {
type = 'scroll-pane',
direction = 'vertical',
vertical_scroll_policy = 'always',
horizontal_scroll_policy = 'never'
}
pane.style.maximal_height = 200
pane.style.horizontally_stretchable = true
pane.style.minimal_height = 200
pane.style.right_padding = 0
local flow = frame.add({type = 'flow'})
adjustSpace(flow)
local slider_frame = frame.add({type = 'table', column_count = 5})
local left_button = slider_frame.add({type = 'button', caption = '-1', name = 'less'})
local slider =
slider_frame.add(
{
type = 'slider',
minimum_value = 1,
maximum_value = 1e3,
value = 1
}
)
local right_button = slider_frame.add({type = 'button', caption = '+1', name = 'more'})
left_button.style.width = 0
left_button.style.height = 0
right_button.style.width = 0
right_button.style.height = 0
slider_frame.add(
{
type = 'label',
caption = 'Qty:'
}
)
local text_input =
slider_frame.add(
{
type = 'textfield',
text = 1
}
)
local coinsleft = frame.add({type = 'flow'})
coinsleft.add(
{
type = 'label',
caption = 'Coins left: ' .. format_number(player_item_count, true)
}
)
this.players[player.index].data.search_text = search_text
this.players[player.index].data.text_input = text_input
this.players[player.index].data.slider = slider
this.players[player.index].data.frame = frame
this.players[player.index].data.item_frame = pane
this.players[player.index].data.coins_left = coinsleft
redraw_market_items(pane, player, search_text)
end
local function gui_click(event)
local this = WPT.get()
local wdt = WD.get_table()
local element = event.element
local player = game.players[event.player_index]
if not validate_player(player) then
return
end
if not this.players[player.index] then
return
end
local data = this.players[player.index].data
if not data then
return
end
if not element.valid then
return
end
local name = element.name
if name == 'less' then
local slider_value = this.players[player.index].data.slider.slider_value
if slider_value > 1 then
data.slider.slider_value = slider_value - 1
data.text_input.text = data.slider.slider_value
redraw_market_items(data.item_frame, player, data.search_text)
end
return
elseif name == 'more' then
local slider_value = data.slider.slider_value
if slider_value <= 1e3 then
data.slider.slider_value = slider_value + 1
data.text_input.text = data.slider.slider_value
redraw_market_items(data.item_frame, player, data.search_text)
end
return
end
if not player.opened then
return
end
if not player.opened.name == 'market' then
return
end
if not data then
return
end
local item = Public.get_items()[name]
if not item then
return
end
local inventory = player.get_main_inventory()
local player_item_count = inventory.get_item_count(item.value)
local slider_value = math.ceil(data.slider.slider_value)
local cost = (item.price * slider_value)
local item_count = item.stack * slider_value
if name == 'clear_threat_level' then
if wdt.threat <= 10000 then
return player.print(
shopkeeper .. ' ' .. player.name .. ', threat is already low!',
{r = 0.98, g = 0.66, b = 0.22}
)
end
player.remove_item({name = item.value, count = cost})
game.print(
shopkeeper .. ' ' .. player.name .. ' has bought the group some extra time. Threat level is reduced by 50%!',
{r = 0.98, g = 0.66, b = 0.22}
)
this.threat_upgrades = this.threat_upgrades + 1
wdt.threat = wdt.threat / 2
redraw_market_items(data.item_frame, player, data.search_text)
redraw_coins_left(data.coins_left, player)
return
end
if name == 'locomotive_max_health' then
player.remove_item({name = item.value, count = cost})
game.print(
shopkeeper ..
' ' .. player.name .. ' has bought the group a train health modifier! The train health is now buffed!',
{r = 0.98, g = 0.66, b = 0.22}
)
this.locomotive_max_health = this.locomotive_max_health + 2500
local m = this.locomotive_health / this.locomotive_max_health
this.locomotive.health = 1000 * m
this.train_upgrades = this.train_upgrades + 1
this.health_upgrades = this.health_upgrades + 1
rendering.set_text(this.health_text, 'HP: ' .. this.locomotive_health .. ' / ' .. this.locomotive_max_health)
redraw_market_items(data.item_frame, player, data.search_text)
redraw_coins_left(data.coins_left, player)
return
end
if name == 'locomotive_xp_aura' then
player.remove_item({name = item.value, count = cost})
game.print(
shopkeeper .. ' ' .. player.name .. ' has bought the group a XP aura modifier! The XP aura is now buffed!',
{r = 0.98, g = 0.66, b = 0.22}
)
this.locomotive_xp_aura = this.locomotive_xp_aura + 5
this.aura_upgrades = this.aura_upgrades + 1
this.train_upgrades = this.train_upgrades + 1
if this.circle then
rendering.destroy(this.circle)
end
this.circle =
rendering.draw_circle {
surface = game.surfaces[this.active_surface_index],
target = this.locomotive,
color = this.locomotive.color,
filled = false,
radius = this.locomotive_xp_aura,
only_in_alt_mode = true
}
redraw_market_items(data.item_frame, player, data.search_text)
redraw_coins_left(data.coins_left, player)
return
end
if name == 'xp_points_boost' then
player.remove_item({name = item.value, count = cost})
game.print(
shopkeeper ..
' ' .. player.name .. ' has bought the group a XP point modifier! You now gain more XP points!',
{r = 0.98, g = 0.66, b = 0.22}
)
this.xp_points = this.xp_points + 0.5
this.xp_points_upgrade = this.xp_points_upgrade + 1
this.train_upgrades = this.train_upgrades + 1
redraw_market_items(data.item_frame, player, data.search_text)
redraw_coins_left(data.coins_left, player)
return
end
if player_item_count >= cost then
if player.can_insert({name = name, count = item_count}) then
player.play_sound({path = 'entity-close/stone-furnace', volume_modifier = 0.65})
player.remove_item({name = item.value, count = cost})
local inserted_count = player.insert({name = name, count = item_count})
if inserted_count < item_count then
player.play_sound({path = 'utility/cannot_build', volume_modifier = 0.65})
player.insert({name = item.value, count = cost})
player.remove_item({name = name, count = inserted_count})
end
redraw_market_items(data.item_frame, player, data.search_text)
redraw_coins_left(data.coins_left, player)
end
end
end
local function gui_closed(event)
local player = game.players[event.player_index]
local this = WPT.get()
local type = event.gui_type
if type == defines.gui_type.custom then
local data = this.players[player.index].data
if not data then
return
end
close_market_gui(player)
end
end
local function inside(pos, area)
local lt = area.left_top
local rb = area.right_bottom
return pos.x >= lt.x and pos.y >= lt.y and pos.x <= rb.x and pos.y <= rb.y
end
local function contains_positions(pos, area)
if inside(pos, area) then
return true
end
return false
end
local function on_player_changed_position(event)
local this = WPT.get()
local player = game.players[event.player_index]
if not this.players[player.index] then
return
end
local data = this.players[player.index].data
if data and data.frame and data.frame.valid then
local position = this.market.position
local area = {
left_top = {x = position.x - 10, y = position.y - 10},
right_bottom = {x = position.x + 10, y = position.y + 10}
}
if contains_positions(player.position, area) then
return
end
if not data then
return
end
close_market_gui(player)
end
end
local function create_market(data, rebuild)
local surface = data.surface
local this = data.this
if rebuild then
local radius = 1024
local area = {{x = -radius, y = -radius}, {x = radius, y = radius}}
for _, entity in pairs(surface.find_entities_filtered {area = area, name = 'market'}) do
entity.destroy()
end
this.market = nil
end
local locomotive = this.locomotive_index
local center_position = {
x = locomotive.area.left_top.x + (locomotive.area.right_bottom.x - locomotive.area.left_top.x) * 0.5,
y = locomotive.area.left_top.y + (locomotive.area.right_bottom.y - locomotive.area.left_top.y) * 0.5
}
this.market = surface.create_entity {name = 'market', position = center_position, force = 'player'}
rendering.draw_text {
text = 'Market',
surface = surface,
target = this.market,
target_offset = {0, 2},
color = {r = 0.98, g = 0.66, b = 0.22},
alignment = 'center'
}
this.market.destructible = false
if not this.loco_surface then
this.loco_surface = this.locomotive.surface
return
end
local position = this.loco_surface.find_non_colliding_position('market', center_position, 128, 0.5)
local e =
this.loco_surface.create_entity(
{name = 'big-biter', position = position, force = 'player', create_build_effect_smoke = false}
)
e.ai_settings.allow_destroy_when_commands_fail = false
e.ai_settings.allow_try_return_to_spawner = false
for x = center_position.x - 5, center_position.x + 5, 1 do
for y = center_position.y - 5, center_position.y + 5, 1 do
if math.random(1, 2) == 1 then
this.loco_surface.spill_item_stack(
{x + math.random(0, 9) * 0.1, y + math.random(0, 9) * 0.1},
{name = 'raw-fish', count = 1},
false
)
end
this.loco_surface.set_tiles({{name = 'blue-refined-concrete', position = {x, y}}}, true)
end
end
for x = center_position.x - 3, center_position.x + 3, 1 do
for y = center_position.y - 3, center_position.y + 3, 1 do
if math.random(1, 2) == 1 then
this.loco_surface.spill_item_stack(
{x + math.random(0, 9) * 0.1, y + math.random(0, 9) * 0.1},
{name = 'raw-fish', count = 1},
false
)
end
this.loco_surface.set_tiles({{name = 'cyan-refined-concrete', position = {x, y}}}, true)
end
end
end
local function place_market()
local this = WPT.get()
local icw_table = ICW.get_table()
if not this.locomotive then
return
end
if not this.locomotive.valid then
return
end
local unit_surface = this.locomotive.unit_number
local surface = game.surfaces[icw_table.wagons[unit_surface].surface.index]
local data = {
this = this,
surface = surface
}
if not this.market then
create_market(data)
elseif not this.market.valid then
create_market(data, true)
end
end
local function on_tick()
if game.tick % 30 == 0 then
place_market()
end
end
Event.on_nth_tick(5, on_tick)
Event.add(defines.events.on_gui_click, gui_click)
Event.add(defines.events.on_gui_opened, gui_opened)
Event.add(defines.events.on_gui_value_changed, slider_changed)
Event.add(defines.events.on_gui_text_changed, text_changed)
Event.add(defines.events.on_gui_closed, gui_closed)
Event.add(defines.events.on_player_changed_position, on_player_changed_position)
return Public

View File

@ -0,0 +1,69 @@
local LootRaffle = require 'functions.loot_raffle'
local Public = {}
local math_random = math.random
local math_abs = math.abs
local math_floor = math.floor
local blacklist = {
['cargo-wagon'] = true,
['locomotive'] = true,
['artillery-wagon'] = true,
['fluid-wagon'] = true
}
function Public.add(surface, position, chest)
local budget = 48 + math_abs(position.y) * 1.75
budget = budget * math_random(25, 175) * 0.01
if math_random(1, 128) == 1 then
budget = budget * 4
chest = 'crash-site-chest-' .. math_random(1, 2)
end
budget = math_floor(budget) + 1
local item_stacks = LootRaffle.roll(budget, 8, blacklist)
local container = surface.create_entity({name = chest, position = position, force = 'neutral'})
for _, item_stack in pairs(item_stacks) do
container.insert(item_stack)
end
container.minable = false
for _ = 1, 3, 1 do
if math_random(1, 8) == 1 then
container.insert({name = 'explosives', count = math_random(25, 50)})
else
break
end
end
end
function Public.add_rare(surface, position, chest, magic)
local budget = magic * 48 + math_abs(position.y) * 1.75
budget = budget * math_random(25, 175) * 0.01
if math_random(1, 128) == 1 then
budget = budget * 6
chest = 'crash-site-chest-' .. math_random(1, 2)
end
budget = math_floor(budget) + 1
local item_stacks = LootRaffle.roll(budget, 8, blacklist)
local container = surface.create_entity({name = chest, position = position, force = 'neutral'})
for _, item_stack in pairs(item_stacks) do
container.insert(item_stack)
end
container.minable = false
for _ = 1, 3, 1 do
if math_random(1, 8) == 1 then
container.insert({name = 'explosives', count = math_random(25, 50)})
else
break
end
end
end
return Public

View File

@ -0,0 +1,530 @@
-- modules
require 'maps.mountain_fortress_v3.generate'
require 'maps.mountain_fortress_v3.player_list'
require 'maps.mountain_fortress_v3.commands'
require 'maps.mountain_fortress_v3.flamethrower_nerf'
require 'modules.dynamic_landfill'
require 'modules.shotgun_buff'
require 'modules.rocks_heal_over_time'
require 'modules.no_deconstruction_of_neutral_entities'
require 'modules.rocks_yield_ore_veins'
require 'modules.spawners_contain_biters'
require 'modules.biters_yield_coins'
require 'modules.wave_defense.main'
require 'modules.pistol_buffs'
require 'modules.mineable_wreckage_yields_scrap'
local CS = require 'maps.mountain_fortress_v3.surface'
local Server = require 'utils.server'
local Explosives = require 'modules.explosives'
local Entities = require 'maps.mountain_fortress_v3.entities'
local update_gui = require 'maps.mountain_fortress_v3.gui'
local ICW = require 'maps.mountain_fortress_v3.icw.main'
local WD = require 'modules.wave_defense.table'
local Map = require 'modules.map_info'
local RPG = require 'maps.mountain_fortress_v3.rpg'
local Terrain = require 'maps.mountain_fortress_v3.terrain'
local Event = require 'utils.event'
local WPT = require 'maps.mountain_fortress_v3.table'
local Locomotive = require 'maps.mountain_fortress_v3.locomotive'.locomotive_spawn
local render_train_hp = require 'maps.mountain_fortress_v3.locomotive'.render_train_hp
local Score = require 'comfy_panel.score'
local Poll = require 'comfy_panel.poll'
local Collapse = require 'modules.collapse'
local Difficulty = require 'modules.difficulty_vote'
local Task = require 'utils.task'
local Public = {}
local starting_items = {['pistol'] = 1, ['firearm-magazine'] = 16, ['rail'] = 16, ['wood'] = 16, ['explosives'] = 32}
local function disable_tech()
game.forces.player.technologies['landfill'].enabled = false
game.forces.player.technologies['optics'].researched = true
game.forces.player.technologies['railway'].researched = true
game.forces.player.technologies['land-mine'].enabled = false
end
local function set_difficulty()
local Diff = Difficulty.get()
local wave_defense_table = WD.get_table()
local player_count = #game.connected_players
if not Diff.difficulty_vote_value then
Diff.difficulty_vote_value = 0.1
end
wave_defense_table.max_active_biters = 768 + player_count * (90 * Diff.difficulty_vote_value)
-- threat gain / wave
wave_defense_table.threat_gain_multiplier = 1.2 + player_count * Diff.difficulty_vote_value * 0.1
local amount = player_count * 0.25 + 2
amount = math.floor(amount)
if amount > 6 then
amount = 6
end
Collapse.set_amount(amount)
wave_defense_table.wave_interval = 3600 - player_count * 60
if wave_defense_table.wave_interval < 1800 then
wave_defense_table.wave_interval = 1800
end
end
local function render_direction(surface)
local counter = WPT.get('soft_reset_counter')
if counter then
rendering.draw_text {
text = 'Welcome to Mountain Fortress v3!\nRun: ' .. counter,
surface = surface,
target = {-0, 10},
color = {r = 0.98, g = 0.66, b = 0.22},
scale = 3,
font = 'heading-1',
alignment = 'center',
scale_with_zoom = false
}
else
rendering.draw_text {
text = 'Welcome to Mountain Fortress v3!',
surface = surface,
target = {-0, 10},
color = {r = 0.98, g = 0.66, b = 0.22},
scale = 3,
font = 'heading-1',
alignment = 'center',
scale_with_zoom = false
}
end
rendering.draw_text {
text = '',
surface = surface,
target = {-0, 20},
color = {r = 0.98, g = 0.66, b = 0.22},
scale = 3,
font = 'heading-1',
alignment = 'center',
scale_with_zoom = false
}
rendering.draw_text {
text = '',
surface = surface,
target = {-0, 30},
color = {r = 0.98, g = 0.66, b = 0.22},
scale = 3,
font = 'heading-1',
alignment = 'center',
scale_with_zoom = false
}
rendering.draw_text {
text = '',
surface = surface,
target = {-0, 40},
color = {r = 0.98, g = 0.66, b = 0.22},
scale = 3,
font = 'heading-1',
alignment = 'center',
scale_with_zoom = false
}
rendering.draw_text {
text = '',
surface = surface,
target = {-0, 50},
color = {r = 0.98, g = 0.66, b = 0.22},
scale = 3,
font = 'heading-1',
alignment = 'center',
scale_with_zoom = false
}
rendering.draw_text {
text = '',
surface = surface,
target = {-0, 60},
color = {r = 0.98, g = 0.66, b = 0.22},
scale = 3,
font = 'heading-1',
alignment = 'center',
scale_with_zoom = false
}
rendering.draw_text {
text = 'Biters will attack this area.',
surface = surface,
target = {-0, 70},
color = {r = 0.98, g = 0.66, b = 0.22},
scale = 3,
font = 'heading-1',
alignment = 'center',
scale_with_zoom = false
}
local x_min = -Terrain.level_width / 2
local x_max = Terrain.level_width / 2
surface.create_entity({name = 'electric-beam', position = {x_min, 74}, source = {x_min, 74}, target = {x_max, 74}})
surface.create_entity({name = 'electric-beam', position = {x_min, 74}, source = {x_min, 74}, target = {x_max, 74}})
end
function Public.reset_map()
local Settings = CS.get()
local Diff = Difficulty.get()
local this = WPT.get()
local wave_defense_table = WD.get_table()
local get_score = Score.get_table()
for _, player in pairs(game.players) do
if player.controller_type == defines.controllers.editor then
player.toggle_map_editor()
end
end
if not this.active_surface_index then
this.active_surface_index = Settings.active_surface_index
else
this.active_surface_index = CS.create_surface()
end
Poll.reset()
ICW.reset()
game.reset_time_played()
WPT.reset_table()
disable_tech()
local surface = game.surfaces[this.active_surface_index]
surface.min_brightness = 0.5
Explosives.set_surface_whitelist({[surface.name] = true})
surface.request_to_generate_chunks({-17, 47}, 1)
surface.force_generate_chunk_requests()
game.forces.player.set_spawn_position({-27, 25}, surface)
game.forces.enemy.set_ammo_damage_modifier('bullet', 1)
game.forces.enemy.set_turret_attack_modifier('gun-turret', 1)
global.bad_fire_history = {}
global.friendly_fire_history = {}
global.landfill_history = {}
global.mining_history = {}
get_score.score_table = {}
Diff.difficulty_poll_closing_timeout = game.tick + 90000
Diff.difficulty_player_votes = {}
Collapse.set_kill_entities(false)
Collapse.set_speed(8)
Collapse.set_amount(1)
Collapse.set_max_line_size(Terrain.level_width)
Collapse.set_surface(surface)
Collapse.set_position({0, 162})
Collapse.set_direction('north')
Collapse.start_now(false)
this.locomotive_health = 10000
this.locomotive_max_health = 10000
this.cargo_health = 10000
this.cargo_max_health = 10000
Locomotive(surface, {x = -18, y = 25})
render_train_hp()
render_direction(surface)
RPG.rpg_reset_all_players()
WD.reset_wave_defense()
wave_defense_table.surface_index = this.active_surface_index
wave_defense_table.target = this.locomotive_cargo
wave_defense_table.nest_building_density = 32
wave_defense_table.game_lost = false
wave_defense_table.spawn_position = {x = 0, y = 100}
set_difficulty()
Task.start_queue()
Task.set_queue_speed(2)
this.chunk_load_tick = game.tick + 500
end
local function on_player_changed_position(event)
local this = WPT.get()
local player = game.players[event.player_index]
local map_name = 'mountain_fortress_v3'
if string.sub(player.surface.name, 0, #map_name) ~= map_name then
return
end
local position = player.position
local surface = game.surfaces[this.active_surface_index]
if position.y >= 74 then
player.teleport({position.x, position.y - 1}, surface)
player.print('Forcefield does not approve.', {r = 0.98, g = 0.66, b = 0.22})
if player.character then
player.character.health = player.character.health - 5
player.character.surface.create_entity({name = 'water-splash', position = position})
if player.character.health <= 0 then
player.character.die('enemy')
end
end
end
end
local function on_player_joined_game(event)
local this = WPT.get()
local surface = game.surfaces[this.active_surface_index]
local player = game.players[event.player_index]
set_difficulty(event)
if not this.players then
this.players = {}
end
if not this.players[player.index] then
this.players[player.index] = {
first_join = false,
data = {}
}
end
if not this.players[player.index].first_join then
player.print('Greetings, ' .. player.name .. '!', {r = 0.98, g = 0.66, b = 0.22})
player.print('Please read the map info.', {r = 0.98, g = 0.66, b = 0.22})
this.players[player.index].first_join = true
end
if player.surface.index ~= this.active_surface_index then
if not player.character then
player.create_character()
end
player.teleport(
surface.find_non_colliding_position('character', game.forces.player.get_spawn_position(surface), 3, 0, 5),
surface
)
for item, amount in pairs(starting_items) do
player.insert({name = item, count = amount})
end
end
end
local function on_player_left_game()
set_difficulty()
end
local function on_pre_player_left_game(event)
local this = WPT.get()
local player = game.players[event.player_index]
if player.controller_type == defines.controllers.editor then
player.toggle_map_editor()
end
if player.character then
this.offline_players[#this.offline_players + 1] = {
index = event.player_index,
name = player.name,
tick = game.tick
}
end
end
local function remove_offline_players()
local this = WPT.get()
local offline_players = WPT.get('offline_players')
local active_surface_index = WPT.get('active_surface_index')
local surface = game.surfaces[active_surface_index]
local keeper = '[color=blue]Cleaner:[/color]'
local player_inv = {}
local items = {}
if #offline_players > 0 then
local later = {}
for i = 1, #offline_players, 1 do
if
offline_players[i] and game.players[offline_players[i].index] and
game.players[offline_players[i].index].connected
then
this.offline_players[i] = nil
else
if offline_players[i] and offline_players[i].tick < game.tick - 54000 then
local name = offline_players[i].name
player_inv[1] =
game.players[offline_players[i].index].get_inventory(defines.inventory.character_main)
player_inv[2] =
game.players[offline_players[i].index].get_inventory(defines.inventory.character_armor)
player_inv[3] =
game.players[offline_players[i].index].get_inventory(defines.inventory.character_guns)
player_inv[4] =
game.players[offline_players[i].index].get_inventory(defines.inventory.character_ammo)
player_inv[5] =
game.players[offline_players[i].index].get_inventory(defines.inventory.character_trash)
local pos = game.forces.player.get_spawn_position(surface)
local e =
surface.create_entity(
{
name = 'character',
position = pos,
force = 'neutral'
}
)
local inv = e.get_inventory(defines.inventory.character_main)
for ii = 1, 5, 1 do
if player_inv[ii].valid then
for iii = 1, #player_inv[ii], 1 do
if player_inv[ii][iii].valid then
items[#items + 1] = player_inv[ii][iii]
end
end
end
end
if #items > 0 then
for item = 1, #items, 1 do
if items[item].valid then
inv.insert(items[item])
end
end
game.print(
keeper .. ' ' .. name .. ' has left his goodies! [gps=' .. pos.x .. ',' .. pos.y .. ']',
{r = 0.98, g = 0.66, b = 0.22}
)
e.die('neutral')
else
e.destroy()
end
for ii = 1, 5, 1 do
if player_inv[ii].valid then
player_inv[ii].clear()
end
end
this.offline_players[i] = nil
else
later[#later + 1] = offline_players[i]
end
end
end
this.offline_players = {}
if #later > 0 then
for i = 1, #later, 1 do
this.offline_players[#offline_players + 1] = later[i]
end
end
end
end
local function disable_recipes()
local force = game.forces.player
force.recipes['cargo-wagon'].enabled = false
force.recipes['fluid-wagon'].enabled = false
force.recipes['artillery-wagon'].enabled = false
force.recipes['locomotive'].enabled = false
force.recipes['pistol'].enabled = false
end
local function on_research_finished(event)
disable_recipes()
event.research.force.character_inventory_slots_bonus = game.forces.player.mining_drill_productivity_bonus * 50 -- +5 Slots / level
local mining_speed_bonus = game.forces.player.mining_drill_productivity_bonus * 5 -- +50% speed / level
if event.research.force.technologies['steel-axe'].researched then
mining_speed_bonus = mining_speed_bonus + 0.5
end -- +50% speed for steel-axe research
event.research.force.manual_mining_speed_modifier = mining_speed_bonus
end
local function is_locomotive_valid()
local this = WPT.get()
if not this.locomotive.valid then
Entities.loco_died()
end
end
local function has_the_game_ended()
local this = WPT.get()
if this.game_reset_tick then
if this.game_reset_tick < game.tick then
if not this.disable_reset then
this.game_reset_tick = nil
Public.reset_map()
else
if not this.reset_the_game then
game.print('Auto reset is disabled. Server is shutting down!', {r = 0.22, g = 0.88, b = 0.22})
local message = 'Auto reset is disabled. Server is shutting down!'
Server.to_discord_bold(table.concat {'*** ', message, ' ***'})
Server.stop_scenario()
this.reset_the_game = true
end
end
end
return
end
end
local function chunk_load()
local this = WPT.get()
if this.chunk_load_tick then
if this.chunk_load_tick < game.tick then
this.chunk_load_tick = nil
Task.set_queue_speed(0.8)
end
end
end
local on_tick = function()
local active_surface_index = WPT.get('active_surface_index')
local surface = game.surfaces[active_surface_index]
local wave_defense_table = WD.get_table()
if game.tick % 30 == 0 then
for _, player in pairs(game.connected_players) do
update_gui(player)
end
if game.tick % 1800 == 0 then
local collapse_pos = Collapse.get_position()
local position = surface.find_non_colliding_position('stone-furnace', collapse_pos, 128, 1)
if position then
wave_defense_table.spawn_position = position
end
remove_offline_players()
Entities.set_scores()
end
is_locomotive_valid()
has_the_game_ended()
chunk_load()
end
end
local on_init = function()
local this = WPT.get()
Public.reset_map()
global.custom_highscore.description = 'Wagon distance reached:'
this.rocks_yield_ore_maximum_amount = 500
this.type_modifier = 1
this.rocks_yield_ore_base_amount = 50
this.rocks_yield_ore_distance_modifier = 0.025
local T = Map.Pop_info()
T.localised_category = 'mountain_fortress'
T.main_caption_color = {r = 150, g = 150, b = 0}
T.sub_caption_color = {r = 0, g = 150, b = 0}
Explosives.set_destructible_tile('out-of-map', 1500)
Explosives.set_destructible_tile('water', 1000)
Explosives.set_destructible_tile('water-green', 1000)
Explosives.set_destructible_tile('deepwater-green', 1000)
Explosives.set_destructible_tile('deepwater', 1000)
Explosives.set_destructible_tile('water-shallow', 1000)
end
Event.on_nth_tick(10, on_tick)
Event.on_init(on_init)
Event.add(defines.events.on_player_joined_game, on_player_joined_game)
Event.add(defines.events.on_player_left_game, on_player_left_game)
Event.add(defines.events.on_player_changed_position, on_player_changed_position)
Event.add(defines.events.on_research_finished, on_research_finished)
Event.add(defines.events.on_pre_player_left_game, on_pre_player_left_game)
return Public

View File

@ -0,0 +1,190 @@
local WPT = require 'maps.mountain_fortress_v3.table'
local Public = {}
local max_spill = 60
local math_random = math.random
local math_floor = math.floor
local math_sqrt = math.sqrt
local valid_rocks = {
['sand-rock-big'] = true,
['rock-big'] = true,
['rock-huge'] = true
}
local rock_yield = {
['rock-big'] = 1,
['rock-huge'] = 2,
['sand-rock-big'] = 1
}
local function create_particles(surface, name, position, amount, cause_position)
local d1 = (-100 + math_random(0, 200)) * 0.0004
local d2 = (-100 + math_random(0, 200)) * 0.0004
if cause_position then
d1 = (cause_position.x - position.x) * 0.025
d2 = (cause_position.y - position.y) * 0.025
end
for i = 1, amount, 1 do
local m = math_random(4, 10)
local m2 = m * 0.005
surface.create_particle(
{
name = name,
position = position,
frame_speed = 1,
vertical_speed = 0.130,
height = 0,
movement = {
(m2 - (math_random(0, m) * 0.01)) + d1,
(m2 - (math_random(0, m) * 0.01)) + d2
}
}
)
end
end
local function mining_chances_ores()
local data = {
{name = 'iron-ore', chance = 545},
{name = 'copper-ore', chance = 545},
{name = 'coal', chance = 545},
{name = 'stone', chance = 545},
{name = 'uranium-ore', chance = 50}
}
return data
end
local harvest_raffle_ores = {}
for _, t in pairs(mining_chances_ores()) do
for _ = 1, t.chance, 1 do
table.insert(harvest_raffle_ores, t.name)
end
end
local size_of_ore_raffle = #harvest_raffle_ores
local function get_amount(data)
local entity = data.entity
local this = data.this
local distance_to_center = math_floor(math_sqrt(entity.position.x ^ 2 + entity.position.y ^ 2))
local type_modifier
local amount
local second_amount
local distance_modifier = 0.25
local base_amount = 25
local second_base_amount = 10
local maximum_amount = 100
if this.type_modifier then
type_modifier = this.type_modifier
end
if this.rocks_yield_ore_distance_modifier then
distance_modifier = this.rocks_yield_ore_distance_modifier
end
if this.rocks_yield_ore_base_amount then
base_amount = this.rocks_yield_ore_base_amount
end
if this.rocks_yield_ore_maximum_amount then
maximum_amount = this.rocks_yield_ore_maximum_amount
end
type_modifier = rock_yield[entity.name] or type_modifier
amount = base_amount + (distance_to_center * distance_modifier)
second_amount = math_floor((second_base_amount + (distance_to_center * distance_modifier)) / 3)
if amount > maximum_amount then
amount = maximum_amount
end
if second_amount > maximum_amount then
second_amount = maximum_amount
end
local m = (70 + math_random(0, 60)) * 0.01
amount = math_floor(amount * type_modifier * m * 0.7)
return amount, second_amount
end
function Public.entity_died_randomness(data)
local entity = data.entity
local surface = data.surface
local harvest
harvest = harvest_raffle_ores[math.random(1, size_of_ore_raffle)]
local position = {x = entity.position.x, y = entity.position.y}
surface.spill_item_stack(position, {name = harvest, count = math_random(1, 5)}, true)
create_particles(surface, 'shell-particle', position, 64, {x = entity.position.x, y = entity.position.y})
end
local function randomness(data)
local entity = data.entity
local player = data.player
local harvest
local harvest_amount
harvest = harvest_raffle_ores[math.random(1, size_of_ore_raffle)]
harvest_amount = get_amount(data)
local position = {x = entity.position.x, y = entity.position.y}
player.surface.create_entity(
{
name = 'flying-text',
position = position,
text = '+' .. harvest_amount .. ' [img=item/' .. harvest .. ']',
color = {r = 0, g = 127, b = 33}
}
)
if harvest_amount > max_spill then
player.surface.spill_item_stack(position, {name = harvest, count = max_spill}, true)
harvest_amount = harvest_amount - max_spill
local inserted_count = player.insert({name = harvest, count = harvest_amount})
harvest_amount = harvest_amount - inserted_count
if harvest_amount > 0 then
player.surface.spill_item_stack(position, {name = harvest, count = harvest_amount}, true)
end
else
player.surface.spill_item_stack(position, {name = harvest, count = harvest_amount}, true)
end
create_particles(player.surface, 'shell-particle', position, 64, {x = player.position.x, y = player.position.y})
end
function Public.on_player_mined_entity(event)
local entity = event.entity
if not entity.valid then
return
end
if not valid_rocks[entity.name] then
return
end
local player = game.players[event.player_index]
local this = WPT.get()
if not player then
return
end
event.buffer.clear()
local data = {
this = this,
entity = entity,
player = player
}
randomness(data)
end
return Public

View File

@ -0,0 +1,661 @@
local Event = require 'utils.event'
local play_time = require 'utils.session_data'
local Tabs = require 'comfy_panel.main'
local RPG = require 'maps.mountain_fortress_v3.rpg'
local symbol_asc = ''
local symbol_desc = ''
local pokemessages = {
'a stick',
'a leaf',
'a moldy carrot',
'a crispy slice of bacon',
'a french fry',
'a realistic toygun',
'a broomstick',
'a thirteen inch iron stick',
'a mechanical keyboard',
'a fly fishing cane',
'a selfie stick',
'an oversized fidget spinner',
'a thumb extender',
'a dirty straw',
'a green bean',
'a banana',
'an umbrella',
"grandpa's walking stick",
'live firework',
'a toilet brush',
'a fake hand',
'an undercooked hotdog',
"a slice of yesterday's microwaved pizza",
'bubblegum',
'a biter leg',
"grandma's toothbrush",
'charred octopus',
'a dollhouse bathtub',
'a length of copper wire',
'a decommissioned nuke',
'a smelly trout',
'an unopened can of deodorant',
'a stone brick',
'a half full barrel of lube',
'a half empty barrel of lube',
'an unexploded cannon shell',
'a blasting programmable speaker',
'a not so straight rail',
'a mismatched pipe to ground',
'a surplus box of landmines',
'decommissioned yellow rounds',
'an oily pumpjack shaft',
'a melted plastic bar in the shape of the virgin mary',
'a bottle of watermelon vitamin water',
'a slice of watermelon',
'a stegosaurus tibia',
"a basking musician's clarinet",
'a twig',
'an undisclosed pokey item',
'a childhood trophy everyone else got',
'a dead starfish',
'a titanium toothpick',
'a nail file',
'a stamp collection',
'a bucket of lego',
'a rolled up carpet',
'a rolled up WELCOME doormat',
"Bobby's favorite bone",
'an empty bottle of cheap vodka',
'a tattooing needle',
'a peeled cucumber',
'a stack of cotton candy',
'a signed baseball bat',
'that 5 dollar bill grandma sent for christmas',
'a stack of overdue phone bills',
"the 'relax' section of the white pages",
'a bag of gym clothes which never made it to the washing machine',
'a handful of peanut butter',
"a pheasant's feather",
'a rusty pickaxe',
'a diamond sword',
'the bill of rights of a banana republic',
"one of those giant airport Toblerone's",
'a long handed inserter',
'a wiimote',
'an easter chocolate rabbit',
'a ball of yarn the cat threw up',
'a slightly expired but perfectly edible cheese sandwich',
'conclusive proof of lizard people existence',
'a pen drive full of high res wallpapers',
'a pet hamster',
'an oversized goldfish',
'a one foot extension cord',
"a CD from Walmart's 1 dollar bucket",
'a magic wand',
'a list of disappointed people who believed in you',
'murder exhibit no. 3',
"a paperback copy of 'Great Expectations'",
'a baby biter',
'a little biter fang',
'the latest diet fad',
'a belt that no longer fits you',
'an abandoned pet rock',
'a lava lamp',
'some spirit herbs',
'a box of fish sticks found at the back of the freezer',
'a bowl of tofu rice',
'a bowl of ramen noodles',
'a live lobster!',
'a miniature golf cart',
'dunce cap',
'a fully furnished x-mas tree',
'an orphaned power pole',
'an horphaned power pole',
'an box of overpriced girl scout cookies',
'the cheapest item from the yard sale',
'a Sharpie',
'a glowstick',
'a thick unibrow hair',
'a very detailed map of Kazakhstan',
'the official Factorio installation DVD',
'a Liberal Arts degree',
'a pitcher of Kool-Aid',
'a 1/4 pound vegan burrito',
'a bottle of expensive wine',
'a hamster sized gravestone',
'a counterfeit Cuban cigar',
'an old Nokia phone',
'a huge inferiority complex',
'a dead real state agent',
'a deck of tarot cards',
'unreleased Wikileaks documents',
'a mean-looking garden dwarf',
'the actual mythological OBESE cat',
'a telescope used to spy on the MILF next door',
'a fancy candelabra',
'the comic version of the Kama Sutra',
"an inflatable 'Netflix & chill' doll",
'whatever it is redlabel gets high on',
"Obama's birth certificate",
'a deck of Cards Against Humanity',
'a copy of META MEME HUMOR for Dummies',
'an abandoned, not-so-young-anymore puppy',
'one of those useless items advertised on TV',
'a genetic blueprint of a Japanese teen idol'
}
local function get_formatted_playtime(x)
if x < 5184000 then
local y = x / 216000
y = tostring(y)
local h = ''
for i = 1, 10, 1 do
local z = string.sub(y, i, i)
if z == '.' then
break
else
h = h .. z
end
end
local m = x % 216000
m = m / 3600
m = math.floor(m)
m = tostring(m)
if h == '0' then
local str = m .. ' minutes'
return str
else
local str = h .. ' hours '
str = str .. m
str = str .. ' minutes'
return str
end
else
local y = x / 5184000
y = tostring(y)
local h = ''
for i = 1, 10, 1 do
local z = string.sub(y, i, i)
if z == '.' then
break
else
h = h .. z
end
end
local m = x % 5184000
m = m / 216000
m = math.floor(m)
m = tostring(m)
if h == '0' then
local str = m .. ' days'
return str
else
local str = h .. ' days '
str = str .. m
str = str .. ' hours'
return str
end
end
end
local function get_rank(player)
local play_table = play_time.get_session_table()
local t = 0
if play_table then
if play_table[player.name] then
t = play_table[player.name]
end
end
local m = (player.online_time + t) / 3600
local ranks = {
'item/burner-mining-drill',
'item/burner-inserter',
'item/stone-furnace',
'item/light-armor',
'item/steam-engine',
'item/inserter',
'item/transport-belt',
'item/underground-belt',
'item/splitter',
'item/assembling-machine-1',
'item/long-handed-inserter',
'item/electronic-circuit',
'item/electric-mining-drill',
'item/dummy-steel-axe',
'item/heavy-armor',
'item/steel-furnace',
'item/gun-turret',
'item/fast-transport-belt',
'item/fast-underground-belt',
'item/fast-splitter',
'item/assembling-machine-2',
'item/fast-inserter',
'item/radar',
'item/filter-inserter',
'item/defender-capsule',
'item/pumpjack',
'item/chemical-plant',
'item/solar-panel',
'item/advanced-circuit',
'item/modular-armor',
'item/accumulator',
'item/construction-robot',
'item/distractor-capsule',
'item/stack-inserter',
'item/electric-furnace',
'item/express-transport-belt',
'item/express-underground-belt',
'item/express-splitter',
'item/assembling-machine-3',
'item/processing-unit',
'item/power-armor',
'item/logistic-robot',
'item/laser-turret',
'item/stack-filter-inserter',
'item/destroyer-capsule',
'item/power-armor-mk2',
'item/flamethrower-turret',
'item/beacon',
'item/steam-turbine',
'item/centrifuge',
'item/nuclear-reactor'
}
--52 ranks
local time_needed = 240 -- in minutes between rank upgrades
m = m / time_needed
m = math.floor(m)
m = m + 1
if m > #ranks then
m = #ranks
end
return ranks[m]
end
local comparators = {
['pokes_asc'] = function(a, b)
return a.pokes > b.pokes
end,
['pokes_desc'] = function(a, b)
return a.pokes < b.pokes
end,
['total_time_played_asc'] = function(a, b)
return a.total_played_ticks < b.total_played_ticks
end,
['total_time_played_desc'] = function(a, b)
return a.total_played_ticks > b.total_played_ticks
end,
['time_played_asc'] = function(a, b)
return a.played_ticks < b.played_ticks
end,
['time_played_desc'] = function(a, b)
return a.played_ticks > b.played_ticks
end,
['rpg_asc'] = function(a, b)
return a.rpg_level < b.rpg_level
end,
['rpg_desc'] = function(a, b)
return a.rpg_level > b.rpg_level
end,
['name_asc'] = function(a, b)
return a.name:lower() < b.name:lower()
end,
['name_desc'] = function(a, b)
return a.name:lower() > b.name:lower()
end
}
local function get_comparator(sort_by)
return comparators[sort_by]
end
local function get_sorted_list(sort_by)
local play_table = play_time.get_session_table()
local rpg_t = RPG.get_table()
local player_list = {}
for i, player in pairs(game.connected_players) do
player_list[i] = {}
player_list[i].rank = get_rank(player)
player_list[i].name = player.name
local t = 0
if play_table[player.name] then
t = play_table[player.name]
end
player_list[i].rpg_level = rpg_t[player.index].level
player_list[i].total_played_time = get_formatted_playtime(t + player.online_time)
player_list[i].total_played_ticks = t + player.online_time
player_list[i].played_time = get_formatted_playtime(player.online_time)
player_list[i].played_ticks = player.online_time
player_list[i].pokes = global.player_list.pokes[player.index]
player_list[i].player_index = player.index
end
local comparator = get_comparator(sort_by)
table.sort(player_list, comparator)
return player_list
end
local function player_list_show(player, frame, sort_by)
local label
-- Frame management
frame.clear()
frame.style.padding = 8
-- Header management
local t = frame.add {type = 'table', name = 'player_list_panel_header_table', column_count = 6}
local column_widths = {tonumber(60), tonumber(150), tonumber(125), tonumber(150), tonumber(150), tonumber(100)}
for _, w in ipairs(column_widths) do
label = t.add {type = 'label', caption = ''}
label.style.minimal_width = w
label.style.maximal_width = w
end
local headers = {
[1] = '[color=0.1,0.7,0.1]' .. -- green
tostring(#game.connected_players) .. '[/color]',
[2] = 'Online' ..
' / ' ..
'[color=0.7,0.1,0.1]' .. -- red
tostring(#game.players - #game.connected_players) .. '[/color]' .. ' Offline',
[3] = 'RPG level',
[4] = 'Total Time',
[5] = 'Current Time',
[6] = 'Poke'
}
local header_modifier = {
['name_asc'] = function(h)
h[2] = symbol_asc .. h[2]
end,
['name_desc'] = function(h)
h[2] = symbol_desc .. h[2]
end,
['rpg_asc'] = function(h)
h[3] = symbol_asc .. h[3]
end,
['rpg_desc'] = function(h)
h[3] = symbol_desc .. h[3]
end,
['total_time_played_asc'] = function(h)
h[4] = symbol_asc .. h[4]
end,
['total_time_played_desc'] = function(h)
h[4] = symbol_desc .. h[4]
end,
['time_played_asc'] = function(h)
h[5] = symbol_asc .. h[5]
end,
['time_played_desc'] = function(h)
h[5] = symbol_desc .. h[5]
end,
['pokes_asc'] = function(h)
h[6] = symbol_asc .. h[6]
end,
['pokes_desc'] = function(h)
h[6] = symbol_desc .. h[6]
end
}
if sort_by then
global.player_list.sorting_method[player.index] = sort_by
else
sort_by = global.player_list.sorting_method[player.index]
end
header_modifier[sort_by](headers)
for k, v in ipairs(headers) do
label =
t.add {
type = 'label',
name = 'player_list_panel_header_' .. k,
caption = v
}
label.style.font = 'default-bold'
label.style.font_color = {r = 0.98, g = 0.66, b = 0.22}
end
-- special style on first header
label = t['player_list_panel_header_1']
label.style.minimal_width = 36
label.style.maximal_width = 36
label.style.horizontal_align = 'right'
-- List management
local player_list_panel_table =
frame.add {
type = 'scroll-pane',
name = 'scroll_pane',
direction = 'vertical',
horizontal_scroll_policy = 'never',
vertical_scroll_policy = 'auto'
}
player_list_panel_table.style.maximal_height = 530
player_list_panel_table =
player_list_panel_table.add {type = 'table', name = 'player_list_panel_table', column_count = 6}
local player_list = get_sorted_list(sort_by)
for i = 1, #player_list, 1 do
-- Icon
local sprite =
player_list_panel_table.add {
type = 'sprite',
name = 'player_rank_sprite_' .. i,
sprite = player_list[i].rank
}
sprite.style.minimal_width = column_widths[1]
sprite.style.maximal_width = column_widths[1]
-- Name
label =
player_list_panel_table.add {
type = 'label',
name = 'player_list_panel_player_names_' .. i,
caption = player_list[i].name
}
label.style.font = 'default'
label.style.font_color = {
r = .4 + game.players[player_list[i].player_index].color.r * 0.6,
g = .4 + game.players[player_list[i].player_index].color.g * 0.6,
b = .4 + game.players[player_list[i].player_index].color.b * 0.6
}
label.style.minimal_width = column_widths[2]
label.style.maximal_width = column_widths[2]
-- RPG level
label =
player_list_panel_table.add {
type = 'label',
name = 'player_list_panel_RPG_level_' .. i,
caption = player_list[i].rpg_level
}
label.style.minimal_width = column_widths[3]
label.style.maximal_width = column_widths[3]
-- Total time
label =
player_list_panel_table.add {
type = 'label',
name = 'player_list_panel_player_total_time_played_' .. i,
caption = player_list[i].total_played_time
}
label.style.minimal_width = column_widths[4]
label.style.maximal_width = column_widths[4]
-- Current time
label =
player_list_panel_table.add {
type = 'label',
name = 'player_list_panel_player_time_played_' .. i,
caption = player_list[i].played_time
}
label.style.minimal_width = column_widths[5]
label.style.maximal_width = column_widths[5]
-- Poke
local flow = player_list_panel_table.add {type = 'flow', name = 'button_flow_' .. i, direction = 'horizontal'}
flow.add {type = 'label', name = 'button_spacer_' .. i, caption = ''}
local button =
flow.add {type = 'button', name = 'poke_player_' .. player_list[i].name, caption = player_list[i].pokes}
button.style.font = 'default'
label.style.font_color = {r = 0.83, g = 0.83, b = 0.83}
button.style.minimal_height = 30
button.style.minimal_width = 30
button.style.maximal_height = 30
button.style.maximal_width = 30
button.style.top_padding = 0
button.style.left_padding = 0
button.style.right_padding = 0
button.style.bottom_padding = 0
end
end
local function on_gui_click(event)
if not event then
return
end
if not event.element then
return
end
if not event.element.valid then
return
end
if not event.element.name then
return
end
local player = game.players[event.element.player_index]
local frame = Tabs.comfy_panel_get_active_frame(player)
if not frame then
return
end
if frame.name ~= 'Players' then
return
end
local name = event.element.name
local actions = {
['player_list_panel_header_2'] = function()
if string.find(event.element.caption, symbol_desc) then
player_list_show(player, frame, 'name_asc')
else
player_list_show(player, frame, 'name_desc')
end
end,
['player_list_panel_header_3'] = function()
if string.find(event.element.caption, symbol_desc) then
player_list_show(player, frame, 'rpg_asc')
else
player_list_show(player, frame, 'rpg_desc')
end
end,
['player_list_panel_header_4'] = function()
if string.find(event.element.caption, symbol_desc) then
player_list_show(player, frame, 'total_time_played_asc')
else
player_list_show(player, frame, 'total_time_played_desc')
end
end,
['player_list_panel_header_5'] = function()
if string.find(event.element.caption, symbol_desc) then
player_list_show(player, frame, 'time_played_asc')
else
player_list_show(player, frame, 'time_played_desc')
end
end,
['player_list_panel_header_6'] = function()
if string.find(event.element.caption, symbol_desc) then
player_list_show(player, frame, 'pokes_asc')
else
player_list_show(player, frame, 'pokes_desc')
end
end
}
if actions[name] then
actions[name]()
return
end
if not event.element.valid then
return
end
--Poke other players
if string.sub(event.element.name, 1, 11) == 'poke_player' then
local poked_player = string.sub(event.element.name, 13, string.len(event.element.name))
if player.name == poked_player then
return
end
if global.player_list.last_poke_tick[event.element.player_index] + 300 < game.tick then
local str = '>> '
str = str .. player.name
str = str .. ' has poked '
str = str .. poked_player
str = str .. ' with '
local z = math.random(1, #pokemessages)
str = str .. pokemessages[z]
str = str .. ' <<'
game.print(str)
global.player_list.last_poke_tick[event.element.player_index] = game.tick
local p = game.players[poked_player]
global.player_list.pokes[p.index] = global.player_list.pokes[p.index] + 1
end
end
end
local function refresh()
for _, player in pairs(game.connected_players) do
local frame = Tabs.comfy_panel_get_active_frame(player)
if frame then
if frame.name ~= 'Players' then
return
end
player_list_show(player, frame, global.player_list.sorting_method[player.index])
end
end
end
local function on_player_joined_game(event)
if not global.player_list.last_poke_tick[event.player_index] then
global.player_list.pokes[event.player_index] = 0
global.player_list.last_poke_tick[event.player_index] = 0
global.player_list.sorting_method[event.player_index] = 'total_time_played_desc'
end
refresh()
end
local function on_player_left_game()
refresh()
end
local on_init = function()
global.player_list = {}
global.player_list.last_poke_tick = {}
global.player_list.pokes = {}
global.player_list.sorting_method = {}
end
comfy_panel_tabs['Players'] = {gui = player_list_show, admin = false}
Event.on_init(on_init)
Event.add(defines.events.on_player_joined_game, on_player_joined_game)
Event.add(defines.events.on_player_left_game, on_player_left_game)
Event.add(defines.events.on_gui_click, on_gui_click)

View File

@ -0,0 +1,67 @@
local Event = require 'utils.event'
local WPT = require 'maps.mountain_fortress_v3.table'
local function balance(t)
local g = 0
local c = 0
for k, v in pairs(t) do
if (v.valid) then
g = g + v.energy
c = c + v.electric_buffer_size
end
end
for k, v in pairs(t) do
if (v.valid) then
local r = (v.electric_buffer_size / c)
v.energy = g * r
end
end
end
local function tick()
local this = WPT.get()
if not this.energy['mountain_fortress_v3'] then
this.energy['mountain_fortress_v3'] = this.ow_energy
end
if not this.energy['loco'] then
this.energy['loco'] = this.lo_energy
end
local mountain_fortress_v3 = this.energy['mountain_fortress_v3']
local loco = this.energy['loco']
if not mountain_fortress_v3 or not loco then
return
end
if not mountain_fortress_v3.valid or not loco.valid then
return
end
balance(this.energy)
end
local function built_entity(event)
local entity = event.created_entity
if not entity.valid then
return
end
local player = game.players[event.player_index]
local surface = entity.surface
local map_name = 'mountain_fortress_v3'
if surface.name ~= map_name then
return
end
if
entity.name == 'steam-engine' or entity.name == 'steam-turbine' or entity.name == 'lab' or
entity.name == 'rocket-silo'
then
if not entity.valid then
return
end
player.print('"' .. entity.name .. '" Does not seem to work down here, thats strange!', {r = 1, g = 0, b = 0})
entity.active = false
end
end
Event.add(defines.events.on_tick, tick)
--Event.add(defines.events.on_built_entity, built_entity)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
local Server = require 'utils.server'
local Modifers = require 'player_modifiers'
local WPT = require 'maps.mountain_fortress_v3.table'
local mapkeeper = '[color=blue]Mapkeeper:[/color]'
local Public = {}
local function reset_forces(new_surface, old_surface)
for _, f in pairs(game.forces) do
local spawn = {
x = game.forces.player.get_spawn_position(old_surface).x,
y = game.forces.player.get_spawn_position(old_surface).y
}
f.reset()
f.reset_evolution()
f.set_spawn_position(spawn, new_surface)
end
for _, tech in pairs(game.forces.player.technologies) do
tech.researched = false
game.forces.player.set_saved_technology_progress(tech, 0)
end
end
local function teleport_players(surface)
game.forces.player.set_spawn_position({-27, 25}, surface)
for _, player in pairs(game.connected_players) do
player.teleport(
surface.find_non_colliding_position('character', game.forces.player.get_spawn_position(surface), 3, 0, 5),
surface
)
end
end
local function equip_players(player_starting_items)
for k, player in pairs(game.connected_players) do
if player.character then
player.character.destroy()
end
player.character = nil
player.set_controller({type = defines.controllers.god})
player.create_character()
for item, amount in pairs(player_starting_items) do
player.insert({name = item, count = amount})
end
Modifers.update_player_modifiers(player)
end
end
function Public.soft_reset_map(old_surface, map_gen_settings, player_starting_items)
local this = WPT.get()
if not this.soft_reset_counter then
this.soft_reset_counter = 0
end
if not this.original_surface_name then
this.original_surface_name = old_surface.name
end
this.soft_reset_counter = this.soft_reset_counter + 1
local new_surface =
game.create_surface(this.original_surface_name .. '_' .. tostring(this.soft_reset_counter), map_gen_settings)
new_surface.request_to_generate_chunks({0, 0}, 0.5)
new_surface.force_generate_chunk_requests()
reset_forces(new_surface, old_surface)
teleport_players(new_surface)
equip_players(player_starting_items)
game.delete_surface(old_surface)
local message = table.concat({mapkeeper .. ' Welcome to ', this.original_surface_name, '!'})
local message_to_discord = table.concat({'** Welcome to ', this.original_surface_name, '! **'})
if this.soft_reset_counter > 1 then
message =
table.concat(
{
mapkeeper,
' The world has been reshaped, welcome to ',
this.original_surface_name,
' number ',
tostring(this.soft_reset_counter),
'!'
}
)
end
game.print(message, {r = 0.98, g = 0.66, b = 0.22})
Server.to_discord_embed(message_to_discord)
return new_surface
end
return Public

View File

@ -0,0 +1,89 @@
require 'util'
local Global = require 'utils.global'
local Event = require 'utils.event'
local surface_name = 'mountain_fortress_v3'
local level_width = require 'maps.mountain_fortress_v3.terrain'.level_width
local Reset = require 'maps.mountain_fortress_v3.soft_reset'
local Public = {}
local this = {
active_surface_index = nil,
surface_name = surface_name
}
Global.register(
this,
function(tbl)
this = tbl
end
)
local starting_items = {['pistol'] = 1, ['firearm-magazine'] = 16, ['rail'] = 16, ['wood'] = 16, ['explosives'] = 32}
local function on_init()
local mgs = game.surfaces['nauvis'].map_gen_settings
mgs.width = 16
mgs.height = 16
game.surfaces['nauvis'].map_gen_settings = mgs
game.surfaces['nauvis'].clear()
Public.create_surface()
end
function Public.create_surface()
local map_gen_settings = {
['seed'] = math.random(10000, 99999),
['width'] = level_width,
['water'] = 0.001,
['starting_area'] = 1,
['cliff_settings'] = {cliff_elevation_interval = 0, cliff_elevation_0 = 0},
['default_enable_all_autoplace_controls'] = true,
['autoplace_settings'] = {
['entity'] = {treat_missing_as_default = false},
['tile'] = {treat_missing_as_default = true},
['decorative'] = {treat_missing_as_default = true}
}
}
local mine = {}
mine['control-setting:moisture:bias'] = 0.33
mine['control-setting:moisture:frequency:multiplier'] = 1
map_gen_settings.property_expression_names = mine
if not this.active_surface_index then
this.active_surface_index = game.create_surface(surface_name, map_gen_settings).index
game.forces.player.set_spawn_position({-27, 25}, game.surfaces[this.active_surface_index])
else
game.forces.player.set_spawn_position({-27, 25}, game.surfaces[this.active_surface_index])
this.active_surface_index =
Reset.soft_reset_map(game.surfaces[this.active_surface_index], map_gen_settings, starting_items).index
end
local surface = game.surfaces[this.active_surface_index]
surface.request_to_generate_chunks({-17, 45}, 1)
surface.force_generate_chunk_requests()
return this.active_surface_index
end
function Public.get_active_surface()
return this.active_surface
end
function Public.get_surface_name()
return this.surface_name
end
function Public.get(key)
if key then
return this[key]
else
return this
end
end
Event.on_init(on_init)
return Public

View File

@ -0,0 +1,58 @@
-- on table to rule them all!
local Global = require 'utils.global'
local Event = require 'utils.event'
local this = {
disable_reset = false,
players = {},
offline_players = {}
}
local Public = {}
Global.register(
this,
function(tbl)
this = tbl
end
)
function Public.reset_table()
this.locomotive_index = nil
this.loco_surface = nil
this.game_lost = false
this.locomotive_health = 10000
this.locomotive_max_health = 10000
this.cargo_health = 10000
this.cargo_max_health = 10000
this.train_upgrades = 0
this.offline_players = {}
this.biter_pets = {}
this.mined_scrap = 0
this.biters_killed = 0
this.locomotive_xp_aura = 40
this.xp_points = 0
this.xp_points_upgrade = 0
this.aura_upgrades = 0
this.health_upgrades = 0
this.threat_upgrades = 0
this.left_top = {
x = 0,
y = 0
}
end
function Public.get(key)
if key then
return this[key]
else
return this
end
end
local on_init = function()
Public.reset_table()
end
Event.on_init(on_init)
return Public

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,114 @@
local tick_tacks = {'*tick*', '*tick*', '*tack*', '*tak*', '*tik*', '*tok*'}
local kaboom_weights = {
{name = 'grenade', chance = 7},
{name = 'cluster-grenade', chance = 1},
{name = 'destroyer-capsule', chance = 1},
{name = 'defender-capsule', chance = 4},
{name = 'distractor-capsule', chance = 2},
{name = 'poison-capsule', chance = 2},
{name = 'explosive-uranium-cannon-projectile', chance = 3},
{name = 'explosive-cannon-projectile', chance = 5}
}
local kabooms = {}
for _, t in pairs(kaboom_weights) do
for x = 1, t.chance, 1 do
table.insert(kabooms, t.name)
end
end
local function create_flying_text(surface, position, text)
if not surface.valid then
return
end
surface.create_entity(
{
name = 'flying-text',
position = position,
text = text,
color = {r = 0.75, g = 0.75, b = 0.75}
}
)
surface.play_sound({path = 'utility/armor_insert', position = position, volume_modifier = 0.75})
end
local function create_kaboom(surface, position, name)
if not surface.valid then
return
end
local target = position
local speed = 0.5
if name == 'defender-capsule' or name == 'destroyer-capsule' or name == 'distractor-capsule' then
surface.create_entity(
{
name = 'flying-text',
position = position,
text = '(((Sentries Engaging Target)))',
color = {r = 0.8, g = 0.0, b = 0.0}
}
)
local nearest_player_unit =
surface.find_nearest_enemy({position = position, max_distance = 128, force = 'enemy'})
if nearest_player_unit then
target = nearest_player_unit.position
end
speed = 0.001
end
surface.create_entity(
{
name = name,
position = position,
force = 'enemy',
target = target,
speed = speed
}
)
end
local function tick_tack_trap(surface, position)
if not surface then
return
end
if not surface.valid then
return
end
if not position then
return
end
if not position.x then
return
end
if not position.y then
return
end
local tick_tack_count = math.random(5, 9)
for t = 60, tick_tack_count * 60, 60 do
if not global.on_tick_schedule[game.tick + t] then
global.on_tick_schedule[game.tick + t] = {}
end
if t < tick_tack_count * 60 then
global.on_tick_schedule[game.tick + t][#global.on_tick_schedule[game.tick + t] + 1] = {
func = create_flying_text,
args = {surface, {x = position.x, y = position.y}, tick_tacks[math.random(1, #tick_tacks)]}
}
else
if math.random(1, 10) == 1 then
global.on_tick_schedule[game.tick + t][#global.on_tick_schedule[game.tick + t] + 1] = {
func = create_flying_text,
args = {surface, {x = position.x, y = position.y}, '( ͡° ͜ʖ ͡°)'}
}
else
global.on_tick_schedule[game.tick + t][#global.on_tick_schedule[game.tick + t] + 1] = {
func = create_kaboom,
args = {surface, {x = position.x, y = position.y}, kabooms[math.random(1, #kabooms)]}
}
end
end
end
end
return tick_tack_trap

View File

@ -29,6 +29,7 @@ local modifiers = {
'character_mining_speed_modifier',
'character_reach_distance_bonus',
'character_resource_reach_distance_bonus',
'character_maximum_following_robot_count_bonus',
'character_running_speed_modifier'
}

View File

@ -155,15 +155,6 @@ function Task.start_queue()
end
end
function Task.reset_queue()
if #task_queue > 50 then
task_queue = {
_head = 1,
_tail = 0
}
end
end
function Task.get_task_queue()
return task_queue
end