From bba09c05cc780684e3b55430e5d18dae9187516e Mon Sep 17 00:00:00 2001 From: Weblure Date: Sun, 29 Sep 2019 18:03:12 -0700 Subject: [PATCH] Ports Xenobio Console Hotkeys from TG ## About The Pull Request This enables xenobiologists to perform all of the (unlocked) slime console functions by simply holding down a modifier key and primary-clicking a slime/monkey/empty floor space. This functionality has been ported from TG and modified to work for Cit. ## List of Changes * Button added to slime console that prints out the hotkey commands to the user. [NEEDS BUTTON ICON] * Shift-click a slime to pick it up, or the floor to drop all held slimes. (Requires Basic Slime Console upgrade) * Ctrl-click a slime to scan it. * Alt-click a slime to feed it a potion. (Requires Advanced Slime Console upgrade) * Ctrl-click on a dead monkey to recycle it, or the floor to place a new monkey. (Requires Monkey Console upgrade) * If the console does not have the required upgrade, an error message will print to the user. * When recycling monkeys, the console will now report how many monkeys it has (will not report decimal increases). * Console now alerts you when you're out of monkeys and reports your current decimal amount. * Console messages are now styled consistently. * You can now pick up a single slime from a pile, instead of all of them at once. ## Why It's Good For The Game Adds QoL features for Xenobiologists and increases department efficiency. Removes the frustration of trying to line up a hyper-sensitive camera reticle on a moving slime with your keyboard, especially for users with laggy clients. ## Notes This PR may contain bugs and poor coding practices. TG allows for consoles to be linked to a physical monkey recycler, sharing its contents across all 3 machines. This PR does NOT port that functionality because I'm lazy and don't know what I'm doing. Special thanks to everyone in #development-discussions-main for helping me with my dumb mistakes and to @Studious Cirno#4894 for suggesting this PR. --- components.dm | 284 ++++++++++++++++++++++++++++ xenobio_camera.dm | 458 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 742 insertions(+) create mode 100644 components.dm create mode 100644 xenobio_camera.dm diff --git a/components.dm b/components.dm new file mode 100644 index 0000000000..8e8c7dc55b --- /dev/null +++ b/components.dm @@ -0,0 +1,284 @@ +#define SEND_SIGNAL(target, sigtype, arguments...) ( !target.comp_lookup || !target.comp_lookup[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) ) + +#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) ) + +//shorthand +#define GET_COMPONENT_FROM(varname, path, target) var##path/##varname = ##target.GetComponent(##path) +#define GET_COMPONENT(varname, path) GET_COMPONENT_FROM(varname, path, src) + +#define COMPONENT_INCOMPATIBLE 1 + +// How multiple components of the exact same type are handled in the same datum + +#define COMPONENT_DUPE_HIGHLANDER 0 //old component is deleted (default) +#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed +#define COMPONENT_DUPE_UNIQUE 2 //new component is deleted +#define COMPONENT_DUPE_UNIQUE_PASSARGS 4 //old component is given the initialization args of the new + +// All signals. Format: +// When the signal is called: (signal arguments) +// All signals send the source datum of the signal as the first argument + +// global signals +// These are signals which can be listened to by any component on any parent +// start global signals with "!", this used to be necessary but now it's just a formatting choice +#define COMSIG_GLOB_NEW_Z "!new_z" //from base of datum/controller/subsystem/mapping/proc/add_new_zlevel(): (list/args) +#define COMSIG_GLOB_VAR_EDIT "!var_edit" //called after a successful var edit somewhere in the world: (list/args) +#define COMSIG_GLOB_LIVING_SAY_SPECIAL "!say_special" //global living say plug - use sparingly: (mob/speaker , message) +////////////////////////////////////////////////////////////////// + +// /datum signals +#define COMSIG_COMPONENT_ADDED "component_added" //when a component is added to a datum: (/datum/component) +#define COMSIG_COMPONENT_REMOVING "component_removing" //before a component is removed from a datum because of RemoveComponent: (/datum/component) +#define COMSIG_PARENT_PREQDELETED "parent_preqdeleted" //before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation +#define COMSIG_PARENT_QDELETED "parent_qdeleted" //after a datum's Destroy() is called: (force, qdel_hint), at this point none of the other components chose to interrupt qdel and Destroy has been called + +// /atom signals +#define COMSIG_PARENT_ATTACKBY "atom_attackby" //from base of atom/attackby(): (/obj/item, /mob/living, params) + #define COMPONENT_NO_AFTERATTACK 1 //Return this in response if you don't want afterattack to be called +#define COMSIG_ATOM_HULK_ATTACK "hulk_attack" //from base of atom/attack_hulk(): (/mob/living/carbon/human) +#define COMSIG_PARENT_EXAMINE "atom_examine" //from base of atom/examine(): (/mob) +#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name" //from base of atom/get_examine_name(): (/mob, list/overrides) + //Positions for overrides list + #define EXAMINE_POSITION_ARTICLE 1 + #define EXAMINE_POSITION_BEFORE 2 + //End positions + #define COMPONENT_EXNAME_CHANGED 1 +#define COMSIG_ATOM_ENTERED "atom_entered" //from base of atom/Entered(): (atom/movable/entering, /atom) +#define COMSIG_ATOM_EXIT "atom_exit" //from base of atom/Exit(): (/atom/movable/exiting, /atom/newloc) + #define COMPONENT_ATOM_BLOCK_EXIT 1 +#define COMSIG_ATOM_EXITED "atom_exited" //from base of atom/Exited(): (atom/movable/exiting, atom/newloc) +#define COMSIG_ATOM_EX_ACT "atom_ex_act" //from base of atom/ex_act(): (severity, target) +#define COMSIG_ATOM_EMP_ACT "atom_emp_act" //from base of atom/emp_act(): (severity) +#define COMSIG_ATOM_FIRE_ACT "atom_fire_act" //from base of atom/fire_act(): (exposed_temperature, exposed_volume) +#define COMSIG_ATOM_BULLET_ACT "atom_bullet_act" //from base of atom/bullet_act(): (/obj/item/projectile, def_zone) +#define COMSIG_ATOM_BLOB_ACT "atom_blob_act" //from base of atom/blob_act(): (/obj/structure/blob) +#define COMSIG_ATOM_ACID_ACT "atom_acid_act" //from base of atom/acid_act(): (acidpwr, acid_volume) +#define COMSIG_ATOM_EMAG_ACT "atom_emag_act" //from base of atom/emag_act(): () +#define COMSIG_ATOM_RAD_ACT "atom_rad_act" //from base of atom/rad_act(intensity) +#define COMSIG_ATOM_NARSIE_ACT "atom_narsie_act" //from base of atom/narsie_act(): () +#define COMSIG_ATOM_RATVAR_ACT "atom_ratvar_act" //from base of atom/ratvar_act(): () +#define COMSIG_ATOM_RCD_ACT "atom_rcd_act" //from base of atom/rcd_act(): (/mob, /obj/item/construction/rcd, passed_mode) +#define COMSIG_ATOM_SING_PULL "atom_sing_pull" //from base of atom/singularity_pull(): (S, current_size) +#define COMSIG_ATOM_SET_LIGHT "atom_set_light" //from base of atom/set_light(): (l_range, l_power, l_color) +#define COMSIG_ATOM_DIR_CHANGE "atom_dir_change" //from base of atom/setDir(): (old_dir, new_dir) +#define COMSIG_ATOM_CONTENTS_DEL "atom_contents_del" //from base of atom/handle_atom_del(): (atom/deleted) +#define COMSIG_ATOM_HAS_GRAVITY "atom_has_gravity" //from base of atom/has_gravity(): (turf/location, list/forced_gravities) +#define COMSIG_ATOM_RAD_PROBE "atom_rad_probe" //from proc/get_rad_contents(): () + #define COMPONENT_BLOCK_RADIATION 1 +#define COMSIG_ATOM_RAD_CONTAMINATING "atom_rad_contam" //from base of datum/radiation_wave/radiate(): (strength) + #define COMPONENT_BLOCK_CONTAMINATION 1 +#define COMSIG_ATOM_RAD_WAVE_PASSING "atom_rad_wave_pass" //from base of datum/radiation_wave/check_obstructions(): (datum/radiation_wave, width) + #define COMPONENT_RAD_WAVE_HANDLED 1 +#define COMSIG_ATOM_CANREACH "atom_can_reach" //from internal loop in atom/movable/proc/CanReach(): (list/next) + #define COMPONENT_BLOCK_REACH 1 +#define COMSIG_ATOM_SCREWDRIVER_ACT "atom_screwdriver_act" //from base of atom/screwdriver_act(): (mob/living/user, obj/item/I) +#define COMSIG_ATOM_INTERCEPT_TELEPORT "intercept_teleport" //called when teleporting into a protected turf: (channel, turf/origin) + #define COMPONENT_BLOCK_TELEPORT 1 +///////////////// +#define COMSIG_ATOM_ATTACK_GHOST "atom_attack_ghost" //from base of atom/attack_ghost(): (mob/dead/observer/ghost) +#define COMSIG_ATOM_ATTACK_HAND "atom_attack_hand" //from base of atom/attack_hand(): (mob/user) +#define COMSIG_ATOM_ATTACK_PAW "atom_attack_paw" //from base of atom/attack_paw(): (mob/user) + #define COMPONENT_NO_ATTACK_HAND 1 //works on all 3. +///////////////// + +#define COMSIG_ENTER_AREA "enter_area" //from base of area/Entered(): (/area) +#define COMSIG_EXIT_AREA "exit_area" //from base of area/Exited(): (/area) + +#define COMSIG_CLICK "atom_click" //from base of atom/Click(): (location, control, params, mob/user) +#define COMSIG_CLICK_SHIFT "shift_click" //from base of atom/ShiftClick(): (/mob) +#define COMSIG_CLICK_CTRL "ctrl_click" //from base of atom/CtrlClickOn(): (/mob) +#define COMSIG_CLICK_ALT "alt_click" //from base of atom/AltClick(): (/mob) +#define COMSIG_CLICK_CTRL_SHIFT "ctrl_shift_click" //from base of atom/CtrlShiftClick(/mob) +#define COMSIG_MOUSEDROP_ONTO "mousedrop_onto" //from base of atom/MouseDrop(): (/atom/over, /mob/user) + #define COMPONENT_NO_MOUSEDROP 1 +#define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto" //from base of atom/MouseDrop_T: (/atom/from, /mob/user) + +// /area signals +#define COMSIG_AREA_ENTERED "area_entered" //from base of area/Entered(): (atom/movable/M) +#define COMSIG_AREA_EXITED "area_exited" //from base of area/Exited(): (atom/movable/M) + +// /turf signals +#define COMSIG_TURF_CHANGE "turf_change" //from base of turf/ChangeTurf(): (path, list/new_baseturfs, flags, list/transferring_comps) +#define COMSIG_TURF_HAS_GRAVITY "turf_has_gravity" //from base of atom/has_gravity(): (atom/asker, list/forced_gravities) + +// /atom/movable signals +#define COMSIG_MOVABLE_MOVED "movable_moved" //from base of atom/movable/Moved(): (/atom, dir) +#define COMSIG_MOVABLE_CROSS "movable_cross" //from base of atom/movable/Cross(): (/atom/movable) +#define COMSIG_MOVABLE_CROSSED "movable_crossed" //from base of atom/movable/Crossed(): (/atom/movable) +#define COMSIG_MOVABLE_UNCROSS "movable_uncross" //from base of atom/movable/Uncross(): (/atom/movable) + #define COMPONENT_MOVABLE_BLOCK_UNCROSS 1 +#define COMSIG_MOVABLE_UNCROSSED "movable_uncrossed" //from base of atom/movable/Uncrossed(): (/atom/movable) +#define COMSIG_MOVABLE_BUMP "movable_bump" //from base of atom/movable/Bump(): (/atom) +#define COMSIG_MOVABLE_IMPACT "movable_impact" //from base of atom/movable/throw_impact(): (/atom/hit_atom, /datum/thrownthing/throwingdatum) +#define COMSIG_MOVABLE_IMPACT_ZONE "item_impact_zone" //from base of mob/living/hitby(): (mob/living/target, hit_zone) +#define COMSIG_MOVABLE_BUCKLE "buckle" //from base of atom/movable/buckle_mob(): (mob, force) +#define COMSIG_MOVABLE_UNBUCKLE "unbuckle" //from base of atom/movable/unbuckle_mob(): (mob, force) +#define COMSIG_MOVABLE_PRE_THROW "movable_pre_throw" //from base of atom/movable/throw_at(): (list/args) + #define COMPONENT_CANCEL_THROW 1 +#define COMSIG_MOVABLE_POST_THROW "movable_post_throw" //from base of atom/movable/throw_at(): (datum/thrownthing, spin) +#define COMSIG_MOVABLE_Z_CHANGED "movable_ztransit" //from base of atom/movable/onTransitZ(): (old_z, new_z) +#define COMSIG_MOVABLE_HEAR "movable_hear" //from base of atom/movable/Hear(): (message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode) +#define COMSIG_MOVABLE_DISPOSING "movable_disposing" //called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source) + +// /mob signals +#define COMSIG_MOB_DEATH "mob_death" //from base of mob/death(): (gibbed) +#define COMSIG_MOB_ALLOWED "mob_allowed" //from base of obj/allowed(mob/M): (/obj) returns bool, if TRUE the mob has id access to the obj +#define COMSIG_MOB_RECEIVE_MAGIC "mob_receive_magic" //from base of mob/anti_magic_check(): (magic, holy, protection_sources) + #define COMPONENT_BLOCK_MAGIC 1 +#define COMSIG_MOB_HUD_CREATED "mob_hud_created" //from base of mob/create_mob_hud(): () +#define COMSIG_MOB_ATTACK_HAND "mob_attack_hand" //from base of +#define COMSIG_MOB_ITEM_ATTACK "mob_item_attack" //from base of /obj/item/attack(): (mob/M, mob/user) +#define COMSIG_MOB_ITEM_AFTERATTACK "mob_item_afterattack" //from base of obj/item/afterattack(): (atom/target, mob/user, proximity_flag, click_parameters) +#define COMSIG_MOB_ATTACK_RANGED "mob_attack_ranged" //from base of mob/RangedAttack(): (atom/A, params) +#define COMSIG_MOB_THROW "mob_throw" //from base of /mob/throw_item(): (atom/target) +#define COMSIG_MOB_UPDATE_SIGHT "mob_update_sight" //from base of /mob/update_sight(): () +#define COMSIG_MOB_SAY "mob_say" // from /mob/living/say(): (proc args list) + #define COMPONENT_UPPERCASE_SPEECH 1 + // used to access COMSIG_MOB_SAY argslist + #define SPEECH_MESSAGE 1 + // #define SPEECH_BUBBLE_TYPE 2 + #define SPEECH_SPANS 3 + /* #define SPEECH_SANITIZE 4 + #define SPEECH_LANGUAGE 5 + #define SPEECH_IGNORE_SPAM 6 + #define SPEECH_FORCED 7 */ + +// /mob/living signals +#define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living) +#define COMSIG_LIVING_IGNITED "living_ignite" //from base of mob/living/IgniteMob() (/mob/living) +#define COMSIG_LIVING_EXTINGUISHED "living_extinguished" //from base of mob/living/ExtinguishMob() (/mob/living) +#define COMSIG_LIVING_ELECTROCUTE_ACT "living_electrocute_act" //from base of mob/living/electrocute_act(): (shock_damage) +#define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock" //sent by stuff like stunbatons and tasers: () + +// /mob/living/carbon signals +#define COMSIG_CARBON_SOUNDBANG "carbon_soundbang" //from base of mob/living/carbon/soundbang_act(): (list(intensity)) + +// /obj signals +#define COMSIG_OBJ_DECONSTRUCT "obj_deconstruct" //from base of obj/deconstruct(): (disassembled) +#define COMSIG_OBJ_BREAK "obj_break" //from base of /obj/obj_break(): (damage_flag) +#define COMSIG_OBJ_SETANCHORED "obj_setanchored" //called in /obj/structure/setAnchored(): (value) + + +// /obj/item signals +#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user) +#define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob) + #define COMPONENT_NO_INTERACT 1 +#define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj" //from base of obj/item/attack_obj(): (/obj, /mob) + #define COMPONENT_NO_ATTACK_OBJ 1 +#define COMSIG_ITEM_PRE_ATTACK "item_pre_attack" //from base of obj/item/pre_attack(): (atom/target, mob/user, params) + #define COMPONENT_NO_ATTACK 1 +#define COMSIG_ITEM_AFTERATTACK "item_afterattack" //from base of obj/item/afterattack(): (atom/target, mob/user, params) +#define COMSIG_ITEM_EQUIPPED "item_equip" //from base of obj/item/equipped(): (/mob/equipper, slot) +#define COMSIG_ITEM_DROPPED "item_drop" //from base of obj/item/dropped(): (mob/user) +#define COMSIG_ITEM_PICKUP "item_pickup" //from base of obj/item/pickup(): (/mob/taker) +#define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone" //from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone) +#define COMSIG_ITEM_IMBUE_SOUL "item_imbue_soul" //return a truthy value to prevent ensouling, checked in /obj/effect/proc_holder/spell/targeted/lichdom/cast(): (mob/user) +#define COMSIG_ITEM_HIT_REACT "item_hit_react" //from base of obj/item/hit_reaction(): (list/args) + +// /obj/item/clothing signals +#define COMSIG_SHOES_STEP_ACTION "shoes_step_action" //from base of obj/item/clothing/shoes/proc/step_action(): () + +// /obj/item/implant signals +#define COMSIG_IMPLANT_ACTIVATED "implant_activated" //from base of /obj/item/implant/proc/activate(): () +#define COMSIG_IMPLANT_IMPLANTING "implant_implanting" //from base of /obj/item/implant/proc/implant(): (list/args) + #define COMPONENT_STOP_IMPLANTING 1 +#define COMSIG_IMPLANT_OTHER "implant_other" //called on already installed implants when a new one is being added in /obj/item/implant/proc/implant(): (list/args, obj/item/implant/new_implant) + //#define COMPONENT_STOP_IMPLANTING 1 //The name makes sense for both + #define COMPONENT_DELETE_NEW_IMPLANT 2 + #define COMPONENT_DELETE_OLD_IMPLANT 4 +#define COMSIG_IMPLANT_EXISTING_UPLINK "implant_uplink_exists" //called on implants being implanted into someone with an uplink implant: (datum/component/uplink) + //This uses all return values of COMSIG_IMPLANT_OTHER + +// /obj/item/pda signals +#define COMSIG_PDA_CHANGE_RINGTONE "pda_change_ringtone" //called on pda when the user changes the ringtone: (mob/living/user, new_ringtone) + #define COMPONENT_STOP_RINGTONE_CHANGE 1 + +// /obj/item/radio signals +#define COMSIG_RADIO_NEW_FREQUENCY "radio_new_frequency" //called from base of /obj/item/radio/proc/set_frequency(): (list/args) + +// /obj/item/pen signals +#define COMSIG_PEN_ROTATED "pen_rotated" //called after rotation in /obj/item/pen/attack_self(): (rotation, mob/living/carbon/user) + + +// /mob/living/carbon/human signals +#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target) +#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker) +#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" //Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted) + +// /datum/species signals +#define COMSIG_SPECIES_GAIN "species_gain" //from datum/species/on_species_gain(): (datum/species/new_species, datum/species/old_species) +#define COMSIG_SPECIES_LOSS "species_loss" //from datum/species/on_species_loss(): (datum/species/lost_species) + +/*******Component Specific Signals*******/ +//Janitor +#define COMSIG_TURF_IS_WET "check_turf_wet" //(): Returns bitflags of wet values. +#define COMSIG_TURF_MAKE_DRY "make_turf_try" //(max_strength, immediate, duration_decrease = INFINITY): Returns bool. +#define COMSIG_COMPONENT_CLEAN_ACT "clean_act" //called on an object to clean it of cleanables. Usualy with soap: (num/strength) + +//Food +#define COMSIG_FOOD_EATEN "food_eaten" //from base of obj/item/reagent_containers/food/snacks/attack(): (mob/living/eater, mob/feeder) + +//Mood +#define COMSIG_ADD_MOOD_EVENT "add_mood" //Called when you send a mood event from anywhere in the code. +#define COMSIG_CLEAR_MOOD_EVENT "clear_mood" //Called when you clear a mood event from anywhere in the code. + +//NTnet +#define COMSIG_COMPONENT_NTNET_RECEIVE "ntnet_receive" //called on an object by its NTNET connection component on receive. (sending_id(number), sending_netname(text), data(datum/netdata)) + +//Nanites +#define COMSIG_HAS_NANITES "has_nanites" //() returns TRUE if nanites are found +#define COMSIG_NANITE_GET_PROGRAMS "nanite_get_programs" //(list/nanite_programs) - makes the input list a copy the nanites' program list +#define COMSIG_NANITE_SET_VOLUME "nanite_set_volume" //(amount) Sets current nanite volume to the given amount +#define COMSIG_NANITE_ADJUST_VOLUME "nanite_adjust" //(amount) Adjusts nanite volume by the given amount +#define COMSIG_NANITE_SET_MAX_VOLUME "nanite_set_max_volume" //(amount) Sets maximum nanite volume to the given amount +#define COMSIG_NANITE_SET_CLOUD "nanite_set_cloud" //(amount(0-100)) Sets cloud ID to the given amount +#define COMSIG_NANITE_SET_SAFETY "nanite_set_safety" //(amount) Sets safety threshold to the given amount +#define COMSIG_NANITE_SET_REGEN "nanite_set_regen" //(amount) Sets regeneration rate to the given amount +#define COMSIG_NANITE_SIGNAL "nanite_signal" //(code(1-9999)) Called when sending a nanite signal to a mob. +#define COMSIG_NANITE_SCAN "nanite_scan" //(mob/user, full_scan) - sends to chat a scan of the nanites to the user, returns TRUE if nanites are detected +#define COMSIG_NANITE_UI_DATA "nanite_ui_data" //(list/data, scan_level) - adds nanite data to the given data list - made for ui_data procs +#define COMSIG_NANITE_ADD_PROGRAM "nanite_add_program" //(datum/nanite_program/new_program, datum/nanite_program/source_program) Called when adding a program to a nanite component + #define COMPONENT_PROGRAM_INSTALLED 1 //Installation successful + #define COMPONENT_PROGRAM_NOT_INSTALLED 2 //Installation failed, but there are still nanites +#define COMSIG_NANITE_SYNC "nanite_sync" //(datum/component/nanites, full_overwrite, copy_activation) Called to sync the target's nanites to a given nanite component + +// /datum/component/storage signals +#define COMSIG_CONTAINS_STORAGE "is_storage" //() - returns bool. +#define COMSIG_TRY_STORAGE_INSERT "storage_try_insert" //(obj/item/inserting, mob/user, silent, force) - returns bool +#define COMSIG_TRY_STORAGE_SHOW "storage_show_to" //(mob/show_to, force) - returns bool. +#define COMSIG_TRY_STORAGE_HIDE_FROM "storage_hide_from" //(mob/hide_from) - returns bool +#define COMSIG_TRY_STORAGE_HIDE_ALL "storage_hide_all" //returns bool +#define COMSIG_TRY_STORAGE_SET_LOCKSTATE "storage_lock_set_state" //(newstate) +#define COMSIG_IS_STORAGE_LOCKED "storage_get_lockstate" //() - returns bool. MUST CHECK IF STORAGE IS THERE FIRST! +#define COMSIG_TRY_STORAGE_TAKE_TYPE "storage_take_type" //(type, atom/destination, amount = INFINITY, check_adjacent, force, mob/user, list/inserted) - returns bool - type can be a list of types. +#define COMSIG_TRY_STORAGE_FILL_TYPE "storage_fill_type" //(type, amount = INFINITY, force = FALSE) //don't fuck this up. Force will ignore max_items, and amount is normally clamped to max_items. +#define COMSIG_TRY_STORAGE_TAKE "storage_take_obj" //(obj, new_loc, force = FALSE) - returns bool +#define COMSIG_TRY_STORAGE_QUICK_EMPTY "storage_quick_empty" //(loc) - returns bool - if loc is null it will dump at parent location. +#define COMSIG_TRY_STORAGE_RETURN_INVENTORY "storage_return_inventory" //(list/list_to_inject_results_into, recursively_search_inside_storages = TRUE) +#define COMSIG_TRY_STORAGE_CAN_INSERT "storage_can_equip" //(obj/item/insertion_candidate, mob/user, silent) - returns bool + +// /datum/action signals +#define COMSIG_ACTION_TRIGGER "action_trigger" //from base of datum/action/proc/Trigger(): (datum/action) + #define COMPONENT_ACTION_BLOCK_TRIGGER 1 + +/*******Non-Signal Component Related Defines*******/ + +//Redirection component init flags +#define REDIRECT_TRANSFER_WITH_TURF 1 + +//Arch +#define ARCH_PROB "probability" //Probability for each item +#define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount + +//Ouch my toes! +#define CALTROP_BYPASS_SHOES 1 +#define CALTROP_IGNORE_WALKERS 2 + +//Xenobio hotkeys +#define COMSIG_XENO_SLIME_CLICK_CTRL "xeno_slime_click_ctrl" //from slime CtrlClickOn(): (/mob) +#define COMSIG_XENO_SLIME_CLICK_ALT "xeno_slime_click_alt" //from slime AltClickOn(): (/mob) +#define COMSIG_XENO_SLIME_CLICK_SHIFT "xeno_slime_click_shift" //from slime ShiftClickOn(): (/mob) +#define COMSIG_XENO_TURF_CLICK_SHIFT "xeno_turf_click_shift" //from turf ShiftClickOn(): (/mob) +#define COMSIG_XENO_TURF_CLICK_CTRL "xeno_turf_click_alt" //from turf AltClickOn(): (/mob) +#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" //from monkey CtrlClickOn(): (/mob) diff --git a/xenobio_camera.dm b/xenobio_camera.dm new file mode 100644 index 0000000000..07b25c6de8 --- /dev/null +++ b/xenobio_camera.dm @@ -0,0 +1,458 @@ +//Xenobio control console +/mob/camera/aiEye/remote/xenobio + visible_icon = 1 + icon = 'icons/obj/abductor.dmi' + icon_state = "camera_target" + var/allowed_area = null + +/mob/camera/aiEye/remote/xenobio/Initialize() + var/area/A = get_area(loc) + allowed_area = A.name + . = ..() + +/mob/camera/aiEye/remote/xenobio/setLoc(var/t) + var/area/new_area = get_area(t) + if(new_area && new_area.name == allowed_area || new_area && new_area.xenobiology_compatible) + return ..() + else + return + +/obj/machinery/computer/camera_advanced/xenobio + name = "Slime management console" + desc = "A computer used for remotely handling slimes." + networks = list("ss13") + circuit = /obj/item/circuitboard/computer/xenobiology + var/datum/action/innate/slime_place/slime_place_action + var/datum/action/innate/slime_pick_up/slime_up_action + var/datum/action/innate/feed_slime/feed_slime_action + var/datum/action/innate/monkey_recycle/monkey_recycle_action + var/datum/action/innate/slime_scan/scan_action + var/datum/action/innate/feed_potion/potion_action + var/datum/action/innate/hotkey_help/hotkey_help + + var/datum/component/redirect/listener + + var/list/stored_slimes + var/obj/item/slimepotion/slime/current_potion + var/max_slimes = 5 + var/monkeys = 0 + + icon_screen = "slime_comp" + icon_keyboard = "rd_key" + + light_color = LIGHT_COLOR_PINK + +/obj/machinery/computer/camera_advanced/xenobio/Initialize() + . = ..() + slime_place_action = new + slime_up_action = new + feed_slime_action = new + monkey_recycle_action = new + scan_action = new + potion_action = new + hotkey_help = new + stored_slimes = list() + listener = AddComponent(/datum/component/redirect, list(COMSIG_ATOM_CONTENTS_DEL = CALLBACK(src, .proc/on_contents_del))) + +/obj/machinery/computer/camera_advanced/xenobio/Destroy() + stored_slimes = null + QDEL_NULL(current_potion) + for(var/i in contents) + var/mob/living/simple_animal/slime/S = i + if(istype(S)) + S.forceMove(drop_location()) + return ..() + +/obj/machinery/computer/camera_advanced/xenobio/CreateEye() + eyeobj = new /mob/camera/aiEye/remote/xenobio(get_turf(src)) + eyeobj.origin = src + eyeobj.visible_icon = 1 + eyeobj.icon = 'icons/obj/abductor.dmi' + eyeobj.icon_state = "camera_target" + +/obj/machinery/computer/camera_advanced/xenobio/GrantActions(mob/living/user) + ..() + + if(slime_up_action && (upgradetier & XENOBIO_UPGRADE_SLIMEBASIC)) //CIT CHANGE - makes slime-related actions require XENOBIO_UPGRADE_SLIMEBASIC + slime_up_action.target = src + slime_up_action.Grant(user) + actions += slime_up_action + + if(slime_place_action && (upgradetier & XENOBIO_UPGRADE_SLIMEBASIC)) //CIT CHANGE - makes slime-related actions require XENOBIO_UPGRADE_SLIMEBASIC + slime_place_action.target = src + slime_place_action.Grant(user) + actions += slime_place_action + + if(feed_slime_action && (upgradetier & XENOBIO_UPGRADE_MONKEYS)) //CIT CHANGE - makes monkey-related actions require XENOBIO_UPGRADE_MONKEYS + feed_slime_action.target = src + feed_slime_action.Grant(user) + actions += feed_slime_action + + if(monkey_recycle_action && (upgradetier & XENOBIO_UPGRADE_MONKEYS)) //CIT CHANGE - makes monkey-related actions require XENOBIO_UPGRADE_MONKEYS + monkey_recycle_action.target = src + monkey_recycle_action.Grant(user) + actions += monkey_recycle_action + + if(scan_action) + scan_action.target = src + scan_action.Grant(user) + actions += scan_action + + if(potion_action && (upgradetier & XENOBIO_UPGRADE_SLIMEADV)) // CIT CHANGE - makes giving slimes potions via console require XENOBIO_UPGRADE_SLIMEADV + potion_action.target = src + potion_action.Grant(user) + actions += potion_action + + if(hotkey_help) + hotkey_help.target = src + hotkey_help.Grant(user) + actions += hotkey_help + + RegisterSignal(user, COMSIG_XENO_SLIME_CLICK_CTRL, .proc/XenoSlimeClickCtrl) + RegisterSignal(user, COMSIG_XENO_SLIME_CLICK_ALT, .proc/XenoSlimeClickAlt) + RegisterSignal(user, COMSIG_XENO_SLIME_CLICK_SHIFT, .proc/XenoSlimeClickShift) + RegisterSignal(user, COMSIG_XENO_TURF_CLICK_SHIFT, .proc/XenoTurfClickShift) + RegisterSignal(user, COMSIG_XENO_TURF_CLICK_CTRL, .proc/XenoTurfClickCtrl) + RegisterSignal(user, COMSIG_XENO_MONKEY_CLICK_CTRL, .proc/XenoMonkeyClickCtrl) + +/obj/machinery/computer/camera_advanced/xenobio/remove_eye_control(mob/living/user) + UnregisterSignal(user, COMSIG_XENO_SLIME_CLICK_CTRL) + UnregisterSignal(user, COMSIG_XENO_SLIME_CLICK_ALT) + UnregisterSignal(user, COMSIG_XENO_SLIME_CLICK_SHIFT) + UnregisterSignal(user, COMSIG_XENO_TURF_CLICK_SHIFT) + UnregisterSignal(user, COMSIG_XENO_TURF_CLICK_CTRL) + UnregisterSignal(user, COMSIG_XENO_MONKEY_CLICK_CTRL) + ..() + +/obj/machinery/computer/camera_advanced/xenobio/proc/on_contents_del(atom/deleted) + if(current_potion == deleted) + current_potion = null + if(deleted in stored_slimes) + stored_slimes -= deleted + +/obj/machinery/computer/camera_advanced/xenobio/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/reagent_containers/food/snacks/monkeycube) && (upgradetier & XENOBIO_UPGRADE_MONKEYS)) //CIT CHANGE - makes monkey-related actions require XENOBIO_UPGRADE_MONKEYS + monkeys++ + to_chat(user, "You feed [O] to [src]. It now has [monkeys] monkey cubes stored.") + qdel(O) + return + else if(istype(O, /obj/item/storage/bag) && (upgradetier & XENOBIO_UPGRADE_MONKEYS)) //CIT CHANGE - makes monkey-related actions require XENOBIO_UPGRADE_MONKEYS + var/obj/item/storage/P = O + var/loaded = FALSE + for(var/obj/G in P.contents) + if(istype(G, /obj/item/reagent_containers/food/snacks/monkeycube)) + loaded = TRUE + monkeys++ + qdel(G) + if(loaded) + to_chat(user, "You fill [src] with the monkey cubes stored in [O]. [src] now has [monkeys] monkey cubes stored.") + return + else if(istype(O, /obj/item/slimepotion/slime) && (upgradetier & XENOBIO_UPGRADE_SLIMEADV)) // CIT CHANGE - makes giving slimes potions via console require XENOBIO_UPGRADE_SLIMEADV + var/replaced = FALSE + if(user && !user.transferItemToLoc(O, src)) + return + if(!QDELETED(current_potion)) + current_potion.forceMove(drop_location()) + replaced = TRUE + current_potion = O + to_chat(user, "You load [O] in the console's potion slot[replaced ? ", replacing the one that was there before" : ""].") + return + ..() + +/datum/action/innate/slime_place + name = "Place Slimes" + icon_icon = 'icons/mob/actions/actions_silicon.dmi' + button_icon_state = "slime_down" + +/datum/action/innate/slime_place/Activate() + if(!target || !isliving(owner)) + return + var/mob/living/C = owner + var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = target + + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) + for(var/mob/living/simple_animal/slime/S in X.stored_slimes) + S.forceMove(remote_eye.loc) + S.visible_message("[S] warps in!") + X.stored_slimes -= S + else + to_chat(owner, "Target is not near a camera. Cannot proceed.") + +/datum/action/innate/slime_pick_up + name = "Pick up Slime" + icon_icon = 'icons/mob/actions/actions_silicon.dmi' + button_icon_state = "slime_up" + +/datum/action/innate/slime_pick_up/Activate() + if(!target || !isliving(owner)) + return + var/mob/living/C = owner + var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = target + + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) + for(var/mob/living/simple_animal/slime/S in remote_eye.loc) + if(X.stored_slimes.len >= X.max_slimes) + break + if(!S.ckey) + if(S.buckled) + S.Feedstop(silent = TRUE) + S.visible_message("[S] vanishes in a flash of light!") + S.forceMove(X) + X.stored_slimes += S + else + to_chat(owner, "Target is not near a camera. Cannot proceed.") + + +/datum/action/innate/feed_slime + name = "Feed Slimes" + icon_icon = 'icons/mob/actions/actions_silicon.dmi' + button_icon_state = "monkey_down" + +/datum/action/innate/feed_slime/Activate() + if(!target || !isliving(owner)) + return + var/mob/living/C = owner + var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = target + + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) + if(X.monkeys >= 1) + var/mob/living/carbon/monkey/food = new /mob/living/carbon/monkey(remote_eye.loc, TRUE, owner) + if (!QDELETED(food)) + food.LAssailant = C + X.monkeys -- + to_chat(owner, "[X] now has [X.monkeys] monkey(s) left.") + else + to_chat(owner, "[X] needs to have at least 1 monkey stored. Currently has [X.monkeys] monkeys stored.") + else + to_chat(owner, "Target is not near a camera. Cannot proceed.") + + +/datum/action/innate/monkey_recycle + name = "Recycle Monkeys" + icon_icon = 'icons/mob/actions/actions_silicon.dmi' + button_icon_state = "monkey_up" + +/datum/action/innate/monkey_recycle/Activate() + if(!target || !isliving(owner)) + return + var/mob/living/C = owner + var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = target + + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) + for(var/mob/living/carbon/monkey/M in remote_eye.loc) + if(M.stat) + M.visible_message("[M] vanishes as [p_theyre()] reclaimed for recycling!") + X.monkeys = round(X.monkeys + 0.2,0.1) + qdel(M) + if (X.monkeys == (round(X.monkeys,1))) + to_chat(C, "[X] now has [X.monkeys] monkey(s) available.") + else + to_chat(owner, "Target is not near a camera. Cannot proceed.") + +/datum/action/innate/slime_scan + name = "Scan Slime" + icon_icon = 'icons/mob/actions/actions_silicon.dmi' + button_icon_state = "slime_scan" + +/datum/action/innate/slime_scan/Activate() + if(!target || !isliving(owner)) + return + var/mob/living/C = owner + var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control + + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) + for(var/mob/living/simple_animal/slime/S in remote_eye.loc) + slime_scan(S, C) + else + to_chat(owner, "Target is not near a camera. Cannot proceed.") + +/datum/action/innate/feed_potion + name = "Apply Potion" + icon_icon = 'icons/mob/actions/actions_silicon.dmi' + button_icon_state = "slime_potion" + +/datum/action/innate/feed_potion/Activate() + if(!target || !isliving(owner)) + return + + var/mob/living/C = owner + var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = target + + if(QDELETED(X.current_potion)) + to_chat(owner, "No potion loaded.") + return + + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) + for(var/mob/living/simple_animal/slime/S in remote_eye.loc) + X.current_potion.attack(S, C) + break + else + to_chat(owner, "Target is not near a camera. Cannot proceed.") + +/datum/action/innate/hotkey_help + name = "Hotkey Help" + icon_icon = 'icons/mob/actions/actions_silicon.dmi' + button_icon_state = "hotkey_help" + +/datum/action/innate/hotkey_help/Activate() + if(!target || !isliving(owner)) + return + to_chat(owner, "Click shortcuts:") + to_chat(owner, "Shift-click a slime to pick it up, or the floor to drop all held slimes. (Requires Basic Slime Console upgrade)") + to_chat(owner, "Ctrl-click a slime to scan it.") + to_chat(owner, "Alt-click a slime to feed it a potion. (Requires Advanced Slime Console upgrade)") + to_chat(owner, "Ctrl-click on a dead monkey to recycle it, or the floor to place a new monkey. (Requires Monkey Console upgrade)") + +// +// Alternate clicks for slime, monkey and open turf if using a xenobio console + +// Scans slime +/mob/living/simple_animal/slime/CtrlClick(mob/user) + SEND_SIGNAL(user, COMSIG_XENO_SLIME_CLICK_CTRL, src) + ..() + +//Feeds a potion to slime +/mob/living/simple_animal/slime/AltClick(mob/user) + SEND_SIGNAL(user, COMSIG_XENO_SLIME_CLICK_ALT, src) + ..() + +//Picks up slime +/mob/living/simple_animal/slime/ShiftClick(mob/user) + SEND_SIGNAL(user, COMSIG_XENO_SLIME_CLICK_SHIFT, src) + ..() + +//Place slimes +/turf/open/ShiftClick(mob/user) + SEND_SIGNAL(user, COMSIG_XENO_TURF_CLICK_SHIFT, src) + ..() + +//Place monkey +/turf/open/CtrlClick(mob/user) + SEND_SIGNAL(user, COMSIG_XENO_TURF_CLICK_CTRL, src) + ..() + +//Pick up monkey +/mob/living/carbon/monkey/CtrlClick(mob/user) + SEND_SIGNAL(user, COMSIG_XENO_MONKEY_CLICK_CTRL, src) + ..() + +// Scans slime +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickCtrl(mob/living/user, mob/living/simple_animal/slime/S) + if(!GLOB.cameranet.checkTurfVis(S.loc)) + to_chat(user, "Target is not near a camera. Cannot proceed.") + return + var/mob/living/C = user + var/mob/camera/aiEye/remote/xenobio/E = C.remote_control + var/area/mobarea = get_area(S.loc) + if(mobarea.name == E.allowed_area || mobarea.xenobiology_compatible) + slime_scan(S, C) + +//Feeds a potion to slime +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickAlt(mob/living/user, mob/living/simple_animal/slime/S) + if(!(upgradetier & XENOBIO_UPGRADE_SLIMEADV)) //CIT CHANGE - makes slime-related actions require XENOBIO_UPGRADE_SLIMEADV + to_chat(user, "This console does not have the advanced slime upgrade.") + return + if(!GLOB.cameranet.checkTurfVis(S.loc)) + to_chat(user, "Target is not near a camera. Cannot proceed.") + return + var/mob/living/C = user + var/mob/camera/aiEye/remote/xenobio/E = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin + var/area/mobarea = get_area(S.loc) + if(QDELETED(X.current_potion)) + to_chat(C, "No potion loaded.") + return + if(mobarea.name == E.allowed_area || mobarea.xenobiology_compatible) + X.current_potion.attack(S, C) + +//Picks up slime +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickShift(mob/living/user, mob/living/simple_animal/slime/S) + if(!(upgradetier & XENOBIO_UPGRADE_SLIMEBASIC)) //CIT CHANGE - makes slime-related actions require XENOBIO_UPGRADE_SLIMEBASIC + to_chat(user, "This console does not have the basic slime upgrade.") + return + if(!GLOB.cameranet.checkTurfVis(S.loc)) + to_chat(user, "Target is not near a camera. Cannot proceed.") + return + var/mob/living/C = user + var/mob/camera/aiEye/remote/xenobio/E = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin + var/area/mobarea = get_area(S.loc) + if(mobarea.name == E.allowed_area || mobarea.xenobiology_compatible) + if(X.stored_slimes.len >= X.max_slimes) + to_chat(C, "Slime storage is full.") + return + if(S.ckey) + to_chat(C, "The slime wiggled free!") + return + if(S.buckled) + S.Feedstop(silent = TRUE) + S.visible_message("[S] vanishes in a flash of light!") + S.forceMove(X) + X.stored_slimes += S + +//Place slimes +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoTurfClickShift(mob/living/user, turf/open/T) + if(!(upgradetier & XENOBIO_UPGRADE_SLIMEBASIC)) //CIT CHANGE - makes slime-related actions require XENOBIO_UPGRADE_SLIMEBASIC + to_chat(user, "This console does not have the basic slime upgrade.") + return + if(!GLOB.cameranet.checkTurfVis(T)) + to_chat(user, "Target is not near a camera. Cannot proceed.") + return + var/mob/living/C = user + var/mob/camera/aiEye/remote/xenobio/E = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin + var/area/turfarea = get_area(T) + if(turfarea.name == E.allowed_area || turfarea.xenobiology_compatible) + for(var/mob/living/simple_animal/slime/S in X.stored_slimes) + S.forceMove(T) + S.visible_message("[S] warps in!") + X.stored_slimes -= S + +//Place monkey +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoTurfClickCtrl(mob/living/user, turf/open/T) + if(!(upgradetier & XENOBIO_UPGRADE_MONKEYS)) // CIT CHANGE - makes monkey-related actions require XENOBIO_UPGRADE_MONKEYS + to_chat(user, "This console does not have the monkey upgrade.") + return + if(!GLOB.cameranet.checkTurfVis(T)) + to_chat(user, "Target is not near a camera. Cannot proceed.") + return + var/mob/living/C = user + var/mob/camera/aiEye/remote/xenobio/E = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin + var/area/turfarea = get_area(T) + if(turfarea.name == E.allowed_area || turfarea.xenobiology_compatible) + if(X.monkeys >= 1) + var/mob/living/carbon/monkey/food = new /mob/living/carbon/monkey(T, TRUE, C) + if (!QDELETED(food)) + food.LAssailant = C + X.monkeys-- + X.monkeys = round(X.monkeys, 0.1) //Prevents rounding errors + to_chat(C, "[X] now has [X.monkeys] monkey(s) stored.") + else + to_chat(C, "[X] needs to have at least 1 monkey stored. Currently has [X.monkeys] monkeys stored.") + +//Pick up monkey +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoMonkeyClickCtrl(mob/living/user, mob/living/carbon/monkey/M) + if(!(upgradetier & XENOBIO_UPGRADE_MONKEYS)) // CIT CHANGE - makes monkey-related actions require XENOBIO_UPGRADE_MONKEYS + to_chat(user, "This console does not have the monkey upgrade.") + return + if(!GLOB.cameranet.checkTurfVis(M.loc)) + to_chat(user, "Target is not near a camera. Cannot proceed.") + return + var/mob/living/C = user + var/mob/camera/aiEye/remote/xenobio/E = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin + var/area/mobarea = get_area(M.loc) + if(mobarea.name == E.allowed_area || mobarea.xenobiology_compatible) + if(!M.stat) + return + M.visible_message("[M] vanishes as [p_theyre()] reclaimed for recycling!") + X.monkeys = round(X.monkeys + 0.2,0.1) + qdel(M) + if (X.monkeys == (round(X.monkeys,1))) + to_chat(C, "[X] now has [X.monkeys] monkey(s) available.")