Files
Matt Atlas e0aa218843 Shell May Cry: A comprehensive rework of synthetics. (#20721)
https://www.youtube.com/watch?v=9mPvZ96pHJI

A pull request commissioned by the Synthetic Lore Team to
comprehensively rework synthetics (read: IPCs) and how they work in
Aurora. The objective is to make IPCs as unique as possible from humans,
upgrading the robotic feel and atmosphere, while also preserving a good
sense of balance in-game.

Key features:

- A comprehensive expansion of synthetic organs, all of which now
fulfill a purpose: hydraulics, cooling units, power systems, actuators,
diagnostics units.
- Customizable organs with benefits and drawbacks, such as with cooling
units and power systems.
- Unique ways to repair the organs and more involved steps.
- Unique damage mechanics - every organ has wiring and electronics which
affect its functioning, and they are defended by plating which provides
natural armour.
- Improved and immersive diagnostics.
- Unique features, benefits, and drawbacks for every IPC frame.
- A rework of the positronic brain, which can be either destroyed or
shut down, alongside effects caused by low integrity.
- A rework of how EMPs affect IPC organs.
- Non-binary damage states for each organ.

To-do:

- [x] Finish the unique features for each frame.
- [x] Look into if mechanical synthskin is possible.
- [x] Power system.
- [x] Posibrain mechanics.
- [ ] Passive cooling expansion.
- [x] EMP mechanics.
- [x] Repair mechanics.
- [x] Mob weight mechanics.
- [ ] Gurney for heavy mobs.
- [x] New augments.
- [ ] IPC tag scanning and flashing.

---------

Signed-off-by: Werner <1331699+Arrow768@users.noreply.github.com>
Co-authored-by: Matt Atlas <liermattia@gmail.com>
Co-authored-by: Geeves <22774890+Geevies@users.noreply.github.com>
Co-authored-by: Werner <1331699+Arrow768@users.noreply.github.com>
2025-12-21 16:26:23 +00:00

403 lines
12 KiB
Plaintext

/obj
layer = OBJ_LAYER
animate_movement = 2
/// Used to store information about the contents of the object.
var/list/matter
/// Whether the object can be recycled (eaten) by something like the Autolathe
var/recyclable = FALSE
/// Size of the object.
var/w_class
/// Used by R&D to determine what research bonuses it grants.
var/list/origin_tech = null
/// Universal "unacidabliness" var, here so you can use it in any obj.
var/unacidable = FALSE
/// Special flags such as whether or not this object can be rotated.
var/obj_flags
/// Integer. Used for varied damage and item volume calculations.
var/throwforce = 1
/// Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]"
var/list/attack_verb
/// Whether this object cuts.
var/sharp = FALSE
/// Whether this object is more likely to dismember its poor foes.
var/edge = FALSE
/// If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING!
var/in_use = FALSE
/// Type of damage this object deals.
var/damtype = DAMAGE_BRUTE
var/force = 0
var/armor_penetration = 0
/// Makes an object not able to slice things.
var/noslice = FALSE
var/being_shocked = 0
/// If set, this holds the 3-letter shortname of a species, used for species-specific worn icons.
var/icon_species_tag = ""
/// If TRUE, this item will automatically change its species tag to match the wearer's species, given the wearer's species is listed in icon_supported_species_tags.
var/icon_auto_adapt = FALSE
/**
* A list of strings used with icon_auto_adapt, a list of species which have differing appearances for this item,
* based on the specie short name
*/
var/list/icon_supported_species_tags
/// If `TRUE`, will use the `icon_species_tag` var for rendering this item in the left/right hand.
var/icon_species_in_hand = FALSE
var/equip_slot = 0
/// Played when the item is used, for example tools.
var/usesound
var/toolspeed = 1
var/surgerysound
/* START BUCKLING VARS */
var/list/can_buckle
var/buckle_movable = 0
var/buckle_dir = 0
var/buckle_lying = -1 //bed-like behavior, forces mob.lying = buckle_lying if != -1
var/buckle_require_restraints = 0 //require people to be handcuffed before being able to buckle. eg: pipes
var/atom/movable/buckled = null
/**
* Stores the original layer of a buckled atom.
*
* Set in `/obj/proc/buckle` when the atom's layer is adjusted.
*
* Used in `/unbuckle()` to restore the original layer.
*/
var/buckled_original_layer = null
var/buckle_delay = 0 //How much extra time to buckle someone to this object.
/* END BUCKLING VARS */
/* START ACCESS VARS */
var/list/req_access
var/list/req_one_access
/* END ACCESS VARS */
/* START PERSISTENCE VARS */
// State check if the subsystem is tracking the object, used for easy state checking without iterating the register
var/persistence_track_active = FALSE
// Tracking ID of the object used by the persistence subsystem
var/persistence_track_id = 0
// Author ckey of the object used in persistence subsystem
// Note: Not every type can have an author, like generated dirt for example
// Additionally, the ckey is only an indicator, for example: A player could pin a paper without having written it
// This should be considered for any moderation purpose
var/persistence_author_ckey = null
// Expiration time used when saving/updating a persistent type, this can be changed depending on the use case by assigning a new value
var/persistance_expiration_time_days = PERSISTENT_DEFAULT_EXPIRATION_DAYS
/* END PERSISTENCE VARS */
/obj/Destroy()
if(persistence_track_active) // Prevent hard deletion of references in the persistence register by removing it preemptively
SSpersistence.deregister_track(src)
STOP_PROCESSING(SSprocessing, src)
unbuckle()
QDEL_NULL(talking_atom)
return ..()
/obj/Topic(href, href_list, var/datum/ui_state/state = GLOB.default_state)
if(..())
return 1
// In the far future no checks are made in an overriding Topic() beyond if(..()) return
// Instead any such checks are made in CanUseTopic()
if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE)
CouldUseTopic(usr)
return 0
CouldNotUseTopic(usr)
return 1
/obj/CanUseTopic(var/mob/user, var/datum/ui_state/state)
if(user.CanUseObjTopic(src))
return ..()
to_chat(user, SPAN_DANGER("[icon2html(src, user)]Access denied!"))
return STATUS_CLOSE
/mob/living/silicon/CanUseObjTopic(var/obj/O)
var/id = src.GetIdCard()
return O.check_access(id)
/mob/proc/CanUseObjTopic()
return 1
/obj/proc/CouldUseTopic(var/mob/user)
user.AddTopicPrint(src)
/mob/proc/AddTopicPrint(var/obj/target)
target.add_hiddenprint(src)
/mob/living/AddTopicPrint(var/obj/target)
if(Adjacent(target))
target.add_fingerprint(src)
else
target.add_hiddenprint(src)
/mob/living/silicon/ai/AddTopicPrint(var/obj/target)
target.add_hiddenprint(src)
/obj/proc/CouldNotUseTopic(var/mob/user)
// Nada
/obj/proc/ai_can_interact(var/mob/user)
if(!Adjacent(user) && within_jamming_range(src, FALSE)) // if not adjacent to it, it uses wireless signal
to_chat(user, SPAN_WARNING("Something in the area of \the [src] is blocking the remote signal!"))
return FALSE
return TRUE
/obj/item/proc/is_used_on(obj/O, mob/user)
/obj/assume_air(datum/gas_mixture/giver)
if(loc)
return loc.assume_air(giver)
else
return null
/obj/remove_air(amount)
if(loc)
return loc.remove_air(amount)
else
return null
/obj/return_air()
if(loc)
return loc.return_air()
/obj/proc/updateUsrDialog()
if(in_use)
var/is_in_use = 0
var/list/nearby = viewers(1, src)
for(var/mob/M in nearby)
if ((M.client && M.machine == src))
is_in_use = 1
src.attack_hand(M)
if (istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/living/silicon/robot))
if (!(usr in nearby))
if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh.
is_in_use = 1
src.attack_ai(usr)
in_use = is_in_use
/obj/proc/updateDialog()
// Check that people are actually using the machine. If not, don't update anymore.
if(in_use)
var/list/nearby = viewers(1, src)
var/is_in_use = 0
for(var/mob/M in nearby)
if ((M.client && M.machine == src))
is_in_use = 1
src.interact(M)
var/ai_in_use = AutoUpdateAI(src)
if(!ai_in_use && !is_in_use)
in_use = 0
/obj/attack_ghost(mob/user)
ui_interact(user)
..()
/obj/proc/interact(mob/user)
return
/mob/proc/unset_machine()
src.machine = null
/mob/proc/set_machine(var/obj/O)
if(src.machine)
unset_machine()
src.machine = O
if(istype(O))
O.in_use = 1
/obj/item/proc/updateSelfDialog()
var/mob/M = src.loc
if(istype(M) && M.client && M.machine == src)
src.attack_self(M)
/obj/proc/hide(var/hide)
set_invisibility(hide ? INVISIBILITY_MAXIMUM : initial(invisibility))
level = hide ? 1 : initial(level)
/obj/proc/hides_under_flooring()
return level == 1
/obj/proc/hear_talk(mob/M as mob, text, verb, datum/language/speaking)
if(talking_atom)
talking_atom.catchMessage(text, M)
/obj/proc/see_emote(mob/M as mob, text, var/emote_type)
return
/obj/proc/tesla_act(var/power, var/melt = FALSE)
if(melt)
visible_message(SPAN_DANGER("\The [src] melts down until ashes are left!"))
new /obj/effect/decal/cleanable/ash(loc)
qdel(src)
return
being_shocked = 1
var/power_bounced = power / 2
tesla_zap(src, 3, power_bounced)
addtimer(CALLBACK(src, PROC_REF(reset_shocked)), 10)
/obj/proc/reset_shocked()
being_shocked = 0
/obj/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
return
//To be called from things that spill objects on the floor.
//Makes an object move around randomly for a couple of tiles
/obj/proc/tumble(var/dist)
if (dist >= 1)
spawn()
dist += rand(0,1)
for(var/i = 1, i <= dist, i++)
if(src)
step(src, pick(NORTH,SOUTH,EAST,WEST))
sleep(rand(2,4))
/**
* Sets the `icon_species_tag` on the `/obj` based on the wearer specie, which is
* then used by the icon generator to select the correct overlay of the object
*
* * wearer - A `/mob/living/carbon/human` to adapt the object to the specie of
*
* Returns `TRUE` on successful adaptation, `FALSE` otherwise
*/
/obj/proc/auto_adapt_species(mob/living/carbon/human/wearer)
SHOULD_NOT_SLEEP(TRUE)
if(icon_auto_adapt)
icon_species_tag = ""
if(wearer && length(icon_supported_species_tags))
if(wearer.species.short_name in icon_supported_species_tags)
icon_species_tag = wearer.species.short_name
return TRUE
return FALSE
//This function should be called on an item when it is:
//Built, autolathed, protolathed, crafted or constructed. At runtime, by players or machines
//It should NOT be called on things that:
//spawn at roundstart, are adminspawned, arrive on shuttles, spawned from vendors, removed from fridges and containers, etc
//This is useful for setting special behaviour for built items that shouldn't apply to those spawned at roundstart
/obj/proc/Created()
return
/obj/proc/rotate(var/mob/user, var/anchored_ignore = FALSE)
if(use_check_and_message(user))
return
if(anchored && !anchored_ignore)
to_chat(user, SPAN_WARNING("\The [src] is bolted down to the floor!"))
return
set_dir(turn(dir, 90))
update_icon()
return TRUE
/obj/AltClick(var/mob/user)
if(obj_flags & OBJ_FLAG_ROTATABLE)
rotate(user)
return
if(obj_flags & OBJ_FLAG_ROTATABLE_ANCHORED)
rotate(user, TRUE)
return
..()
/obj/get_examine_text(mob/user, distance, is_adjacent, infix, suffix, get_extended = FALSE)
. = ..()
if((obj_flags & OBJ_FLAG_ROTATABLE) || (obj_flags & OBJ_FLAG_ROTATABLE_ANCHORED))
. += SPAN_SUBTLE("Can be rotated with alt-click.")
if(contaminated)
. += SPAN_ALIEN("\The [src] has been contaminated!")
// whether mobs can unequip and drop items into us or not
/obj/proc/can_hold_dropped_items()
return TRUE
/obj/proc/damage_flags()
. = 0
if(has_edge(src))
. |= DAMAGE_FLAG_EDGE
if(is_sharp(src))
. |= DAMAGE_FLAG_SHARP
if(damtype == DAMAGE_BURN)
. |= DAMAGE_FLAG_LASER
/obj/proc/set_pixel_offsets()
return
//wash an object
/obj/proc/clean()
clean_blood()
color = initial(color)
/obj/proc/output_spoken_message(var/message, var/message_verb = "transmits", var/display_overhead = TRUE, var/overhead_time = 2 SECONDS)
audible_message("\The <b>[src.name]</b> [message_verb], \"[message]\"")
if(display_overhead)
var/list/hearers = get_hearers_in_view(7, src)
var/list/clients_in_hearers = list()
for(var/mob/mob in hearers)
if(mob.client)
clients_in_hearers += mob.client
if(length(clients_in_hearers))
langchat_speech(message, hearers, GLOB.all_languages, skip_language_check = TRUE)
/// Override this to customize the effects an activated signaler has.
/obj/proc/do_signaler()
return
/**
* Called when an access cable - from an IPC or from a roboticist tool - is inserted into an object.
*/
/obj/proc/insert_cable(obj/item/access_cable/cable, mob/user)
user.drop_from_inventory(cable)
cable.forceMove(src)
cable.target = src
/**
* Called when an access cable is removed from something, forcibly or not.
* Override as needed to clear variables up.
*/
/obj/proc/remove_cable(obj/item/access_cable/cable)
SHOULD_CALL_PARENT(TRUE)
cable.target = null
/**
* Called when an access cable - from an IPC or from a roboticist tool - is used on an object to interact with it.
* For example, brings up diagnostics for an access port if it's hooked into one.
*/
/obj/proc/cable_interact(obj/item/access_cable/cable, mob/user)
return
/*#############################################
PERSISTENT
#############################################*/
/**
* Called by the persistence subsystem to retrieve relevant persistent information to be stored in the database.
* Expected to be overriden by derived objects.
* RETURN: Associated list with custom information (e.g.: ["test" = "abc", "counter" = 123])
*/
/obj/proc/persistence_get_content()
return
/**
* Called by the persistence subsystem to apply persistent data on the created object.
* Expected to be overriden by derived objects.
* PARAMS:
* content = Associated list with custom information (e.g.: ["test" = "abc", "counter" = 123]).
* x,y,z = x-y-z coordinates of object, can be null.
*/
/obj/proc/persistence_apply_content(content, x, y, z)
return