/datum/technomancer/spell/energy_siphon name = "Energy Siphon" desc = "This creates a link to a target that drains electricity, converts it to energy that the Core can use, then absorbs it. \ Every second, electricity is stolen until the link is broken by the target moving too far away, or having no more energy left. \ Can drain from powercells, microbatteries, and other Cores. The beam created by the siphoning is harmful to touch." enhancement_desc = "Rate of siphoning is doubled." spell_power_desc = "Rate of siphoning is scaled up based on spell power." cost = 100 obj_path = /obj/item/spell/energy_siphon ability_icon_state = "tech_energysiphon" category = UTILITY_SPELLS /obj/item/spell/energy_siphon name = "energy siphon" desc = "Now you are an energy vampire." icon_state = "energy_siphon" cast_methods = CAST_RANGED aspect = ASPECT_SHOCK var/atom/movable/siphoning = null // What the spell is currently draining. Does nothing if null. var/list/things_to_siphon = list() //Things which are actually drained as a result of the above not being null. var/flow_rate = 1000 // Limits how much electricity can be drained per second. Measured by default in god knows what. /obj/item/spell/energy_siphon/New() ..() START_PROCESSING(SSobj, src) /obj/item/spell/energy_siphon/Destroy() stop_siphoning() STOP_PROCESSING(SSobj, src) return ..() /obj/item/spell/energy_siphon/process() if(!siphoning) return if(!pay_energy(100)) to_chat(owner, "You can't afford to maintain the siphon link!") stop_siphoning() return if(get_dist(siphoning, get_turf(src)) > 4) to_chat(owner, "\The [siphoning] is too far to drain from!") stop_siphoning() return if(!(siphoning in view(owner))) to_chat(owner, "\The [siphoning] cannot be seen!") stop_siphoning() return siphon(siphoning, owner) /obj/item/spell/energy_siphon/on_ranged_cast(atom/hit_atom, mob/user) if(istype(hit_atom, /atom/movable) && within_range(hit_atom, 4)) var/atom/movable/AM = hit_atom populate_siphon_list(AM) if(!things_to_siphon.len) to_chat(user, "You cannot steal energy from \a [AM].") return 0 siphoning = AM update_icon() add_attack_logs(user,AM,"Siphoned energy from [src]") else stop_siphoning() /obj/item/spell/energy_siphon/proc/populate_siphon_list(atom/movable/target) things_to_siphon.Cut() things_to_siphon |= target // The recursive check below does not add the object being checked to its list. things_to_siphon |= recursive_content_check(target, things_to_siphon, recursion_limit = 3, client_check = 0, sight_check = 0, include_mobs = 1, include_objects = 1, ignore_show_messages = 1) for(var/atom/movable/AM in things_to_siphon) if(ishuman(AM)) // We can drain FBPs, so we can skip the test below. var/mob/living/carbon/human/H = AM if(H.isSynthetic()) continue if(AM.drain_power(1) <= 0) // This checks if whatever's in the list can be drained from. things_to_siphon.Remove(AM) /obj/item/spell/energy_siphon/proc/stop_siphoning() siphoning = null things_to_siphon.Cut() update_icon() #define SIPHON_CELL_TO_ENERGY 0.5 #define SIPHON_FBP_TO_ENERGY 5.0 #define SIPHON_CORE_TO_ENERGY 0.5 // This is called every tick, so long as a link exists between the target and the Technomancer. /obj/item/spell/energy_siphon/proc/siphon(atom/movable/siphoning, mob/living/user) var/list/things_to_drain = things_to_siphon // Temporary list copy of what we're gonna steal from. var/charge_to_give = 0 // How much energy to give to the Technomancer at the end. var/flow_remaining = calculate_spell_power(flow_rate) if(!siphoning) return 0 update_icon() //playsound(source = src, soundin = 'TODO', vol = 30, vary = 0, extrarange = 0, falloff = 0, is_global = 0) if(check_for_scepter()) flow_remaining = flow_remaining * 2 // First, we drain normal batteries. if(things_to_drain.len) // Don't bother with empty stuff. for(var/atom/movable/AM in things_to_drain) if(AM.drain_power(1) <= 0) things_to_drain.Remove(AM) if(!things_to_drain.len) return var/charge_to_steal = round(flow_remaining / things_to_drain.len) // This is to drain all the cells evenly. for(var/atom/movable/AM in things_to_drain) var/big_number = AM.drain_power(0,0,charge_to_steal / CELLRATE) // This drains the cell, and leaves us with a big number. flow_remaining = flow_remaining - (big_number * CELLRATE) // Which we reduce to our needed number by multiplying. AM.update_icon() // So guns and batteries will display correctly. charge_to_give = charge_to_give + (flow_rate - flow_remaining) * SIPHON_CELL_TO_ENERGY // If we have 'leftover' flow, let's try to do more. if(round(flow_remaining)) if(ishuman(siphoning)) var/mob/living/carbon/human/H = siphoning // Let's drain from FBPs. Note that it is possible for the caster to drain themselves if they are an FBP and desperate. if(H.isSynthetic()) var/nutrition_to_steal = flow_remaining * 0.025 // Should steal about 25 nutrition per second by default. var/old_nutrition = H.nutrition H.nutrition = max(H.nutrition - nutrition_to_steal, 0) var/nutrition_delta = old_nutrition - H.nutrition charge_to_give += nutrition_delta * SIPHON_FBP_TO_ENERGY flow_remaining = flow_remaining - nutrition_to_steal / 0.025 // Let's steal some energy from another Technomancer. if(istype(H.back, /obj/item/technomancer_core) && H != user) var/obj/item/technomancer_core/their_core = H.back if(their_core.pay_energy(flow_remaining / 2)) // Don't give energy from nothing. charge_to_give += flow_remaining * SIPHON_CORE_TO_ENERGY flow_remaining = 0 if(charge_to_give) // Shock anyone standing in the beam. create_lightning(user, siphoning) // Now we can actually fill up the core. if(core.energy < core.max_energy) give_energy(charge_to_give) to_chat(user, "Stolen [charge_to_give * CELLRATE] kJ and converted to [charge_to_give] Core energy.") if( (core.max_energy - core.energy) < charge_to_give ) // We have some overflow, if this is true. if(user.isSynthetic()) // Let's do something with it, if we're a robot. charge_to_give = charge_to_give - (core.max_energy - core.energy) user.adjust_nutrition(charge_to_give / SIPHON_FBP_TO_ENERGY) to_chat(user, "Redirected energy to internal microcell.") else to_chat(user, "Stolen [charge_to_give * CELLRATE] kJ.") adjust_instability(2) if(flow_remaining == flow_rate) // We didn't drain anything. to_chat(user, "\The [siphoning] cannot be drained any further.") stop_siphoning() /obj/item/spell/energy_siphon/update_icon() ..() if(siphoning) icon_state = "energy_siphon_drain" else icon_state = "energy_siphon" /obj/item/spell/energy_siphon/proc/create_lightning(mob/user, atom/source) if(user && source && user != source) spawn(0) var/i = 7 // process() takes two seconds to tick, this ensures the appearance of a ongoing beam. while(i) var/obj/item/projectile/beam/lightning/energy_siphon/lightning = new(get_turf(source)) lightning.firer = user lightning.old_style_target(user) lightning.fire() i-- sleep(3) /obj/item/projectile/beam/lightning/energy_siphon name = "energy stream" icon_state = "lightning" range = 6 // Backup plan in-case the effect somehow misses the Technomancer. power = 5 // This fires really fast, so this may add up if someone keeps standing in the beam. penetrating = 5 /obj/item/projectile/beam/lightning/energy_siphon/Bump(atom/A as mob|obj|turf|area, forced=0) if(A == firer) // For this, you CAN shoot yourself. on_impact(A) density = FALSE invisibility = 101 qdel(src) return 1 ..() /obj/item/projectile/beam/lightning/energy_siphon/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0) if(target_mob == firer) // This shouldn't actually occur due to Bump(), but just in-case. return 1 if(ishuman(target_mob)) // Otherwise someone else stood in the beam and is going to pay for it. var/mob/living/carbon/human/H = target_mob var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO)) H.electrocute_act(power, src, H.get_siemens_coefficient_organ(affected), affected, 0) else target_mob.electrocute_act(power, src, 0.75, BP_TORSO) return 0 // Since this is a continous beam, it needs to keep flying until it hits the Technomancer. #undef SIPHON_CELL_TO_ENERGY #undef SIPHON_FBP_TO_ENERGY #undef SIPHON_CORE_TO_ENERGY