diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index f07c56d9b7c..94b22d55ba7 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -297,14 +297,16 @@ //Used as an upper limit for species that continuously gain nutriment #define NUTRITION_LEVEL_ALMOST_FULL 535 -//Charge levels for Ethereals, in joules. +// The standard charge all other Ethereal charge defines are scaled against. +#define STANDARD_ETHEREAL_CHARGE (1 * STANDARD_CELL_CHARGE) +// Charge levels for Ethereals, in joules. #define ETHEREAL_CHARGE_NONE 0 -#define ETHEREAL_CHARGE_LOWPOWER (0.4 * STANDARD_CELL_CHARGE) -#define ETHEREAL_CHARGE_NORMAL (1 * STANDARD_CELL_CHARGE) -#define ETHEREAL_CHARGE_ALMOSTFULL (1.5 * STANDARD_CELL_CHARGE) -#define ETHEREAL_CHARGE_FULL (2 * STANDARD_CELL_CHARGE) -#define ETHEREAL_CHARGE_OVERLOAD (2.5 * STANDARD_CELL_CHARGE) -#define ETHEREAL_CHARGE_DANGEROUS (3 * STANDARD_CELL_CHARGE) +#define ETHEREAL_CHARGE_LOWPOWER (0.4 * STANDARD_ETHEREAL_CHARGE) +#define ETHEREAL_CHARGE_NORMAL (1 * STANDARD_ETHEREAL_CHARGE) +#define ETHEREAL_CHARGE_ALMOSTFULL (1.5 * STANDARD_ETHEREAL_CHARGE) +#define ETHEREAL_CHARGE_FULL (2 * STANDARD_ETHEREAL_CHARGE) +#define ETHEREAL_CHARGE_OVERLOAD (2.5 * STANDARD_ETHEREAL_CHARGE) +#define ETHEREAL_CHARGE_DANGEROUS (3 * STANDARD_ETHEREAL_CHARGE) #define CRYSTALIZE_COOLDOWN_LENGTH (120 SECONDS) @@ -452,7 +454,7 @@ #define DOOR_CRUSH_DAMAGE 15 //the amount of damage that airlocks deal when they crush you #define HUNGER_FACTOR 0.05 //factor at which mob nutrition decreases -#define ETHEREAL_DISCHARGE_RATE (8e-3 * STANDARD_CELL_CHARGE) // Rate at which ethereal stomach charge decreases +#define ETHEREAL_DISCHARGE_RATE (1e-3 * STANDARD_ETHEREAL_CHARGE) // Rate at which ethereal stomach charge decreases /// How much nutrition eating clothes as moth gives and drains #define CLOTHING_NUTRITION_GAIN 15 #define REAGENTS_METABOLISM 0.2 //How many units of reagent are consumed per second, by default. diff --git a/code/modules/power/apc/apc_attack.dm b/code/modules/power/apc/apc_attack.dm index c156d8bb070..1d3c1809a9f 100644 --- a/code/modules/power/apc/apc_attack.dm +++ b/code/modules/power/apc/apc_attack.dm @@ -2,7 +2,9 @@ /// How long it takes an ethereal to drain or charge APCs. Also used as a spam limiter. #define ETHEREAL_APC_DRAIN_TIME (3 SECONDS) /// How much power ethereals gain/drain from APCs. -#define ETHEREAL_APC_POWER_GAIN (10 * STANDARD_CELL_CHARGE) +#define ETHEREAL_APC_POWER_GAIN (0.1 * STANDARD_ETHEREAL_CHARGE) +/// Delay between ethereal action and balloon alert, to avoid colliding with previously queued balloon alerts. +#define ETHEREAL_APC_ALERT_DELAY (0.75 SECONDS) /obj/machinery/power/apc/attack_hand_secondary(mob/user, list/modifiers) . = ..() @@ -12,68 +14,100 @@ return if(!ishuman(user)) return - var/mob/living/carbon/human/apc_interactor = user - var/obj/item/organ/stomach/ethereal/maybe_ethereal_stomach = apc_interactor.get_organ_slot(ORGAN_SLOT_STOMACH) + var/mob/living/carbon/human/human_user = user + var/obj/item/organ/stomach/ethereal/maybe_ethereal_stomach = human_user.get_organ_slot(ORGAN_SLOT_STOMACH) if(!istype(maybe_ethereal_stomach)) togglelock(user) else if(maybe_ethereal_stomach.cell.charge() >= ETHEREAL_CHARGE_NORMAL) togglelock(user) - ethereal_interact(user, modifiers) + ethereal_interact(human_user, maybe_ethereal_stomach, modifiers) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN /// Special behavior for when an ethereal interacts with an APC. -/obj/machinery/power/apc/proc/ethereal_interact(mob/living/user, list/modifiers) - if(!ishuman(user)) +/obj/machinery/power/apc/proc/ethereal_interact(mob/living/carbon/human/user, obj/item/organ/stomach/ethereal/used_stomach, list/modifiers) + if(!LAZYACCESS(modifiers, RIGHT_CLICK)) return - var/mob/living/carbon/human/ethereal = user - var/obj/item/organ/stomach/maybe_stomach = ethereal.get_organ_slot(ORGAN_SLOT_STOMACH) - // how long we wanna wait before we show the balloon alert. don't want it to be very long in case the ethereal wants to opt-out of doing that action, just long enough to where it doesn't collide with previously queued balloon alerts. - var/alert_timer_duration = 0.75 SECONDS + if(isnull(cell)) + return + if(used_stomach.drain_time > world.time) + return + if(user.combat_mode) + discharge_to_ethereal(user, used_stomach) + else + charge_from_ethereal(user, used_stomach) - if(!istype(maybe_stomach, /obj/item/organ/stomach/ethereal)) - return - var/charge_limit = ETHEREAL_CHARGE_DANGEROUS - ETHEREAL_APC_POWER_GAIN - var/obj/item/organ/stomach/ethereal/stomach = maybe_stomach - var/obj/item/stock_parts/power_store/stomach_cell = stomach.cell - if(!((stomach?.drain_time < world.time) && LAZYACCESS(modifiers, RIGHT_CLICK))) - return - if(ethereal.combat_mode) - if(cell.charge <= (cell.maxcharge / 2)) // ethereals can't drain APCs under half charge, this is so that they are forced to look to alternative power sources if the station is running low - addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), ethereal, "safeties prevent draining!"), alert_timer_duration) - return - if(stomach_cell.charge() > charge_limit) - addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), ethereal, "charge is full!"), alert_timer_duration) - return - stomach.drain_time = world.time + ETHEREAL_APC_DRAIN_TIME - addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), ethereal, "draining power"), alert_timer_duration) - while(do_after(user, ETHEREAL_APC_DRAIN_TIME, target = src)) - if(cell.charge <= (cell.maxcharge / 2) || (stomach_cell.charge() > charge_limit)) - return - balloon_alert(ethereal, "received charge") - stomach.adjust_charge(ETHEREAL_APC_POWER_GAIN) - cell.use(ETHEREAL_APC_POWER_GAIN) +/// Handles discharging our internal cell to an ethereal and their stomach +/obj/machinery/power/apc/proc/discharge_to_ethereal(mob/living/carbon/human/user, obj/item/organ/stomach/ethereal/used_stomach) + var/half_max_charge = cell.max_charge() / 2 + // Ethereals can't drain APCs under half charge, so that they are forced to look to alternative power sources if the station is running low + if(cell.charge() < half_max_charge) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), user, "safeties prevent draining!"), ETHEREAL_APC_ALERT_DELAY) return - if(cell.charge >= cell.maxcharge - ETHEREAL_APC_POWER_GAIN) - addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), ethereal, "APC can't receive more power!"), alert_timer_duration) + var/obj/item/stock_parts/power_store/stomach_cell = used_stomach.cell + used_stomach.drain_time = world.time + ETHEREAL_APC_DRAIN_TIME + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), user, "draining power..."), ETHEREAL_APC_ALERT_DELAY) + while(do_after(user, ETHEREAL_APC_DRAIN_TIME, target = src)) + if(isnull(used_stomach) || (used_stomach != user.get_organ_slot(ORGAN_SLOT_STOMACH))) + balloon_alert(user, "stomach removed!?") + return + if(isnull(cell)) + balloon_alert(user, "cell removed!") + return + if(cell.charge() < half_max_charge) + balloon_alert(user, "safeties kicked in!") + return + + var/our_available_charge = cell.charge() - half_max_charge + var/stomach_used_charge = stomach_cell.used_charge() + var/potential_charge = min(our_available_charge, stomach_used_charge) + var/to_drain = min(ETHEREAL_APC_POWER_GAIN, potential_charge) + var/energy_drained = cell.use(to_drain, force = TRUE) + used_stomach.adjust_charge(energy_drained) + + if(stomach_cell.used_charge() <= 0) + balloon_alert(user, "your charge is full!") + return + if(cell.charge() <= 0) + balloon_alert(user, "apc is empty!") + return + +/// Handles charging our internal cell from an ethereal and their stomach +/obj/machinery/power/apc/proc/charge_from_ethereal(mob/living/carbon/human/user, obj/item/organ/stomach/ethereal/used_stomach) + if(cell.charge() >= cell.max_charge()) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), user, "apc full!"), ETHEREAL_APC_ALERT_DELAY) return - if(stomach_cell.charge() < ETHEREAL_APC_POWER_GAIN) - addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), ethereal, "charge is too low!"), alert_timer_duration) + var/obj/item/stock_parts/power_store/stomach_cell = used_stomach.cell + if(stomach_cell.charge() <= 0) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), user, "charge is too low!"), ETHEREAL_APC_ALERT_DELAY) return - stomach.drain_time = world.time + ETHEREAL_APC_DRAIN_TIME - addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), ethereal, "transfering power"), alert_timer_duration) + + used_stomach.drain_time = world.time + ETHEREAL_APC_DRAIN_TIME + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), user, "transferring power..."), ETHEREAL_APC_ALERT_DELAY) if(!do_after(user, ETHEREAL_APC_DRAIN_TIME, target = src)) return - if((cell.charge >= (cell.maxcharge - ETHEREAL_APC_POWER_GAIN)) || (stomach_cell.charge() < ETHEREAL_APC_POWER_GAIN)) - balloon_alert(ethereal, "can't transfer power!") + if(isnull(used_stomach) || (used_stomach != user.get_organ_slot(ORGAN_SLOT_STOMACH))) + balloon_alert(user, "stomach removed!?") return - if(istype(stomach)) - while(do_after(user, ETHEREAL_APC_DRAIN_TIME, target = src)) - balloon_alert(ethereal, "transferred power") - cell.give(-stomach.adjust_charge(-ETHEREAL_APC_POWER_GAIN)) - else - balloon_alert(ethereal, "can't transfer power!") + if(isnull(cell)) + balloon_alert(user, "cell removed!") + return + + var/stomach_charge = stomach_cell.charge() + var/our_used_charge = cell.used_charge() + var/potential_charge = min(stomach_charge, our_used_charge) + var/to_drain = min(ETHEREAL_APC_POWER_GAIN, potential_charge) + var/energy_drained = used_stomach.adjust_charge(-to_drain) + cell.give(-energy_drained) + + if(cell.used_charge() <= 0) + balloon_alert(user, "apc is full!") + return + if(stomach_cell.charge() <= 0) + balloon_alert(user, "out of charge!") + return + // attack with hand - remove cell (if cover open) or interact with the APC /obj/machinery/power/apc/attack_hand(mob/user, list/modifiers) @@ -134,3 +168,4 @@ #undef ETHEREAL_APC_DRAIN_TIME #undef ETHEREAL_APC_POWER_GAIN +#undef ETHEREAL_APC_ALERT_DELAY diff --git a/code/modules/power/power_store.dm b/code/modules/power/power_store.dm index ddf35e2bed3..269faef2821 100644 --- a/code/modules/power/power_store.dm +++ b/code/modules/power/power_store.dm @@ -1,6 +1,7 @@ -#define CELL_DRAIN_TIME 35 -#define CELL_POWER_GAIN (0.06 * STANDARD_CELL_CHARGE) -#define CELL_POWER_DRAIN (0.75 * STANDARD_CELL_CHARGE) +#define ETHEREAL_CELL_DRAIN_TIME (3.5 SECONDS) +#define ETHEREAL_CELL_POWER_DRAIN (0.75 * STANDARD_CELL_CHARGE) +/// The factor by which we multiply drain to get how much we gain +#define ETHEREAL_CELL_POWER_GAIN_FACTOR 0.08 /** * # Power store abstract type @@ -288,36 +289,51 @@ tesla_zap(source = src, zap_range = 10, power = discharged_energy) /obj/item/stock_parts/power_store/attack_self(mob/user) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - var/obj/item/organ/stomach/maybe_stomach = H.get_organ_slot(ORGAN_SLOT_STOMACH) + . = ..() + if(.) + return - if(istype(maybe_stomach, /obj/item/organ/stomach/ethereal)) + if(!ishuman(user)) + return - var/charge_limit = ETHEREAL_CHARGE_DANGEROUS - CELL_POWER_GAIN - var/obj/item/organ/stomach/ethereal/stomach = maybe_stomach - var/obj/item/stock_parts/power_store/stomach_cell = stomach.cell - if((stomach.drain_time > world.time) || !stomach) - return - if(charge < CELL_POWER_DRAIN) - to_chat(H, span_warning("[src] doesn't have enough power!")) - return - if(stomach_cell.charge() > charge_limit) - to_chat(H, span_warning("Your charge is full!")) - return - to_chat(H, span_notice("You begin clumsily channeling power from [src] into your body.")) - stomach.drain_time = world.time + CELL_DRAIN_TIME - while(do_after(user, CELL_DRAIN_TIME, target = src)) - if((charge < CELL_POWER_DRAIN) || (stomach_cell.charge() > charge_limit)) - return - if(istype(stomach)) - to_chat(H, span_notice("You receive some charge from [src], wasting some in the process.")) - stomach.adjust_charge(CELL_POWER_GAIN) - charge -= CELL_POWER_DRAIN //you waste way more than you receive, so that ethereals cant just steal one cell and forget about hunger - else - to_chat(H, span_warning("You can't receive charge from [src]!")) + var/mob/living/carbon/human/human_user = user + var/obj/item/organ/stomach/ethereal/user_stomach = human_user.get_organ_slot(ORGAN_SLOT_STOMACH) + if(!istype(user_stomach)) + return + if(user_stomach.drain_time > world.time) + return + + ethereal_drain(human_user, user_stomach) + +/// Handles letting an ethereal drain our charge into their stomach +/obj/item/stock_parts/power_store/proc/ethereal_drain(mob/living/carbon/human/user, obj/item/organ/stomach/ethereal/used_stomach) + if(charge() <= 0) + balloon_alert(user, "out of charge!") + return + + var/obj/item/stock_parts/power_store/stomach_cell = used_stomach.cell + used_stomach.drain_time = world.time + ETHEREAL_CELL_DRAIN_TIME + to_chat(user, span_notice("You begin clumsily channeling power from [src] into your body.")) + + while(do_after(user, ETHEREAL_CELL_DRAIN_TIME, target = src)) + if(isnull(used_stomach) || (used_stomach != user.get_organ_slot(ORGAN_SLOT_STOMACH))) + balloon_alert(user, "stomach removed!?") return + var/our_charge = charge() + var/scaled_stomach_used_charge = stomach_cell.used_charge() / ETHEREAL_CELL_POWER_GAIN_FACTOR + var/potential_charge = min(our_charge, scaled_stomach_used_charge) + var/to_drain = min(ETHEREAL_CELL_POWER_DRAIN, potential_charge) + var/energy_drained = use(to_drain, force = TRUE) + used_stomach.adjust_charge(energy_drained * ETHEREAL_CELL_POWER_GAIN_FACTOR) + update_appearance(UPDATE_OVERLAYS) + + if(stomach_cell.used_charge() <= 0) + balloon_alert(user, "your charge is full!") + return + if(charge() <= 0) + balloon_alert(user, "out of charge!") + return /obj/item/stock_parts/power_store/blob_act(obj/structure/blob/B) SSexplosions.high_mov_atom += src @@ -328,6 +344,6 @@ /obj/item/stock_parts/power_store/get_part_rating() return maxcharge * 10 + charge -#undef CELL_DRAIN_TIME -#undef CELL_POWER_GAIN -#undef CELL_POWER_DRAIN +#undef ETHEREAL_CELL_DRAIN_TIME +#undef ETHEREAL_CELL_POWER_DRAIN +#undef ETHEREAL_CELL_POWER_GAIN_FACTOR diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm index 76c88142c92..f9281151476 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm @@ -2655,7 +2655,7 @@ var/mob/living/carbon/exposed_carbon = exposed_mob var/obj/item/organ/stomach/ethereal/stomach = exposed_carbon.get_organ_slot(ORGAN_SLOT_STOMACH) if(istype(stomach)) - stomach.adjust_charge(reac_volume * 0.001 * ETHEREAL_CHARGE_NORMAL) + stomach.adjust_charge(reac_volume * ETHEREAL_DISCHARGE_RATE) /datum/reagent/consumable/ethanol/telepole name = "Telepole" @@ -2675,7 +2675,7 @@ var/mob/living/carbon/exposed_carbon = exposed_mob var/obj/item/organ/stomach/ethereal/stomach = exposed_carbon.get_organ_slot(ORGAN_SLOT_STOMACH) if(istype(stomach)) - stomach.adjust_charge(reac_volume * 0.008 * ETHEREAL_CHARGE_NORMAL) + stomach.adjust_charge(reac_volume * 8 * ETHEREAL_DISCHARGE_RATE) /datum/reagent/consumable/ethanol/pod_tesla name = "Pod Tesla" @@ -2702,7 +2702,7 @@ var/mob/living/carbon/exposed_carbon = exposed_mob var/obj/item/organ/stomach/ethereal/stomach = exposed_carbon.get_organ_slot(ORGAN_SLOT_STOMACH) if(istype(stomach)) - stomach.adjust_charge(reac_volume * 0.03 * ETHEREAL_CHARGE_NORMAL) + stomach.adjust_charge(reac_volume * 30 * ETHEREAL_DISCHARGE_RATE) // Welcome to the Blue Room Bar and Grill, home to Mars' finest cocktails /datum/reagent/consumable/ethanol/rice_beer diff --git a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm index 529d6719763..f5016f4ffc5 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm @@ -1296,4 +1296,4 @@ var/mob/living/carbon/exposed_carbon = exposed_mob var/obj/item/organ/stomach/ethereal/stomach = exposed_carbon.get_organ_slot(ORGAN_SLOT_STOMACH) if(istype(stomach)) - stomach.adjust_charge(reac_volume * 0.02 * ETHEREAL_CHARGE_NORMAL) + stomach.adjust_charge(reac_volume * 20 * ETHEREAL_DISCHARGE_RATE) diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 3b866fa2d7f..afce0b40f27 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -979,7 +979,7 @@ var/mob/living/carbon/exposed_carbon = exposed_mob var/obj/item/organ/stomach/ethereal/stomach = exposed_carbon.get_organ_slot(ORGAN_SLOT_STOMACH) if(istype(stomach)) - stomach.adjust_charge(reac_volume * 0.03 * ETHEREAL_CHARGE_NORMAL) + stomach.adjust_charge(reac_volume * 30 * ETHEREAL_DISCHARGE_RATE) /datum/reagent/consumable/liquidelectricity/enriched/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..()