mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 10:43:20 +00:00
Ports advanced shield generators from Baystation
- Creates new advanced shield generators, designed to replace old hull and bubble shield generators. - Upgrades the floor mounted and handheld shield diffusers. - Makes underfloor shield diffusers actually constructable. - Handheld diffusers orderable via uplink. - Removes supply packs and research datums for the old generators, but leaves their code in place for maps that still use them. - Integrates with the meteor and electrical storm events. - Integrates with mob AI (they know how to attack it)
This commit is contained in:
49
code/__defines/shields.dm
Normal file
49
code/__defines/shields.dm
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
#define SHIELD_DAMTYPE_PHYSICAL 1 // Physical damage - bullets, meteors, various hand objects - aka. "brute" damtype.
|
||||
#define SHIELD_DAMTYPE_EM 2 // Electromagnetic damage - Ion weaponry, stun beams, ...
|
||||
#define SHIELD_DAMTYPE_HEAT 3 // Heat damage - Lasers, fire
|
||||
|
||||
// TODO - Thats power not energy you silly goose! I think we mean 45 kilojoules
|
||||
#define ENERGY_PER_HP (45 KILOWATTS)// Base amount energy that will be deducted from the generator's internal reserve per 1 HP of damage taken
|
||||
#define ENERGY_UPKEEP_PER_TILE (4 KILOWATTS) // Base upkeep per tile protected. Multiplied by various enabled shield modes. Without them the field does literally nothing.
|
||||
#define ENERGY_UPKEEP_IDLE 45 // Base upkeep when idle; modified by other factors.
|
||||
|
||||
// This shield model is slightly inspired by Sins of a Solar Empire series. In short, shields are designed to analyze what hits them, and adapt themselves against that type of damage.
|
||||
// This means shields will become increasingly effective against things like emitters - as they will adapt to heat damage, however they will be vulnerable to brute and EM damage.
|
||||
// In a theoretical assault scenario, it is best to combine all damage types, so mitigation can't build up. The value is capped to prevent full scale invulnerability.
|
||||
|
||||
#define MAX_MITIGATION_BASE 40 // % Base maximal reachable mitigation.
|
||||
#define MAX_MITIGATION_RESEARCH 10 // % Added to MAX_MITIGATION_BASE when generator is built using more advanced components. This value is added for each "tier" of used component, ie. basic one has 1, the best one has 5. Actual maximum should be 90% in this case (with best components). Make sure you won't get above 100%!
|
||||
#define MITIGATION_HIT_GAIN 5 // Mitigation gain per hit of respective damage type.
|
||||
#define MITIGATION_HIT_LOSS 4 // Mitigation loss per hit. If we get hit once by EM damage type, EM mitigation will grow, while Physical and Heat mitigation values drop.
|
||||
#define MITIGATION_LOSS_PASSIVE 0.5 // Mitigation of all damage types will drop by this every tick, up to 0.
|
||||
|
||||
// Shield modes allow you to calibrate the field to fit specific needs. It is, for example, possible to create a field that will block airflow, but let people pass.
|
||||
// Relevant mode bitflags (maximal of 16 flags due to current BYOND limitations)
|
||||
#define MODEFLAG_HYPERKINETIC 1
|
||||
#define MODEFLAG_PHOTONIC 2
|
||||
#define MODEFLAG_NONHUMANS 4
|
||||
#define MODEFLAG_HUMANOIDS 8
|
||||
#define MODEFLAG_ANORGANIC 16
|
||||
#define MODEFLAG_ATMOSPHERIC 32
|
||||
#define MODEFLAG_HULL 64
|
||||
#define MODEFLAG_BYPASS 128
|
||||
#define MODEFLAG_OVERCHARGE 256
|
||||
#define MODEFLAG_MODULATE 512
|
||||
#define MODEFLAG_MULTIZ 1024
|
||||
#define MODEFLAG_EM 2048
|
||||
|
||||
// Return codes for shield hits.
|
||||
#define SHIELD_ABSORBED 1 // The shield has completely absorbed the hit
|
||||
#define SHIELD_BREACHED_MINOR 2 // The hit was absorbed, but a small gap will be created in the field (1-3 tiles)
|
||||
#define SHIELD_BREACHED_MAJOR 3 // Same as above, with 2-5 tile gap
|
||||
#define SHIELD_BREACHED_CRITICAL 4 // Same as above, with 4-8 tile gap
|
||||
#define SHIELD_BREACHED_FAILURE 5 // Same as above, with 8-16 tile gap. Occurs when the hit exhausts all remaining shield energy.
|
||||
|
||||
#define SHIELD_OFF 0 // The shield is offline
|
||||
#define SHIELD_DISCHARGING 1 // The shield is shutting down and discharging.
|
||||
#define SHIELD_RUNNING 2 // The shield is running
|
||||
#define SHIELD_IDLE 3 // The shield is being kept at an idle state
|
||||
#define SHIELD_SPINNING_UP 4 // Going from idle to running
|
||||
|
||||
#define SHIELD_SHUTDOWN_DISPERSION_RATE (10000 KILOWATTS) // The rate at which shield energy disperses when shutdown is initiated.
|
||||
@@ -35,48 +35,6 @@
|
||||
containertype = /obj/structure/closet/crate/engineering
|
||||
containername = "Superconducting Transmission Coil crate"
|
||||
|
||||
/datum/supply_pack/eng/shield_capacitor
|
||||
name = "Shield Capacitor"
|
||||
contains = list(/obj/machinery/shield_capacitor)
|
||||
cost = 20
|
||||
containertype = /obj/structure/closet/crate/engineering
|
||||
containername = "shield capacitor crate"
|
||||
|
||||
/datum/supply_pack/eng/shield_capacitor/advanced
|
||||
name = "Advanced Shield Capacitor"
|
||||
contains = list(/obj/machinery/shield_capacitor/advanced)
|
||||
cost = 30
|
||||
containertype = /obj/structure/closet/crate/engineering
|
||||
containername = "advanced shield capacitor crate"
|
||||
|
||||
/datum/supply_pack/eng/bubble_shield
|
||||
name = "Bubble Shield Generator"
|
||||
contains = list(/obj/machinery/shield_gen)
|
||||
cost = 40
|
||||
containertype = /obj/structure/closet/crate/engineering
|
||||
containername = "shield bubble generator crate"
|
||||
|
||||
/datum/supply_pack/eng/bubble_shield/advanced
|
||||
name = "Advanced Bubble Shield Generator"
|
||||
contains = list(/obj/machinery/shield_gen/advanced)
|
||||
cost = 60
|
||||
containertype = /obj/structure/closet/crate/engineering
|
||||
containername = "advanced bubble shield generator crate"
|
||||
|
||||
/datum/supply_pack/eng/hull_shield
|
||||
name = "Hull Shield Generator"
|
||||
contains = list(/obj/machinery/shield_gen/external)
|
||||
cost = 80
|
||||
containertype = /obj/structure/closet/crate/engineering
|
||||
containername = "shield hull generator crate"
|
||||
|
||||
/datum/supply_pack/eng/hull_shield/advanced
|
||||
name = "Advanced Hull Shield Generator"
|
||||
contains = list(/obj/machinery/shield_gen/external/advanced)
|
||||
cost = 120
|
||||
containertype = /obj/structure/closet/crate/engineering
|
||||
containername = "advanced hull shield generator crate"
|
||||
|
||||
/datum/supply_pack/eng/electrical
|
||||
name = "Electrical maintenance crate"
|
||||
contains = list(
|
||||
@@ -173,29 +131,19 @@
|
||||
containername = "Particle Accelerator crate"
|
||||
access = access_ce
|
||||
|
||||
/datum/supply_pack/eng/shield_gen
|
||||
contains = list(/obj/item/weapon/circuitboard/shield_gen)
|
||||
name = "Bubble shield generator circuitry"
|
||||
cost = 30
|
||||
containertype = /obj/structure/closet/crate/secure/engineering
|
||||
containername = "bubble shield generator circuitry crate"
|
||||
access = access_ce
|
||||
|
||||
/datum/supply_pack/eng/shield_gen_ex
|
||||
contains = list(/obj/item/weapon/circuitboard/shield_gen_ex)
|
||||
name = "Hull shield generator circuitry"
|
||||
cost = 30
|
||||
containertype = /obj/structure/closet/crate/secure/engineering
|
||||
containername = "hull shield generator circuitry crate"
|
||||
access = access_ce
|
||||
|
||||
/datum/supply_pack/eng/shield_cap
|
||||
contains = list(/obj/item/weapon/circuitboard/shield_cap)
|
||||
name = "Bubble shield capacitor circuitry"
|
||||
cost = 30
|
||||
containertype = /obj/structure/closet/crate/secure/engineering
|
||||
containername = "shield capacitor circuitry crate"
|
||||
access = access_ce
|
||||
/datum/supply_pack/eng/shield_generator
|
||||
name = "Shield Generator Construction Kit"
|
||||
contains = list(
|
||||
/obj/item/weapon/circuitboard/shield_generator,
|
||||
/obj/item/weapon/stock_parts/capacitor,
|
||||
/obj/item/weapon/stock_parts/micro_laser,
|
||||
/obj/item/weapon/smes_coil,
|
||||
/obj/item/weapon/stock_parts/console_screen,
|
||||
/obj/item/weapon/stock_parts/subspace/amplifier
|
||||
)
|
||||
cost = 80
|
||||
containertype = /obj/structure/closet/crate/engineering
|
||||
containername = "shield generator construction kit crate"
|
||||
|
||||
/datum/supply_pack/eng/smbig
|
||||
name = "Supermatter Core"
|
||||
|
||||
@@ -53,6 +53,12 @@
|
||||
name = "Black Ammunition Duffle Bag"
|
||||
path = /obj/item/weapon/storage/backpack/dufflebag/syndie/ammo
|
||||
|
||||
/datum/uplink_item/item/tools/shield_diffuser
|
||||
name = "Handheld Shield Diffuser"
|
||||
desc = "A small device used to disrupt energy barriers, and allow passage through them."
|
||||
item_cost = 16
|
||||
path = /obj/item/weapon/shield_diffuser
|
||||
|
||||
/datum/uplink_item/item/tools/space_suit
|
||||
name = "Space Suit"
|
||||
item_cost = 15
|
||||
|
||||
46
code/datums/wires/shield_generator.dm
Normal file
46
code/datums/wires/shield_generator.dm
Normal file
@@ -0,0 +1,46 @@
|
||||
/datum/wires/shield_generator
|
||||
holder_type = /obj/machinery/power/shield_generator
|
||||
wire_count = 5
|
||||
|
||||
var/const/SHIELDGEN_WIRE_POWER = 1 // Cut to disable power input into the generator. Pulse does nothing. Mend to restore.
|
||||
var/const/SHIELDGEN_WIRE_HACK = 2 // Pulse to hack the generator, enabling hacked modes. Cut to unhack. Mend does nothing.
|
||||
var/const/SHIELDGEN_WIRE_CONTROL = 4 // Cut to lock most shield controls. Mend to unlock them. Pulse does nothing.
|
||||
var/const/SHIELDGEN_WIRE_AICONTROL = 8 // Cut to disable AI control. Mend to restore.
|
||||
var/const/SHIELDGEN_WIRE_NOTHING = 16 // A blank wire that doesn't have any specific function
|
||||
|
||||
/datum/wires/shield_generator/CanUse(var/mob/living/L)
|
||||
var/obj/machinery/power/shield_generator/S = holder
|
||||
if(S.panel_open)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/wires/shield_generator/GetInteractWindow()
|
||||
var/obj/machinery/power/shield_generator/S = holder
|
||||
. += ..()
|
||||
. += show_hint(0x1, S.mode_changes_locked, "The orange light is on.", "The orange light is off.")
|
||||
. += show_hint(0x2, S.ai_control_disabled, "The blue light is off.", "The blue light is blinking.")
|
||||
. += show_hint(0x4, S.hacked, "The violet light is pulsing.", "The violet light is steady.")
|
||||
. += show_hint(0x8, S.input_cut, "The red light is off.", "The red light is on.")
|
||||
|
||||
/datum/wires/shield_generator/UpdateCut(index, mended)
|
||||
var/obj/machinery/power/shield_generator/S = holder
|
||||
switch(index)
|
||||
if(SHIELDGEN_WIRE_POWER)
|
||||
S.input_cut = !mended
|
||||
if(SHIELDGEN_WIRE_HACK)
|
||||
if(!mended)
|
||||
S.hacked = 0
|
||||
if(S.check_flag(MODEFLAG_BYPASS))
|
||||
S.toggle_flag(MODEFLAG_BYPASS)
|
||||
if(S.check_flag(MODEFLAG_OVERCHARGE))
|
||||
S.toggle_flag(MODEFLAG_OVERCHARGE)
|
||||
if(SHIELDGEN_WIRE_CONTROL)
|
||||
S.mode_changes_locked = !mended
|
||||
if(SHIELDGEN_WIRE_AICONTROL)
|
||||
S.ai_control_disabled = !mended
|
||||
|
||||
/datum/wires/shield_generator/UpdatePulsed(var/index)
|
||||
var/obj/machinery/power/shield_generator/S = holder
|
||||
switch(index)
|
||||
if(SHIELDGEN_WIRE_HACK)
|
||||
S.hacked = 1
|
||||
@@ -183,6 +183,8 @@
|
||||
var/turf/simulated/wall/W = T
|
||||
W.take_damage(wall_power) // Stronger walls can halt asteroids.
|
||||
|
||||
/obj/effect/meteor/proc/get_shield_damage()
|
||||
return max(((max(hits, 2)) * (heavy + 1) * rand(6, 12)) / hitpwr , 0)
|
||||
|
||||
//process getting 'hit' by colliding with a dense object
|
||||
//or randomly when ramming turfs
|
||||
@@ -307,6 +309,9 @@
|
||||
// Worst case scenario: Comparable to a standard yield EMP grenade.
|
||||
empulse(src, rand(1, 3), rand(2, 4), rand(3, 7), rand(5, 10))
|
||||
|
||||
/obj/effect/meteor/emp/get_shield_damage()
|
||||
return ..() * rand(2,4)
|
||||
|
||||
//Station buster Tunguska
|
||||
/obj/effect/meteor/tunguska
|
||||
name = "tunguska meteor"
|
||||
|
||||
@@ -2,6 +2,37 @@
|
||||
#error T_BOARD macro is not defined but we need it!
|
||||
#endif
|
||||
|
||||
//
|
||||
// New shield generator
|
||||
//
|
||||
|
||||
/obj/item/weapon/circuitboard/shield_generator
|
||||
name = T_BOARD("advanced shield generator")
|
||||
board_type = new /datum/frame/frame_types/machine
|
||||
build_path = /obj/machinery/power/shield_generator
|
||||
origin_tech = list(TECH_MAGNET = 3, TECH_POWER = 4, TECH_BLUESPACE = 2, TECH_ENGINEERING = 3)
|
||||
req_components = list(
|
||||
/obj/item/weapon/stock_parts/capacitor = 1,
|
||||
/obj/item/weapon/stock_parts/micro_laser = 1,
|
||||
/obj/item/weapon/smes_coil = 1,
|
||||
/obj/item/weapon/stock_parts/console_screen = 1,
|
||||
/obj/item/weapon/stock_parts/subspace/amplifier = 1,
|
||||
/obj/item/stack/cable_coil = 5)
|
||||
|
||||
/obj/item/weapon/circuitboard/shield_diffuser
|
||||
name = T_BOARD("shield diffuser")
|
||||
board_type = new /datum/frame/frame_types/machine
|
||||
build_path = /obj/machinery/shield_diffuser
|
||||
origin_tech = list(TECH_MAGNET = 4, TECH_POWER = 2, TECH_ILLEGAL = 1)
|
||||
req_components = list(
|
||||
/obj/item/weapon/stock_parts/capacitor = 1,
|
||||
/obj/item/weapon/stock_parts/micro_laser = 1,
|
||||
/obj/item/weapon/stock_parts/console_screen = 1)
|
||||
|
||||
//
|
||||
// Legacy shield generators
|
||||
//
|
||||
|
||||
/obj/item/weapon/circuitboard/shield_gen_ex
|
||||
name = T_BOARD("hull shield generator")
|
||||
board_type = new /datum/frame/frame_types/machine
|
||||
|
||||
@@ -285,6 +285,12 @@
|
||||
ai_log("destroy_surroundings() : Attacking hull shield.", AI_LOG_INFO)
|
||||
return melee_attack(shield)
|
||||
|
||||
// Kill energy shields in the way.
|
||||
for(var/obj/effect/shield/S in problem_turf)
|
||||
if(S.density) // Don't attack shields that are already down.
|
||||
ai_log("destroy_surroundings() : Attacking energy shield.", AI_LOG_INFO)
|
||||
return melee_attack(S)
|
||||
|
||||
// Kill common obstacle in the way like tables.
|
||||
var/obj/structure/obstacle = locate(/obj/structure, problem_turf)
|
||||
if(istype(obstacle, /obj/structure/window) || istype(obstacle, /obj/structure/closet) || istype(obstacle, /obj/structure/table) || istype(obstacle, /obj/structure/grille))
|
||||
|
||||
@@ -35,8 +35,16 @@
|
||||
|
||||
/datum/event/electrical_storm/tick()
|
||||
..()
|
||||
// See if shields can stop it first (It would be nice to port baystation's cooler shield gens perhaps)
|
||||
// TODO - We need a better shield generator system to handle this properly.
|
||||
// See if shields can stop it first
|
||||
var/list/shields = list()
|
||||
for(var/obj/machinery/power/shield_generator/G in global.machines)
|
||||
if((G.z in affecting_z) && G.running && G.check_flag(MODEFLAG_EM))
|
||||
shields += G
|
||||
if(shields.len)
|
||||
var/obj/machinery/power/shield_generator/shield_gen = pick(shields)
|
||||
//Minor breaches aren't enough to let through frying amounts of power
|
||||
if(shield_gen.deal_shield_damage(30 * severity, SHIELD_DAMTYPE_EM) <= SHIELD_BREACHED_MINOR)
|
||||
return
|
||||
if(!valid_apcs.len)
|
||||
log_debug("No valid APCs found for electrical storm event ship=[victim]!")
|
||||
return
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
startWhen = 30 // About one minute early warning
|
||||
endWhen = 60 // Adjusted automatically in tick()
|
||||
has_skybox_image = TRUE
|
||||
var/next_meteor = 6
|
||||
var/alarmWhen = 30
|
||||
var/next_meteor = 40
|
||||
var/waves = 1
|
||||
var/start_side
|
||||
var/next_meteor_lower = 10
|
||||
@@ -32,6 +33,12 @@
|
||||
command_announcement.Announce("\The [location_name()] is now in a meteor shower.", "Meteor Alert")
|
||||
|
||||
/datum/event/meteor_wave/tick()
|
||||
// Begin sending the alarm signals to shield diffusers so the field is already regenerated (if it exists) by the time actual meteors start flying around.
|
||||
if(activeFor >= alarmWhen)
|
||||
for(var/obj/machinery/shield_diffuser/SD in global.machines)
|
||||
if(SD.z in affecting_z)
|
||||
SD.meteor_alarm(10)
|
||||
|
||||
if(waves && activeFor >= next_meteor)
|
||||
send_wave()
|
||||
|
||||
@@ -105,6 +112,7 @@
|
||||
next_meteor_lower = 5
|
||||
next_meteor_upper = 10
|
||||
next_meteor = 0
|
||||
alarmWhen = 0
|
||||
|
||||
/datum/event/meteor_wave/overmap/tick()
|
||||
if(victim && !victim.is_still()) // Meteors mostly fly in your face
|
||||
|
||||
@@ -570,36 +570,6 @@ CIRCUITS BELOW
|
||||
build_path = /obj/item/weapon/circuitboard/telecomms/exonet_node
|
||||
sort_string = "PAAAH"
|
||||
|
||||
/datum/design/circuit/shield
|
||||
req_tech = list(TECH_BLUESPACE = 4, TECH_PHORON = 3)
|
||||
materials = list("glass" = 2000, "gold" = 1000)
|
||||
|
||||
/datum/design/circuit/shield/AssembleDesignName()
|
||||
name = "Shield generator circuit design ([name])"
|
||||
/datum/design/circuit/shield/AssembleDesignDesc()
|
||||
if(!desc)
|
||||
desc = "Allows for the construction of \a [name] shield generator."
|
||||
|
||||
/datum/design/circuit/shield/bubble
|
||||
name = "bubble"
|
||||
id = "shield_gen"
|
||||
build_path = /obj/item/weapon/circuitboard/shield_gen
|
||||
sort_string = "VAAAZ" // Duplicate string, really need to redo this whole thing
|
||||
|
||||
/datum/design/circuit/shield/hull
|
||||
name = "hull"
|
||||
id = "shield_gen_ex"
|
||||
build_path = /obj/item/weapon/circuitboard/shield_gen_ex
|
||||
sort_string = "VAAAB"
|
||||
|
||||
/datum/design/circuit/shield/capacitor
|
||||
name = "capacitor"
|
||||
desc = "Allows for the construction of a shield capacitor circuit board."
|
||||
id = "shield_cap"
|
||||
req_tech = list(TECH_MAGNET = 3, TECH_POWER = 4)
|
||||
build_path = /obj/item/weapon/circuitboard/shield_cap
|
||||
sort_string = "VAAAC"
|
||||
|
||||
/datum/design/circuit/ntnet_relay
|
||||
name = "NTNet Quantum Relay"
|
||||
id = "ntnet_relay"
|
||||
@@ -621,6 +591,20 @@ CIRCUITS BELOW
|
||||
build_path = /obj/item/weapon/circuitboard/microwave/advanced
|
||||
sort_string = "MAAAC"
|
||||
|
||||
/datum/design/circuit/shield_generator
|
||||
name = "shield generator"
|
||||
id = "shield_generator"
|
||||
req_tech = list(TECH_MAGNET = 3, TECH_POWER = 4, TECH_BLUESPACE = 2, TECH_ENGINEERING = 3)
|
||||
build_path = /obj/item/weapon/circuitboard/shield_generator
|
||||
sort_string = "OAAAA"
|
||||
|
||||
/datum/design/circuit/shield_diffuser
|
||||
name = "shield diffuser"
|
||||
id = "shield_diffuser"
|
||||
req_tech = list(TECH_MAGNET = 4, TECH_POWER = 2, TECH_ENGINEERING = 5)
|
||||
build_path = /obj/item/weapon/circuitboard/shield_diffuser
|
||||
sort_string = "OAAAB"
|
||||
|
||||
/datum/design/circuit/pointdefense
|
||||
name = "point defense battery"
|
||||
id = "pointdefense"
|
||||
@@ -634,12 +618,3 @@ CIRCUITS BELOW
|
||||
req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 3, TECH_COMBAT = 2)
|
||||
build_path = /obj/item/weapon/circuitboard/pointdefense_control
|
||||
sort_string = "OAABB"
|
||||
|
||||
/* I have no idea how this was even running before, but it doesn't seem to be necessary.
|
||||
///////////////////////////////////
|
||||
/////////Shield Generators/////////
|
||||
///////////////////////////////////
|
||||
/datum/design/circuit/shield
|
||||
req_tech = list(TECH_BLUESPACE = 4, TECH_PHORON = 3)
|
||||
materials = list("$glass" = 2000, "sacid" = 20, "$phoron" = 10000, "$diamond" = 5000, "$gold" = 10000)
|
||||
*/
|
||||
|
||||
338
code/modules/shieldgen/energy_shield.dm
Normal file
338
code/modules/shieldgen/energy_shield.dm
Normal file
@@ -0,0 +1,338 @@
|
||||
//
|
||||
// This is the shield effect object for the supercool shield gens.
|
||||
//
|
||||
/obj/effect/shield
|
||||
name = "energy shield"
|
||||
desc = "An impenetrable field of energy, capable of blocking anything as long as it's active."
|
||||
icon = 'icons/obj/machines/shielding.dmi'
|
||||
icon_state = "shield_normal"
|
||||
anchored = 1
|
||||
plane = MOB_PLANE
|
||||
layer = ABOVE_MOB_LAYER
|
||||
density = 1
|
||||
invisibility = 0
|
||||
var/obj/machinery/power/shield_generator/gen = null // Owning generator
|
||||
var/disabled_for = 0
|
||||
var/diffused_for = 0
|
||||
can_atmos_pass = ATMOS_PASS_YES
|
||||
|
||||
/obj/effect/shield/update_icon()
|
||||
if(gen && gen.check_flag(MODEFLAG_PHOTONIC) && !disabled_for && !diffused_for)
|
||||
set_opacity(1)
|
||||
else
|
||||
set_opacity(0)
|
||||
|
||||
if(gen && gen.check_flag(MODEFLAG_OVERCHARGE))
|
||||
icon_state = "shield_overcharged"
|
||||
else
|
||||
icon_state = "shield_normal"
|
||||
|
||||
if(density)
|
||||
set_light(3, 3, "#66FFFF")
|
||||
else
|
||||
set_light(0)
|
||||
|
||||
|
||||
// Prevents singularities and pretty much everything else from moving the field segments away.
|
||||
// The only thing that is allowed to move us is the Destroy() proc.
|
||||
/obj/effect/shield/forceMove()
|
||||
if(QDELING(src))
|
||||
return ..()
|
||||
return 0
|
||||
|
||||
/obj/effect/shield/Destroy()
|
||||
if(can_atmos_pass != ATMOS_PASS_YES)
|
||||
update_nearby_tiles()
|
||||
. = ..()
|
||||
if(gen)
|
||||
if(src in gen.field_segments)
|
||||
gen.field_segments -= src
|
||||
if(src in gen.damaged_segments)
|
||||
gen.damaged_segments -= src
|
||||
gen = null
|
||||
|
||||
// Temporarily collapses this shield segment.
|
||||
/obj/effect/shield/proc/fail(var/duration)
|
||||
if(duration <= 0)
|
||||
return
|
||||
|
||||
if(gen)
|
||||
gen.damaged_segments |= src
|
||||
disabled_for += duration
|
||||
set_density(0)
|
||||
set_invisibility(INVISIBILITY_MAXIMUM)
|
||||
update_nearby_tiles()
|
||||
update_icon()
|
||||
update_explosion_resistance()
|
||||
|
||||
|
||||
// Regenerates this shield segment.
|
||||
/obj/effect/shield/proc/regenerate()
|
||||
if(!gen)
|
||||
return
|
||||
|
||||
disabled_for = max(0, disabled_for - 1)
|
||||
diffused_for = max(0, diffused_for - 1)
|
||||
|
||||
if(!disabled_for && !diffused_for)
|
||||
set_density(1)
|
||||
set_invisibility(0)
|
||||
update_nearby_tiles()
|
||||
update_icon()
|
||||
update_explosion_resistance()
|
||||
gen.damaged_segments -= src
|
||||
|
||||
|
||||
/obj/effect/shield/proc/diffuse(var/duration)
|
||||
// The shield is trying to counter diffusers. Cause lasting stress on the shield.
|
||||
if(gen.check_flag(MODEFLAG_BYPASS) && !disabled_for)
|
||||
take_damage(duration * rand(8, 12), SHIELD_DAMTYPE_EM)
|
||||
return
|
||||
|
||||
diffused_for = max(duration, 0)
|
||||
gen.damaged_segments |= src
|
||||
set_density(0)
|
||||
set_invisibility(INVISIBILITY_MAXIMUM)
|
||||
update_nearby_tiles()
|
||||
update_icon()
|
||||
update_explosion_resistance()
|
||||
|
||||
/obj/effect/shield/attack_generic(var/source, var/damage, var/emote)
|
||||
take_damage(damage, SHIELD_DAMTYPE_PHYSICAL)
|
||||
if(gen.check_flag(MODEFLAG_OVERCHARGE) && istype(source, /mob/living/))
|
||||
overcharge_shock(source)
|
||||
..(source, damage, emote)
|
||||
|
||||
|
||||
// Fails shield segments in specific range. Range of 1 affects the shielded turf only.
|
||||
/obj/effect/shield/proc/fail_adjacent_segments(var/range, var/hitby = null)
|
||||
if(hitby)
|
||||
visible_message("<span class='danger'>\The [src] flashes a bit as \the [hitby] collides with it, eventually fading out in a rain of sparks!</span>")
|
||||
else
|
||||
visible_message("<span class='danger'>\The [src] flashes a bit as it eventually fades out in a rain of sparks!</span>")
|
||||
fail(range * 2)
|
||||
|
||||
for(var/obj/effect/shield/S in range(range, src))
|
||||
// Don't affect shields owned by other shield generators
|
||||
if(S.gen != src.gen)
|
||||
continue
|
||||
// The closer we are to impact site, the longer it takes for shield to come back up.
|
||||
S.fail(-(-range + get_dist(src, S)) * 2)
|
||||
|
||||
// Small visual effect, makes the shield tiles brighten up by becoming more opaque for a moment, and spreads to nearby shields.
|
||||
/obj/effect/shield/proc/flash_adjacent_segments(var/range)
|
||||
range = between(1, range, 10) // Sanity check
|
||||
for(var/obj/effect/shield/S in range(range, src))
|
||||
// Don't affect shields owned by other shield generators
|
||||
if(S.gen != src.gen || S == src)
|
||||
continue
|
||||
// Note: Range is a non-exact aproximation of the spread effect. If it doesn't look good
|
||||
// we'll need to switch to actually walking along the shields to get exact number of steps away.
|
||||
addtimer(CALLBACK(S, .proc/impact_flash), get_dist(src, S) * 2)
|
||||
impact_flash()
|
||||
|
||||
// Small visual effect, makes the shield tiles brighten up by becoming more opaque for a moment
|
||||
/obj/effect/shield/proc/impact_flash()
|
||||
alpha = 100
|
||||
animate(src, alpha = initial(alpha), time = 1 SECOND)
|
||||
|
||||
// Just for fun
|
||||
/obj/effect/shield/attack_hand(var/user)
|
||||
flash_adjacent_segments(3)
|
||||
|
||||
/obj/effect/shield/take_damage(var/damage, var/damtype, var/hitby)
|
||||
if(!gen)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
if(!damtype)
|
||||
crash_with("CANARY: shield.take_damage() callled without damtype.")
|
||||
|
||||
if(!damage)
|
||||
return
|
||||
|
||||
damage = round(damage)
|
||||
|
||||
new /obj/effect/temp_visual/shield_impact_effect(get_turf(src))
|
||||
|
||||
switch(gen.deal_shield_damage(damage, damtype))
|
||||
if(SHIELD_ABSORBED)
|
||||
flash_adjacent_segments(round(damage/10)) // Nice visual effect only.
|
||||
return
|
||||
if(SHIELD_BREACHED_MINOR)
|
||||
fail_adjacent_segments(rand(1, 3), hitby)
|
||||
return
|
||||
if(SHIELD_BREACHED_MAJOR)
|
||||
fail_adjacent_segments(rand(2, 5), hitby)
|
||||
return
|
||||
if(SHIELD_BREACHED_CRITICAL)
|
||||
fail_adjacent_segments(rand(4, 8), hitby)
|
||||
return
|
||||
if(SHIELD_BREACHED_FAILURE)
|
||||
fail_adjacent_segments(rand(8, 16), hitby)
|
||||
return
|
||||
|
||||
|
||||
// As we have various shield modes, this handles whether specific things can pass or not.
|
||||
/obj/effect/shield/CanPass(var/atom/movable/mover, var/turf/target)
|
||||
// Somehow we don't have a generator. This shouldn't happen. Delete the shield.
|
||||
if(!gen)
|
||||
qdel(src)
|
||||
return 1
|
||||
|
||||
if(disabled_for || diffused_for)
|
||||
return 1
|
||||
|
||||
if(mover)
|
||||
return mover.can_pass_shield(gen)
|
||||
return 1
|
||||
|
||||
/obj/effect/shield/proc/set_can_atmos_pass(var/new_value)
|
||||
if(new_value == can_atmos_pass)
|
||||
return
|
||||
can_atmos_pass = new_value
|
||||
update_nearby_tiles()
|
||||
|
||||
|
||||
// EMP. It may seem weak but keep in mind that multiple shield segments are likely to be affected.
|
||||
/obj/effect/shield/emp_act(var/severity)
|
||||
if(!disabled_for)
|
||||
take_damage(rand(30,60) / severity, SHIELD_DAMTYPE_EM)
|
||||
|
||||
|
||||
// Explosions
|
||||
/obj/effect/shield/ex_act(var/severity)
|
||||
if(!disabled_for)
|
||||
take_damage(rand(10,15) / severity, SHIELD_DAMTYPE_PHYSICAL)
|
||||
|
||||
|
||||
// Fire
|
||||
/obj/effect/shield/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
if(!disabled_for)
|
||||
take_damage(rand(5,10), SHIELD_DAMTYPE_HEAT)
|
||||
|
||||
|
||||
// Projectiles
|
||||
/obj/effect/shield/bullet_act(var/obj/item/projectile/proj)
|
||||
if(proj.damage_type == BURN)
|
||||
take_damage(proj.get_structure_damage(), SHIELD_DAMTYPE_HEAT)
|
||||
else if (proj.damage_type == BRUTE)
|
||||
take_damage(proj.get_structure_damage(), SHIELD_DAMTYPE_PHYSICAL)
|
||||
else
|
||||
take_damage(proj.get_structure_damage(), SHIELD_DAMTYPE_EM)
|
||||
|
||||
|
||||
// Attacks with hand tools. Blocked by Hyperkinetic flag.
|
||||
/obj/effect/shield/attackby(var/obj/item/weapon/I as obj, var/mob/user as mob)
|
||||
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
|
||||
user.do_attack_animation(src)
|
||||
|
||||
if(gen.check_flag(MODEFLAG_HYPERKINETIC))
|
||||
user.visible_message("<span class='danger'>\The [user] hits \the [src] with \the [I]!</span>")
|
||||
if(I.damtype == BURN)
|
||||
take_damage(I.force, SHIELD_DAMTYPE_HEAT)
|
||||
else if (I.damtype == BRUTE)
|
||||
take_damage(I.force, SHIELD_DAMTYPE_PHYSICAL)
|
||||
else
|
||||
take_damage(I.force, SHIELD_DAMTYPE_EM)
|
||||
else
|
||||
user.visible_message("<span class='danger'>\The [user] tries to attack \the [src] with \the [I], but it passes through!</span>")
|
||||
|
||||
|
||||
// Special treatment for meteors because they would otherwise penetrate right through the shield.
|
||||
/obj/effect/shield/Bumped(var/atom/movable/mover)
|
||||
if(!gen)
|
||||
qdel(src)
|
||||
return 0
|
||||
mover.shield_impact(src)
|
||||
return ..()
|
||||
|
||||
// Meteors call this instad of Bumped for some reason
|
||||
/obj/effect/shield/handle_meteor_impact(var/obj/effect/meteor/meteor)
|
||||
meteor.shield_impact(src)
|
||||
return !QDELETED(meteor) // If it was stopped it will have been deleted
|
||||
|
||||
/obj/effect/shield/proc/overcharge_shock(var/mob/living/M)
|
||||
M.adjustFireLoss(rand(20, 40))
|
||||
M.Weaken(5)
|
||||
to_chat(M, "<span class='danger'>As you come into contact with \the [src] a surge of energy paralyses you!</span>")
|
||||
take_damage(10, SHIELD_DAMTYPE_EM)
|
||||
|
||||
// Called when a flag is toggled. Can be used to add on-toggle behavior, such as visual changes.
|
||||
/obj/effect/shield/proc/flags_updated()
|
||||
if(!gen)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
// Update airflow - If atmospheric we block air as long as we're enabled (density works for this)
|
||||
set_can_atmos_pass(gen.check_flag(MODEFLAG_ATMOSPHERIC) ? ATMOS_PASS_DENSITY : ATMOS_PASS_YES)
|
||||
update_icon()
|
||||
update_explosion_resistance()
|
||||
|
||||
/obj/effect/shield/proc/update_explosion_resistance()
|
||||
if(gen && gen.check_flag(MODEFLAG_HYPERKINETIC))
|
||||
explosion_resistance = INFINITY
|
||||
else
|
||||
explosion_resistance = 0
|
||||
|
||||
//
|
||||
// Visual effect of shield taking impact
|
||||
//
|
||||
/obj/effect/temp_visual/shield_impact_effect
|
||||
name = "shield impact"
|
||||
icon = 'icons/obj/machines/shielding.dmi'
|
||||
icon_state = "shield_impact"
|
||||
plane = MOB_PLANE
|
||||
layer = ABOVE_MOB_LAYER
|
||||
duration = 2 SECONDS
|
||||
randomdir = FALSE
|
||||
|
||||
//
|
||||
// Shield collision checks below
|
||||
//
|
||||
|
||||
// Called only if shield is active/not destroyed etc.
|
||||
/atom/movable/proc/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
|
||||
return 1
|
||||
|
||||
|
||||
// Other mobs
|
||||
/mob/living/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
|
||||
return !gen.check_flag(MODEFLAG_NONHUMANS)
|
||||
|
||||
// Human mobs
|
||||
/mob/living/carbon/human/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
|
||||
if(isSynthetic())
|
||||
return !gen.check_flag(MODEFLAG_ANORGANIC)
|
||||
return !gen.check_flag(MODEFLAG_HUMANOIDS)
|
||||
|
||||
// Silicon mobs
|
||||
/mob/living/silicon/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
|
||||
return !gen.check_flag(MODEFLAG_ANORGANIC)
|
||||
|
||||
|
||||
// Generic objects. Also applies to bullets and meteors.
|
||||
/obj/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
|
||||
return !gen.check_flag(MODEFLAG_HYPERKINETIC)
|
||||
|
||||
// Beams
|
||||
/obj/item/projectile/beam/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
|
||||
return !gen.check_flag(MODEFLAG_PHOTONIC)
|
||||
|
||||
|
||||
// Shield on-impact logic here. This is called only if the object is actually blocked by the field (can_pass_shield applies first)
|
||||
/atom/movable/proc/shield_impact(var/obj/effect/shield/S)
|
||||
return
|
||||
|
||||
/mob/living/shield_impact(var/obj/effect/shield/S)
|
||||
if(!S.gen.check_flag(MODEFLAG_OVERCHARGE))
|
||||
return
|
||||
S.overcharge_shock(src)
|
||||
|
||||
/obj/effect/meteor/shield_impact(var/obj/effect/shield/S)
|
||||
if(!S.gen.check_flag(MODEFLAG_HYPERKINETIC))
|
||||
return
|
||||
S.take_damage(get_shield_damage(), SHIELD_DAMTYPE_PHYSICAL, src)
|
||||
visible_message("<span class='danger'>\The [src] breaks into dust!</span>")
|
||||
make_debris()
|
||||
qdel(src)
|
||||
@@ -1,20 +1,20 @@
|
||||
/obj/item/weapon/shield_diffuser
|
||||
name = "portable shield diffuser"
|
||||
desc = "A small handheld device designed to disrupt energy barriers"
|
||||
desc = "A small handheld device designed to disrupt energy barriers."
|
||||
description_info = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern), in a similar way the floor mounted variant does. It is, however, portable and run by an internal battery. Can be recharged with a regular recharger."
|
||||
icon = 'icons/obj/machines/shielding.dmi'
|
||||
icon_state = "hdiffuser_off"
|
||||
origin_tech = list(TECH_MAGNET = 5, TECH_POWER = 5, TECH_ILLEGAL = 2)
|
||||
var/obj/item/weapon/cell/device/cell
|
||||
var/enabled = 0
|
||||
|
||||
|
||||
/obj/item/weapon/shield_diffuser/New()
|
||||
/obj/item/weapon/shield_diffuser/Initialize()
|
||||
. = ..()
|
||||
cell = new(src)
|
||||
..()
|
||||
|
||||
/obj/item/weapon/shield_diffuser/Destroy()
|
||||
qdel(cell)
|
||||
cell = null
|
||||
QDEL_NULL(cell)
|
||||
if(enabled)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
. = ..()
|
||||
@@ -24,10 +24,15 @@
|
||||
|
||||
/obj/item/weapon/shield_diffuser/process()
|
||||
if(!enabled)
|
||||
return
|
||||
return PROCESS_KILL
|
||||
|
||||
for(var/direction in cardinal)
|
||||
var/turf/simulated/shielded_tile = get_step(get_turf(src), direction)
|
||||
for(var/obj/effect/shield/S in shielded_tile)
|
||||
// 10kJ per pulse, but gap in the shield lasts for longer than regular diffusers.
|
||||
if(istype(S) && !S.diffused_for && !S.disabled_for && cell.checked_use(10 KILOWATTS * CELLRATE))
|
||||
S.diffuse(20)
|
||||
// Legacy shield support
|
||||
for(var/obj/effect/energy_field/S in shielded_tile)
|
||||
if(istype(S) && cell.checked_use(10 KILOWATTS * CELLRATE))
|
||||
qdel(S)
|
||||
@@ -38,16 +43,16 @@
|
||||
else
|
||||
icon_state = "hdiffuser_off"
|
||||
|
||||
/obj/item/weapon/shield_diffuser/attack_self()
|
||||
/obj/item/weapon/shield_diffuser/attack_self(mob/user)
|
||||
enabled = !enabled
|
||||
update_icon()
|
||||
if(enabled)
|
||||
START_PROCESSING(SSobj, src)
|
||||
else
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].")
|
||||
to_chat(user, "You turn \the [src] [enabled ? "on" : "off"].")
|
||||
|
||||
/obj/item/weapon/shield_diffuser/examine()
|
||||
/obj/item/weapon/shield_diffuser/examine(mob/user)
|
||||
. = ..()
|
||||
to_chat(usr, "The charge meter reads [cell ? cell.percent() : 0]%")
|
||||
to_chat(usr, "It is [enabled ? "enabled" : "disabled"].")
|
||||
to_chat(user, "The charge meter reads [cell ? cell.percent() : 0]%")
|
||||
to_chat(user, "It is [enabled ? "enabled" : "disabled"].")
|
||||
@@ -4,6 +4,7 @@
|
||||
description_info = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern). They are commonly installed around exterior airlocks to prevent shields from blocking EVA access."
|
||||
icon = 'icons/obj/machines/shielding.dmi'
|
||||
icon_state = "fdiffuser_on"
|
||||
circuit = /obj/item/weapon/circuitboard/shield_diffuser
|
||||
use_power = USE_POWER_ACTIVE
|
||||
idle_power_usage = 25 // Previously 100.
|
||||
active_power_usage = 500 // Previously 2000
|
||||
@@ -13,12 +14,17 @@
|
||||
var/alarm = 0
|
||||
var/enabled = 1
|
||||
|
||||
/obj/machinery/shield_diffuser/New()
|
||||
..()
|
||||
/obj/machinery/shield_diffuser/Initialize()
|
||||
. = ..()
|
||||
// TODO - Remove this bit once machines are converted to Initialize
|
||||
if(ispath(circuit))
|
||||
circuit = new circuit(src)
|
||||
default_apply_parts()
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
hide(!T.is_plating())
|
||||
|
||||
//If underfloor, hide the cable
|
||||
//If underfloor, hide the cable^H^H diffuser
|
||||
/obj/machinery/shield_diffuser/hide(var/i)
|
||||
if(istype(loc, /turf))
|
||||
invisibility = i ? 101 : 0
|
||||
@@ -38,6 +44,9 @@
|
||||
return
|
||||
for(var/direction in cardinal)
|
||||
var/turf/simulated/shielded_tile = get_step(get_turf(src), direction)
|
||||
for(var/obj/effect/shield/S in shielded_tile)
|
||||
S.diffuse(5)
|
||||
// Legacy shield support
|
||||
for(var/obj/effect/energy_field/S in shielded_tile)
|
||||
qdel(S)
|
||||
|
||||
@@ -50,16 +59,27 @@
|
||||
else
|
||||
icon_state = "fdiffuser_on"
|
||||
|
||||
/obj/machinery/shield_diffuser/attack_hand()
|
||||
/obj/machinery/shield_diffuser/attack_hand(mob/user as mob)
|
||||
if((. = ..()))
|
||||
return
|
||||
if(alarm)
|
||||
to_chat(usr, "You press an override button on \the [src], re-enabling it.")
|
||||
to_chat(user, "You press an override button on \the [src], re-enabling it.")
|
||||
alarm = 0
|
||||
update_icon()
|
||||
return
|
||||
enabled = !enabled
|
||||
update_use_power(enabled ? USE_POWER_ACTIVE : USE_POWER_IDLE)
|
||||
update_icon()
|
||||
to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].")
|
||||
to_chat(user, "You turn \the [src] [enabled ? "on" : "off"].")
|
||||
|
||||
/obj/machinery/shield_diffuser/attackby(var/obj/item/W, var/mob/user)
|
||||
if(default_deconstruction_screwdriver(user, W))
|
||||
return
|
||||
if(default_deconstruction_crowbar(user, W))
|
||||
return
|
||||
if(default_part_replacement(user, W))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/machinery/shield_diffuser/proc/meteor_alarm(var/duration)
|
||||
if(!duration)
|
||||
@@ -71,4 +91,4 @@
|
||||
. = ..()
|
||||
to_chat(user, "It is [enabled ? "enabled" : "disabled"].")
|
||||
if(alarm)
|
||||
to_chat(user, "A red LED labeled \"Proximity Alarm\" is blinking on the control panel.")
|
||||
to_chat(user, "A red LED labeled \"Proximity Alarm\" is blinking on the control panel.")
|
||||
|
||||
533
code/modules/shieldgen/shield_generator.dm
Normal file
533
code/modules/shieldgen/shield_generator.dm
Normal file
@@ -0,0 +1,533 @@
|
||||
//
|
||||
// This generator is for the supercool big shields intended for ships that do nice stuff with overmaps.
|
||||
//
|
||||
/obj/machinery/power/shield_generator
|
||||
name = "advanced shield generator"
|
||||
desc = "A heavy-duty shield generator and capacitor, capable of generating energy shields at large distances."
|
||||
icon = 'icons/obj/machines/shielding.dmi'
|
||||
icon_state = "generator0"
|
||||
circuit = /obj/item/weapon/circuitboard/shield_generator
|
||||
density = 1
|
||||
var/datum/wires/shield_generator/wires = null
|
||||
var/list/field_segments = list() // List of all shield segments owned by this generator.
|
||||
var/list/damaged_segments = list() // List of shield segments that have failed and are currently regenerating.
|
||||
var/shield_modes = 0 // Enabled shield mode flags
|
||||
var/mitigation_em = 0 // Current EM mitigation
|
||||
var/mitigation_physical = 0 // Current Physical mitigation
|
||||
var/mitigation_heat = 0 // Current Burn mitigation
|
||||
var/mitigation_max = 0 // Maximal mitigation reachable with this generator. Set by RefreshParts()
|
||||
var/max_energy = 0 // Maximal stored energy. In joules. Depends on the type of used SMES coil when constructing this generator.
|
||||
var/current_energy = 0 // Current stored energy.
|
||||
var/field_radius = 1 // Current field radius.
|
||||
var/target_radius = 1 // Desired field radius.
|
||||
var/running = SHIELD_OFF // Whether the generator is enabled or not.
|
||||
var/input_cap = 1 MEGAWATTS // Currently set input limit. Set to 0 to disable limits altogether. The shield will try to input this value per tick at most
|
||||
var/upkeep_power_usage = 0 // Upkeep power usage last tick.
|
||||
var/upkeep_multiplier = 1 // Multiplier of upkeep values.
|
||||
var/power_usage = 0 // Total power usage last tick.
|
||||
var/overloaded = 0 // Whether the field has overloaded and shut down to regenerate.
|
||||
var/hacked = 0 // Whether the generator has been hacked by cutting the safety wire.
|
||||
var/offline_for = 0 // The generator will be inoperable for this duration in ticks.
|
||||
var/input_cut = 0 // Whether the input wire is cut.
|
||||
var/mode_changes_locked = 0 // Whether the control wire is cut, locking out changes.
|
||||
var/ai_control_disabled = 0 // Whether the AI control is disabled.
|
||||
var/list/mode_list = null // A list of shield_mode datums.
|
||||
var/full_shield_strength = 0 // The amount of power shields need to be at full operating strength.
|
||||
var/initial_shield_modes = MODEFLAG_HYPERKINETIC|MODEFLAG_EM|MODEFLAG_ATMOSPHERIC|MODEFLAG_HUMANOIDS
|
||||
|
||||
var/idle_multiplier = 1 // Trades off cost vs. spin-up time from idle to running
|
||||
var/idle_valid_values = list(1, 2, 5, 10)
|
||||
var/spinup_delay = 20
|
||||
var/spinup_counter = 0
|
||||
|
||||
/obj/machinery/power/shield_generator/update_icon()
|
||||
if(running)
|
||||
icon_state = "generator1"
|
||||
set_light(1, 2, "#66FFFF")
|
||||
else
|
||||
icon_state = "generator0"
|
||||
set_light(0)
|
||||
|
||||
|
||||
/obj/machinery/power/shield_generator/Initialize()
|
||||
. = ..()
|
||||
if(!wires)
|
||||
wires = new(src)
|
||||
// TODO - Remove this bit once machines are converted to Initialize
|
||||
if(ispath(circuit))
|
||||
circuit = new circuit(src)
|
||||
default_apply_parts()
|
||||
connect_to_network()
|
||||
|
||||
mode_list = list()
|
||||
for(var/st in subtypesof(/datum/shield_mode))
|
||||
var/datum/shield_mode/SM = new st()
|
||||
mode_list.Add(SM)
|
||||
toggle_flag(initial_shield_modes)
|
||||
|
||||
/obj/machinery/power/shield_generator/Destroy()
|
||||
shutdown_field()
|
||||
field_segments = null
|
||||
damaged_segments = null
|
||||
mode_list = null
|
||||
. = ..()
|
||||
|
||||
|
||||
/obj/machinery/power/shield_generator/RefreshParts()
|
||||
max_energy = 0
|
||||
full_shield_strength = 0
|
||||
for(var/obj/item/weapon/smes_coil/S in component_parts)
|
||||
full_shield_strength += (S.ChargeCapacity * 5)
|
||||
max_energy = full_shield_strength * 20
|
||||
current_energy = between(0, current_energy, max_energy)
|
||||
|
||||
mitigation_max = MAX_MITIGATION_BASE + MAX_MITIGATION_RESEARCH * total_component_rating_of_type(/obj/item/weapon/stock_parts/capacitor)
|
||||
mitigation_em = between(0, mitigation_em, mitigation_max)
|
||||
mitigation_physical = between(0, mitigation_physical, mitigation_max)
|
||||
mitigation_heat = between(0, mitigation_heat, mitigation_max)
|
||||
..()
|
||||
|
||||
|
||||
// Shuts down the shield, removing all shield segments and unlocking generator settings.
|
||||
/obj/machinery/power/shield_generator/proc/shutdown_field()
|
||||
for(var/obj/effect/shield/S in field_segments)
|
||||
qdel(S)
|
||||
|
||||
running = SHIELD_OFF
|
||||
current_energy = 0
|
||||
mitigation_em = 0
|
||||
mitigation_physical = 0
|
||||
mitigation_heat = 0
|
||||
update_icon()
|
||||
|
||||
|
||||
// Generates the field objects. Deletes existing field, if applicable.
|
||||
/obj/machinery/power/shield_generator/proc/regenerate_field()
|
||||
if(field_segments.len)
|
||||
for(var/obj/effect/shield/S in field_segments)
|
||||
qdel(S)
|
||||
|
||||
// The generator is not turned on, so don't generate any new tiles.
|
||||
if(!running)
|
||||
return
|
||||
|
||||
var/list/shielded_turfs
|
||||
|
||||
if(check_flag(MODEFLAG_HULL))
|
||||
shielded_turfs = fieldtype_hull()
|
||||
else
|
||||
shielded_turfs = fieldtype_square()
|
||||
|
||||
for(var/turf/T in shielded_turfs)
|
||||
var/obj/effect/shield/S = new(T)
|
||||
S.gen = src
|
||||
S.flags_updated()
|
||||
field_segments |= S
|
||||
update_icon()
|
||||
|
||||
|
||||
// Recalculates and updates the upkeep multiplier
|
||||
/obj/machinery/power/shield_generator/proc/update_upkeep_multiplier()
|
||||
var/new_upkeep = 1.0
|
||||
for(var/datum/shield_mode/SM in mode_list)
|
||||
if(check_flag(SM.mode_flag))
|
||||
new_upkeep *= SM.multiplier
|
||||
|
||||
upkeep_multiplier = new_upkeep
|
||||
|
||||
|
||||
/obj/machinery/power/shield_generator/process()
|
||||
upkeep_power_usage = 0
|
||||
power_usage = 0
|
||||
|
||||
if(offline_for)
|
||||
offline_for = max(0, offline_for - 1)
|
||||
// We're turned off.
|
||||
if(running == SHIELD_OFF)
|
||||
return
|
||||
|
||||
if(target_radius != field_radius && running != SHIELD_RUNNING) // Do not recalculate the field while it's running; that's extremely laggy.
|
||||
field_radius += (target_radius > field_radius) ? 1 : -1
|
||||
|
||||
// We are shutting down, therefore our stored energy disperses faster than usual.
|
||||
else if(running == SHIELD_DISCHARGING)
|
||||
current_energy -= SHIELD_SHUTDOWN_DISPERSION_RATE
|
||||
else if(running == SHIELD_SPINNING_UP)
|
||||
spinup_counter--
|
||||
if(spinup_counter <= 0)
|
||||
running = SHIELD_RUNNING
|
||||
regenerate_field()
|
||||
|
||||
mitigation_em = between(0, mitigation_em - MITIGATION_LOSS_PASSIVE, mitigation_max)
|
||||
mitigation_heat = between(0, mitigation_heat - MITIGATION_LOSS_PASSIVE, mitigation_max)
|
||||
mitigation_physical = between(0, mitigation_physical - MITIGATION_LOSS_PASSIVE, mitigation_max)
|
||||
|
||||
if(running == SHIELD_RUNNING)
|
||||
upkeep_power_usage = round((field_segments.len - damaged_segments.len) * ENERGY_UPKEEP_PER_TILE * upkeep_multiplier)
|
||||
else if(running > SHIELD_RUNNING)
|
||||
upkeep_power_usage = round(ENERGY_UPKEEP_IDLE * idle_multiplier * (field_radius * 8) * upkeep_multiplier) // Approximates number of turfs.
|
||||
|
||||
if(powernet && (running >= SHIELD_RUNNING) && !input_cut)
|
||||
var/energy_buffer = 0
|
||||
energy_buffer = draw_power(min(upkeep_power_usage, input_cap))
|
||||
power_usage += round(energy_buffer)
|
||||
|
||||
if(energy_buffer < upkeep_power_usage)
|
||||
current_energy -= round(upkeep_power_usage - energy_buffer) // If we don't have enough energy from the grid, take it from the internal battery instead.
|
||||
|
||||
// Now try to recharge our internal energy.
|
||||
var/energy_to_demand
|
||||
if(input_cap)
|
||||
energy_to_demand = between(0, max_energy - current_energy, input_cap - energy_buffer)
|
||||
else
|
||||
energy_to_demand = max(0, max_energy - current_energy)
|
||||
energy_buffer = draw_power(energy_to_demand)
|
||||
power_usage += energy_buffer
|
||||
current_energy += round(energy_buffer)
|
||||
else
|
||||
current_energy -= round(upkeep_power_usage) // We are shutting down, or we lack external power connection. Use energy from internal source instead.
|
||||
|
||||
if(current_energy <= 0)
|
||||
energy_failure()
|
||||
|
||||
if(!overloaded)
|
||||
for(var/obj/effect/shield/S in damaged_segments)
|
||||
S.regenerate()
|
||||
else if (field_integrity() > 25)
|
||||
overloaded = 0
|
||||
|
||||
/obj/machinery/power/shield_generator/attackby(obj/item/O as obj, mob/user as mob)
|
||||
if(panel_open && (O?.is_multitool() || O?.is_wirecutter()))
|
||||
wires.Interact(user)
|
||||
return TRUE
|
||||
if(default_deconstruction_screwdriver(user, O))
|
||||
return
|
||||
if(O?.is_crowbar() || O?.is_wrench() || istype(O, /obj/item/weapon/storage/part_replacer))
|
||||
if(offline_for)
|
||||
to_chat(user, "<span class='warning'>Wait until \the [src] cools down from emergency shutdown first!</span>")
|
||||
return
|
||||
if(running)
|
||||
to_chat(user, "<span class='notice'>Turn off \the [src] first!</span>")
|
||||
return
|
||||
if(default_deconstruction_crowbar(user, O))
|
||||
return
|
||||
if(default_part_replacement(user, O))
|
||||
return
|
||||
if(default_unfasten_wrench(user, O, 40))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/machinery/power/shield_generator/proc/energy_failure()
|
||||
if(running == SHIELD_DISCHARGING)
|
||||
shutdown_field()
|
||||
else
|
||||
current_energy = 0
|
||||
overloaded = 1
|
||||
for(var/obj/effect/shield/S in field_segments)
|
||||
S.fail(1)
|
||||
|
||||
/obj/machinery/power/shield_generator/proc/set_idle(var/new_state)
|
||||
if(new_state)
|
||||
if(running == SHIELD_IDLE)
|
||||
return
|
||||
running = SHIELD_IDLE
|
||||
for(var/obj/effect/shield/S in field_segments)
|
||||
qdel(S)
|
||||
else
|
||||
if(running != SHIELD_IDLE)
|
||||
return
|
||||
running = SHIELD_SPINNING_UP
|
||||
spinup_counter = round(spinup_delay / idle_multiplier)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/power/shield_generator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
var/data[0]
|
||||
|
||||
data["running"] = running
|
||||
data["modes"] = get_flag_descriptions()
|
||||
data["overloaded"] = overloaded
|
||||
data["mitigation_max"] = mitigation_max
|
||||
data["mitigation_physical"] = round(mitigation_physical, 0.1)
|
||||
data["mitigation_em"] = round(mitigation_em, 0.1)
|
||||
data["mitigation_heat"] = round(mitigation_heat, 0.1)
|
||||
data["field_integrity"] = field_integrity()
|
||||
data["max_energy"] = round(max_energy / 1000000, 0.1)
|
||||
data["current_energy"] = round(current_energy / 1000000, 0.1)
|
||||
data["percentage_energy"] = round(data["current_energy"] / data["max_energy"] * 100)
|
||||
data["total_segments"] = field_segments ? field_segments.len : 0
|
||||
data["functional_segments"] = damaged_segments ? data["total_segments"] - damaged_segments.len : data["total_segments"]
|
||||
data["field_radius"] = field_radius
|
||||
data["target_radius"] = target_radius
|
||||
data["input_cap_kw"] = round(input_cap / 1000)
|
||||
data["upkeep_power_usage"] = round(upkeep_power_usage / 1000, 0.1)
|
||||
data["power_usage"] = round(power_usage / 1000)
|
||||
data["hacked"] = hacked
|
||||
data["offline_for"] = offline_for * 2
|
||||
data["idle_multiplier"] = idle_multiplier
|
||||
data["idle_valid_values"] = idle_valid_values
|
||||
data["spinup_counter"] = spinup_counter
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "shield_generator.tmpl", src.name, 500, 800)
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/power/shield_generator/attack_hand(mob/user)
|
||||
if((. = ..()))
|
||||
return
|
||||
if(panel_open && Adjacent(user))
|
||||
wires.Interact(user)
|
||||
return
|
||||
if(CanUseTopic(user, global.default_state) > STATUS_CLOSE)
|
||||
ui_interact(user)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/power/shield_generator/CanUseTopic(var/mob/user)
|
||||
if(issilicon(user) && !Adjacent(user) && ai_control_disabled)
|
||||
return STATUS_UPDATE
|
||||
if(panel_open)
|
||||
return min(..(), STATUS_DISABLED)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/power/shield_generator/Topic(href, href_list, datum/topic_state/state = default_state)
|
||||
if((. = ..()))
|
||||
return
|
||||
|
||||
if(href_list["begin_shutdown"])
|
||||
if(running < SHIELD_RUNNING) // Discharging or off
|
||||
return
|
||||
var/alert = alert(usr, "Are you sure you wish to do this? It will drain the power inside the internal storage rapidly.", "Are you sure?", "Yes", "No")
|
||||
if(!CanInteract(usr, state))
|
||||
return
|
||||
if(running < SHIELD_RUNNING)
|
||||
return
|
||||
if(alert == "Yes")
|
||||
set_idle(TRUE) // do this first to clear the field
|
||||
running = SHIELD_DISCHARGING
|
||||
return TOPIC_REFRESH
|
||||
|
||||
if(href_list["start_generator"])
|
||||
if(offline_for)
|
||||
return
|
||||
set_idle(TRUE)
|
||||
return TOPIC_REFRESH
|
||||
|
||||
if(href_list["toggle_idle"])
|
||||
if(running < SHIELD_RUNNING)
|
||||
return TOPIC_HANDLED
|
||||
set_idle(text2num(href_list["toggle_idle"]))
|
||||
return TOPIC_REFRESH
|
||||
|
||||
// Instantly drops the shield, but causes a cooldown before it may be started again. Also carries a risk of EMP at high charge.
|
||||
if(href_list["emergency_shutdown"])
|
||||
if(!running)
|
||||
return TOPIC_HANDLED
|
||||
|
||||
var/choice = input(usr, "Are you sure that you want to initiate an emergency shield shutdown? This will instantly drop the shield, and may result in unstable release of stored electromagnetic energy. Proceed at your own risk.") in list("Yes", "No")
|
||||
if((choice != "Yes") || !running)
|
||||
return TOPIC_HANDLED
|
||||
|
||||
// If the shield would take 5 minutes to disperse and shut down using regular methods, it will take x1.5 (7 minutes and 30 seconds) of this time to cool down after emergency shutdown
|
||||
offline_for = round(current_energy / (SHIELD_SHUTDOWN_DISPERSION_RATE / 1.5))
|
||||
var/old_energy = current_energy
|
||||
shutdown_field()
|
||||
log_and_message_admins("has triggered \the [src]'s emergency shutdown!", usr)
|
||||
spawn()
|
||||
empulse(src, old_energy / 60000000, old_energy / 32000000, 1) // If shields are charged at 450 MJ, the EMP will be 7.5, 14.0625. 90 MJ, 1.5, 2.8125
|
||||
old_energy = 0
|
||||
|
||||
return TOPIC_REFRESH
|
||||
|
||||
if(mode_changes_locked)
|
||||
return TOPIC_REFRESH
|
||||
|
||||
if(href_list["set_range"])
|
||||
var/new_range = input(usr, "Enter new field range (1-[world.maxx]). Leave blank to cancel.", "Field Radius Control", field_radius) as num
|
||||
if(!new_range)
|
||||
return TOPIC_HANDLED
|
||||
target_radius = between(1, new_range, world.maxx)
|
||||
return TOPIC_REFRESH
|
||||
|
||||
if(href_list["set_input_cap"])
|
||||
var/new_cap = round(input(usr, "Enter new input cap (in kW). Enter 0 or nothing to disable input cap.", "Generator Power Control", round(input_cap / 1000)) as num)
|
||||
if(!new_cap)
|
||||
input_cap = 0
|
||||
return
|
||||
input_cap = max(0, new_cap) * 1000
|
||||
return TOPIC_REFRESH
|
||||
|
||||
if(href_list["toggle_mode"])
|
||||
// Toggling hacked-only modes requires the hacked var to be set to 1
|
||||
if((text2num(href_list["toggle_mode"]) & (MODEFLAG_BYPASS | MODEFLAG_OVERCHARGE)) && !hacked)
|
||||
return TOPIC_HANDLED
|
||||
|
||||
toggle_flag(text2num(href_list["toggle_mode"]))
|
||||
return TOPIC_REFRESH
|
||||
|
||||
if(href_list["switch_idle"])
|
||||
if(running == SHIELD_SPINNING_UP)
|
||||
return TOPIC_REFRESH
|
||||
var/new_idle = text2num(href_list["switch_idle"])
|
||||
if(new_idle in idle_valid_values)
|
||||
idle_multiplier = new_idle
|
||||
return TOPIC_REFRESH
|
||||
|
||||
/obj/machinery/power/shield_generator/proc/field_integrity()
|
||||
if(full_shield_strength)
|
||||
return round(CLAMP01(current_energy / full_shield_strength) * 100)
|
||||
return 0
|
||||
|
||||
|
||||
// Takes specific amount of damage
|
||||
/obj/machinery/power/shield_generator/proc/deal_shield_damage(var/damage, var/shield_damtype)
|
||||
var/energy_to_use = damage * ENERGY_PER_HP
|
||||
if(check_flag(MODEFLAG_MODULATE))
|
||||
mitigation_em -= MITIGATION_HIT_LOSS
|
||||
mitigation_heat -= MITIGATION_HIT_LOSS
|
||||
mitigation_physical -= MITIGATION_HIT_LOSS
|
||||
|
||||
switch(shield_damtype)
|
||||
if(SHIELD_DAMTYPE_PHYSICAL)
|
||||
mitigation_physical += MITIGATION_HIT_LOSS + MITIGATION_HIT_GAIN
|
||||
energy_to_use *= 1 - (mitigation_physical / 100)
|
||||
if(SHIELD_DAMTYPE_EM)
|
||||
mitigation_em += MITIGATION_HIT_LOSS + MITIGATION_HIT_GAIN
|
||||
energy_to_use *= 1 - (mitigation_em / 100)
|
||||
if(SHIELD_DAMTYPE_HEAT)
|
||||
mitigation_heat += MITIGATION_HIT_LOSS + MITIGATION_HIT_GAIN
|
||||
energy_to_use *= 1 - (mitigation_heat / 100)
|
||||
|
||||
mitigation_em = between(0, mitigation_em, mitigation_max)
|
||||
mitigation_heat = between(0, mitigation_heat, mitigation_max)
|
||||
mitigation_physical = between(0, mitigation_physical, mitigation_max)
|
||||
|
||||
current_energy -= energy_to_use
|
||||
|
||||
// Overload the shield, which will shut it down until we recharge above 25% again
|
||||
if(current_energy < 0)
|
||||
energy_failure()
|
||||
return SHIELD_BREACHED_FAILURE
|
||||
|
||||
if(prob(10 - field_integrity()))
|
||||
return SHIELD_BREACHED_CRITICAL
|
||||
if(prob(20 - field_integrity()))
|
||||
return SHIELD_BREACHED_MAJOR
|
||||
if(prob(35 - field_integrity()))
|
||||
return SHIELD_BREACHED_MINOR
|
||||
return SHIELD_ABSORBED
|
||||
|
||||
|
||||
// Checks whether specific flags are enabled
|
||||
/obj/machinery/power/shield_generator/proc/check_flag(var/flag)
|
||||
return (shield_modes & flag)
|
||||
|
||||
|
||||
/obj/machinery/power/shield_generator/proc/toggle_flag(var/flag)
|
||||
shield_modes ^= flag
|
||||
update_upkeep_multiplier()
|
||||
for(var/obj/effect/shield/S in field_segments)
|
||||
S.flags_updated()
|
||||
|
||||
if((flag & (MODEFLAG_HULL|MODEFLAG_MULTIZ)) && (running == SHIELD_RUNNING))
|
||||
regenerate_field()
|
||||
|
||||
if(flag & MODEFLAG_MODULATE)
|
||||
mitigation_em = 0
|
||||
mitigation_physical = 0
|
||||
mitigation_heat = 0
|
||||
|
||||
|
||||
/obj/machinery/power/shield_generator/proc/get_flag_descriptions()
|
||||
var/list/all_flags = list()
|
||||
for(var/datum/shield_mode/SM in mode_list)
|
||||
if(SM.hacked_only && !hacked)
|
||||
continue
|
||||
all_flags.Add(list(list(
|
||||
"name" = SM.mode_name,
|
||||
"desc" = SM.mode_desc,
|
||||
"flag" = SM.mode_flag,
|
||||
"status" = check_flag(SM.mode_flag),
|
||||
"hacked" = SM.hacked_only,
|
||||
"multiplier" = SM.multiplier
|
||||
)))
|
||||
return all_flags
|
||||
|
||||
|
||||
// These two procs determine tiles that should be shielded given the field range. They are quite CPU intensive and may trigger BYOND infinite loop checks, therefore they are set
|
||||
// as background procs to prevent locking up the server. They are only called when the field is generated, or when hull mode is toggled on/off.
|
||||
/obj/machinery/power/shield_generator/proc/fieldtype_square()
|
||||
set background = 1
|
||||
var/list/out = list()
|
||||
var/list/base_turfs = get_base_turfs()
|
||||
|
||||
for(var/turf/gen_turf in base_turfs)
|
||||
var/turf/T
|
||||
for (var/x_offset = -field_radius; x_offset <= field_radius; x_offset++)
|
||||
T = locate(gen_turf.x + x_offset, gen_turf.y - field_radius, gen_turf.z)
|
||||
if(T)
|
||||
out += T
|
||||
T = locate(gen_turf.x + x_offset, gen_turf.y + field_radius, gen_turf.z)
|
||||
if(T)
|
||||
out += T
|
||||
|
||||
for (var/y_offset = -field_radius+1; y_offset < field_radius; y_offset++)
|
||||
T = locate(gen_turf.x - field_radius, gen_turf.y + y_offset, gen_turf.z)
|
||||
if(T)
|
||||
out += T
|
||||
T = locate(gen_turf.x + field_radius, gen_turf.y + y_offset, gen_turf.z)
|
||||
if(T)
|
||||
out += T
|
||||
return out
|
||||
|
||||
|
||||
/obj/machinery/power/shield_generator/proc/fieldtype_hull()
|
||||
set background = 1
|
||||
. = list()
|
||||
var/list/base_turfs = get_base_turfs()
|
||||
|
||||
// Old code found all space turfs and added them if it had a non-space neighbor.
|
||||
// This one finds all non-space turfs and adds all its non-space neighbors.
|
||||
for(var/turf/gen_turf in base_turfs)
|
||||
var/area/TA = null // Variable for area checking. Defining it here so memory does not have to be allocated repeatedly.
|
||||
for(var/turf/T in trange(field_radius, gen_turf))
|
||||
// Don't expand to space or on shuttle areas.
|
||||
if(istype(T, /turf/space) || istype(T, /turf/simulated/open))
|
||||
continue
|
||||
|
||||
// Find adjacent space/shuttle tiles and cover them. Shuttles won't be blocked if shield diffuser is mapped in and turned on.
|
||||
for(var/turf/TN in orange(1, T))
|
||||
TA = get_area(TN)
|
||||
//if ((istype(TN, /turf/space) || (istype(TN, /turf/simulated/open) && (istype(TA, /area/space) || TA.area_flags & AREA_FLAG_EXTERNAL))))
|
||||
if((istype(TN, /turf/space) || (istype(TN, /turf/simulated/open) && (istype(TA, /area/space)))))
|
||||
. |= TN
|
||||
continue
|
||||
|
||||
// Returns a list of turfs from which a field will propagate. If multi-Z mode is enabled, this will return a "column" of turfs above and below the generator.
|
||||
/obj/machinery/power/shield_generator/proc/get_base_turfs()
|
||||
var/list/turfs = list()
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
if(!istype(T))
|
||||
return
|
||||
|
||||
turfs.Add(T)
|
||||
|
||||
// Multi-Z mode is disabled
|
||||
if(!check_flag(MODEFLAG_MULTIZ))
|
||||
return turfs
|
||||
|
||||
while(HasAbove(T.z))
|
||||
T = GetAbove(T)
|
||||
if(istype(T))
|
||||
turfs.Add(T)
|
||||
|
||||
T = get_turf(src)
|
||||
|
||||
while(HasBelow(T.z))
|
||||
T = GetBelow(T)
|
||||
if(istype(T))
|
||||
turfs.Add(T)
|
||||
|
||||
return turfs
|
||||
83
code/modules/shieldgen/shield_modes.dm
Normal file
83
code/modules/shieldgen/shield_modes.dm
Normal file
@@ -0,0 +1,83 @@
|
||||
// Definitions for shield modes. Names, descriptions and power usage multipliers can be changed here.
|
||||
// Do not change the mode_flag variables without a good reason!
|
||||
|
||||
/datum/shield_mode
|
||||
var/mode_name // User-friendly name of this mode.
|
||||
var/mode_desc // A short description of what the mode does.
|
||||
var/mode_flag // Mode bitflag. See defines file.
|
||||
var/multiplier // Energy usage multiplier. Each enabled mode multiplies upkeep power usage by this number. Values between 1-2 are good balance-wise. Hacked modes can go up to 3-4
|
||||
var/hacked_only = 0 // Set to 1 to allow usage of this shield mode only on hacked generators.
|
||||
|
||||
/datum/shield_mode/hyperkinetic
|
||||
mode_name = "Hyperkinetic Projectiles"
|
||||
mode_desc = "This mode blocks various fast moving physical objects, such as bullets, blunt weapons, meteors and other."
|
||||
mode_flag = MODEFLAG_HYPERKINETIC
|
||||
multiplier = 1.2
|
||||
|
||||
/datum/shield_mode/photonic
|
||||
mode_name = "Photonic Dispersion"
|
||||
mode_desc = "This mode blocks majority of light. This includes beam weaponry and most of the visible light spectrum."
|
||||
mode_flag = MODEFLAG_PHOTONIC
|
||||
multiplier = 1.3
|
||||
|
||||
/datum/shield_mode/em
|
||||
mode_name = "Electro-Magnetic Shielding"
|
||||
mode_desc = "This mode blocks various high-power emissions like electrical storms."
|
||||
mode_flag = MODEFLAG_EM
|
||||
multiplier = 1.3
|
||||
|
||||
/datum/shield_mode/humanoids
|
||||
mode_name = "Humanoid Lifeforms"
|
||||
mode_desc = "This mode blocks various humanoid lifeforms. Does not affect fully synthetic humanoids."
|
||||
mode_flag = MODEFLAG_HUMANOIDS
|
||||
multiplier = 1.5
|
||||
|
||||
/datum/shield_mode/silicon
|
||||
mode_name = "Silicon Lifeforms"
|
||||
mode_desc = "This mode blocks various silicon based lifeforms."
|
||||
mode_flag = MODEFLAG_ANORGANIC
|
||||
multiplier = 1.5
|
||||
|
||||
/datum/shield_mode/mobs
|
||||
mode_name = "Unknown Lifeforms"
|
||||
mode_desc = "This mode blocks various other non-humanoid and non-silicon lifeforms. Typical uses include blocking carps."
|
||||
mode_flag = MODEFLAG_NONHUMANS
|
||||
multiplier = 1.5
|
||||
|
||||
/datum/shield_mode/atmosphere
|
||||
mode_name = "Atmospheric Containment"
|
||||
mode_desc = "This mode blocks air flow and acts as atmosphere containment."
|
||||
mode_flag = MODEFLAG_ATMOSPHERIC
|
||||
multiplier = 1.3
|
||||
|
||||
/datum/shield_mode/hull
|
||||
mode_name = "Hull Shielding"
|
||||
mode_desc = "This mode recalibrates the field to cover surface of the installation instead of projecting a bubble shaped field."
|
||||
mode_flag = MODEFLAG_HULL
|
||||
multiplier = 1
|
||||
|
||||
/datum/shield_mode/adaptive
|
||||
mode_name = "Adaptive Field Harmonics"
|
||||
mode_desc = "This mode modulates the shield harmonic frequencies, allowing the field to adapt to various damage types."
|
||||
mode_flag = MODEFLAG_MODULATE
|
||||
multiplier = 2
|
||||
|
||||
/datum/shield_mode/bypass
|
||||
mode_name = "Diffuser Bypass"
|
||||
mode_desc = "This mode disables the built-in safeties which allows the generator to counter effect of various shield diffusers. This tends to create a very large strain on the generator. Does not work with enabled safety protocols."
|
||||
mode_flag = MODEFLAG_BYPASS
|
||||
multiplier = 3
|
||||
hacked_only = 1
|
||||
|
||||
/datum/shield_mode/overcharge
|
||||
mode_name = "Field Overcharge"
|
||||
mode_desc = "This mode polarises the field, causing damage on contact. Does not work with enabled safety protocols."
|
||||
mode_flag = MODEFLAG_OVERCHARGE
|
||||
multiplier = 3
|
||||
hacked_only = 1
|
||||
|
||||
/datum/shield_mode/multiz
|
||||
mode_name = "Multi-Dimensional Field Warp"
|
||||
mode_desc = "Recalibrates the field projection array to increase the vertical height of the field, allowing it's usage on multi-deck stations or ships."
|
||||
mode_flag = MODEFLAG_MULTIZ
|
||||
multiplier = 1
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 18 KiB |
158
nano/templates/shield_generator.tmpl
Normal file
158
nano/templates/shield_generator.tmpl
Normal file
@@ -0,0 +1,158 @@
|
||||
{{if data.offline_for}}
|
||||
<h1>EMERGENCY SHUTDOWN</h1>
|
||||
<b>An emergency shutdown has been initiated - generator cooling down</b><br>
|
||||
<i>Please wait until the generator cools down before resuming operation. Estimated time left: {{:data.offline_for}} seconds.</i>
|
||||
{{else}}
|
||||
<h2>SYSTEM STATUS</h2>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Generator is:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if data.running == 2}}
|
||||
{{if data.overloaded}}
|
||||
Recovering
|
||||
{{else}}
|
||||
Active
|
||||
{{/if}}
|
||||
{{else data.running == 1}}
|
||||
Shutting Down
|
||||
{{else data.running == 3}}
|
||||
Inactive
|
||||
{{else data.running == 4}}
|
||||
Spinning Up
|
||||
{{if data.target_radius != data.field_radius}}
|
||||
(Adjusting Radius)
|
||||
{{else}}
|
||||
({{:data.spinup_counter * 2}}s)
|
||||
{{/if}}
|
||||
{{else}}
|
||||
Offline
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Energy Storage:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:data.current_energy}}/{{:data.max_energy}} MJ ({{:data.percentage_energy}}%)
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Shield Integrity:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:data.field_integrity}}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Mitigation:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:data.mitigation_em}}% EM / {{:data.mitigation_physical}}% PH / {{:data.mitigation_heat}}% HE / {{:data.mitigation_max}}% MAX
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Upkeep Energy Use:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:data.upkeep_power_usage}} kW
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Total Energy Use:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if data.input_cap_kw}}
|
||||
{{:data.power_usage}} / {{:data.input_cap_kw}} kW
|
||||
{{else}}
|
||||
{{:data.power_usage}} kW (No Limit)
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Field Size:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:data.functional_segments}} / {{:data.total_segments}} m2 (radius {{:data.field_radius}}, target {{:data.target_radius}})
|
||||
</div>
|
||||
</div>
|
||||
<h2>CONTROLS</h2>
|
||||
<table>
|
||||
<tr>
|
||||
{{if (data.running >= 2)}}
|
||||
<td>{{:helper.link('Turn off', null, {'begin_shutdown' : '1'})}}
|
||||
{{if (data.running == 3)}}
|
||||
<td>{{:helper.link('Activate', null, {'toggle_idle' : '0'})}} </td>
|
||||
{{else}}
|
||||
<td>{{:helper.link('Deactivate', null, {'toggle_idle' : '1'})}} </td>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<td>{{:helper.link('Turn on', null, {'start_generator' : '1'})}}
|
||||
{{/if}}
|
||||
{{if data.running}}
|
||||
{{if data.hacked}}
|
||||
<td>{{:helper.link('EMERGENCY SHUTDOWN', null, {'emergency_shutdown' : '1'}, null, 'redButton')}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<tr>
|
||||
<td>{{:helper.link('Set Field Range', null, {'set_range' : '1'})}}
|
||||
<td>{{:helper.link('Set Input Cap', null, {'set_input_cap' : '1'})}}
|
||||
<tr>
|
||||
<td>Set inactive power use intensity:</td>
|
||||
<td>
|
||||
{{for data.idle_valid_values}}
|
||||
{{:helper.link(value, null, {'switch_idle' : value}, (value == data.idle_multiplier) ? 'selected' : (data.running == 4 ? 'disabled' : null))}}
|
||||
{{/for}}
|
||||
</td>
|
||||
</table>
|
||||
<h2>FIELD CALIBRATION</h2>
|
||||
<hr>
|
||||
{{for data.modes}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Mode:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:value.name}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Description:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:value.desc}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Status:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if value.status}}
|
||||
Enabled
|
||||
{{else}}
|
||||
Disabled
|
||||
{{/if}}
|
||||
{{:helper.link('Toggle', null, {'toggle_mode' : value.flag})}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Multiplier:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:value.multiplier}}
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
@@ -68,6 +68,7 @@
|
||||
#include "code\__defines\qdel.dm"
|
||||
#include "code\__defines\research.dm"
|
||||
#include "code\__defines\roguemining_vr.dm"
|
||||
#include "code\__defines\shields.dm"
|
||||
#include "code\__defines\shuttle.dm"
|
||||
#include "code\__defines\sound.dm"
|
||||
#include "code\__defines\species_languages.dm"
|
||||
@@ -451,6 +452,7 @@
|
||||
#include "code\datums\wires\radio.dm"
|
||||
#include "code\datums\wires\robot.dm"
|
||||
#include "code\datums\wires\seedstorage.dm"
|
||||
#include "code\datums\wires\shield_generator.dm"
|
||||
#include "code\datums\wires\smartfridge.dm"
|
||||
#include "code\datums\wires\smes.dm"
|
||||
#include "code\datums\wires\suit_storage_unit.dm"
|
||||
@@ -3230,12 +3232,15 @@
|
||||
#include "code\modules\shieldgen\directional_shield.dm"
|
||||
#include "code\modules\shieldgen\emergency_shield.dm"
|
||||
#include "code\modules\shieldgen\energy_field.dm"
|
||||
#include "code\modules\shieldgen\energy_shield.dm"
|
||||
#include "code\modules\shieldgen\handheld_defuser.dm"
|
||||
#include "code\modules\shieldgen\sheldwallgen.dm"
|
||||
#include "code\modules\shieldgen\shield_capacitor.dm"
|
||||
#include "code\modules\shieldgen\shield_diffuser.dm"
|
||||
#include "code\modules\shieldgen\shield_gen.dm"
|
||||
#include "code\modules\shieldgen\shield_gen_external.dm"
|
||||
#include "code\modules\shieldgen\shield_generator.dm"
|
||||
#include "code\modules\shieldgen\shield_modes.dm"
|
||||
#include "code\modules\shuttles\antagonist.dm"
|
||||
#include "code\modules\shuttles\crashes.dm"
|
||||
#include "code\modules\shuttles\departmental.dm"
|
||||
|
||||
Reference in New Issue
Block a user