Pulse Demon Rework (#28731)

* first pass

* almost there

* done!

* tweaks

* de-linting?

* Update code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm

Co-authored-by: 1080pCat <96908085+1080pCat@users.noreply.github.com>
Signed-off-by: Kyani <65205627+EmeraldCandy@users.noreply.github.com>

* tweaks

* tweak

* wizard enhancement

* fix

---------

Signed-off-by: Kyani <65205627+EmeraldCandy@users.noreply.github.com>
Co-authored-by: 1080pCat <96908085+1080pCat@users.noreply.github.com>
This commit is contained in:
Kyani
2025-05-05 16:37:24 +00:00
committed by GitHub
co-authored by 1080pCat
parent 95316573e3
commit a519ea6778
5 changed files with 196 additions and 91 deletions
@@ -1,5 +1,7 @@
// original implementation: https://ss13.moe/wiki/index.php/Pulse_Demon
#define PULSEDEMON_PLATING_SPARK_CHANCE 20
#define PULSEDEMON_APC_CHARGE_MULTIPLIER 2
#define PULSEDEMON_SMES_DRAIN_MULTIPLIER 10
@@ -14,7 +16,7 @@
gender = NEUTER
speak_chance = 20
damage_coeff = list(BRUTE = 0, BURN = 0.5, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) // Pulse demons take damage from nothing except some from lasers
damage_coeff = list(BRUTE = 0.25, BURN = 0.5, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) // Pulse demons take reduced damage from most sources. Use ions
emote_hear = list("vibrates", "sizzles")
speak_emote = list("modulates")
@@ -33,7 +35,7 @@
maxHealth = 50
health = 50
speed = -0.5
speed = -0.3
mob_size = MOB_SIZE_TINY
density = FALSE
del_on_death = TRUE
@@ -58,24 +60,24 @@
var/list/hurt_sounds = list("sound/voice/pdwail1.ogg", "sound/voice/pdwail2.ogg", "sound/voice/pdwail3.ogg")
/// Current quantity of energy the demon currently holds (Joules), spent while purchasing, upgrading or using spells or upgrades. Use adjust_charge to modify this.
var/charge = 1000
var/charge = 100 KJ
/// Maximum quantity of energy the demon can hold at once (Joules).
var/maxcharge = 1000
var/maxcharge = 100 KJ
/// Book keeping for objective win conditions (Joules).
var/charge_drained = 0
/// Controls whether the demon will drain power from sources. Toggled by a spell.
var/do_drain = TRUE
/// Amount of power (Watts) to drain from power sources every Life tick.
var/power_drain_rate = 1000
var/power_drain_rate = 20 KJ
/// Maximum value for power_drain_rate based on upgrades. (Watts)
var/max_drain_rate = 1000
var/max_drain_rate = 100 KJ
/// Amount of power (Watts) required to regenerate health.
var/power_per_regen = 1000
var/power_per_regen = 200 KJ
/// Amount of health lost per Life tick when the power requirement was not met.
var/health_loss_rate = 5
/// Amount of health regenerated per Life tick when the power requirement was met.
var/health_regen_rate = 3
var/health_regen_rate = 2
/// Lock health regeneration while this is not 0, decreases by 1 every Life tick.
var/regen_lock = 0
/// Tracking to prevent multiple EMPs in the same tick from instakilling a demon.
@@ -84,12 +86,12 @@
/// Controls whether the demon can move outside of cables. Toggled by a spell.
var/can_exit_cable = FALSE
/// Speed used while moving inside cables.
var/inside_cable_speed = -0.5
var/inside_cable_speed = -0.3
/// Speed used while moving outside cables. Can be upgraded.
var/outside_cable_speed = 5
/// The time it takes to hijack APCs and cyborgs.
var/hijack_time = 30 SECONDS
var/hijack_time = 20 SECONDS
/// The color of light the demon emits. The range of the light is proportional to energy stored.
var/glow_color = "#bbbb00"
@@ -118,9 +120,24 @@
var/list/apc_images = list()
/// List of all previously hijacked APCs.
var/list/hijacked_apcs = list()
/// Current APC amount for use as upgrade currency
var/list/apcs_remaining = 0
/// Does the pulse demon attempt to use internal power to bypass shock immunity
var/strong_shocks = FALSE
/// Reference to the APC currently being hijacked.
var/obj/machinery/power/apc/apc_being_hijacked
/mob/living/simple_animal/demon/pulse_demon/wizard
name = "Empowered Pulse Demon"
real_name = "Empowered Pulse Demon"
desc = "A strange electrical apparition that lives in wires. This one appears more charged than usual."
/mob/living/simple_animal/demon/pulse_demon/wizard/Initialize(mapload)
. = ..()
src.apcs_remaining = 20
src.maxcharge = 500 KJ
src.charge = 100 KJ
/mob/living/simple_animal/demon/pulse_demon/Initialize(mapload)
. = ..()
if(!mapload)
@@ -249,8 +266,9 @@
var/list/greeting = list()
greeting.Add("<span class='warning'><font size=3><b>You are a pulse demon.</b></font></span>")
greeting.Add("<b>A being made of pure electrical energy, you travel through the station's wires and infest machinery.</b>")
greeting.Add("<b>Navigate the station's power cables to find power sources to steal from, and hijack APCs to interact with their connected machines.</b>")
greeting.Add("<b>If the wire or power source you're connected to runs out of power you'll start losing health and eventually die, but you are otherwise immune to damage.</b>")
greeting.Add("<b>Navigate the station's power cables to find power sources to steal from to power your abilities.</b>")
greeting.Add("<b>Hijack APCs by entering them to unlock more powerful abilities, gain more maximum charge, and gain access to connected machines.</b>")
greeting.Add("<b>If the wire or power source you're connected to runs out of power you'll start losing health and eventually die, but you are otherwise resistant to damage.</b>")
greeting.Add("<span class='motd'>For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/Pulse_Demon)</span>")
for(var/datum/objective/new_obj in list(/datum/objective/pulse_demon/infest, /datum/objective/pulse_demon/drain, /datum/objective/pulse_demon/tamper))
mind.add_mind_objective(new_obj)
@@ -259,7 +277,9 @@
SSticker.mode.traitors |= mind
/mob/living/simple_animal/demon/pulse_demon/proc/give_spells()
AddSpell(new /datum/spell/pulse_demon/open_upgrades)
AddSpell(new /datum/spell/pulse_demon/cycle_camera)
AddSpell(new /datum/spell/pulse_demon/toggle/penetrating_shock(strong_shocks))
AddSpell(new /datum/spell/pulse_demon/toggle/do_drain(do_drain))
AddSpell(new /datum/spell/pulse_demon/toggle/can_exit_cable(can_exit_cable))
AddSpell(new /datum/spell/pulse_demon/cablehop)
@@ -268,7 +288,6 @@
AddSpell(new /datum/spell/pulse_demon/overload)
AddSpell(new /datum/spell/pulse_demon/remotehijack)
AddSpell(new /datum/spell/pulse_demon/remotedrain)
AddSpell(new /datum/spell/pulse_demon/open_upgrades)
/mob/living/simple_animal/demon/pulse_demon/get_status_tab_items()
var/list/status_tab_data = ..()
@@ -277,6 +296,7 @@
status_tab_data[++status_tab_data.len] = list("Maximum Energy:", "[format_si_suffix(maxcharge)]J")
status_tab_data[++status_tab_data.len] = list("Drained Energy:", "[format_si_suffix(charge_drained)]J")
status_tab_data[++status_tab_data.len] = list("Hijacked APCs:", "[length(hijacked_apcs)]")
status_tab_data[++status_tab_data.len] = list("Unused APCs:", "[apcs_remaining]")
status_tab_data[++status_tab_data.len] = list("Drain Rate:", "[format_si_suffix(power_drain_rate)]W")
status_tab_data[++status_tab_data.len] = list("Hijack Time:", "[hijack_time / 10] seconds")
@@ -295,6 +315,7 @@
var/light_radius = min(charge / 25000, 25)
empulse(T, heavy_radius, light_radius)
playsound(T, pick(hurt_sounds), 30, TRUE)
SEND_SIGNAL(src, COMSIG_MOB_DEATH)
/mob/living/simple_animal/demon/pulse_demon/proc/exit_to_turf()
var/turf/T = get_turf(src)
@@ -443,15 +464,15 @@
S.action.UpdateButtons()
return realdelta
// logarithmic scale for glow strength, see table:
// 1.5 <= 25k
// 2 at 50k
// 2.5 at 100k
// 3 at 200k
// 3.5 at 400k, etc
// linear scale for glow strength, see table:
// 1.5 <= 300000 ()
// 2 at 400000
// 2.5 at 50000
// 3 at 600000 etc
/mob/living/simple_animal/demon/pulse_demon/proc/update_glow()
var/range = 2 + (log(2, charge + 1) - log(2, 50000)) / 2
range = max(range, 1.5)
var/range = charge / 200000
range = clamp(range, 1.5, 5)
set_light(range, 2, glow_color)
/mob/living/simple_animal/demon/pulse_demon/proc/drain_APC(obj/machinery/power/apc/A, multiplier = 1)
@@ -627,8 +648,8 @@
// Looks weird but it gets the job done
/mob/living/simple_animal/demon/pulse_demon/proc/calc_maxcharge(hijacked_apcs)
if(!hijacked_apcs) // No APCs hijacked? No extra charge
return 1000
return 20000 * clamp(hijacked_apcs, 0, 20) + 500000 * clamp(hijacked_apcs - 20, 0, 30) + 1000000 * clamp(hijacked_apcs - 50, 0, 50) + 500000000 * max(0, hijacked_apcs - 100)
return 100000
return 100000 + (hijacked_apcs * 25000)
/mob/living/simple_animal/demon/pulse_demon/proc/finish_hijack_apc(obj/machinery/power/apc/A, remote = FALSE)
var/image/apc_image = image('icons/obj/power.dmi', A, "apcemag", ABOVE_LIGHTING_LAYER, A.dir)
@@ -636,12 +657,33 @@
LAZYADD(apc_images[get_turf(A)], apc_image)
client.images += apc_image
apcs_remaining = apcs_remaining + 1
hijacked_apcs += A
RegisterSignal(A, COMSIG_PARENT_QDELETING, PROC_REF(apc_deleted_handler))
if(!remote)
update_controlling_area()
maxcharge = calc_maxcharge(length(hijacked_apcs)) + (maxcharge - calc_maxcharge(length(hijacked_apcs) - 1))
to_chat(src, "<span class='notice'>Hijacking complete! You now control [length(hijacked_apcs)] APCs.</span>")
to_chat(src, "<span class='notice'>Hijacking complete! You now control [length(hijacked_apcs)] APCs and have [apcs_remaining] left to spend.</span>")
var/turf/T = get_turf(A)
var/distance = 0
strengthen_cables(T, distance)
/mob/living/simple_animal/demon/pulse_demon/proc/strengthen_cables(turf/T, distance)
distance += 1
for(var/obj/structure/cable/cable in T.contents)
if(cable.strengthened == TRUE)
continue
cable.strengthened = TRUE
cable.RegisterSignal(src, COMSIG_MOB_DEATH, TYPE_PROC_REF(/obj/structure/cable, unstrengthen_cables))
if(distance >= 15)
return
for(var/obj/structure/cable/cable in range(1, T))
if(get_turf(cable) == T)
continue
if(cable.strengthened == TRUE)
continue
var/turf/next_turf = get_turf(cable)
strengthen_cables(next_turf, distance)
/mob/living/simple_animal/demon/pulse_demon/proc/on_atom_entered(datum/source, atom/movable/entered)
SIGNAL_HANDLER // COMSIG_ATOM_ENTERED
@@ -665,11 +707,18 @@
if(current_cable && current_cable.powernet && current_cable.powernet.available_power)
// returns used energy, not damage dealt, but ez conversion with /20
dealt = electrocute_mob(L, current_cable.powernet, src, siemens_coeff) / 20
else if(charge >= 1000)
else
dealt = L.electrocute_act(30, src, siemens_coeff)
adjust_charge(-1000)
if(dealt > 0)
do_sparks(rand(2, 4), FALSE, src)
if(dealt == 0 && strong_shocks)
if(charge >= 200 KJ)
charge -= 200 KJ
do_sparks(rand(2, 4), FALSE, src)
dealt = L.electrocute_act(30, src, siemens_coeff = 1, flags = SHOCK_NOGLOVES) //bypass that nasty shock resistance
else
to_chat(src, "<span class = 'danger'>You dont have enough charge to bypass their insulation! You need at least 50KJ of energy!")
add_attack_logs(src, L, "shocked ([dealt] damage)")
/mob/living/simple_animal/demon/pulse_demon/proc/is_under_tile()
@@ -723,10 +772,10 @@
switch(severity)
if(EMP_LIGHT)
adjustHealth(round(max(initial(health) / 4, round(maxHealth / 8))))
regen_lock = 3
regen_lock = 5
if(EMP_HEAVY)
adjustHealth(round(max(initial(health) / 3, round(maxHealth / 6))))
regen_lock = 5
regen_lock = 8
emp_debounce = TRUE
addtimer(VARSET_CALLBACK(src, emp_debounce, FALSE), 0.1 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
@@ -13,10 +13,12 @@
antimagic_flags = NONE
action_background_icon_state = "bg_pulsedemon"
var/locked = TRUE
var/unlock_cost = 1 KJ
var/unlock_cost = 1
var/cast_cost = 1 KJ
var/upgrade_cost = 1 KJ
var/upgrade_cost = 1
var/requires_area = FALSE
var/revealing = FALSE
var/reveal_time = 10 SECONDS
base_cooldown = 20 SECONDS
level_max = 4
@@ -26,11 +28,11 @@
/datum/spell/pulse_demon/proc/update_info()
if(locked)
name = "[initial(name)] (Locked) ([format_si_suffix(unlock_cost)]W)"
desc = "[initial(desc)] It costs [format_si_suffix(unlock_cost)]W to unlock. <b>Alt-Click</b> this spell to unlock it."
name = "[initial(name)] (Locked) ([format_si_suffix(unlock_cost)] APC\s)"
desc = "[initial(desc)] It costs [format_si_suffix(unlock_cost)] APC\s to unlock. <b>Alt-Click</b> this spell to unlock it."
else
name = "[initial(name)][cast_cost == 0 ? "" : " ([format_si_suffix(cast_cost)]W)"]"
desc = "[initial(desc)][spell_level == level_max ? "" : " It costs [format_si_suffix(upgrade_cost)]W to upgrade. <b>Alt-Click</b> this spell to upgrade it."]"
desc = "[initial(desc)][spell_level == level_max ? "" : " It costs [format_si_suffix(upgrade_cost)] APC\s to upgrade. <b>Alt-Click</b> this spell to upgrade it."]"
action.name = name
action.desc = desc
action.UpdateButtons()
@@ -43,7 +45,7 @@
if(locked)
if(show_message)
to_chat(user, "<span class='warning'>This ability is locked! Alt-click the button to purchase this ability.</span>")
to_chat(user, "<span class='notice'>It costs [format_si_suffix(unlock_cost)]W to unlock.</span>")
to_chat(user, "<span class='notice'>It costs [format_si_suffix(unlock_cost)] APC\s to unlock.</span>")
return FALSE
if(user.charge < cast_cost)
if(show_message)
@@ -66,6 +68,8 @@
return FALSE
if(try_cast_action(user, targets[1]))
user.adjust_charge(-cast_cost)
if(revealing)
INVOKE_ASYNC(src, PROC_REF(reveal_demon), user, reveal_time)
return TRUE
else
revert_cast(user)
@@ -77,42 +81,47 @@
/datum/spell/pulse_demon/proc/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
return FALSE
/datum/spell/pulse_demon/proc/reveal_demon(mob/user, reveal_time)
user.layer = ABOVE_NORMAL_TURF_LAYER
do_sparks(rand(2, 4), FALSE, user)
sleep(reveal_time)
user.layer = ABOVE_PLATING_LAYER
// handles purchasing and upgrading abilities
/datum/spell/pulse_demon/AltClick(mob/living/simple_animal/demon/pulse_demon/user)
if(!istype(user))
return
if(locked)
if(user.charge >= unlock_cost)
user.adjust_charge(-unlock_cost)
if(user.apcs_remaining >= unlock_cost)
user.apcs_remaining -= unlock_cost
locked = FALSE
to_chat(user, "<span class='notice'>You have unlocked [initial(name)]!</span>")
if(cast_cost > 0)
to_chat(user, "<span class='notice'>It costs [format_si_suffix(cast_cost)]W to use once.</span>")
if(level_max > 0 && spell_level < level_max)
to_chat(user, "<span class='notice'>It will cost [format_si_suffix(upgrade_cost)]W to upgrade.</span>")
to_chat(user, "<span class='notice'>It will cost [format_si_suffix(upgrade_cost)] APC\s to upgrade.</span>")
update_info()
else
to_chat(user, "<span class='warning'>You cannot afford this ability! It costs [format_si_suffix(unlock_cost)]W to unlock.</span>")
to_chat(user, "<span class='warning'>You cannot afford this ability! It costs [format_si_suffix(unlock_cost)] APC\s to unlock.</span>")
else
if(spell_level >= level_max)
to_chat(user, "<span class='warning'>You have already fully upgraded this ability!</span>")
else if(user.charge >= upgrade_cost)
user.adjust_charge(-upgrade_cost)
else if(user.apcs_remaining >= upgrade_cost)
user.apcs_remaining -= upgrade_cost
spell_level = min(spell_level + 1, level_max)
upgrade_cost = round(initial(upgrade_cost) * (1.5 ** spell_level))
do_upgrade(user)
if(spell_level == level_max)
to_chat(user, "<span class='notice'>You have fully upgraded [initial(name)]!</span>")
else
to_chat(user, "<span class='notice'>The next upgrade will cost [format_si_suffix(upgrade_cost)]W to unlock.</span>")
to_chat(user, "<span class='notice'>The next upgrade will cost [format_si_suffix(upgrade_cost)] APC\s to unlock.</span>")
update_info()
else
to_chat(user, "<span class='warning'>You cannot afford to upgrade this ability! It costs [format_si_suffix(upgrade_cost)]W to upgrade.</span>")
to_chat(user, "<span class='warning'>You cannot afford to upgrade this ability! It costs [format_si_suffix(upgrade_cost)] APC\s to upgrade.</span>")
/datum/spell/pulse_demon/proc/do_upgrade(mob/living/simple_animal/demon/pulse_demon/user)
cooldown_handler.recharge_duration = round(base_cooldown / (1.5 ** spell_level))
@@ -122,9 +131,11 @@
name = "Cable Hop"
desc = "Jump to another cable in view."
action_icon_state = "pd_cablehop"
unlock_cost = 15 KJ
cast_cost = 5 KJ
upgrade_cost = 75 KJ
unlock_cost = 4
cast_cost = 200 KJ
upgrade_cost = 2
revealing = TRUE
reveal_time = 5 SECONDS
/datum/spell/pulse_demon/cablehop/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
var/turf/O = get_turf(user)
@@ -151,10 +162,11 @@
name = "Electromagnetic Tamper"
desc = "Unlocks hidden programming in machines. Must be inside a hijacked APC to use."
action_icon_state = "pd_emag"
unlock_cost = 50 KJ
cast_cost = 20 KJ
upgrade_cost = 200 KJ
unlock_cost = 4
cast_cost = 200 KJ
upgrade_cost = 2
requires_area = TRUE
revealing = TRUE
/datum/spell/pulse_demon/emagtamper/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
to_chat(user, "<span class='warning'>You attempt to tamper with [target]!</span>")
@@ -165,10 +177,11 @@
name = "Electromagnetic Pulse"
desc = "Creates an EMP where you click. Be careful not to use it on yourself!"
action_icon_state = "pd_emp"
unlock_cost = 50 KJ
cast_cost = 10 KJ
upgrade_cost = 200 KJ
unlock_cost = 4
cast_cost = 500 KJ
upgrade_cost = 2
requires_area = TRUE
revealing = TRUE
/datum/spell/pulse_demon/emp/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
to_chat(user, "<span class='warning'>You attempt to EMP [target]!</span>")
@@ -177,12 +190,16 @@
/datum/spell/pulse_demon/overload
name = "Overload Machine"
desc = "Overloads a machine, causing it to explode."
desc = "Overloads a machine, causing it to explode. Becomes powerful enough to breach when fully upgraded."
action_icon_state = "pd_overload"
unlock_cost = 300 KJ
cast_cost = 50 KJ
upgrade_cost = 500 KJ
unlock_cost = 8
cast_cost = 1000 KJ
upgrade_cost = 4
level_max = 4
requires_area = TRUE
revealing = TRUE
base_cooldown = 60 SECONDS
reveal_time = 20 SECONDS
/datum/spell/pulse_demon/overload/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
var/obj/machinery/M = target
@@ -193,12 +210,15 @@
to_chat(user, "<span class='warning'>That machine cannot be overloaded.</span>")
return FALSE
target.audible_message("<span class='italics'>You hear a loud electrical buzzing sound coming from [target]!</span>")
addtimer(CALLBACK(src, PROC_REF(detonate), M), 5 SECONDS)
addtimer(CALLBACK(src, PROC_REF(detonate), user, M), 5 SECONDS)
return TRUE
/datum/spell/pulse_demon/overload/proc/detonate(obj/machinery/target)
/datum/spell/pulse_demon/overload/proc/detonate(mob/living/simple_animal/demon/pulse_demon/user, obj/machinery/target)
if(!QDELETED(target))
explosion(get_turf(target), 0, 1, 1, 0, cause = "Pulse Demon: [name]")
if(spell_level == level_max)
explosion(get_turf(target), 0, 1, 2, 2, smoke = TRUE, cause = "Pulse Demon: [name]")
else
explosion(get_turf(target), 0, 0, 2, 2, smoke = TRUE, cause = "Pulse Demon: [name]")
if(!QDELETED(target))
qdel(target)
@@ -206,8 +226,8 @@
name = "Remote Hijack"
desc = "Remotely hijacks an APC."
action_icon_state = "pd_remotehack"
unlock_cost = 15 KJ
cast_cost = 10 KJ
unlock_cost = 2
cast_cost = 200 KJ
level_max = 0
base_cooldown = 3 SECONDS // you have to wait for the regular hijack time anyway
@@ -224,9 +244,9 @@
name = "Remote Drain"
desc = "Remotely drains a power source."
action_icon_state = "pd_remotedrain"
unlock_cost = 5 KJ
cast_cost = 100
upgrade_cost = 100 KJ
unlock_cost = 2
cast_cost = 50 KJ
upgrade_cost = 2
/datum/spell/pulse_demon/remotedrain/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
if(isapc(target))
@@ -295,9 +315,9 @@
desc = "Toggle whether you can move outside of cables or power sources."
base_message = "move outside of cables."
action_icon_state = "pd_toggle_exit"
unlock_cost = 100 KJ
upgrade_cost = 300 KJ
upgrade_cost = 2
level_max = 3
locked = FALSE
/datum/spell/pulse_demon/toggle/can_exit_cable/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
if(user.can_exit_cable && !(user.current_cable || user.current_power))
@@ -354,6 +374,22 @@
user.forceMove(locateUID(user.controlling_area.cameras[current_camera + 1]))
return TRUE
/datum/spell/pulse_demon/toggle/penetrating_shock
name = "Toggle Intense Shocks"
desc = "Toggle whether to use 200 KJ of energy to bypass electric-resistant victims immunity when attacking."
base_message = "use strong shocks when attacking."
action_icon_state = "pd_strong_shocks"
locked = TRUE
unlock_cost = 4
cast_cost = 0
level_max = 0
/datum/spell/pulse_demon/toggle/penetrating_shock/create_new_targeting()
return new /datum/spell_targeting/self
/datum/spell/pulse_demon/toggle/penetrating_shock/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
user.strong_shocks = do_toggle(!user.strong_shocks, user)
/datum/spell/pulse_demon/open_upgrades
name = "Open Upgrade Menu"
desc = "Open the upgrades menu. Alt-click for descriptions and costs."
@@ -389,34 +425,43 @@
var/cost
switch(upgrade)
if(PD_UPGRADE_HIJACK_SPEED)
if(user.hijack_time <= 1 SECONDS)
if(user.hijack_time <= 6 SECONDS)
return -1
cost = (100 / (user.hijack_time / (1 SECONDS))) * 20 KJ
if(user.hijack_time > 12 SECONDS)
cost = 1
else
cost = 2
if(PD_UPGRADE_DRAIN_SPEED)
if(user.max_drain_rate >= 500 KJ)
return -1
cost = user.max_drain_rate * 15
cost = 1
if(PD_UPGRADE_MAX_HEALTH)
if(user.maxHealth >= 200)
return -1
cost = user.maxHealth * 5 KJ
if(user.maxHealth <= 100)
cost = 1
else
cost = 2
if(PD_UPGRADE_HEALTH_REGEN)
if(user.health_regen_rate >= 100)
if(user.health_regen_rate >= 10)
return -1
cost = user.health_regen_rate * 50 KJ
cost = 1
if(PD_UPGRADE_HEALTH_LOSS)
if(user.health_loss_rate <= 1)
if(user.health_loss_rate <= 0)
return -1
cost = (100 / user.health_loss_rate) * 20 KJ
if(user.health_loss_rate >= 3)
cost = 1
else
cost = 2
if(PD_UPGRADE_HEALTH_COST)
if(user.power_per_regen <= 1)
if(user.power_per_regen <= 1 KJ)
return -1
cost = (100 / user.power_per_regen) * 50 KJ
cost = 1
if(PD_UPGRADE_MAX_CHARGE)
cost = user.maxcharge
cost = 1
else
return -1
return round(cost)
return cost
/datum/spell/pulse_demon/open_upgrades/proc/get_upgrades(mob/living/simple_animal/demon/pulse_demon/user)
var/upgrades = list()
@@ -424,7 +469,7 @@
var/cost = calc_cost(user, upgrade)
if(cost == -1)
continue
upgrades["[upgrade] ([format_si_suffix(cost)]W)"] = upgrade_icons[upgrade]
upgrades["[upgrade] ([format_si_suffix(cost)] APC\s)"] = upgrade_icons[upgrade]
return upgrades
/datum/spell/pulse_demon/open_upgrades/AltClick(mob/living/simple_animal/demon/pulse_demon/user)
@@ -450,18 +495,18 @@
var/cost = calc_cost(user, choice)
if(cost == -1)
return FALSE
if(user.charge < cost)
to_chat(user, "<span class='warning'>You do not have enough charge to purchase this upgrade!</span>")
if(user.apcs_remaining < cost)
to_chat(user, "<span class='warning'>You do not have enough unused APCs to purchase this upgrade!</span>")
return FALSE
user.adjust_charge(-cost)
user.apcs_remaining -= cost
switch(choice)
if(PD_UPGRADE_HIJACK_SPEED)
user.hijack_time = max(round(user.hijack_time / 1.5), 1 SECONDS)
user.hijack_time = max(round(user.hijack_time - 3 SECONDS), 6 SECONDS)
to_chat(user, "<span class='notice'>You have upgraded your [choice], it now takes [user.hijack_time / (1 SECONDS)] second\s to hijack APCs.</span>")
if(PD_UPGRADE_DRAIN_SPEED)
var/old = user.max_drain_rate
user.max_drain_rate = min(round(user.max_drain_rate * 1.5), 500 KJ)
user.max_drain_rate = user.max_drain_rate + 20 KJ
if(user.power_drain_rate == old)
user.power_drain_rate = user.max_drain_rate
to_chat(user, "<span class='notice'>You have upgraded your [choice], you can now drain [format_si_suffix(user.max_drain_rate)]W.</span>")
@@ -469,23 +514,25 @@
user.maxHealth = min(round(user.maxHealth * 1.5), 200)
to_chat(user, "<span class='notice'>You have upgraded your [choice], your max health is now [user.maxHealth].</span>")
if(PD_UPGRADE_HEALTH_REGEN)
user.health_regen_rate = min(round(user.health_regen_rate * 1.5), 100)
user.health_regen_rate = min(round(user.health_regen_rate + 2), 10)
to_chat(user, "<span class='notice'>You have upgraded your [choice], you will now regenerate [user.health_regen_rate] health per cycle when powered.</span>")
if(PD_UPGRADE_HEALTH_LOSS)
user.health_loss_rate = max(round(user.health_loss_rate / 1.5), 1)
to_chat(user, "<span class='notice'>You have upgraded your [choice], you will now lose [user.health_loss_rate] health per cycle when unpowered.</span>")
user.health_loss_rate = max(round(user.health_loss_rate - 1), 0)
if(user.health_loss_rate == 0)
to_chat(user, "<span class='notice'>You have upgraded your [choice], You will no longer lose health when on an unpowered cable.</span>")
else
to_chat(user, "<span class='notice'>You have upgraded your [choice], you will now lose [user.health_loss_rate] health per cycle when unpowered.</span>")
if(PD_UPGRADE_HEALTH_COST)
user.power_per_regen = max(round(user.power_per_regen / 1.5), 1)
to_chat(user, "<span class='notice'>You have upgraded your [choice], it now takes [format_si_suffix(user.power_per_regen)]W of power to regenerate health.</span>")
to_chat(user, "<span class='notice'>Additionally, if you enable draining while on a cable, any excess power that would've been used regenerating will be added to your charge.</span>")
if(PD_UPGRADE_MAX_CHARGE)
user.maxcharge = round(user.maxcharge * 2)
user.maxcharge = user.maxcharge + 50 KJ
to_chat(user, "<span class='notice'>You have upgraded your [choice], you can now store [format_si_suffix(user.maxcharge)]J of energy.</span>")
else
return FALSE
return TRUE
#undef PULSEDEMON_REMOTE_DRAIN_MULTIPLIER
#undef PD_UPGRADE_HIJACK_SPEED
#undef PD_UPGRADE_DRAIN_SPEED
@@ -401,7 +401,7 @@
var/shatter_msg = "<span class='notice'>You shatter the bulb, no turning back now!</span>"
var/veil_msg = "<span class='warning'>The creature sparks energetically and zips away...</span>"
var/objective_verb = "Electrocute"
var/mob/living/demon_type = /mob/living/simple_animal/demon/pulse_demon
var/mob/living/demon_type = /mob/living/simple_animal/demon/pulse_demon/wizard
/obj/item/antag_spawner/pulse_demon/attack_self__legacy__attackchain(mob/user)
if(level_blocks_magic(user.z))
@@ -440,7 +440,7 @@
var/datum/mind/player_mind = new /datum/mind(C.key)
player_mind.active = TRUE
var/mob/living/simple_animal/demon/pulse_demon/demon = new(T)
var/mob/living/simple_animal/demon/pulse_demon/wizard/demon = new(T)
player_mind.transfer_to(demon)
player_mind.assigned_role = SPECIAL_ROLE_DEMON
player_mind.special_role = SPECIAL_ROLE_DEMON
+9
View File
@@ -40,6 +40,7 @@ By design, d1 is the smallest direction and d2 is the highest
var/tmp/d2 = 1
/// The regional powernet this cable is registered to
var/datum/regional_powernet/powernet
var/strengthened = FALSE
/obj/structure/cable/Initialize(mapload)
. = ..()
@@ -143,6 +144,9 @@ By design, d1 is the smallest direction and d2 is the highest
return
if(shock(user, 50))
return
if(strengthened)
to_chat(user, "<span class = 'danger'>The cable resists your attempts to cut it!")
return
user.visible_message("[user] cuts the cable.", "<span class='notice'>You cut the cable.</span>")
investigate_log("was cut by [key_name(usr, 1)] in [get_area(user)]([T.x], [T.y], [T.z] - [ADMIN_JMP(T)])",INVESTIGATE_WIRES)
deconstruct()
@@ -430,6 +434,11 @@ By design, d1 is the smallest direction and d2 is the highest
/obj/structure/cable/white
color = COLOR_WHITE
/obj/structure/cable/proc/unstrengthen_cables(mob/demon)
SIGNAL_HANDLER // COMSIG_MOB_DEATH
src.strengthened = FALSE
UnregisterSignal(demon, COMSIG_MOB_DEATH)
//
// This ASCII art represents my brain after looking at cable
// code for too long, half of this was written before I was even