diff --git a/citadel.dme b/citadel.dme index 17b96ddd329..317d16b7e89 100644 --- a/citadel.dme +++ b/citadel.dme @@ -30,6 +30,7 @@ #include "code\__DEFINES\_profile.dm" #include "code\__DEFINES\_protect.dm" #include "code\__DEFINES\_tick.dm" +#include "code\__DEFINES\ability.dm" #include "code\__DEFINES\access.dm" #include "code\__DEFINES\actionspeed_modification.dm" #include "code\__DEFINES\appearance.dm" @@ -412,7 +413,6 @@ #include "code\_onclick\hud\rigmech.dm" #include "code\_onclick\hud\robot.dm" #include "code\_onclick\hud\spell_screen_objects.dm" -#include "code\_onclick\hud\actions\action.dm" #include "code\_onclick\hud\intents\throwing.dm" #include "code\_onclick\hud\inventory\inventory.dm" #include "code\_onclick\hud\objects\waypoint_tracker.dm" @@ -554,7 +554,9 @@ #include "code\controllers\subsystem\sound\_sound.dm" #include "code\controllers\subsystem\sound\channel_manager.dm" #include "code\controllers\subsystem\sound\soundbyte_manager.dm" +#include "code\datums\ability.dm" #include "code\datums\access.dm" +#include "code\datums\action.dm" #include "code\datums\ai_law_sets.dm" #include "code\datums\ai_laws.dm" #include "code\datums\beam.dm" @@ -4126,6 +4128,7 @@ #include "code\modules\shuttles\shuttles_vr.dm" #include "code\modules\shuttles\shuttles_web.dm" #include "code\modules\shuttles\web_datums.dm" +#include "code\modules\species\abilites.dm" #include "code\modules\species\character_species.dm" #include "code\modules\species\species.dm" #include "code\modules\species\species_attack.dm" @@ -4163,6 +4166,7 @@ #include "code\modules\species\station\station_special_abilities.dm" #include "code\modules\species\station\vasilissan.dm" #include "code\modules\species\station\werebeast.dm" +#include "code\modules\species\station\xenochimera.dm" #include "code\modules\species\station\standard\akula.dm" #include "code\modules\species\station\standard\human.dm" #include "code\modules\species\station\standard\human_subspecies.dm" @@ -4182,7 +4186,6 @@ #include "code\modules\species\station\xenomorph_hybrids\xeno_hybrids.dm" #include "code\modules\species\virtual_reality\avatar.dm" #include "code\modules\species\virtual_reality\opaque_form.dm" -#include "code\modules\species\xenochimera\xenochimera.dm" #include "code\modules\species\xenomorphs\alien_embryo.dm" #include "code\modules\species\xenomorphs\alien_facehugger.dm" #include "code\modules\species\xenomorphs\alien_powers.dm" diff --git a/code/__DEFINES/ability.dm b/code/__DEFINES/ability.dm new file mode 100644 index 00000000000..914080f3b89 --- /dev/null +++ b/code/__DEFINES/ability.dm @@ -0,0 +1,15 @@ +//? interact_type; used on tgui side - update that if you change these. + +/// no default interact, no hotbinding +#define ABILITY_INTERACT_NONE "none" +/// trigger interact, hot-bindable +#define ABILITY_INTERACT_TRIGGER "trigger" +/// toggle interact, hot-bindable +#define ABILITY_INTERACT_TOGGLE "toggle" + +//? ability_check_flags - if you add new ones, make sure to modify available_check() and unavailable_reason(). + +#define ABILITY_CHECK_CONSCIOUS (1<<0) +#define ABILITY_CHECK_STANDING (1<<1) +#define ABILITY_CHECK_FREE_HAND (1<<2) +#define ABILITY_CHECK_STUNNED (1<<3) diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index ce2cf1ce75f..cc2774428d1 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -1,4 +1,25 @@ +/// Generic source - don't use this if at all possible. #define GENERIC_TRAIT "generic" + +//? Mob Sources + +/// From species +#define SPECIES_TRAIT "species" + +//? Role / Jobs + +/// Roles +#define ROLE_TRAIT "role" + +//? Admin / Special Sources + +/// legacy: Cannot be removed without admin intervention +#define ROUNDSTART_TRAIT "roundstart" +/// From vv +#define ADMIN_TRAIT "admin" + +//? Unsorted / Legacy + /* #define EYE_DAMAGE "eye_damage" #define GENETIC_MUTATION "genetic" @@ -11,26 +32,11 @@ #define SPECIES_TRAIT "species" #define ORGAN_TRAIT "organ" */ -///cannot be removed without admin intervention -#define ROUNDSTART_TRAIT "roundstart" + #define HOLOGRAM_TRAIT "hologram" -#define GHOSTROLE_TRAIT "ghostrole" -#define JOB_TRAIT "job" ///Mime trait. #define TRAIT_MIME "mime" -/* -#define CYBORG_ITEM_TRAIT "cyborg-item" -*/ -/// (B)admins only. -#define ADMIN_TRAIT "admin" -/* -#define CHANGELING_TRAIT "changeling" -#define CULT_TRAIT "cult" -/// The item is magically cursed -#define CURSED_ITEM_TRAIT "cursed-item" -#define ABSTRACT_ITEM_TRAIT "abstract-item" -#define STATUS_EFFECT_TRAIT "status-effect" -*/ + #define CLOTHING_TRAIT "clothing" #define RIG_TRAIT "rig" #define MAGBOOT_TRAIT "magboot" @@ -42,62 +48,10 @@ #define MMI_TRAIT "mmi" #define STASIS_BAG_TRAIT "stasis_bag" #define GRIPPER_TRAIT "gripper" -/* -#define GLASSES_TRAIT "glasses" -/// inherited from riding vehicles -#define VEHICLE_TRAIT "vehicle" -#define INNATE_TRAIT "innate" - -// unique trait sources, still defines -#define CLONING_POD_TRAIT "cloning-pod" -#define STATUE_MUTE "statue" -#define CHANGELING_DRAIN "drain" -#define CHANGELING_HIVEMIND_MUTE "ling_mute" -#define ABYSSAL_GAZE_BLIND "abyssal_gaze" -#define HIGHLANDER "highlander" -#define TRAIT_HULK "hulk" -#define STASIS_MUTE "stasis" -#define GENETICS_SPELL "genetics_spell" -#define EYES_COVERED "eyes_covered" -#define CULT_EYES "cult_eyes" -#define TRAIT_SANTA "santa" -#define SCRYING_ORB "scrying-orb" -#define ABDUCTOR_ANTAGONIST "abductor-antagonist" -#define NUKEOP_TRAIT "nuke-op" -#define DEATHSQUAD_TRAIT "deathsquad" -#define MEGAFAUNA_TRAIT "megafauna" -#define CLOWN_NUKE_TRAIT "clown-nuke" -#define STICKY_MOUSTACHE_TRAIT "sticky-moustache" -#define CHAINSAW_FRENZY_TRAIT "chainsaw-frenzy" -#define CHRONO_GUN_TRAIT "chrono-gun" -#define REVERSE_BEAR_TRAP_TRAIT "reverse-bear-trap" -#define CURSED_MASK_TRAIT "cursed-mask" -#define HIS_GRACE_TRAIT "his-grace" -*/ #define HAND_REPLACEMENT_TRAIT "magic-hand" -/* -#define HOT_POTATO_TRAIT "hot-potato" -#define SABRE_SUICIDE_TRAIT "sabre-suicide" -#define ABDUCTOR_VEST_TRAIT "abductor-vest" -#define CAPTURE_THE_FLAG_TRAIT "capture-the-flag" -#define EYE_OF_GOD_TRAIT "eye-of-god" -#define SHAMEBRERO_TRAIT "shamebrero" -#define CHRONOSUIT_TRAIT "chronosuit" -#define LOCKED_HELMET_TRAIT "locked-helmet" -#define NINJA_SUIT_TRAIT "ninja-suit" -#define ANTI_DROP_IMPLANT_TRAIT "anti-drop-implant" -#define VR_ZONE_TRAIT "vr_zone_trait" -#define SLEEPING_CARP_TRAIT "sleeping_carp" -#define SANGUIOSE_TRAIT "sanguiose" -#define FROGENITE_TRAIT "frogenite" -#define FERVEATIUM_TRAIT "ferveatium" -*/ + #define CHANGELING_TRAIT "changeling" #define TECHNOMANCER_TRAIT "technomancer" #define CPR_TRAIT "cpr" - -//! component source -/// rad_insulation -#define TRAIT_SOURCE_COMPONENT_RAD_INSULATION "c_rad_insulation" diff --git a/code/datums/ability.dm b/code/datums/ability.dm new file mode 100644 index 00000000000..45bbc11785e --- /dev/null +++ b/code/datums/ability.dm @@ -0,0 +1,287 @@ +/** + * ability system + * + * - single, mob owner + * - supports trigger / toggle presets + * - supports custom tgui + * + * used for intrinsic species / antagonist / body abiltiies + */ +/datum/ability + //? basics + /// name + var/name = "Unnamed ability" + /// desc + var/desc = "Some sort of ability." + /// category - used for tgui + var/category = "Misc" + + //? mob + /// owning mob - can be null if we aren't bound / granted to anyone. + var/mob/owner + + //? binding + /// action datum + var/datum/action/action + /// action button icon + var/action_icon = 'icons/screen/actions/actions.dmi' + /// action button icon state + var/action_state = "default" + /// action button background icon + var/background_icon = 'icons/screen/actions/backgrounds.dmi' + /// action button background icon state - the _on overlay will be added while active, automatically. + var/background_state = "alien" + /// currently hotbound? + var/bound = FALSE + /// automatically hotbound? + var/always_bind = FALSE + + //? interaction + /// default interaction mode + var/interact_type = ABILITY_INTERACT_NONE + /// tgui id + var/tgui_id = "TGUIAbility" + + //? checks + /// check flags - see [code/__DEFINES/ability.dm] + var/ability_check_flags = NONE + /// mobility check flags + var/mobility_check_flags = NONE + + //? state + /// cooldown delay, if we have a cooldown + var/cooldown = 0 + /// for toggled abilities, turning off incurs the cooldown - otherwise, cooldown begins on toggling off. + var/cooldown_for_deactivation = TRUE + /// windup delay, if we have a windup + var/windup = 0 + /// windup requires standing still + var/windup_requires_still = TRUE + /// last use world.time + var/last_used + /// timerid for cooldown finish action button update + var/cooldown_visual_timerid + /// for toggle interacts: are we enabled? + var/enabled = FALSE + +/datum/ability/Destroy() + if(!isnull(owner)) + disassociate(owner) + if(!isnull(action)) + QDEL_NULL(action) + return ..() + +/** + * generates our action button if it doesn't exist + * + * @return our action button. + */ +/datum/ability/proc/generate_action() + if(!isnull(action)) + return action + action = new(src) + action.name = hotbind_name() + action.desc = hotbind_desc() + action.button_managed = TRUE + action.button_icon = action_icon + action.button_icon_state = action_state + action.background_icon = background_icon + action.background_icon_state = background_state + update_action() + return action + +/datum/ability/proc/update_action() + var/availability = 1 + if(cooldown) + availability = clamp((world.time - last_used) / cooldown, 0, 1) + action?.push_button_update(availability, (interact_type == ABILITY_INTERACT_TOGGLE) && enabled) + recheck_queued_action_update() + +/datum/ability/proc/recheck_queued_action_update() + if(cooldown_visual_timerid) + deltimer(cooldown_visual_timerid) + cooldown_visual_timerid = null + var/next_available = 0 + if(cooldown && (world.time < last_used + cooldown)) + next_available = max(next_available, (last_used + cooldown) - world.time) + if(next_available > 0) + addtimer(CALLBACK(src, PROC_REF(update_action)), next_available, TIMER_STOPPABLE) + +/datum/ability/ui_action_click(datum/action/action, mob/user) + . = ..() + action_trigger(user) + +/datum/ability/proc/action_trigger(mob/user) + attempt_trigger(user) + +/datum/ability/proc/hotbind_name() + return "[category] - [name]" + +/datum/ability/proc/hotbind_desc() + return desc + +/** + * called to try to trigger. + * + * @params + * * user - triggering user. this is usually owner, but sometimes isn't. + * * toggling - null if not toggled ability / not toggling, TRUE / FALSE for on / off. + */ +/datum/ability/proc/attempt_trigger(mob/user, toggling) + if(interact_type == ABILITY_INTERACT_TOGGLE) + if(!isnull(toggling) && toggling == enabled) + return + if(isnull(toggling)) + toggling = !enabled + if(!check_trigger(user, toggling, TRUE)) + return + if(windup) + if(!do_after(user, windup, ignore_movement = !windup_requires_still)) + return + if(!check_trigger(user, toggling, TRUE)) + return + on_trigger(user, toggling) + +/** + * called to check a trigger. + * + * @params + * * user - triggering user. this is usually owner, but sometimes isn't. + * * toggling - null if not toggled ability / not toggling, TRUE / FALSE for on / off. + * * feedback - output feedback messages + */ +/datum/ability/proc/check_trigger(mob/user, toggling, feedback) + if((isnull(toggling) || toggling || (!toggling && cooldown_for_deactivation)) && (cooldown + last_used > world.time)) + to_chat(user, SPAN_WARNING("[src] is still on cooldown! ([round((world.time - last_used) * 0.1, 0.1)] / [round(cooldown * 0.1, 0.1)])")) + return FALSE + if(!available_check()) + to_chat(user, SPAN_WARNING("You can't do that right now!")) + return FALSE + return TRUE + +/** + * called on trigger + * + * @params + * * user - triggering user. this is usually owner, but sometimes isn't. + * * toggling - null if not toggled ability / not toggling, TRUE / FALSE for on / off. + */ +/datum/ability/proc/on_trigger(mob/user, toggling) + last_used = world.time + if(interact_type != ABILITY_INTERACT_TOGGLE) + update_action() + return + if(!isnull(toggling) && toggling == enabled) + return + if(isnull(toggling)) + toggling = !enabled + if(enabled && !toggling) + disable() + else if(!enabled && toggling) + enable() + +/datum/ability/proc/enable(update_action) + enabled = TRUE + if(update_action) + update_action() + on_enable() + +/datum/ability/proc/disable(update_action) + enabled = FALSE + if(update_action) + update_action() + on_disable() + +/datum/ability/proc/on_enable() + return + +/datum/ability/proc/on_disable() + return + +/datum/ability/proc/quickbind() + if(bound) + return + bound = TRUE + generate_action() + if(!isnull(owner)) + action?.grant(owner) + +/datum/ability/proc/unbind() + bound = FALSE + if(!isnull(owner)) + action?.remove(owner) + +/datum/ability/proc/associate(mob/M) + if(owner == M) + return + ASSERT(isnull(owner)) + owner = M + owner.register_ability(src) + if(bound) + action?.grant(M) + update_action() + else if(always_bind) + quickbind() + +/datum/ability/proc/disassociate(mob/M) + ASSERT(owner == M) + if(bound) + action?.remove(owner) + owner.unregister_ability(src) + owner = null + +/** + * checks if we can be used + */ +/datum/ability/proc/available_check() + if(isnull(owner)) + return FALSE + if(ability_check_flags == NONE) + return TRUE + if((ability_check_flags & ABILITY_CHECK_CONSCIOUS) && !IS_CONSCIOUS(owner)) + return FALSE + if((ability_check_flags & ABILITY_CHECK_STANDING) && owner.lying) + return FALSE + if((ability_check_flags & ABILITY_CHECK_FREE_HAND) && !(owner.has_free_hand())) + return FALSE + if((ability_check_flags & ABILITY_CHECK_STUNNED) && (!IS_CONSCIOUS(owner) || owner.stunned || owner.weakened || owner.incapacitated())) + return FALSE + return TRUE + +/** + * checks if we can be used, returning reason on failure or null for success. + */ +/datum/ability/proc/unavailable_reason() + if(isnull(owner)) + return "!ERROR - NO OWNER!" + if(ability_check_flags == NONE) + return + if((ability_check_flags & ABILITY_CHECK_CONSCIOUS) && !IS_CONSCIOUS(owner)) + return "You cannot do that while unconscious." + if((ability_check_flags & ABILITY_CHECK_STANDING) && owner.lying) + return "You cannot do that while on the ground." + if((ability_check_flags & ABILITY_CHECK_FREE_HAND) && !(owner.has_free_hand())) + return "You cannot do that without a free hand." + if((ability_check_flags & ABILITY_CHECK_STUNNED) && (!IS_CONSCIOUS(owner) || owner.stunned || owner.weakened || owner.incapacitated())) + return "You cannot do that while incapacitated." + +/** + * static data for tgui panel + */ +/datum/ability/ui_static_data(mob/user) + return list( + "$tgui" = tgui_id, + "$src" = REF(src), + "interact_type" = interact_type, + "can_bind" = !always_bind && (interact_type != ABILITY_INTERACT_NONE), + "bound" = bound, + "name" = name, + "desc" = desc, + ) + +/** + * action datums for abilities + */ +/datum/action/ability + abstract_type = /datum/action/ability + target_type = /datum/ability diff --git a/code/_onclick/hud/actions/action.dm b/code/datums/action.dm similarity index 85% rename from code/_onclick/hud/actions/action.dm rename to code/datums/action.dm index e11ad0ba78f..fe3d121f07a 100644 --- a/code/_onclick/hud/actions/action.dm +++ b/code/datums/action.dm @@ -12,6 +12,8 @@ #define ACTION_CHECK_INSIDE (1<<4) #define ACTION_CHECK_CONSCIOUS (1<<5) +//? Action Datum + // todo: multiple owners // todo: ability datums? cooldown needs more checking /** @@ -47,13 +49,25 @@ var/check_flags = 0 var/processing = 0 var/active = 0 - var/atom/movable/screen/movable/action_button/button = null var/button_icon = 'icons/screen/actions/actions.dmi' + var/button_icon_state = "default" - var/background_icon = 'icons/screen/actions/actions.dmi' - var/background_icon_state = "bg_default" + + var/background_icon = 'icons/screen/actions/backgrounds.dmi' + /// background icon state in [background_icon] - the state_on overlay will be added when this is active, automatically. + var/background_icon_state = "default" var/mob/owner + //? Button + /// Our actual on-screen action button + var/atom/movable/screen/movable/action_button/button + /// last button availability + var/button_availability + /// last button toggle + var/button_toggled + /// are button updates managed? if so, we don't auto update. + var/button_managed = FALSE + /datum/action/New(datum/target) if(!target_compatible(target)) qdel(src) @@ -163,7 +177,12 @@ /datum/action/proc/UpdateName() return name -/datum/action/proc/update_button(atom/movable/screen/movable/action_button/button) +/** + * updates button state to match our stored state. + */ +/datum/action/proc/update_button() + auto_button_update(update = FALSE) + button.icon = background_icon button.icon_state = background_icon_state @@ -178,11 +197,38 @@ img.pixel_y = 0 button.add_overlay(img) - if(!IsAvailable()) + if(button_availability < 1) button.color = rgb(128,0,0,128) else button.color = rgb(255,255,255,255) +/** + * pushes immediate button update + * + * @params + * * availability - 0 to 1 of how ready we are + * * active - turned on? + * * update - update button appearance? + */ +/datum/action/proc/push_button_update(availability, active, update = TRUE) + button_availability = availability + button_toggled = active + if(update) + update_button() + +/** + * automatically updates button + * + * @params + * * update - update button appearance? + */ +/datum/action/proc/auto_button_update(update) + if(button_managed) + return + push_button_update(IsAvailable()? 1 : 0, active, update) + +//? Action Button + /atom/movable/screen/movable/action_button var/datum/action/owner screen_loc = "LEFT,TOP" @@ -200,7 +246,7 @@ /atom/movable/screen/movable/action_button/proc/UpdateIcon() if(!owner) return - owner.update_button(src) + owner.update_button() //Hide/Show Action Buttons ... Button /atom/movable/screen/movable/action_button/hide_toggle diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 150845a1ede..3c2383c847c 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -30,7 +30,12 @@ var/ckey /// Replaces mob/var/original_name var/name + // todo: /mob, not /living + /// the mob we're currently inhabiting. the mind can be referenced by many mobs, however, only one may be 'owned' by it. + /// this functionality is used for things like aghosting and astral projection, as even though the player is in another mob, + /// their actual mob is what owns their mind. var/mob/living/current + var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds. var/active = FALSE @@ -38,6 +43,10 @@ /// characteristics holder var/datum/characteristics_holder/characteristics + //? Abilities + /// mind-level abilities + var/list/datum/ability/abilities + //? Preferences /** * original save data @@ -97,8 +106,11 @@ /datum/mind/Destroy() QDEL_NULL(characteristics) + QDEL_LIST_NULL(abilities) return ..() +//? Characteristics + /** * make sure we have a characteristics holder */ @@ -108,31 +120,64 @@ characteristics.associate_with_mind(src) return characteristics -/datum/mind/proc/transfer_to(mob/living/new_character) - if(!istype(new_character)) - log_world("## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn") - if(current) //remove ourself from our old body's mind variable - if(changeling) - current.remove_changeling_powers() - remove_verb(current, /datum/changeling/proc/EvolutionMenu) - current.mind = null - characteristics?.disassociate_from_mob(current) +//? Transfer - SSnanoui.user_transferred(current, new_character) // transfer active NanoUI instances to new user - if(new_character.mind) //remove any mind currently in our new body's mind variable - new_character.mind.current = null +/datum/mind/proc/disassociate() + ASSERT(!isnull(current)) - current = new_character //link ourself to our new body - new_character.mind = src //and link our new body to ourself - characteristics?.associate_with_mob(current) + // LEGACY: remove changeling + if(changeling) + current.remove_changeling_powers() + remove_verb(current, /datum/changeling/proc/EvolutionMenu) + // remove characteristics + characteristics?.disassociate_from_mob(current) + // remove abilities + for(var/datum/ability/ability as anything in abilities) + ability.disassociate(current) + // null mind + current.mind = null + // done + current = null + +/datum/mind/proc/associate(mob/new_character) + ASSERT(isnull(current)) + ASSERT(isnull(new_character.mind)) + + // start + current = new_character + + // set mind + new_character.mind = src + // add characteristics + characteristics?.associate_with_mob(new_character) + // add abilities + for(var/datum/ability/ability as anything in abilities) + ability.associate(new_character) + // LEGACY: add changeling if(changeling) new_character.make_changeling() + //* transfer player if necessary if(active) new_character.ckey = ckey //now transfer the ckey to link the client to our new body - // if(new_character.client) //TODO: Eye Contact - // LAZYCLEARLIST(new_character.client.recent_examines) + +/datum/mind/proc/transfer(mob/new_character) + if(isnull(current)) + associate(new_character) + return + + var/mob/old_character = current + + disassociate() + + if(!isnull(new_character.mind)) + new_character.mind.disassociate() + + SStgui.on_transfer(old_character, new_character) + SSnanoui.user_transferred(old_character, new_character) + + associate(new_character) /datum/mind/proc/store_memory(new_text) if((length(memory) + length(new_text)) <= MAX_MESSAGE_LEN) @@ -651,3 +696,42 @@ original_save_data[CHARACTER_DATA_RELIGION], ) listclearnulls(.) + +//? Abilities + +/** + * adds an ability to us + * + * @params + * * ability - a datum or path. once passed in, this datum is owned by the mind, and the mind can delete it at any time! if a path is passed in, this will runtime on duplicates - paths must always be unique if used in this way. + * + * @return TRUE / FALSE success or failure + */ +/datum/mind/proc/add_ability(datum/ability/ability) + if(ispath(ability)) + . = FALSE + ASSERT(!(locate(ability) in abilities)) + ability = new ability + abilities += ability + if(current) + ability.associate(current) + return TRUE + +/** + * removes, and deletes, an ability on us + * + * @params + * * ability - a datum or path. paths should only be used if it's an unique ability nothing else should grant! + * + * @return TRUE / FALSE success or failure + */ +/datum/mind/proc/remove_ability(datum/ability/ability) + if(ispath(ability)) + ability = locate(ability) in abilities + if(isnull(ability)) + return FALSE + abilities -= ability + if(current) + ability.disassociate(current) + qdel(ability) + return TRUE diff --git a/code/datums/soul_link.dm b/code/datums/soul_link.dm index dd1f0f6e00f..ede05249433 100644 --- a/code/datums/soul_link.dm +++ b/code/datums/soul_link.dm @@ -125,12 +125,12 @@ /datum/soul_link/shared_body/owner_died(gibbed, mob/living/owner) if(soul_owner && soul_sharer) if(soul_sharer.mind) - soul_sharer.mind.transfer_to(soul_owner) + soul_sharer.mind.transfer(soul_owner) soul_sharer.death(gibbed) /datum/soul_link/shared_body/sharer_died(gibbed, mob/living/sharer) if(soul_owner && soul_sharer && soul_sharer.mind) - soul_sharer.mind.transfer_to(soul_owner) + soul_sharer.mind.transfer(soul_owner) @@ -147,7 +147,7 @@ for(var/l in souls) var/mob/living/L = l if(L.stat != DEAD && L.mind) - L.mind.transfer_to(soul_owner) + L.mind.transfer(soul_owner) soul_owner.revive(TRUE, TRUE) L.death(FALSE) diff --git a/code/game/antagonist/antagonist_update.dm b/code/game/antagonist/antagonist_update.dm index 1f01982feac..51ca350bab0 100644 --- a/code/game/antagonist/antagonist_update.dm +++ b/code/game/antagonist/antagonist_update.dm @@ -8,7 +8,7 @@ if((flags & ANTAG_OVERRIDE_MOB) && (!player.current || (mob_path && !istype(player.current, mob_path)))) var/mob/holder = player.current player.current = new mob_path(get_turf(player.current)) - player.transfer_to(player.current) + player.transfer(player.current) if(holder) qdel(holder) player.original = player.current if(!preserve_appearance && (flags & ANTAG_SET_APPEARANCE)) diff --git a/code/game/antagonist/station/rogue_ai.dm b/code/game/antagonist/station/rogue_ai.dm index 067c60559c0..93abc5510a1 100644 --- a/code/game/antagonist/station/rogue_ai.dm +++ b/code/game/antagonist/station/rogue_ai.dm @@ -87,7 +87,7 @@ var/datum/antagonist/rogue_ai/malf if((flags & ANTAG_OVERRIDE_MOB) && (!player.current || (mob_path && !istype(player.current, mob_path)))) var/mob/holder = player.current player.current = new mob_path(get_turf(player.current), null, null, 1) - player.transfer_to(player.current) + player.transfer(player.current) if(holder) qdel(holder) player.original = player.current return player.current diff --git a/code/game/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm index 6108cf7d869..0a699099519 100644 --- a/code/game/dna/genes/monkey.dm +++ b/code/game/dna/genes/monkey.dm @@ -58,7 +58,7 @@ O.loc = M.loc if(M.mind) - M.mind.transfer_to(O) //transfer our mind to the cute little monkey + M.mind.transfer(O) //transfer our mind to the cute little monkey if (connected) //inside dna thing var/obj/machinery/dna_scannernew/C = connected @@ -129,7 +129,7 @@ O.loc = M.loc if(M.mind) - M.mind.transfer_to(O) //transfer our mind to the human + M.mind.transfer(O) //transfer our mind to the human if (connected) //inside dna thing var/obj/machinery/dna_scannernew/C = connected diff --git a/code/game/gamemodes/changeling/powers/lesser_form.dm b/code/game/gamemodes/changeling/powers/lesser_form.dm index 949e8b42711..36b3c549a35 100644 --- a/code/game/gamemodes/changeling/powers/lesser_form.dm +++ b/code/game/gamemodes/changeling/powers/lesser_form.dm @@ -105,7 +105,7 @@ I.loc = O I.implanted = O - C.mind.transfer_to(O) + C.mind.transfer(O) O.make_changeling() O.changeling_update_languages(changeling.absorbed_languages) diff --git a/code/game/gamemodes/cult/cultify/mob.dm b/code/game/gamemodes/cult/cultify/mob.dm index ea550a7d7cc..132ab9b87af 100644 --- a/code/game/gamemodes/cult/cultify/mob.dm +++ b/code/game/gamemodes/cult/cultify/mob.dm @@ -17,7 +17,7 @@ /mob/living/cultify() if(iscultist(src) && client) var/mob/living/simple_mob/construct/harvester/C = new(get_turf(src)) - mind.transfer_to(C) + mind.transfer(C) to_chat(C, "The Geometer of Blood is overjoyed to be reunited with its followers, and accepts your body in sacrifice. As reward, you have been gifted with the shell of an Harvester.
Your tendrils can use and draw runes without need for a tome, your eyes can see beings through walls, and your mind can open any door. Use these assets to serve Nar-Sie and bring him any remaining living human in the world.
You can teleport yourself back to Nar-Sie along with any being under yourself at any time using your \"Harvest\" spell.
") dust() else if(client) diff --git a/code/game/gamemodes/meme/meme.dm b/code/game/gamemodes/meme/meme.dm index adf15c78fd4..d8c23794d2a 100644 --- a/code/game/gamemodes/meme/meme.dm +++ b/code/game/gamemodes/meme/meme.dm @@ -78,7 +78,7 @@ for(var/datum/mind/meme in memes) var/mob/living/parasite/meme/M = new var/mob/original = meme.current - meme.transfer_to(M) + meme.transfer(M) M.clearHUD() // get the host for this meme diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index cf2de35d7fa..88f5a72be48 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -119,7 +119,7 @@ // Here let's calculate their health so the pod doesn't immediately eject them!!! H.updatehealth() - clonemind.transfer_to(H) + clonemind.transfer(H) H.ckey = R.ckey to_chat(H, SPAN_BOLDDANGER("Consciousness slowly creeps over you as your body regenerates.
") + SPAN_USERDANGER("Your recent memories are fuzzy, and it's hard to remember anything from today...
") + SPAN_NOTICE(SPAN_ROSE("So this is what cloning feels like?"))) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index ee8b313617c..f7f52c603fd 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -2572,7 +2572,7 @@ O.updatehealth() src.occupant = O if(AI.mind) - AI.mind.transfer_to(O) + AI.mind.transfer(O) AI.name = "Inactive AI" AI.real_name = "Inactive AI" AI.icon_state = "ai-empty" @@ -2580,7 +2580,7 @@ AI.name = O.name AI.real_name = O.real_name if(O.mind) - O.mind.transfer_to(AI) + O.mind.transfer(AI) AI.control_disabled = 0 AI.laws = O.laws AI.oxyloss = O.getOxyLoss() diff --git a/code/game/objects/items/devices/body_snatcher_vr.dm b/code/game/objects/items/devices/body_snatcher_vr.dm index f2c7651e870..e6d20f7b2da 100644 --- a/code/game/objects/items/devices/body_snatcher_vr.dm +++ b/code/game/objects/items/devices/body_snatcher_vr.dm @@ -51,9 +51,9 @@ user_mind.current = null prey_mind.current = null user_mind.active = TRUE //If they are 'active', their client is automatically pushed to the mob - user_mind.transfer_to(M) //This works. Transfers mind & Ckey. + user_mind.transfer(M) //This works. Transfers mind & Ckey. prey_mind.active = TRUE - prey_mind.transfer_to(user) + prey_mind.transfer(user) M.ooc_notes = user_ooc_notes //Let's keep their OOC notes over to their new body. user.ooc_notes = target_ooc_notes user.sleeping = 10 //Device knocks out both the user and the target. diff --git a/code/game/objects/items/devices/scanners_vr.dm b/code/game/objects/items/devices/scanners_vr.dm index d2263dae4b8..d37f06b0ecb 100644 --- a/code/game/objects/items/devices/scanners_vr.dm +++ b/code/game/objects/items/devices/scanners_vr.dm @@ -215,8 +215,8 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob if(!sleevemate_mob) sleevemate_mob = new() - stored_mind.active = TRUE //Setting this causes transfer_to, to key them into the mob - stored_mind.transfer_to(sleevemate_mob) + stored_mind.active = TRUE //Setting this causes transfer, to key them into the mob + stored_mind.transfer(sleevemate_mob) SC.catch_mob(sleevemate_mob) stored_mind = null to_chat(usr,"Mind transferred into Soulcatcher!") @@ -242,7 +242,7 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob to_chat(usr,"\The [src] no longer has a stored mind.") return stored_mind.active = TRUE - stored_mind.transfer_to(target) + stored_mind.transfer(target) stored_mind = null to_chat(usr,"Mind transferred into [target]!") update_icon() diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index ea7e4a48d90..899b8e0e39f 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -204,7 +204,7 @@ O.updatename("Default") if(M.brainmob) - M.brainmob.mind.transfer_to(O) + M.brainmob.mind.transfer(O) if(O.mind && O.mind.special_role) O.mind.store_memory("In case you look at this after being borged, the objectives are only here until I find a way to make them not show up for you, as I can't simply delete them without screwing up round-end reporting. --NeoFite") for(var/datum/language/L in M.brainmob.languages) diff --git a/code/game/objects/structures/ghost_pods/human.dm b/code/game/objects/structures/ghost_pods/human.dm index 92dda473cec..cfa00f1c4cd 100644 --- a/code/game/objects/structures/ghost_pods/human.dm +++ b/code/game/objects/structures/ghost_pods/human.dm @@ -43,7 +43,7 @@ H.adjustCloneLoss(rand(1,5)) if(M.mind) - M.mind.transfer_to(H) + M.mind.transfer(H) to_chat(M, "You are a [occupant_type]!") if(make_antag) to_chat(M, "Your intent may not be completely beneficial.") @@ -169,7 +169,7 @@ H.adjustCloneLoss(rand(1,5)) if(M.mind) - M.mind.transfer_to(H) + M.mind.transfer(H) to_chat(M, "You are a [occupant_type]!") if(make_antag) to_chat(M, "Your intent may not be completely beneficial.") diff --git a/code/game/objects/structures/ghost_pods/mysterious.dm b/code/game/objects/structures/ghost_pods/mysterious.dm index 58206a0c6b0..a3031fd4648 100644 --- a/code/game/objects/structures/ghost_pods/mysterious.dm +++ b/code/game/objects/structures/ghost_pods/mysterious.dm @@ -17,7 +17,7 @@ density = FALSE var/mob/living/simple_mob/animal/passive/dog/corgi/R = new(get_turf(src)) if(M.mind) - M.mind.transfer_to(R) + M.mind.transfer(R) to_chat(M, "You are a Corgi! Woof!") R.ckey = M.ckey visible_message("With a bright flash of light, \the [src] disappears, and in its place stands a small corgi.") diff --git a/code/game/objects/structures/ghost_pods/silicon.dm b/code/game/objects/structures/ghost_pods/silicon.dm index 82b0dfdfb44..84032c20c6d 100644 --- a/code/game/objects/structures/ghost_pods/silicon.dm +++ b/code/game/objects/structures/ghost_pods/silicon.dm @@ -21,7 +21,7 @@ R.adjustBruteLoss(rand(5, 30)) R.adjustFireLoss(rand(5, 10)) if(M.mind) - M.mind.transfer_to(R) + M.mind.transfer(R) // Put this text here before ckey change so that their laws are shown below it, since borg login() shows it. to_chat(M, "You are a Lost Drone, discovered inside the wreckage of your previous home. \ Something has reactivated you, with their intentions unknown to you, and yours unknown to them. They are a foreign entity, \ @@ -50,7 +50,7 @@ density = FALSE var/mob/living/silicon/robot/gravekeeper/R = new(get_turf(src)) if(M.mind) - M.mind.transfer_to(R) + M.mind.transfer(R) // Put this text here before ckey change so that their laws are shown below it, since borg login() shows it. to_chat(M, "You are a Gravekeeper Drone, activated once again to tend to the restful dead.") to_chat(M, "Be sure to examine your currently loaded lawset closely. Remember, your \ @@ -74,7 +74,7 @@ /obj/structure/ghost_pod/ghost_activated/swarm_drone/create_occupant(var/mob/M) var/mob/living/silicon/robot/drone/swarm/R = new drone_type(get_turf(src)) if(M.mind) - M.mind.transfer_to(R) + M.mind.transfer(R) to_chat(M, "You are [R], the remnant of some distant species, mechanical or flesh, living or dead.") R.ckey = M.ckey visible_message("As \the [src] shudders, it glows before lifting itself with three shimmering limbs!") @@ -118,7 +118,7 @@ R.adjustBruteLoss(rand(5, 30)) R.adjustFireLoss(rand(5, 10)) if(M.mind) - M.mind.transfer_to(R) + M.mind.transfer(R) // Put this text here before ckey change so that their laws are shown below it, since borg login() shows it. to_chat(M, "You are a Stray Drone, discovered inside the wreckage of your previous home. \ Something has reactivated you, with their intentions unknown to you, and yours unknown to them. They are a foreign entity, \ diff --git a/code/modules/ghostroles/instantiator.dm b/code/modules/ghostroles/instantiator.dm index b1aa9a5408a..58bc071225a 100644 --- a/code/modules/ghostroles/instantiator.dm +++ b/code/modules/ghostroles/instantiator.dm @@ -2,7 +2,7 @@ * Handles mob creation, equip, and ckey transfer. */ /datum/ghostrole_instantiator - /// traits to add to mob : will be made with GHOSTROLE_TRAIT source + /// traits to add to mob : will be made with ROLE_TRAIT source var/list/mob_traits /datum/ghostrole_instantiator/proc/Run(client/C, atom/location, list/params) @@ -33,7 +33,7 @@ CRASH("Invalid path: [type_to_make]") var/mob/living/L = new type_to_make(location) for(var/trait in mob_traits) - ADD_TRAIT(L, trait, GHOSTROLE_TRAIT) + ADD_TRAIT(L, trait, ROLE_TRAIT) return L /datum/ghostrole_instantiator/simple/proc/GetMobType(client/C, atom/location, list/params) @@ -46,7 +46,7 @@ /datum/ghostrole_instantiator/human/Create(client/C, atom/location, list/params) var/mob/living/carbon/human/H = new(location) for(var/trait in mob_traits) - ADD_TRAIT(H, trait, GHOSTROLE_TRAIT) + ADD_TRAIT(H, trait, ROLE_TRAIT) return H /datum/ghostrole_instantiator/human/Equip(client/C, mob/M, list/params) diff --git a/code/modules/ghostroles/roles/ghost_cafe.dm b/code/modules/ghostroles/roles/ghost_cafe.dm index 44aa3229c80..251cc87ad0d 100644 --- a/code/modules/ghostroles/roles/ghost_cafe.dm +++ b/code/modules/ghostroles/roles/ghost_cafe.dm @@ -24,11 +24,11 @@ if(!..()) return 0 var/mob/M = target - if(HAS_TRAIT_FROM(M,TRAIT_SIXTHSENSE,GHOSTROLE_TRAIT)) - REMOVE_TRAIT(M,TRAIT_SIXTHSENSE,GHOSTROLE_TRAIT) + if(HAS_TRAIT_FROM(M,TRAIT_SIXTHSENSE,ROLE_TRAIT)) + REMOVE_TRAIT(M,TRAIT_SIXTHSENSE,ROLE_TRAIT) to_chat(M,"You're no longer hearing deadchat.") else - ADD_TRAIT(M,TRAIT_SIXTHSENSE,GHOSTROLE_TRAIT) + ADD_TRAIT(M,TRAIT_SIXTHSENSE,ROLE_TRAIT) to_chat(M,"You're once again longer hearing deadchat.") /datum/action/disguise diff --git a/code/modules/ghostroles/roles/golem.dm b/code/modules/ghostroles/roles/golem.dm index 4a28ea35f28..58a71b6bda3 100644 --- a/code/modules/ghostroles/roles/golem.dm +++ b/code/modules/ghostroles/roles/golem.dm @@ -106,7 +106,7 @@ )) if(!created) CRASH("Couldn't make a valid golem, cancelling.") - user.mind.transfer_to(created) + user.mind.transfer(created) user.death() qdel(src) return diff --git a/code/modules/mob/inventory/hands.dm b/code/modules/mob/inventory/hands.dm index 611c62b44ed..c6f1f81b3b3 100644 --- a/code/modules/mob/inventory/hands.dm +++ b/code/modules/mob/inventory/hands.dm @@ -195,3 +195,9 @@ /mob/proc/drop_right_held_item(flags) return drop_held_item_of_index(2, flags) + +/** + * means if we have an empty hand able to accept an arbitrary item. + */ +/mob/proc/has_free_hand() + return FALSE diff --git a/code/modules/mob/living/carbon/alien/progression.dm b/code/modules/mob/living/carbon/alien/progression.dm index 1ac3e86c05d..71e0851eee3 100644 --- a/code/modules/mob/living/carbon/alien/progression.dm +++ b/code/modules/mob/living/carbon/alien/progression.dm @@ -34,7 +34,7 @@ adult.faction = src.faction if(mind) - mind.transfer_to(adult) + mind.transfer(adult) if (can_namepick_as_adult) var/newname = sanitize(input(adult, "You have become an adult. Choose a name for yourself.", "Adult Name") as null|text, MAX_NAME_LEN) diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index c84459f2c39..8c6f67d51cb 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -246,7 +246,7 @@ brainmob.timeofhostdeath = H.timeofdeath brainmob.set_stat(CONSCIOUS) if(H.mind) - H.mind.transfer_to(brainmob) + H.mind.transfer(brainmob) return /obj/item/mmi/digital/attack_self(mob/user) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 2e48fe1e756..f384d930b52 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -908,7 +908,7 @@ if(H.brainmob) if(H.brainmob.real_name == src.real_name) if(H.brainmob.mind) - H.brainmob.mind.transfer_to(src) + H.brainmob.mind.transfer(src) qdel(H) // Reapply markings/appearance from prefs for player mobs diff --git a/code/modules/mob/living/carbon/human/human_powers.dm b/code/modules/mob/living/carbon/human/human_powers.dm index 61d0d4edeaf..fd19f9a9f4d 100644 --- a/code/modules/mob/living/carbon/human/human_powers.dm +++ b/code/modules/mob/living/carbon/human/human_powers.dm @@ -187,7 +187,7 @@ transfer_languages(src, S) if(mind) - mind.transfer_to(S) + mind.transfer(S) message_admins("\The [src] has split into nymphs; player now controls [key_name_admin(S)]") log_admin("\The [src] has split into nymphs; player now controls [key_name(S)]") diff --git a/code/modules/mob/living/carbon/metroid/powers.dm b/code/modules/mob/living/carbon/metroid/powers.dm index 74d74db5612..89b3a9c95d7 100644 --- a/code/modules/mob/living/carbon/metroid/powers.dm +++ b/code/modules/mob/living/carbon/metroid/powers.dm @@ -158,7 +158,7 @@ var/mob/living/carbon/slime/new_slime = pick(babies) new_slime.universal_speak = universal_speak if(src.mind) - src.mind.transfer_to(new_slime) + src.mind.transfer(new_slime) else new_slime.key = src.key qdel(src) diff --git a/code/modules/mob/living/inventory.dm b/code/modules/mob/living/inventory.dm index bc0332249d1..7369dc3608c 100644 --- a/code/modules/mob/living/inventory.dm +++ b/code/modules/mob/living/inventory.dm @@ -252,3 +252,6 @@ /mob/living/has_hands() return has_hands + +/mob/living/has_free_hand() + return !l_hand || !r_hand diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index f522926a222..f845769d2ed 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -194,7 +194,7 @@ var/list/ai_verbs_default = list( return else if (B.brainmob.mind) - B.brainmob.mind.transfer_to(src) + B.brainmob.mind.transfer(src) on_mob_init() diff --git a/code/modules/mob/living/silicon/ai/ai_remote_control.dm b/code/modules/mob/living/silicon/ai/ai_remote_control.dm index 18214094e20..129f461dee1 100644 --- a/code/modules/mob/living/silicon/ai/ai_remote_control.dm +++ b/code/modules/mob/living/silicon/ai/ai_remote_control.dm @@ -43,7 +43,7 @@ soul_link(/datum/soul_link/shared_body, src, target) deployed_shell = target target.deploy_init(src) - mind.transfer_to(target) + mind.transfer(target) teleop = target // So the AI 'hears' messages near its core. target.post_deploy() diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 1488f555de0..9f14583d92c 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -345,7 +345,7 @@ src.ckey = player.ckey if(player.mob && player.mob.mind) - player.mob.mind.transfer_to(src) + player.mob.mind.transfer(src) lawupdate = 0 to_chat(src, "Systems rebooted. Loading base pattern maintenance protocol... loaded.") diff --git a/code/modules/mob/living/silicon/robot/drone/drone_remote_control.dm b/code/modules/mob/living/silicon/robot/drone/drone_remote_control.dm index bd2b5739672..7bfc736ba38 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_remote_control.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_remote_control.dm @@ -27,7 +27,7 @@ speech_synthesizer_langs = controlling_ai.speech_synthesizer_langs.Copy() set_stat(CONSCIOUS) if(user.mind) - user.mind.transfer_to(src) + user.mind.transfer(src) else key = user.key updatename() @@ -65,7 +65,7 @@ new_drone.speech_synthesizer_langs = new_drone.controlling_ai.speech_synthesizer_langs.Copy() if(user.mind) - user.mind.transfer_to(new_drone) + user.mind.transfer(new_drone) else new_drone.key = user.key new_drone.updatename() @@ -83,7 +83,7 @@ if(controlling_ai) if(mind) - mind.transfer_to(controlling_ai) + mind.transfer(controlling_ai) else controlling_ai.key = key to_chat(controlling_ai, "[message]") diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 02c9753cd19..186676f5226 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -292,7 +292,7 @@ else mmi.brainmob.languages = languages mmi.brainmob.remove_language("Robot Talk") - mind.transfer_to(mmi.brainmob) + mind.transfer(mmi.brainmob) else if(!shell) // Shells don't have brainmbos in their MMIs. to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") ghostize() diff --git a/code/modules/mob/living/silicon/robot/robot_remote_control.dm b/code/modules/mob/living/silicon/robot/robot_remote_control.dm index 39bb68d6fc0..3f500fbca25 100644 --- a/code/modules/mob/living/silicon/robot/robot_remote_control.dm +++ b/code/modules/mob/living/silicon/robot/robot_remote_control.dm @@ -84,7 +84,7 @@ GLOBAL_LIST_EMPTY(available_ai_shells) return if(message) to_chat(src, SPAN_NOTICE(message)) - mind.transfer_to(mainframe) + mind.transfer(mainframe) deployed = FALSE updateicon() mainframe.teleop = null diff --git a/code/modules/mob/living/simple_animal/animals/spiderbot.dm b/code/modules/mob/living/simple_animal/animals/spiderbot.dm index 59dc823c869..0c6226ee5b6 100644 --- a/code/modules/mob/living/simple_animal/animals/spiderbot.dm +++ b/code/modules/mob/living/simple_animal/animals/spiderbot.dm @@ -195,7 +195,7 @@ var/turf/T = get_turf(loc) if(T) mmi.forceMove(T) - if(mind) mind.transfer_to(mmi.brainmob) + if(mind) mind.transfer(mmi.brainmob) mmi = null real_name = initial(real_name) name = real_name diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 46c40907e50..59203145621 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -363,7 +363,7 @@ var/mob/living/simple_animal/slime/new_slime = pick(babies) new_slime.universal_speak = universal_speak if(src.mind) - src.mind.transfer_to(new_slime) + src.mind.transfer(new_slime) else new_slime.key = src.key qdel(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm index b9e3aef02d4..3171656b468 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm @@ -176,7 +176,7 @@ if(src.mind) src.mind.special_role = "Borer Husk" - src.mind.transfer_to(host) + src.mind.transfer(host) H.ChangeToHusk() diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm index 3ed39cfad9b..708f680e3cc 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm @@ -220,7 +220,7 @@ var/mob/living/simple_mob/slime/new_slime = pick(babies) new_slime.universal_speak = universal_speak if(src.mind) - src.mind.transfer_to(new_slime) + src.mind.transfer(new_slime) else new_slime.key = src.key qdel(src) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index d4fca3bfa56..780c1a0b51c 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -27,6 +27,8 @@ var/datum/atom_hud/alternate_appearance/AA = v AA.onNewMob(src) hook_vr("mob_new",list(src)) + // abilities + init_abilities() // inventory init_inventory() // rendering @@ -80,8 +82,18 @@ qdel(spell_master) remove_screen_obj_references() client.screen = list() - if(mind && mind.current == src) - spellremove(src) + // mind + if(!isnull(mind)) + if(mind.current == src) + // mind is ours, let it disassociate + // todo: legacy spell + spellremove(src) + mind?.disassociate() + else + // mind is not ours, null it out + mind = null + // abilities + dispose_abilities() // this kicks out client ghostize() if(hud_used) @@ -1119,7 +1131,8 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) /mob/z_pass_out(atom/movable/AM, dir, turf/new_loc) return TRUE -//! Pixel Offsets +//? Pixel Offsets + /mob/proc/get_buckled_pixel_x_offset() if(!buckled) return 0 @@ -1186,14 +1199,16 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) pixel_y += val SEND_SIGNAL(src, COMSIG_MOVABLE_PIXEL_OFFSET_CHANGED) -//! Reachability +//? Reachability + /mob/CanReachOut(atom/movable/mover, atom/target, obj/item/tool, list/cache) return FALSE /mob/CanReachIn(atom/movable/mover, atom/target, obj/item/tool, list/cache) return FALSE -//! Radioactivity +//? Radioactivity + /mob/clean_radiation(str, mul, cheap) . = ..() if(cheap) @@ -1201,6 +1216,38 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) for(var/obj/item/I as anything in get_equipped_items(TRUE, TRUE)) I.clean_radiation(str, mul, cheap) +//? Abilities + +/mob/proc/init_abilities() + var/list/built = list() + var/list/registering = list() + for(var/datum/ability/ability_path as anything in abilities) + if(istype(ability_path)) + built += ability_path // don't re-associate existing ones. + else if(ispath(ability_path, /datum/ability)) + registering += new ability_path + abilities = built + for(var/datum/ability/ability as anything in registering) + ability.associate(src) + +/mob/proc/dispose_abilities() + for(var/datum/ability/ability in abilities) + ability.disassociate(src) + abilities = null + +/** + * mob side registration of abilities. must be called from /datum/ability/proc/associate! + */ +/mob/proc/register_ability(datum/ability/ability) + LAZYINITLIST(abilities) + abilities += ability + +/** + * mob side unregistration of abilities. must be called from /datum/ability/proc/disassociate! + */ +/mob/proc/unregister_ability(datum/ability/ability) + LAZYREMOVE(abilities, ability) + //! Misc /** * Whether the mob can use Topic to interact with machines diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 310485a6e4b..07a858a9f3c 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -78,6 +78,10 @@ /// shifted pixel y var/shift_pixel_y = 0 + //? Abilities + /// our abilities - set to list of paths to init to intrinsic abilities. + var/list/datum/ability/abilities + //? Inventory /// our inventory datum, if any. var/datum/inventory/inventory diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm index d09bb6693ee..a3bc7272535 100644 --- a/code/modules/mob/mob_transformation_simple.dm +++ b/code/modules/mob/mob_transformation_simple.dm @@ -44,7 +44,7 @@ M.dna = src.dna.Clone() if(mind) - mind.transfer_to(M) + mind.transfer(M) else M.key = key diff --git a/code/modules/mob/new_player/login.dm b/code/modules/mob/new_player/login.dm index 192471fa842..77592ec6fc4 100644 --- a/code/modules/mob/new_player/login.dm +++ b/code/modules/mob/new_player/login.dm @@ -29,7 +29,7 @@ GLOBAL_DATUM_INIT(lobby_image, /obj/effect/lobby_image, new) to_chat(src, client.getAlertDesc()) if(!mind) - mind = new /datum/mind(key) + mind = new /datum/mind(ckey) mind.active = 1 mind.current = src diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index efe293de7aa..a7146ab65f2 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -629,7 +629,7 @@ //mind.traits = client.prefs.traits.Copy() // Conflict //! Preferences shim: transfer stuff over client.prefs.imprint_mind(mind) - mind.transfer_to(new_character) // Won't transfer key since the mind is not active + mind.transfer(new_character) // Won't transfer key since the mind is not active new_character.name = real_name new_character.dna.ready_dna(new_character) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 8b05661d4fe..cad3cbaecb4 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -66,7 +66,7 @@ O.aiRestorePowerRoutine = 0 if(mind) - mind.transfer_to(O) + mind.transfer(O) O.mind.original = O else O.key = key @@ -122,7 +122,7 @@ O.invisibility = 0 if(mind) //TODO - mind.transfer_to(O) + mind.transfer(O) if(O.mind && O.mind.assigned_role == "Cyborg") O.mind.original = O if(O.mind.role_alt_title == "Drone") diff --git a/code/modules/nanites/chamber.dm b/code/modules/nanites/chamber.dm index 3b01b717fd0..ff2a489e145 100644 --- a/code/modules/nanites/chamber.dm +++ b/code/modules/nanites/chamber.dm @@ -184,7 +184,7 @@ var/mob/living/carbon/human/new_protean = new(src) new_protean.set_species(/datum/species/protean, force = TRUE) new_protean.real_name = protean_core.brainmob.mind.name - protean_core.brainmob.mind.transfer_to(new_protean) + protean_core.brainmob.mind.transfer(new_protean) // todo: organ / species rework var/obj/item/organ/external/their_chest = new_protean.organs_by_name[BP_TORSO] var/datum/robolimb/nt_path = /datum/robolimb/nanotrasen diff --git a/code/modules/nifsoft/software/13_soulcatcher.dm b/code/modules/nifsoft/software/13_soulcatcher.dm index 4f1ab28e728..2e87981751a 100644 --- a/code/modules/nifsoft/software/13_soulcatcher.dm +++ b/code/modules/nifsoft/software/13_soulcatcher.dm @@ -302,7 +302,7 @@ //Put the mind and player into the mob // login should handle the perspective reset, now that nif is set. - M.mind.transfer_to(brainmob) + M.mind.transfer(brainmob) brainmob.name = brainmob.mind.name brainmob.real_name = brainmob.mind.name diff --git a/code/modules/organs/internal/subtypes/brain.dm b/code/modules/organs/internal/subtypes/brain.dm index 185bea4d987..4d4c5cf1c83 100644 --- a/code/modules/organs/internal/subtypes/brain.dm +++ b/code/modules/organs/internal/subtypes/brain.dm @@ -93,7 +93,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) brainmob.add_modifier(M.type) if(H.mind) - H.mind.transfer_to(brainmob) + H.mind.transfer(brainmob) brainmob.languages = H.languages @@ -131,7 +131,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) if(brainmob) if(brainmob.mind) - brainmob.mind.transfer_to(target) + brainmob.mind.transfer(target) else target.ckey = brainmob.ckey ..() @@ -265,7 +265,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) qdel(E) E = null H.regenerate_icons() - clonemind.transfer_to(H) + clonemind.transfer(H) for(var/modifier_type in R.genetic_modifiers) H.add_modifier(modifier_type) diff --git a/code/modules/organs/subtypes/machine.dm b/code/modules/organs/subtypes/machine.dm index 16d068f437f..ff476af9db9 100644 --- a/code/modules/organs/subtypes/machine.dm +++ b/code/modules/organs/subtypes/machine.dm @@ -89,7 +89,7 @@ . = stored_mmi stored_mmi.forceMove(drop_location()) if(owner.mind) - owner.mind.transfer_to(stored_mmi.brainmob) + owner.mind.transfer(stored_mmi.brainmob) ..() qdel(src) diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/change.dm index 1162957fb57..866989306a4 100644 --- a/code/modules/projectiles/projectile/change.dm +++ b/code/modules/projectiles/projectile/change.dm @@ -84,7 +84,7 @@ new_mob.a_intent = "hurt" if(M.mind) - M.mind.transfer_to(new_mob) + M.mind.transfer(new_mob) else new_mob.key = M.key diff --git a/code/modules/projectiles/unsorted/magic.dm b/code/modules/projectiles/unsorted/magic.dm index da916b7e44c..a9cdd126f4b 100644 --- a/code/modules/projectiles/unsorted/magic.dm +++ b/code/modules/projectiles/unsorted/magic.dm @@ -272,7 +272,7 @@ S.color = P.color S.atom_colours = P.atom_colours.Copy() if(L.mind) - L.mind.transfer_to(S) + L.mind.transfer(S) if(owner) to_chat(S, "You are an animated statue. You cannot move when monitored, but are nearly invincible and deadly when unobserved! Do not harm [owner], your creator.") P.forceMove(S) diff --git a/code/modules/random_map/drop/droppod.dm b/code/modules/random_map/drop/droppod.dm index 2b6f735677e..0e0d53e51a1 100644 --- a/code/modules/random_map/drop/droppod.dm +++ b/code/modules/random_map/drop/droppod.dm @@ -211,7 +211,7 @@ var/automatic_pod if(spawned_mob && selected_player) if(selected_player.mob.mind) - selected_player.mob.mind.transfer_to(spawned_mob) + selected_player.mob.mind.transfer(spawned_mob) else spawned_mob.ckey = selected_player.mob.ckey spawned_mobs = list(spawned_mob) diff --git a/code/modules/resleeving/machines.dm b/code/modules/resleeving/machines.dm index c8c26768da0..4301b275598 100644 --- a/code/modules/resleeving/machines.dm +++ b/code/modules/resleeving/machines.dm @@ -547,7 +547,7 @@ for(var/datum/language/L in MR.languages) occupant.add_language(L.name) MR.mind_ref.active = 1 //Well, it's about to be. - MR.mind_ref.transfer_to(occupant) //Does mind+ckey+client. + MR.mind_ref.transfer(occupant) //Does mind+ckey+client. occupant.identifying_gender = MR.id_gender occupant.ooc_notes = MR.mind_oocnotes occupant.apply_vore_prefs() //Cheap hack for now to give them SOME bellies. diff --git a/code/modules/resleeving/sleevecard.dm b/code/modules/resleeving/sleevecard.dm index 650e3408463..0400c0a6502 100644 --- a/code/modules/resleeving/sleevecard.dm +++ b/code/modules/resleeving/sleevecard.dm @@ -59,7 +59,7 @@ for(var/datum/language/L in MR.languages) infomorph.add_language(L.name) MR.mind_ref.active = 1 //Well, it's about to be. - MR.mind_ref.transfer_to(infomorph) //Does mind+ckey+client. + MR.mind_ref.transfer(infomorph) //Does mind+ckey+client. infomorph.ooc_notes = MR.mind_oocnotes infomorph.apply_vore_prefs() //Cheap hack for now to give them SOME bellies. diff --git a/code/modules/species/abilites.dm b/code/modules/species/abilites.dm new file mode 100644 index 00000000000..909952166cf --- /dev/null +++ b/code/modules/species/abilites.dm @@ -0,0 +1,2 @@ +/datum/ability/species + abstract_type = /datum/ability/species diff --git a/code/modules/species/protean/protean.dm b/code/modules/species/protean/protean.dm index f53b47dedd6..6cca8869a8d 100644 --- a/code/modules/species/protean/protean.dm +++ b/code/modules/species/protean/protean.dm @@ -115,16 +115,16 @@ /mob/living/carbon/human/proc/rig_transform, /mob/living/proc/usehardsuit) //prots get all the special verbs since they can't select traits. species_statpanel = TRUE - var/global/list/abilities = list() + var/global/list/protean_abilities = list() var/monochromatic = FALSE //IGNORE ME /datum/species/protean/New() ..() - if(!LAZYLEN(abilities)) + if(!LAZYLEN(protean_abilities)) var/list/powertypes = subtypesof(/obj/effect/protean_ability) for(var/path in powertypes) - abilities += new path() + protean_abilities += new path() /datum/species/protean/create_organs(var/mob/living/carbon/human/H) var/obj/item/nif/saved_nif = H.nif @@ -229,7 +229,7 @@ STATPANEL_DATA_LINE("- -- --- REFACTORY ERROR! --- -- -") STATPANEL_DATA_LINE("- -- --- Abilities (Shift+LMB Examines) --- -- -") - for(var/ability in abilities) + for(var/ability in protean_abilities) var/obj/effect/protean_ability/A = ability A.atom_button_text() STATPANEL_DATA_CLICK("[icon2html(A, C)] [A.ability_name]", "[A.name]", "\ref[A]") diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index 2e8d3ac8ab8..6982f449d01 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -18,11 +18,10 @@ * - A global cache of species by typepath will still be maintained for "static" usages of these datums, like for preferences rendering. */ /datum/species - /// Abstract type. abstract_type = /datum/species - ///////////////////////////////////////////////////////////// - //// Basic info, should always be at the top of the file //// - ///////////////////////////////////////////////////////////// + + //? Intrinsic Information + /// uid - **must be unique** - Identifies the exact species you are using var/uid /// id usually identical to uid, if we are a subspecies we use the parent species id/uid here @@ -41,7 +40,7 @@ ///Used for metabolizing reagents. var/reagent_tag - //// Additional info + //? Additional info /// what you see on tooltip/examine var/examine_name /// what you see on health analyzers/IC @@ -55,16 +54,14 @@ /// descriptors - Bay's height and fitness difference system var/list/descriptors = list() - //// Gender stuff + //? Gender stuff var/list/genders = list(MALE, FEMALE) /// If true, people examining a member of this species whom are not also the same species will see them as gender neutral. Because aliens. var/ambiguous_genders = FALSE - /// Organ tag where they are located if they can be kicked for increased pain. var/sexybits_location = BP_GROIN // Come on... You know it's there for most of them. - //// HUD - //! ## HUD data vars. + //? HUD var/datum/hud_data/hud var/hud_type /// This modifies how intensely the health hud is colored. @@ -72,7 +69,7 @@ /// do we have a species statpanel? var/species_statpanel = FALSE - //// Icons + //? Icons /// Normal icon set. var/icobase = 'icons/mob/species/human/body.dmi' /// Mutated icon set. @@ -83,15 +80,22 @@ var/husk_icon = 'icons/mob/species/default_husk.dmi' /// Used for mob icon generation for non-32x32 species. var/icon_template = 'icons/mob/species/template.dmi' + /// Makes the icon wider/thinner. + var/icon_scale_x = 1 + /// Makes the icon taller/shorter. + var/icon_scale_y = 1 + /// Used for offsetting large icons. + var/pixel_offset_x = 0 + /// Used for offsetting large icons. + var/pixel_offset_y = 0 - //// Overlays + //? Overlays /// Used by changelings. Should also be used for icon previews. var/base_color /// This is used in character setup preview generation (prefences_setup.dm) and human mob rendering (update_icons.dm) var/color_mult = 0 /// force non greyscale icons to greyscale before multiplying? WARNING :CITADEL JANK, REPLACE ASAP var/color_force_greyscale = FALSE - var/damage_overlays = 'icons/mob/species/human/damage_overlay.dmi' var/damage_mask = 'icons/mob/species/human/damage_mask.dmi' var/blood_mask = 'icons/mob/species/human/blood_mask.dmi' @@ -100,22 +104,11 @@ /// Icons used for worn items in suit storage slot. var/suit_storage_icon = 'icons/mob/clothing/belt_mirror.dmi' - //// Scale - /// Makes the icon wider/thinner. - var/icon_scale_x = 1 - /// Makes the icon taller/shorter. - var/icon_scale_y = 1 - //// Offset - /// Used for offsetting large icons. - var/pixel_offset_x = 0 - /// Used for offsetting large icons. - var/pixel_offset_y = 0 - - //// Alternate appearance + //? Alternate appearance /// The basic skin colours this species uses. var/list/base_skin_colours - //// Tail + //? Tail /// Name of tail state in species effects icon file. var/tail /// If set, the icon to obtain tail animation states from. @@ -124,13 +117,13 @@ /// This is for overriding tail rendering with a specific icon in icobase, for static tails only, since tails would wag when dead if you used this. var/icobase_tail = 0 - //// Wing + //? Wing var/wing_hair var/wing var/wing_animation var/icobase_wing - //// Organs + //? Organs /// Determines the organs that the species spawns with and which required-organ checks are conducted. var/list/has_organ = list( O_HEART = /obj/item/organ/internal/heart, @@ -158,11 +151,11 @@ BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) ) - //// Speech + //? Speech /// Part of icon_state to use for speech bubbles when talking. See talk.dmi for available icons. var/speech_bubble_appearance = "normal" - //// Sounds + //? Sounds var/scream_verb = "screams" var/male_scream_sound = list('sound/voice/screams/sound_voice_scream_scream_m1.ogg', 'sound/voice/screams/sound_voice_scream_scream_m2.ogg') var/female_scream_sound = list('sound/voice/screams/sound_voice_scream_scream_f1.ogg', 'sound/voice/screams/sound_voice_scream_scream_f2.ogg', 'sound/voice/screams/sound_voice_scream_scream_f3.ogg') @@ -176,13 +169,13 @@ /// The likelihood of a speech sound playing. var/list/speech_chance = list() - //// Age + //? Age /// The minimum age a species is allowed to be played as. For our purposes, this is global. var/min_age = 18 /// The maximum age a species is allowed to be played as. This is generally determined by lifespan. var/max_age = 70 - //// Languages + //? Languages /// default language used when speaking - typepaths are allowed var/default_language = LANGUAGE_ID_COMMON /// do we have galactic common? this is so common we just have this as a var @@ -201,7 +194,7 @@ /// This list is a guess at things that no one other than the parent species should be able to speak var/list/assisted_langs = list(LANGUAGE_EAL, LANGUAGE_SKRELLIAN, LANGUAGE_SKRELLIANFAR, LANGUAGE_ROOTLOCAL, LANGUAGE_ROOTGLOBAL, LANGUAGE_VOX) - //// Cultures + //? Cultures /// default origin var/default_origin = /datum/lore/character_background/origin/custom /// default citizenship @@ -213,7 +206,7 @@ /// default culture var/default_culture = /datum/lore/character_background/culture/custom - //// Flags + //? Flags /// Various specific features. var/species_flags = NONE /// Flags that specify who can spawn as this species @@ -239,8 +232,7 @@ var/pass_flags = 0 - - //// Stats + //? Stats /// Point at which the mob will enter crit. var/total_health = 100 /// Physical damage multiplier. @@ -265,7 +257,7 @@ var/siemens_coefficient = 1 var/virus_immune - //// Blood + //? Blood /// What marks are left when walking var/obj/effect/debris/cleanable/blood/tracks/move_trail = /obj/effect/debris/cleanable/blood/tracks/footprints @@ -301,7 +293,7 @@ /// Can eat some mobs. 1 for mice, 2 for monkeys, 3 for people. var/gluttonous - //// Sight + //? Sight /// Native darksight distance. var/darksight = 2 /// Permanent weldervision. @@ -311,7 +303,7 @@ /// If set, the species will be affected by flashbangs regardless if they have eyes or not, as they see in large areas. var/dispersed_eyes - //// Attacks + //? Attacks /// Possible unarmed attacks that the mob will use in combat, var/list/unarmed_types = list( /datum/unarmed_attack, @@ -320,12 +312,15 @@ /// For empty hand harm-intent attack var/list/unarmed_attacks = null + //? Abilities + Legacy Verbs / Spells + /// abilities - typepaths, new'd on init. + var/list/datum/ability/abilities /// Species-specific verbs. var/list/inherent_verbs = list() /// Species-specific spells. var/list/inherent_spells = list() - //// Movement + //? Movement /// Passive movement speed malus (or boost, if negative) var/slowdown = 0 /// How much faster or slower the species is in water @@ -335,17 +330,17 @@ /// How affected by item slowdown the species is. var/item_slowdown_mod = 1 - //// Special condition + //? Special condition /// Multiplier for 'Regenerate' power speed, in human_powers.dm var/active_regen_mult = 1 /// If set, mob will be damaged in light over this value and heal in light below its negative. var/light_dam - //// Special Traits + //? Special Traits var/is_vampire = FALSE // If this is set to true, the person can't get nutrition from food. var/is_cyberpsycho = FALSE // If you turn this to true, the person's capacity stat decreases. (aka - Their symptoms worsen) - //// Breath + //? Breath /// Non-oxygen gas breathed, if any. var/breath_type = /datum/gas/oxygen /// Poisonous air. @@ -358,7 +353,7 @@ /// Species will gain this much temperature every second var/passive_temp_gain = 0 - //// Cold Air + //? Cold Air /// Cold damage level 1 below this point. var/cold_level_1 = 260 /// Cold damage level 2 below this point. @@ -381,7 +376,7 @@ "Your chilly flesh stands out in goosebumps." ) - //// Hot Air + //? Hot Air /// Heat damage level 1 above this point. var/heat_level_1 = 360 /// Heat damage level 2 above this point. @@ -404,7 +399,7 @@ "Your skin prickles in the heat." ) - //// Pressure + //? Pressure /// Dangerously high pressure. var/hazard_high_pressure = HAZARD_HIGH_PRESSURE /// High pressure warning. @@ -420,7 +415,7 @@ /// Minimum required pressure for breath, in kPa var/minimum_breath_pressure = 16 - //// Custom species stuff + //? Custom species stuff var/lightweight = FALSE //Oof! Nonhelpful bump stumbles. var/trashcan = FALSE //It's always sunny in the wrestling ring. var/base_species = null // Unused outside of a few species @@ -430,13 +425,17 @@ /// traits var/list/traits = list() - //// Misc + //? Misc var/mob_size = MOB_MEDIUM var/show_ssd = "fast asleep" /// This allows you to pick up crew var/holder_type = /obj/item/holder/micro - //// on death drops + //? Traits + /// Intrinsic datum traits to apply to the mob + var/list/mob_traits + + //? on death drops /// The color of the species flesh. var/flesh_color = "#FFC896" var/meat_type = /obj/item/reagent_containers/food/snacks/meat/human @@ -445,7 +444,7 @@ var/exotic_type = /obj/item/stack/sinew var/remains_type = /obj/effect/decal/remains/xeno - //// Dying + //? Dying var/gibbed_anim = "gibbed-h" var/dusted_anim = "dust-h" var/death_sound @@ -453,7 +452,7 @@ var/knockout_message = "has been knocked unconscious!" var/cloning_modifier = /datum/modifier/cloning_sickness - //// Primitive species + //? Primitive species /// Lesser form, if any (ie. monkey for humans) var/primitive_form /// Greater form, if any, ie. human for monkeys. @@ -461,12 +460,10 @@ /// Can use small items. var/has_fine_manipulation = TRUE - ////// OLD Vars - + //? OLD Vars /// Whether the species can infect wounds, only works with claws / bites var/infect_wounds = 0 - /datum/species/New() if(hud_type) hud = new hud_type() @@ -495,6 +492,16 @@ inherent_verbs = list() inherent_verbs |= /mob/living/carbon/human/proc/regurgitate + if(abilities) + var/list/built = list() + for(var/path in abilities) + if(istype(path, /datum/ability)) + built += path + else if(!ispath(path)) + continue + built += new path + abilities = built + /** * called when we apply to a mob * @@ -524,6 +531,12 @@ var/datum/trait/T = all_traits[name] T.apply(src, H) + for(var/trait in mob_traits) + ADD_TRAIT(H, trait, SPECIES_TRAIT) + + for(var/datum/ability/ability as anything in abilities) + ability.associate(H) + /** * called when we are removed from a mob */ @@ -542,6 +555,12 @@ var/datum/trait/T = all_traits[name] T.remove(src, H) + for(var/trait in mob_traits) + REMOVE_TRAIT(H, trait, SPECIES_TRAIT) + + for(var/datum/ability/ability as anything in abilities) + ability.disassociate(H) + /datum/species/proc/sanitize_species_name(var/name) return sanitizeName(name, MAX_NAME_LEN) diff --git a/code/modules/species/station/diona.dm b/code/modules/species/station/diona.dm index d58b1fc59c8..ca10c24541d 100644 --- a/code/modules/species/station/diona.dm +++ b/code/modules/species/station/diona.dm @@ -123,7 +123,7 @@ var/mob/living/carbon/alien/diona/S = new(get_turf(H)) if(H.mind) - H.mind.transfer_to(S) + H.mind.transfer(S) if(H.isSynthetic()) H.visible_message(SPAN_DANGER("\The [H] collapses into parts, revealing a solitary diona nymph at the core.")) diff --git a/code/modules/species/xenochimera/xenochimera.dm b/code/modules/species/station/xenochimera.dm similarity index 90% rename from code/modules/species/xenochimera/xenochimera.dm rename to code/modules/species/station/xenochimera.dm index 8a065c45e31..b45a4cb0862 100644 --- a/code/modules/species/xenochimera/xenochimera.dm +++ b/code/modules/species/station/xenochimera.dm @@ -107,6 +107,10 @@ /datum/unarmed_attack/bite/sharp, ) + abilities = list( + /datum/ability/species/xenochimera/regenerate, + ) + inherent_verbs = list( //Xenochimera get all the special verbs since they can't select traits. /mob/living/carbon/human/proc/sonar_ping, /mob/living/carbon/human/proc/succubus_drain, @@ -141,7 +145,6 @@ inherent_spells = list( /spell/targeted/chimera/thermal_sight, /spell/targeted/chimera/voice_mimic, - /spell/targeted/chimera/regenerate, /spell/targeted/chimera/hatch, /spell/targeted/chimera/no_breathe, ) @@ -656,3 +659,86 @@ SPAN_DANGER("[src] pulls the tendrils out!"), SPAN_WARNING("The sensation fades. You feel made anew."), ) + +//? Abilities + +/datum/ability/species/xenochimera + abstract_type = /datum/ability/species/xenochimera + category = "Xenochimera" + ability_check_flags = NONE + always_bind = TRUE + action_icon = 'icons/screen/actions/changeling.dmi' + + var/nutrition_cost_minimum = 50 + var/nutrition_cost_proportional = 20 //percentage of nutriment it should cost if it's higher than the minimum + var/nutrition_enforced = FALSE + + +/datum/ability/species/xenochimera/check_trigger(mob/user, toggling) + . = ..() + if(!.) + return + if(!ishuman(owner)) + return FALSE + var/mob/living/carbon/human/H = owner + if(nutrition_enforced) + if((nutrition_cost_minimum > H.nutrition) || nutrition_cost_minimum > ((H.nutrition * nutrition_cost_proportional) / 100) ) + to_chat(user,"We don't have enough nutriment. This ability is costly...") + return FALSE + + +/datum/ability/species/xenochimera/on_trigger(mob/user, toggling) + . = ..() + if(!ishuman(owner)) + return + var/mob/living/carbon/human/H = owner + var/nut = (H.nutrition * nutrition_cost_proportional) / 100 + var/final_cost + if(nut > nutrition_cost_minimum) + final_cost = nut + else + final_cost = nutrition_cost_minimum + + if((H.nutrition - final_cost) >= 0) + H.nutrition -= final_cost + else + H.nutrition = 0 //We're already super starved, and feral, so cast it for free, you're likely using it to get food at this point. + +/datum/ability/species/xenochimera/regenerate + name = "Regeneration" + desc = "We shed our skin, purging it of damage, regrowing limbs." + action_state = "fleshmend" + nutrition_cost_minimum = 500 + nutrition_cost_proportional = 75 + nutrition_enforced = TRUE + cooldown = 1 MINUTE + windup = 10 SECONDS + var/healing_amount = 60 + +/datum/ability/species/xenochimera/regenerate/on_trigger() + . = ..() + if(!ishuman(owner)) + return + var/mob/living/carbon/human/H = owner + H.restore_blood() + H.species.create_organs(H) + H.restore_all_organs() + H.adjustBruteLoss(-healing_amount) + H.adjustFireLoss(-healing_amount) + H.adjustOxyLoss(-healing_amount) + H.adjustCloneLoss(-healing_amount) + H.adjustBrainLoss(-healing_amount) + H.blinded = FALSE + H.SetBlinded(FALSE) + H.eye_blurry = FALSE + H.ear_deaf = FALSE + H.ear_damage = FALSE + + H.regenerate_icons() + + playsound(H, 'sound/effects/blobattack.ogg', 30, 1) + var/T = get_turf(src) + new /obj/effect/gibspawner/human(T, H.dna,H.dna.blood_color,H.dna.blood_color) + H.visible_message("With a sickening squish, [src] reforms their whole body, casting their old parts on the floor!", + "We reform our body. We are whole once more.", + "You hear organic matter ripping and tearing!") diff --git a/code/modules/species/virtual_reality/avatar.dm b/code/modules/species/virtual_reality/avatar.dm index 44f0daffdb0..3ba4ee14e3b 100644 --- a/code/modules/species/virtual_reality/avatar.dm +++ b/code/modules/species/virtual_reality/avatar.dm @@ -79,7 +79,7 @@ // Move the mind avatar.Sleeping(1) - src.mind.transfer_to(avatar) + src.mind.transfer(avatar) to_chat(avatar, "You have enterred Virtual Reality!\nAll normal gameplay rules still apply.\nWounds you suffer here won't persist when you leave VR, but some of the pain will.\nYou can leave VR at any time by using the \"Exit Virtual Reality\" verb in the Abilities tab, or by ghosting.\nYou can modify your appearance by using various \"Change \[X\]\" verbs in the Abilities tab.") to_chat(avatar, " You black out for a moment, and wake to find yourself in a new body in virtual reality.") // So this is what VR feels like? @@ -101,7 +101,7 @@ // Move the mind back to the original mob // vr_holder.Sleeping(1) - src.mind.transfer_to(vr_holder) + src.mind.transfer(vr_holder) to_chat(vr_holder, "You black out for a moment, and wake to find yourself back in your own body.") // Two-thirds damage is transferred as agony for /humans // Getting hurt in VR doesn't damage the physical body, but you still got hurt. diff --git a/code/modules/spells/spellbook.dm b/code/modules/spells/spellbook.dm index 7eec3368fd1..5590fec37c5 100644 --- a/code/modules/spells/spellbook.dm +++ b/code/modules/spells/spellbook.dm @@ -358,14 +358,14 @@ var/mob/observer/dead/ghost = stored_swap.ghostize(0) ghost.spell_list = stored_swap.spell_list - user.mind.transfer_to(stored_swap) + user.mind.transfer(stored_swap) stored_swap.spell_list = user.spell_list if(stored_swap.mind.special_verbs.len) for(var/V in user.mind.special_verbs) add_verb(user, V) - ghost.mind.transfer_to(user) + ghost.mind.transfer(user) user.key = ghost.key user.spell_list = ghost.spell_list diff --git a/code/modules/spells/targeted/chimera_spells.dm b/code/modules/spells/targeted/chimera_spells.dm index 1c7d1e7457f..9e4317704dc 100644 --- a/code/modules/spells/targeted/chimera_spells.dm +++ b/code/modules/spells/targeted/chimera_spells.dm @@ -112,69 +112,6 @@ else return - -//////////////// -//Regeneration// -//////////////// -//Huge cooldown, huge cost, but will actually heal most of your issues. - -/spell/targeted/chimera/regenerate - name = "Regeneration" - desc = "We shed our skin, purging it of damage, regrowing limbs." - - spell_flags = INCLUDEUSER - hud_state = "ling_fleshmend" - invocation = "none" - invocation_type = SpI_NONE - charge_max = 10 MINUTES - duration = 0 - nutrition_cost_minimum = 500 - nutrition_cost_proportional = 75 - var/healing_amount = 60 - var/delay = 1 MINUTE - - -/spell/targeted/chimera/regenerate/cast_check(skipcharge = 0,mob/user = usr) - if(..()) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if((nutrition_cost_minimum > H.nutrition) || nutrition_cost_minimum > ((H.nutrition * nutrition_cost_proportional) / 100) ) - to_chat(H,"We don't have enough nutriment. This ability is costly...") - return FALSE - else return TRUE - -/spell/targeted/chimera/regenerate/cast(list/targets, mob/user = usr) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(do_after(H, delay, null, FALSE, TRUE, INCAPACITATION_DISABLED)) - H.restore_blood() - H.species.create_organs(H) - H.restore_all_organs() - H.adjustBruteLoss(-healing_amount) - H.adjustFireLoss(-healing_amount) - H.adjustOxyLoss(-healing_amount) - H.adjustCloneLoss(-healing_amount) - H.adjustBrainLoss(-healing_amount) - H.blinded = FALSE - H.SetBlinded(FALSE) - H.eye_blurry = FALSE - H.ear_deaf = FALSE - H.ear_damage = FALSE - - H.regenerate_icons() - - playsound(H, 'sound/effects/blobattack.ogg', 30, 1) - var/T = get_turf(src) - new /obj/effect/gibspawner/human(T, H.dna,H.dna.blood_color,H.dna.blood_color) - H.visible_message("With a sickening squish, [src] reforms their whole body, casting their old parts on the floor!", - "We reform our body. We are whole once more.", - "You hear organic matter ripping and tearing!") - ..() - else - to_chat(user,"We were interrupted!") - charge_counter = 9.8 MINUTES - - //////////////// //Revive spell// //////////////// diff --git a/code/modules/spells/targeted/mind_transfer.dm b/code/modules/spells/targeted/mind_transfer.dm index 37c6720fce4..5da0396dca4 100644 --- a/code/modules/spells/targeted/mind_transfer.dm +++ b/code/modules/spells/targeted/mind_transfer.dm @@ -50,7 +50,7 @@ var/mob/observer/dead/ghost = victim.ghostize(0) ghost.spell_list += victim.spell_list//If they have spells, transfer them. Now we basically have a backup mob. - caster.mind.transfer_to(victim) + caster.mind.transfer(victim) for(var/spell/S in victim.spell_list) //get rid of spells the new way victim.remove_spell(S) //This will make it so that players will not get the HUD and all that spell bugginess that caused copies of spells and stuff of that nature. @@ -62,7 +62,7 @@ for(var/V in caster.mind.special_verbs)//Not too important but could come into play. add_verb(caster, V) - ghost.mind.transfer_to(caster) + ghost.mind.transfer(caster) caster.key = ghost.key //have to transfer the key since the mind was not active for(var/spell/S in ghost.spell_list) caster.add_spell(S) diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index f0054f26d96..f60cd02957b 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -460,7 +460,7 @@ holder.update_from_mmi() if(M.brainmob && M.brainmob.mind) - M.brainmob.mind.transfer_to(target) + M.brainmob.mind.transfer(target) target.languages = M.brainmob.languages spawn(0) //Name yourself on your own damn time @@ -551,7 +551,7 @@ user.drop_item_to_ground(tool, INV_OP_FORCE) if(D && D.mind) - D.mind.transfer_to(target) + D.mind.transfer(target) target.languages |= D.languages qdel(D) diff --git a/code/modules/tgui/module.dm b/code/modules/tgui/module.dm index 2001a1364da..9626edc1a4c 100644 --- a/code/modules/tgui/module.dm +++ b/code/modules/tgui/module.dm @@ -19,6 +19,8 @@ * * if you're doing anything that will require more than a few modules (hello rigsuits/cyborgs/species), * do not use the module system as is. make your own synchronization and update system ontop. + * + * /datum/tgui_module is just a wrapper. the $tgui and $src data keys are what powers a module. */ /datum/tgui_module /// root datum - only one for the moment, sorry diff --git a/icons/screen/actions/backgrounds.dmi b/icons/screen/actions/backgrounds.dmi index 71e300ff6e4..b0399acb06d 100644 Binary files a/icons/screen/actions/backgrounds.dmi and b/icons/screen/actions/backgrounds.dmi differ diff --git a/icons/screen/actions/changeling.dmi b/icons/screen/actions/changeling.dmi new file mode 100644 index 00000000000..95e34e84898 Binary files /dev/null and b/icons/screen/actions/changeling.dmi differ diff --git a/tgui/packages/tgui/interfaces/modules/TGUIAbility.tsx b/tgui/packages/tgui/interfaces/modules/TGUIAbility.tsx new file mode 100644 index 00000000000..d0dfa8f88ab --- /dev/null +++ b/tgui/packages/tgui/interfaces/modules/TGUIAbility.tsx @@ -0,0 +1,31 @@ +import { BooleanLike } from "../../../common/react"; +import { ModuleData, useModule } from "../../backend"; +import { Modular } from "../../layouts/Modular"; + +interface TGUIAbilityProps { + +} + +enum TGUIAbilityInteraction { + NONE = "none", + TRIGGER = "trigger", + TOGGLE = "toggle", +} + +interface TGUIAbilityData extends ModuleData { + interact_type: TGUIAbilityInteraction; + name: string; + desc: string; + bound: BooleanLike; + can_bind: BooleanLike; +} + +export const TGUIAbility = (props: TGUIAbilityProps, context) => { + const { data, act } = useModule(context); + + return ( + + Not Implemented + + ); +}; diff --git a/tgui/packages/tgui/interfaces/modules/TGUICardMod.tsx b/tgui/packages/tgui/interfaces/modules/TGUICardMod.tsx index 69069cdc0a1..bd87268179e 100644 --- a/tgui/packages/tgui/interfaces/modules/TGUICardMod.tsx +++ b/tgui/packages/tgui/interfaces/modules/TGUICardMod.tsx @@ -3,8 +3,8 @@ * @license MIT */ -import { BooleanLike } from "common/react"; -import { capitalize } from "common/string"; +import { BooleanLike } from "../../../common/react"; +import { capitalize } from "../../../common/string"; import { ModuleProps, ModuleData, useModule, useLocalState } from "../../backend"; import { Button, Flex, Input, LabeledList, Section, Tabs } from "../../components"; import { SectionProps } from "../../components/Section";