Renews Maintenance Drones (#58249)

It's the second try! (First: #48456)

Drones are little robots inspired by Keepers from the Mass Effect games. With laws to maintain both the station, and their distance from the matters of others, they were a great ghost role that let you fix up the station when you were otherwise out of the round. They were in the game for quite a while, but were abused. The abusive players completely ignored the laws and did whatever they wanted. Being tiny ventcrawlers, this became a huge issue for admins.

I attempted to bring them back, this time with some gameplay restrictions to enforce their laws a bit better. It seemed to go well, but I required headmin approval, and this all happened near a headmin election, so the project died.

Recently, some people have wanted to bring drones back. So, they gave me a ring, and I started a discussion with the admin team to see what other changes they would want in addition to the previous ones.

Here are those changes:

    They can't interact at all when close to another being, living or dead
    Their binary chat has been removed to separate them from AI and borgs
    They can only hold a whitelist of items necessary to do their job
    They can only interact with machines necessary to do their job
    The drone satchel is gone, and drones now have built in basic tools
    They also can't interact in some blacklisted high risk areas
    Finally, you must play 40 hours total as a Silicon role in order to play as a drone

Additionally, the drone dispenser is not mapped, and the drones must be researched to be constructed.
Why It's Good For The Game

Drones are a fun way to pass the time, and this time they have a lot less freedom. You can still perform the basic function of maintaining the station, but not without difficulty and tedium. This will allow other station roles to perform much better than the drones, while still letting them chug along at their slower pace.
This commit is contained in:
Jonathan Rubenstein
2021-04-13 13:10:36 +12:00
committed by GitHub
parent b11fa4d6e1
commit 456e475da5
36 changed files with 86004 additions and 85294 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+27 -2
View File
@@ -290,8 +290,15 @@
#define COMSIG_LIVING_START_PULL "living_start_pull"
///called on /living when someone is pulled (mob/living/puller)
#define COMSIG_LIVING_GET_PULLED "living_start_pulled"
///called on /living, when pull is attempted, but before it completes, from base of [/mob/living/start_pulling]: (atom/movable/thing, force)
#define COMSIG_LIVING_TRY_PULL "living_try_pull"
#define COMSIG_LIVING_CANCEL_PULL (1 << 0)
///from base of [/atom/proc/interact]: (mob/user)
#define COMSIG_ATOM_UI_INTERACT "atom_ui_interact"
///called on /living when attempting to pick up an item, from base of /mob/living/put_in_hand_check(): (obj/item/I)
#define COMSIG_LIVING_TRY_PUT_IN_HAND "living_try_put_in_hand"
/// Can't pick up
#define COMPONENT_LIVING_CANT_PUT_IN_HAND (1<<0)
/// from /datum/component/singularity/proc/can_move(), as well as /obj/energy_ball/proc/can_move()
/// if a callback returns `SINGULARITY_TRY_MOVE_BLOCK`, then the singularity will not move to that turf
@@ -575,6 +582,15 @@
///from end of fully_heal(): (admin_revive)
#define COMSIG_LIVING_POST_FULLY_HEAL "living_post_fully_heal"
///Called on user, from base of /datum/strippable_item/try_(un)equip() (atom/target, obj/item/equipping?)
///also from /mob/living/stripPanel(Un)equip)()
#define COMSIG_TRY_STRIP "try_strip"
#define COMPONENT_CANT_STRIP (1<<0)
///Called on user, from base of /datum/strippable_item/alternate_action() (atom/target)
#define COMSIG_TRY_ALT_ACTION "try_alt_action"
#define COMPONENT_CANT_ALT_ACTION (1<<0)
///From /datum/component/creamed/Initialize()
#define COMSIG_MOB_CREAMED "mob_creamed"
///From /obj/item/gun/proc/check_botched()
@@ -669,6 +685,10 @@
///from /obj/machinery/computer/arcade/prizevend(mob/user, prizes = 1)
#define COMSIG_ARCADE_PRIZEVEND "arcade_prizevend"
///from /obj/machinery/can_interact(mob/user): Called on user when attempting to interact with a machine (obj/machinery/machine)
#define COMSIG_TRY_USE_MACHINE "try_use_machine"
#define COMPONENT_CANT_USE_MACHINE (1<<0)
///from obj/machinery/iv_drip/IV_attach(target, usr) : (attachee)
#define COMSIG_IV_ATTACH "iv_attach"
///from obj/machinery/iv_drip/IV_detach() : (detachee)
@@ -701,6 +721,9 @@
///from base of obj/item/equipped(): (/mob/equipper, slot)
#define COMSIG_ITEM_EQUIPPED "item_equip"
///called on [/obj/item] before unequip from base of [mob/proc/doUnEquip]: (force, atom/newloc, no_move, invdrop, silent)
#define COMSIG_ITEM_PRE_UNEQUIP "item_pre_unequip"
#define COMPONENT_ITEM_BLOCK_UNEQUIP (1<<0)
///from base of obj/item/on_grind(): ())
#define COMSIG_ITEM_ON_GRIND "on_grind"
///from base of obj/item/on_juice(): ()
@@ -1166,9 +1189,11 @@
#define COMSIG_MOB_CTRL_CLICKED "mob_ctrl_clicked"
///From base of mob/update_movespeed():area
#define COMSIG_MOB_MOVESPEED_UPDATED "mob_update_movespeed"
///from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity)
///(NOT on humans) from mob/living/*/UnarmedAttack(): (atom/target, proximity, modifiers)
#define COMSIG_LIVING_UNARMED_ATTACK "living_unarmed_attack"
///from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity, modifiers)
#define COMSIG_HUMAN_EARLY_UNARMED_ATTACK "human_early_unarmed_attack"
///from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity)
///from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity, modifiers)
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack"
+1
View File
@@ -594,6 +594,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Self-explainatory.
#define BEAUTY_ELEMENT_TRAIT "beauty_element"
#define MOOD_COMPONENT_TRAIT "mood_component"
#define DRONE_SHY_TRAIT "drone_shy"
/**
* Trait granted by [/mob/living/carbon/Initialize] and
+50 -37
View File
@@ -1,3 +1,14 @@
/// Checks for RIGHT_CLICK in modifiers and runs attack_hand_secondary if so. Returns TRUE if normal chain blocked
/mob/living/proc/right_click_attack_chain(atom/target, list/modifiers)
if (!LAZYACCESS(modifiers, RIGHT_CLICK))
return
var/secondary_result = target.attack_hand_secondary(src, modifiers)
if (secondary_result == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || secondary_result == SECONDARY_ATTACK_CONTINUE_CHAIN)
return TRUE
else if (secondary_result != SECONDARY_ATTACK_CALL_NORMAL)
CRASH("attack_hand_secondary did not return a SECONDARY_ATTACK_* define.")
/*
Humans:
Adds an exception for gloves, to allow special glove types like the ninja ones.
@@ -32,15 +43,10 @@
if(dna?.species?.spec_unarmedattack(src, A, modifiers)) //Because species like monkeys dont use attack hand
return
if (LAZYACCESS(modifiers, RIGHT_CLICK))
var/secondary_result = A.attack_hand_secondary(src, modifiers)
if(!right_click_attack_chain(A, modifiers))
A.attack_hand(src, modifiers)
if (secondary_result == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || secondary_result == SECONDARY_ATTACK_CONTINUE_CHAIN)
return
else if (secondary_result != SECONDARY_ATTACK_CALL_NORMAL)
CRASH("attack_hand_secondary did not return a SECONDARY_ATTACK_* define.")
A.attack_hand(src, modifiers)
/// Return TRUE to cancel other attack hand effects that respect it. Modifiers is the assoc list for click info such as if it was a right click.
/atom/proc/attack_hand(mob/user, list/modifiers)
@@ -114,10 +120,15 @@
/*
Animals & All Unspecified
*/
/mob/living/UnarmedAttack(atom/A, proximity_flag, list/modifiers)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
// If the UnarmedAttack chain is blocked
#define LIVING_UNARMED_ATTACK_BLOCKED(target_atom) (HAS_TRAIT(src, TRAIT_HANDS_BLOCKED) \
|| SEND_SIGNAL(src, COMSIG_LIVING_UNARMED_ATTACK, target_atom, proximity_flag, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN)
/mob/living/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
return
A.attack_animal(src, modifiers)
attack_target.attack_animal(src, modifiers)
/atom/proc/attack_animal(mob/user, list/modifiers)
SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_ANIMAL, user)
@@ -133,10 +144,10 @@
Aliens
Defaults to same as monkey in most places
*/
/mob/living/carbon/alien/UnarmedAttack(atom/A, proximity_flag, list/modifiers)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
/mob/living/carbon/alien/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
return
A.attack_alien(src, modifiers)
attack_target.attack_alien(src, modifiers)
/atom/proc/attack_alien(mob/living/carbon/alien/user, list/modifiers)
attack_paw(user, modifiers)
@@ -144,10 +155,10 @@
// Babby aliens
/mob/living/carbon/alien/larva/UnarmedAttack(atom/A, proximity_flag, list/modifiers)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
/mob/living/carbon/alien/larva/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
return
A.attack_larva(src)
attack_target.attack_larva(src)
/atom/proc/attack_larva(mob/user)
return
@@ -157,12 +168,12 @@
Slimes
Nothing happening here
*/
/mob/living/simple_animal/slime/UnarmedAttack(atom/A, proximity_flag, list/modifiers)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
/mob/living/simple_animal/slime/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
return
if(isturf(A))
if(isturf(attack_target))
return ..()
A.attack_slime(src)
attack_target.attack_slime(src)
/atom/proc/attack_slime(mob/user)
return
@@ -171,20 +182,22 @@
/*
Drones
*/
/mob/living/simple_animal/drone/UnarmedAttack(atom/A, proximity_flag, list/modifiers)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
/mob/living/simple_animal/drone/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
return
A.attack_drone(src)
attack_target.attack_drone(src, modifiers)
/atom/proc/attack_drone(mob/living/simple_animal/drone/user)
attack_hand(user) //defaults to attack_hand. Override it when you don't want drones to do same stuff as humans.
/// Defaults to attack_hand or attack_hand_secondary. Override it when you don't want drones to do same stuff as humans.
/atom/proc/attack_drone(mob/living/simple_animal/drone/user, list/modifiers)
if(!user.right_click_attack_chain(src, modifiers))
attack_hand(user, modifiers)
/*
Brain
*/
/mob/living/brain/UnarmedAttack(atom/A, proximity_flag, list/modifiers)//Stops runtimes due to attack_animal being the default
/mob/living/brain/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)//Stops runtimes due to attack_animal being the default
return
@@ -192,7 +205,7 @@
pAI
*/
/mob/living/silicon/pai/UnarmedAttack(atom/A, proximity_flag, list/modifiers)//Stops runtimes due to attack_animal being the default
/mob/living/silicon/pai/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)//Stops runtimes due to attack_animal being the default
return
@@ -200,11 +213,11 @@
Simple animals
*/
/mob/living/simple_animal/UnarmedAttack(atom/A, proximity_flag, list/modifiers)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
/mob/living/simple_animal/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
return
if(dextrous && (isitem(A) || !combat_mode))
A.attack_hand(src, modifiers)
if(dextrous && (isitem(attack_target) || !combat_mode))
attack_target.attack_hand(src, modifiers)
update_inv_hands()
else
return ..()
@@ -214,16 +227,16 @@
Hostile animals
*/
/mob/living/simple_animal/hostile/UnarmedAttack(atom/A, proximity_flag, list/modifiers)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
/mob/living/simple_animal/hostile/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
return
target = A
if(dextrous && (isitem(A) || !combat_mode))
target = attack_target
if(dextrous && (isitem(attack_target) || !combat_mode))
..()
else
AttackingTarget(A)
AttackingTarget(attack_target)
#undef LIVING_UNARMED_ATTACK_BLOCKED
/*
New Players:
@@ -405,3 +405,11 @@
min_val = 0
/datum/config_entry/flag/dynamic_config_enabled
/datum/config_entry/string/drone_required_role
config_entry_value = "Silicon"
/datum/config_entry/number/drone_role_playtime
config_entry_value = 40
min_val = 0
integer = FALSE // It is in hours, but just in case one wants to specify minutes.
+73
View File
@@ -0,0 +1,73 @@
/** Holder Loving Component
*
* This component is assigned to an [/obj/item], and also keeps track of a [holder].
* The [parent] is 'bound' to [holder]. [parent] will be kept either directly
* inside [holder], or in the inventory of a [/mob] that is itself holding [holder].
*
* If [parent] is placed in a [loc] that is not [holder] or [holder].[loc]
* (if it's a mob), it is placed back inside [holder].
*
* This is intended for items that are a 'part' of another item.
*
* It can also delete [parent] when [holder] is deleted.
*
*/
/datum/component/holderloving
can_transfer = TRUE
/** Item that parent is bound to.
* We try to keep parent either directly in holder, or in holder's loc if loc is a mob,
* and warp parent into holder if they go anywhere else.
*/
var/atom/holder
/// If parent is deleted when the holder gets deleted
var/del_parent_with_holder = FALSE
/datum/component/holderloving/Initialize(holder, del_parent_with_holder)
if(!isitem(parent) || !holder)
return COMPONENT_INCOMPATIBLE
src.holder = holder
if(del_parent_with_holder)
src.del_parent_with_holder = del_parent_with_holder
/datum/component/holderloving/RegisterWithParent()
RegisterSignal(holder, COMSIG_MOVABLE_MOVED, .proc/check_my_loc)
RegisterSignal(holder, COMSIG_PARENT_QDELETING, .proc/holder_deleting)
RegisterSignal(parent, list(
COMSIG_ITEM_DROPPED,
COMSIG_ITEM_EQUIPPED,
COMSIG_STORAGE_ENTERED,
COMSIG_STORAGE_EXITED,
), .proc/check_my_loc)
/datum/component/holderloving/UnregisterFromParent()
UnregisterSignal(holder, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))
UnregisterSignal(parent, list(
COMSIG_ITEM_DROPPED,
COMSIG_ITEM_EQUIPPED,
COMSIG_STORAGE_ENTERED,
COMSIG_STORAGE_EXITED,
))
/datum/component/holderloving/PostTransfer()
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
/datum/component/holderloving/InheritComponent(datum/component/holderloving/friend, i_am_original, list/arguments)
if(i_am_original)
holder = friend.holder
/datum/component/holderloving/proc/check_valid_loc(atom/location)
return (location == holder || ( location == holder.loc && ismob(holder.loc) ))
/datum/component/holderloving/proc/holder_deleting(datum/source, force)
SIGNAL_HANDLER
if(del_parent_with_holder)
qdel(parent)
else
qdel(src)
/datum/component/holderloving/proc/check_my_loc(datum/source)
SIGNAL_HANDLER
var/obj/item/item_parent = parent
if(!check_valid_loc(item_parent.loc))
item_parent.forceMove(holder)
+35
View File
@@ -0,0 +1,35 @@
/// You can only hold whitelisted items
/datum/component/itempicky
can_transfer = TRUE
/// Typecache of items you can hold
var/whitelist
/// Message shown if you try to pick up an item not in the whitelist
var/message = "You don't like %TARGET, why would you hold it?"
/datum/component/itempicky/Initialize(whitelist, message)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
src.whitelist = whitelist
if(message)
src.message = message
/datum/component/itempicky/RegisterWithParent()
RegisterSignal(parent, COMSIG_LIVING_TRY_PUT_IN_HAND, .proc/particularly)
/datum/component/itempicky/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_LIVING_TRY_PUT_IN_HAND)
/datum/component/itempicky/PostTransfer()
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
/datum/component/itempicky/InheritComponent(datum/component/itempicky/friend, i_am_original, list/arguments)
if(i_am_original)
whitelist = friend.whitelist
message = friend.message
/datum/component/itempicky/proc/particularly(datum/source, obj/item/pickingup)
SIGNAL_HANDLER
if(!is_type_in_typecache(pickingup, whitelist))
to_chat(source, "<span class='warning'>[replacetext(message, "%TARGET", pickingup)]</span>")
return COMPONENT_LIVING_CANT_PUT_IN_HAND
+103
View File
@@ -0,0 +1,103 @@
#define SHY_COMPONENT_CACHE_TIME 0.5 SECONDS
/// You can't use items on anyone other than yourself if there are other living mobs around you
/datum/component/shy
can_transfer = TRUE
/// How close you are before you get shy
var/shy_range = 4
/// Typecache of mob types you are okay around
var/list/whitelist
/// Message shown when you are is_shy
var/message = "You find yourself too shy to do that around %TARGET!"
/// Are you shy around a dead body?
var/dead_shy = FALSE
/// Invalidate last_result at this time
COOLDOWN_DECLARE(result_cooldown)
/// What was our last result?
var/last_result = FALSE
/datum/component/shy/Initialize(whitelist, shy_range, message, dead_shy)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
src.whitelist = whitelist
if(shy_range)
src.shy_range = shy_range
if(message)
src.message = message
if(dead_shy)
src.dead_shy = dead_shy
/datum/component/shy/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOB_CLICKON, .proc/on_clickon)
RegisterSignal(parent, COMSIG_LIVING_TRY_PULL, .proc/on_try_pull)
RegisterSignal(parent, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HUMAN_EARLY_UNARMED_ATTACK), .proc/on_unarmed_attack)
RegisterSignal(parent, COMSIG_TRY_STRIP, .proc/on_try_strip)
RegisterSignal(parent, COMSIG_TRY_ALT_ACTION, .proc/on_try_alt_action)
/datum/component/shy/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_MOB_CLICKON,
COMSIG_LIVING_TRY_PULL,
COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HUMAN_EARLY_UNARMED_ATTACK,
COMSIG_TRY_STRIP,
COMSIG_TRY_ALT_ACTION,
))
/datum/component/shy/PostTransfer()
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
/datum/component/shy/InheritComponent(datum/component/shy/friend, i_am_original, list/arguments)
if(i_am_original)
shy_range = friend.shy_range
whitelist = friend.whitelist
message = friend.message
/// Returns TRUE or FALSE if you are within shy_range tiles from a /mob/living
/datum/component/shy/proc/is_shy(atom/target)
var/result = FALSE
var/mob/owner = parent
if(target in owner.DirectAccess())
return
if(!COOLDOWN_FINISHED(src, result_cooldown))
return last_result
var/list/strangers = view(shy_range, get_turf(owner))
if(length(strangers) && locate(/mob/living) in strangers)
for(var/mob/living/person in strangers)
if(person != owner && !is_type_in_typecache(person, whitelist) && (person.stat != DEAD || dead_shy))
to_chat(owner, "<span class='warning'>[replacetext(message, "%TARGET", person)]</span>")
result = TRUE
break
last_result = result
COOLDOWN_START(src, result_cooldown, SHY_COMPONENT_CACHE_TIME)
return result
/datum/component/shy/proc/on_clickon(datum/source, atom/target, params)
SIGNAL_HANDLER
return is_shy(target) && COMSIG_MOB_CANCEL_CLICKON
/datum/component/shy/proc/on_try_pull(datum/source, atom/movable/target, force)
SIGNAL_HANDLER
return is_shy(target) && COMSIG_LIVING_CANCEL_PULL
/datum/component/shy/proc/on_unarmed_attack(datum/source, atom/target, proximity, modifiers)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/shy/proc/on_try_strip(datum/source, atom/target, obj/item/equipping)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANT_STRIP
/datum/component/shy/proc/on_try_alt_action(datum/source, atom/target)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANT_ALT_ACTION
#undef SHY_COMPONENT_CACHE_TIME
+74
View File
@@ -0,0 +1,74 @@
/// You can't use items on anyone other than yourself if you stand in a blacklisted room
/datum/component/shy_in_room
can_transfer = TRUE
/// Typecache of areas you can't stand
var/list/blacklist
/// Message shown when you are in a blacklisted room
var/message = "%ROOM is too creepy to do that!"
/// _blacklist, and _message map to vars
/datum/component/shy_in_room/Initialize(blacklist, message)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
src.blacklist = blacklist
if(message)
src.message = message
/datum/component/shy_in_room/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOB_CLICKON, .proc/on_clickon)
RegisterSignal(parent, COMSIG_LIVING_TRY_PULL, .proc/on_try_pull)
RegisterSignal(parent, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HUMAN_EARLY_UNARMED_ATTACK), .proc/on_unarmed_attack)
RegisterSignal(parent, COMSIG_TRY_STRIP, .proc/on_try_strip)
RegisterSignal(parent, COMSIG_TRY_ALT_ACTION, .proc/on_try_alt_action)
/datum/component/shy_in_room/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_MOB_CLICKON,
COMSIG_LIVING_TRY_PULL,
COMSIG_LIVING_UNARMED_ATTACK,
COMSIG_HUMAN_EARLY_UNARMED_ATTACK,
COMSIG_TRY_STRIP,
COMSIG_TRY_ALT_ACTION,
))
/datum/component/shy_in_room/PostTransfer()
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
/datum/component/shy_in_room/InheritComponent(datum/component/shy_in_room/friend, i_am_original, list/arguments)
if(i_am_original)
blacklist = friend.blacklist
message = friend.message
/// Returns TRUE or FALSE if you are in a blacklisted area
/datum/component/shy_in_room/proc/is_shy(atom/target)
var/mob/owner = parent
if(!length(blacklist) || (target in owner.DirectAccess()))
return
var/area/room = get_area(owner)
if(is_type_in_typecache(room, blacklist))
to_chat(owner, "<span class='warning'>[replacetext(message, "%ROOM", room)]</span>")
return TRUE
/datum/component/shy_in_room/proc/on_clickon(datum/source, atom/target, params)
SIGNAL_HANDLER
return is_shy(target) && COMSIG_MOB_CANCEL_CLICKON
/datum/component/shy_in_room/proc/on_try_pull(datum/source, atom/movable/target, force)
SIGNAL_HANDLER
return is_shy(target) && COMSIG_LIVING_CANCEL_PULL
/datum/component/shy_in_room/proc/on_unarmed_attack(datum/source, atom/target, proximity, modifiers)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/shy_in_room/proc/on_try_strip(datum/source, atom/target, obj/item/equipping)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANT_STRIP
/datum/component/shy_in_room/proc/on_try_alt_action(datum/source, atom/target)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANT_ALT_ACTION
+36
View File
@@ -0,0 +1,36 @@
/// You can't use machines unless they are in a whitelist
/datum/component/technointrovert
can_transfer = TRUE
/// Typecache of allowed machinery
var/list/whitelist
/// Message presented when
var/message = "That %TARGET is strange! Let's avoid it."
/datum/component/technointrovert/Initialize(whitelist, message)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
src.whitelist = whitelist
if(message)
src.message = message
/datum/component/technointrovert/RegisterWithParent()
RegisterSignal(parent, COMSIG_TRY_USE_MACHINE, .proc/is_in_whitelist)
/datum/component/technointrovert/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_TRY_USE_MACHINE)
/datum/component/technointrovert/PostTransfer()
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
/datum/component/technointrovert/InheritComponent(datum/component/technointrovert/friend, i_am_original, list/arguments)
if(i_am_original)
whitelist = friend.whitelist
message = friend.message
/datum/component/technointrovert/proc/is_in_whitelist(datum/source, obj/machinery/machine)
SIGNAL_HANDLER
if(!is_type_in_typecache(machine, whitelist))
to_chat(source, "<span class='warning'>[replacetext(message, "%TARGET", machine)]</span>")
return COMPONENT_CANT_USE_MACHINE
+9
View File
@@ -74,6 +74,8 @@
/// This should be used for checking if an item CAN be equipped.
/// It should not perform the equipping itself.
/datum/strippable_item/proc/try_equip(atom/source, obj/item/equipping, mob/user)
if(SEND_SIGNAL(user, COMSIG_TRY_STRIP, source, equipping) & COMPONENT_CANT_STRIP)
return FALSE
if (HAS_TRAIT(equipping, TRAIT_NODROP))
to_chat(user, "<span class='warning'>You can't put [equipping] on [source], it's stuck to your hand!</span>")
return FALSE
@@ -128,6 +130,8 @@
return FALSE
if (ismob(source))
if(SEND_SIGNAL(user, COMSIG_TRY_STRIP, source, item) & COMPONENT_CANT_STRIP)
return FALSE
var/mob/mob_source = source
if (!item.canStrip(user, mob_source))
return FALSE
@@ -177,7 +181,12 @@
/// Performs an alternative action on this strippable_item.
/// `has_alternate_action` needs to be TRUE.
/// Returns FALSE if blocked by signal, TRUE otherwise.
/datum/strippable_item/proc/alternate_action(atom/source, mob/user)
SHOULD_CALL_PARENT(TRUE)
if(SEND_SIGNAL(user, COMSIG_TRY_ALT_ACTION, source) & COMPONENT_CANT_ALT_ACTION)
return FALSE
return TRUE
/// Returns whether or not this item should show.
/datum/strippable_item/proc/should_show(atom/source, mob/user)
+5
View File
@@ -846,6 +846,11 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
area_flags = BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED
sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/engineering/supermatter/room
name = "Supermatter Engine Room"
icon_state = "engine_sm_room"
sound_environment = SOUND_AREA_LARGE_ENCLOSED
/area/engineering/break_room
name = "Engineering Foyer"
icon_state = "engine_break"
+6 -1
View File
@@ -342,6 +342,9 @@
if(!isliving(user))
return FALSE //no ghosts in the machine allowed, sorry
if(SEND_SIGNAL(user, COMSIG_TRY_USE_MACHINE, src) & COMPONENT_CANT_USE_MACHINE)
return FALSE
var/mob/living/living_user = user
var/is_dextrous = FALSE
@@ -352,7 +355,7 @@
if(!issilicon(user) && !is_dextrous && !user.can_hold_items())
return FALSE //spiders gtfo
if(issilicon(user)) // If we are a silicon, make sure the machine allows silicons to interact with it
if(!(interaction_flags_machine & INTERACT_MACHINE_ALLOW_SILICON))
return FALSE
@@ -477,6 +480,8 @@
/obj/machinery/_try_interact(mob/user)
if((interaction_flags_machine & INTERACT_MACHINE_WIRES_IF_OPEN) && panel_open && (attempt_wire_interaction(user) == WIRE_INTERACTION_BLOCK))
return TRUE
if(SEND_SIGNAL(user, COMSIG_TRY_USE_MACHINE, src) & COMPONENT_CANT_USE_MACHINE)
return TRUE
return ..()
/obj/machinery/CheckParts(list/parts_list)
+6
View File
@@ -96,6 +96,12 @@
starting_amount = 0
cooldownTime = 600
/obj/machinery/drone_dispenser/classic
name = "classic drone shell dispenser"
desc = "A hefty machine that, when supplied with iron and glass, will periodically create a classic drone shell. Does not need to be manually operated."
dispense_type = /obj/effect/mob_spawn/drone/classic
end_create_message = "dispenses a classic drone shell."
// An example of a custom drone dispenser.
// This one requires no materials and creates basic hivebots
/obj/machinery/drone_dispenser/hivebot
+1 -1
View File
@@ -481,7 +481,7 @@
while(length(candidates) && teamsize)
chosen_candidate = pick(candidates)
candidates -= chosen_candidate
nerd = new /mob/living/simple_animal/drone(spawnpoint)
nerd = new /mob/living/simple_animal/drone/classic(spawnpoint)
nerd.key = chosen_candidate.key
log_game("[key_name(nerd)] has been selected as a Nanotrasen emergency response drone")
teamsize--
+1 -2
View File
@@ -244,8 +244,7 @@ Key procs
/datum/language/narsie = list(LANGUAGE_ATOM))
/datum/language_holder/drone
understood_languages = list(/datum/language/drone = list(LANGUAGE_ATOM),
/datum/language/machine = list(LANGUAGE_ATOM))
understood_languages = list(/datum/language/drone = list(LANGUAGE_ATOM))
spoken_languages = list(/datum/language/drone = list(LANGUAGE_ATOM))
blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
+5 -1
View File
@@ -186,7 +186,8 @@
return FALSE //nonliving mobs don't have hands
/mob/living/put_in_hand_check(obj/item/I)
if(istype(I) && ((mobility_flags & MOBILITY_PICKUP) || (I.item_flags & ABSTRACT)))
if(istype(I) && ((mobility_flags & MOBILITY_PICKUP) || (I.item_flags & ABSTRACT)) \
&& !(SEND_SIGNAL(src, COMSIG_LIVING_TRY_PUT_IN_HAND, I) & COMPONENT_LIVING_CANT_PUT_IN_HAND))
return TRUE
return FALSE
@@ -306,6 +307,9 @@
if(HAS_TRAIT(I, TRAIT_NODROP) && !force)
return FALSE
if((SEND_SIGNAL(I, COMSIG_ITEM_PRE_UNEQUIP, force, newloc, no_move, invdrop, silent) & COMPONENT_ITEM_BLOCK_UNEQUIP) && !force)
return FALSE
var/hand_index = get_held_index_of_item(I)
if(hand_index)
held_items[hand_index] = null
@@ -10,7 +10,9 @@
return get_strippable_alternate_action_internals(get_item(source), source)
/datum/strippable_item/mob_item_slot/back/alternate_action(atom/source, mob/user)
return strippable_alternate_action_internals(get_item(source), source, user)
if(!..())
return
strippable_alternate_action_internals(get_item(source), source, user)
/datum/strippable_item/mob_item_slot/mask
key = STRIPPABLE_ITEM_MASK
@@ -52,6 +52,8 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list(
return jumpsuit?.can_adjust ? "adjust_jumpsuit" : null
/datum/strippable_item/mob_item_slot/jumpsuit/alternate_action(atom/source, mob/user)
if (!..())
return
var/obj/item/clothing/under/jumpsuit = get_item(source)
if (!istype(jumpsuit))
return null
@@ -94,6 +96,8 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list(
return "unknot"
/datum/strippable_item/mob_item_slot/feet/alternate_action(atom/source, mob/user)
if(!..())
return
var/obj/item/clothing/shoes/shoes = get_item(source)
if (!istype(shoes))
return
@@ -108,7 +112,9 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list(
return get_strippable_alternate_action_internals(get_item(source), source)
/datum/strippable_item/mob_item_slot/suit_storage/alternate_action(atom/source, mob/user)
return strippable_alternate_action_internals(get_item(source), source, user)
if (!..())
return
strippable_alternate_action_internals(get_item(source), source, user)
/datum/strippable_item/mob_item_slot/id
key = STRIPPABLE_ITEM_ID
@@ -122,7 +128,9 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list(
return get_strippable_alternate_action_internals(get_item(source), source)
/datum/strippable_item/mob_item_slot/belt/alternate_action(atom/source, mob/user)
return strippable_alternate_action_internals(get_item(source), source, user)
if (!..())
return
strippable_alternate_action_internals(get_item(source), source, user)
/datum/strippable_item/mob_item_slot/pocket
/// Which pocket we're referencing. Used for visible text.
+7 -1
View File
@@ -263,6 +263,8 @@
return FALSE
if(throwing || !(mobility_flags & MOBILITY_PULL))
return FALSE
if(SEND_SIGNAL(src, COMSIG_LIVING_TRY_PULL, AM, force) & COMSIG_LIVING_CANCEL_PULL)
return FALSE
AM.add_fingerprint(src)
@@ -990,6 +992,8 @@
// The src mob is trying to strip an item from someone
// Override if a certain type of mob should be behave differently when stripping items (can't, for example)
/mob/living/stripPanelUnequip(obj/item/what, mob/who, where)
if(SEND_SIGNAL(src, COMSIG_TRY_STRIP, who, what) & COMPONENT_CANT_STRIP)
return
if(!what.canStrip(who))
to_chat(src, "<span class='warning'>You can't remove \the [what.name], it appears to be stuck!</span>")
return
@@ -1016,7 +1020,9 @@
// Override if a certain mob should be behave differently when placing items (can't, for example)
/mob/living/stripPanelEquip(obj/item/what, mob/who, where)
what = src.get_active_held_item()
if(what && (HAS_TRAIT(what, TRAIT_NODROP)))
if(!what || (SEND_SIGNAL(src, COMSIG_TRY_STRIP, who, what) & COMPONENT_CANT_STRIP))
return
if(HAS_TRAIT(what, TRAIT_NODROP))
to_chat(src, "<span class='warning'>You can't put \the [what.name] on [who], it's stuck to your hand!</span>")
return
if(what)
@@ -95,7 +95,7 @@
/// Headwear slot
var/obj/item/head
/// Default [/mob/living/simple_animal/drone/var/internal_storage] item
var/obj/item/default_storage = /obj/item/storage/backpack/duffelbag/drone
var/obj/item/default_storage = /obj/item/storage/drone_tools
/// Default [/mob/living/simple_animal/drone/var/head] item
var/obj/item/default_hatmask
/**
@@ -111,6 +111,8 @@
var/visualAppearance = MAINTDRONE
/// Hacked state, see [/mob/living/simple_animal/drone/proc/update_drone_hack]
var/hacked = FALSE
/// If we have laws to minimize bothering others. Enables or disables drone laws enforcement components (use [/mob/living/simple_animal/drone/proc/set_shy] to set)
var/shy = TRUE
/// Flavor text announced to drones on [/mob/proc/Login]
var/flavortext = \
"\n<big><span class='warning'>DO NOT INTERFERE WITH THE ROUND AS A DRONE OR YOU WILL BE DRONE BANNED</span></big>\n"+\
@@ -121,6 +123,52 @@
"<span class='notice'> - Interacting with non-living beings (dragging bodies, looting bodies, etc.)</span>\n"+\
"<span class='warning'>These rules are at admin discretion and will be heavily enforced.</span>\n"+\
"<span class='warning'><u>If you do not have the regular drone laws, follow your laws to the best of your ability.</u></span>"
/// blacklisted drone areas, direct
var/list/drone_area_blacklist_flat = list(/area/engineering/atmos, /area/engineering/atmospherics_engine)
/// blacklisted drone areas, recursive/includes descendants
var/list/drone_area_blacklist_recursive = list(/area/engineering/supermatter)
/// whitelisted drone machines, direct
var/list/drone_machinery_whitelist_flat
/// whitelisted drone machines, recursive/includes descendants
var/list/drone_machinery_whitelist_recursive = list(
/obj/machinery/atmospherics,
/obj/machinery/autolathe,
/obj/machinery/cell_charger,
/obj/machinery/disposal,
/obj/machinery/drone_dispenser,
/obj/machinery/light,
/obj/machinery/pipedispenser,
/obj/machinery/recharger,
/obj/machinery/rnd/production,
)
/// whitelisted drone items, direct
var/list/drone_item_whitelist_flat = list(
/obj/item/crowbar/drone,
/obj/item/screwdriver/drone,
/obj/item/wrench/drone,
/obj/item/weldingtool/drone,
/obj/item/wirecutters/drone,
)
/// whitelisted drone items, recursive/includes descendants
var/list/drone_item_whitelist_recursive = list(
/obj/item/airlock_painter,
/obj/item/circuitboard,
/obj/item/conveyor_switch_construct,
/obj/item/electronics,
/obj/item/light,
/obj/item/pipe_meter,
/obj/item/stack/cable_coil,
/obj/item/stack/circuit_stack,
/obj/item/stack/conveyor,
/obj/item/stack/pipe_cleaner_coil,
/obj/item/stack/rods,
/obj/item/stack/sheet,
/obj/item/stack/tile,
/obj/item/stock_parts,
/obj/item/toner,
/obj/item/clothing/head,
/obj/item/clothing/mask,
)
/mob/living/simple_animal/drone/Initialize()
. = ..()
@@ -140,6 +188,8 @@
ADD_TRAIT(access_card, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
shy_update()
alert_drones(DRONE_NET_CONNECT)
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
@@ -274,7 +324,7 @@
for(var/areaname in our_sort)
if (areaname == home.name)
var/list/alarm = our_sort[areaname]
var/list/sources = alarm[3]
var/list/sources = alarm[2]
if (!(source in sources))
sources += source
return TRUE
@@ -310,6 +360,30 @@
if(cleared)
to_chat(src, "--- [class] alarm in [A.name] has been cleared.")
/mob/living/simple_animal/drone/proc/set_shy(new_shy)
shy = new_shy
shy_update()
/mob/living/simple_animal/drone/proc/shy_update()
var/list/drone_bad_areas = make_associative(drone_area_blacklist_flat) + typecacheof(drone_area_blacklist_recursive)
var/list/drone_good_machines = make_associative(drone_machinery_whitelist_flat) + typecacheof(drone_machinery_whitelist_recursive)
var/list/drone_good_items = make_associative(drone_item_whitelist_flat) + typecacheof(drone_item_whitelist_recursive)
if(shy)
ADD_TRAIT(src, TRAIT_PACIFISM, DRONE_SHY_TRAIT)
LoadComponent(/datum/component/shy, typecacheof(/mob/living/simple_animal/drone), 4, "Your laws prevent this action near %TARGET.", TRUE)
LoadComponent(/datum/component/shy_in_room, drone_bad_areas, "Touching anything in %ROOM could break your laws.")
LoadComponent(/datum/component/technointrovert, drone_good_machines, "Using %TARGET could break your laws.")
LoadComponent(/datum/component/itempicky, drone_good_items, "Using %TARGET could break your laws.")
else
REMOVE_TRAIT(src, TRAIT_PACIFISM, DRONE_SHY_TRAIT)
qdel(GetComponent(/datum/component/shy))
qdel(GetComponent(/datum/component/shy_in_room))
qdel(GetComponent(/datum/component/technointrovert))
qdel(GetComponent(/datum/component/itempicky))
/mob/living/simple_animal/drone/handle_temperature_damage()
return
/mob/living/simple_animal/drone/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25)
if(affect_silicon)
return ..()
@@ -41,6 +41,3 @@
*/
/mob/living/simple_animal/drone/proc/drone_chat(msg)
alert_drones("<i>Drone Chat: <span class='name'>[name]</span> <span class='message'>[say_quote(msg)]</span></i>", TRUE)
/mob/living/simple_animal/drone/binarycheck()
return TRUE
@@ -0,0 +1,85 @@
/obj/item/storage/drone_tools
name = "built-in tools"
desc = "Access your built-in tools."
icon = 'icons/hud/screen_drone.dmi'
icon_state = "tool_storage"
item_flags = ABSTRACT
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
/obj/item/storage/drone_tools/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
/obj/item/storage/drone_tools/ComponentInitialize()
. = ..()
var/static/list/drone_builtins = list(
/obj/item/crowbar/drone,
/obj/item/screwdriver/drone,
/obj/item/wrench/drone,
/obj/item/weldingtool/drone,
/obj/item/wirecutters/drone,
)
var/datum/component/storage/storage_component = GetComponent(/datum/component/storage)
storage_component.max_combined_w_class = 40
storage_component.max_w_class = WEIGHT_CLASS_NORMAL
storage_component.max_items = 5
storage_component.rustle_sound = FALSE
storage_component.set_holdable(drone_builtins)
/obj/item/storage/drone_tools/PopulateContents()
var/list/builtintools = list()
builtintools += new /obj/item/crowbar/drone(src)
builtintools += new /obj/item/screwdriver/drone(src)
builtintools += new /obj/item/wrench/drone(src)
builtintools += new /obj/item/weldingtool/drone(src)
builtintools += new /obj/item/wirecutters/drone(src)
for(var/obj/item/tool as anything in builtintools)
tool.AddComponent(/datum/component/holderloving, src, TRUE)
/obj/item/crowbar/drone
name = "built-in crowbar"
desc = "A crowbar built into your chassis."
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "crowbar_cyborg"
inhand_icon_state = "crowbar"
/obj/item/screwdriver/drone
name = "built-in screwdriver"
desc = "A screwdriver built into your chassis."
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "screwdriver_cyborg"
inhand_icon_state = "screwdriver"
random_color = FALSE
/obj/item/screwdriver/drone/worn_overlays(isinhands = FALSE, icon_file)
. = list()
if(isinhands)
var/mutable_appearance/head = mutable_appearance(icon_file, "screwdriver_head")
head.appearance_flags = RESET_COLOR
. += head
/obj/item/wrench/drone
name = "built-in wrench"
desc = "A wrench built into your chassis."
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "wrench_cyborg"
inhand_icon_state = "wrench"
/obj/item/weldingtool/drone
name = "built-in welding tool"
desc = "A welding tool built into your chassis."
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "indwelder_cyborg"
/obj/item/wirecutters/drone
name = "built-in wirecutters"
desc = "Wirecutters built into your chassis."
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "wirecutters_cyborg"
inhand_icon_state = "cutters"
random_color = FALSE
@@ -1,5 +1,3 @@
#define DRONE_MINIMUM_AGE 14
///////////////////
//DRONES AS ITEMS//
///////////////////
@@ -20,8 +18,14 @@
density = FALSE
death = FALSE
roundstart = FALSE
mob_name = "drone"
///Type of drone that will be spawned
mob_type = /mob/living/simple_animal/drone
banType = ROLE_DRONE
show_flavour = FALSE
short_desc = "You are a Maintenance Drone."
flavour_text = "Born out of science, your purpose is to maintain Space Station 13. Maintenance Drones can become the backbone of a healthy station."
important_info = "You MUST read and follow your laws carefully."
/obj/effect/mob_spawn/drone/Initialize()
. = ..()
@@ -30,12 +34,19 @@
notify_ghosts("A drone shell has been created in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_DRONE, notify_suiciders = FALSE)
AddElement(/datum/element/point_of_interest)
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/effect/mob_spawn/drone/attack_ghost(mob/user)
if(CONFIG_GET(flag/use_age_restriction_for_jobs))
if(!isnum(user.client.player_age)) //apparently what happens when there's no DB connected. just don't let anybody be a drone without admin intervention
return
if(user.client.player_age < DRONE_MINIMUM_AGE)
to_chat(user, "<span class='danger'>You're too new to play as a drone! Please try again in [DRONE_MINIMUM_AGE - user.client.player_age] days.</span>")
return
. = ..()
/obj/effect/mob_spawn/drone/allow_spawn(mob/user)
var/client/user_client = user.client
var/mob/living/simple_animal/drone/drone_type = mob_type
if(!initial(drone_type.shy) || isnull(user_client) || !CONFIG_GET(flag/use_exp_restrictions_other))
return ..()
var/required_role = CONFIG_GET(string/drone_required_role)
var/required_playtime = CONFIG_GET(number/drone_role_playtime) * 60
if(required_playtime <= 0)
return ..()
var/current_playtime = user_client?.calc_exp_type(required_role)
if (current_playtime < required_playtime)
var/minutes_left = required_playtime - current_playtime
var/playtime_left = DisplayTimeText(minutes_left * (1 MINUTES))
to_chat(user, "<span class='danger'>You need to play [playtime_left] more as [required_role] to spawn as a Maintenance Drone!</span>")
return FALSE
return ..()
@@ -29,6 +29,7 @@
default_storage = /obj/item/uplink
default_hatmask = /obj/item/clothing/head/helmet/space/hardsuit/syndi
hacked = TRUE
shy = FALSE
flavortext = null
/mob/living/simple_animal/drone/syndrone/Initialize()
@@ -61,11 +62,15 @@
icon_state = "syndrone_item"
mob_name = "syndrone"
mob_type = /mob/living/simple_animal/drone/syndrone
short_desc = "You are a Syndicate Maintenance Drone."
flavour_text = "In a prior life, you maintained a Nanotrasen Research Station. Abducted from your home, you were given some upgrades... and now serve an enemy of your former masters."
important_info = ""
/obj/effect/mob_spawn/drone/syndrone/badass
name = "badass syndrone shell"
mob_name = "badass syndrone"
mob_type = /mob/living/simple_animal/drone/syndrone/badass
flavour_text = "In a prior life, you maintained a Nanotrasen Research Station. Abducted from your home, you were given some BETTER upgrades... and now serve an enemy of your former masters."
/obj/effect/mob_spawn/drone/snowflake
name = "snowflake drone shell"
@@ -92,6 +97,14 @@
icon_living = icon_state
icon_dead = "[visualAppearance]_dead"
/obj/effect/mob_spawn/drone/classic
mob_type = /mob/living/simple_animal/drone/classic
/mob/living/simple_animal/drone/classic
name = "classic drone shell"
shy = FALSE
default_storage = /obj/item/storage/backpack/duffelbag/drone
/obj/effect/mob_spawn/drone/derelict
name = "derelict drone shell"
desc = "A long-forgotten drone shell. It seems kind of... Space Russian."
@@ -126,4 +139,3 @@
AddComponent(/datum/component/stationstuck, PUNISHMENT_GIB, "01000110 01010101 01000011 01001011 00100000 01011001 01001111 01010101<br>WARNING: Dereliction of KS13 detected. Self-destruct activated.")
@@ -152,6 +152,7 @@
to_chat(src, laws)
to_chat(src, "<i>Your onboard antivirus has initiated lockdown. Motor servos are impaired, ventilation access is denied, and your display reports that you are hacked to all nearby.</i>")
hacked = TRUE
set_shy(FALSE)
mind.special_role = "hacked drone"
REMOVE_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
speed = 1 //gotta go slow
@@ -167,6 +168,7 @@
to_chat(src, laws)
to_chat(src, "<i>Having been restored, your onboard antivirus reports the all-clear and you are able to perform all actions again.</i>")
hacked = FALSE
set_shy(initial(shy))
mind.special_role = null
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
speed = initial(speed)
@@ -186,6 +188,7 @@
*/
/mob/living/simple_animal/drone/proc/liberate()
laws = "1. You are a Free Drone."
set_shy(FALSE)
to_chat(src, laws)
/**
@@ -851,3 +851,13 @@
construction_time = 100
build_path = /obj/item/assembly/flash/handheld
category = list("Misc")
/datum/design/maint_drone
name = "Maintenance Drone"
desc = "\"Repairs the station without bothering you!\" is what the marketing says."
id = "maint_drone"
build_type = MECHFAB
materials = list(/datum/material/iron = 800, /datum/material/glass = 350)
construction_time = 150
build_path = /obj/effect/mob_spawn/drone
category = list("Misc")
@@ -304,6 +304,14 @@
design_ids = list("exodrone_console","exoscanner_console","exoscanner","exodrone_launcher")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/dex_robotics
id = "dex_robotics"
display_name = "Dexterous Robotics Research"
description = "The fine art of opposable thumbs."
prereq_ids = list("adv_engi", "adv_robotics", "biotech")
design_ids = list("maint_drone")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/neural_programming
id = "neural_programming"
display_name = "Neural Programming"
+7
View File
@@ -563,3 +563,10 @@ RATCAP 64
## Maximum fine for a citation
MAXFINE 2000
## Which role is required of playtime to be eligible for Maintenance Drones
## See code/__DEFINES/preferences.dm for available options, use the string portion without quotes
#DRONE_REQUIRED_ROLE Silicon
## How many played hours of DRONE_REQUIRED_ROLE required to be a Maintenance Done
#DRONE_ROLE_PLAYTIME 40
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 42 KiB

+6
View File
@@ -493,10 +493,12 @@
#include "code\datums\components\grillable.dm"
#include "code\datums\components\gunpoint.dm"
#include "code\datums\components\heirloom.dm"
#include "code\datums\components\holderloving.dm"
#include "code\datums\components\honkspam.dm"
#include "code\datums\components\hot_ice.dm"
#include "code\datums\components\igniter.dm"
#include "code\datums\components\infective.dm"
#include "code\datums\components\itempicky.dm"
#include "code\datums\components\jousting.dm"
#include "code\datums\components\knockback.dm"
#include "code\datums\components\knockoff.dm"
@@ -527,6 +529,8 @@
#include "code\datums\components\rotation.dm"
#include "code\datums\components\shielded.dm"
#include "code\datums\components\shrink.dm"
#include "code\datums\components\shy.dm"
#include "code\datums\components\shy_in_room.dm"
#include "code\datums\components\singularity.dm"
#include "code\datums\components\sitcomlaughter.dm"
#include "code\datums\components\sizzle.dm"
@@ -546,6 +550,7 @@
#include "code\datums\components\swarming.dm"
#include "code\datums\components\tackle.dm"
#include "code\datums\components\tactical.dm"
#include "code\datums\components\technointrovert.dm"
#include "code\datums\components\tether.dm"
#include "code\datums\components\thermite.dm"
#include "code\datums\components\twohanded.dm"
@@ -2657,6 +2662,7 @@
#include "code\modules\mob\living\simple_animal\friendly\snake.dm"
#include "code\modules\mob\living\simple_animal\friendly\drone\_drone.dm"
#include "code\modules\mob\living\simple_animal\friendly\drone\drone_say.dm"
#include "code\modules\mob\living\simple_animal\friendly\drone\drone_tools.dm"
#include "code\modules\mob\living\simple_animal\friendly\drone\drones_as_items.dm"
#include "code\modules\mob\living\simple_animal\friendly\drone\extra_drone_types.dm"
#include "code\modules\mob\living\simple_animal\friendly\drone\interaction.dm"