local Token = require 'utils.token' local Task = require 'utils.task' local Color = require 'utils.color_presets' local ICW = require 'maps.mountain_fortress_v3.icw.main' local Event = require 'utils.event' local Global = require 'utils.global' local Alert = require 'utils.alert' local WPT = require 'maps.mountain_fortress_v3.table' local WD = require 'modules.wave_defense.table' local Collapse = require 'modules.collapse' local Difficulty = require 'modules.difficulty_vote_by_amount' local ICW_Func = require 'maps.mountain_fortress_v3.icw.functions' local math2d = require 'math2d' local Misc = require 'commands.misc' local this = { power_sources = {index = 1}, refill_turrets = {index = 1}, magic_crafters = {index = 1}, magic_fluid_crafters = {index = 1}, art_table = {index = 1}, surface_cleared = false } local starting_items = { ['pistol'] = 1, ['firearm-magazine'] = 16, ['rail'] = 16, ['wood'] = 16, ['explosives'] = 32 } local random_respawn_messages = { 'The doctors stitched you up as best they could.', 'Ow! Your right leg hurts.', 'Ow! Your left leg hurts.', 'You can feel your whole body aching.', "You still have some bullet wounds that aren't patched up.", 'You feel dizzy but adrenalin is granting you speed.', 'Adrenalin is kicking in, but your body is damaged.' } local health_values = { '0.35', '0.40', '0.45', '0.50', '0.55', '0.60', '0.65', '0.70', '0.75', '0.80', '0.85', '0.90', '0.95', '1' } Global.register( this, function(t) this = t end ) local Public = {} local random = math.random local floor = math.floor local round = math.round local remove = table.remove local sqrt = math.sqrt local magic_crafters_per_tick = 3 local magic_fluid_crafters_per_tick = 8 local tile_damage = 50 local artillery_target_entities = { 'character', 'tank', 'car', 'radar', 'lab', 'furnace', 'locomotive', 'cargo-wagon', 'fluid-wagon', 'artillery-wagon', 'artillery-turret', 'laser-turret', 'gun-turret', 'flamethrower-turret', 'silo', 'spidertron' } local function debug_str(msg) local debug = WPT.get('debug') if not debug then return end print('Mtn: ' .. msg) end local function show_text(msg, pos, color, surface) if color == nil then surface.create_entity({name = 'flying-text', position = pos, text = msg}) else surface.create_entity({name = 'flying-text', position = pos, text = msg, color = color}) end end local function fast_remove(tbl, index) local count = #tbl if index > count then return elseif index < count then tbl[index] = tbl[count] end tbl[count] = nil end local function do_refill_turrets() local refill_turrets = this.refill_turrets local index = refill_turrets.index if index > #refill_turrets then refill_turrets.index = 1 return end local turret_data = refill_turrets[index] local turret = turret_data.turret if not turret.valid then fast_remove(refill_turrets, index) return end refill_turrets.index = index + 1 local data = turret_data.data if data.liquid then turret.fluidbox[1] = data elseif data then turret.insert(data) end end --[[ local function do_turret_energy() local power_sources = this.power_sources for index = 1, #power_sources do local ps_data = power_sources[index] if not (ps_data and ps_data.valid) then fast_remove(power_sources, index) return end ps_data.energy = 0xfffff end end ]] local function do_magic_crafters() local magic_crafters = this.magic_crafters local limit = #magic_crafters if limit == 0 then return end local index = magic_crafters.index for i = 1, magic_crafters_per_tick do if index > limit then index = 1 end local data = magic_crafters[index] local entity = data.entity if not entity.valid then fast_remove(magic_crafters, index) limit = limit - 1 if limit == 0 then return end else index = index + 1 local tick = game.tick local last_tick = data.last_tick local rate = data.rate local count = (tick - last_tick) * rate local fcount = floor(count) if fcount > 1 then fcount = 1 end if fcount > 0 then if entity.get_output_inventory().can_insert({name = data.item, count = fcount}) then entity.get_output_inventory().insert {name = data.item, count = fcount} entity.products_finished = entity.products_finished + fcount data.last_tick = round(tick - (count - fcount) / rate) end end end end magic_crafters.index = index end local function do_magic_fluid_crafters() local magic_fluid_crafters = this.magic_fluid_crafters local limit = #magic_fluid_crafters if limit == 0 then return end local index = magic_fluid_crafters.index for i = 1, magic_fluid_crafters_per_tick do if index > limit then index = 1 end local data = magic_fluid_crafters[index] local entity = data.entity if not entity.valid then fast_remove(magic_fluid_crafters, index) limit = limit - 1 if limit == 0 then return end else index = index + 1 local tick = game.tick local last_tick = data.last_tick local rate = data.rate local count = (tick - last_tick) * rate local fcount = floor(count) if fcount > 0 then local fluidbox_index = data.fluidbox_index local fb = entity.fluidbox local fb_data = fb[fluidbox_index] or {name = data.item, amount = 0} fb_data.amount = fb_data.amount + fcount fb[fluidbox_index] = fb_data entity.products_finished = entity.products_finished + fcount data.last_tick = tick - (count - fcount) / rate end end end magic_fluid_crafters.index = index end local artillery_target_callback = Token.register( function(data) local position = data.position local entity = data.entity if not entity.valid then return end local tx, ty = position.x, position.y local pos = entity.position local x, y = pos.x, pos.y local dx, dy = tx - x, ty - y local d = dx * dx + dy * dy if d >= 1024 and d <= 441398 then -- 704 in depth~ if entity.name == 'character' then entity.surface.create_entity { name = 'artillery-projectile', position = position, target = entity, force = 'enemy', speed = 1.5 } elseif entity.name ~= 'character' then entity.surface.create_entity { name = 'rocket', position = position, target = entity, force = 'enemy', speed = 1.5 } end end end ) local function do_artillery_turrets_targets() local art_table = this.art_table local index = art_table.index if index > #art_table then art_table.index = 1 return end art_table.index = index + 1 local outpost = art_table[index] local now = game.tick if now - outpost.last_fire_tick < 480 then return end local turrets = outpost.artillery_turrets for i = #turrets, 1, -1 do local turret = turrets[i] if not turret.valid then fast_remove(turrets, i) end end local count = #turrets if count == 0 then fast_remove(art_table, index) return end outpost.last_fire_tick = now local turret = turrets[1] local area = outpost.artillery_area local surface = turret.surface local entities = surface.find_entities_filtered {area = area, name = artillery_target_entities, force = 'player'} if #entities == 0 then return end local position = turret.position for i = 1, count do local entity = entities[random(#entities)] if entity and entity.valid then local data = {position = position, entity = entity} Task.set_timeout_in_ticks(i * 60, artillery_target_callback, data) end end end local function add_magic_crafter_output(entity, output, distance) local magic_fluid_crafters = this.magic_fluid_crafters local magic_crafters = this.magic_crafters local rate = output.min_rate + output.distance_factor * distance local fluidbox_index = output.fluidbox_index local data = { entity = entity, last_tick = round(game.tick), base_rate = round(rate, 8), rate = round(rate, 8), item = output.item, fluidbox_index = fluidbox_index } if fluidbox_index then magic_fluid_crafters[#magic_fluid_crafters + 1] = data else magic_crafters[#magic_crafters + 1] = data end end local function tick() do_refill_turrets() do_magic_crafters() do_magic_fluid_crafters() do_artillery_turrets_targets() end Public.deactivate_callback = Token.register( function(entity) if entity and entity.valid then entity.active = false entity.operable = false entity.destructible = false end end ) Public.neutral_force = Token.register( function(entity) if entity and entity.valid then entity.force = 'neutral' end end ) Public.enemy_force = Token.register( function(entity) if entity and entity.valid then entity.force = 'enemy' end end ) Public.active_not_destructible_callback = Token.register( function(entity) if entity and entity.valid then entity.active = true entity.operable = false entity.destructible = false end end ) Public.disable_minable_callback = Token.register( function(entity) if entity and entity.valid then entity.minable = false end end ) Public.disable_minable_and_ICW_callback = Token.register( function(entity) if entity and entity.valid then entity.minable = false ICW.register_wagon(entity, true) end end ) Public.disable_destructible_callback = Token.register( function(entity) if entity and entity.valid then entity.destructible = false entity.minable = false end end ) Public.disable_active_callback = Token.register( function(entity) if entity and entity.valid then entity.active = false end end ) local disable_active_callback = Public.disable_active_callback Public.refill_turret_callback = Token.register( function(turret, data) local refill_turrets = this.refill_turrets local callback_data = data.callback_data turret.direction = 3 refill_turrets[#refill_turrets + 1] = {turret = turret, data = callback_data} end ) Public.refill_artillery_turret_callback = Token.register( function(turret, data) local refill_turrets = this.refill_turrets local art_table = this.art_table local index = art_table.index turret.active = false turret.direction = 3 refill_turrets[#refill_turrets + 1] = {turret = turret, data = data.callback_data} local artillery_data = art_table[index] if not artillery_data then artillery_data = {} end local artillery_turrets = artillery_data.artillery_turrets if not artillery_turrets then artillery_turrets = {} artillery_data.artillery_turrets = artillery_turrets local pos = turret.position local x, y = pos.x, pos.y artillery_data.artillery_area = {{x - 112, y}, {x + 112, y + 212}} artillery_data.last_fire_tick = 0 art_table[#art_table + 1] = artillery_data end artillery_turrets[#artillery_turrets + 1] = turret end ) Public.refill_liquid_turret_callback = Token.register( function(turret, data) local refill_turrets = this.refill_turrets local callback_data = data.callback_data callback_data.liquid = true refill_turrets[#refill_turrets + 1] = {turret = turret, data = callback_data} end ) Public.power_source_callback = Token.register( function(turret) local power_sources = this.power_sources power_sources[#power_sources + 1] = turret end ) Public.magic_item_crafting_callback = Token.register( function(entity, data) local callback_data = data.callback_data if not (entity and entity.valid) then return end entity.minable = false entity.destructible = false entity.operable = false local force = game.forces.player local tech = callback_data.tech if tech then if not force.technologies[tech].researched then entity.destroy() return end end local recipe = callback_data.recipe if recipe then entity.set_recipe(recipe) else local furance_item = callback_data.furance_item if furance_item then local inv = entity.get_inventory(defines.inventory.furnace_result) inv.insert(furance_item) end end local p = entity.position local x, y = p.x, p.y local distance = sqrt(x * x + y * y) local output = callback_data.output if #output == 0 then add_magic_crafter_output(entity, output, distance) else for i = 1, #output do local o = output[i] add_magic_crafter_output(entity, o, distance) end end if not callback_data.keep_active then Task.set_timeout_in_ticks(2, disable_active_callback, entity) -- causes problems with refineries. end end ) Public.magic_item_crafting_callback_weighted = Token.register( function(entity, data) local callback_data = data.callback_data if not (entity and entity.valid) then return end entity.minable = false entity.destructible = false entity.operable = false local weights = callback_data.weights local loot = callback_data.loot local p = entity.position local i = random() * weights.total local index = table.binary_search(weights, i) if (index < 0) then index = bit32.bnot(index) end local stack = loot[index].stack if not stack then return end local force = game.forces.player local tech = stack.tech if tech then if force.technologies[tech] then if not force.technologies[tech].researched then entity.destroy() return end end end local recipe = stack.recipe if recipe then entity.set_recipe(recipe) else local furance_item = stack.furance_item if furance_item then local inv = entity.get_inventory(defines.inventory.furnace_result) inv.insert(furance_item) end end local x, y = p.x, p.y local distance = sqrt(x * x + y * y) local output = stack.output if #output == 0 then add_magic_crafter_output(entity, output, distance) else for o_i = 1, #output do local o = output[o_i] add_magic_crafter_output(entity, o, distance) end end if not callback_data.keep_active then Task.set_timeout_in_ticks(2, disable_active_callback, entity) -- causes problems with refineries. end end ) function Public.prepare_weighted_loot(loot) local total = 0 local weights = {} for i = 1, #loot do local v = loot[i] total = total + v.weight weights[#weights + 1] = total end weights.total = total return weights end function Public.do_random_loot(entity, weights, loot) if not entity.valid then return end entity.operable = false --entity.destructible = false local i = random() * weights.total local index = table.binary_search(weights, i) if (index < 0) then index = bit32.bnot(index) end local stack = loot[index].stack if not stack then return end local df = stack.distance_factor local count if df then local p = entity.position local x, y = p.x, p.y local d = sqrt(x * x + y * y) count = stack.count + d * df else count = stack.count end entity.insert {name = stack.name, count = count} end function Public.remove_offline_players() local offline_players_enabled = WPT.get('offline_players_enabled') if not offline_players_enabled then return end local offline_players = WPT.get('offline_players') local active_surface_index = WPT.get('active_surface_index') local surface = game.surfaces[active_surface_index] local player_inv = {} local items = {} if #offline_players > 0 then 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 offline_players[i] = nil else if offline_players[i] and game.players[offline_players[i].index] and offline_players[i].tick < game.tick - 108000 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) if not next(player_inv) then offline_players[i] = nil break end 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) e.character_inventory_slots_bonus = #player_inv[1] 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 local message = ({'main.cleaner', name}) local data = { position = pos } Alert.alert_all_players_location(data, message) 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 offline_players[i] = nil break end end end end end local function calc_players() local players = game.connected_players local check_afk_players = WPT.get('check_afk_players') if not check_afk_players then return #players end local total = 0 for i = 1, #players do local player = players[i] if player.afk_time < 36000 then total = total + 1 end end if total <= 0 then total = #players end return total end local retry_final_boost_movement_speed_on_respawn = Token.register( function(data) local player = data.player local old_speed = data.old_speed if not player or not player.valid then return end if not player.character or not player.character.valid then return end player.character.character_running_speed_modifier = old_speed player.print('Movement speed bonus removed!', Color.info) end ) local retry_boost_movement_speed_on_respawn = Token.register( function(data) local player = data.player local old_speed = data.old_speed if not player or not player.valid then return end if not player.character or not player.character.valid then Task.set_timeout_in_ticks(10, retry_final_boost_movement_speed_on_respawn, {player = player, old_speed = old_speed}) return end player.character.character_running_speed_modifier = old_speed player.print('Movement speed bonus removed!', Color.info) end ) local remove_boost_movement_speed_on_respawn = Token.register( function(data) local player = data.player local old_speed = data.old_speed if not player or not player.valid then return end if not player.character or not player.character.valid then Task.set_timeout_in_ticks(10, retry_boost_movement_speed_on_respawn, {player = player, old_speed = old_speed}) return end player.character.character_running_speed_modifier = old_speed player.print('Movement speed bonus removed!', Color.info) end ) local boost_movement_speed_on_respawn = Token.register( function(data) local player = data.player if not player or not player.valid then return end if not player.character or not player.character.valid then return end local old_speed = player.character_running_speed_modifier local new_speed = player.character_running_speed_modifier + 1 Task.set_timeout_in_ticks(800, remove_boost_movement_speed_on_respawn, {player = player, old_speed = old_speed}) player.character.character_running_speed_modifier = new_speed player.print('Movement speed bonus applied! Be quick and fetch your corpse!', Color.info) end ) function Public.set_difficulty() local game_lost = WPT.get('game_lost') if game_lost then return end local Diff = Difficulty.get() local wave_defense_table = WD.get_table() local check_if_threat_below_zero = WPT.get('check_if_threat_below_zero') local collapse_amount = WPT.get('collapse_amount') local collapse_speed = WPT.get('collapse_speed') local difficulty = WPT.get('difficulty') local mining_bonus_till_wave = WPT.get('mining_bonus_till_wave') local mining_bonus = WPT.get('mining_bonus') local disable_mining_boost = WPT.get('disable_mining_boost') local wave_number = WD.get_wave() local player_count = calc_players() if not Diff.difficulty_vote_value then Diff.difficulty_vote_value = 0.1 end if Diff.name == "I'm too young to die" then wave_defense_table.max_active_biters = 768 + player_count * (90 * Diff.difficulty_vote_value) elseif Diff.name == 'Hurt me plenty' then wave_defense_table.max_active_biters = 845 + player_count * (90 * Diff.difficulty_vote_value) elseif Diff.name == 'Ultra-violence' then wave_defense_table.max_active_biters = 1000 + player_count * (90 * Diff.difficulty_vote_value) end if wave_defense_table.max_active_biters >= 4000 then wave_defense_table.max_active_biters = 4000 end -- threat gain / wave if Diff.name == "I'm too young to die" then wave_defense_table.threat_gain_multiplier = 1.2 + player_count * Diff.difficulty_vote_value * 0.1 elseif Diff.name == 'Hurt me plenty' then wave_defense_table.threat_gain_multiplier = 2 + player_count * Diff.difficulty_vote_value * 0.1 elseif Diff.name == 'Ultra-violence' then wave_defense_table.threat_gain_multiplier = 4 + player_count * Diff.difficulty_vote_value * 0.1 end -- local amount = player_count * 0.40 + 2 -- too high? local amount = player_count * difficulty.multiply + 2 amount = floor(amount) if amount < difficulty.lowest then amount = difficulty.lowest elseif amount > difficulty.highest then amount = difficulty.highest -- lowered from 20 to 10 end local threat_check = nil if check_if_threat_below_zero then threat_check = wave_defense_table.threat <= 0 end if Diff.name == "I'm too young to die" then wave_defense_table.wave_interval = 3600 - player_count * 60 if wave_defense_table.wave_interval < 2200 or threat_check then wave_defense_table.wave_interval = 2200 end elseif Diff.name == 'Hurt me plenty' then wave_defense_table.wave_interval = 2600 - player_count * 60 if wave_defense_table.wave_interval < 1900 or threat_check then wave_defense_table.wave_interval = 1900 end elseif Diff.name == 'Ultra-violence' then wave_defense_table.wave_interval = 1600 - player_count * 60 if wave_defense_table.wave_interval < 1600 or threat_check then wave_defense_table.wave_interval = 1600 end end if collapse_amount then Collapse.set_amount(collapse_amount) else Collapse.set_amount(amount) end if collapse_speed then Collapse.set_speed(collapse_speed) else if player_count >= 1 and player_count <= 8 then Collapse.set_speed(8) elseif player_count > 8 and player_count <= 20 then Collapse.set_speed(7) elseif player_count > 20 and player_count <= 35 then Collapse.set_speed(6) elseif player_count > 35 then Collapse.set_speed(5) end end if player_count >= 1 and not disable_mining_boost then local force = game.forces.player if wave_number < mining_bonus_till_wave then -- the mining speed of the players will increase drastically since RPG is also loaded. -- additional mining speed comes from steel axe research: 100%, and difficulty settings: too young to die 50%, hurt me plenty 25% force.manual_mining_speed_modifier = force.manual_mining_speed_modifier - mining_bonus if player_count <= 5 then mining_bonus = 3 -- set a static 300% bonus if there are <= 5 players. elseif player_count >= 6 and player_count <= 10 then mining_bonus = 1 -- set a static 100% bonus if there are <= 10 players. elseif player_count >= 11 then mining_bonus = 0 -- back to 0% with more than 11 players end force.manual_mining_speed_modifier = force.manual_mining_speed_modifier + mining_bonus WPT.set('mining_bonus', mining_bonus) -- Setting mining_bonus globally so it remembers how much to reduce else force.manual_mining_speed_modifier = force.manual_mining_speed_modifier - mining_bonus WPT.set('disable_mining_boost', true) end end end function Public.render_direction(surface) local counter = WPT.get('soft_reset_counter') local winter_mode = WPT.get('winter_mode') local text = 'Welcome to Mountain Fortress v3!' if winter_mode then text = 'Welcome to Wintery Mountain Fortress v3!' end if counter then rendering.draw_text { text = text .. '\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 = text, 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 = -WPT.level_width / 2 local x_max = WPT.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.boost_difficulty() local difficulty_set = WPT.get('difficulty_set') if difficulty_set then return end local breached_wall = WPT.get('breached_wall') local difficulty = Difficulty.get() local name = difficulty.difficulties[difficulty.difficulty_vote_index].name if game.tick < difficulty.difficulty_poll_closing_timeout and breached_wall <= 1 then return end Difficulty.get().name = name Difficulty.get().difficulty_poll_closing_timeout = game.tick Difficulty.get().button_tooltip = difficulty.tooltip[difficulty.difficulty_vote_index] Difficulty.difficulty_gui() local message = ({'main.diff_set', name}) local data = { position = WPT.get('locomotive').position } Alert.alert_all_players_location(data, message) local force = game.forces.player if name == "I'm too young to die" then force.manual_mining_speed_modifier = force.manual_mining_speed_modifier + 0.5 force.character_running_speed_modifier = 0.15 force.manual_crafting_speed_modifier = 0.15 WPT.set('coin_amount', 1) WPT.set('upgrades').flame_turret.limit = 12 WPT.set('upgrades').landmine.limit = 50 WPT.set('locomotive_health', 10000) WPT.set('locomotive_max_health', 10000) WPT.set('bonus_xp_on_join', 500) WD.set('next_wave', game.tick + 3600 * 15) WPT.set('spidertron_unlocked_at_zone', 10) WD.set_normal_unit_current_health(1.0) WD.set_normal_unit_current_per_wave(0.15) WD.set_boss_unit_current_health(2) WD.set_boss_unit_current_per_wave(1.5) WPT.set('difficulty_set', true) elseif name == 'Hurt me plenty' then force.manual_mining_speed_modifier = force.manual_mining_speed_modifier + 0.25 force.character_running_speed_modifier = 0.1 force.manual_crafting_speed_modifier = 0.1 WPT.set('coin_amount', 2) WPT.set('upgrades').flame_turret.limit = 10 WPT.set('upgrades').landmine.limit = 50 WPT.set('locomotive_health', 7000) WPT.set('locomotive_max_health', 7000) WPT.set('bonus_xp_on_join', 300) WD.set('next_wave', game.tick + 3600 * 8) WPT.set('spidertron_unlocked_at_zone', 8) WD.set_normal_unit_current_health(1.6) WD.set_normal_unit_current_per_wave(0.5) WD.set_boss_unit_current_health(3) WD.set_boss_unit_current_per_wave(5) WPT.set('difficulty_set', true) elseif name == 'Ultra-violence' then force.character_running_speed_modifier = 0 force.manual_crafting_speed_modifier = 0 WPT.set('coin_amount', 4) WPT.set('upgrades').flame_turret.limit = 3 WPT.set('upgrades').landmine.limit = 10 WPT.set('locomotive_health', 5000) WPT.set('locomotive_max_health', 5000) WPT.set('bonus_xp_on_join', 50) WD.set('next_wave', game.tick + 3600 * 5) WPT.set('spidertron_unlocked_at_zone', 6) WD.set_normal_unit_current_health(2) WD.set_normal_unit_current_per_wave(1) WD.set_boss_unit_current_health(4) WD.set_boss_unit_current_per_wave(10) WPT.set('difficulty_set', true) end end function Public.set_spawn_position() local collapse_pos = Collapse.get_position() local locomotive = WPT.get('locomotive') if not locomotive or not locomotive.valid then return end local l = locomotive.position local retries = 0 local function check_tile(surface, tile, tbl, inc) if not (surface and surface.valid) then return false end if not tile then return false end local get_tile = surface.get_tile(tile) if get_tile.valid and get_tile.name == 'out-of-map' then remove(tbl, inc - inc + 1) return true else return false end end ::retry:: local y_value_position = WPT.get('y_value_position') local locomotive_positions = WPT.get('locomotive_pos') local total_pos = #locomotive_positions.tbl local active_surface_index = WPT.get('active_surface_index') local surface = game.surfaces[active_surface_index] if not (surface and surface.valid) then return end local spawn_near_collapse = WPT.get('spawn_near_collapse') if spawn_near_collapse.active then local collapse_position = surface.find_non_colliding_position('rocket-silo', collapse_pos, 64, 2) if not collapse_position then collapse_position = surface.find_non_colliding_position('solar-panel', collapse_pos, 32, 2) end if not collapse_position then collapse_position = surface.find_non_colliding_position('steel-chest', collapse_pos, 32, 2) end local sizeof = locomotive_positions.tbl[total_pos - total_pos + 1] if not sizeof then goto continue end if check_tile(surface, sizeof, locomotive_positions.tbl, total_pos) then retries = retries + 1 if retries == 2 then goto continue end goto retry end local locomotive_position = surface.find_non_colliding_position('steel-chest', sizeof, 128, 1) local distance_from = floor(math2d.position.distance(locomotive_position, locomotive.position)) local l_y = l.y local t_y = locomotive_position.y local c_y = collapse_pos.y if total_pos > spawn_near_collapse.total_pos then if l_y - t_y <= spawn_near_collapse.compare then if locomotive_position then if check_tile(surface, sizeof, locomotive_positions.tbl, total_pos) then debug_str('total_pos was higher - found oom') retries = retries + 1 if retries == 2 then goto continue end goto retry end debug_str('total_pos was higher - spawning at locomotive_position') WD.set_spawn_position(locomotive_position) end elseif c_y - t_y <= spawn_near_collapse.compare_next then if distance_from >= spawn_near_collapse.distance_from then local success = check_tile(surface, locomotive_position, locomotive_positions.tbl, total_pos) if success then debug_str('distance_from was higher - found oom') return end debug_str('distance_from was higher - spawning at locomotive_position') WD.set_spawn_position({x = locomotive_position.x, y = collapse_pos.y - y_value_position}) else debug_str('distance_from was lower - spawning at locomotive_position') WD.set_spawn_position({x = locomotive_position.x, y = collapse_pos.y - y_value_position}) end else if collapse_position then debug_str('total_pos was higher - spawning at collapse_position') collapse_position = {x = collapse_position.x, y = collapse_position.y - y_value_position} WD.set_spawn_position(collapse_position) end end else if collapse_position then debug_str('total_pos was lower - spawning at collapse_position') collapse_position = {x = collapse_position.x, y = collapse_position.y - y_value_position} WD.set_spawn_position(collapse_position) end end end ::continue:: end function Public.on_player_joined_game(event) local active_surface_index = WPT.get('active_surface_index') local players = WPT.get('players') local player = game.players[event.player_index] local surface = game.surfaces[active_surface_index] Public.set_difficulty() ICW_Func.is_minimap_valid(player, surface) if player.online_time < 1 then if not players[player.index] then players[player.index] = {} end local message = ({'main.greeting', player.name}) Alert.alert_player(player, 15, message) for item, amount in pairs(starting_items) do player.insert({name = item, count = amount}) end end local top = player.gui.top if top['mod_gui_top_frame'] then top['mod_gui_top_frame'].destroy() end if player.surface.index ~= active_surface_index then player.teleport(surface.find_non_colliding_position('character', game.forces.player.get_spawn_position(surface), 3, 0, 5), surface) else local p = {x = player.position.x, y = player.position.y} local get_tile = surface.get_tile(p) if get_tile.valid and get_tile.name == 'out-of-map' then local pos = surface.find_non_colliding_position('character', game.forces.player.get_spawn_position(surface), 3, 0, 5) if pos then player.teleport(pos, surface) else pos = game.forces.player.get_spawn_position(surface) player.teleport(pos, surface) end end end local locomotive = WPT.get('locomotive') if not locomotive or not locomotive.valid then return end if player.position.y > locomotive.position.y then player.teleport(surface.find_non_colliding_position('character', game.forces.player.get_spawn_position(surface), 3, 0, 5), surface) end end function Public.on_player_left_game() Public.set_difficulty() end function Public.is_creativity_mode_on() local creative_enabled = Misc.get('creative_enabled') if creative_enabled then WD.set('next_wave', 1000) Collapse.start_now(true) Public.set_difficulty() end end function Public.disable_creative() local creative_enabled = Misc.get('creative_enabled') if creative_enabled then Misc.set('creative_enabled', false) end end function Public.on_pre_player_left_game(event) local offline_players_enabled = WPT.get('offline_players_enabled') if not offline_players_enabled then return end local offline_players = WPT.get('offline_players') local player = game.players[event.player_index] local ticker = game.tick if player.character then offline_players[#offline_players + 1] = { index = event.player_index, name = player.name, tick = ticker } end end function Public.on_player_respawned(event) local player = game.get_player(event.player_index) if not player or not player.valid then return end if player.character and player.character.valid then Task.set_timeout_in_ticks(15, boost_movement_speed_on_respawn, {player = player}) player.character.health = round(player.character.health * health_values[random(1, #health_values)]) player.print(random_respawn_messages[random(1, #random_respawn_messages)]) end end function Public.on_player_changed_position(event) local active_surface_index = WPT.get('active_surface_index') if not active_surface_index then return end 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[active_surface_index] local p = {x = player.position.x, y = player.position.y} local config_tile = WPT.get('void_or_tile') if config_tile == 'lab-dark-2' then local get_tile = surface.get_tile(p) if get_tile.valid and get_tile.name == 'lab-dark-2' then if random(1, 2) == 1 then if random(1, 2) == 1 then show_text('This path is not for players!', p, {r = 0.98, g = 0.66, b = 0.22}, surface) end player.surface.create_entity({name = 'fire-flame', position = player.position}) player.character.health = player.character.health - tile_damage if player.character.health == 0 then player.character.die() local message = ({'main.death_message_' .. random(1, 7), player.name}) game.print(message, {r = 0.98, g = 0.66, b = 0.22}) end end end end if position.y >= 74 then player.teleport({position.x, position.y - 1}, surface) player.print(({'main.forcefield'}), {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 disable_recipes = function(force) force.recipes['cargo-wagon'].enabled = false force.recipes['fluid-wagon'].enabled = false force.recipes['car'].enabled = false force.recipes['tank'].enabled = false force.recipes['artillery-wagon'].enabled = false force.recipes['artillery-turret'].enabled = false force.recipes['artillery-shell'].enabled = false force.recipes['artillery-targeting-remote'].enabled = false force.recipes['locomotive'].enabled = false force.recipes['pistol'].enabled = false force.recipes['spidertron-remote'].enabled = false force.recipes['discharge-defense-equipment'].enabled = false force.recipes['discharge-defense-remote'].enabled = false end function Public.disable_tech() local force = game.forces.player force.technologies['landfill'].enabled = false force.technologies['spidertron'].enabled = false force.technologies['spidertron'].researched = false force.technologies['atomic-bomb'].enabled = false force.technologies['atomic-bomb'].researched = false force.technologies['artillery-shell-range-1'].enabled = false force.technologies['artillery-shell-range-1'].researched = false force.technologies['artillery-shell-speed-1'].enabled = false force.technologies['artillery-shell-speed-1'].researched = false force.technologies['optics'].researched = true force.technologies['railway'].researched = true force.technologies['land-mine'].enabled = false force.technologies['fluid-wagon'].enabled = false force.technologies['cliff-explosives'].enabled = false disable_recipes(force) end local disable_tech = Public.disable_tech function Public.on_research_finished(event) disable_tech() local research = event.research local bonus_drill = game.forces.bonus_drill local player = game.forces.player research.force.character_inventory_slots_bonus = player.mining_drill_productivity_bonus * 50 -- +5 Slots / bonus_drill.mining_drill_productivity_bonus = bonus_drill.mining_drill_productivity_bonus + 0.03 if bonus_drill.mining_drill_productivity_bonus >= 3 then bonus_drill.mining_drill_productivity_bonus = 3 end if research.name == 'steel-axe' then local msg = 'Steel-axe technology has been researched, 100% has been applied.\nBuy Pickaxe-upgrades in the market to boost it even more!' Alert.alert_all_players(30, msg, nil, 'achievement/tech-maniac', 0.6) end local force_name = research.force.name if not force_name then return end local flamethrower_damage = WPT.get('flamethrower_damage') flamethrower_damage[force_name] = -0.85 if research.name == 'military' then game.forces[force_name].set_turret_attack_modifier('flamethrower-turret', flamethrower_damage[force_name]) game.forces[force_name].set_ammo_damage_modifier('flamethrower', flamethrower_damage[force_name]) end if string.sub(research.name, 0, 18) == 'refined-flammables' then flamethrower_damage[force_name] = flamethrower_damage[force_name] + 0.10 game.forces[force_name].set_turret_attack_modifier('flamethrower-turret', flamethrower_damage[force_name]) game.forces[force_name].set_ammo_damage_modifier('flamethrower', flamethrower_damage[force_name]) end end Public.firearm_magazine_ammo = {name = 'firearm-magazine', count = 200} Public.piercing_rounds_magazine_ammo = {name = 'piercing-rounds-magazine', count = 200} Public.uranium_rounds_magazine_ammo = {name = 'uranium-rounds-magazine', count = 200} Public.light_oil_ammo = {name = 'light-oil', amount = 100} Public.artillery_shell_ammo = {name = 'artillery-shell', count = 15} Public.laser_turrent_power_source = {buffer_size = 2400000, power_production = 40000} function Public.reset_table() this.power_sources = {index = 1} this.refill_turrets = {index = 1} this.magic_crafters = {index = 1} this.magic_fluid_crafters = {index = 1} end local on_player_joined_game = Public.on_player_joined_game local on_player_left_game = Public.on_player_left_game local on_research_finished = Public.on_research_finished local on_player_changed_position = Public.on_player_changed_position local on_pre_player_left_game = Public.on_pre_player_left_game local on_player_respawned = Public.on_player_respawned 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_research_finished, on_research_finished) Event.add(defines.events.on_player_changed_position, on_player_changed_position) Event.add(defines.events.on_pre_player_left_game, on_pre_player_left_game) Event.add(defines.events.on_player_respawned, on_player_respawned) Event.on_nth_tick(10, tick) -- Event.on_nth_tick(5, do_turret_energy) return Public