mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2024-12-26 22:56:43 +02:00
2622 lines
87 KiB
Lua
2622 lines
87 KiB
Lua
-- This file is part of thesixthroc's Pirate Ship softmod, licensed under GPLv3 and stored at https://github.com/ComfyFactory/ComfyFactorio and https://github.com/danielmartin0/ComfyFactorio-Pirates.
|
|
|
|
local Memory = require('maps.pirates.memory')
|
|
local Balance = require('maps.pirates.balance')
|
|
local Math = require('maps.pirates.math')
|
|
local Common = require('maps.pirates.common')
|
|
local SurfacesCommon = require('maps.pirates.surfaces.common')
|
|
local CoreData = require('maps.pirates.coredata')
|
|
local Utils = require('maps.pirates.utils_local')
|
|
local _inspect = require('utils.inspect').inspect
|
|
local Ai = require('maps.pirates.ai')
|
|
-- local Structures = require 'maps.pirates.structures.structures'
|
|
local Boats = require('maps.pirates.structures.boats.boats')
|
|
local Surfaces = require('maps.pirates.surfaces.surfaces')
|
|
-- local Progression = require 'maps.pirates.progression'
|
|
local IslandEnum = require('maps.pirates.surfaces.islands.island_enum')
|
|
local Roles = require('maps.pirates.roles.roles')
|
|
local Permissions = require('maps.pirates.permissions')
|
|
-- local Gui = require 'maps.pirates.gui.gui'
|
|
-- local Sea = require 'maps.pirates.surfaces.sea.sea'
|
|
-- local Hold = require 'maps.pirates.surfaces.hold'
|
|
-- local Cabin = require 'maps.pirates.surfaces.cabin'
|
|
-- local Crowsnest = require 'maps.pirates.surfaces.crowsnest'
|
|
-- local Ores = require 'maps.pirates.ores'
|
|
-- local Parrot = require 'maps.pirates.parrot'
|
|
local Kraken = require('maps.pirates.surfaces.sea.kraken')
|
|
local Jailed = require('utils.datastore.jail_data')
|
|
local Crew = require('maps.pirates.crew')
|
|
local Quest = require('maps.pirates.quest')
|
|
local Shop = require('maps.pirates.shop.shop')
|
|
local Loot = require('maps.pirates.loot')
|
|
local Task = require('utils.task')
|
|
local Token = require('utils.token')
|
|
local Classes = require('maps.pirates.roles.classes')
|
|
local Ores = require('maps.pirates.ores')
|
|
local Server = require('utils.server')
|
|
-- local Modifers = require 'player_modifiers'
|
|
local GuiWelcome = require('maps.pirates.gui.welcome')
|
|
local tick_tack_trap = require('utils.functions.tick_tack_trap')
|
|
|
|
local Public = {}
|
|
|
|
function Public.silo_die()
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
local force = memory.force
|
|
if memory.game_lost == true then
|
|
return
|
|
end
|
|
|
|
destination.dynamic_data.rocketsilohp = 0
|
|
if
|
|
destination.dynamic_data.rocketsilos
|
|
and destination.dynamic_data.rocketsilos[1]
|
|
and destination.dynamic_data.rocketsilos[1].valid
|
|
then
|
|
local surface = destination.dynamic_data.rocketsilos[1].surface
|
|
surface.create_entity({
|
|
name = 'big-artillery-explosion',
|
|
position = destination.dynamic_data.rocketsilos[1].position,
|
|
})
|
|
|
|
if memory.boat and memory.boat.surface_name and surface.name == memory.boat.surface_name then
|
|
if CoreData.rocket_silo_death_causes_loss then
|
|
-- Crew.lose_life()
|
|
Crew.try_lose({ 'pirates.loss_silo_destroyed' })
|
|
elseif not destination.dynamic_data.rocket_launched then
|
|
if
|
|
destination.static_params
|
|
and destination.static_params.base_cost_to_undock
|
|
and destination.static_params.base_cost_to_undock['launch_rocket'] == true
|
|
then
|
|
Crew.try_lose({ 'pirates.loss_silo_destroyed_before_necessary_launch' })
|
|
else
|
|
Common.notify_force(force, { 'pirates.silo_destroyed' })
|
|
end
|
|
end
|
|
end
|
|
|
|
destination.dynamic_data.rocketsilos[1].die()
|
|
destination.dynamic_data.rocketsilos = nil
|
|
end
|
|
end
|
|
|
|
local function biters_chew_stuff_faster(event)
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
|
|
if
|
|
not (
|
|
event.cause
|
|
and event.cause.valid
|
|
and event.cause.force
|
|
and event.cause.force.name
|
|
and event.entity
|
|
and event.entity.valid
|
|
and event.entity.force
|
|
and event.entity.force.name
|
|
)
|
|
then
|
|
return
|
|
end
|
|
if event.cause.force.name ~= memory.enemy_force_name then
|
|
return
|
|
end --Enemy Forces only
|
|
|
|
if event.entity.force.name == 'neutral' or event.entity.force.name == 'environment' then
|
|
event.entity.health = event.entity.health - event.final_damage_amount * 5
|
|
event.final_damage_amount = event.final_damage_amount * 6
|
|
if destination and destination.type == Surfaces.enum.ISLAND and destination.subtype == IslandEnum.enum.MAZE then
|
|
event.entity.health = event.entity.health - event.final_damage_amount
|
|
event.final_damage_amount = event.final_damage_amount * 2
|
|
end
|
|
elseif event.entity.name == 'pipe' then
|
|
event.entity.health = event.entity.health - event.final_damage_amount * 0.5
|
|
event.final_damage_amount = event.final_damage_amount * 1.5
|
|
elseif event.entity.name == 'stone-furnace' then
|
|
event.entity.health = event.entity.health - event.final_damage_amount * 0.5
|
|
event.final_damage_amount = event.final_damage_amount * 1.5
|
|
elseif
|
|
event.entity.name == 'wooden-chest'
|
|
or event.entity.name == 'stone-chest'
|
|
or event.entity.name == 'steel-chest'
|
|
then
|
|
event.entity.health = event.entity.health - event.final_damage_amount * 0.5
|
|
event.final_damage_amount = event.final_damage_amount * 1.5
|
|
end
|
|
end
|
|
|
|
local function handle_damage_in_restricted_areas(event)
|
|
-- local memory = Memory.get_crew_memory()
|
|
local entity = event.entity
|
|
|
|
if event.cause and event.cause.valid and entity and entity.valid then
|
|
local surfacedata = Surfaces.SurfacesCommon.decode_surface_name(entity.surface.name)
|
|
-- local dest = Common.current_destination()
|
|
if
|
|
surfacedata.type == Surfaces.enum.CROWSNEST
|
|
or surfacedata.type == Surfaces.enum.LOBBY
|
|
or surfacedata.type == Surfaces.enum.CABIN
|
|
then
|
|
entity.health = entity.health + event.final_damage_amount
|
|
end
|
|
end
|
|
end
|
|
|
|
local function handle_damage_to_silo(event)
|
|
local memory = Memory.get_crew_memory()
|
|
local entity = event.entity
|
|
|
|
if event.cause and event.cause.valid and entity and entity.valid and entity.force.name == memory.force_name then
|
|
local destination = Common.current_destination()
|
|
if
|
|
destination.dynamic_data.rocketsilos
|
|
and destination.dynamic_data.rocketsilos[1]
|
|
and destination.dynamic_data.rocketsilos[1].valid
|
|
and entity == Common.current_destination().dynamic_data.rocketsilos[1]
|
|
then
|
|
if string.sub(event.cause.force.name, 1, 4) ~= 'crew' then -- may as well check this
|
|
-- play alert sound for all crew members
|
|
if memory.seconds_until_alert_sound_can_be_played_again <= 0 then
|
|
memory.seconds_until_alert_sound_can_be_played_again = Balance.alert_sound_max_frequency_in_seconds
|
|
|
|
for _, player_index in pairs(memory.crewplayerindices) do
|
|
local player = game.players[player_index]
|
|
player.play_sound({ path = 'utility/alert_destroyed', volume_modifier = 1 })
|
|
end
|
|
end
|
|
|
|
local damage = event.original_damage_amount
|
|
/ Balance.silo_resistance_factor
|
|
* (1 + Balance.biter_timeofday_bonus_damage(event.cause.surface.darkness))
|
|
local remaining_health = Common.entity_damage_healthbar(entity, damage, destination.dynamic_data)
|
|
if remaining_health and remaining_health <= 0 then
|
|
Public.silo_die()
|
|
else
|
|
destination.dynamic_data.rocketsilohp = remaining_health
|
|
end
|
|
else
|
|
entity.health = entity.max_health
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function handle_damage_to_enemyboat_spawners(event)
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
|
|
if
|
|
destination.dynamic_data.enemyboats
|
|
and #destination.dynamic_data.enemyboats > 0
|
|
and event.entity
|
|
and event.entity.valid
|
|
and event.entity.force.name == memory.enemy_force_name
|
|
then
|
|
for i = 1, #destination.dynamic_data.enemyboats do
|
|
local eb = destination.dynamic_data.enemyboats[i]
|
|
if eb.spawner and eb.spawner.valid and event.entity == eb.spawner then
|
|
-- if eb.spawner and eb.spawner.valid and event.entity == eb.spawner and eb.state == Structures.Boats.enum_state.APPROACHING then
|
|
local damage = event.final_damage_amount
|
|
local remaining_health = Common.entity_damage_healthbar(event.entity, damage)
|
|
|
|
if remaining_health and remaining_health <= 0 then
|
|
event.entity.die()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Does not include krakens or biter boat spawners
|
|
local function handle_damage_to_elite_spawners(event)
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
|
|
if
|
|
destination.dynamic_data.elite_spawners
|
|
and #destination.dynamic_data.elite_spawners > 0
|
|
and event.entity
|
|
and event.entity.valid
|
|
and event.entity.force.name == memory.enemy_force_name
|
|
then
|
|
for i = 1, #destination.dynamic_data.elite_spawners do
|
|
local spawner = destination.dynamic_data.elite_spawners[i]
|
|
if spawner and spawner.valid and event.entity == spawner then
|
|
local damage = event.final_damage_amount
|
|
|
|
local remaining_health = Common.entity_damage_healthbar(event.entity, damage)
|
|
|
|
if remaining_health and remaining_health <= 0 then
|
|
event.entity.die()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function handle_damage_to_elite_biters(event)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
local elite_biters = memory.elite_biters
|
|
if elite_biters and event.entity and event.entity.valid and event.entity.force.name == memory.enemy_force_name then
|
|
if elite_biters[event.entity.unit_number] then
|
|
local damage = event.final_damage_amount
|
|
|
|
local remaining_health = Common.entity_damage_healthbar(event.entity, damage)
|
|
|
|
if remaining_health and remaining_health <= 0 then
|
|
event.entity.die()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function handle_damage_to_artillery(event)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
if not (event.entity and event.entity.valid and event.entity.name and event.entity.name == 'artillery-turret') then
|
|
return
|
|
end
|
|
|
|
if
|
|
event.cause
|
|
and event.cause.valid
|
|
and event.cause.name
|
|
and Utils.contains(CoreData.enemy_units, event.cause.name)
|
|
then
|
|
if event.cause.force.name ~= memory.enemy_force_name then
|
|
return
|
|
end
|
|
|
|
-- play alert sound for all crew members
|
|
if memory.seconds_until_alert_sound_can_be_played_again <= 0 then
|
|
memory.seconds_until_alert_sound_can_be_played_again = Balance.alert_sound_max_frequency_in_seconds
|
|
|
|
for _, player_index in pairs(memory.crewplayerindices) do
|
|
local player = game.players[player_index]
|
|
player.play_sound({ path = 'utility/alert_destroyed', volume_modifier = 1 })
|
|
end
|
|
end
|
|
|
|
-- remove resistances:
|
|
-- event.entity.health = event.entity.health + event.final_damage_amount - event.original_damage_amount
|
|
|
|
local damage = event.original_damage_amount / Balance.cannon_resistance_factor
|
|
damage = damage * (1 + Balance.biter_timeofday_bonus_damage(event.cause.surface.darkness))
|
|
local remaining_health = Common.entity_damage_healthbar(event.entity, damage, memory.boat)
|
|
|
|
if remaining_health and remaining_health <= 0 then
|
|
event.entity.die()
|
|
end
|
|
else
|
|
event.entity.health = event.entity.max_health --nothing else should damage it
|
|
end
|
|
end
|
|
|
|
local function handle_damage_to_krakens(event)
|
|
if not event.entity then
|
|
return
|
|
end
|
|
if not event.entity.valid then
|
|
return
|
|
end
|
|
if not event.entity.name then
|
|
return
|
|
end
|
|
if event.entity.name ~= 'biter-spawner' then
|
|
return
|
|
end
|
|
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
if event.entity.force.name ~= memory.enemy_force_name then
|
|
return
|
|
end
|
|
|
|
local surface_name = memory.boat and memory.boat.surface_name
|
|
if surface_name ~= memory.sea_name then
|
|
return
|
|
end
|
|
|
|
local unit_number = event.entity.unit_number
|
|
|
|
if event.damage_type.name and event.damage_type.name == 'poison' then
|
|
event.final_damage_amount = event.final_damage_amount / 1.25
|
|
elseif event.damage_type.name and (event.damage_type.name == 'explosion') then
|
|
event.final_damage_amount = event.final_damage_amount / 1.5
|
|
elseif event.damage_type.name and (event.damage_type.name == 'fire') then
|
|
event.final_damage_amount = event.final_damage_amount / 1.25
|
|
end
|
|
-- and additionally:
|
|
if event.cause and event.cause.valid and event.cause.name == 'artillery-turret' then
|
|
event.final_damage_amount = event.final_damage_amount / 1.5
|
|
end
|
|
|
|
if event.damage_type.name and (event.damage_type.name == 'laser') then
|
|
event.final_damage_amount = event.final_damage_amount / 7 --laser turrets are in range. give some resistance
|
|
end
|
|
|
|
-- There should be a better way to do it than this...
|
|
if memory.healthbars and memory.healthbars[unit_number] then
|
|
local kraken_id = memory.healthbars[unit_number].id
|
|
local remaining_health = Common.entity_damage_healthbar(event.entity, event.final_damage_amount)
|
|
if remaining_health and remaining_health <= 0 then
|
|
Kraken.kraken_die(kraken_id)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function handle_damage_to_players(event)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
if not event.cause then
|
|
return
|
|
end
|
|
if not event.cause.valid then
|
|
return
|
|
end
|
|
if not event.cause.name then
|
|
return
|
|
end
|
|
|
|
-- if not (event.cause.name == 'small-biter') or (event.cause.name == 'small-spitter') or (event.cause.name == 'medium-biter') or (event.cause.name == 'medium-spitter') or (event.cause.name == 'big-biter') or (event.cause.name == 'big-spitter') or (event.cause.name == 'behemoth-biter') or (event.cause.name == 'behemoth-spitter') then return end
|
|
|
|
if not (event.entity and event.entity.valid and event.entity.name and event.entity.name == 'character') then
|
|
return
|
|
end
|
|
|
|
if not event.entity.player or not event.entity.player.valid then
|
|
return
|
|
end
|
|
|
|
local player_index = event.entity.player.index
|
|
local player = game.players[player_index]
|
|
|
|
if not player then
|
|
return
|
|
end
|
|
if not player.valid then
|
|
return
|
|
end
|
|
if not player.character then
|
|
return
|
|
end
|
|
if not player.character.valid then
|
|
return
|
|
end
|
|
|
|
local class = Classes.get_class(player_index)
|
|
local damage_multiplier = 1
|
|
|
|
--game.print('on damage info: {name: ' .. event.damage_type.name .. ', object_name: ' .. event.damage_type.object_name .. '}')
|
|
|
|
if event.damage_type.name == 'poison' then --make all poison damage stronger against players
|
|
damage_multiplier = damage_multiplier * Balance.poison_damage_multiplier
|
|
else
|
|
if class then
|
|
if class == Classes.enum.SCOUT then
|
|
damage_multiplier = damage_multiplier * Balance.scout_damage_taken_multiplier
|
|
-- merchant is disabled
|
|
-- elseif class == Classes.enum.MERCHANT then
|
|
-- damage_multiplier = damage_multiplier * 1.10
|
|
elseif class == Classes.enum.SAMURAI then
|
|
damage_multiplier = damage_multiplier * Balance.samurai_damage_taken_multiplier
|
|
elseif class == Classes.enum.HATAMOTO then
|
|
damage_multiplier = damage_multiplier * Balance.hatamoto_damage_taken_multiplier
|
|
elseif class == Classes.enum.ROCK_EATER then
|
|
damage_multiplier = damage_multiplier * Balance.rock_eater_damage_taken_multiplier
|
|
elseif class == Classes.enum.IRON_LEG then
|
|
if
|
|
memory.class_auxiliary_data[player_index]
|
|
and memory.class_auxiliary_data[player_index].iron_leg_active
|
|
then
|
|
damage_multiplier = damage_multiplier * Balance.iron_leg_damage_taken_multiplier
|
|
end
|
|
elseif class == Classes.enum.VETERAN then
|
|
local chance = Balance.veteran_on_hit_slow_chance
|
|
if Math.random() <= chance then
|
|
-- only certain targets accept stickers
|
|
if
|
|
Utils.contains(CoreData.enemy_units, event.cause.name)
|
|
and Common.validate_player_and_character(player)
|
|
then
|
|
player.character.surface.create_entity({
|
|
name = 'slowdown-sticker',
|
|
position = player.character.position,
|
|
speed = 1.5,
|
|
force = player.force,
|
|
target = event.cause,
|
|
})
|
|
end
|
|
end
|
|
-- else
|
|
-- damage_multiplier = damage_multiplier * (1 + Balance.bonus_damage_to_humans())
|
|
end
|
|
end
|
|
end
|
|
|
|
if event.cause.force.name == memory.enemy_force_name then
|
|
damage_multiplier = damage_multiplier * (1 + Balance.biter_timeofday_bonus_damage(event.cause.surface.darkness))
|
|
end --Enemy Forces
|
|
|
|
-- game.print('name: ' .. event.cause.name .. ' damage: ' .. event.final_damage_amount)
|
|
-- game.print('multiplier: ' .. damage_multiplier)
|
|
|
|
if damage_multiplier > 1 then
|
|
event.entity.health = event.entity.health - event.final_damage_amount * (damage_multiplier - 1)
|
|
elseif damage_multiplier < 1 and event.final_health > 0 then --lethal damage case isn't this easy
|
|
event.entity.health = event.entity.health + event.final_damage_amount * (1 - damage_multiplier)
|
|
end
|
|
|
|
-- deal with damage reduction on lethal damage for players
|
|
-- Piratux wrote this — it tracks player health (except passive regen), and intervenes on a lethal damage event, should work most of the time.
|
|
local global_memory = Memory.get_global_memory()
|
|
|
|
if damage_multiplier < 1 and event.final_health <= 0 then
|
|
local damage_dealt = event.final_damage_amount * damage_multiplier
|
|
if damage_dealt < global_memory.last_players_health[player_index] then
|
|
event.entity.health = global_memory.last_players_health[player_index] - damage_dealt
|
|
end
|
|
end
|
|
|
|
global_memory.last_players_health[player_index] = event.entity.health
|
|
end
|
|
|
|
local function handle_enemy_nighttime_damage_bonus(event)
|
|
if not event.cause then
|
|
return
|
|
end
|
|
if not event.cause.valid then
|
|
return
|
|
end
|
|
if not event.cause.name then
|
|
return
|
|
end
|
|
if not event.cause.surface then
|
|
return
|
|
end
|
|
if not event.cause.surface.valid then
|
|
return
|
|
end
|
|
|
|
if event.entity.name == 'character' then
|
|
return
|
|
end
|
|
|
|
if event.damage_type.name == 'impact' then
|
|
return
|
|
end --avoid circularity
|
|
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
-- if not (event.cause.name == 'small-biter') or (event.cause.name == 'small-spitter') or (event.cause.name == 'medium-biter') or (event.cause.name == 'medium-spitter') or (event.cause.name == 'big-biter') or (event.cause.name == 'big-spitter') or (event.cause.name == 'behemoth-biter') or (event.cause.name == 'behemoth-spitter') then return end
|
|
if event.cause.force.name ~= memory.enemy_force_name then
|
|
return
|
|
end --Enemy Forces
|
|
|
|
local bonusDamage = event.final_damage_amount * Balance.biter_timeofday_bonus_damage(event.cause.surface.darkness)
|
|
|
|
if bonusDamage > 0 then
|
|
event.entity.damage(bonusDamage, event.cause.force, 'impact', event.cause)
|
|
end
|
|
end
|
|
|
|
local function handle_damage_dealt_by_players(event)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
if not event.cause then
|
|
return
|
|
end
|
|
if not event.cause.valid then
|
|
return
|
|
end
|
|
if not event.entity.valid then
|
|
return
|
|
end
|
|
if event.cause.name ~= 'character' then
|
|
return
|
|
end
|
|
if event.entity.name == 'character' then
|
|
return
|
|
end
|
|
|
|
local character = event.cause
|
|
local player = character.player
|
|
|
|
local physical = event.damage_type.name == 'physical'
|
|
local acid = event.damage_type.name == 'acid'
|
|
|
|
local player_index = player.index
|
|
local class = Classes.get_class(player_index)
|
|
|
|
-- Lethal damage must be unaffected, otherwise enemy will never die.
|
|
-- @Future reference: when implementing damage changes for mobs with healthbar, make this check with healthbar health too
|
|
if event.final_health > 0 then
|
|
if physical then
|
|
-- QUARTERMASTER BUFFS
|
|
local nearby_players = character.surface.find_entities_filtered({
|
|
position = character.position,
|
|
radius = Balance.quartermaster_range,
|
|
type = { 'character' },
|
|
})
|
|
|
|
for _, p2 in pairs(nearby_players) do
|
|
if p2.player and p2.player.valid then
|
|
local p2_index = p2.player.index
|
|
if event.entity.valid and Classes.get_class(p2_index) == Classes.enum.QUARTERMASTER then
|
|
-- event.entity.damage((Balance.quartermaster_bonus_physical_damage - 1) * event.final_damage_amount, character.force, 'impact', character) --triggers this function again, but not physical this time
|
|
Common.damage_hostile_entity(
|
|
event.entity,
|
|
(Balance.quartermaster_bonus_physical_damage - 1) * event.final_damage_amount
|
|
)
|
|
event.final_damage_amount = event.final_damage_amount
|
|
* Balance.quartermaster_bonus_physical_damage
|
|
end
|
|
end
|
|
end
|
|
|
|
-- PISTOL BUFFS
|
|
if character.shooting_state.state ~= defines.shooting.not_shooting then
|
|
local weapon = character.get_inventory(defines.inventory.character_guns)[character.selected_gun_index]
|
|
local ammo = character.get_inventory(defines.inventory.character_ammo)[character.selected_gun_index]
|
|
if
|
|
event.entity.valid
|
|
and weapon.valid_for_read
|
|
and ammo.valid_for_read
|
|
and weapon.name == 'pistol'
|
|
and (
|
|
ammo.name == 'firearm-magazine'
|
|
or ammo.name == 'piercing-rounds-magazine'
|
|
or ammo.name == 'uranium-rounds-magazine'
|
|
)
|
|
then
|
|
-- event.entity.damage((Balance.pistol_damage_multiplier() - 1) * event.final_damage_amount, character.force, 'impact', character) --triggers this function again, but not physical this time
|
|
Common.damage_hostile_entity(
|
|
event.entity,
|
|
(Balance.pistol_damage_multiplier() - 1) * event.final_damage_amount
|
|
)
|
|
event.final_damage_amount = event.final_damage_amount * Balance.pistol_damage_multiplier()
|
|
end
|
|
end
|
|
end
|
|
|
|
if event.entity.valid and class and class == Classes.enum.SCOUT then
|
|
-- event.entity.health = event.entity.health + (1 - Balance.scout_damage_dealt_multiplier) * event.final_damage_amount
|
|
Common.damage_hostile_entity(
|
|
event.entity,
|
|
-(1 - Balance.scout_damage_dealt_multiplier) * event.final_damage_amount
|
|
)
|
|
event.final_damage_amount = event.final_damage_amount * Balance.scout_damage_dealt_multiplier
|
|
elseif event.entity.valid and class and (class == Classes.enum.SAMURAI or class == Classes.enum.HATAMOTO) then
|
|
local samurai = class == Classes.enum.SAMURAI
|
|
local hatamoto = class == Classes.enum.HATAMOTO
|
|
|
|
local no_weapon = not (
|
|
character.get_inventory(defines.inventory.character_guns)
|
|
and character.get_inventory(defines.inventory.character_guns)[character.selected_gun_index]
|
|
and character.get_inventory(defines.inventory.character_guns)[character.selected_gun_index].valid_for_read
|
|
)
|
|
|
|
local melee = (physical or acid) and no_weapon
|
|
|
|
local extra_damage_to_deal = 0
|
|
|
|
local big_number = 1000
|
|
|
|
local extra_physical_damage_from_research_multiplier = 1 + memory.force.get_ammo_damage_modifier('bullet')
|
|
|
|
if melee then
|
|
if physical then
|
|
if samurai then
|
|
extra_damage_to_deal = Balance.samurai_damage_dealt_with_melee
|
|
* extra_physical_damage_from_research_multiplier
|
|
elseif hatamoto then
|
|
extra_damage_to_deal = Balance.hatamoto_damage_dealt_with_melee
|
|
* extra_physical_damage_from_research_multiplier
|
|
end
|
|
elseif acid then --this hacky stuff is to implement repeated spillover splash damage, whilst getting around the fact that if overkill damage takes something to zero health, we can't tell in that event how much double-overkill damage should be dealt by reading off its HP. This code assumes that characters only deal acid damage via this function.
|
|
extra_damage_to_deal = event.original_damage_amount * big_number
|
|
end
|
|
else
|
|
if samurai then
|
|
-- event.entity.health = event.entity.health + (1 - Balance.samurai_damage_dealt_when_not_melee_multiplier) * event.final_damage_amount
|
|
Common.damage_hostile_entity(
|
|
event.entity,
|
|
-(1 - Balance.samurai_damage_dealt_when_not_melee_multiplier) * event.final_damage_amount
|
|
)
|
|
event.final_damage_amount = event.final_damage_amount
|
|
* Balance.samurai_damage_dealt_when_not_melee_multiplier
|
|
elseif hatamoto then
|
|
-- event.entity.health = event.entity.health + (1 - Balance.hatamoto_damage_dealt_when_not_melee_multiplier) * event.final_damage_amount
|
|
Common.damage_hostile_entity(
|
|
event.entity,
|
|
-(1 - Balance.hatamoto_damage_dealt_when_not_melee_multiplier) * event.final_damage_amount
|
|
)
|
|
event.final_damage_amount = event.final_damage_amount
|
|
* Balance.hatamoto_damage_dealt_when_not_melee_multiplier
|
|
end
|
|
end
|
|
|
|
-- @TODO: This should preferably be reworked, so that "event_on_entity_damaged()" could be simpler by just returning multiplier, although doing AoE is quite fun.
|
|
-- @TODO: "event.entity.health >= extra_damage_to_deal" is pointless when enemy has virtual healthbar
|
|
if event.entity and event.entity.valid and extra_damage_to_deal > 0 then
|
|
if event.entity.health >= extra_damage_to_deal then
|
|
-- event.entity.damage(extra_damage_to_deal, character.force, 'impact', character) --using .damage rather than subtracting from health directly plays better with entities which use healthbars
|
|
Common.damage_hostile_entity(event.entity, extra_damage_to_deal)
|
|
event.final_damage_amount = event.final_damage_amount + extra_damage_to_deal
|
|
else
|
|
local surplus = (extra_damage_to_deal - event.entity.health) * 0.8
|
|
event.entity.die(character.force, character)
|
|
local nearest = character.surface.find_nearest_enemy({
|
|
position = character.position,
|
|
max_distance = 2,
|
|
force = player.force,
|
|
})
|
|
if nearest and nearest.valid then
|
|
nearest.damage(surplus / big_number, character.force, 'acid', character)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function handle_poison_resistance_in_swamp(event)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
local entity = event.entity
|
|
if not entity.valid then
|
|
return
|
|
end
|
|
|
|
if not (event.damage_type.name and event.damage_type.name == 'poison') then
|
|
return
|
|
end
|
|
|
|
local destination = Common.current_destination()
|
|
if not (destination and destination.subtype == IslandEnum.enum.SWAMP) then
|
|
return
|
|
end
|
|
|
|
if destination.surface_name ~= entity.surface.name then
|
|
return
|
|
end
|
|
|
|
if
|
|
not (
|
|
(entity.type and entity.type == 'tree')
|
|
or (event.entity.force and event.entity.force.name == memory.enemy_force_name)
|
|
)
|
|
then
|
|
return
|
|
end
|
|
|
|
event.entity.health = event.entity.health + event.final_damage_amount
|
|
event.final_damage_amount = 0
|
|
end
|
|
|
|
local function handle_maze_walls_damage_resistance(event)
|
|
-- local memory = Memory.get_crew_memory()
|
|
|
|
local entity = event.entity
|
|
if not entity.valid then
|
|
return
|
|
end
|
|
|
|
local destination = Common.current_destination()
|
|
if not (destination and destination.subtype == IslandEnum.enum.MAZE) then
|
|
return
|
|
end
|
|
|
|
if destination.surface_name ~= entity.surface.name then
|
|
return
|
|
end
|
|
|
|
if
|
|
not (
|
|
(entity.type and entity.type == 'tree')
|
|
or entity.name == 'huge-rock'
|
|
or entity.name == 'big-rock'
|
|
or entity.name == 'big-sand-rock'
|
|
)
|
|
then
|
|
return
|
|
end
|
|
|
|
if event.damage_type.name and (event.damage_type.name == 'explosion' or event.damage_type.name == 'poison') then
|
|
event.entity.health = event.entity.health + event.final_damage_amount
|
|
event.final_damage_amount = 0
|
|
elseif event.damage_type.name and event.damage_type.name == 'fire' then
|
|
-- put out forest fires:
|
|
for _, e2 in
|
|
pairs(entity.surface.find_entities_filtered({
|
|
area = {
|
|
{ entity.position.x - 4, entity.position.y - 4 },
|
|
{ entity.position.x + 4, entity.position.y + 4 },
|
|
},
|
|
name = 'fire-flame-on-tree',
|
|
}))
|
|
do
|
|
if e2.valid then
|
|
e2.destroy()
|
|
end
|
|
end
|
|
else
|
|
if event.cause and event.cause.valid then
|
|
if string.sub(event.cause.force.name, 1, 4) == 'crew' then --player damage only
|
|
event.entity.health = event.entity.health + event.final_damage_amount * 0.9
|
|
event.final_damage_amount = event.final_damage_amount * 0.1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- functions like this need to be rewritten so they play nicely with healthbars:
|
|
-- local function damage_to_enemies(event)
|
|
-- local memory = Memory.get_crew_memory()
|
|
|
|
-- if not (event.entity and event.entity.valid and event.entity.force and event.entity.force.valid) then return end
|
|
|
|
-- if event.entity.force.name ~= memory.enemy_force_name then return end
|
|
-- local evo = memory.evolution_factor
|
|
|
|
-- if evo and evo > 1 and event.final_health > 0 then --lethal damage needs to be unaffected, else they never die
|
|
|
|
-- local surplus = evo - 1
|
|
|
|
-- local damage_multiplier = 1/(1 + Common.surplus_evo_biter_health_fractional_modifier(surplus))
|
|
|
|
-- if damage_multiplier < 1 then
|
|
-- event.entity.health = event.entity.health + event.final_damage_amount * (1 - damage_multiplier)
|
|
-- end
|
|
-- end
|
|
|
|
-- -- commented out as this is done elsewhere:
|
|
-- -- if event.damage_type.name == 'poison' then
|
|
-- -- event.entity.health = event.entity.health + event.final_damage_amount
|
|
-- -- end
|
|
-- end
|
|
|
|
-- @TODO: Possible rework solution: "event_on_entity_damaged()" should accumulate final damage dealt multiplier, and "fix" entity health or deal proper damage to virtual healthbar at the very end of the function. Entity deaths should be handled at "event_on_entity_died()" instead, to avoid the mess.
|
|
-- NOTE: "event.cause" may not always be provided.
|
|
-- However, special care needs to be taken when "event.cause" is nil and entity has healthbar (better not ignore such damage or it can cause issues, such as needing to handle their death on "entity_died" functions as opposed to here)
|
|
local function event_on_entity_damaged(event)
|
|
local crew_id = nil
|
|
if not crew_id and event.entity.surface.valid then
|
|
crew_id = SurfacesCommon.decode_surface_name(event.entity.surface.name).crewid
|
|
end
|
|
if not crew_id and event.force.valid then
|
|
crew_id = Common.get_id_from_force_name(event.force.name)
|
|
end
|
|
if not crew_id and event.entity.valid then
|
|
crew_id = Common.get_id_from_force_name(event.entity.force.name)
|
|
end
|
|
Memory.set_working_id(crew_id)
|
|
|
|
-- local memory = Memory.get_crew_memory()
|
|
-- local difficulty = memory.difficulty
|
|
|
|
if not event.entity.valid then
|
|
return
|
|
end
|
|
|
|
handle_damage_to_silo(event)
|
|
handle_damage_to_krakens(event)
|
|
handle_damage_to_enemyboat_spawners(event)
|
|
handle_damage_to_elite_spawners(event)
|
|
handle_damage_to_elite_biters(event)
|
|
handle_damage_to_artillery(event)
|
|
handle_damage_in_restricted_areas(event)
|
|
|
|
if not (event.entity.valid and event.entity.health) then
|
|
return
|
|
end -- need to call again, healthbar'd object might have been killed by script, so we shouldn't proceed now
|
|
|
|
handle_damage_to_players(event)
|
|
|
|
handle_enemy_nighttime_damage_bonus(event)
|
|
|
|
if not (event.entity.valid and event.entity.health) then
|
|
return
|
|
end -- need to call again, healthbar'd object might have been killed by script, so we shouldn't proceed now
|
|
|
|
biters_chew_stuff_faster(event)
|
|
|
|
if not (event.entity.valid and event.entity.health) then
|
|
return
|
|
end -- need to call again, healthbar'd object might have been killed by script, so we shouldn't proceed now
|
|
|
|
handle_poison_resistance_in_swamp(event)
|
|
handle_maze_walls_damage_resistance(event)
|
|
|
|
handle_damage_dealt_by_players(event)
|
|
|
|
-- damage_to_enemies(event)
|
|
end
|
|
|
|
function Public.load_some_map_chunks(destination_index, fraction, force_load) --in a 'spear' from the left
|
|
--WARNING: if force_load is true, THIS DOES NOT PLAY NICELY WITH DELAYED TASKS. log(_inspect{global_memory.working_id}) was observed to vary before and after this function.
|
|
force_load = force_load or false
|
|
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
local destination_data = memory.destinations[destination_index]
|
|
if not destination_data then
|
|
return
|
|
end
|
|
local surface_name = destination_data.surface_name
|
|
if not surface_name then
|
|
return
|
|
end
|
|
local surface = game.surfaces[surface_name]
|
|
if not surface then
|
|
return
|
|
end
|
|
|
|
local w, h = surface.map_gen_settings.width, surface.map_gen_settings.height
|
|
local c = { x = 0, y = 0 }
|
|
if destination_data.static_params and destination_data.static_params.islandcenter_position then
|
|
c = destination_data.static_params.islandcenter_position
|
|
w = w - 2 * Math.abs(c.x)
|
|
h = h - 2 * Math.abs(c.y)
|
|
end
|
|
local l = Math.max(Math.floor(w / 32), Math.floor(h / 32))
|
|
|
|
local i, j, s = 0, 0, { x = 0, y = 0 }
|
|
while i < 4 * l ^ 2 and j <= fraction * w / 32 * h / 32 do
|
|
i = i + 1
|
|
|
|
if s.y < 0 then
|
|
s.y = -s.y
|
|
elseif s.y > 0 then
|
|
s = { x = s.x + 1, y = 1 - s.y }
|
|
else
|
|
s = { x = 0, y = -(s.x + 1) }
|
|
end
|
|
|
|
if s.x <= w / 32 and s.y <= h / 32 / 2 and s.y >= -h / 32 / 2 then
|
|
surface.request_to_generate_chunks({ x = c.x - w / 2 + 32 * s.x, y = c.y + 32 * s.y }, 0.1)
|
|
j = j + 1
|
|
end
|
|
end
|
|
if force_load then
|
|
surface.force_generate_chunk_requests() --WARNING: THIS DOES NOT PLAY NICELY WITH DELAYED TASKS. log(_inspect{global_memory.working_id}) was observed to vary before and after this function.
|
|
end
|
|
end
|
|
|
|
function Public.load_some_map_chunks_random_order(surface, destination_data, fraction) -- The reason we might want to do this is because of algorithms like the labyrinth code, which make directionally biased patterns if you don't generate chunks in a random order
|
|
if not surface then
|
|
return
|
|
end
|
|
if not destination_data then
|
|
return
|
|
end
|
|
|
|
local shuffled_chunks
|
|
if not destination_data.dynamic_data then
|
|
destination_data.dynamic_data = {}
|
|
end
|
|
if not destination_data.dynamic_data.shuffled_chunks then
|
|
local w, h = surface.map_gen_settings.width, surface.map_gen_settings.height
|
|
local c = { x = 0, y = 0 }
|
|
if destination_data.static_params and destination_data.static_params.islandcenter_position then
|
|
c = destination_data.static_params.islandcenter_position
|
|
w = w - 2 * Math.abs(c.x)
|
|
h = h - 2 * Math.abs(c.y)
|
|
end
|
|
|
|
local chunks_list = {}
|
|
for i = 0, Math.ceil(w / 32 - 1), 1 do
|
|
for j = 0, Math.ceil(h / 32 - 1), 1 do
|
|
table.insert(chunks_list, { x = c.x - w / 2 + 32 * i, y = c.y - h / 2 + 32 * j })
|
|
end
|
|
end
|
|
|
|
destination_data.dynamic_data.shuffled_chunks = Math.shuffle(chunks_list)
|
|
end
|
|
shuffled_chunks = destination_data.dynamic_data.shuffled_chunks
|
|
|
|
for i = 1, #shuffled_chunks do
|
|
if i > fraction * #shuffled_chunks then
|
|
break
|
|
end
|
|
surface.request_to_generate_chunks(shuffled_chunks[i], 0.2)
|
|
end
|
|
end
|
|
|
|
-- local function event_pre_player_mined_item(event)
|
|
-- -- figure out which crew this is about:
|
|
-- -- local crew_id = nil
|
|
-- -- if event.player_index and game.players[event.player_index].valid then crew_id = Common.get_id_from_force_name(game.players[event.player_index].force.name) end
|
|
-- -- Memory.set_working_id(crew_id)
|
|
-- -- local memory = Memory.get_crew_memory()
|
|
|
|
-- -- if memory.planet[1].type.id == 11 then --rocky planet
|
|
-- -- if event.entity.name == 'huge-rock' or event.entity.name == 'big-rock' or event.entity.name == 'big-sand-rock' then
|
|
-- -- Event_functions.trap(event.entity, false)
|
|
-- -- event.entity.destroy()
|
|
-- -- Event_functions.rocky_loot(event)
|
|
-- -- end
|
|
-- -- end
|
|
-- end
|
|
|
|
local function player_mined_tree(event)
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
local player = game.players[event.player_index]
|
|
local entity = event.entity
|
|
local class = Classes.get_class(event.player_index)
|
|
local enemy_force = game.forces[memory.enemy_force_name]
|
|
|
|
local available = destination.dynamic_data.wood_remaining
|
|
local starting = destination.static_params.starting_wood
|
|
|
|
if not (available and destination.type == Surfaces.enum.ISLAND) then
|
|
return
|
|
end
|
|
|
|
if destination.subtype == IslandEnum.enum.MAZE then
|
|
if Math.random(1, 38) == 1 then
|
|
tick_tack_trap(entity.surface, entity.position, enemy_force)
|
|
return
|
|
end
|
|
end
|
|
|
|
local give = {}
|
|
|
|
local baseamount = 5
|
|
--minimum 1 wood
|
|
local amount = Math.clamp(
|
|
1,
|
|
Math.max(1, Math.ceil(available)),
|
|
Math.ceil(baseamount * Balance.game_resources_scale() * available / starting)
|
|
)
|
|
|
|
destination.dynamic_data.wood_remaining = destination.dynamic_data.wood_remaining - amount
|
|
|
|
give[#give + 1] = { name = 'wood', count = amount }
|
|
|
|
if class == Classes.enum.LUMBERJACK then
|
|
Classes.lumberjack_bonus_items(give)
|
|
else
|
|
if Math.random(Balance.every_nth_tree_gives_coins) == 1 then --tuned
|
|
local a = Balance.coin_amount_from_tree()
|
|
give[#give + 1] = { name = 'coin', count = a }
|
|
memory.playtesting_stats.coins_gained_by_trees_and_rocks = memory.playtesting_stats.coins_gained_by_trees_and_rocks
|
|
+ a
|
|
end
|
|
end
|
|
|
|
Common.give(player, give, entity.position)
|
|
|
|
if destination.subtype ~= IslandEnum.enum.FIRST then
|
|
if Math.random(512) == 1 then
|
|
local placed = Ores.try_ore_spawn(entity.surface, entity.position, entity.name, 0, true)
|
|
if placed then
|
|
Common.notify_player_expected(player, { 'pirates.ore_discovered' })
|
|
end
|
|
elseif Math.random(1024) == 1 then
|
|
local e = entity.surface.create_entity({
|
|
name = 'wooden-chest',
|
|
position = entity.position,
|
|
force = memory.ancient_friendly_force_name,
|
|
})
|
|
if e and e.valid then
|
|
e.minable = false
|
|
e.rotatable = false
|
|
e.destructible = false
|
|
|
|
local inv = e.get_inventory(defines.inventory.chest)
|
|
local loot = Loot.wooden_chest_loot()
|
|
for i = 1, #loot do
|
|
local l = loot[i]
|
|
inv.insert(l)
|
|
end
|
|
|
|
Common.notify_player_expected(player, { 'pirates.chest_discovered' })
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function player_mined_fish(event)
|
|
local memory = Memory.get_crew_memory()
|
|
local boat = memory.boat
|
|
local destination = Common.current_destination()
|
|
local player = game.players[event.player_index]
|
|
local entity = event.entity
|
|
local class = Classes.get_class(event.player_index)
|
|
|
|
-- Prevent dull strategy being staying in sea for long time catching as many fish as possible (as there is kind of infinite amount there)
|
|
-- NOTE: This however doesn't prevent catching fish with inserters, but that shouldn't matter much?
|
|
local boat_is_at_sea = Boats.is_boat_at_sea()
|
|
local fish_caught_while_at_sea = -1
|
|
if boat_is_at_sea and boat and boat.fish_caught_while_at_sea then
|
|
fish_caught_while_at_sea = boat.fish_caught_while_at_sea
|
|
end
|
|
|
|
if
|
|
not boat_is_at_sea
|
|
or (boat_is_at_sea and fish_caught_while_at_sea < Balance.maximum_fish_allowed_to_catch_at_sea)
|
|
then
|
|
if fish_caught_while_at_sea ~= -1 then
|
|
boat.fish_caught_while_at_sea = boat.fish_caught_while_at_sea + 1
|
|
end
|
|
|
|
local fish_amount = Balance.base_caught_fish_amount
|
|
local to_give = {}
|
|
|
|
if class == Classes.enum.FISHERMAN then
|
|
fish_amount = fish_amount + Balance.fisherman_fish_bonus
|
|
to_give[#to_give + 1] = { name = 'raw-fish', count = fish_amount }
|
|
elseif class == Classes.enum.MASTER_ANGLER then
|
|
fish_amount = fish_amount + Balance.master_angler_fish_bonus
|
|
to_give[#to_give + 1] = { name = 'raw-fish', count = fish_amount }
|
|
to_give[#to_give + 1] = { name = 'coin', count = Balance.master_angler_coin_bonus }
|
|
elseif class == Classes.enum.DREDGER then
|
|
fish_amount = fish_amount + Balance.dredger_fish_bonus
|
|
to_give[#to_give + 1] = { name = 'raw-fish', count = fish_amount }
|
|
to_give[#to_give + 1] = Loot.dredger_loot()[1]
|
|
else
|
|
to_give[#to_give + 1] = { name = 'raw-fish', count = fish_amount }
|
|
end
|
|
|
|
Common.give(player, to_give, entity.position)
|
|
|
|
if
|
|
destination
|
|
and destination.dynamic_data
|
|
and destination.dynamic_data.quest_type
|
|
and not destination.dynamic_data.quest_complete
|
|
then
|
|
if destination.dynamic_data.quest_type == Quest.enum.FISH then
|
|
destination.dynamic_data.quest_progress = destination.dynamic_data.quest_progress + fish_amount
|
|
Quest.try_resolve_quest()
|
|
end
|
|
end
|
|
else
|
|
Common.notify_player_error(player, { 'pirates.cant_catch_fish' })
|
|
end
|
|
end
|
|
|
|
local function player_mined_resource(event)
|
|
local memory = Memory.get_crew_memory()
|
|
-- local destination = Common.current_destination()
|
|
local player = game.players[event.player_index]
|
|
local entity = event.entity
|
|
-- local class = Classes.get_class(event.player_index)
|
|
|
|
local give = {}
|
|
|
|
-- prospector and chief excavator are disabled
|
|
-- if memory.overworldx > 0 then --no coins on first map, else the optimal strategy is to handmine everything there
|
|
-- if memory.classes_table and memory.classes_table[event.player_index] and memory.classes_table[event.player_index] == Classes.enum.PROSPECTOR then
|
|
-- local a = 3
|
|
-- give[#give + 1] = {name = 'coin', count = a}
|
|
-- memory.playtesting_stats.coins_gained_by_ore = memory.playtesting_stats.coins_gained_by_ore + a
|
|
-- give[#give + 1] = {name = entity.name, count = 6}
|
|
-- elseif memory.classes_table and memory.classes_table[event.player_index] and memory.classes_table[event.player_index] == Classes.enum.CHIEF_EXCAVATOR then
|
|
-- local a = 4
|
|
-- give[#give + 1] = {name = 'coin', count = a}
|
|
-- memory.playtesting_stats.coins_gained_by_ore = memory.playtesting_stats.coins_gained_by_ore + a
|
|
-- give[#give + 1] = {name = entity.name, count = 12}
|
|
-- else
|
|
-- if memory.overworldx > 0 then
|
|
-- local a = 1
|
|
-- give[#give + 1] = {name = 'coin', count = a}
|
|
-- memory.playtesting_stats.coins_gained_by_ore = memory.playtesting_stats.coins_gained_by_ore + a
|
|
-- end
|
|
-- give[#give + 1] = {name = entity.name, count = 2}
|
|
-- end
|
|
-- else
|
|
-- give[#give + 1] = {name = entity.name, count = 2}
|
|
-- end
|
|
|
|
if memory.overworldx > 0 then --no coins on first map, else the optimal strategy is to handmine everything there
|
|
local a = 1
|
|
give[#give + 1] = { name = 'coin', count = a }
|
|
memory.playtesting_stats.coins_gained_by_ore = memory.playtesting_stats.coins_gained_by_ore + a
|
|
end
|
|
|
|
local mining_bonus = player.force.mining_drill_productivity_bonus + 1
|
|
|
|
local whole_amount = math.floor(mining_bonus)
|
|
local fractional_part = mining_bonus - whole_amount
|
|
|
|
local ore_amount = whole_amount
|
|
if math.random() < fractional_part then
|
|
ore_amount = ore_amount + 1
|
|
end
|
|
|
|
give[#give + 1] = { name = entity.name, count = ore_amount }
|
|
|
|
Common.give(player, give, entity.position)
|
|
end
|
|
|
|
local function player_mined_rock(event)
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
local player = game.players[event.player_index]
|
|
local entity = event.entity
|
|
local enemy_force = game.forces[memory.enemy_force_name]
|
|
-- local class = Classes.get_class(event.player_index)
|
|
|
|
-- local available = destination.dynamic_data.rock_material_remaining
|
|
-- local starting = destination.static_params.starting_rock_material
|
|
|
|
-- if not (available and destination.type == Surfaces.enum.ISLAND) then return end
|
|
|
|
if destination.subtype == IslandEnum.enum.MAZE then
|
|
if Math.random(1, 35) == 1 then
|
|
tick_tack_trap(entity.surface, entity.position, enemy_force)
|
|
end
|
|
end
|
|
|
|
if destination.subtype == IslandEnum.enum.CAVE then
|
|
Ores.try_give_ore(player, entity.position, entity.name)
|
|
|
|
if Math.random(1, 35) == 1 then
|
|
tick_tack_trap(entity.surface, entity.position, enemy_force)
|
|
elseif Math.random(1, 20) == 1 then
|
|
entity.surface.create_entity({ name = 'compilatron', position = entity.position, force = memory.force })
|
|
|
|
if
|
|
destination
|
|
and destination.dynamic_data
|
|
and destination.dynamic_data.quest_type
|
|
and not destination.dynamic_data.quest_complete
|
|
then
|
|
if destination.dynamic_data.quest_type == Quest.enum.COMPILATRON then
|
|
destination.dynamic_data.quest_progress = destination.dynamic_data.quest_progress + 1
|
|
Quest.try_resolve_quest()
|
|
end
|
|
end
|
|
elseif Math.random(1, 10) == 1 then
|
|
if Math.random(1, 4) == 1 then
|
|
entity.surface.create_entity({
|
|
name = Common.get_random_worm_type(memory.evolution_factor),
|
|
position = entity.position,
|
|
force = memory.enemy_force_name,
|
|
})
|
|
else
|
|
local biter = entity.surface.create_entity({
|
|
name = Common.get_random_unit_type(memory.evolution_factor),
|
|
position = entity.position,
|
|
force = memory.enemy_force_name,
|
|
})
|
|
Common.try_make_biter_elite(biter)
|
|
end
|
|
end
|
|
else
|
|
local c = event.buffer.get_contents()
|
|
local c2 = {}
|
|
|
|
if memory.overworldx >= 0 then --used to be only later levels
|
|
if entity.name == 'huge-rock' then
|
|
local a = Math.ceil(1.5 * Balance.coin_amount_from_rock())
|
|
c2[#c2 + 1] = { name = 'coin', count = a, color = CoreData.colors.coin }
|
|
memory.playtesting_stats.coins_gained_by_trees_and_rocks = memory.playtesting_stats.coins_gained_by_trees_and_rocks
|
|
+ a
|
|
if Math.random(1, 35) == 1 then
|
|
c2[#c2 + 1] = { name = 'crude-oil-barrel', count = 1, color = CoreData.colors.oil }
|
|
end
|
|
else
|
|
local a = Balance.coin_amount_from_rock()
|
|
c2[#c2 + 1] = { name = 'coin', count = a, color = CoreData.colors.coin }
|
|
memory.playtesting_stats.coins_gained_by_trees_and_rocks = memory.playtesting_stats.coins_gained_by_trees_and_rocks
|
|
+ a
|
|
if Math.random(1, 35 * 3) == 1 then
|
|
c2[#c2 + 1] = { name = 'crude-oil-barrel', count = 1, color = CoreData.colors.oil }
|
|
end
|
|
end
|
|
end
|
|
|
|
for _, item in ipairs(c) do
|
|
if item.name == 'coal' and #c2 <= 1 then --if oil, then no coal
|
|
c2[#c2 + 1] = {
|
|
name = item.name,
|
|
count = Math.ceil(item.count * (player.force.mining_drill_productivity_bonus + 1)),
|
|
color = CoreData.colors.coal,
|
|
}
|
|
elseif item.name == 'stone' then
|
|
c2[#c2 + 1] = {
|
|
name = item.name,
|
|
count = Math.ceil(item.count * (player.force.mining_drill_productivity_bonus + 1)),
|
|
color = CoreData.colors.stone,
|
|
}
|
|
end
|
|
end
|
|
Common.give(player, c2, entity.position)
|
|
|
|
-- destination.dynamic_data.rock_material_remaining = available
|
|
|
|
if Surfaces.get_scope(destination).break_rock then
|
|
destination.dynamic_data.ore_spawn_points_to_avoid = destination.dynamic_data.ore_spawn_points_to_avoid
|
|
or {}
|
|
local points_to_avoid = destination.dynamic_data.ore_spawn_points_to_avoid
|
|
local can_place_ores = true
|
|
|
|
-- Sometimes there can be very little amount of rocks here, so it probably isn't bad idea to spawn ore on top of another
|
|
if destination.subtype ~= IslandEnum.enum.WALKWAYS then
|
|
for _, pos in ipairs(points_to_avoid) do
|
|
if Math.distance(pos, entity.position) < Balance.min_ore_spawn_distance then
|
|
can_place_ores = false
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if can_place_ores then
|
|
local placed = Surfaces.get_scope(destination).break_rock(entity.surface, entity.position, entity.name)
|
|
if placed then
|
|
points_to_avoid[#points_to_avoid + 1] = { x = entity.position.x, y = entity.position.y }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function event_on_player_mined_entity(event)
|
|
if not event.buffer then
|
|
return
|
|
end
|
|
if not event.player_index then
|
|
return
|
|
end
|
|
|
|
local player = game.players[event.player_index]
|
|
|
|
if not Common.validate_player_and_character(player) then
|
|
return
|
|
end
|
|
|
|
local entity = event.entity
|
|
if not entity.valid then
|
|
return
|
|
end
|
|
|
|
local crew_id = Common.get_id_from_force_name(player.force.name)
|
|
Memory.set_working_id(crew_id)
|
|
|
|
if player.character.surface.name == 'gulag' then
|
|
event.buffer.clear()
|
|
return
|
|
end
|
|
|
|
if entity.type == 'tree' then
|
|
player_mined_tree(event)
|
|
event.buffer.clear()
|
|
elseif entity.type == 'fish' then
|
|
player_mined_fish(event)
|
|
event.buffer.clear()
|
|
elseif
|
|
entity.name == 'coal'
|
|
or entity.name == 'stone'
|
|
or entity.name == 'copper-ore'
|
|
or entity.name == 'iron-ore'
|
|
then
|
|
player_mined_resource(event)
|
|
event.buffer.clear()
|
|
elseif entity.name == 'huge-rock' or entity.name == 'big-rock' or entity.name == 'big-sand-rock' then
|
|
player_mined_rock(event)
|
|
event.buffer.clear()
|
|
end
|
|
end
|
|
|
|
local function shred_nearby_simple_entities(entity)
|
|
local memory = Memory.get_crew_memory()
|
|
if memory.evolution_factor < 0.25 then
|
|
return
|
|
end
|
|
local simple_entities = entity.surface.find_entities_filtered({
|
|
type = { 'simple-entity', 'tree' },
|
|
area = { { entity.position.x - 3, entity.position.y - 3 }, { entity.position.x + 3, entity.position.y + 3 } },
|
|
})
|
|
if #simple_entities == 0 then
|
|
return
|
|
end
|
|
for i = 1, #simple_entities, 1 do
|
|
if not simple_entities[i] then
|
|
break
|
|
end
|
|
if simple_entities[i].valid then
|
|
simple_entities[i].die(memory.enemy_force_name, simple_entities[i])
|
|
end
|
|
end
|
|
end
|
|
|
|
local function base_kill_rewards(event)
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
local entity = event.entity
|
|
if not (entity and entity.valid) then
|
|
return
|
|
end
|
|
if not (event.force and event.force.valid) then
|
|
return
|
|
end
|
|
local entity_name = entity.name
|
|
|
|
-- Don't give coins for friendly biter death
|
|
if
|
|
Utils.contains(CoreData.enemy_units, entity_name)
|
|
and entity.force
|
|
and entity.force.name == memory.force_name
|
|
then
|
|
return
|
|
end
|
|
|
|
local revenge_target
|
|
if event.cause and event.cause.valid and event.cause.name == 'character' then
|
|
revenge_target = event.cause
|
|
end
|
|
|
|
-- This gives enemy loot straight to combat robot owner's inventory instead of dropping it on the ground
|
|
if
|
|
event.cause
|
|
and (event.cause.name == 'defender' or event.cause.name == 'distractor' or event.cause.name == 'destroyer')
|
|
then
|
|
if event.cause.combat_robot_owner and event.cause.combat_robot_owner.valid then
|
|
revenge_target = event.cause.combat_robot_owner
|
|
end
|
|
end
|
|
|
|
local class_is_chef = false
|
|
|
|
if revenge_target and revenge_target.valid and revenge_target.player and revenge_target.player.index then
|
|
class_is_chef = Classes.get_class(revenge_target.player.index) == Classes.enum.CHEF
|
|
end
|
|
|
|
local iron_amount
|
|
local coin_amount
|
|
local fish_amount
|
|
|
|
if entity_name == 'small-worm-turret' then
|
|
iron_amount = 5
|
|
coin_amount = 50
|
|
fish_amount = 1 * Balance.chef_fish_received_for_worm_kill
|
|
memory.playtesting_stats.coins_gained_by_nests_and_worms = memory.playtesting_stats.coins_gained_by_nests_and_worms
|
|
+ coin_amount
|
|
elseif entity_name == 'medium-worm-turret' then
|
|
iron_amount = 20
|
|
coin_amount = 90
|
|
fish_amount = 2 * Balance.chef_fish_received_for_worm_kill
|
|
memory.playtesting_stats.coins_gained_by_nests_and_worms = memory.playtesting_stats.coins_gained_by_nests_and_worms
|
|
+ coin_amount
|
|
elseif entity_name == 'biter-spawner' or entity_name == 'spitter-spawner' then
|
|
iron_amount = 30
|
|
coin_amount = 100
|
|
fish_amount = 0 -- cooking spawners don't really fit class fantasy imo
|
|
memory.playtesting_stats.coins_gained_by_nests_and_worms = memory.playtesting_stats.coins_gained_by_nests_and_worms
|
|
+ coin_amount
|
|
elseif entity_name == 'big-worm-turret' then
|
|
iron_amount = 30
|
|
coin_amount = 140
|
|
fish_amount = 2 * Balance.chef_fish_received_for_worm_kill
|
|
memory.playtesting_stats.coins_gained_by_nests_and_worms = memory.playtesting_stats.coins_gained_by_nests_and_worms
|
|
+ coin_amount
|
|
elseif entity_name == 'behemoth-worm-turret' then
|
|
iron_amount = 50
|
|
coin_amount = 260
|
|
fish_amount = 3 * Balance.chef_fish_received_for_worm_kill
|
|
memory.playtesting_stats.coins_gained_by_nests_and_worms = memory.playtesting_stats.coins_gained_by_nests_and_worms
|
|
+ coin_amount
|
|
elseif memory.overworldx > 0 then --avoid coin farming on first island
|
|
if entity_name == 'small-biter' then
|
|
-- if Math.random(2) == 1 then
|
|
-- coin_amount = 1
|
|
-- end
|
|
coin_amount = 1
|
|
fish_amount = 0 * Balance.chef_fish_received_for_biter_kill
|
|
memory.playtesting_stats.coins_gained_by_biters = memory.playtesting_stats.coins_gained_by_biters
|
|
+ coin_amount
|
|
elseif entity_name == 'small-spitter' then
|
|
coin_amount = 1
|
|
fish_amount = 0 * Balance.chef_fish_received_for_biter_kill
|
|
memory.playtesting_stats.coins_gained_by_biters = memory.playtesting_stats.coins_gained_by_biters
|
|
+ coin_amount
|
|
elseif entity_name == 'medium-biter' then
|
|
coin_amount = 2
|
|
fish_amount = 1 * Balance.chef_fish_received_for_biter_kill
|
|
memory.playtesting_stats.coins_gained_by_biters = memory.playtesting_stats.coins_gained_by_biters
|
|
+ coin_amount
|
|
elseif entity_name == 'medium-spitter' then
|
|
coin_amount = 2
|
|
fish_amount = 1 * Balance.chef_fish_received_for_biter_kill
|
|
memory.playtesting_stats.coins_gained_by_biters = memory.playtesting_stats.coins_gained_by_biters
|
|
+ coin_amount
|
|
elseif entity_name == 'big-biter' then
|
|
coin_amount = 4
|
|
fish_amount = 2 * Balance.chef_fish_received_for_biter_kill
|
|
memory.playtesting_stats.coins_gained_by_biters = memory.playtesting_stats.coins_gained_by_biters
|
|
+ coin_amount
|
|
elseif entity_name == 'big-spitter' then
|
|
coin_amount = 4
|
|
fish_amount = 2 * Balance.chef_fish_received_for_biter_kill
|
|
memory.playtesting_stats.coins_gained_by_biters = memory.playtesting_stats.coins_gained_by_biters
|
|
+ coin_amount
|
|
elseif entity_name == 'behemoth-biter' then
|
|
coin_amount = 8
|
|
fish_amount = 3 * Balance.chef_fish_received_for_biter_kill
|
|
memory.playtesting_stats.coins_gained_by_biters = memory.playtesting_stats.coins_gained_by_biters
|
|
+ coin_amount
|
|
elseif entity_name == 'behemoth-spitter' then
|
|
coin_amount = 8
|
|
fish_amount = 3 * Balance.chef_fish_received_for_biter_kill
|
|
memory.playtesting_stats.coins_gained_by_biters = memory.playtesting_stats.coins_gained_by_biters
|
|
+ coin_amount
|
|
end
|
|
end
|
|
|
|
local stack = {}
|
|
|
|
if iron_amount and iron_amount > 0 then
|
|
stack[#stack + 1] = { name = 'iron-plate', count = iron_amount }
|
|
end
|
|
|
|
if coin_amount and coin_amount > 0 then
|
|
stack[#stack + 1] = { name = 'coin', count = coin_amount }
|
|
end
|
|
|
|
if class_is_chef and fish_amount and fish_amount > 0 then
|
|
stack[#stack + 1] = { name = 'raw-fish', count = fish_amount }
|
|
end
|
|
|
|
local short_form = (not iron_amount) and true or false
|
|
|
|
-- revenge_target.player can be nil if player kills itself
|
|
if revenge_target and revenge_target.player then
|
|
Common.give(revenge_target.player, stack, nil, short_form, entity.surface, entity.position)
|
|
else
|
|
if event.cause and event.cause.valid and event.cause.position then
|
|
Common.give(nil, stack, event.cause.position, short_form, entity.surface, entity.position)
|
|
else
|
|
Common.give(nil, stack, entity.position, short_form, entity.surface)
|
|
end
|
|
end
|
|
|
|
if
|
|
(entity_name == 'biter-spawner' or entity_name == 'spitter-spawner')
|
|
and entity.position
|
|
and entity.surface
|
|
and entity.surface.valid
|
|
then
|
|
--check if its a boat biter entity
|
|
local boat_spawner = false
|
|
if destination.dynamic_data.enemyboats then
|
|
for i = 1, #destination.dynamic_data.enemyboats do
|
|
local eb = destination.dynamic_data.enemyboats[i]
|
|
if eb.spawner and eb.spawner.valid and event.entity == eb.spawner then
|
|
boat_spawner = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if boat_spawner then
|
|
Ai.revenge_group(entity.surface, entity.position, revenge_target, 'biter', 0.3, 2)
|
|
elseif entity_name == 'biter-spawner' then
|
|
Ai.revenge_group(entity.surface, entity.position, revenge_target, 'biter')
|
|
else
|
|
Ai.revenge_group(entity.surface, entity.position, revenge_target, 'spitter')
|
|
end
|
|
end
|
|
end
|
|
|
|
local function spawner_died(event)
|
|
-- local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
|
|
if destination and destination.type == Surfaces.enum.ISLAND and destination.dynamic_data then
|
|
local not_boat = true
|
|
if destination.dynamic_data.enemyboats and #destination.dynamic_data.enemyboats > 0 then
|
|
for i = 1, #destination.dynamic_data.enemyboats do
|
|
local eb = destination.dynamic_data.enemyboats[i]
|
|
if
|
|
eb.spawner
|
|
and eb.spawner.valid
|
|
and event.entity
|
|
and event.entity.valid
|
|
and event.entity == eb.spawner
|
|
then
|
|
not_boat = false
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if not_boat then
|
|
local extra_evo = Balance.evolution_per_nest_kill()
|
|
Common.increment_evo(extra_evo)
|
|
|
|
destination.dynamic_data.evolution_accrued_nests = (destination.dynamic_data.evolution_accrued_nests or 0)
|
|
+ extra_evo
|
|
end
|
|
end
|
|
end
|
|
|
|
local function event_on_entity_died(event)
|
|
--== MODDING NOTE: event.cause is not always provided.
|
|
local entity = event.entity
|
|
if not (entity and entity.valid) then
|
|
return
|
|
end
|
|
if not (event.force and event.force.valid) then
|
|
return
|
|
end
|
|
|
|
local crew_id = Common.get_id_from_force_name(entity.force.name)
|
|
Memory.set_working_id(crew_id)
|
|
local memory = Memory.get_crew_memory()
|
|
local boat = memory.boat
|
|
if not Common.is_id_valid(memory.id) then
|
|
return
|
|
end
|
|
|
|
base_kill_rewards(event)
|
|
|
|
if memory.scripted_biters and entity.type == 'unit' and entity.force.name == memory.enemy_force_name then
|
|
memory.scripted_biters[entity.unit_number] = nil
|
|
end
|
|
|
|
if entity.force.index == 3 or entity.force.name == 'environment' then
|
|
if event.cause and event.cause.valid and event.cause.force.name == memory.enemy_force_name then
|
|
shred_nearby_simple_entities(entity)
|
|
end
|
|
end
|
|
|
|
if event.entity and event.entity.valid and event.entity.force and event.entity.force.name == memory.force_name then
|
|
if boat and boat.cannonscount and entity.name == 'artillery-turret' then
|
|
boat.cannonscount = boat.cannonscount - 1
|
|
-- if boat.cannonscount <= 0 then
|
|
-- Crew.try_lose()
|
|
-- end
|
|
Crew.try_lose({ 'pirates.loss_cannon_destroyed' })
|
|
end
|
|
end
|
|
|
|
if entity and entity.valid and entity.force and entity.force.name == memory.enemy_force_name then
|
|
if entity.name == 'biter-spawner' or entity.name == 'spitter-spawner' then
|
|
spawner_died(event)
|
|
-- I think the only reason krakens don't trigger this right now is that they are destroyed rather than .die()
|
|
else
|
|
local destination = Common.current_destination()
|
|
if
|
|
destination
|
|
and destination.dynamic_data
|
|
and destination.dynamic_data.quest_type
|
|
and not destination.dynamic_data.quest_complete
|
|
then
|
|
if destination.dynamic_data.quest_type == Quest.enum.WORMS and entity.type == 'turret' then
|
|
destination.dynamic_data.quest_progress = destination.dynamic_data.quest_progress + 1
|
|
Quest.try_resolve_quest()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- elite biter death
|
|
local elite_biters = memory.elite_biters
|
|
if
|
|
elite_biters
|
|
and entity
|
|
and entity.valid
|
|
and entity.force.name == memory.enemy_force_name
|
|
and elite_biters
|
|
and elite_biters[entity.unit_number]
|
|
then
|
|
local surface = entity.surface
|
|
if surface and surface.valid then
|
|
-- Shoot spit around and spawn biters where spit lands
|
|
local arc_size = (2 * Math.pi) / Balance.biters_spawned_on_elite_biter_death
|
|
for i = 1, Balance.biters_spawned_on_elite_biter_death, 1 do
|
|
local offset = Math.random_vec_in_arc(Math.random(4, 8), arc_size * i, arc_size)
|
|
local target_pos = Math.vector_sum(entity.position, offset)
|
|
|
|
local stream = surface.create_entity({
|
|
name = 'acid-stream-spitter-big',
|
|
position = entity.position,
|
|
force = memory.enemy_force_name,
|
|
source = entity.position,
|
|
target = target_pos,
|
|
max_range = 500,
|
|
speed = 0.1,
|
|
})
|
|
|
|
if not memory.elite_biters_stream_registrations then
|
|
memory.elite_biters_stream_registrations = {}
|
|
end
|
|
memory.elite_biters_stream_registrations[#memory.elite_biters_stream_registrations + 1] = {
|
|
number = script.register_on_object_destroyed(stream),
|
|
position = target_pos,
|
|
name = entity.name,
|
|
surface_name = surface.name, -- surface name is needed to know where biter died
|
|
}
|
|
end
|
|
end
|
|
|
|
memory.elite_biters[entity.unit_number] = nil
|
|
end
|
|
end
|
|
|
|
-- function Public.research_apply_buffs(event)
|
|
-- local memory = Memory.get_crew_memory()
|
|
-- local force = memory.force
|
|
|
|
-- if Balance.research_buffs[event.research.name] then
|
|
-- local tech = Balance.research_buffs[event.research.name]
|
|
-- -- @FIXME: This code is from another scenario but doesn't work
|
|
-- for k, v in pairs(tech) do
|
|
-- force[k] = force[k] + v
|
|
-- end
|
|
-- end
|
|
-- end
|
|
|
|
local function event_on_research_finished(event)
|
|
-- figure out which crew this is about:
|
|
local research = event.research
|
|
local force = research.force
|
|
local crew_id = Common.get_id_from_force_name(force.name)
|
|
Memory.set_working_id(crew_id)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
if not memory.game_lost then --this condition should prevent discord messages being fired when the crew disbands and gets reset
|
|
-- using a localised string means we have to write this out (recall that "" signals concatenation)
|
|
memory.force.print(
|
|
{ '', '>> ', { 'pirates.research_notification', research.localised_name } },
|
|
{ color = CoreData.colors.notify_force_light }
|
|
)
|
|
|
|
Server.to_discord_embed_raw({
|
|
'',
|
|
'[' .. memory.name .. '] ',
|
|
{ 'pirates.research_notification', prototypes.technology[research.name].localised_name },
|
|
}, true)
|
|
end
|
|
|
|
for _, e in ipairs(research.effects) do
|
|
local t = e.type
|
|
if t == 'ammo-damage' then
|
|
local category = e.ammo_category
|
|
local factor = Balance.player_ammo_damage_modifiers()[category]
|
|
|
|
if factor then
|
|
local current_m = force.get_ammo_damage_modifier(category)
|
|
local m = e.modifier
|
|
force.set_ammo_damage_modifier(category, current_m + factor * m)
|
|
end
|
|
elseif t == 'gun-speed' then
|
|
local category = e.ammo_category
|
|
local factor = Balance.player_gun_speed_modifiers()[category]
|
|
|
|
if factor then
|
|
local current_m = force.get_gun_speed_modifier(category)
|
|
local m = e.modifier
|
|
force.set_gun_speed_modifier(category, current_m + factor * m)
|
|
end
|
|
elseif t == 'turret-attack' then
|
|
local category = e.turret_id
|
|
local factor = Balance.player_turret_attack_modifiers()[category]
|
|
|
|
if factor then
|
|
local current_m = force.get_turret_attack_modifier(category)
|
|
local m = e.modifier
|
|
force.set_turret_attack_modifier(category, current_m + factor * m)
|
|
end
|
|
end
|
|
end
|
|
|
|
Crew.disable_recipes(force)
|
|
end
|
|
|
|
local function event_on_player_joined_game(event)
|
|
local global_memory = Memory.get_global_memory()
|
|
|
|
local player = game.players[event.player_index]
|
|
|
|
--figure out if we should drop them back into a crew:
|
|
|
|
if not Server.get_current_time() then -- don't run this on servers because I'd need to negotiate that with the rest of Comfy
|
|
player.print({ 'pirates.thesixthroc_support_toast' }, { color = { r = 1, g = 0.4, b = 0.9 } })
|
|
end
|
|
|
|
if _DEBUG then
|
|
game.print('Debug mode on. Use /go to get started, /1 /4 /32 etc to change game speed.')
|
|
end
|
|
|
|
local crew_to_put_back_in = nil
|
|
for _, memory in pairs(global_memory.crew_memories) do
|
|
if
|
|
Common.is_id_valid(memory.id)
|
|
and memory.crewstatus == Crew.enum.ADVENTURING
|
|
and memory.temporarily_logged_off_player_data[player.index]
|
|
then
|
|
crew_to_put_back_in = memory.id
|
|
break
|
|
end
|
|
end
|
|
|
|
-- if not _DEBUG then
|
|
-- Gui.info.toggle_window(player)
|
|
-- end
|
|
|
|
if crew_to_put_back_in then
|
|
log('INFO: ' .. player.name .. ' (crew ID: ' .. crew_to_put_back_in .. ') joined the game')
|
|
|
|
Memory.set_working_id(crew_to_put_back_in)
|
|
Crew.join_crew(player, true)
|
|
|
|
local memory = Memory.get_crew_memory()
|
|
if (not memory.run_is_protected) and #memory.crewplayerindices <= 1 then
|
|
Roles.make_captain(player)
|
|
end
|
|
|
|
if _DEBUG then
|
|
log('putting player back in their old crew')
|
|
end
|
|
else
|
|
log('INFO: ' .. player.name .. ' (crew ID: NONE) joined the game')
|
|
if player.character and player.character.valid then
|
|
player.character.destroy()
|
|
end
|
|
player.set_controller({ type = defines.controllers.god })
|
|
player.create_character()
|
|
|
|
local spawnpoint = Common.lobby_spawnpoint
|
|
local surface = game.surfaces[CoreData.lobby_surface_name]
|
|
|
|
player.teleport(surface.find_non_colliding_position('character', spawnpoint, 32, 0.5) or spawnpoint, surface)
|
|
Permissions.update_privileges(player)
|
|
|
|
if not player.name then
|
|
return
|
|
end
|
|
|
|
-- start at Common.starting_island_spawnpoint or not?
|
|
|
|
if game.tick == 0 then
|
|
Common.ensure_chunks_at(surface, spawnpoint, 5)
|
|
end
|
|
|
|
Common.notify_player_expected(player, { 'pirates.welcome_main_chat' })
|
|
|
|
if not _DEBUG then
|
|
GuiWelcome.show_welcome_window(player)
|
|
end
|
|
|
|
player.force = Common.lobby_force_name
|
|
|
|
-- NOTE: It was suggested to always spawn players in lobby, in hopes that they may want to create their crew increasing the popularity of scenario. Hence the following code is disabled.
|
|
|
|
-- WARNING: If re-enabling autojoin, make sure it respects private/protected runs.
|
|
|
|
-- Auto-join the oldest crew:
|
|
-- local ages = {}
|
|
-- for _, memory in pairs(global_memory.crew_memories) do
|
|
-- if Common.is_id_valid(memory.id)
|
|
-- and (not memory.run_is_private)
|
|
-- and memory.crewstatus == Crew.enum.ADVENTURING
|
|
-- and memory.capacity
|
|
-- and memory.crewplayerindices
|
|
-- and #memory.crewplayerindices < memory.capacity
|
|
-- and (not (memory.tempbanned_from_joining_data
|
|
-- and memory.tempbanned_from_joining_data[player.index]
|
|
-- and game.tick < memory.tempbanned_from_joining_data[player.index] + Common.ban_from_rejoining_crew_ticks)) then
|
|
-- ages[#ages+1] = {id = memory.id, age = memory.age, large = (memory.capacity >= Common.minimum_run_capacity_to_enforce_space_for)}
|
|
-- end
|
|
-- end
|
|
-- table.sort(
|
|
-- ages,
|
|
-- function(a, b) --true if a should be to the left of b
|
|
-- if a.large and (not b.large) then
|
|
-- return true
|
|
-- elseif (not a.large) and b.large then
|
|
-- return false
|
|
-- else
|
|
-- return a.age > b.age
|
|
-- end
|
|
-- end
|
|
-- )
|
|
-- if ages[1] then
|
|
-- Crew.join_crew(player)
|
|
|
|
-- local memory = global_memory.crew_memories[ages[1].id]
|
|
-- if (not memory.run_is_protected) and #memory.crewplayerindices <= 1 then
|
|
-- Roles.make_captain(player)
|
|
-- end
|
|
|
|
-- if ages[2] then
|
|
-- if ages[1].large and (not ages[#ages].large) then
|
|
-- Common.notify_player_announce(player, {'pirates.goto_oldest_crew_with_large_capacity'})
|
|
-- else
|
|
-- Common.notify_player_announce(player, {'pirates.goto_oldest_crew'})
|
|
-- end
|
|
-- end
|
|
|
|
-- if memory.run_is_protected and (not Roles.captain_exists()) then
|
|
-- Common.notify_player_expected(player, {'pirates.player_joins_protected_run_with_no_captain'})
|
|
-- Common.notify_player_expected(player, {'pirates.create_new_crew_tip'})
|
|
-- end
|
|
-- end
|
|
end
|
|
|
|
if player.character and player.character.valid then
|
|
global_memory.last_players_health[event.player_index] = player.character.health
|
|
end
|
|
end
|
|
|
|
local function event_on_pre_player_left_game(event)
|
|
local player = game.players[event.player_index]
|
|
|
|
local global_memory = Memory.get_global_memory()
|
|
|
|
-- figure out which crew this is about:
|
|
local crew_id = Common.get_id_from_force_name(player.force.name)
|
|
if crew_id then
|
|
log('INFO: ' .. player.name .. ' (crew ID: ' .. crew_id .. ') left the game')
|
|
else
|
|
log('INFO: ' .. player.name .. ' (crew ID: NONE) left the game')
|
|
end
|
|
|
|
Memory.set_working_id(crew_id)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
for k, proposal in pairs(global_memory.crewproposals) do
|
|
if proposal and proposal.created_by_player and proposal.created_by_player == event.player_index then
|
|
global_memory.crewproposals[k] = nil
|
|
end
|
|
end
|
|
|
|
if not Common.is_id_valid(crew_id) then
|
|
if player.character and player.character.valid then
|
|
player.character.destroy()
|
|
end
|
|
return -- nothing more needed
|
|
end
|
|
|
|
if player.controller_type == defines.controllers.editor then
|
|
player.toggle_map_editor()
|
|
end
|
|
|
|
for _, id in pairs(memory.crewplayerindices) do
|
|
if player.index == id then
|
|
Crew.leave_crew(player, false, true)
|
|
break
|
|
end
|
|
end
|
|
|
|
global_memory.last_players_health[event.player_index] = nil
|
|
end
|
|
|
|
-- local function event_on_player_left_game(event)
|
|
-- -- n/a
|
|
-- end
|
|
|
|
-- local function on_player_changed_position(event)
|
|
-- local memory = Chrono_table.get_table()
|
|
-- if memory.planet[1].type.id == 14 then --lava planet
|
|
-- Event_functions.lava_planet(event)
|
|
-- end
|
|
-- end
|
|
|
|
local function on_player_changed_surface(event)
|
|
local player = game.players[event.player_index]
|
|
local jailed = Jailed.get_jailed_table()
|
|
|
|
if player.name and jailed and jailed[player.name] then
|
|
-- not quite sure this is necessary, but let's send their items to the crew:
|
|
Common.send_important_items_from_player_to_crew(player, true)
|
|
return
|
|
end
|
|
|
|
-- prevent connecting power between surfaces: (for the ship we do this automatically, but no need to let players do it in the general case:)
|
|
if not player.is_cursor_empty() then
|
|
if player.cursor_stack and player.cursor_stack.valid_for_read then
|
|
local blacklisted = {
|
|
['small-electric-pole'] = true,
|
|
['medium-electric-pole'] = true,
|
|
['big-electric-pole'] = true,
|
|
['substation'] = true,
|
|
}
|
|
if blacklisted[player.cursor_stack.name] then
|
|
player.get_main_inventory().insert(player.cursor_stack)
|
|
player.cursor_stack.clear()
|
|
end
|
|
end
|
|
if player.cursor_ghost then
|
|
player.cursor_ghost = nil
|
|
end
|
|
end
|
|
|
|
Permissions.update_privileges(player)
|
|
|
|
GuiWelcome.close_welcome_window(player)
|
|
end
|
|
|
|
function Public.player_entered_vehicle(player, vehicle)
|
|
if not Common.validate_player_and_character(player) then
|
|
return
|
|
end
|
|
if not vehicle then
|
|
log('no vehicle')
|
|
return
|
|
end
|
|
-- if not vehicle.name then log('no vehicle') return end
|
|
-- if not vehicle.valid then log('vehicle invalid') return end
|
|
|
|
local player_relative_pos =
|
|
{ x = player.character.position.x - vehicle.position.x, y = player.character.position.y - vehicle.position.y }
|
|
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
local player_boat_relative_pos
|
|
if memory and memory.boat and memory.boat.position then
|
|
player_boat_relative_pos = {
|
|
x = player.character.position.x - memory.boat.position.x,
|
|
y = player.character.position.y - memory.boat.position.y,
|
|
}
|
|
else
|
|
player_boat_relative_pos = {
|
|
x = player.character.position.x - vehicle.position.x,
|
|
y = player.character.position.y - vehicle.position.y,
|
|
}
|
|
end
|
|
|
|
local surfacedata = Surfaces.SurfacesCommon.decode_surface_name(player.character.surface.name)
|
|
|
|
if vehicle.name == 'car' then
|
|
-- A way to make player driven vehicles work
|
|
if vehicle.minable then
|
|
return
|
|
end
|
|
|
|
if
|
|
surfacedata.type ~= Surfaces.enum.CROWSNEST
|
|
and surfacedata.type ~= Surfaces.enum.CABIN
|
|
and surfacedata.type ~= Surfaces.enum.LOBBY
|
|
then
|
|
if player_boat_relative_pos.x < -47 then
|
|
Surfaces.player_goto_cabin(player, { x = 2, y = player_relative_pos.y })
|
|
else
|
|
Surfaces.player_goto_crows_nest(player, player_relative_pos)
|
|
end
|
|
player.play_sound({ path = 'utility/picked_up_item' })
|
|
elseif surfacedata.type == Surfaces.enum.CROWSNEST then
|
|
Surfaces.player_exit_crows_nest(player, player_relative_pos)
|
|
player.play_sound({ path = 'utility/picked_up_item' })
|
|
elseif surfacedata.type == Surfaces.enum.CABIN then
|
|
Surfaces.player_exit_cabin(player, player_relative_pos)
|
|
player.play_sound({ path = 'utility/picked_up_item' })
|
|
end
|
|
vehicle.color = { 148, 106, 52 }
|
|
|
|
player.driving = false
|
|
elseif vehicle.name == 'locomotive' then
|
|
if
|
|
surfacedata.type ~= Surfaces.enum.HOLD
|
|
and surfacedata.type ~= Surfaces.enum.LOBBY
|
|
and Math.abs(player_boat_relative_pos.y) < 8
|
|
then --<8 in order not to enter holds of boats you haven't bought yet
|
|
Surfaces.player_goto_hold(player, player_relative_pos, 1)
|
|
player.play_sound({ path = 'utility/picked_up_item' })
|
|
elseif surfacedata.type == Surfaces.enum.HOLD then
|
|
local current_hold_index = surfacedata.destination_index
|
|
if current_hold_index >= memory.hold_surface_count then
|
|
Surfaces.player_exit_hold(player, player_relative_pos)
|
|
else
|
|
Surfaces.player_goto_hold(player, player_relative_pos, current_hold_index + 1)
|
|
end
|
|
player.play_sound({ path = 'utility/picked_up_item' })
|
|
end
|
|
|
|
player.driving = false
|
|
end
|
|
end
|
|
|
|
local function event_on_player_driving_changed_state(event)
|
|
local player = game.players[event.player_index]
|
|
local vehicle = event.entity
|
|
|
|
local crew_id = Common.get_id_from_force_name(player.force.name)
|
|
Memory.set_working_id(crew_id)
|
|
|
|
Public.player_entered_vehicle(player, vehicle)
|
|
end
|
|
|
|
function Public.event_on_chunk_generated(event)
|
|
local surface = event.surface
|
|
if not surface then
|
|
return
|
|
end
|
|
if not surface.valid then
|
|
return
|
|
end
|
|
if surface.name == 'nauvis' or surface.name == 'piratedev1' or surface.name == 'gulag' then
|
|
return
|
|
end
|
|
|
|
local seed = surface.map_gen_settings.seed
|
|
local name = surface.name
|
|
|
|
local surface_name_decoded = Surfaces.SurfacesCommon.decode_surface_name(name)
|
|
local type = surface_name_decoded.type
|
|
-- local subtype = surface_name_decoded.subtype
|
|
local chunk_destination_index = surface_name_decoded.destination_index
|
|
local crewid = surface_name_decoded.crewid
|
|
|
|
Memory.set_working_id(crewid)
|
|
|
|
local chunk_left_top = event.area.left_top
|
|
local width, height = nil, nil
|
|
local terraingen_coordinates_offset = { x = 0, y = 0 }
|
|
local static_params = {}
|
|
local other_map_generation_data = {}
|
|
local scope
|
|
local overworldx = 0
|
|
|
|
local memory = Memory.get_crew_memory()
|
|
if type == Surfaces.enum.ISLAND and memory.destinations and memory.destinations[chunk_destination_index] then
|
|
local destination = memory.destinations[chunk_destination_index]
|
|
scope = Surfaces.get_scope(surface_name_decoded)
|
|
static_params = destination.static_params
|
|
other_map_generation_data = destination.dynamic_data.other_map_generation_data or {}
|
|
terraingen_coordinates_offset = static_params.terraingen_coordinates_offset
|
|
width = static_params.width
|
|
height = static_params.height
|
|
overworldx = destination.overworld_position.x
|
|
end
|
|
|
|
if not scope then
|
|
scope = Surfaces[type]
|
|
end
|
|
|
|
local noise_params, terrain_fn, chunk_structures_fn
|
|
if scope then
|
|
if scope.Data then
|
|
if scope.Data.noiseparams then
|
|
noise_params = scope.Data.noiseparams
|
|
end
|
|
if (not width) and scope.Data.width then
|
|
width = scope.Data.width
|
|
end
|
|
if (not height) and scope.Data.height then
|
|
height = scope.Data.height
|
|
end
|
|
end
|
|
if scope.terrain then
|
|
terrain_fn = scope.terrain
|
|
end
|
|
if scope.chunk_structures then
|
|
chunk_structures_fn = scope.chunk_structures
|
|
end
|
|
end
|
|
|
|
if not terrain_fn then
|
|
return
|
|
end
|
|
|
|
if not width then
|
|
width = 999
|
|
log('no surface width? ' .. type)
|
|
end
|
|
if not height then
|
|
height = 999
|
|
end
|
|
|
|
local tiles, entities, decoratives, specials = {}, {}, {}, {}
|
|
-- local noise_generator = nil
|
|
local noise_generator = Utils.noise_generator(noise_params, seed)
|
|
|
|
for y = 0.5, 31.5, 1 do
|
|
for x = 0.5, 31.5, 1 do
|
|
local p = { x = chunk_left_top.x + x, y = chunk_left_top.y + y }
|
|
|
|
if p.x >= -width / 2 and p.y >= -height / 2 and p.x <= width / 2 and p.y <= height / 2 then
|
|
terrain_fn({
|
|
p = Utils.psum({ p, { 1, terraingen_coordinates_offset } }),
|
|
true_p = p,
|
|
true_left_top = chunk_left_top,
|
|
left_top = Utils.psum({ chunk_left_top, { 1, terraingen_coordinates_offset } }),
|
|
noise_generator = noise_generator,
|
|
static_params = static_params,
|
|
tiles = tiles,
|
|
entities = entities,
|
|
decoratives = decoratives,
|
|
specials = specials,
|
|
seed = seed,
|
|
other_map_generation_data = other_map_generation_data,
|
|
iconized_generation = false,
|
|
overworldx = overworldx,
|
|
})
|
|
else
|
|
tiles[#tiles + 1] =
|
|
{ name = 'out-of-map', position = Utils.psum({ p, { 1, terraingen_coordinates_offset } }) }
|
|
end
|
|
end
|
|
end
|
|
|
|
if chunk_structures_fn then
|
|
chunk_structures_fn({
|
|
true_left_top = chunk_left_top,
|
|
left_top = Utils.psum({ chunk_left_top, { 1, terraingen_coordinates_offset } }),
|
|
noise_generator = noise_generator,
|
|
static_params = static_params,
|
|
specials = specials,
|
|
entities = entities,
|
|
seed = seed,
|
|
other_map_generation_data = other_map_generation_data,
|
|
biter_base_density_scale = Balance.biter_base_density_scale(),
|
|
})
|
|
end
|
|
|
|
local tiles_corrected = {}
|
|
for i = 1, #tiles do
|
|
local t = tiles[i]
|
|
t.position = Utils.psum({ t.position, { -1, terraingen_coordinates_offset } })
|
|
tiles_corrected[i] = t
|
|
end
|
|
local correct_tiles = true --tile borders etc
|
|
|
|
if #tiles_corrected > 0 then
|
|
surface.set_tiles(tiles_corrected, correct_tiles)
|
|
end
|
|
|
|
local destination = Common.current_destination()
|
|
|
|
if destination.dynamic_data then
|
|
if not destination.dynamic_data.structures_waiting_to_be_placed then
|
|
destination.dynamic_data.structures_waiting_to_be_placed = {}
|
|
end
|
|
|
|
-- to avoid having chests on water, add a landfill tile underneath them
|
|
local landfill_tiles = {}
|
|
|
|
for _, special in pairs(specials) do
|
|
-- recoordinatize:
|
|
special.position = Utils.psum({ special.position, { -1, terraingen_coordinates_offset } })
|
|
|
|
if special.name == 'buried-treasure' then
|
|
if destination.dynamic_data.buried_treasure and crewid ~= 0 then
|
|
destination.dynamic_data.buried_treasure[#destination.dynamic_data.buried_treasure + 1] =
|
|
{ treasure = Loot.buried_treasure_loot(), position = special.position }
|
|
end
|
|
elseif special.name == 'chest' then
|
|
local e = surface.create_entity({
|
|
name = 'wooden-chest',
|
|
position = special.position,
|
|
force = memory.ancient_friendly_force_name,
|
|
})
|
|
if e and e.valid then
|
|
e.minable = false
|
|
e.rotatable = false
|
|
e.destructible = false
|
|
|
|
local water_tiles = surface.find_tiles_filtered({
|
|
position = special.position,
|
|
radius = 0.1,
|
|
collision_mask = 'water_tile',
|
|
})
|
|
|
|
if water_tiles then
|
|
for _, t in pairs(water_tiles) do
|
|
landfill_tiles[#landfill_tiles + 1] = { name = 'landfill', position = t.position }
|
|
end
|
|
end
|
|
|
|
local inv = e.get_inventory(defines.inventory.chest)
|
|
local loot = Loot.wooden_chest_loot()
|
|
for i = 1, #loot do
|
|
local l = loot[i]
|
|
inv.insert(l)
|
|
end
|
|
end
|
|
elseif special.name == 'market' then
|
|
local e = surface.create_entity({
|
|
name = 'market',
|
|
position = special.position,
|
|
force = memory.ancient_friendly_force_name,
|
|
})
|
|
if e and e.valid then
|
|
e.minable = false
|
|
e.rotatable = false
|
|
e.destructible = false
|
|
|
|
for _, o in pairs(special.offers) do
|
|
e.add_market_item(o)
|
|
end
|
|
end
|
|
elseif
|
|
special.name == 'crash-site-spaceship-wreck-big-2'
|
|
or special.name == 'crash-site-spaceship-wreck-big-1'
|
|
then
|
|
local e = surface.create_entity({
|
|
name = special.name,
|
|
position = special.position,
|
|
force = memory.ancient_friendly_force_name,
|
|
})
|
|
if e and e.valid then
|
|
e.minable = false
|
|
e.rotatable = false
|
|
e.destructible = false
|
|
|
|
local inv = e.get_inventory(defines.inventory.chest)
|
|
|
|
local loot = Loot.iron_chest_loot()
|
|
|
|
for i = 1, #loot do
|
|
local l = loot[i]
|
|
inv.insert(l)
|
|
end
|
|
end
|
|
end
|
|
|
|
if special.components then
|
|
destination.dynamic_data.structures_waiting_to_be_placed[#destination.dynamic_data.structures_waiting_to_be_placed + 1] =
|
|
{ data = special, tick = game.tick }
|
|
end
|
|
end
|
|
|
|
if #landfill_tiles > 0 then
|
|
surface.set_tiles(landfill_tiles, true, false, false)
|
|
end
|
|
end
|
|
|
|
for i = 1, #entities do
|
|
local e = entities[i]
|
|
e.position = Utils.psum({ e.position, { -1, terraingen_coordinates_offset } })
|
|
local e2 = e
|
|
-- e2.build_check_type = defines.build_check_type.ghost_revive
|
|
-- log(_inspect(e2))
|
|
|
|
-- Allow placing worms in water in walkways
|
|
-- NOTE: Tile check there is to prevent worms from spawning outside island
|
|
if
|
|
surface.can_place_entity(e2)
|
|
or (
|
|
destination.subtype == IslandEnum.enum.WALKWAYS
|
|
and string.sub(e.name, -11) == 'worm-turret'
|
|
and surface.get_tile(e.position.x, e.position.y).name == 'water-shallow'
|
|
)
|
|
then
|
|
local ee = surface.create_entity(e)
|
|
if e.indestructible then
|
|
ee.destructible = false
|
|
end
|
|
end
|
|
end
|
|
|
|
local decoratives_corrected = {}
|
|
for i = 1, #decoratives do
|
|
local d = decoratives[i]
|
|
d.position = Utils.psum({ d.position, { -1, terraingen_coordinates_offset } })
|
|
decoratives_corrected[i] = d
|
|
end
|
|
if #decoratives_corrected > 0 then
|
|
surface.create_decoratives({ decoratives = decoratives_corrected })
|
|
end
|
|
end
|
|
|
|
local function event_on_rocket_launched(event)
|
|
-- figure out which crew this is about:
|
|
local crew_id = Common.get_id_from_force_name(event.rocket.force.name)
|
|
Memory.set_working_id(crew_id)
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
|
|
local rocket_launched_belongs_to_island = false
|
|
if destination.dynamic_data.rocketsilos then
|
|
for i = 1, #destination.dynamic_data.rocketsilos do
|
|
if event.rocket_silo == destination.dynamic_data.rocketsilos[i] then
|
|
rocket_launched_belongs_to_island = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
-- We don't want to do anything if rocket was launched by silo that doesn't belong to island
|
|
-- NOTE: On rare occasions if rocket was launched but the silo died in the meantime, this will not give rewards to the crew (idk how to fix it though)
|
|
if not rocket_launched_belongs_to_island then
|
|
return
|
|
end
|
|
|
|
local rocket_launch_coal_reward = Balance.rocket_launch_fuel_reward()
|
|
local rocket_launch_coin_reward = Balance.rocket_launch_coin_reward()
|
|
|
|
destination.dynamic_data.rocket_launched = true
|
|
if memory.stored_fuel then
|
|
memory.stored_fuel = memory.stored_fuel + rocket_launch_coal_reward
|
|
Common.give_items_to_crew({ { name = 'coin', count = rocket_launch_coin_reward } })
|
|
memory.playtesting_stats.coins_gained_by_rocket_launches = memory.playtesting_stats.coins_gained_by_rocket_launches
|
|
+ rocket_launch_coin_reward
|
|
end
|
|
|
|
local force = memory.force
|
|
local message = {
|
|
'pirates.granted_2',
|
|
{ 'pirates.granted_rocket_launch' },
|
|
Math.floor(rocket_launch_coin_reward / 100) / 10 .. 'k [item=coin]',
|
|
Math.floor(rocket_launch_coal_reward / 100) / 10 .. 'k [item=coal]',
|
|
}
|
|
Common.notify_force_light(force, message)
|
|
|
|
if destination.dynamic_data.quest_type == Quest.enum.TIME and not destination.dynamic_data.quest_complete then
|
|
destination.dynamic_data.quest_progressneeded = 1
|
|
Quest.try_resolve_quest()
|
|
end
|
|
|
|
if destination.dynamic_data.quest_type == Quest.enum.NODAMAGE and not destination.dynamic_data.quest_complete then
|
|
destination.dynamic_data.quest_progress = destination.dynamic_data.rocketsilohp
|
|
Quest.try_resolve_quest()
|
|
end
|
|
|
|
if destination.dynamic_data.rocketsilos then
|
|
for i = 1, #destination.dynamic_data.rocketsilos do
|
|
local s = destination.dynamic_data.rocketsilos[i]
|
|
if s and s.valid then
|
|
s.destructible = true
|
|
s.die()
|
|
end
|
|
end
|
|
destination.dynamic_data.rocketsilos = nil
|
|
end
|
|
end
|
|
|
|
local function event_on_built_entity(event)
|
|
local entity = event.entity
|
|
if not entity then
|
|
return
|
|
end
|
|
if not entity.valid then
|
|
return
|
|
end
|
|
|
|
if not event.player_index then
|
|
return
|
|
end
|
|
|
|
local player = game.players[event.player_index]
|
|
|
|
if not Common.validate_player_and_character(player) then
|
|
return
|
|
end
|
|
|
|
local crew_id = Common.get_id_from_force_name(player.force.name)
|
|
Memory.set_working_id(crew_id)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
if entity.name == 'land-mine' then
|
|
memory.players_to_last_landmine_placement_tick = memory.players_to_last_landmine_placement_tick or {}
|
|
memory.players_to_last_landmine_placement_tick[player.index] = game.tick
|
|
end
|
|
|
|
if
|
|
memory.boat
|
|
and memory.boat.surface_name
|
|
and player.character.surface == game.surfaces[memory.boat.surface_name]
|
|
and entity.valid
|
|
and entity.position
|
|
then
|
|
if
|
|
(entity.type and (entity.type == 'underground-belt'))
|
|
or (entity.name == 'entity-ghost' and entity.ghost_type and (entity.ghost_type == 'underground-belt'))
|
|
then
|
|
if Boats.on_boat(memory.boat, entity.position) then
|
|
-- if (entity.type and (entity.type == 'underground-belt' or entity.type == 'pipe-to-ground')) or (entity.name == 'entity-ghost' and entity.ghost_type and (entity.ghost_type == 'underground-belt' or entity.ghost_type == 'pipe-to-ground')) then
|
|
if not (entity.name and entity.name == 'entity-ghost') then
|
|
player.insert({ name = entity.name, count = 1 })
|
|
end
|
|
entity.destroy()
|
|
Common.notify_player_error(player, { 'pirates.error_build_undergrounds_on_boat' })
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
-- hanas code for selective spidertrons:
|
|
-- local objective = Chrono_table.get_table()
|
|
-- if entity.name == 'spidertron' then
|
|
-- if objective.world.id ~= 7 or entity.surface.name == 'cargo_wagon' then
|
|
-- entity.destroy()
|
|
-- local player = game.players[event.player_index]
|
|
-- Alert.alert_player_warning(player, 8, {'chronosphere.spidertron_not_allowed'})
|
|
-- player.insert({name = 'spidertron', count = 1})
|
|
-- end
|
|
-- end
|
|
end
|
|
|
|
local function event_on_console_chat(event)
|
|
if not (event.message and event.player_index) then
|
|
return
|
|
end
|
|
|
|
local player = game.players[event.player_index]
|
|
if not (player and player.valid) then
|
|
return
|
|
end
|
|
|
|
local global_memory = Memory.get_global_memory()
|
|
local tag = player.tag or ''
|
|
local color = player.chat_color
|
|
|
|
local crew_id = Common.get_id_from_force_name(player.force.name)
|
|
Memory.set_working_id(crew_id)
|
|
local memory = Memory.get_crew_memory()
|
|
|
|
local message_prefix = player.name .. tag
|
|
local full_message = message_prefix .. ': ' .. event.message
|
|
|
|
if player.force.name == Common.lobby_force_name then
|
|
for _, index in pairs(global_memory.crew_active_ids) do
|
|
local recipient_force_name = global_memory.crew_memories[index].force_name
|
|
game.forces[recipient_force_name].print(message_prefix .. ' [LOBBY]: ' .. event.message, { color = color })
|
|
end
|
|
else
|
|
if memory.name then
|
|
full_message = message_prefix .. ' [' .. memory.name .. ']: ' .. event.message
|
|
end
|
|
game.forces.player.print(full_message, { color = color })
|
|
end
|
|
end
|
|
|
|
local function event_on_market_item_purchased(event)
|
|
Shop.event_on_market_item_purchased(event)
|
|
end
|
|
|
|
local remove_boost_movement_speed_on_respawn = Token.register(function(data)
|
|
local player = data.player
|
|
local crew_id = data.crew_id
|
|
if not (player and player.valid) then
|
|
return
|
|
end
|
|
|
|
-- their color was strobing, so now reset it to their chat color:
|
|
player.color = player.chat_color
|
|
|
|
Memory.set_working_id(crew_id)
|
|
local memory = Memory.get_crew_memory()
|
|
if not Common.is_id_valid(memory.id) then
|
|
return
|
|
end --check if crew disbanded
|
|
if memory.game_lost then
|
|
return
|
|
end
|
|
memory.speed_boost_characters[player.index] = nil
|
|
|
|
Common.notify_player_expected(player, { 'pirates.respawn_speed_bonus_removed' })
|
|
end)
|
|
|
|
local boost_movement_speed_on_respawn = Token.register(function(data)
|
|
local player = data.player
|
|
local crew_id = data.crew_id
|
|
if not player or not player.valid then
|
|
return
|
|
end
|
|
|
|
Memory.set_working_id(crew_id)
|
|
local memory = Memory.get_crew_memory()
|
|
if not Common.is_id_valid(memory.id) then
|
|
return
|
|
end --check if crew disbanded
|
|
if memory.game_lost then
|
|
return
|
|
end
|
|
memory.speed_boost_characters[player.index] = true
|
|
|
|
Task.set_timeout_in_ticks(1200, remove_boost_movement_speed_on_respawn, { player = player, crew_id = crew_id })
|
|
Common.notify_player_expected(player, { 'pirates.respawn_speed_bonus_applied' })
|
|
end)
|
|
|
|
local function event_on_player_respawned(event)
|
|
local player = game.players[event.player_index]
|
|
|
|
if not Common.validate_player_and_character(player) then
|
|
return
|
|
end
|
|
|
|
local crew_id = Common.get_id_from_force_name(player.force.name)
|
|
|
|
Memory.set_working_id(crew_id)
|
|
local memory = Memory.get_crew_memory()
|
|
local boat = memory.boat
|
|
|
|
if player.character.surface == game.surfaces[Common.current_destination().surface_name] then
|
|
if Boats.is_boat_at_sea() then
|
|
-- assuming sea is always default:
|
|
local seasurface = game.surfaces[memory.sea_name]
|
|
player.character.teleport(memory.spawnpoint, seasurface)
|
|
elseif boat and (boat.state == Boats.enum_state.LANDED or boat.state == Boats.enum_state.RETREATING) then
|
|
Task.set_timeout_in_ticks(360, boost_movement_speed_on_respawn, { player = player, crew_id = crew_id })
|
|
|
|
local global_memory = Memory.get_global_memory()
|
|
global_memory.last_players_health[event.player_index] = player.character.health
|
|
end
|
|
end
|
|
end
|
|
|
|
local function event_on_entity_spawned(event)
|
|
local entity = event.entity
|
|
if not entity then
|
|
return
|
|
end
|
|
if not entity.valid then
|
|
return
|
|
end
|
|
|
|
local surface = entity.surface
|
|
if not surface then
|
|
return
|
|
end
|
|
if not surface.valid then
|
|
return
|
|
end
|
|
|
|
local crew_id = SurfacesCommon.decode_surface_name(surface.name).crewid
|
|
if not Common.is_id_valid(crew_id) then
|
|
return
|
|
end
|
|
|
|
Memory.set_working_id(crew_id)
|
|
Common.try_make_biter_elite(entity)
|
|
end
|
|
|
|
local function event_on_gui_opened(event)
|
|
-- If the object is a chest, close the gui
|
|
local entity = event.entity
|
|
if not entity then
|
|
return
|
|
end
|
|
if not entity.valid then
|
|
return
|
|
end
|
|
|
|
local player = game.players[event.player_index]
|
|
if not player then
|
|
return
|
|
end
|
|
if not player.valid then
|
|
return
|
|
end
|
|
|
|
if player.permission_group.name == 'cabin_privileged' then
|
|
if entity.name == 'red-chest' then
|
|
-- Even the captain has to wait for items to be removed from the red chests by loaders:
|
|
player.opened = nil
|
|
end
|
|
elseif player.permission_group.name == 'cabin' then
|
|
if
|
|
entity.name == 'wooden-chest'
|
|
or entity.name == 'iron-chest'
|
|
or entity.name == 'steel-chest'
|
|
or entity.name == 'red-chest'
|
|
or entity.name == 'blue-chest'
|
|
then
|
|
player.opened = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
local event = require('utils.event')
|
|
event.add(defines.events.on_built_entity, event_on_built_entity)
|
|
event.add(defines.events.on_entity_damaged, event_on_entity_damaged)
|
|
event.add(defines.events.on_entity_died, event_on_entity_died)
|
|
-- event.add(defines.events.on_player_repaired_entity, event_on_player_repaired_entity)
|
|
event.add(defines.events.on_player_joined_game, event_on_player_joined_game)
|
|
event.add(defines.events.on_pre_player_left_game, event_on_pre_player_left_game)
|
|
-- event.add(defines.events.on_player_left_game, event_on_player_left_game)
|
|
-- event.add(defines.events.on_pre_player_mined_item, event_pre_player_mined_item)
|
|
event.add(defines.events.on_player_mined_entity, event_on_player_mined_entity)
|
|
event.add(defines.events.on_research_finished, event_on_research_finished)
|
|
event.add(defines.events.on_player_changed_surface, on_player_changed_surface)
|
|
event.add(defines.events.on_player_driving_changed_state, event_on_player_driving_changed_state)
|
|
-- event.add(defines.events.on_player_changed_position, event_on_player_changed_position)
|
|
-- event.add(defines.events.on_technology_effects_reset, event_on_technology_effects_reset)
|
|
-- event.add(defines.events.on_chunk_generated, PiratesApiEvents.on_chunk_generated) --moved to main in order to make the debug properties clear
|
|
event.add(defines.events.on_rocket_launched, event_on_rocket_launched)
|
|
event.add(defines.events.on_console_chat, event_on_console_chat)
|
|
event.add(defines.events.on_market_item_purchased, event_on_market_item_purchased)
|
|
event.add(defines.events.on_player_respawned, event_on_player_respawned)
|
|
event.add(defines.events.on_entity_spawned, event_on_entity_spawned)
|
|
event.add(defines.events.on_gui_opened, event_on_gui_opened)
|
|
return Public
|