From 9bf24f665994fd79e60fed230006db9a525c92ae Mon Sep 17 00:00:00 2001 From: Gerkiz Date: Mon, 11 Apr 2022 21:13:33 +0200 Subject: [PATCH] fix minor issue with rpg --- modules/rpg/main.lua | 2542 ++++++++++++++++++++-------------------- modules/rpg/spells.lua | 1804 ++++++++++++++-------------- 2 files changed, 2173 insertions(+), 2173 deletions(-) diff --git a/modules/rpg/main.lua b/modules/rpg/main.lua index 4a0f9b13..2e652544 100644 --- a/modules/rpg/main.lua +++ b/modules/rpg/main.lua @@ -1,1271 +1,1271 @@ ---RPG Modules -local Public = require 'modules.rpg.core' -local Gui = require 'utils.gui' -local Event = require 'utils.event' -local AntiGrief = require 'utils.antigrief' -local SpamProtection = require 'utils.spam_protection' -local BiterHealthBooster = require 'modules.biter_health_booster_v2' -local Explosives = require 'modules.explosives' - -local WD = require 'modules.wave_defense.table' -local Math2D = require 'math2d' - ---RPG Settings -local enemy_types = Public.enemy_types -local die_cause = Public.die_cause -local points_per_level = Public.points_per_level -local nth_tick = Public.nth_tick - ---RPG Frames -local main_frame_name = Public.main_frame_name - -local sub = string.sub -local round = math.round -local floor = math.floor -local random = math.random -local sqrt = math.sqrt -local abs = math.abs - -local function log_aoe_punch(callback) - local debug = Public.get('rpg_extra').debug_aoe_punch - if not debug then - return - end - callback() -end - -local function on_gui_click(event) - if not event then - return - end - local player = game.players[event.player_index] - if not (player and player.valid) then - return - end - - if not event.element then - return - end - if not event.element.valid then - return - end - local element = event.element - if player.gui.screen[main_frame_name] then - local is_spamming = SpamProtection.is_spamming(player, nil, 'RPG Gui Click') - if is_spamming then - return - end - end - - local surface_name = Public.get('rpg_extra').surface_name - if sub(player.surface.name, 0, #surface_name) ~= surface_name then - return - end - - if element.type ~= 'sprite-button' then - return - end - - local shift = event.shift - - if element.caption ~= '✚' then - return - end - if element.sprite ~= 'virtual-signal/signal-red' then - return - end - - local rpg_t = Public.get_value_from_player(player.index) - - local index = element.name - if not rpg_t[index] then - return - end - if not player.character then - return - end - - if shift then - if event.button == defines.mouse_button_type.left then - local count = rpg_t.points_left - if not count then - return - end - rpg_t.points_left = 0 - rpg_t[index] = rpg_t[index] + count - if not rpg_t.reset then - rpg_t.total = rpg_t.total + count - end - Public.toggle(player, true) - Public.update_player_stats(player) - elseif event.button == defines.mouse_button_type.right then - local left = rpg_t.points_left / 2 - if left > 2 then - for _ = 1, left, 1 do - if rpg_t.points_left <= 0 then - Public.toggle(player, true) - return - end - rpg_t.points_left = rpg_t.points_left - 1 - rpg_t[index] = rpg_t[index] + 1 - if not rpg_t.reset then - rpg_t.total = rpg_t.total + 1 - end - Public.update_player_stats(player) - end - end - Public.toggle(player, true) - end - elseif event.button == defines.mouse_button_type.right then - for _ = 1, points_per_level, 1 do - if rpg_t.points_left <= 0 then - Public.toggle(player, true) - return - end - rpg_t.points_left = rpg_t.points_left - 1 - rpg_t[index] = rpg_t[index] + 1 - if not rpg_t.reset then - rpg_t.total = rpg_t.total + 1 - end - Public.update_player_stats(player) - end - Public.toggle(player, true) - return - end - - if rpg_t.points_left <= 0 then - Public.toggle(player, true) - return - end - rpg_t.points_left = rpg_t.points_left - 1 - rpg_t[index] = rpg_t[index] + 1 - if not rpg_t.reset then - rpg_t.total = rpg_t.total + 1 - end - Public.update_player_stats(player) - Public.toggle(player, true) -end - -local function train_type_cause(cause) - local players = {} - if cause.train.passengers then - for _, player in pairs(cause.train.passengers) do - players[#players + 1] = player - end - end - return players -end - -local get_cause_player = { - ['character'] = function(cause) - if not cause.player then - return - end - return {cause.player} - end, - ['combat-robot'] = function(cause) - if not cause.last_user then - return - end - if not game.players[cause.last_user.index] then - return - end - return {game.players[cause.last_user.index]} - end, - ['car'] = function(cause) - local players = {} - local driver = cause.get_driver() - if driver then - if driver.player then - players[#players + 1] = driver.player - end - end - local passenger = cause.get_passenger() - if passenger then - if passenger.player then - players[#players + 1] = passenger.player - end - end - return players - end, - ['spider-vehicle'] = function(cause) - local players = {} - local driver = cause.get_driver() - if driver then - if driver.player then - players[#players + 1] = driver.player - end - end - local passenger = cause.get_passenger() - if passenger then - if passenger.player then - players[#players + 1] = passenger.player - end - end - return players - end, - ['locomotive'] = train_type_cause, - ['cargo-wagon'] = train_type_cause, - ['artillery-wagon'] = train_type_cause, - ['fluid-wagon'] = train_type_cause -} - -local function on_entity_died(event) - if not event.entity or not event.entity.valid then - return - end - - local entity = event.entity - - --Grant XP for hand placed land mines - if entity.last_user then - if entity.type == 'land-mine' then - if event.cause then - if event.cause.valid then - if event.cause.force.index == entity.force.index then - return - end - end - end - Public.gain_xp(entity.last_user, 1) - Public.reward_mana(entity.last_user, 1) - return - end - end - - local rpg_extra = Public.get('rpg_extra') - - if rpg_extra.enable_wave_defense then - if rpg_extra.rpg_xp_yield['big-biter'] <= 16 then - local wave_number = WD.get_wave() - if wave_number >= 1000 then - rpg_extra.rpg_xp_yield['big-biter'] = 16 - rpg_extra.rpg_xp_yield['behemoth-biter'] = 64 - end - end - end - - local biter_health_boost = BiterHealthBooster.get('biter_health_boost') - local biter_health_boost_units = BiterHealthBooster.get('biter_health_boost_units') - - if not event.cause or not event.cause.valid then - return - end - - local cause = event.cause - local type = cause.type - if not type then - goto continue - end - - if cause.force.index == 1 then - if die_cause[type] then - if rpg_extra.rpg_xp_yield[entity.name] then - local amount = rpg_extra.rpg_xp_yield[entity.name] - amount = amount / 5 - if biter_health_boost then - local health_pool = biter_health_boost_units[entity.unit_number] - if health_pool then - amount = amount * (1 / health_pool[2]) - end - end - - if rpg_extra.turret_kills_to_global_pool then - Public.add_to_global_pool(amount, false) - end - else - Public.add_to_global_pool(0.5, false) - end - return - end - end - - ::continue:: - - if cause.force.index == entity.force.index then - return - end - - if not get_cause_player[cause.type] then - return - end - - local players = get_cause_player[cause.type](cause) - if not players then - return - end - if not players[1] then - return - end - - --Grant modified XP for health boosted units - if biter_health_boost then - if enemy_types[entity.type] then - local health_pool = biter_health_boost_units[entity.unit_number] - if health_pool then - for _, player in pairs(players) do - if entity.unit_number then - local mana_to_reward = random(1, 5) - if mana_to_reward > 1 then - Public.reward_mana(player, mana_to_reward) - end - end - if rpg_extra.rpg_xp_yield[entity.name] then - local amount = rpg_extra.rpg_xp_yield[entity.name] * (1 / health_pool[2]) - if amount < rpg_extra.rpg_xp_yield[entity.name] then - amount = rpg_extra.rpg_xp_yield[entity.name] - end - if rpg_extra.turret_kills_to_global_pool then - local inserted = Public.add_to_global_pool(amount, true) - Public.gain_xp(player, inserted, true) - else - Public.gain_xp(player, amount) - end - else - Public.gain_xp(player, 0.5 * (1 / health_pool[2])) - end - end - return - end - end - end - - --Grant normal XP - for _, player in pairs(players) do - if entity.unit_number then - local mana_to_reward = random(1, 5) - if mana_to_reward > 1 then - Public.reward_mana(player, mana_to_reward) - end - end - if rpg_extra.rpg_xp_yield[entity.name] then - local amount = rpg_extra.rpg_xp_yield[entity.name] - if rpg_extra.turret_kills_to_global_pool then - local inserted = Public.add_to_global_pool(amount, true) - Public.gain_xp(player, inserted, true) - else - Public.gain_xp(player, amount) - end - else - Public.gain_xp(player, 0.5) - end - end -end - -local function regen_health_player(players) - for i = 1, #players do - local player = players[i] - local heal_per_tick = Public.get_heal_modifier(player) - if heal_per_tick <= 0 then - goto continue - end - heal_per_tick = round(heal_per_tick) - if player and player.valid and not player.in_combat then - if player.character and player.character.valid then - player.character.health = player.character.health + heal_per_tick - end - end - - ::continue:: - - Public.update_health(player) - end -end - -local function regen_mana_player(players) - for i = 1, #players do - local player = players[i] - local mana_per_tick = Public.get_mana_modifier(player) - local rpg_extra = Public.get('rpg_extra') - local rpg_t = Public.get_value_from_player(player.index) - if mana_per_tick <= 0.1 then - mana_per_tick = rpg_extra.mana_per_tick - end - - if rpg_extra.force_mana_per_tick then - mana_per_tick = 1 - end - - if player and player.valid and not player.in_combat then - if player.character and player.character.valid then - if rpg_t.mana < 0 then - rpg_t.mana = 0 - end - if rpg_t.mana >= rpg_t.mana_max then - goto continue - end - rpg_t.mana = rpg_t.mana + mana_per_tick - if rpg_t.mana >= rpg_t.mana_max then - rpg_t.mana = rpg_t.mana_max - end - rpg_t.mana = (round(rpg_t.mana * 10) / 10) - end - end - - ::continue:: - - Public.update_mana(player) - end -end - -local function give_player_flameboots(player) - local rpg_t = Public.get_value_from_player(player.index) - if not rpg_t then - return - end - - if not rpg_t.flame_boots then - return - end - - if not player.character then - return - end - if player.character.driving then - return - end - - if not rpg_t.mana then - return - end - - if rpg_t.mana <= 0 then - player.print(({'rpg_main.flame_boots_worn_out'}), {r = 0.22, g = 0.77, b = 0.44}) - rpg_t.flame_boots = false - return - end - - if rpg_t.mana % 500 == 0 then - player.print(({'rpg_main.flame_mana_remaining', rpg_t.mana}), {r = 0.22, g = 0.77, b = 0.44}) - end - - local p = player.position - - player.surface.create_entity({name = 'fire-flame', position = p}) - - rpg_t.mana = rpg_t.mana - 5 - if rpg_t.mana <= 0 then - rpg_t.mana = 0 - end - if player.gui.screen[main_frame_name] then - local f = player.gui.screen[main_frame_name] - local data = Gui.get_data(f) - if data.mana and data.mana.valid then - data.mana.caption = rpg_t.mana - end - end -end - -local function has_health_boost(entity, damage, final_damage_amount, cause) - local biter_health_boost = BiterHealthBooster.get('biter_health_boost') - local biter_health_boost_units = BiterHealthBooster.get('biter_health_boost_units') - - local get_health_pool - - --Handle the custom health pool of the biter health booster, if it is used in the map. - if biter_health_boost then - local health_pool = biter_health_boost_units[entity.unit_number] - if health_pool then - get_health_pool = health_pool[1] - --Set entity health relative to health pool - local max_health = health_pool[3].max_health - local m = health_pool[1] / max_health - local final_health = round(entity.prototype.max_health * m) - - health_pool[1] = round(health_pool[1] + final_damage_amount) - health_pool[1] = round(health_pool[1] - damage) - - --Set entity health relative to health pool - entity.health = final_health - - if health_pool[1] <= 0 then - local entity_number = entity.unit_number - entity.die(entity.force.name, cause) - - if biter_health_boost_units[entity_number] then - biter_health_boost_units[entity_number] = nil - end - end - else - entity.health = entity.health + final_damage_amount - entity.health = entity.health - damage - if entity.health <= 0 then - entity.die(cause.force.name, cause) - end - end - else - --Handle vanilla damage. - entity.health = entity.health + final_damage_amount - entity.health = entity.health - damage - if entity.health <= 0 then - entity.die(cause.force.name, cause) - end - end - - return get_health_pool -end - -local function set_health_boost(entity, damage, cause) - local biter_health_boost = BiterHealthBooster.get('biter_health_boost') - local biter_health_boost_units = BiterHealthBooster.get('biter_health_boost_units') - - local get_health_pool - - --Handle the custom health pool of the biter health booster, if it is used in the map. - if biter_health_boost then - local health_pool = biter_health_boost_units[entity.unit_number] - if health_pool then - get_health_pool = health_pool[1] - --Set entity health relative to health pool - local max_health = health_pool[3].max_health - local m = health_pool[1] / max_health - local final_health = round(entity.prototype.max_health * m) - - health_pool[1] = round(health_pool[1] - damage) - - --Set entity health relative to health pool - entity.health = final_health - - if health_pool[1] <= 0 then - local entity_number = entity.unit_number - entity.die(entity.force.name, cause) - - if biter_health_boost_units[entity_number] then - biter_health_boost_units[entity_number] = nil - end - end - end - end - - return get_health_pool -end - ---Melee damage modifier -local function aoe_punch(character, target, damage, get_health_pool) - if not (target and target.valid) then - return - end - - local base_vector = {target.position.x - character.position.x, target.position.y - character.position.y} - - local vector = {base_vector[1], base_vector[2]} - vector[1] = vector[1] * 1000 - vector[2] = vector[2] * 1000 - - character.surface.create_entity( - { - name = 'flying-text', - position = {character.position.x + base_vector[1] * 0.5, character.position.y + base_vector[2] * 0.5}, - text = ({'rpg_main.aoe_punch_text'}), - color = {255, 0, 0} - } - ) - character.surface.create_entity({name = 'blood-explosion-huge', position = target.position}) - character.surface.create_entity( - { - name = 'big-artillery-explosion', - position = {target.position.x + vector[1] * 0.5, target.position.y + vector[2] * 0.5} - } - ) - - if abs(vector[1]) > abs(vector[2]) then - local d = abs(vector[1]) - if abs(vector[1]) > 0 then - vector[1] = vector[1] / d - end - if abs(vector[2]) > 0 then - vector[2] = vector[2] / d - end - else - local d = abs(vector[2]) - if abs(vector[2]) > 0 then - vector[2] = vector[2] / d - end - if abs(vector[1]) > 0 and d > 0 then - vector[1] = vector[1] / d - end - end - - vector[1] = vector[1] * 1.5 - vector[2] = vector[2] * 1.5 - - local a = 0.20 - - local cs = character.surface - local cp = character.position - - for i = 1, 16, 1 do - for x = i * -1 * a, i * a, 1 do - for y = i * -1 * a, i * a, 1 do - local p = {cp.x + x + vector[1] * i, cp.y + y + vector[2] * i} - cs.create_trivial_smoke({name = 'train-smoke', position = p}) - for _, e in pairs(cs.find_entities({{p[1] - a, p[2] - a}, {p[1] + a, p[2] + a}})) do - if e.valid then - if e.health then - if e.destructible and e.minable and e.force.index ~= 3 then - if e.force.index ~= character.force.index then - if get_health_pool then - local max_unit_health = floor(get_health_pool * 0.00015) - if max_unit_health <= 0 then - max_unit_health = 4 - end - if max_unit_health >= 10 then - max_unit_health = 10 - end - local final = floor(damage * max_unit_health) - set_health_boost(e, final, character) - if e.valid and e.health <= 0 and get_health_pool <= 0 then - e.die(e.force.name, character) - end - else - if e.valid then - e.health = e.health - damage * 0.05 - if e.health <= 0 then - e.die(e.force.name, character) - end - end - end - end - end - end - end - end - end - end - end -end - -local function is_position_near(area, entity) - local status = false - - local function inside(pos) - local lt = area.left_top - local rb = area.right_bottom - - return pos.x >= lt.x and pos.y >= lt.y and pos.x <= rb.x and pos.y <= rb.y - end - - if inside(entity, area) then - status = true - end - - return status -end - -local function on_entity_damaged(event) - if not event.cause then - return - end - if not event.cause.valid then - return - end - if event.cause.force.index == 2 then - return - end - if event.cause.name ~= 'character' then - return - end - - if event.damage_type.name ~= 'physical' then - return - end - - if not event.entity.valid then - return - end - - local entity = event.entity - local cause = event.cause - local original_damage_amount = event.original_damage_amount - local final_damage_amount = event.final_damage_amount - - if cause.get_inventory(defines.inventory.character_ammo)[cause.selected_gun_index].valid_for_read or cause.get_inventory(defines.inventory.character_guns)[cause.selected_gun_index].valid_for_read then - local is_explosive_bullets_enabled = Public.get_explosive_bullets() - if is_explosive_bullets_enabled then - Public.explosive_bullets(event) - end - return - end - if not cause.player then - return - end - - local p = cause.player - - local surface_name = Public.get('rpg_extra').surface_name - if sub(p.surface.name, 0, #surface_name) ~= surface_name then - return - end - - if entity.force.index == cause.force.index then - return - end - - local position = p.position - - local area = { - left_top = {x = position.x - 5, y = position.y - 5}, - right_bottom = {x = position.x + 5, y = position.y + 5} - } - - if not is_position_near(area, entity.position) then - return - end - - local item = p.cursor_stack - - if item and item.valid_for_read then - if item.name == 'discharge-defense-remote' then - return - end - end - - Public.reward_mana(cause.player, 2) - - --Grant the player life-on-hit. - cause.health = cause.health + Public.get_life_on_hit(cause.player) - - --Calculate modified damage. - local damage = Public.get_final_damage(cause.player, entity, original_damage_amount) - local enable_aoe_punch = Public.get('rpg_extra').enable_aoe_punch - local rpg_t = Public.get_value_from_player(cause.player.index) - - --Floating messages and particle effects. - if random(1, 7) == 1 then - damage = damage * random(250, 350) * 0.01 - cause.surface.create_entity( - { - name = 'flying-text', - position = entity.position, - text = '‼' .. floor(damage), - color = {255, 0, 0} - } - ) - cause.surface.create_entity({name = 'blood-explosion-huge', position = entity.position}) - else - damage = damage * random(100, 125) * 0.01 - cause.player.create_local_flying_text( - { - text = floor(damage), - position = entity.position, - color = {150, 150, 150}, - time_to_live = 90, - speed = 2 - } - ) - end - - local get_health_pool = has_health_boost(entity, damage, final_damage_amount, cause) - - --Cause a one punch. - if enable_aoe_punch then - if rpg_t.aoe_punch then - local chance = Public.get_aoe_punch_chance(cause.player) * 10 - local chance_to_hit = random(0, 999) - local success = chance_to_hit < chance - log_aoe_punch( - function() - if success then - print('[OnePunch]: Chance: ' .. chance .. ' Chance to hit: ' .. chance_to_hit .. ' Success: true' .. ' Damage: ' .. damage) - else - print('[OnePunch]: Chance: ' .. chance .. ' Chance to hit: ' .. chance_to_hit .. ' Success: false' .. ' Damage: ' .. damage) - end - end - ) - if success then - aoe_punch(cause, entity, damage, get_health_pool) -- only kill the biters if their health is below or equal to zero - return - end - end - end - - local is_explosive_bullets_enabled = Public.get_explosive_bullets() - if is_explosive_bullets_enabled then - Public.explosive_bullets(event) - end -end - -local function on_player_repaired_entity(event) - if random(1, 4) ~= 1 then - return - end - - local entity = event.entity - - if not entity then - return - end - - if not entity.valid then - return - end - - if not entity.health then - return - end - - local player = game.players[event.player_index] - - if not player or not player.valid or not player.character then - return - end - - Public.gain_xp(player, 0.05) - Public.reward_mana(player, 0.2) - - local repair_speed = Public.get_magicka(player) - if repair_speed <= 0 then - return - end - entity.health = entity.health + repair_speed -end - -local function on_player_rotated_entity(event) - local player = game.players[event.player_index] - - if not player or not player.valid then - return - end - if not player.character then - return - end - - local rpg_t = Public.get_value_from_player(player.index) - if rpg_t.rotated_entity_delay > game.tick then - return - end - rpg_t.rotated_entity_delay = game.tick + 20 - Public.gain_xp(player, 0.20) -end - -local function on_player_changed_position(event) - local player = game.players[event.player_index] - if not player or not player.valid then - return - end - - local enable_flame_boots = Public.get('rpg_extra').enable_flame_boots - - if enable_flame_boots then - give_player_flameboots(player) - end - - if random(1, 64) ~= 1 then - return - end - if not player.character then - return - end - if player.character.driving then - return - end - Public.gain_xp(player, 1.0) -end - -local building_and_mining_blacklist = { - ['tile-ghost'] = true, - ['entity-ghost'] = true, - ['item-entity'] = true -} - -local function on_player_died(event) - local player = game.players[event.player_index] - - if not player or not player.valid then - return - end - - Public.remove_frame(player) -end - -local function on_pre_player_left_game(event) - local player = game.players[event.player_index] - - if not player or not player.valid then - return - end - - Public.remove_frame(player) -end - -local function on_pre_player_mined_item(event) - local entity = event.entity - if not entity.valid then - return - end - if building_and_mining_blacklist[entity.type] then - return - end - if entity.force.index ~= 3 then - return - end - local player = game.players[event.player_index] - - if not player or not player.valid then - return - end - - local surface_name = Public.get('rpg_extra').surface_name - if sub(player.surface.name, 0, #surface_name) ~= surface_name then - return - end - - local rpg_t = Public.get_value_from_player(player.index) - if rpg_t.last_mined_entity_position.x == entity.position.x and rpg_t.last_mined_entity_position.y == entity.position.y then - return - end - rpg_t.last_mined_entity_position.x = entity.position.x - rpg_t.last_mined_entity_position.y = entity.position.y - - local distance_multiplier = floor(sqrt(entity.position.x ^ 2 + entity.position.y ^ 2)) * 0.0005 + 1 - - local xp_modifier_when_mining = Public.get('rpg_extra').xp_modifier_when_mining - - local xp_amount - if entity.type == 'resource' then - xp_amount = 0.9 * distance_multiplier - else - xp_amount = (1.5 + entity.prototype.max_health * xp_modifier_when_mining) * distance_multiplier - end - - if player.gui.screen[main_frame_name] then - local f = player.gui.screen[main_frame_name] - local data = Gui.get_data(f) - if data.exp_gui and data.exp_gui.valid then - data.exp_gui.caption = floor(rpg_t.xp) - end - end - - Public.gain_xp(player, xp_amount) - Public.reward_mana(player, 0.5 * distance_multiplier) -end - -local function on_player_crafted_item(event) - if not event.recipe.energy then - return - end - local player = game.players[event.player_index] - if not player or not player.valid then - return - end - - if player.cheat_mode then - return - end - - local rpg_extra = Public.get('rpg_extra') - local is_blacklisted = rpg_extra.tweaked_crafting_items - local tweaked_crafting_items_enabled = rpg_extra.tweaked_crafting_items_enabled - - local item = event.item_stack - - local amount = 0.40 * random(1, 2) - local recipe = event.recipe - - if tweaked_crafting_items_enabled then - if item and item.valid then - if is_blacklisted[item.name] then - amount = 0.2 - end - end - end - - local final_xp = recipe.energy * amount - - Public.gain_xp(player, final_xp) - Public.reward_mana(player, amount) -end - -local function on_player_respawned(event) - local player = game.players[event.player_index] - local rpg_t = Public.get_value_from_player(player.index) - if not rpg_t then - Public.rpg_reset_player(player) - return - end - Public.update_player_stats(player) - Public.draw_level_text(player) - Public.update_health(player) - Public.update_mana(player) -end - -local function on_player_joined_game(event) - local player = game.players[event.player_index] - local rpg_t = Public.get_value_from_player(player.index) - local rpg_extra = Public.get('rpg_extra') - if not rpg_t then - Public.rpg_reset_player(player) - if rpg_extra.reward_new_players > 10 then - Public.gain_xp(player, rpg_extra.reward_new_players) - end - end - for _, p in pairs(game.connected_players) do - Public.draw_level_text(p) - end - Public.draw_gui_char_button(player) - if not player.character then - return - end - Public.update_player_stats(player) -end - -local function get_near_coord_modifier(range) - local coord = {x = (range * -1) + random(0, range * 2), y = (range * -1) + random(0, range * 2)} - for i = 1, 5, 1 do - local new_coord = {x = (range * -1) + random(0, range * 2), y = (range * -1) + random(0, range * 2)} - if new_coord.x ^ 2 + new_coord.y ^ 2 < coord.x ^ 2 + coord.y ^ 2 then - coord = new_coord - end - end - return coord -end - -local function damage_entity(e) - if not e or not e.valid then - return - end - - if not e.health then - return - end - - if e.force.name == 'player' then - return - end - - if not e.destructible then - return - end - - e.surface.create_entity({name = 'ground-explosion', position = e.position}) - - if e.type == 'entity-ghost' then - e.destroy() - return - end - - e.health = e.health - random(30, 90) - if e.health <= 0 then - e.die('enemy') - end -end - -local function floaty_hearts(entity, c) - local position = {x = entity.position.x - 0.75, y = entity.position.y - 1} - local b = 1.35 - for _ = 1, c, 1 do - local p = { - (position.x + 0.4) + (b * -1 + random(0, b * 20) * 0.1), - position.y + (b * -1 + random(0, b * 20) * 0.1) - } - entity.surface.create_entity({name = 'flying-text', position = p, text = '♥', color = {random(150, 255), 0, 255}}) - end -end - -local function tame_unit_effects(player, entity) - floaty_hearts(entity, 7) - - rendering.draw_text { - text = '~' .. player.name .. "'s pet~", - surface = player.surface, - target = entity, - target_offset = {0, -2.6}, - color = { - r = player.color.r * 0.6 + 0.25, - g = player.color.g * 0.6 + 0.25, - b = player.color.b * 0.6 + 0.25, - a = 1 - }, - scale = 1.05, - font = 'default-large-semibold', - alignment = 'center', - scale_with_zoom = false - } -end - -local function on_player_used_capsule(event) - local enable_mana = Public.get('rpg_extra').enable_mana - local surface_name = Public.get('rpg_extra').surface_name - if not enable_mana then - return - end - - local conjure_items = Public.spells - local projectile_types = Public.get_projectiles - - local player = game.players[event.player_index] - if not player or not player.valid then - return - end - - if not player.character or not player.character.valid then - return - end - - if sub(player.surface.name, 0, #surface_name) ~= surface_name then - return - end - - local item = event.item - - if not item then - return - end - - local name = item.name - - if name ~= 'raw-fish' then - return - end - - Public.get_heal_modifier_from_using_fish(player) - - local rpg_t = Public.get_value_from_player(player.index) - - if not rpg_t.enable_entity_spawn then - return - end - - if rpg_t.last_spawned >= game.tick then - return Public.cast_spell(player, true) - end - - local mana = rpg_t.mana - local surface = player.surface - - local object = conjure_items[rpg_t.dropdown_select_index] - if not object then - return - end - - local position = event.position - if not position then - return - end - - local radius = 15 - local area = { - left_top = {x = position.x - radius, y = position.y - radius}, - right_bottom = {x = position.x + radius, y = position.y + radius} - } - - if rpg_t.level < object.level then - return Public.cast_spell(player, true) - end - - if not object.enabled then - return - end - - if not Math2D.bounding_box.contains_point(area, player.position) then - Public.cast_spell(player, true) - return - end - - if mana < object.mana_cost then - return Public.cast_spell(player, true) - end - - local target_pos - if object.target then - target_pos = {position.x, position.y} - elseif projectile_types[object.entityName] then - local coord_modifier = get_near_coord_modifier(projectile_types[object.entityName].max_range) - target_pos = {position.x + coord_modifier.x, position.y + coord_modifier.y} - end - - local range - if object.range then - range = object.range - else - range = 0 - end - - local force - if object.force then - force = object.force - else - force = 'player' - end - - local data = { - self = object, - player = player, - damage_entity = damage_entity, - position = position, - surface = surface, - force = force, - target_pos = target_pos, - range = range, - mana = rpg_t.mana, - tame_unit_effects = tame_unit_effects, - explosives = Explosives - } - - object.callback(data) - - local msg = player.name .. ' casted ' .. object.entityName .. '. ' - - rpg_t.last_spawned = game.tick + object.tick - Public.update_mana(player) - - local reward_xp = object.mana_cost * 0.085 - if reward_xp < 1 then - reward_xp = 1 - end - - Public.gain_xp(player, reward_xp) - - AntiGrief.insert_into_capsule_history(player, position, msg) -end - -local function on_player_changed_surface(event) - local player = game.get_player(event.player_index) - Public.draw_level_text(player) -end - -local function on_player_removed(event) - Public.remove_player(event.player_index) -end - -local function tick() - local ticker = game.tick - local players = game.connected_players - local count = #players - local enable_flameboots = Public.get('rpg_extra').enable_flameboots - local enable_mana = Public.get('rpg_extra').enable_mana - - if ticker % nth_tick == 0 then - Public.global_pool(players, count) - end - - if ticker % 30 == 0 then - regen_health_player(players) - if enable_mana then - regen_mana_player(players) - end - if enable_flameboots then - give_player_flameboots(players) - end - end -end - -Event.add(defines.events.on_pre_player_left_game, on_pre_player_left_game) -Event.add(defines.events.on_player_died, on_player_died) -Event.add(defines.events.on_entity_damaged, on_entity_damaged) -Event.add(defines.events.on_entity_died, on_entity_died) -Event.add(defines.events.on_gui_click, on_gui_click) -Event.add(defines.events.on_player_changed_position, on_player_changed_position) -Event.add(defines.events.on_player_crafted_item, on_player_crafted_item) -Event.add(defines.events.on_player_joined_game, on_player_joined_game) -Event.add(defines.events.on_player_created, on_player_joined_game) -Event.add(defines.events.on_player_repaired_entity, on_player_repaired_entity) -Event.add(defines.events.on_player_respawned, on_player_respawned) -Event.add(defines.events.on_player_rotated_entity, on_player_rotated_entity) -Event.add(defines.events.on_pre_player_mined_item, on_pre_player_mined_item) -Event.add(defines.events.on_player_used_capsule, on_player_used_capsule) -Event.add(defines.events.on_player_changed_surface, on_player_changed_surface) -Event.add(defines.events.on_player_removed, on_player_removed) -Event.on_nth_tick(10, tick) - -return Public +--RPG Modules +local Public = require 'modules.rpg.core' +local Gui = require 'utils.gui' +local Event = require 'utils.event' +local AntiGrief = require 'utils.antigrief' +local SpamProtection = require 'utils.spam_protection' +local BiterHealthBooster = require 'modules.biter_health_booster_v2' +local Explosives = require 'modules.explosives' + +local WD = require 'modules.wave_defense.table' +local Math2D = require 'math2d' + +--RPG Settings +local enemy_types = Public.enemy_types +local die_cause = Public.die_cause +local points_per_level = Public.points_per_level +local nth_tick = Public.nth_tick + +--RPG Frames +local main_frame_name = Public.main_frame_name + +local sub = string.sub +local round = math.round +local floor = math.floor +local random = math.random +local sqrt = math.sqrt +local abs = math.abs + +local function log_aoe_punch(callback) + local debug = Public.get('rpg_extra').debug_aoe_punch + if not debug then + return + end + callback() +end + +local function on_gui_click(event) + if not event then + return + end + local player = game.players[event.player_index] + if not (player and player.valid) then + return + end + + if not event.element then + return + end + if not event.element.valid then + return + end + local element = event.element + if player.gui.screen[main_frame_name] then + local is_spamming = SpamProtection.is_spamming(player, nil, 'RPG Gui Click') + if is_spamming then + return + end + end + + local surface_name = Public.get('rpg_extra').surface_name + if sub(player.surface.name, 0, #surface_name) ~= surface_name then + return + end + + if element.type ~= 'sprite-button' then + return + end + + local shift = event.shift + + if element.caption ~= '✚' then + return + end + if element.sprite ~= 'virtual-signal/signal-red' then + return + end + + local rpg_t = Public.get_value_from_player(player.index) + + local index = element.name + if not rpg_t[index] then + return + end + if not player.character then + return + end + + if shift then + if event.button == defines.mouse_button_type.left then + local count = rpg_t.points_left + if not count then + return + end + rpg_t.points_left = 0 + rpg_t[index] = rpg_t[index] + count + if not rpg_t.reset then + rpg_t.total = rpg_t.total + count + end + Public.toggle(player, true) + Public.update_player_stats(player) + elseif event.button == defines.mouse_button_type.right then + local left = rpg_t.points_left / 2 + if left > 2 then + for _ = 1, left, 1 do + if rpg_t.points_left <= 0 then + Public.toggle(player, true) + return + end + rpg_t.points_left = rpg_t.points_left - 1 + rpg_t[index] = rpg_t[index] + 1 + if not rpg_t.reset then + rpg_t.total = rpg_t.total + 1 + end + Public.update_player_stats(player) + end + end + Public.toggle(player, true) + end + elseif event.button == defines.mouse_button_type.right then + for _ = 1, points_per_level, 1 do + if rpg_t.points_left <= 0 then + Public.toggle(player, true) + return + end + rpg_t.points_left = rpg_t.points_left - 1 + rpg_t[index] = rpg_t[index] + 1 + if not rpg_t.reset then + rpg_t.total = rpg_t.total + 1 + end + Public.update_player_stats(player) + end + Public.toggle(player, true) + return + end + + if rpg_t.points_left <= 0 then + Public.toggle(player, true) + return + end + rpg_t.points_left = rpg_t.points_left - 1 + rpg_t[index] = rpg_t[index] + 1 + if not rpg_t.reset then + rpg_t.total = rpg_t.total + 1 + end + Public.update_player_stats(player) + Public.toggle(player, true) +end + +local function train_type_cause(cause) + local players = {} + if cause.train.passengers then + for _, player in pairs(cause.train.passengers) do + players[#players + 1] = player + end + end + return players +end + +local get_cause_player = { + ['character'] = function(cause) + if not cause.player then + return + end + return {cause.player} + end, + ['combat-robot'] = function(cause) + if not cause.last_user then + return + end + if not game.players[cause.last_user.index] then + return + end + return {game.players[cause.last_user.index]} + end, + ['car'] = function(cause) + local players = {} + local driver = cause.get_driver() + if driver then + if driver.player then + players[#players + 1] = driver.player + end + end + local passenger = cause.get_passenger() + if passenger then + if passenger.player then + players[#players + 1] = passenger.player + end + end + return players + end, + ['spider-vehicle'] = function(cause) + local players = {} + local driver = cause.get_driver() + if driver then + if driver.player then + players[#players + 1] = driver.player + end + end + local passenger = cause.get_passenger() + if passenger then + if passenger.player then + players[#players + 1] = passenger.player + end + end + return players + end, + ['locomotive'] = train_type_cause, + ['cargo-wagon'] = train_type_cause, + ['artillery-wagon'] = train_type_cause, + ['fluid-wagon'] = train_type_cause +} + +local function on_entity_died(event) + if not event.entity or not event.entity.valid then + return + end + + local entity = event.entity + + --Grant XP for hand placed land mines + if entity.last_user then + if entity.type == 'land-mine' then + if event.cause then + if event.cause.valid then + if event.cause.force.index == entity.force.index then + return + end + end + end + Public.gain_xp(entity.last_user, 1) + Public.reward_mana(entity.last_user, 1) + return + end + end + + local rpg_extra = Public.get('rpg_extra') + + if rpg_extra.enable_wave_defense then + if rpg_extra.rpg_xp_yield['big-biter'] <= 16 then + local wave_number = WD.get_wave() + if wave_number >= 1000 then + rpg_extra.rpg_xp_yield['big-biter'] = 16 + rpg_extra.rpg_xp_yield['behemoth-biter'] = 64 + end + end + end + + local biter_health_boost = BiterHealthBooster.get('biter_health_boost') + local biter_health_boost_units = BiterHealthBooster.get('biter_health_boost_units') + + if not event.cause or not event.cause.valid then + return + end + + local cause = event.cause + local type = cause.type + if not type then + goto continue + end + + if cause.force.index == 1 then + if die_cause[type] then + if rpg_extra.rpg_xp_yield[entity.name] then + local amount = rpg_extra.rpg_xp_yield[entity.name] + amount = amount / 5 + if biter_health_boost then + local health_pool = biter_health_boost_units[entity.unit_number] + if health_pool then + amount = amount * (1 / health_pool[2]) + end + end + + if rpg_extra.turret_kills_to_global_pool then + Public.add_to_global_pool(amount, false) + end + else + Public.add_to_global_pool(0.5, false) + end + return + end + end + + ::continue:: + + if cause.force.index == entity.force.index then + return + end + + if not get_cause_player[cause.type] then + return + end + + local players = get_cause_player[cause.type](cause) + if not players then + return + end + if not players[1] then + return + end + + --Grant modified XP for health boosted units + if biter_health_boost then + if enemy_types[entity.type] then + local health_pool = biter_health_boost_units[entity.unit_number] + if health_pool then + for _, player in pairs(players) do + if entity.unit_number then + local mana_to_reward = random(1, 5) + if mana_to_reward > 1 then + Public.reward_mana(player, mana_to_reward) + end + end + if rpg_extra.rpg_xp_yield[entity.name] then + local amount = rpg_extra.rpg_xp_yield[entity.name] * (1 / health_pool[2]) + if amount < rpg_extra.rpg_xp_yield[entity.name] then + amount = rpg_extra.rpg_xp_yield[entity.name] + end + if rpg_extra.turret_kills_to_global_pool then + local inserted = Public.add_to_global_pool(amount, true) + Public.gain_xp(player, inserted, true) + else + Public.gain_xp(player, amount) + end + else + Public.gain_xp(player, 0.5 * (1 / health_pool[2])) + end + end + return + end + end + end + + --Grant normal XP + for _, player in pairs(players) do + if entity.unit_number then + local mana_to_reward = random(1, 5) + if mana_to_reward > 1 then + Public.reward_mana(player, mana_to_reward) + end + end + if rpg_extra.rpg_xp_yield[entity.name] then + local amount = rpg_extra.rpg_xp_yield[entity.name] + if rpg_extra.turret_kills_to_global_pool then + local inserted = Public.add_to_global_pool(amount, true) + Public.gain_xp(player, inserted, true) + else + Public.gain_xp(player, amount) + end + else + Public.gain_xp(player, 0.5) + end + end +end + +local function regen_health_player(players) + for i = 1, #players do + local player = players[i] + local heal_per_tick = Public.get_heal_modifier(player) + if heal_per_tick <= 0 then + goto continue + end + heal_per_tick = round(heal_per_tick) + if player and player.valid and not player.in_combat then + if player.character and player.character.valid then + player.character.health = player.character.health + heal_per_tick + end + end + + ::continue:: + + Public.update_health(player) + end +end + +local function regen_mana_player(players) + for i = 1, #players do + local player = players[i] + local mana_per_tick = Public.get_mana_modifier(player) + local rpg_extra = Public.get('rpg_extra') + local rpg_t = Public.get_value_from_player(player.index) + if mana_per_tick <= 0.1 then + mana_per_tick = rpg_extra.mana_per_tick + end + + if rpg_extra.force_mana_per_tick then + mana_per_tick = 1 + end + + if player and player.valid and not player.in_combat then + if player.character and player.character.valid then + if rpg_t.mana < 0 then + rpg_t.mana = 0 + end + if rpg_t.mana >= rpg_t.mana_max then + goto continue + end + rpg_t.mana = rpg_t.mana + mana_per_tick + if rpg_t.mana >= rpg_t.mana_max then + rpg_t.mana = rpg_t.mana_max + end + rpg_t.mana = (round(rpg_t.mana * 10) / 10) + end + end + + ::continue:: + + Public.update_mana(player) + end +end + +local function give_player_flameboots(player) + local rpg_t = Public.get_value_from_player(player.index) + if not rpg_t then + return + end + + if not rpg_t.flame_boots then + return + end + + if not player.character then + return + end + if player.character.driving then + return + end + + if not rpg_t.mana then + return + end + + if rpg_t.mana <= 0 then + player.print(({'rpg_main.flame_boots_worn_out'}), {r = 0.22, g = 0.77, b = 0.44}) + rpg_t.flame_boots = false + return + end + + if rpg_t.mana % 500 == 0 then + player.print(({'rpg_main.flame_mana_remaining', rpg_t.mana}), {r = 0.22, g = 0.77, b = 0.44}) + end + + local p = player.position + + player.surface.create_entity({name = 'fire-flame', position = p}) + + rpg_t.mana = rpg_t.mana - 5 + if rpg_t.mana <= 0 then + rpg_t.mana = 0 + end + if player.gui.screen[main_frame_name] then + local f = player.gui.screen[main_frame_name] + local data = Gui.get_data(f) + if data.mana and data.mana.valid then + data.mana.caption = rpg_t.mana + end + end +end + +local function has_health_boost(entity, damage, final_damage_amount, cause) + local biter_health_boost = BiterHealthBooster.get('biter_health_boost') + local biter_health_boost_units = BiterHealthBooster.get('biter_health_boost_units') + + local get_health_pool + + --Handle the custom health pool of the biter health booster, if it is used in the map. + if biter_health_boost then + local health_pool = biter_health_boost_units[entity.unit_number] + if health_pool then + get_health_pool = health_pool[1] + --Set entity health relative to health pool + local max_health = health_pool[3].max_health + local m = health_pool[1] / max_health + local final_health = round(entity.prototype.max_health * m) + + health_pool[1] = round(health_pool[1] + final_damage_amount) + health_pool[1] = round(health_pool[1] - damage) + + --Set entity health relative to health pool + entity.health = final_health + + if health_pool[1] <= 0 then + local entity_number = entity.unit_number + entity.die(entity.force.name, cause) + + if biter_health_boost_units[entity_number] then + biter_health_boost_units[entity_number] = nil + end + end + else + entity.health = entity.health + final_damage_amount + entity.health = entity.health - damage + if entity.health <= 0 then + entity.die(cause.force.name, cause) + end + end + else + --Handle vanilla damage. + entity.health = entity.health + final_damage_amount + entity.health = entity.health - damage + if entity.health <= 0 then + entity.die(cause.force.name, cause) + end + end + + return get_health_pool +end + +local function set_health_boost(entity, damage, cause) + local biter_health_boost = BiterHealthBooster.get('biter_health_boost') + local biter_health_boost_units = BiterHealthBooster.get('biter_health_boost_units') + + local get_health_pool + + --Handle the custom health pool of the biter health booster, if it is used in the map. + if biter_health_boost then + local health_pool = biter_health_boost_units[entity.unit_number] + if health_pool then + get_health_pool = health_pool[1] + --Set entity health relative to health pool + local max_health = health_pool[3].max_health + local m = health_pool[1] / max_health + local final_health = round(entity.prototype.max_health * m) + + health_pool[1] = round(health_pool[1] - damage) + + --Set entity health relative to health pool + entity.health = final_health + + if health_pool[1] <= 0 then + local entity_number = entity.unit_number + entity.die(entity.force.name, cause) + + if biter_health_boost_units[entity_number] then + biter_health_boost_units[entity_number] = nil + end + end + end + end + + return get_health_pool +end + +--Melee damage modifier +local function aoe_punch(character, target, damage, get_health_pool) + if not (target and target.valid) then + return + end + + local base_vector = {target.position.x - character.position.x, target.position.y - character.position.y} + + local vector = {base_vector[1], base_vector[2]} + vector[1] = vector[1] * 1000 + vector[2] = vector[2] * 1000 + + character.surface.create_entity( + { + name = 'flying-text', + position = {character.position.x + base_vector[1] * 0.5, character.position.y + base_vector[2] * 0.5}, + text = ({'rpg_main.aoe_punch_text'}), + color = {255, 0, 0} + } + ) + character.surface.create_entity({name = 'blood-explosion-huge', position = target.position}) + character.surface.create_entity( + { + name = 'big-artillery-explosion', + position = {target.position.x + vector[1] * 0.5, target.position.y + vector[2] * 0.5} + } + ) + + if abs(vector[1]) > abs(vector[2]) then + local d = abs(vector[1]) + if abs(vector[1]) > 0 then + vector[1] = vector[1] / d + end + if abs(vector[2]) > 0 then + vector[2] = vector[2] / d + end + else + local d = abs(vector[2]) + if abs(vector[2]) > 0 then + vector[2] = vector[2] / d + end + if abs(vector[1]) > 0 and d > 0 then + vector[1] = vector[1] / d + end + end + + vector[1] = vector[1] * 1.5 + vector[2] = vector[2] * 1.5 + + local a = 0.20 + + local cs = character.surface + local cp = character.position + + for i = 1, 16, 1 do + for x = i * -1 * a, i * a, 1 do + for y = i * -1 * a, i * a, 1 do + local p = {cp.x + x + vector[1] * i, cp.y + y + vector[2] * i} + cs.create_trivial_smoke({name = 'train-smoke', position = p}) + for _, e in pairs(cs.find_entities({{p[1] - a, p[2] - a}, {p[1] + a, p[2] + a}})) do + if e.valid then + if e.health then + if e.destructible and e.minable and e.force.index ~= 3 then + if e.force.index ~= character.force.index then + if get_health_pool then + local max_unit_health = floor(get_health_pool * 0.00015) + if max_unit_health <= 0 then + max_unit_health = 4 + end + if max_unit_health >= 10 then + max_unit_health = 10 + end + local final = floor(damage * max_unit_health) + set_health_boost(e, final, character) + if e.valid and e.health <= 0 and get_health_pool <= 0 then + e.die(e.force.name, character) + end + else + if e.valid then + e.health = e.health - damage * 0.05 + if e.health <= 0 then + e.die(e.force.name, character) + end + end + end + end + end + end + end + end + end + end + end +end + +local function is_position_near(area, entity) + local status = false + + local function inside(pos) + local lt = area.left_top + local rb = area.right_bottom + + return pos.x >= lt.x and pos.y >= lt.y and pos.x <= rb.x and pos.y <= rb.y + end + + if inside(entity, area) then + status = true + end + + return status +end + +local function on_entity_damaged(event) + if not event.cause then + return + end + if not event.cause.valid then + return + end + if event.cause.force.index == 2 then + return + end + if event.cause.name ~= 'character' then + return + end + + if event.damage_type.name ~= 'physical' then + return + end + + if not event.entity.valid then + return + end + + local entity = event.entity + local cause = event.cause + local original_damage_amount = event.original_damage_amount + local final_damage_amount = event.final_damage_amount + + if cause.get_inventory(defines.inventory.character_ammo)[cause.selected_gun_index].valid_for_read or cause.get_inventory(defines.inventory.character_guns)[cause.selected_gun_index].valid_for_read then + local is_explosive_bullets_enabled = Public.get_explosive_bullets() + if is_explosive_bullets_enabled then + Public.explosive_bullets(event) + end + return + end + if not cause.player then + return + end + + local p = cause.player + + local surface_name = Public.get('rpg_extra').surface_name + if sub(p.surface.name, 0, #surface_name) ~= surface_name then + return + end + + if entity.force.index == cause.force.index then + return + end + + local position = p.position + + local area = { + left_top = {x = position.x - 5, y = position.y - 5}, + right_bottom = {x = position.x + 5, y = position.y + 5} + } + + if not is_position_near(area, entity.position) then + return + end + + local item = p.cursor_stack + + if item and item.valid_for_read then + if item.name == 'discharge-defense-remote' then + return + end + end + + Public.reward_mana(cause.player, 2) + + --Grant the player life-on-hit. + cause.health = cause.health + Public.get_life_on_hit(cause.player) + + --Calculate modified damage. + local damage = Public.get_final_damage(cause.player, entity, original_damage_amount) + local enable_aoe_punch = Public.get('rpg_extra').enable_aoe_punch + local rpg_t = Public.get_value_from_player(cause.player.index) + + --Floating messages and particle effects. + if random(1, 7) == 1 then + damage = damage * random(250, 350) * 0.01 + cause.surface.create_entity( + { + name = 'flying-text', + position = entity.position, + text = '‼' .. floor(damage), + color = {255, 0, 0} + } + ) + cause.surface.create_entity({name = 'blood-explosion-huge', position = entity.position}) + else + damage = damage * random(100, 125) * 0.01 + cause.player.create_local_flying_text( + { + text = floor(damage), + position = entity.position, + color = {150, 150, 150}, + time_to_live = 90, + speed = 2 + } + ) + end + + local get_health_pool = has_health_boost(entity, damage, final_damage_amount, cause) + + --Cause a one punch. + if enable_aoe_punch then + if rpg_t.aoe_punch then + local chance = Public.get_aoe_punch_chance(cause.player) * 10 + local chance_to_hit = random(0, 999) + local success = chance_to_hit < chance + log_aoe_punch( + function() + if success then + print('[OnePunch]: Chance: ' .. chance .. ' Chance to hit: ' .. chance_to_hit .. ' Success: true' .. ' Damage: ' .. damage) + else + print('[OnePunch]: Chance: ' .. chance .. ' Chance to hit: ' .. chance_to_hit .. ' Success: false' .. ' Damage: ' .. damage) + end + end + ) + if success then + aoe_punch(cause, entity, damage, get_health_pool) -- only kill the biters if their health is below or equal to zero + return + end + end + end + + local is_explosive_bullets_enabled = Public.get_explosive_bullets() + if is_explosive_bullets_enabled then + Public.explosive_bullets(event) + end +end + +local function on_player_repaired_entity(event) + if random(1, 4) ~= 1 then + return + end + + local entity = event.entity + + if not entity then + return + end + + if not entity.valid then + return + end + + if not entity.health then + return + end + + local player = game.players[event.player_index] + + if not player or not player.valid or not player.character then + return + end + + Public.gain_xp(player, 0.05) + Public.reward_mana(player, 0.2) + + local repair_speed = Public.get_magicka(player) + if repair_speed <= 0 then + return + end + entity.health = entity.health + repair_speed +end + +local function on_player_rotated_entity(event) + local player = game.players[event.player_index] + + if not player or not player.valid then + return + end + if not player.character then + return + end + + local rpg_t = Public.get_value_from_player(player.index) + if rpg_t.rotated_entity_delay > game.tick then + return + end + rpg_t.rotated_entity_delay = game.tick + 20 + Public.gain_xp(player, 0.20) +end + +local function on_player_changed_position(event) + local player = game.players[event.player_index] + if not player or not player.valid then + return + end + + local enable_flame_boots = Public.get('rpg_extra').enable_flame_boots + + if enable_flame_boots then + give_player_flameboots(player) + end + + if random(1, 64) ~= 1 then + return + end + if not player.character then + return + end + if player.character.driving then + return + end + Public.gain_xp(player, 1.0) +end + +local building_and_mining_blacklist = { + ['tile-ghost'] = true, + ['entity-ghost'] = true, + ['item-entity'] = true +} + +local function on_player_died(event) + local player = game.players[event.player_index] + + if not player or not player.valid then + return + end + + Public.remove_frame(player) +end + +local function on_pre_player_left_game(event) + local player = game.players[event.player_index] + + if not player or not player.valid then + return + end + + Public.remove_frame(player) +end + +local function on_pre_player_mined_item(event) + local entity = event.entity + if not entity.valid then + return + end + if building_and_mining_blacklist[entity.type] then + return + end + if entity.force.index ~= 3 then + return + end + local player = game.players[event.player_index] + + if not player or not player.valid then + return + end + + local surface_name = Public.get('rpg_extra').surface_name + if sub(player.surface.name, 0, #surface_name) ~= surface_name then + return + end + + local rpg_t = Public.get_value_from_player(player.index) + if rpg_t.last_mined_entity_position.x == entity.position.x and rpg_t.last_mined_entity_position.y == entity.position.y then + return + end + rpg_t.last_mined_entity_position.x = entity.position.x + rpg_t.last_mined_entity_position.y = entity.position.y + + local distance_multiplier = floor(sqrt(entity.position.x ^ 2 + entity.position.y ^ 2)) * 0.0005 + 1 + + local xp_modifier_when_mining = Public.get('rpg_extra').xp_modifier_when_mining + + local xp_amount + if entity.type == 'resource' then + xp_amount = 0.9 * distance_multiplier + else + xp_amount = (1.5 + entity.prototype.max_health * xp_modifier_when_mining) * distance_multiplier + end + + if player.gui.screen[main_frame_name] then + local f = player.gui.screen[main_frame_name] + local data = Gui.get_data(f) + if data.exp_gui and data.exp_gui.valid then + data.exp_gui.caption = floor(rpg_t.xp) + end + end + + Public.gain_xp(player, xp_amount) + Public.reward_mana(player, 0.5 * distance_multiplier) +end + +local function on_player_crafted_item(event) + if not event.recipe.energy then + return + end + local player = game.players[event.player_index] + if not player or not player.valid then + return + end + + if player.cheat_mode then + return + end + + local rpg_extra = Public.get('rpg_extra') + local is_blacklisted = rpg_extra.tweaked_crafting_items + local tweaked_crafting_items_enabled = rpg_extra.tweaked_crafting_items_enabled + + local item = event.item_stack + + local amount = 0.40 * random(1, 2) + local recipe = event.recipe + + if tweaked_crafting_items_enabled then + if item and item.valid then + if is_blacklisted[item.name] then + amount = 0.2 + end + end + end + + local final_xp = recipe.energy * amount + + Public.gain_xp(player, final_xp) + Public.reward_mana(player, amount) +end + +local function on_player_respawned(event) + local player = game.players[event.player_index] + local rpg_t = Public.get_value_from_player(player.index) + if not rpg_t then + Public.rpg_reset_player(player) + return + end + Public.update_player_stats(player) + Public.draw_level_text(player) + Public.update_health(player) + Public.update_mana(player) +end + +local function on_player_joined_game(event) + local player = game.players[event.player_index] + local rpg_t = Public.get_value_from_player(player.index) + local rpg_extra = Public.get('rpg_extra') + if not rpg_t then + Public.rpg_reset_player(player) + if rpg_extra.reward_new_players > 10 then + Public.gain_xp(player, rpg_extra.reward_new_players) + end + end + for _, p in pairs(game.connected_players) do + Public.draw_level_text(p) + end + Public.draw_gui_char_button(player) + if not player.character then + return + end + Public.update_player_stats(player) +end + +local function get_near_coord_modifier(range) + local coord = {x = (range * -1) + random(0, range * 2), y = (range * -1) + random(0, range * 2)} + for i = 1, 5, 1 do + local new_coord = {x = (range * -1) + random(0, range * 2), y = (range * -1) + random(0, range * 2)} + if new_coord.x ^ 2 + new_coord.y ^ 2 < coord.x ^ 2 + coord.y ^ 2 then + coord = new_coord + end + end + return coord +end + +local function damage_entity(e) + if not e or not e.valid then + return + end + + if not e.health then + return + end + + if e.force.name == 'player' then + return + end + + if not e.destructible then + return + end + + e.surface.create_entity({name = 'ground-explosion', position = e.position}) + + if e.type == 'entity-ghost' then + e.destroy() + return + end + + e.health = e.health - random(30, 90) + if e.health <= 0 then + e.die('enemy') + end +end + +local function floaty_hearts(entity, c) + local position = {x = entity.position.x - 0.75, y = entity.position.y - 1} + local b = 1.35 + for _ = 1, c, 1 do + local p = { + (position.x + 0.4) + (b * -1 + random(0, b * 20) * 0.1), + position.y + (b * -1 + random(0, b * 20) * 0.1) + } + entity.surface.create_entity({name = 'flying-text', position = p, text = '♥', color = {random(150, 255), 0, 255}}) + end +end + +local function tame_unit_effects(player, entity) + floaty_hearts(entity, 7) + + rendering.draw_text { + text = '~' .. player.name .. "'s pet~", + surface = player.surface, + target = entity, + target_offset = {0, -2.6}, + color = { + r = player.color.r * 0.6 + 0.25, + g = player.color.g * 0.6 + 0.25, + b = player.color.b * 0.6 + 0.25, + a = 1 + }, + scale = 1.05, + font = 'default-large-semibold', + alignment = 'center', + scale_with_zoom = false + } +end + +local function on_player_used_capsule(event) + local enable_mana = Public.get('rpg_extra').enable_mana + local surface_name = Public.get('rpg_extra').surface_name + if not enable_mana then + return + end + + local conjure_items = Public.spells + local projectile_types = Public.get_projectiles + + local player = game.players[event.player_index] + if not player or not player.valid then + return + end + + if not player.character or not player.character.valid then + return + end + + if sub(player.surface.name, 0, #surface_name) ~= surface_name then + return + end + + local item = event.item + + if not item then + return + end + + local name = item.name + + if name ~= 'raw-fish' then + return + end + + Public.get_heal_modifier_from_using_fish(player) + + local rpg_t = Public.get_value_from_player(player.index) + + if not rpg_t.enable_entity_spawn then + return + end + + if rpg_t.last_spawned >= game.tick then + return Public.cast_spell(player, true) + end + + local mana = rpg_t.mana + local surface = player.surface + + local object = conjure_items[rpg_t.dropdown_select_index] + if not object then + return + end + + local position = event.position + if not position then + return + end + + local radius = 15 + local area = { + left_top = {x = position.x - radius, y = position.y - radius}, + right_bottom = {x = position.x + radius, y = position.y + radius} + } + + if rpg_t.level < object.level then + return Public.cast_spell(player, true) + end + + if not object.enabled then + return + end + + if not Math2D.bounding_box.contains_point(area, player.position) then + Public.cast_spell(player, true) + return + end + + if mana < object.mana_cost then + return Public.cast_spell(player, true) + end + + local target_pos + if object.target then + target_pos = {position.x, position.y} + elseif projectile_types[object.entityName] then + local coord_modifier = get_near_coord_modifier(projectile_types[object.entityName].max_range) + target_pos = {position.x + coord_modifier.x, position.y + coord_modifier.y} + end + + local range + if object.range then + range = object.range + else + range = 0 + end + + local force + if object.force then + force = object.force + else + force = 'player' + end + + local data = { + self = object, + player = player, + damage_entity = damage_entity, + position = position, + surface = surface, + force = force, + target_pos = target_pos, + range = range, + tame_unit_effects = tame_unit_effects, + explosives = Explosives, + rpg_t = rpg_t + } + + object.callback(data) + + local msg = player.name .. ' casted ' .. object.entityName .. '. ' + + rpg_t.last_spawned = game.tick + object.tick + Public.update_mana(player) + + local reward_xp = object.mana_cost * 0.085 + if reward_xp < 1 then + reward_xp = 1 + end + + Public.gain_xp(player, reward_xp) + + AntiGrief.insert_into_capsule_history(player, position, msg) +end + +local function on_player_changed_surface(event) + local player = game.get_player(event.player_index) + Public.draw_level_text(player) +end + +local function on_player_removed(event) + Public.remove_player(event.player_index) +end + +local function tick() + local ticker = game.tick + local players = game.connected_players + local count = #players + local enable_flameboots = Public.get('rpg_extra').enable_flameboots + local enable_mana = Public.get('rpg_extra').enable_mana + + if ticker % nth_tick == 0 then + Public.global_pool(players, count) + end + + if ticker % 30 == 0 then + regen_health_player(players) + if enable_mana then + regen_mana_player(players) + end + if enable_flameboots then + give_player_flameboots(players) + end + end +end + +Event.add(defines.events.on_pre_player_left_game, on_pre_player_left_game) +Event.add(defines.events.on_player_died, on_player_died) +Event.add(defines.events.on_entity_damaged, on_entity_damaged) +Event.add(defines.events.on_entity_died, on_entity_died) +Event.add(defines.events.on_gui_click, on_gui_click) +Event.add(defines.events.on_player_changed_position, on_player_changed_position) +Event.add(defines.events.on_player_crafted_item, on_player_crafted_item) +Event.add(defines.events.on_player_joined_game, on_player_joined_game) +Event.add(defines.events.on_player_created, on_player_joined_game) +Event.add(defines.events.on_player_repaired_entity, on_player_repaired_entity) +Event.add(defines.events.on_player_respawned, on_player_respawned) +Event.add(defines.events.on_player_rotated_entity, on_player_rotated_entity) +Event.add(defines.events.on_pre_player_mined_item, on_pre_player_mined_item) +Event.add(defines.events.on_player_used_capsule, on_player_used_capsule) +Event.add(defines.events.on_player_changed_surface, on_player_changed_surface) +Event.add(defines.events.on_player_removed, on_player_removed) +Event.on_nth_tick(10, tick) + +return Public diff --git a/modules/rpg/spells.lua b/modules/rpg/spells.lua index 50fbbacb..fc23187c 100644 --- a/modules/rpg/spells.lua +++ b/modules/rpg/spells.lua @@ -1,902 +1,902 @@ -local Public = require 'modules.rpg.table' - -local spells = {} -local random = math.random - -local function create_projectiles(data) - local self = data.self - local player = data.player - local damage_entity = data.damage_entity - local position = data.position - local surface = data.surface - local force = data.force - local target_pos = data.target_pos - local range = data.range - - local function do_projectile(player_surface, name, _position, _force, target, max_range) - player_surface.create_entity( - { - name = name, - position = _position, - force = _force, - source = _position, - target = target, - max_range = max_range, - speed = 0.4 - } - ) - end - - if self.aoe then - for _ = 1, self.amount do - local damage_area = { - left_top = {x = position.x - 2, y = position.y - 2}, - right_bottom = {x = position.x + 2, y = position.y + 2} - } - do_projectile(surface, self.entityName, position, force, target_pos, range) - if self.damage then - for _, e in pairs(surface.find_entities_filtered({area = damage_area})) do - damage_entity(e) - end - end - end - else - local damage_area = { - left_top = {x = position.x - 2, y = position.y - 2}, - right_bottom = {x = position.x + 2, y = position.y + 2} - } - do_projectile(surface, self.entityName, position, force, target_pos, range) - if self.damage then - for _, e in pairs(surface.find_entities_filtered({area = damage_area})) do - damage_entity(e) - end - end - end - Public.cast_spell(player) - Public.remove_mana(player, self.mana_cost) -end - -local function create_entity(data) - local self = data.self - local player = data.player - local mana = data.mana - local position = data.position - local surface = data.surface - local force = data.force - local tame_unit_effects = data.tame_unit_effects - - if self.biter then - local e = surface.create_entity({name = self.entityName, position = position, force = force}) - tame_unit_effects(player, e) - Public.remove_mana(player, self.mana_cost) - return - end - - if self.aoe then - for x = 1, -1, -1 do - for y = 1, -1, -1 do - local pos = {x = position.x + x, y = position.y + y} - if surface.can_place_entity {name = self.entityName, position = pos} then - if self.mana_cost > mana then - break - end - local e = surface.create_entity({name = self.entityName, position = pos, force = force}) - e.direction = player.character.direction - Public.remove_mana(player, self.mana_cost) - end - end - end - else - if surface.can_place_entity {name = self.entityName, position = position} then - local e = surface.create_entity({name = self.entityName, position = position, force = force}) - e.direction = player.character.direction - Public.remove_mana(player, self.mana_cost) - end - end - Public.cast_spell(player) - Public.remove_mana(player, self.mana_cost) -end - -local function insert_onto(data) - local self = data.self - local player = data.player - - player.insert({name = self.entityName, count = self.amount}) - Public.cast_spell(player) - Public.remove_mana(player, self.mana_cost) -end - -spells[#spells + 1] = { - name = {'entity-name.stone-wall'}, - entityName = 'stone-wall', - level = 1, - type = 'item', - mana_cost = 60, - tick = 100, - aoe = true, - enabled = true, - sprite = 'recipe/stone-wall', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.wooden-chest'}, - entityName = 'wooden-chest', - level = 1, - type = 'item', - mana_cost = 50, - tick = 100, - aoe = true, - enabled = true, - sprite = 'recipe/wooden-chest', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.iron-chest'}, - entityName = 'iron-chest', - level = 10, - type = 'item', - mana_cost = 110, - tick = 200, - aoe = true, - enabled = true, - sprite = 'recipe/iron-chest', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.steel-chest'}, - entityName = 'steel-chest', - level = 30, - type = 'item', - mana_cost = 150, - tick = 300, - aoe = true, - enabled = true, - sprite = 'recipe/steel-chest', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.transport-belt'}, - entityName = 'transport-belt', - level = 1, - type = 'item', - mana_cost = 80, - tick = 100, - aoe = true, - enabled = true, - sprite = 'recipe/transport-belt', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.fast-transport-belt'}, - entityName = 'fast-transport-belt', - level = 10, - type = 'item', - mana_cost = 110, - tick = 200, - aoe = true, - enabled = true, - sprite = 'recipe/fast-transport-belt', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.express-transport-belt'}, - entityName = 'express-transport-belt', - level = 30, - type = 'item', - mana_cost = 150, - tick = 300, - aoe = true, - enabled = true, - sprite = 'recipe/express-transport-belt', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.underground-belt'}, - entityName = 'underground-belt', - level = 1, - type = 'item', - mana_cost = 80, - tick = 100, - aoe = true, - enabled = true, - sprite = 'recipe/underground-belt', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.fast-underground-belt'}, - entityName = 'fast-underground-belt', - level = 10, - type = 'item', - mana_cost = 110, - tick = 200, - aoe = true, - enabled = true, - sprite = 'recipe/fast-underground-belt', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.express-underground-belt'}, - entityName = 'express-underground-belt', - level = 30, - type = 'item', - mana_cost = 150, - tick = 300, - aoe = true, - enabled = true, - sprite = 'recipe/express-underground-belt', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.pipe'}, - entityName = 'pipe', - level = 1, - type = 'item', - mana_cost = 50, - tick = 100, - aoe = true, - enabled = true, - sprite = 'recipe/pipe', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.pipe-to-ground'}, - entityName = 'pipe-to-ground', - level = 1, - type = 'item', - mana_cost = 100, - tick = 100, - aoe = true, - enabled = true, - sprite = 'recipe/pipe-to-ground', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.tree'}, - entityName = 'tree-05', - level = 30, - type = 'entity', - mana_cost = 100, - tick = 350, - aoe = true, - enabled = true, - sprite = 'entity/tree-05', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.sand-rock-big'}, - entityName = 'sand-rock-big', - level = 60, - type = 'entity', - mana_cost = 80, - tick = 350, - aoe = true, - enabled = true, - sprite = 'entity/sand-rock-big', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.small-biter'}, - entityName = 'small-biter', - level = 30, - biter = true, - type = 'entity', - mana_cost = 55, - tick = 200, - enabled = true, - sprite = 'entity/small-biter', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.small-spitter'}, - entityName = 'small-spitter', - level = 30, - biter = true, - type = 'entity', - mana_cost = 55, - tick = 200, - enabled = true, - sprite = 'entity/small-spitter', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.medium-biter'}, - entityName = 'medium-biter', - level = 60, - biter = true, - type = 'entity', - mana_cost = 100, - tick = 300, - enabled = true, - sprite = 'entity/medium-biter', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.medium-spitter'}, - entityName = 'medium-spitter', - level = 60, - biter = true, - type = 'entity', - mana_cost = 100, - tick = 300, - enabled = true, - sprite = 'entity/medium-spitter', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.biter-spawner'}, - entityName = 'biter-spawner', - level = 100, - biter = true, - type = 'entity', - mana_cost = 800, - tick = 1420, - enabled = false, - sprite = 'entity/biter-spawner', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'entity-name.spitter-spawner'}, - entityName = 'spitter-spawner', - level = 100, - biter = true, - type = 'entity', - mana_cost = 800, - tick = 1420, - enabled = false, - sprite = 'entity/spitter-spawner', - callback = function(data) - create_entity(data) - end -} - -spells[#spells + 1] = { - name = {'item-name.shotgun-shell'}, - entityName = 'shotgun-shell', - target = true, - amount = 1, - damage = true, - force = 'player', - level = 10, - type = 'item', - mana_cost = 40, - tick = 150, - enabled = true, - sprite = 'recipe/shotgun-shell', - callback = function(data) - create_projectiles(data) - end -} -spells[#spells + 1] = { - name = {'item-name.grenade'}, - entityName = 'grenade', - target = true, - amount = 1, - damage = true, - force = 'player', - level = 30, - type = 'item', - mana_cost = 100, - tick = 150, - enabled = true, - sprite = 'recipe/grenade', - callback = function(data) - create_projectiles(data) - end -} -spells[#spells + 1] = { - name = {'item-name.cluster-grenade'}, - entityName = 'cluster-grenade', - target = true, - amount = 2, - damage = true, - force = 'player', - level = 50, - type = 'item', - mana_cost = 225, - tick = 200, - enabled = true, - sprite = 'recipe/cluster-grenade', - callback = function(data) - create_projectiles(data) - end -} -spells[#spells + 1] = { - name = {'item-name.cannon-shell'}, - entityName = 'cannon-shell', - target = true, - amount = 1, - damage = true, - force = 'player', - level = 30, - type = 'item', - mana_cost = 125, - tick = 150, - enabled = true, - sprite = 'recipe/cannon-shell', - callback = function(data) - create_projectiles(data) - end -} -spells[#spells + 1] = { - name = {'item-name.explosive-cannon-shell'}, - entityName = 'explosive-cannon-shell', - target = true, - amount = 2, - damage = true, - force = 'player', - level = 50, - type = 'item', - mana_cost = 250, - tick = 200, - enabled = true, - sprite = 'recipe/explosive-cannon-shell', - callback = function(data) - create_projectiles(data) - end -} -spells[#spells + 1] = { - name = {'item-name.uranium-cannon-shell'}, - entityName = 'uranium-cannon-shell', - target = true, - amount = 2, - damage = true, - force = 'player', - level = 70, - type = 'item', - mana_cost = 400, - tick = 200, - enabled = true, - sprite = 'recipe/uranium-cannon-shell', - callback = function(data) - create_projectiles(data) - end -} -spells[#spells + 1] = { - name = {'item-name.rocket'}, - entityName = 'rocket', - range = 240, - target = true, - amount = 4, - damage = true, - force = 'enemy', - level = 40, - type = 'item', - mana_cost = 60, - tick = 320, - enabled = true, - sprite = 'recipe/rocket', - callback = function(data) - create_projectiles(data) - end -} -spells[#spells + 1] = { - name = {'spells.pointy_explosives'}, - entityName = 'pointy_explosives', - target = true, - amount = 1, - range = 0, - damage = true, - force = 'player', - level = 70, - type = 'special', - mana_cost = 100, - tick = 100, - enabled = true, - sprite = 'recipe/explosives', - callback = function(data) - local self = data.self - local player = data.player - local Explosives = data.explosives - local position = data.position - - local entities = - player.surface.find_entities_filtered { - force = player.force, - type = 'container', - area = {{position.x - 1, position.y - 1}, {position.x + 1, position.y + 1}} - } - - local detonate_chest - for i = 1, #entities do - local e = entities[i] - detonate_chest = e - end - if detonate_chest and detonate_chest.valid then - local success = Explosives.detonate_chest(detonate_chest) - if success then - Public.remove_mana(player, self.mana_cost) - end - Public.cast_spell(player) - end - end -} -spells[#spells + 1] = { - name = {'spells.repair_aoe'}, - entityName = 'repair_aoe', - target = true, - amount = 1, - range = 50, - damage = false, - force = 'player', - level = 45, - type = 'special', - mana_cost = 150, - tick = 100, - enabled = true, - sprite = 'recipe/repair-pack', - callback = function(data) - local self = data.self - local player = data.player - local position = data.position - - Public.repair_aoe(player, position) - Public.cast_spell(player) - Public.remove_mana(player, self.mana_cost) - end -} -spells[#spells + 1] = { - name = {'spells.acid_stream'}, - entityName = 'acid-stream-spitter-big', - target = true, - amount = 2, - range = 0, - damage = true, - force = 'player', - level = 50, - type = 'special', - mana_cost = 70, - tick = 100, - enabled = true, - sprite = 'virtual-signal/signal-S', - callback = function(data) - create_projectiles(data) - end -} -spells[#spells + 1] = { - name = {'spells.tank'}, - entityName = 'tank', - amount = 1, - capsule = true, - force = 'player', - level = 1000, - type = 'special', - mana_cost = 10000, -- they who know, will know - tick = 320, - enabled = false, - sprite = 'entity/tank', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'spells.spidertron'}, - entityName = 'spidertron', - amount = 1, - capsule = true, - force = 'player', - level = 2000, - type = 'special', - mana_cost = 19500, -- they who know, will know - tick = 320, - enabled = false, - sprite = 'entity/spidertron', - callback = function(data) - create_entity(data) - end -} -spells[#spells + 1] = { - name = {'spells.raw_fish'}, - entityName = 'raw-fish', - target = false, - amount = 4, - capsule = true, - damage = false, - range = 30, - force = 'player', - level = 50, - type = 'special', - mana_cost = 140, - tick = 320, - enabled = true, - sprite = 'item/raw-fish', - callback = function(data) - insert_onto(data) - end -} -spells[#spells + 1] = { - name = {'spells.dynamites'}, - entityName = 'explosives', - target = false, - amount = 3, - capsule = true, - damage = false, - range = 30, - force = 'player', - level = 25, - type = 'special', - mana_cost = 140, - tick = 320, - enabled = true, - sprite = 'item/explosives', - callback = function(data) - insert_onto(data) - end -} -spells[#spells + 1] = { - name = {'spells.comfylatron'}, - entityName = 'suicidal_comfylatron', - target = false, - amount = 4, - damage = false, - range = 30, - force = 'player', - level = 60, - type = 'special', - mana_cost = 150, - tick = 320, - enabled = true, - sprite = 'entity/compilatron', - callback = function(data) - local self = data.self - local player = data.player - local position = data.position - local surface = data.surface - - Public.suicidal_comfylatron(position, surface) - Public.cast_spell(player) - Public.remove_mana(player, self.mana_cost) - end -} -spells[#spells + 1] = { - name = {'spells.distractor'}, - entityName = 'distractor-capsule', - target = true, - amount = 1, - damage = false, - range = 30, - force = 'player', - level = 50, - type = 'special', - mana_cost = 220, - tick = 320, - enabled = true, - sprite = 'recipe/distractor-capsule', - callback = function(data) - create_projectiles(data) - end -} -spells[#spells + 1] = { - name = {'spells.warp'}, - entityName = 'warp-gate', - target = true, - force = 'player', - level = 60, - type = 'special', - mana_cost = 340, - tick = 2000, - enabled = true, - sprite = 'virtual-signal/signal-W', - callback = function(data) - local player = data.player - local surface = data.surface - - local pos = surface.find_non_colliding_position('character', game.forces.player.get_spawn_position(surface), 3, 0, 5) - if pos then - player.teleport(pos, surface) - else - pos = game.forces.player.get_spawn_position(surface) - player.teleport(pos, surface) - end - Public.remove_mana(player, 999999) - Public.damage_player_over_time(player, random(8, 16)) - player.play_sound {path = 'utility/armor_insert', volume_modifier = 1} - Public.cast_spell(player) - end -} - -Public.projectile_types = { - ['explosives'] = {name = 'grenade', count = 0.5, max_range = 32, tick_speed = 1}, - ['land-mine'] = {name = 'grenade', count = 1, max_range = 32, tick_speed = 1}, - ['grenade'] = {name = 'grenade', count = 1, max_range = 40, tick_speed = 1}, - ['cluster-grenade'] = {name = 'cluster-grenade', count = 1, max_range = 40, tick_speed = 3}, - ['artillery-shell'] = {name = 'artillery-projectile', count = 1, max_range = 60, tick_speed = 3}, - ['cannon-shell'] = {name = 'cannon-projectile', count = 1, max_range = 60, tick_speed = 1}, - ['explosive-cannon-shell'] = {name = 'explosive-cannon-projectile', count = 1, max_range = 60, tick_speed = 1}, - ['explosive-uranium-cannon-shell'] = { - name = 'explosive-uranium-cannon-projectile', - count = 1, - max_range = 60, - tick_speed = 1 - }, - ['uranium-cannon-shell'] = {name = 'uranium-cannon-projectile', count = 1, max_range = 60, tick_speed = 1}, - ['atomic-bomb'] = {name = 'atomic-rocket', count = 1, max_range = 80, tick_speed = 20}, - ['explosive-rocket'] = {name = 'explosive-rocket', count = 1, max_range = 48, tick_speed = 1}, - ['rocket'] = {name = 'rocket', count = 1, max_range = 48, tick_speed = 1}, - ['flamethrower-ammo'] = {name = 'flamethrower-fire-stream', count = 4, max_range = 28, tick_speed = 1}, - ['crude-oil-barrel'] = {name = 'flamethrower-fire-stream', count = 3, max_range = 24, tick_speed = 1}, - ['petroleum-gas-barrel'] = {name = 'flamethrower-fire-stream', count = 4, max_range = 24, tick_speed = 1}, - ['light-oil-barrel'] = {name = 'flamethrower-fire-stream', count = 4, max_range = 24, tick_speed = 1}, - ['heavy-oil-barrel'] = {name = 'flamethrower-fire-stream', count = 4, max_range = 24, tick_speed = 1}, - ['acid-stream-spitter-big'] = { - name = 'acid-stream-spitter-big', - count = 3, - max_range = 16, - tick_speed = 1, - force = 'enemy' - }, - ['lubricant-barrel'] = {name = 'acid-stream-spitter-big', count = 3, max_range = 16, tick_speed = 1}, - ['shotgun-shell'] = {name = 'shotgun-pellet', count = 16, max_range = 24, tick_speed = 1}, - ['piercing-shotgun-shell'] = {name = 'piercing-shotgun-pellet', count = 16, max_range = 24, tick_speed = 1}, - ['firearm-magazine'] = {name = 'shotgun-pellet', count = 16, max_range = 24, tick_speed = 1}, - ['piercing-rounds-magazine'] = {name = 'piercing-shotgun-pellet', count = 16, max_range = 24, tick_speed = 1}, - ['uranium-rounds-magazine'] = {name = 'piercing-shotgun-pellet', count = 32, max_range = 24, tick_speed = 1}, - ['cliff-explosives'] = {name = 'cliff-explosives', count = 1, max_range = 48, tick_speed = 2} -} - -Public.get_projectiles = Public.projectile_types -Public.spells = spells - ---- Retrieves the spells table or a given spell. ----@param key string -function Public.get_spells(key) - if game then - return error('Calling Public.get_spells() after on_init() or on_load() has run is a desync risk.', 2) - end - if Public.spells[key] then - return Public.spells[key] - else - return Public.spells - end -end - ---- Disables a spell. ----@param key string/number --- Table would look like: --- Public.disable_spell({1, 2, 3, 4, 5, 6, 7, 8}) -function Public.disable_spell(key) - if game then - return error('Calling Public.disable_spell() after on_init() or on_load() has run is a desync risk.', 2) - end - - if type(key) == 'table' then - for _, k in pairs(key) do - Public.spells[k].enabled = false - end - elseif Public.spells[key] then - Public.spells[key].enabled = false - end -end - ---- Clears the spell table. -function Public.clear_spell_table() - if game then - return error('Calling Public.clear_spell_table() after on_init() or on_load() has run is a desync risk.', 2) - end - - Public.spells = {} -end - ---- Adds a spell to the rpg_spells ----@param tbl table -function Public.set_new_spell(tbl) - if game then - return error('Calling Public.set_new_spell() after on_init() or on_load() has run is a desync risk.', 2) - end - - if tbl then - if not tbl.name then - return error('A spell requires a name. string', 2) - end - if not tbl.entityName then - return error('A spell requires an object to create. string', 2) - end - if not tbl.target then - return error('A spell requires position. boolean', 2) - end - if not tbl.amount then - return error('A spell requires an amount of creation. ', 2) - end - if not tbl.range then - return error('A spell requires a range. ', 2) - end - if not tbl.damage then - return error('A spell requires damage. ', 2) - end - if not tbl.force then - return error('A spell requires a force. string', 2) - end - if not tbl.level then - return error('A spell requires a level. ', 2) - end - if not tbl.type then - return error('A spell requires a type. ', 2) - end - if not tbl.mana_cost then - return error('A spell requires mana_cost. ', 2) - end - if not tbl.tick then - return error('A spell requires tick. ', 2) - end - if not tbl.enabled then - return error('A spell requires enabled. boolean', 2) - end - - Public.spells[#Public.spells + 1] = tbl - end -end - ---- This rebuilds all spells. Make sure to make changes on_init if you don't --- want all spells enabled. -function Public.rebuild_spells() - local new_spells = {} - local spell_names = {} - - for i = 1, #spells do - if spells[i].enabled then - new_spells[#new_spells + 1] = spells[i] - spell_names[#spell_names + 1] = spells[i].name - end - end - - return new_spells, spell_names -end - ---- This will disable the cooldown of all spells. -function Public.disable_cooldowns_on_spells() - if game then - return error('Calling Public.disable_cooldowns_on_spells() after on_init() or on_load() has run is a desync risk.', 2) - end - - local new_spells = {} - - for i = 1, #spells do - if spells[i].enabled then - spells[i].tick = 0 - new_spells[#new_spells + 1] = spells[i] - end - end - - Public.spells = new_spells - - return new_spells -end - -return Public +local Public = require 'modules.rpg.table' + +local spells = {} +local random = math.random + +local function create_projectiles(data) + local self = data.self + local player = data.player + local damage_entity = data.damage_entity + local position = data.position + local surface = data.surface + local force = data.force + local target_pos = data.target_pos + local range = data.range + + local function do_projectile(player_surface, name, _position, _force, target, max_range) + player_surface.create_entity( + { + name = name, + position = _position, + force = _force, + source = _position, + target = target, + max_range = max_range, + speed = 0.4 + } + ) + end + + if self.aoe then + for _ = 1, self.amount do + local damage_area = { + left_top = {x = position.x - 2, y = position.y - 2}, + right_bottom = {x = position.x + 2, y = position.y + 2} + } + do_projectile(surface, self.entityName, position, force, target_pos, range) + if self.damage then + for _, e in pairs(surface.find_entities_filtered({area = damage_area})) do + damage_entity(e) + end + end + end + else + local damage_area = { + left_top = {x = position.x - 2, y = position.y - 2}, + right_bottom = {x = position.x + 2, y = position.y + 2} + } + do_projectile(surface, self.entityName, position, force, target_pos, range) + if self.damage then + for _, e in pairs(surface.find_entities_filtered({area = damage_area})) do + damage_entity(e) + end + end + end + Public.cast_spell(player) + Public.remove_mana(player, self.mana_cost) +end + +local function create_entity(data) + local self = data.self + local player = data.player + local rpg_t = data.rpg_t + local position = data.position + local surface = data.surface + local force = data.force + local tame_unit_effects = data.tame_unit_effects + + if self.biter then + local e = surface.create_entity({name = self.entityName, position = position, force = force}) + tame_unit_effects(player, e) + Public.remove_mana(player, self.mana_cost) + return + end + + if self.aoe then + for x = 1, -1, -1 do + for y = 1, -1, -1 do + local pos = {x = position.x + x, y = position.y + y} + if surface.can_place_entity {name = self.entityName, position = pos} then + if self.mana_cost > rpg_t.mana then + break + end + local e = surface.create_entity({name = self.entityName, position = pos, force = force}) + e.direction = player.character.direction + Public.remove_mana(player, self.mana_cost) + end + end + end + else + if surface.can_place_entity {name = self.entityName, position = position} then + local e = surface.create_entity({name = self.entityName, position = position, force = force}) + e.direction = player.character.direction + Public.remove_mana(player, self.mana_cost) + end + end + Public.cast_spell(player) + Public.remove_mana(player, self.mana_cost) +end + +local function insert_onto(data) + local self = data.self + local player = data.player + + player.insert({name = self.entityName, count = self.amount}) + Public.cast_spell(player) + Public.remove_mana(player, self.mana_cost) +end + +spells[#spells + 1] = { + name = {'entity-name.stone-wall'}, + entityName = 'stone-wall', + level = 1, + type = 'item', + mana_cost = 60, + tick = 100, + aoe = true, + enabled = true, + sprite = 'recipe/stone-wall', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.wooden-chest'}, + entityName = 'wooden-chest', + level = 1, + type = 'item', + mana_cost = 50, + tick = 100, + aoe = true, + enabled = true, + sprite = 'recipe/wooden-chest', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.iron-chest'}, + entityName = 'iron-chest', + level = 10, + type = 'item', + mana_cost = 110, + tick = 200, + aoe = true, + enabled = true, + sprite = 'recipe/iron-chest', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.steel-chest'}, + entityName = 'steel-chest', + level = 30, + type = 'item', + mana_cost = 150, + tick = 300, + aoe = true, + enabled = true, + sprite = 'recipe/steel-chest', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.transport-belt'}, + entityName = 'transport-belt', + level = 1, + type = 'item', + mana_cost = 80, + tick = 100, + aoe = true, + enabled = true, + sprite = 'recipe/transport-belt', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.fast-transport-belt'}, + entityName = 'fast-transport-belt', + level = 10, + type = 'item', + mana_cost = 110, + tick = 200, + aoe = true, + enabled = true, + sprite = 'recipe/fast-transport-belt', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.express-transport-belt'}, + entityName = 'express-transport-belt', + level = 30, + type = 'item', + mana_cost = 150, + tick = 300, + aoe = true, + enabled = true, + sprite = 'recipe/express-transport-belt', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.underground-belt'}, + entityName = 'underground-belt', + level = 1, + type = 'item', + mana_cost = 80, + tick = 100, + aoe = true, + enabled = true, + sprite = 'recipe/underground-belt', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.fast-underground-belt'}, + entityName = 'fast-underground-belt', + level = 10, + type = 'item', + mana_cost = 110, + tick = 200, + aoe = true, + enabled = true, + sprite = 'recipe/fast-underground-belt', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.express-underground-belt'}, + entityName = 'express-underground-belt', + level = 30, + type = 'item', + mana_cost = 150, + tick = 300, + aoe = true, + enabled = true, + sprite = 'recipe/express-underground-belt', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.pipe'}, + entityName = 'pipe', + level = 1, + type = 'item', + mana_cost = 50, + tick = 100, + aoe = true, + enabled = true, + sprite = 'recipe/pipe', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.pipe-to-ground'}, + entityName = 'pipe-to-ground', + level = 1, + type = 'item', + mana_cost = 100, + tick = 100, + aoe = true, + enabled = true, + sprite = 'recipe/pipe-to-ground', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.tree'}, + entityName = 'tree-05', + level = 30, + type = 'entity', + mana_cost = 100, + tick = 350, + aoe = true, + enabled = true, + sprite = 'entity/tree-05', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.sand-rock-big'}, + entityName = 'sand-rock-big', + level = 60, + type = 'entity', + mana_cost = 80, + tick = 350, + aoe = true, + enabled = true, + sprite = 'entity/sand-rock-big', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.small-biter'}, + entityName = 'small-biter', + level = 30, + biter = true, + type = 'entity', + mana_cost = 55, + tick = 200, + enabled = true, + sprite = 'entity/small-biter', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.small-spitter'}, + entityName = 'small-spitter', + level = 30, + biter = true, + type = 'entity', + mana_cost = 55, + tick = 200, + enabled = true, + sprite = 'entity/small-spitter', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.medium-biter'}, + entityName = 'medium-biter', + level = 60, + biter = true, + type = 'entity', + mana_cost = 100, + tick = 300, + enabled = true, + sprite = 'entity/medium-biter', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.medium-spitter'}, + entityName = 'medium-spitter', + level = 60, + biter = true, + type = 'entity', + mana_cost = 100, + tick = 300, + enabled = true, + sprite = 'entity/medium-spitter', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.biter-spawner'}, + entityName = 'biter-spawner', + level = 100, + biter = true, + type = 'entity', + mana_cost = 800, + tick = 1420, + enabled = false, + sprite = 'entity/biter-spawner', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'entity-name.spitter-spawner'}, + entityName = 'spitter-spawner', + level = 100, + biter = true, + type = 'entity', + mana_cost = 800, + tick = 1420, + enabled = false, + sprite = 'entity/spitter-spawner', + callback = function(data) + create_entity(data) + end +} + +spells[#spells + 1] = { + name = {'item-name.shotgun-shell'}, + entityName = 'shotgun-shell', + target = true, + amount = 1, + damage = true, + force = 'player', + level = 10, + type = 'item', + mana_cost = 40, + tick = 150, + enabled = true, + sprite = 'recipe/shotgun-shell', + callback = function(data) + create_projectiles(data) + end +} +spells[#spells + 1] = { + name = {'item-name.grenade'}, + entityName = 'grenade', + target = true, + amount = 1, + damage = true, + force = 'player', + level = 30, + type = 'item', + mana_cost = 100, + tick = 150, + enabled = true, + sprite = 'recipe/grenade', + callback = function(data) + create_projectiles(data) + end +} +spells[#spells + 1] = { + name = {'item-name.cluster-grenade'}, + entityName = 'cluster-grenade', + target = true, + amount = 2, + damage = true, + force = 'player', + level = 50, + type = 'item', + mana_cost = 225, + tick = 200, + enabled = true, + sprite = 'recipe/cluster-grenade', + callback = function(data) + create_projectiles(data) + end +} +spells[#spells + 1] = { + name = {'item-name.cannon-shell'}, + entityName = 'cannon-shell', + target = true, + amount = 1, + damage = true, + force = 'player', + level = 30, + type = 'item', + mana_cost = 125, + tick = 150, + enabled = true, + sprite = 'recipe/cannon-shell', + callback = function(data) + create_projectiles(data) + end +} +spells[#spells + 1] = { + name = {'item-name.explosive-cannon-shell'}, + entityName = 'explosive-cannon-shell', + target = true, + amount = 2, + damage = true, + force = 'player', + level = 50, + type = 'item', + mana_cost = 250, + tick = 200, + enabled = true, + sprite = 'recipe/explosive-cannon-shell', + callback = function(data) + create_projectiles(data) + end +} +spells[#spells + 1] = { + name = {'item-name.uranium-cannon-shell'}, + entityName = 'uranium-cannon-shell', + target = true, + amount = 2, + damage = true, + force = 'player', + level = 70, + type = 'item', + mana_cost = 400, + tick = 200, + enabled = true, + sprite = 'recipe/uranium-cannon-shell', + callback = function(data) + create_projectiles(data) + end +} +spells[#spells + 1] = { + name = {'item-name.rocket'}, + entityName = 'rocket', + range = 240, + target = true, + amount = 4, + damage = true, + force = 'enemy', + level = 40, + type = 'item', + mana_cost = 60, + tick = 320, + enabled = true, + sprite = 'recipe/rocket', + callback = function(data) + create_projectiles(data) + end +} +spells[#spells + 1] = { + name = {'spells.pointy_explosives'}, + entityName = 'pointy_explosives', + target = true, + amount = 1, + range = 0, + damage = true, + force = 'player', + level = 70, + type = 'special', + mana_cost = 100, + tick = 100, + enabled = true, + sprite = 'recipe/explosives', + callback = function(data) + local self = data.self + local player = data.player + local Explosives = data.explosives + local position = data.position + + local entities = + player.surface.find_entities_filtered { + force = player.force, + type = 'container', + area = {{position.x - 1, position.y - 1}, {position.x + 1, position.y + 1}} + } + + local detonate_chest + for i = 1, #entities do + local e = entities[i] + detonate_chest = e + end + if detonate_chest and detonate_chest.valid then + local success = Explosives.detonate_chest(detonate_chest) + if success then + Public.remove_mana(player, self.mana_cost) + end + Public.cast_spell(player) + end + end +} +spells[#spells + 1] = { + name = {'spells.repair_aoe'}, + entityName = 'repair_aoe', + target = true, + amount = 1, + range = 50, + damage = false, + force = 'player', + level = 45, + type = 'special', + mana_cost = 150, + tick = 100, + enabled = true, + sprite = 'recipe/repair-pack', + callback = function(data) + local self = data.self + local player = data.player + local position = data.position + + Public.repair_aoe(player, position) + Public.cast_spell(player) + Public.remove_mana(player, self.mana_cost) + end +} +spells[#spells + 1] = { + name = {'spells.acid_stream'}, + entityName = 'acid-stream-spitter-big', + target = true, + amount = 2, + range = 0, + damage = true, + force = 'player', + level = 50, + type = 'special', + mana_cost = 70, + tick = 100, + enabled = true, + sprite = 'virtual-signal/signal-S', + callback = function(data) + create_projectiles(data) + end +} +spells[#spells + 1] = { + name = {'spells.tank'}, + entityName = 'tank', + amount = 1, + capsule = true, + force = 'player', + level = 1000, + type = 'special', + mana_cost = 10000, -- they who know, will know + tick = 320, + enabled = false, + sprite = 'entity/tank', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'spells.spidertron'}, + entityName = 'spidertron', + amount = 1, + capsule = true, + force = 'player', + level = 2000, + type = 'special', + mana_cost = 19500, -- they who know, will know + tick = 320, + enabled = false, + sprite = 'entity/spidertron', + callback = function(data) + create_entity(data) + end +} +spells[#spells + 1] = { + name = {'spells.raw_fish'}, + entityName = 'raw-fish', + target = false, + amount = 4, + capsule = true, + damage = false, + range = 30, + force = 'player', + level = 50, + type = 'special', + mana_cost = 140, + tick = 320, + enabled = true, + sprite = 'item/raw-fish', + callback = function(data) + insert_onto(data) + end +} +spells[#spells + 1] = { + name = {'spells.dynamites'}, + entityName = 'explosives', + target = false, + amount = 3, + capsule = true, + damage = false, + range = 30, + force = 'player', + level = 25, + type = 'special', + mana_cost = 140, + tick = 320, + enabled = true, + sprite = 'item/explosives', + callback = function(data) + insert_onto(data) + end +} +spells[#spells + 1] = { + name = {'spells.comfylatron'}, + entityName = 'suicidal_comfylatron', + target = false, + amount = 4, + damage = false, + range = 30, + force = 'player', + level = 60, + type = 'special', + mana_cost = 150, + tick = 320, + enabled = true, + sprite = 'entity/compilatron', + callback = function(data) + local self = data.self + local player = data.player + local position = data.position + local surface = data.surface + + Public.suicidal_comfylatron(position, surface) + Public.cast_spell(player) + Public.remove_mana(player, self.mana_cost) + end +} +spells[#spells + 1] = { + name = {'spells.distractor'}, + entityName = 'distractor-capsule', + target = true, + amount = 1, + damage = false, + range = 30, + force = 'player', + level = 50, + type = 'special', + mana_cost = 220, + tick = 320, + enabled = true, + sprite = 'recipe/distractor-capsule', + callback = function(data) + create_projectiles(data) + end +} +spells[#spells + 1] = { + name = {'spells.warp'}, + entityName = 'warp-gate', + target = true, + force = 'player', + level = 60, + type = 'special', + mana_cost = 340, + tick = 2000, + enabled = true, + sprite = 'virtual-signal/signal-W', + callback = function(data) + local player = data.player + local surface = data.surface + + local pos = surface.find_non_colliding_position('character', game.forces.player.get_spawn_position(surface), 3, 0, 5) + if pos then + player.teleport(pos, surface) + else + pos = game.forces.player.get_spawn_position(surface) + player.teleport(pos, surface) + end + Public.remove_mana(player, 999999) + Public.damage_player_over_time(player, random(8, 16)) + player.play_sound {path = 'utility/armor_insert', volume_modifier = 1} + Public.cast_spell(player) + end +} + +Public.projectile_types = { + ['explosives'] = {name = 'grenade', count = 0.5, max_range = 32, tick_speed = 1}, + ['land-mine'] = {name = 'grenade', count = 1, max_range = 32, tick_speed = 1}, + ['grenade'] = {name = 'grenade', count = 1, max_range = 40, tick_speed = 1}, + ['cluster-grenade'] = {name = 'cluster-grenade', count = 1, max_range = 40, tick_speed = 3}, + ['artillery-shell'] = {name = 'artillery-projectile', count = 1, max_range = 60, tick_speed = 3}, + ['cannon-shell'] = {name = 'cannon-projectile', count = 1, max_range = 60, tick_speed = 1}, + ['explosive-cannon-shell'] = {name = 'explosive-cannon-projectile', count = 1, max_range = 60, tick_speed = 1}, + ['explosive-uranium-cannon-shell'] = { + name = 'explosive-uranium-cannon-projectile', + count = 1, + max_range = 60, + tick_speed = 1 + }, + ['uranium-cannon-shell'] = {name = 'uranium-cannon-projectile', count = 1, max_range = 60, tick_speed = 1}, + ['atomic-bomb'] = {name = 'atomic-rocket', count = 1, max_range = 80, tick_speed = 20}, + ['explosive-rocket'] = {name = 'explosive-rocket', count = 1, max_range = 48, tick_speed = 1}, + ['rocket'] = {name = 'rocket', count = 1, max_range = 48, tick_speed = 1}, + ['flamethrower-ammo'] = {name = 'flamethrower-fire-stream', count = 4, max_range = 28, tick_speed = 1}, + ['crude-oil-barrel'] = {name = 'flamethrower-fire-stream', count = 3, max_range = 24, tick_speed = 1}, + ['petroleum-gas-barrel'] = {name = 'flamethrower-fire-stream', count = 4, max_range = 24, tick_speed = 1}, + ['light-oil-barrel'] = {name = 'flamethrower-fire-stream', count = 4, max_range = 24, tick_speed = 1}, + ['heavy-oil-barrel'] = {name = 'flamethrower-fire-stream', count = 4, max_range = 24, tick_speed = 1}, + ['acid-stream-spitter-big'] = { + name = 'acid-stream-spitter-big', + count = 3, + max_range = 16, + tick_speed = 1, + force = 'enemy' + }, + ['lubricant-barrel'] = {name = 'acid-stream-spitter-big', count = 3, max_range = 16, tick_speed = 1}, + ['shotgun-shell'] = {name = 'shotgun-pellet', count = 16, max_range = 24, tick_speed = 1}, + ['piercing-shotgun-shell'] = {name = 'piercing-shotgun-pellet', count = 16, max_range = 24, tick_speed = 1}, + ['firearm-magazine'] = {name = 'shotgun-pellet', count = 16, max_range = 24, tick_speed = 1}, + ['piercing-rounds-magazine'] = {name = 'piercing-shotgun-pellet', count = 16, max_range = 24, tick_speed = 1}, + ['uranium-rounds-magazine'] = {name = 'piercing-shotgun-pellet', count = 32, max_range = 24, tick_speed = 1}, + ['cliff-explosives'] = {name = 'cliff-explosives', count = 1, max_range = 48, tick_speed = 2} +} + +Public.get_projectiles = Public.projectile_types +Public.spells = spells + +--- Retrieves the spells table or a given spell. +---@param key string +function Public.get_spells(key) + if game then + return error('Calling Public.get_spells() after on_init() or on_load() has run is a desync risk.', 2) + end + if Public.spells[key] then + return Public.spells[key] + else + return Public.spells + end +end + +--- Disables a spell. +---@param key string/number +-- Table would look like: +-- Public.disable_spell({1, 2, 3, 4, 5, 6, 7, 8}) +function Public.disable_spell(key) + if game then + return error('Calling Public.disable_spell() after on_init() or on_load() has run is a desync risk.', 2) + end + + if type(key) == 'table' then + for _, k in pairs(key) do + Public.spells[k].enabled = false + end + elseif Public.spells[key] then + Public.spells[key].enabled = false + end +end + +--- Clears the spell table. +function Public.clear_spell_table() + if game then + return error('Calling Public.clear_spell_table() after on_init() or on_load() has run is a desync risk.', 2) + end + + Public.spells = {} +end + +--- Adds a spell to the rpg_spells +---@param tbl table +function Public.set_new_spell(tbl) + if game then + return error('Calling Public.set_new_spell() after on_init() or on_load() has run is a desync risk.', 2) + end + + if tbl then + if not tbl.name then + return error('A spell requires a name. string', 2) + end + if not tbl.entityName then + return error('A spell requires an object to create. string', 2) + end + if not tbl.target then + return error('A spell requires position. boolean', 2) + end + if not tbl.amount then + return error('A spell requires an amount of creation. ', 2) + end + if not tbl.range then + return error('A spell requires a range. ', 2) + end + if not tbl.damage then + return error('A spell requires damage. ', 2) + end + if not tbl.force then + return error('A spell requires a force. string', 2) + end + if not tbl.level then + return error('A spell requires a level. ', 2) + end + if not tbl.type then + return error('A spell requires a type. ', 2) + end + if not tbl.mana_cost then + return error('A spell requires mana_cost. ', 2) + end + if not tbl.tick then + return error('A spell requires tick. ', 2) + end + if not tbl.enabled then + return error('A spell requires enabled. boolean', 2) + end + + Public.spells[#Public.spells + 1] = tbl + end +end + +--- This rebuilds all spells. Make sure to make changes on_init if you don't +-- want all spells enabled. +function Public.rebuild_spells() + local new_spells = {} + local spell_names = {} + + for i = 1, #spells do + if spells[i].enabled then + new_spells[#new_spells + 1] = spells[i] + spell_names[#spell_names + 1] = spells[i].name + end + end + + return new_spells, spell_names +end + +--- This will disable the cooldown of all spells. +function Public.disable_cooldowns_on_spells() + if game then + return error('Calling Public.disable_cooldowns_on_spells() after on_init() or on_load() has run is a desync risk.', 2) + end + + local new_spells = {} + + for i = 1, #spells do + if spells[i].enabled then + spells[i].tick = 0 + new_spells[#new_spells + 1] = spells[i] + end + end + + Public.spells = new_spells + + return new_spells +end + +return Public