mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-22 20:58:14 +01:00
353 lines
9.9 KiB
Plaintext
353 lines
9.9 KiB
Plaintext
/**
|
|
* Just a supertype for power cell designs
|
|
*/
|
|
/datum/prototype/design/generated/power_cell
|
|
abstract_type = /datum/prototype/design/generated/power_cell
|
|
category = DESIGN_CATEGORY_POWER
|
|
|
|
/**
|
|
* Power storage cells
|
|
*/
|
|
/obj/item/cell
|
|
name = "power cell"
|
|
desc = "A rechargable electrochemical power cell."
|
|
icon = 'icons/obj/power.dmi'
|
|
icon_state = "cell"
|
|
item_state = "cell"
|
|
origin_tech = list(TECH_POWER = 1)
|
|
damage_force = 5.0
|
|
throw_force = 5.0
|
|
throw_speed = 3
|
|
throw_range = 5
|
|
w_class = WEIGHT_CLASS_NORMAL
|
|
suit_storage_class = SUIT_STORAGE_CLASS_SOFTWEAR | SUIT_STORAGE_CLASS_HARDWEAR
|
|
|
|
armor_type = /datum/armor/object/light
|
|
integrity = 200
|
|
integrity_max = 200
|
|
integrity_failure = 100
|
|
|
|
/**
|
|
* Materials base is intentionally nulled by default on /cell.
|
|
*
|
|
* This will invoke type generation. If this is set by a mapper or varedit, it will ignore
|
|
* typegen.
|
|
*/
|
|
materials_base = null
|
|
|
|
//* -- Set by type generation -- *//
|
|
|
|
/// should we run typegen from the power cell datum we're provided?
|
|
/// * This is statically set by the power cell type generation defines.
|
|
/// * This should never be modified after Initialize() runs.
|
|
var/tmp/typegen_active = FALSE
|
|
var/tmp/typegen_material_multiplier = 1
|
|
|
|
//* Behavior *//
|
|
/// Power cell prototype. Fetched at init.
|
|
/// * This is nullable! If this is null, we don't perform typegen.
|
|
var/datum/prototype/power_cell/cell_datum
|
|
/// what cell types we are
|
|
var/cell_type = NONE
|
|
|
|
//* Charge *//
|
|
/// current charge
|
|
var/charge
|
|
/// maximum charge
|
|
/// * also used as a base for type generation
|
|
var/max_charge = POWER_CELL_CAPACITY_BASE
|
|
/// last time power was drawn from us
|
|
var/last_use
|
|
|
|
//* Rendering *//
|
|
/// perform default rendering
|
|
var/rendering_system = FALSE
|
|
/// total states; 0 to disable auto render
|
|
var/indicator_count
|
|
/// our indicator color
|
|
var/indicator_color = "#00aa00"
|
|
/// our stripe color; null for no stripe
|
|
var/stripe_color
|
|
|
|
//* Self Recharge *//
|
|
/// do we self recharge?
|
|
var/self_recharge = FALSE
|
|
/// cell units to give, per second, if self-recharging
|
|
var/self_recharge_amount = 50
|
|
/// how long to wait until after the last use to start recharging
|
|
var/self_recharge_delay = 0 SECONDS
|
|
|
|
//* legacy below *//
|
|
/// Are we EMP immune?
|
|
var/emp_proof = FALSE
|
|
var/rigged = 0 // true if rigged to explode
|
|
var/rating = 1
|
|
|
|
/obj/item/cell/Initialize(mapload)
|
|
cell_datum = RSpower_cells.fetch_local_or_throw(cell_datum)
|
|
if(typegen_active)
|
|
// Typegen is active, run type generation stuff.
|
|
// This will trample all map edits. If you're editing a map, you shouldn't be editing power cells.
|
|
// Or if you are, you should null out their cell datum.
|
|
if(isnull(materials_base) && cell_datum.typegen_materials_base)
|
|
if(has_typelist(NAMEOF(src, materials_base)))
|
|
materials_base = get_typelist(NAMEOF(src, materials_base))
|
|
else
|
|
// This will **trample** the materials_base check in /obj initialization, so we have to the
|
|
// entire cycle.
|
|
var/list/generating_materials = cell_datum.typegen_materials_base.Copy()
|
|
if(typegen_material_multiplier != 1)
|
|
for(var/key in generating_materials)
|
|
generating_materials[key] *= typegen_material_multiplier
|
|
for(var/key in cell_datum.typegen_materials_base_adjust)
|
|
generating_materials[key] += cell_datum.typegen_materials_base_adjust[key]
|
|
generating_materials = SSmaterials.preprocess_kv_keys_to_ids(generating_materials)
|
|
materials_base = typelist(NAMEOF(src, materials_base), generating_materials)
|
|
. = ..()
|
|
if(isnull(charge))
|
|
charge = max_charge
|
|
update_icon()
|
|
if(self_recharge)
|
|
START_PROCESSING(SSobj, src)
|
|
|
|
/obj/item/cell/Destroy()
|
|
if(datum_flags & DF_ISPROCESSING)
|
|
STOP_PROCESSING(SSobj, src)
|
|
return ..()
|
|
|
|
/obj/item/cell/get_worth(flags)
|
|
. = ..()
|
|
if(typegen_active)
|
|
. *= cell_datum?.typegen_worth_multiplier
|
|
|
|
/obj/item/cell/get_rating()
|
|
return rating
|
|
|
|
/obj/item/cell/get_cell(inducer)
|
|
return src
|
|
|
|
/obj/item/cell/drain_energy(datum/actor, amount, flags)
|
|
if(charge <= 0)
|
|
return 0
|
|
return use(DYNAMIC_KJ_TO_CELL_UNITS(amount)) * GLOB.cellrate
|
|
|
|
/obj/item/cell/can_drain_energy(datum/actor, flags)
|
|
return TRUE
|
|
|
|
/obj/item/cell/examine(mob/user, dist)
|
|
. = ..()
|
|
if(get_dist(src, user) <= 1)
|
|
. += "It has a power rating of [max_charge].\nThe charge meter reads [round(src.percent() )]%."
|
|
|
|
/obj/item/cell/attackby(obj/item/W, mob/user)
|
|
..()
|
|
if(istype(W, /obj/item/reagent_containers/syringe))
|
|
var/obj/item/reagent_containers/syringe/S = W
|
|
to_chat(user, "You inject the solution into the power cell.")
|
|
if(S.reagents.has_reagent("phoron", 5))
|
|
rigged = 1
|
|
log_admin("LOG: [user.name] ([user.ckey]) injected a power cell with phoron, rigging it to explode.")
|
|
message_admins("LOG: [user.name] ([user.ckey]) injected a power cell with phoron, rigging it to explode.")
|
|
S.reagents.clear_reagents()
|
|
|
|
/obj/item/cell/proc/explode()
|
|
var/turf/T = get_turf(src.loc)
|
|
/*
|
|
* 1000-cell explosion(T, -1, 0, 1, 1)
|
|
* 2500-cell explosion(T, -1, 0, 1, 1)
|
|
* 10000-cell explosion(T, -1, 1, 3, 3)
|
|
* 15000-cell explosion(T, -1, 2, 4, 4)
|
|
* */
|
|
if (charge==0)
|
|
return
|
|
var/devastation_range = 0
|
|
var/heavy_impact_range = round(sqrt(charge) / 60)
|
|
var/light_impact_range = round(sqrt(charge) / 30)
|
|
var/flash_range = light_impact_range
|
|
if (light_impact_range==0)
|
|
rigged = 0
|
|
corrupt()
|
|
return
|
|
log_admin("LOG: Rigged power cell explosion, last touched by [fingerprintslast]")
|
|
message_admins("LOG: Rigged power cell explosion, last touched by [fingerprintslast]")
|
|
explosion(T, devastation_range, heavy_impact_range, light_impact_range, flash_range)
|
|
qdel(src)
|
|
|
|
/obj/item/cell/proc/corrupt()
|
|
charge /= 2
|
|
max_charge /= 2
|
|
if (prob(10))
|
|
rigged = 1 //broken batteries are dangerous
|
|
|
|
/obj/item/cell/emp_act(severity)
|
|
. = ..()
|
|
if(emp_proof)
|
|
return
|
|
//remove this once emp changes on dev are merged in
|
|
if(isrobot(loc))
|
|
var/mob/living/silicon/robot/R = loc
|
|
severity *= R.cell_emp_mult
|
|
use(charge / (severity + 1))
|
|
update_icon()
|
|
|
|
/obj/item/cell/legacy_ex_act(severity)
|
|
switch(severity)
|
|
if(1.0)
|
|
qdel(src)
|
|
return
|
|
if(2.0)
|
|
if (prob(50))
|
|
qdel(src)
|
|
return
|
|
if (prob(50))
|
|
corrupt()
|
|
if(3.0)
|
|
if (prob(25))
|
|
qdel(src)
|
|
return
|
|
if (prob(25))
|
|
corrupt()
|
|
|
|
/obj/item/cell/proc/get_electrocute_damage()
|
|
//1kW = 5
|
|
//10kW = 24
|
|
//100kW = 45
|
|
//250kW = 53
|
|
//1MW = 66
|
|
//10MW = 88
|
|
//100MW = 110
|
|
//1GW = 132
|
|
if(charge >= 1000)
|
|
var/damage = log(1.1,charge)
|
|
damage = damage - (log(1.1,damage)*1.5)
|
|
return round(damage)
|
|
else
|
|
return 0
|
|
|
|
/obj/item/cell/using_as_item(atom/target, datum/event_args/actor/clickchain/clickchain, clickchain_flags)
|
|
. = ..()
|
|
if(. & CLICKCHAIN_FLAGS_INTERACT_ABORT)
|
|
return
|
|
if(ismob(target))
|
|
var/mob/casted_mob = target
|
|
// synth recharging...
|
|
if(casted_mob.synth_cell_charging_supported() && (casted_mob == clickchain.performer) && (clickchain.using_intent == INTENT_HELP))
|
|
if(INTERACTING_WITH_FOR(clickchain.performer, src, "synth-cell-self-charging"))
|
|
return CLICKCHAIN_DO_NOT_PROPAGATE
|
|
|
|
clickchain.visible_feedback(
|
|
target = casted_mob,
|
|
range = MESSAGE_RANGE_INVENTORY_SOFT,
|
|
visible = SPAN_NOTICE("[clickchain.performer] touches their fingers to [src] and starts to drain power from it."),
|
|
otherwise_self = SPAN_NOTICE("You touch your fingers to [src] and start pulling power from it."),
|
|
)
|
|
var/flow_per_second = casted_mob.synth_cell_charging_max_power_flow()
|
|
var/transferred_energy = 0
|
|
START_INTERACTING_WITH(casted_mob, src, "synth-cell-self-charging")
|
|
if(flow_per_second > 0)
|
|
var/last_flow
|
|
var/last_tick = world.time
|
|
do
|
|
if(!do_after(clickchain.performer, 0.5 SECONDS, clickchain.performer, DO_AFTER_IGNORE_USER_MOVEMENT, MOBILITY_CAN_UI, 0))
|
|
break
|
|
var/elapsed = world.time - last_tick
|
|
last_flow = casted_mob.synth_cell_charging_give_power(min(elapsed * flow_per_second, DYNAMIC_CELL_UNITS_TO_J(charge)))
|
|
use(DYNAMIC_J_TO_CELL_UNITS(last_flow))
|
|
transferred_energy += last_flow
|
|
while(last_flow > 0)
|
|
STOP_INTERACTING_WITH(casted_mob, src, "synth-cell-self-charging")
|
|
if(transferred_energy > 0)
|
|
clickchain.chat_feedback(
|
|
SPAN_NOTICE("Siphoned [round(transferred_energy * 0.001, 0.1)]kJ from [src]."),
|
|
target = src,
|
|
)
|
|
return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING
|
|
|
|
//* Main *//
|
|
|
|
/obj/item/cell/proc/give(amount, force)
|
|
// LEGACY - explosion?
|
|
if(rigged && amount > 0)
|
|
explode()
|
|
return FALSE
|
|
// END
|
|
if(force)
|
|
. = amount
|
|
else
|
|
. = clamp(amount, 0, max_charge - charge)
|
|
charge += .
|
|
if(.)
|
|
update_icon()
|
|
// :3 for guns to update, someone find a better solution later
|
|
loc?.update_icon()
|
|
|
|
/obj/item/cell/proc/amount_missing()
|
|
. = max(0, max_charge - charge)
|
|
|
|
/obj/item/cell/proc/fully_charged()
|
|
return check_charge(max_charge)
|
|
|
|
/obj/item/cell/proc/check_charge(amount)
|
|
return charge >= amount
|
|
|
|
/obj/item/cell/proc/checked_use(amount, reserve)
|
|
if(!check_charge(amount + reserve))
|
|
return 0
|
|
return use(amount)
|
|
|
|
/obj/item/cell/proc/use(amount)
|
|
// LEGACY - explosion?
|
|
if(rigged && amount > 0)
|
|
explode()
|
|
return 0
|
|
// END
|
|
. = min(charge, amount)
|
|
charge -= .
|
|
last_use = world.time
|
|
update_icon()
|
|
|
|
//* Calculations *//
|
|
|
|
/**
|
|
* Returns percent remaining, [0, 100]
|
|
*/
|
|
/obj/item/cell/proc/percent()
|
|
return max_charge ? (100 * charge / max_charge) : 0
|
|
|
|
/**
|
|
* Returns ratio remaining, [0, 1]
|
|
*/
|
|
/obj/item/cell/proc/ratio()
|
|
return max_charge ? (charge / max_charge) : 0
|
|
|
|
//* Processing *//
|
|
|
|
/obj/item/cell/process(delta_time)
|
|
if(!self_recharge)
|
|
return PROCESS_KILL
|
|
if(world.time < last_use + self_recharge_delay)
|
|
return
|
|
give(self_recharge_amount * delta_time)
|
|
|
|
//* Rendering *//
|
|
|
|
/obj/item/cell/update_icon()
|
|
if(rendering_system)
|
|
cut_overlays()
|
|
if(stripe_color)
|
|
var/image/stripe = image(icon, "cell-stripe")
|
|
stripe.color = stripe_color
|
|
add_overlay(stripe)
|
|
if(indicator_count)
|
|
var/image/indicator = image(icon, "cell-[charge <= 1? "empty" : "[ceil(charge / max_charge * 5)]"]")
|
|
indicator.color = indicator_color
|
|
add_overlay(indicator)
|
|
return ..()
|
|
|
|
//* Setters *//
|
|
|
|
/obj/item/cell/proc/set_charge(amount, update)
|
|
charge = clamp(amount, 0, max_charge)
|
|
if(update)
|
|
update_icon()
|