Ports Malf AI rolling (and some other tipping refactors) (#21900)

* some thoughts

* I really forgot to commit all of this didn't I

* Rework vendor crits

* fixes some angle jank

* yeag

* Patches up most bugs with tilting things over

* Fixes up most of the AI logic

* rolling AI can now take teleporters, visual effect is now only seen by the AI

* better effects

* Remove a bunch of now useless code

* I keep forgetting this

* cleanup in aisle seven

* oops

* some more testing

* adminbus hours

* Fixes some buildmode stuff, does some more testing

* Add documentation

* Demo bugs

* Apply suggestions from code review

Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com>

* rework crit damage amount

* Adapt recent tg changes

* Fix some small bugs in testing, contra review 1

* contra review 2

* Fixes tipping with pacifism

* restore removed icons

* Fix some remote untipping

* restore the roll action

* finally address contra's review (sorry)

* remove debug tool

* Move untilt logic into component

---------

Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com>
This commit is contained in:
Luc
2023-10-31 20:24:52 +00:00
committed by GitHub
co-authored by Henri215
parent 49afcce327
commit 95d3a49929
23 changed files with 650 additions and 245 deletions
+9
View File
@@ -226,6 +226,8 @@
#define COMSIG_CLICK_CTRL "ctrl_click"
///from base of atom/AltClick(): (/mob)
#define COMSIG_CLICK_ALT "alt_click"
/// Cancel the alt-click, since this isn't properly part of the attack chain
#define COMPONENT_CANCEL_ALTCLICK (1<<0)
///from base of atom/CtrlShiftClick(/mob)
#define COMSIG_CLICK_CTRL_SHIFT "ctrl_shift_click"
///from base of atom/MouseDrop(): (/atom/over, /mob/user)
@@ -304,6 +306,13 @@
#define HEARING_SPANS 6
#define HEARING_MESSAGE_MODE 7 */
/// Called just before something gets untilted
#define COMSIG_MOVABLE_TRY_UNTILT "movable_try_untilt"
/// Return this to block an untilt attempt
#define COMPONENT_BLOCK_UNTILT (1<<0)
/// Called when something gets untilted, from /datum/element/tilted/proc/do_untilt(atom/movable/source, mob/user)
#define COMSIG_MOVABLE_UNTILTED "movable_untilted"
///called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source)
#define COMSIG_MOVABLE_DISPOSING "movable_disposing"
///called when the movable is removed from a disposal holder object: /obj/structure/disposalpipe/proc/expel(): (obj/structure/disposalholder/H, turf/T, direction)
+7
View File
@@ -149,6 +149,13 @@
continue // These are not valid objectives to add.
GLOB.admin_objective_list[initial(O.name)] = path
for(var/path in subtypesof(/datum/tilt_crit))
var/datum/tilt_crit/crit = path
if(isnull(initial(crit.name)))
continue
crit = new path()
GLOB.tilt_crits[path] = crit
/* // Uncomment to debug chemical reaction list.
/client/verb/debug_chemical_list()
+4 -1
View File
@@ -627,7 +627,7 @@ Returns 1 if the chain up to the area contains the given typepath
return 1
/proc/is_blocked_turf(turf/T, exclude_mobs)
/proc/is_blocked_turf(turf/T, exclude_mobs, list/excluded_objs)
if(T.density)
return TRUE
if(locate(/mob/living/silicon/ai) in T) //Prevents jaunting onto the AI core cheese, AI should always block a turf due to being a dense mob even when unanchored
@@ -636,7 +636,10 @@ Returns 1 if the chain up to the area contains the given typepath
for(var/mob/living/L in T)
if(L.density)
return TRUE
var/any_excluded_objs = length(excluded_objs)
for(var/obj/O in T)
if(any_excluded_objs && (O in excluded_objs))
continue
if(O.density)
return TRUE
return FALSE
+3
View File
@@ -66,3 +66,6 @@ GLOBAL_LIST_EMPTY(blurb_witnesses)
/// List of looping sounds
GLOBAL_LIST_EMPTY(looping_sounds)
/// List of possible crits from things tipping over
GLOBAL_LIST_EMPTY(tilt_crits)
+2 -1
View File
@@ -337,7 +337,8 @@
..()
/atom/proc/AltClick(mob/user)
SEND_SIGNAL(src, COMSIG_CLICK_ALT, user)
if(SEND_SIGNAL(src, COMSIG_CLICK_ALT, user) & COMPONENT_CANCEL_ALTCLICK)
return
var/turf/T = get_turf(src)
if(T && (isturf(loc) || isturf(src)) && user.TurfAdjacent(T))
user.listed_turf = T
+109
View File
@@ -0,0 +1,109 @@
/**
* A component that should be attached to things that have been tilted over, and can be righted.
* This can optionally block normal attack_hand interactions
*/
/datum/component/tilted
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/// How long it should take to untilt
var/untilt_duration
/// Whether we should block any interactions with it
var/block_interactions
/// The angle by which we rotated as a result of tilting. Should help us avoid cases where something gets tilted until it's upright.
var/rotated_angle
/datum/component/tilted/Initialize(_untilt_duration = 16 SECONDS, _block_interactions = FALSE, _rotated_angle)
. = ..()
untilt_duration = _untilt_duration
block_interactions = _block_interactions
rotated_angle = _rotated_angle
/datum/component/tilted/InheritComponent(datum/component/C, i_am_original, _untilt_duration, _block_interactions, _rotated_angle)
. = ..()
untilt_duration = _untilt_duration
block_interactions = _block_interactions
rotated_angle += _rotated_angle
if(rotated_angle % 360 == 0)
qdel(src)
/datum/component/tilted/RegisterWithParent()
. = ..()
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(on_alt_click))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_interact))
RegisterSignal(parent, COMSIG_MOVABLE_TRY_UNTILT, PROC_REF(on_try_untilt))
RegisterSignal(parent, COMSIG_MOVABLE_UNTILTED, PROC_REF(on_untilt))
/datum/component/tilted/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, COMSIG_CLICK_ALT)
UnregisterSignal(parent, COMSIG_PARENT_EXAMINE)
UnregisterSignal(parent, COMSIG_ATOM_ATTACK_HAND)
UnregisterSignal(parent, COMSIG_MOVABLE_TRY_UNTILT)
UnregisterSignal(parent, COMSIG_MOVABLE_UNTILTED)
/datum/component/tilted/proc/on_examine(datum/source, mob/user, list/examine_list)
SIGNAL_HANDLER // COMSIG_PARENT_EXAMINE
examine_list += "<span class='notice'>It's been tilted over. <i>Alt+Click</i> it to right it.</span>"
/datum/component/tilted/proc/on_alt_click(atom/source, mob/user)
SIGNAL_HANDLER // COMSIG_CLICK_ALT
INVOKE_ASYNC(src, PROC_REF(untilt), user, untilt_duration)
return COMPONENT_CANCEL_ALTCLICK
/datum/component/tilted/proc/on_interact(atom/source, mob/user)
SIGNAL_HANDLER // COMSIG_ATOM_ATTACK_HAND
if(block_interactions)
to_chat(user, "<span class='warning'>You can't do that right now, you need to right it first!</span>")
return COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/tilted/proc/on_untilt(atom/source, mob/user)
SIGNAL_HANDLER
qdel(src)
/datum/component/tilted/proc/on_try_untilt(atom/source, mob/living/user)
SIGNAL_HANDLER
INVOKE_ASYNC(src, PROC_REF(untilt), user, untilt_duration)
/// Untilt a tilted object.
/datum/component/tilted/proc/untilt(mob/living/user, duration = 10 SECONDS)
var/atom/movable/atom_parent = parent
if(!istype(atom_parent))
return
if(!istype(user) || !atom_parent.Adjacent(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
return
if(user)
user.visible_message(
"[user] begins to right [parent].",
"You begin to right [parent]."
)
if(!do_after(user, duration, TRUE, parent))
return
user.visible_message(
"<span class='notice'>[user] rights [parent].</span>",
"<span class='notice'>You right [parent].</span>",
"<span class='notice'>You hear a loud clang.</span>"
)
if(QDELETED(atom_parent))
return
atom_parent.unbuckle_all_mobs(TRUE)
SEND_SIGNAL(parent, COMSIG_MOVABLE_UNTILTED, user)
atom_parent.layer = initial(atom_parent.layer)
var/matrix/M = matrix()
M.Turn(0)
atom_parent.transform = M
if(istype(user) && user.incapacitated())
return COMPONENT_BLOCK_UNTILT
+1 -38
View File
@@ -235,48 +235,11 @@
return
if(L == owner || L.stat == DEAD || isslime(L) || ismonkeybasic(L)) //xenobio moment
continue
new /obj/effect/temp_visual/lwap_ping(owner.loc, owner, L)
new /obj/effect/temp_visual/single_user/lwap_ping(owner.loc, owner, L)
locks++
#undef LWAP_LOCK_CAP
/obj/effect/temp_visual/lwap_ping
duration = 0.5 SECONDS
randomdir = FALSE
icon = 'icons/obj/projectiles.dmi'
/// The image shown to lwap users
var/image/lwap_image
/// The person with the lwap at the moment, really just used to remove this from their screen
var/source_UID
/// The icon state applied to the image created for this ping.
var/real_icon_state = "red_laser"
/obj/effect/temp_visual/lwap_ping/Initialize(mapload, mob/living/looker, mob/living/creature)
. = ..()
if(!looker || !creature)
return INITIALIZE_HINT_QDEL
lwap_image = image(icon = icon, loc = src, icon_state = real_icon_state, layer = ABOVE_ALL_MOB_LAYER, pixel_x = ((creature.x - looker.x) * 32), pixel_y = ((creature.y - looker.y) * 32))
lwap_image.plane = ABOVE_LIGHTING_PLANE
lwap_image.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
source_UID = looker.UID()
add_mind(looker)
/obj/effect/temp_visual/lwap_ping/Destroy()
var/mob/living/previous_user = locateUID(source_UID)
if(previous_user)
remove_mind(previous_user)
// Null so we don't shit the bed when we delete
lwap_image = null
return ..()
/// Add the image to the lwap user's screen
/obj/effect/temp_visual/lwap_ping/proc/add_mind(mob/living/looker)
looker.client?.images |= lwap_image
/// Remove the image from the lwap user's screen
/obj/effect/temp_visual/lwap_ping/proc/remove_mind(mob/living/looker)
looker.client?.images -= lwap_image
/datum/status_effect/delayed
id = "delayed_status_effect"
status_type = STATUS_EFFECT_MULTIPLE
+161
View File
@@ -663,3 +663,164 @@
/atom/movable/MouseExited(location, control, params)
usr.hud_used.screentip_text.maptext = ""
/atom/movable/proc/choose_crush_crit(mob/living/carbon/victim)
if(!length(GLOB.tilt_crits))
return
for(var/crit_path in shuffle(GLOB.tilt_crits))
var/datum/tilt_crit/C = GLOB.tilt_crits[crit_path]
if(C.is_valid(src, victim))
return C
/atom/movable/proc/handle_squish_carbon(mob/living/carbon/victim, damage_to_deal, datum/tilt_crit/crit)
// Damage points to "refund", if a crit already beats the shit out of you we can shelve some of the extra damage.
var/crit_rebate = 0
if(HAS_TRAIT(victim, TRAIT_DWARF))
// also double damage if you're short
damage_to_deal *= 2
if(crit)
crit_rebate = crit.tip_crit_effect(src, victim)
if(crit.harmless)
return
add_attack_logs(null, victim, "critically crushed by [src] causing [crit]")
else
add_attack_logs(null, victim, "crushed by [src]")
// 30% chance to spread damage across the entire body, 70% chance to target two limbs in particular
damage_to_deal = max(damage_to_deal - crit_rebate, 0)
if(prob(30))
victim.apply_damage(damage_to_deal, BRUTE, BODY_ZONE_CHEST, spread_damage = TRUE)
else
var/picked_zone
var/num_parts_to_pick = 2
for(var/i in 1 to num_parts_to_pick)
picked_zone = pick(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_ARM, BODY_ZONE_R_LEG)
victim.apply_damage((damage_to_deal) * (1 / num_parts_to_pick), BRUTE, picked_zone)
victim.AddElement(/datum/element/squish, 80 SECONDS)
#define NO_CRUSH_DIR "no_dir"
/**
* Tip over this atom onto a turf, crushing things in its path.
*
* Arguments:
* * target_turf - The turf to fall onto.
* * should_crit - If true, we'll try to crit things that we crush.
* * crit_damage_factor - If a crit is rolled, crush_damage will be multiplied by this amount.
* * forced_crit - If passed, this crit will be applied to everything it crushes.
* * weaken_time - The amount of time that weaken will be applied to crushed mobs.
* * knockdown_time - The amount of time that knockdown will be applied to crushed mobs.
* * ignore_gravity - If false, we won't fall over in zero G.
* * should_rotate - If false, we won't rotate when we fall.
* * angle - The angle by which we'll rotate. If this is null/0, we'll randomly rotate 90 degrees clockwise or counterclockwise.
* * rightable - If true, the tilted component will be applied, allowing people to alt-click to right it.
* * block_interactions_until_righted - If true, interactions with the object will be blocked until it's righted.
* * crush_dir - An override on the cardinal direction we're crushing.
*/
/atom/movable/proc/fall_and_crush(turf/target_turf, crush_damage, should_crit = FALSE, crit_damage_factor = 2, datum/tilt_crit/forced_crit, weaken_time = 4 SECONDS, knockdown_time = 10 SECONDS, ignore_gravity = FALSE, should_rotate = TRUE, angle, rightable = FALSE, block_interactions_until_righted = FALSE, crush_dir = NO_CRUSH_DIR)
if(QDELETED(src) || isnull(target_turf))
return
if(crush_dir == NO_CRUSH_DIR)
crush_dir = get_dir(get_turf(src), target_turf)
var/has_tried_to_move = FALSE
if(is_blocked_turf(target_turf, TRUE, excluded_objs=list(src)))
has_tried_to_move = TRUE
if(!Move(target_turf, crush_dir))
// we'll try to move, and if we didn't end up going anywhere, then we do nothing.
visible_message("<span class='warning'>[src] seems to rock, but doesn't fall over!</span>")
return
for(var/atom/target in (target_turf.contents) + target_turf)
if(isarea(target) || target == src) // don't crush ourselves
continue
if(isobserver(target))
continue
// ignore things that are under the ground
if(isobj(target) && (target.invisibility > SEE_INVISIBLE_LIVING) || iseffect(target) || isitem(target) || target.level == 1)
continue
var/datum/tilt_crit/crit_case = forced_crit
if(isnull(forced_crit) && should_crit)
crit_case = choose_crush_crit(target)
// note that it could still be null after this point, in which case it won't crit
var/damage_to_deal = crush_damage
if(isliving(target))
var/mob/living/L = target
if(crit_case)
damage_to_deal *= crit_damage_factor
if(iscarbon(L))
handle_squish_carbon(L, damage_to_deal, crit_case)
else
L.apply_damage(damage_to_deal, BRUTE)
L.Weaken(weaken_time)
L.emote("scream")
L.KnockDown(knockdown_time)
playsound(L, 'sound/effects/blobattack.ogg', 40, TRUE)
playsound(L, 'sound/effects/splat.ogg', 50, TRUE)
add_attack_logs(src, L, "crushed by [src]")
else if(isobj(target)) // don't crush things on the floor, that'd probably be annoying
var/obj/O = target
O.take_damage(damage_to_deal, BRUTE, "", FALSE)
else
continue
target.visible_message(
"<span class='danger'>[target] is crushed by [src]!</span>",
"<span class='userdanger'>[src] crushes you!</span>",
"<span class='warning'>You hear a loud crunch!</span>"
)
tilt_over(target_turf, angle, should_rotate, rightable, block_interactions_until_righted)
// for things that trigger on Crossed()
if(!has_tried_to_move)
Move(target_turf, crush_dir)
return TRUE
#undef NO_CRUSH_DIR
/**
* Tip over an atom without too much fuss. This won't cause damage to anything, and just rotates the thing and (optionally) adds the component.
*
* Arguments:
* * target - The turf to tilt over onto
* * rotation_angle - The angle to rotate by. If not given, defaults to random rotating by 90 degrees clockwise or counterclockwise
* * should_rotate - Whether or not we should rotate at all
* * rightable - Whether or not this object should be rightable, attaching the tilted component to it
* * block_interactions_until_righted - If true, this object will need to be righted before it can be interacted with
*/
/atom/movable/proc/tilt_over(turf/target, rotation_angle, should_rotate, rightable, block_interactions_until_righted)
visible_message("<span class='danger'>[src] tips over!</span>", "<span class='danger'>You hear a loud crash!</span>")
playsound(src, "sound/effects/bang.ogg", 100, TRUE)
var/rot_angle = rotation_angle ? rotation_angle : pick(90, -90)
if(should_rotate)
var/matrix/to_turn = turn(transform, rot_angle)
animate(src, transform = to_turn, 0.2 SECONDS)
if(target && target != get_turf(src))
throw_at(target, 1, 1, spin = FALSE)
if(rightable)
layer = ABOVE_MOB_LAYER
AddComponent(/datum/component/tilted, 14 SECONDS, block_interactions_until_righted, rot_angle)
/// Untilt a tilted object.
/atom/movable/proc/untilt(mob/living/user, duration = 10 SECONDS)
SEND_SIGNAL(src, COMSIG_MOVABLE_TRY_UNTILT, user)
/// useful callback for things that want special behavior on crush
/atom/movable/proc/on_crush_thing(atom/thing)
return
@@ -1,3 +1,9 @@
#define MALF_AI_ROLL_TIME 0.5 SECONDS
#define MALF_AI_ROLL_COOLDOWN (1 SECONDS + MALF_AI_ROLL_TIME)
#define MALF_AI_ROLL_DAMAGE 75
// crit percent
#define MALF_AI_ROLL_CRIT_CHANCE 5
//The malf AI action subtype. All malf actions are subtypes of this.
/datum/action/innate/ai
name = "AI Action"
@@ -474,7 +480,7 @@
/obj/effect/proc_holder/ranged_ai/overload_machine
active = FALSE
ranged_mousepointer = 'icons/effects/overload_machine_target.dmi'
ranged_mousepointer = 'icons/effects/cult_target.dmi'
enable_text = "<span class='notice'>You tap into the station's powernet. Click on a machine to detonate it, or use the ability again to cancel.</span>"
disable_text = "<span class='notice'>You release your hold on the powernet.</span>"
@@ -820,3 +826,91 @@
actual_action.fix_borg(robot_target)
remove_ranged_ability(ranged_ability_user, "<span class='warning'>[robot_target] successfully rebooted.</span>")
return TRUE
/datum/AI_Module/core_tilt
module_name = "Rolling Servos"
mod_pick_name = "watchforrollingcores"
description = "Allows you to slowly roll your core around, crushing anything in your path with your bulk."
cost = 10
one_purchase = FALSE
power_type = /datum/action/innate/ai/ranged/core_tilt
unlock_sound = 'sound/effects/bang.ogg'
unlock_text = "<span class='notice'>You gain the ability to roll over and crush anything in your way.</span>"
/datum/action/innate/ai/ranged/core_tilt
name = "Roll Over"
button_icon_state = "roll_over"
desc = "Allows you to roll over in the direction of your choosing, crushing anything in your way."
auto_use_uses = FALSE
linked_ability_type = /obj/effect/proc_holder/ranged_ai/roll_over
/obj/effect/proc_holder/ranged_ai/roll_over
active = FALSE
ranged_mousepointer = 'icons/effects/cult_target.dmi'
enable_text = "<span class='notice'>Your inner servos shift as you prepare to roll around. Click adjacent tiles to roll into them!</span>"
disable_text = "<span class='notice'>You disengage your rolling protocols.</span>"
COOLDOWN_DECLARE(time_til_next_tilt)
/// How long does it take us to roll?
var/roll_over_time = MALF_AI_ROLL_TIME
/// How long does it take for the ability to cool down, on top of [roll_over_time]?
var/roll_over_cooldown = MALF_AI_ROLL_COOLDOWN
/obj/effect/proc_holder/ranged_ai/roll_over/InterceptClickOn(mob/living/caller, params, atom/target_atom)
if(..())
return
if(!isAI(ranged_ability_user))
return
if(ranged_ability_user.incapacitated() || !isturf(ranged_ability_user.loc))
remove_ranged_ability()
return
if(!COOLDOWN_FINISHED(src, time_til_next_tilt))
to_chat(ranged_ability_user, "<span class='warning'>Your rolling capacitors are still powering back up!</span>")
return
var/turf/target = get_turf(target_atom)
if(isnull(target))
return
if(target == get_turf(ranged_ability_user))
to_chat(ranged_ability_user, "<span class='warning'>You can't roll over on yourself!</span>")
return
var/picked_dir = get_dir(caller, target)
if(!picked_dir)
return FALSE
// we can move during the timer so we cant just pass the ref
var/turf/temp_target = get_step(ranged_ability_user, picked_dir)
new /obj/effect/temp_visual/single_user/ai_telegraph(temp_target, ranged_ability_user)
ranged_ability_user.visible_message("<span class='danger'>[ranged_ability_user] seems to be winding up!</span>")
addtimer(CALLBACK(src, PROC_REF(do_roll_over), caller, picked_dir), MALF_AI_ROLL_TIME)
to_chat(ranged_ability_user, "<span class='warning'>Overloading machine circuitry...</span>")
COOLDOWN_START(src, time_til_next_tilt, roll_over_cooldown)
return TRUE
/obj/effect/proc_holder/ranged_ai/roll_over/proc/do_roll_over(mob/living/silicon/ai/ai_caller, picked_dir)
var/turf/target = get_step(ai_caller, picked_dir) // in case we moved we pass the dir not the target turf
if(isnull(target) || ai_caller.incapacitated() || !isturf(ai_caller.loc))
return
var/paralyze_time = clamp(6 SECONDS, 0 SECONDS, (roll_over_cooldown * 0.9)) // the clamp prevents stunlocking as the max is always a little less than the cooldown between rolls
ai_caller.allow_teleporter = TRUE
ai_caller.fall_and_crush(target, MALF_AI_ROLL_DAMAGE, prob(MALF_AI_ROLL_CRIT_CHANCE), 2, null, paralyze_time, crush_dir = picked_dir, angle = get_rotation_from_dir(picked_dir))
ai_caller.allow_teleporter = FALSE
/obj/effect/proc_holder/ranged_ai/roll_over/proc/get_rotation_from_dir(dir)
switch(dir)
if(NORTH, NORTHWEST, WEST, SOUTHWEST)
return 270 // try our best to not return 180 since it works badly with animate
if(EAST, NORTHEAST, SOUTH, SOUTHEAST)
return 90
else
stack_trace("non-standard dir entered to get_rotation_from_dir. (got: [dir])")
return 0
+3
View File
@@ -540,3 +540,6 @@
*/
/obj/machinery/proc/flicker()
return FALSE
/obj/machinery/fall_and_crush(turf/target_turf, crush_damage, should_crit, crit_damage_factor, datum/tilt_crit/forced_crit, weaken_time, knockdown_time, ignore_gravity, should_rotate, angle, rightable, block_interactions)
. = ..(target_turf, crush_damage, should_crit, crit_damage_factor, forced_crit, weaken_time, knockdown_time, ignore_gravity = FALSE, should_rotate = TRUE, rightable = TRUE, block_interactions_until_righted = TRUE)
+4 -1
View File
@@ -314,14 +314,17 @@
*/
/obj/machinery/teleport/proc/blockAI(atom/A)
if(isAI(A) || istype(A, /obj/structure/AIcore))
visible_message("<span class='warning'>The teleporter rejects the AI unit.</span>")
if(isAI(A))
var/mob/living/silicon/ai/T = A
if(T.allow_teleporter)
return FALSE
var/list/TPError = list("<span class='warning'>Firmware instructions dictate you must remain on your assigned station!</span>",
"<span class='warning'>You cannot interface with this technology and get rejected!</span>",
"<span class='warning'>External firewalls prevent you from utilizing this machine!</span>",
"<span class='warning'>Your AI core's anti-bluespace failsafes trigger and prevent teleportation!</span>")
to_chat(T, "[pick(TPError)]")
visible_message("<span class='warning'>The teleporter rejects the AI unit.</span>")
return TRUE
return FALSE
@@ -2,31 +2,47 @@
* Framework for custom vendor crits.
*/
/datum/vendor_crit
/datum/tilt_crit
/// Name of a crit. Only crits with a name will be options.
var/name
/// If it'll deal damage or not
var/harmless = FALSE
/// If we should be thrown against the mob or not.
var/fall_towards_mob = TRUE
/// List of types which we should be valid for
var/list/valid_types_whitelist = list(/atom/movable)
/// Typecache of valid types
var/list/valid_typecache
/datum/tilt_crit/New()
valid_typecache = typecacheof(valid_types_whitelist)
/**
* Return whether or not the crit selected is valid.
*/
/datum/vendor_crit/proc/is_valid(obj/machinery/economy/vending/machine, mob/living/carbon/victim)
return TRUE
/datum/tilt_crit/proc/is_valid(atom/movable/tilter, mob/living/carbon/victim)
SHOULD_CALL_PARENT(TRUE)
return is_type_in_typecache(tilter, valid_typecache)
/***
* Perform the tip crit effect on a victim.
* Arguments:
* * machine - The machine that was tipped over
* * user - The unfortunate victim upon whom it was tipped over
* * incoming_damage - The amount of damage that was already being dealt to the victim
* Returns: The "crit rebate", or the amount of damage to subtract from the original amount of damage dealt, to soften the blow.
*/
/datum/vendor_crit/proc/tip_crit_effect(obj/machinery/economy/vending/machine, mob/living/carbon/victim)
/datum/tilt_crit/proc/tip_crit_effect(atom/movable/tilter, mob/living/carbon/victim, incoming_damage)
return 0
/datum/vendor_crit/shatter
/datum/tilt_crit/shatter
name = "Leg Crush"
/datum/vendor_crit/shatter/tip_crit_effect(obj/machinery/economy/vending/machine, mob/living/carbon/victim)
/datum/tilt_crit/shatter/is_valid(atom/movable/tilter, mob/living/carbon/victim)
. = ..()
return . && iscarbon(victim)
/datum/tilt_crit/shatter/tip_crit_effect(atom/movable/tilter, mob/living/carbon/victim, incoming_damage)
victim.bleed(150)
var/obj/item/organ/external/leg/right = victim.get_organ(BODY_ZONE_R_LEG)
var/obj/item/organ/external/leg/left = victim.get_organ(BODY_ZONE_L_LEG)
@@ -43,28 +59,41 @@
)
// that's a LOT of damage, let's rebate most of it.
return machine.squish_damage * (5/6)
return incoming_damage * (5/6)
/datum/vendor_crit/pin
/datum/tilt_crit/pin
name = "Pin"
/datum/vendor_crit/pin/tip_crit_effect(obj/machinery/economy/vending/machine, mob/living/carbon/victim)
machine.forceMove(get_turf(victim))
machine.buckle_mob(victim, force=TRUE)
/datum/tilt_crit/pin/is_valid(atom/movable/tilter, mob/living/victim)
. = ..()
return . && isliving(victim)
/datum/tilt_crit/pin/tip_crit_effect(atom/movable/tilter, mob/living/victim, incoming_damage)
tilter.forceMove(get_turf(victim))
tilter.buckle_mob(victim, force=TRUE)
victim.visible_message(
"<span class='danger'>[victim] gets pinned underneath [machine]!</span>",
"<span class='userdanger'>You are pinned down by [machine]!</span>"
"<span class='danger'>[victim] gets pinned underneath [tilter]!</span>",
"<span class='userdanger'>You are pinned down by [tilter]!</span>"
)
return 0
/datum/vendor_crit/embed
/datum/vendor_crit/embed/is_valid(obj/machinery/economy/vending/machine, mob/living/carbon/victim)
/datum/tilt_crit/vendor
valid_types_whitelist = list(/obj/machinery/economy/vending)
/datum/tilt_crit/vendor/embed
name = "Panel Shatter"
/datum/tilt_crit/vendor/embed/is_valid(obj/machinery/economy/vending/machine, mob/living/carbon/victim)
. = ..()
if(!. || !istype(machine))
return
if(machine.num_shards <= 0)
return FALSE
return iscarbon(victim)
/datum/vendor_crit/embed/tip_crit_effect(obj/machinery/economy/vending/machine, mob/living/carbon/victim)
/datum/tilt_crit/vendor/embed/tip_crit_effect(obj/machinery/economy/vending/machine, mob/living/carbon/victim, incoming_damage)
victim.visible_message(
"<span class='danger'>[machine]'s panel shatters against [victim]!</span>",
"<span class='userdanger>[machine] lands on you, its panel shattering!</span>"
@@ -85,16 +114,21 @@
playsound(machine, "shatter", 50)
return machine.squish_damage * (3/4)
return incoming_damage * (3 / 4)
/datum/vendor_crit/pop_head
/datum/tilt_crit/pop_head
name = "Head Pop"
/datum/vendor_crit/pop_head/tip_crit_effect(obj/machinery/economy/vending/machine, mob/living/carbon/victim)
/datum/tilt_crit/pop_head/is_valid(atom/movable/tilter, mob/living/carbon/victim)
. = ..()
return . && iscarbon(victim)
/datum/tilt_crit/pop_head/tip_crit_effect(atom/movable/tilter, mob/living/carbon/victim, incoming_damage)
// pop!
var/obj/item/organ/external/head/H = victim.get_organ("head")
var/obj/item/organ/internal/brain/B = victim.get_int_organ_tag("brain")
if(H)
victim.visible_message("<span class='danger'>[H] gets crushed under [machine], and explodes in a shower of gore!</span>", "<span class='userdanger'>Oh f-</span>")
victim.visible_message("<span class='danger'>[H] gets crushed under [tilter], and explodes in a shower of gore!</span>", "<span class='userdanger'>Oh f-</span>")
var/gibspawner = /obj/effect/gibspawner/human
if(ismachineperson(victim))
gibspawner = /obj/effect/gibspawner/robot
@@ -107,16 +141,17 @@
H.disfigure()
victim.apply_damage(50, BRUTE, BODY_ZONE_HEAD)
else
H.visible_message("<span class='danger'>[victim]'s head seems to be crushed under [machine]...but wait, they had none in the first place!</span>")
H.visible_message("<span class='danger'>[victim]'s head seems to be crushed under [tilter]...but wait, they had none in the first place!</span>")
if(B in H)
victim.adjustBrainLoss(80)
return 0
/datum/vendor_crit/lucky
/datum/tilt_crit/lucky
harmless = TRUE
name = "Lucky"
/datum/vendor_crit/lucky/tip_crit_effect(obj/machinery/economy/vending/machine, mob/living/carbon/victim)
/datum/tilt_crit/lucky/tip_crit_effect(obj/machinery/economy/vending/machine, mob/living/carbon/victim, incoming_damage)
victim.visible_message(
"<span class='danger'>[machine] crashes around [victim], but doesn't seem to crush them!</span>",
"<span class='userdanger'>[machine] crashes around you, but only around you! You're fine!</span>"
+34 -179
View File
@@ -1,11 +1,3 @@
// Using these to decide how a vendor crush should be handled after crushing a carbon.
/// Just jump ship, the crit handled everything it needs to.
#define VENDOR_CRUSH_HANDLED 0
/// Throw the vendor at the target's tile.
#define VENDOR_THROW_AT_TARGET 1
/// Don't actually throw at the target, just tip it in place.
#define VENDOR_TIP_IN_PLACE 2
/**
* Datum used to hold information about a product in a vending machine
*/
@@ -132,16 +124,6 @@
var/crit_damage_factor = 2
/// Factor of extra damage to deal when you knock it over onto yourself
var/self_knockover_factor = 1.5
/// All possible crits that could be applied. We only need to build this up once
var/static/list/all_possible_crits = list()
/// Possible crit effects from this vending machine tipping.
var/list/possible_crits = list(
/datum/vendor_crit/pop_head,
/datum/vendor_crit/embed,
/datum/vendor_crit/pin,
/datum/vendor_crit/shatter,
/datum/vendor_crit/lucky
)
/// number of shards to apply when a crit embeds
var/num_shards = 7
/// Last time the machine was punched
@@ -189,14 +171,11 @@
if(account_database)
vendor_account = account_database.vendor_account
if(!length(all_possible_crits))
for(var/typepath in subtypesof(/datum/vendor_crit))
all_possible_crits[typepath] = new typepath()
update_icon(UPDATE_OVERLAYS)
reconnect_database()
power_change()
RegisterSignal(src, COMSIG_MOVABLE_UNTILTED, PROC_REF(on_untilt))
RegisterSignal(src, COMSIG_MOVABLE_TRY_UNTILT, PROC_REF(on_try_untilt))
/obj/machinery/economy/vending/Destroy()
SStgui.close_uis(wires)
@@ -206,10 +185,6 @@
/obj/machinery/economy/vending/examine(mob/user)
. = ..()
if(tilted)
. += "<span class='warning'>It's been tipped over and won't be usable unless it's righted.</span>"
. += "<span class='notice'>You can <b>Alt-Click</b> it to right it when adjacent.</span>"
if(aggressive)
. += "<span class='warning'>Its product lights seem to be blinking ominously...</span>"
@@ -359,11 +334,9 @@
else
..()
/obj/machinery/economy/vending/AltClick(mob/user)
if(!tilted || !Adjacent(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
return
untilt(user)
/obj/machinery/economy/vending/proc/on_try_untilt(atom/source, mob/user)
if(user && (!Adjacent(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)))
return COMPONENT_BLOCK_UNTILT
/obj/machinery/economy/vending/attackby(obj/item/I, mob/user, params)
if(tilted)
@@ -940,6 +913,10 @@
if(dump_amount >= 16)
return
/obj/machinery/economy/vending/proc/on_untilt(atom/source, mob/user)
SIGNAL_HANDLER // COMSIG_MOVABLE_UNTILTED
tilted = FALSE
//Somebody cut an important wire and now we're following a new definition of "pitch."
/obj/machinery/economy/vending/proc/throw_item()
var/obj/throw_item = null
@@ -965,167 +942,50 @@
/obj/machinery/economy/vending/onTransitZ()
return
/**
* Select a random valid crit.
*/
/obj/machinery/economy/vending/proc/choose_crit(mob/living/carbon/victim)
if(!length(possible_crits))
return
for(var/crit_path in shuffle(possible_crits))
var/datum/vendor_crit/C = all_possible_crits[crit_path]
if(C.is_valid(src, victim))
return C
/obj/machinery/economy/vending/proc/handle_squish_carbon(mob/living/carbon/victim, damage_to_deal, crit, from_combat)
// Damage points to "refund", if a crit already beats the shit out of you we can shelve some of the extra damage.
var/crit_rebate = 0
var/should_throw_at_target = TRUE
if(HAS_TRAIT(victim, TRAIT_DWARF))
// also double damage if you're short
damage_to_deal *= 2
var/datum/vendor_crit/critical_attack = choose_crit(victim)
if(!from_combat && crit && critical_attack)
crit_rebate = critical_attack.tip_crit_effect(src, victim)
if(critical_attack.harmless)
tilt_over(critical_attack.fall_towards_mob ? victim : null)
return VENDOR_CRUSH_HANDLED
should_throw_at_target = critical_attack.fall_towards_mob
add_attack_logs(null, victim, "critically crushed by [src] causing [critical_attack]")
else
victim.visible_message(
"<span class='danger'>[victim] is crushed by [src]!</span>",
"<span class='userdanger'>[src] crushes you!</span>",
"<span class='warning'>You hear a loud crunch!</span>"
)
add_attack_logs(null, victim, "crushed by [src]")
// 30% chance to spread damage across the entire body, 70% chance to target two limbs in particular
damage_to_deal = max(damage_to_deal - crit_rebate, 0)
if(prob(30))
victim.apply_damage(damage_to_deal, BRUTE, BODY_ZONE_CHEST, spread_damage = TRUE)
else
var/picked_zone
var/num_parts_to_pick = 2
for(var/i = 1 to num_parts_to_pick)
picked_zone = pick(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_ARM, BODY_ZONE_R_LEG)
victim.apply_damage((damage_to_deal) * (1 / num_parts_to_pick), BRUTE, picked_zone)
victim.AddElement(/datum/element/squish, 80 SECONDS)
victim.emote("scream")
return should_throw_at_target ? VENDOR_THROW_AT_TARGET : VENDOR_TIP_IN_PLACE
/**
* Tilts the machine onto the atom passed in.
*
* Arguments:
* * victim - The thing the machine is falling on top of
* * crit - if true, some special damage effects might happen.
* * from_combat - If true, hold off on some of the additional damage and extra effects.
*/
/obj/machinery/economy/vending/proc/tilt(atom/victim, crit = FALSE, from_combat = FALSE, from_anywhere = FALSE)
if(QDELETED(src) || !has_gravity(src) || !tiltable || tilted)
return
tilted = TRUE
layer = ABOVE_MOB_LAYER
if(from_anywhere)
forceMove(get_turf(victim))
if(Adjacent(victim))
var/damage = squish_damage
var/picked_angle = pick(90, 270)
var/should_crit = !from_combat && crit
if(!crit && !from_combat)
// only deal this extra bit of damage if they wouldn't otherwise be taking the double damage from critting
damage *= self_knockover_factor
. = fall_and_crush(get_turf(victim), damage, should_crit, crit_damage_factor, null, from_combat ? 4 SECONDS : 6 SECONDS, 12 SECONDS, FALSE, picked_angle)
if(.)
tilted = TRUE
layer = ABOVE_MOB_LAYER
var/should_throw_at_target = TRUE
. = FALSE
if(from_anywhere)
forceMove(get_turf(victim))
if(!victim || !in_range(victim, src))
tilt_over()
return
for(var/mob/living/L in get_turf(victim))
// Damage to deal outright
var/damage_to_deal = squish_damage
if(!from_combat)
L.Weaken(6 SECONDS)
if(crit)
// increase damage if you knock it over onto yourself
damage_to_deal *= crit_damage_factor
else
damage_to_deal *= self_knockover_factor
else
L.Weaken(4 SECONDS)
if(iscarbon(L))
var/throw_spec = handle_squish_carbon(victim, damage_to_deal, crit, from_combat)
switch(throw_spec)
if(VENDOR_CRUSH_HANDLED)
return TRUE
if(VENDOR_THROW_AT_TARGET)
should_throw_at_target = TRUE
if(VENDOR_TIP_IN_PLACE)
should_throw_at_target = FALSE
else
L.visible_message(
"<span class='danger'>[L] is crushed by [src]!</span>",
"<span class='userdanger'>[src] falls on top of you, crushing you!</span>"
)
L.apply_damage(damage_to_deal, BRUTE)
add_attack_logs(null, L, "crushed by [src]")
. = TRUE
L.KnockDown(12 SECONDS)
playsound(L, "sound/effects/blobattack.ogg", 40, TRUE)
playsound(L, "sound/effects/splat.ogg", 50, TRUE)
tilt_over(should_throw_at_target ? victim : null)
/obj/machinery/economy/vending/proc/tilt_over(mob/victim)
visible_message("<span class='danger'>[src] tips over!</span>", "<span class='danger'>You hear a loud crash!</span>")
playsound(src, "sound/effects/bang.ogg", 100, TRUE)
var/matrix/M = matrix()
M.Turn(pick(90, 270))
transform = M
if(victim && get_turf(victim) != get_turf(src))
if(get_turf(victim) != get_turf(src))
throw_at(get_turf(victim), 1, 1, spin = FALSE)
/obj/machinery/economy/vending/proc/untilt(mob/user)
if(!tilted)
return
if(user)
user.visible_message(
"[user] begins to right [src].",
"You begin to right [src]."
)
if(!do_after(user, 7 SECONDS, TRUE, src))
return
user.visible_message(
"<span class='notice'>[user] rights [src].</span>",
"<span class='notice'>You right [src].</span>",
"<span class='notice'>You hear a loud clang.</span>"
)
unbuckle_all_mobs(TRUE)
tilted = FALSE
layer = initial(layer)
var/matrix/M = matrix()
M.Turn(0)
transform = M
tilt_over(should_throw_at_target ? victim : null)
/obj/machinery/economy/vending/shove_impact(mob/living/target, mob/living/attacker)
if(HAS_TRAIT(target, TRAIT_FLATTENED))
return
add_attack_logs(attacker, target, "shoved into a vending machine ([src])")
if(!HAS_TRAIT(attacker, TRAIT_PACIFISM))
add_attack_logs(attacker, target, "shoved into a vending machine ([src])")
tilt(target, from_combat = TRUE)
else
else if(HAS_TRAIT_FROM(attacker, TRAIT_PACIFISM, GHOST_ROLE)) // should only apply to the ghost bar
add_attack_logs(attacker, target, "shoved into a vending machine ([src]), but flattened themselves.")
tilt(attacker, crit = TRUE, from_anywhere = TRUE) // get fucked
else
attacker.visible_message("<span class='notice'>[attacker] lightly presses [target] against [src].</span>", "<span class='warning'>You lightly press [target] against [src], you don't want to hurt [target.p_them()]!</span>")
return TRUE
/obj/machinery/economy/vending/hit_by_thrown_carbon(mob/living/carbon/human/C, datum/thrownthing/throwingdatum, damage, mob_hurt, self_hurt)
@@ -1153,8 +1013,3 @@
premium = list()
*/
#undef VENDOR_CRUSH_HANDLED
#undef VENDOR_THROW_AT_TARGET
#undef VENDOR_TIP_IN_PLACE
@@ -399,6 +399,77 @@
icon_state = "rcd_short_reverse"
duration = 3.1 SECONDS
/**
* A visual effect that will be shown only to a particular user for a period of time.
*/
/obj/effect/temp_visual/single_user
/// The image to show to the user
var/image/displayed_image
/// The UID of the person who the image is being displayed to
var/source_UID
/// The real icon state to be applied to the image
var/image_icon_state
/// The plane to apply the image to
var/image_plane = ABOVE_LIGHTING_PLANE
/// The layer to apply the image to
var/image_layer = ABOVE_ALL_MOB_LAYER
/// The icon to pull the image from
var/image_icon
/obj/effect/temp_visual/single_user/Initialize(mapload, mob/living/user)
. = ..()
if(!user)
return INITIALIZE_HINT_QDEL
displayed_image = create_image(user)
displayed_image.plane = image_plane
displayed_image.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
source_UID = user.UID()
add_mind(user)
/obj/effect/temp_visual/single_user/proc/create_image(mob/living/looker)
return image(icon = image_icon, loc = src, icon_state = image_icon_state, layer = image_layer)
/obj/effect/temp_visual/single_user/Destroy()
var/mob/living/previous_user = locateUID(source_UID)
if(previous_user)
remove_mind(previous_user)
// Null so we don't shit the bed when we delete
displayed_image = null
return ..()
/// Add the image to the user's screen
/obj/effect/temp_visual/single_user/proc/add_mind(mob/living/looker)
looker.client?.images |= displayed_image
/// Remove the image from the user's screen
/obj/effect/temp_visual/single_user/proc/remove_mind(mob/living/looker)
looker.client?.images -= displayed_image
/obj/effect/temp_visual/single_user/lwap_ping
duration = 0.5 SECONDS
randomdir = FALSE
image_icon = 'icons/obj/projectiles.dmi'
image_icon_state = "red_laser"
/obj/effect/temp_visual/single_user/lwap_ping/Initialize(mapload, mob/living/looker, mob/living/creature)
if(!looker || !creature)
return INITIALIZE_HINT_QDEL
. = ..()
displayed_image.pixel_x = (creature.x - looker.x) * 32
displayed_image.pixel_y = (creature.y - looker.y) * 32
/obj/effect/temp_visual/single_user/ai_telegraph
duration = 2 SECONDS
randomdir = FALSE
image_layer = BELOW_MOB_LAYER
image_plane = GAME_PLANE
image_icon = 'icons/mob/telegraphing/telegraph_holographic.dmi'
image_icon_state = "target_box"
/obj/effect/temp_visual/obliteration
duration = 2 SECONDS
+3
View File
@@ -206,3 +206,6 @@
take_damage(power / 8000, BURN, ENERGY)
power -= power / 2000 //walls take a lot out of ya
. = ..()
/obj/structure/fall_and_crush(turf/target_turf, crush_damage, should_crit, crit_damage_factor, datum/tilt_crit/forced_crit, weaken_time, knockdown_time, ignore_gravity, should_rotate, angle, rightable, block_interactions)
. = ..(target_turf, crush_damage, should_crit, crit_damage_factor, forced_crit, weaken_time, knockdown_time, ignore_gravity, should_rotate, angle, rightable = TRUE, block_interactions_until_righted = FALSE)
+72
View File
@@ -0,0 +1,72 @@
/datum/buildmode_mode/tilting
key = "tilt"
/// The thing we're tilting over
var/atom/movable/tilter
var/crush_damage = 25
var/crit_chance = 0
var/datum/tilt_crit/forced_crit
var/weaken_time = 4 SECONDS
var/knockdown_time = 14 SECONDS
var/ignore_gravity = TRUE
var/should_rotate = TRUE
var/rotation_angle
var/rightable = TRUE
var/block_interactions_until_righted = TRUE
/datum/buildmode_mode/tilting/show_help(mob/user)
to_chat(user, "<span class='notice'>***********************************************************</span>")
to_chat(user, "<span class='notice'>Left Mouse Button on obj/mob = Select atom to tilt</span>")
to_chat(user, "<span class='notice'>Right Mouse Button on turf/obj/mob = Tilt selected atom onto target</span>")
to_chat(user, "<span class='notice'>Right Mouse Button + Alt = Untilt selected atom</span>")
to_chat(user, "<span class='warning'>Right-click the main action button to customize tilting behavior.</span>")
to_chat(user, "<span class='notice'>***********************************************************</span>")
/datum/buildmode_mode/tilting/change_settings(mob/user)
crush_damage = input(user, "Crush Damage", "Damage", initial(crush_damage)) as num|null
crit_chance = input(user, "Crit Chance (out of 100)", "Crit chance", 0) as num|null
if(crit_chance > 0)
var/forced_crit_path = input(user, "Force a specific crit?", "Forced Crit", null) as null|anything in GLOB.tilt_crits
if(forced_crit_path)
forced_crit = GLOB.tilt_crits[forced_crit_path]
weaken_time = input(user, "How long to weaken (in seconds)?", "Weaken Time", 4) as num|null
weaken_time = weaken_time SECONDS
knockdown_time = input(user, "How long to knockdown (in seconds)?", "Knockdown Time", 12) as num|null
knockdown_time = knockdown_time SECONDS
ignore_gravity = alert(user, "Ignore gravity?", "Ignore gravity", "Yes", "No") == "Yes"
should_rotate = alert(user, "Should it rotate on falling?", "Should rotate", "Yes", "No") == "Yes"
if(should_rotate)
rotation_angle = input(user, "Which angle to rotate at? (if empty, defaults to 90 degrees in either direction)", "Rotation angle", 0) as num|null
rightable = alert(user, "Should it be rightable with alt-click?", "Rightable", "Yes", "No") == "Yes"
if(rightable)
block_interactions_until_righted = alert(user, "Should it block interactions until righted (by alt-clicking)?", "Block interactions", "Yes", "No") == "Yes"
/datum/buildmode_mode/tilting/handle_click(mob/user, params, atom/movable/object)
var/list/pa = params2list(params)
var/left_click = pa.Find("left")
var/right_click = pa.Find("right")
var/alt_click = pa.Find("alt")
if(left_click)
if(!ismovable(object))
return
tilter = object
to_chat(user, "Selected object '[tilter]' to tilt.")
if(right_click)
if(!tilter)
to_chat(user, "<span class='warning'>You need to select something to tilt (or untilt) first.</span>")
return
if(tilter.GetComponent(/datum/component/tilted) && alt_click)
tilter.untilt(duration = 0)
log_admin("Build Mode: [key_name(user)] has righted [tilter] ([tilter.x],[tilter.y],[tilter.z])")
return
if(!object || isnull(get_turf(object)))
to_chat(user, "<span class='warning'>You need to select a target first.</span>")
return
tilter.fall_and_crush(get_turf(object), crush_damage, prob(crit_chance), 2, forced_crit, weaken_time, knockdown_time, ignore_gravity, should_rotate, rotation_angle, rightable, block_interactions_until_righted)
log_admin("Build Mode: [key_name(user)] tilted [tilter] onto [ADMIN_COORDJMP(object)]")
+5
View File
@@ -1342,3 +1342,8 @@ so that different stomachs can handle things in different ways VB*/
if(wear_suit.flags_inv & HIDEGLOVES)
clean_hands = FALSE
..(clean_hands, clean_mask, clean_feet)
/mob/living/carbon/fall_and_crush(turf/target_turf, crush_damage, should_crit, crit_damage_factor, datum/tilt_crit/forced_crit, weaken_time, knockdown_time, ignore_gravity, should_rotate = TRUE, angle)
// keep most of what's passed in, but don't change the angle
. = ..(target_turf, crush_damage, should_crit, crit_damage_factor, forced_crit, weaken_time, knockdown_time, should_rotate = FALSE, rightable = FALSE)
KnockDown(10 SECONDS)
@@ -104,6 +104,9 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
var/acceleration = 1
var/tracking = FALSE //this is 1 if the AI is currently tracking somebody, but the track has not yet been completed.
/// If true, this AI core can use the teleporter.
var/allow_teleporter = FALSE
var/obj/machinery/camera/portable/builtInCamera
var/obj/structure/AIcore/deactivated/linked_core //For exosuit control
@@ -1194,6 +1194,9 @@
power += amount
message_admins("[src] has been activated and given an increase EER of [amount] at [ADMIN_JMP(src)]")
/obj/machinery/atmospherics/supermatter_crystal/on_crush_thing(atom/thing)
Bumped(thing)
/obj/machinery/atmospherics/supermatter_crystal/proc/make_next_event_time()
// Some completely random bullshit to make a "bell curve"
var/fake_time = rand(5 MINUTES, 25 MINUTES)