mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
## About The Pull Request MODSuits now slow you progressively as parts are deployed, and not at all when no parts are deployed. Previous to this change MODsuits would slow you _more_ for having a part not deployed than for having it deployed and powered, meaning you would be slower using it as a backpack than you would be when using it as a suit. This leads to people typically taking it off entirely and holding it in their hands instead of wearing it. ## Why It's Good For The Game We talked about this in the coderbus meeting and the general reasoning is; - We want people to use MODsuits for the utility they provide. It's good when people think these items are desirable and ask the roboticist to make one for them. - MODsuits being slower when unpowered makes sense but is _mostly_ just that way for flavour reasons. - Moving slower is (perhaps unintuitively?) one of the strongest detriments we have in the game for using something. - MODsuits when used as backpacks are _already_ worse than a standard backpack (less storage space unless complexity is dedicated to increase it again) and it's not necessarily clear that they need an additional downside. - It feels odd to be penalised _more_ strongly penalised when just wearing a backpack and not benefiting from any utility aspect of the suit than you are when actively benefiting from having the suit. - It also feels odd to be penalised at all for having it while not benefiting from it. - The fact that they have a deployment time already reduces their effectiveness in terms of being "snapped" on and off in response to loss of atmosphere. - The "juggling" behaviour that players resort to when they can't just wear their suit as a backpack (swapping between a held backpack and a held modsuit control with keybindings) is the kind of powergaming trick I don't really like but is reasonably easy to do in order to bypass the downside anyway, removing this removes any need to train players to do this. - Personally I feel that if people are wearing their MODsuit undeployed while wandering around the station that's actually preferable to me than if they had it deployed full-time, which is what they are more likely to be doing if leaving it undeployed makes them slower (even if the undeployed speed was the same as the deployed speed). At that point, they might as well simply have it powered in order to benefit from being atmospherics-proof and deal with having to charge it every so often. **Doesn't this just make a MODsuit into a better backpack?** No, it has lower capacity. A standard MODsuit storage has a max weight capacity of 15 split across up to 7 items. A regular backpack has a max weight capacity of 21 split across up to 21 items (significantly more granularity). **Won't this make people wear MODsuits all the time?** Apart from ones that you get with your job, the Roboticist only starts with a handful of mod cores to make more suits out of. If we achieved even 1/3rd crew saturation on a busy round that would mean a significant use of cargo to acquire more cores, which is a design goal. I think the idea that it would reach total crew saturation is basically unthinkable, we already limit the number of suits that can exist at once via cargo. Also, most suits with good utility _still_ make you slower _when deployed_ so any time you are actually benefiting from its utility you are experiencing a drawback. ## Changelog 🆑 balance: MODSuit parts now slow you only when deployed, regardless of whether they are sealed. balance: MODSuit parts no longer slow you when they aren't deployed. /🆑
217 lines
6.9 KiB
Plaintext
217 lines
6.9 KiB
Plaintext
/obj/item/mod/control/transfer_ai(interaction, mob/user, mob/living/silicon/ai/intAI, obj/item/aicard/card)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
if(!open) //mod must be open
|
|
balloon_alert(user, "panel closed!")
|
|
return
|
|
switch(interaction)
|
|
if(AI_TRANS_TO_CARD)
|
|
if(!ai_assistant)
|
|
balloon_alert(user, "no ai in unit!")
|
|
return
|
|
balloon_alert(user, "transferring to card...")
|
|
if(!do_after(user, 5 SECONDS, target = src))
|
|
balloon_alert(user, "interrupted!")
|
|
return
|
|
if(!ai_assistant)
|
|
balloon_alert(user, "no ai in unit!")
|
|
return
|
|
balloon_alert(user, "ai transferred to card")
|
|
ai_exit_mod(card)
|
|
|
|
if(AI_TRANS_FROM_CARD) //Using an AI card to upload to the suit.
|
|
intAI = card.AI
|
|
if(!intAI)
|
|
balloon_alert(user, "no ai in card!")
|
|
return
|
|
if(ai_assistant)
|
|
balloon_alert(user, "already has ai!")
|
|
return
|
|
if(intAI.deployed_shell) //Recall AI if shelled so it can be checked for a client
|
|
intAI.disconnect_shell()
|
|
if(intAI.stat || !intAI.client)
|
|
balloon_alert(user, "ai unresponsive!")
|
|
return
|
|
balloon_alert(user, "transferring to unit...")
|
|
if(!do_after(user, 5 SECONDS, target = src))
|
|
balloon_alert(user, "interrupted!")
|
|
return
|
|
if(ai_assistant)
|
|
return
|
|
balloon_alert(user, "ai transferred to unit")
|
|
ai_enter_mod(intAI)
|
|
card.AI = null
|
|
|
|
/// Place an AI in control of your suit functions
|
|
/obj/item/mod/control/proc/ai_enter_mod(mob/living/silicon/ai/new_ai)
|
|
new_ai.control_disabled = FALSE
|
|
new_ai.radio_enabled = TRUE
|
|
new_ai.ai_restore_power()
|
|
new_ai.cancel_camera()
|
|
new_ai.controlled_equipment = src
|
|
new_ai.remote_control = src
|
|
new_ai.forceMove(src)
|
|
on_gained_assistant(new_ai)
|
|
|
|
/// Remove an AI's control of your suit functions
|
|
/obj/item/mod/control/proc/ai_exit_mod(obj/item/aicard/card)
|
|
var/mob/living/silicon/ai/old_ai = ai_assistant
|
|
old_ai.ai_restore_power()//So the AI initially has power.
|
|
old_ai.control_disabled = TRUE
|
|
old_ai.radio_enabled = FALSE
|
|
old_ai.disconnect_shell()
|
|
old_ai.forceMove(card)
|
|
card.AI = old_ai
|
|
old_ai.controlled_equipment = null
|
|
on_removed_assistant(old_ai)
|
|
|
|
/// Place a pAI in control of your suit functions
|
|
/obj/item/mod/control/proc/insert_pai(mob/user, obj/item/pai_card/card)
|
|
if (!isnull(ai_assistant))
|
|
balloon_alert(user, "slot occupied!")
|
|
return FALSE
|
|
if (isnull(card.pai?.mind))
|
|
balloon_alert(user, "pAI unresponsive!")
|
|
return FALSE
|
|
balloon_alert(user, "transferring to unit...")
|
|
if (!do_after(user, 5 SECONDS, target = src))
|
|
balloon_alert(user, "interrupted!")
|
|
return FALSE
|
|
if (!user.transferItemToLoc(card, src))
|
|
balloon_alert(user, "transfer failed!")
|
|
return FALSE
|
|
balloon_alert(user, "pAI transferred to unit")
|
|
var/mob/living/silicon/pai/pai_assistant = card.pai
|
|
pai_assistant.can_transmit = TRUE
|
|
pai_assistant.can_receive = TRUE
|
|
pai_assistant.can_holo = FALSE
|
|
if (pai_assistant.holoform)
|
|
pai_assistant.fold_in()
|
|
SStgui.close_uis(card)
|
|
on_gained_assistant(card.pai)
|
|
return TRUE
|
|
|
|
/// Removes pAI control from a modsuit
|
|
/obj/item/mod/control/proc/remove_pai(mob/user, forced = FALSE)
|
|
if (isnull(ai_assistant))
|
|
balloon_alert(user, "no pAI!")
|
|
return FALSE
|
|
if (!forced)
|
|
if (!open)
|
|
balloon_alert(user, "panel closed!")
|
|
return FALSE
|
|
balloon_alert(user, "uninstalling card...")
|
|
if (!do_after(user, 5 SECONDS, target = src))
|
|
balloon_alert(user, "interrupted!")
|
|
return FALSE
|
|
|
|
balloon_alert(user, "pAI removed")
|
|
var/mob/living/silicon/pai/pai_helper = ai_assistant
|
|
pai_helper.can_holo = TRUE
|
|
pai_helper.card.forceMove(get_turf(src))
|
|
on_removed_assistant()
|
|
|
|
/// Called when a new ai assistant is inserted
|
|
/obj/item/mod/control/proc/on_gained_assistant(mob/living/silicon/new_helper)
|
|
ai_assistant = new_helper
|
|
balloon_alert(new_helper, "transferred to a mod unit")
|
|
for(var/datum/action/action as anything in actions)
|
|
action.Grant(new_helper)
|
|
|
|
/// Called when an existing ai assistant is removed
|
|
/obj/item/mod/control/proc/on_removed_assistant()
|
|
for(var/datum/action/action as anything in actions)
|
|
action.Remove(ai_assistant)
|
|
ai_assistant.remote_control = null
|
|
balloon_alert(ai_assistant, "transferred to a card")
|
|
ai_assistant = null
|
|
|
|
#define MOVE_DELAY 2
|
|
#define WEARER_DELAY 1
|
|
#define LONE_DELAY 5
|
|
#define CHARGE_PER_STEP (DEFAULT_CHARGE_DRAIN * 2.5)
|
|
#define AI_FALL_TIME (1 SECONDS)
|
|
|
|
/obj/item/mod/control/relaymove(mob/user, direction)
|
|
if((!active && wearer) || get_charge() < CHARGE_PER_STEP || user != ai_assistant || !COOLDOWN_FINISHED(src, cooldown_mod_move) || (wearer?.pulledby?.grab_state > GRAB_PASSIVE))
|
|
return FALSE
|
|
var/datum/mod_part/legs_to_move = get_part_datum_from_slot(ITEM_SLOT_FEET)
|
|
if(wearer && (!legs_to_move || !legs_to_move.sealed))
|
|
return FALSE
|
|
var/timemodifier = MOVE_DELAY * (ISDIAGONALDIR(direction) ? sqrt(2) : 1) * (wearer ? WEARER_DELAY : LONE_DELAY)
|
|
if(wearer && !wearer.Process_Spacemove(direction))
|
|
return FALSE
|
|
else if(!wearer && (!has_gravity() || !isturf(loc)))
|
|
return FALSE
|
|
COOLDOWN_START(src, cooldown_mod_move, movedelay * timemodifier + slowdown_deployed)
|
|
subtract_charge(CHARGE_PER_STEP)
|
|
playsound(src, 'sound/vehicles/mecha/mechmove01.ogg', 25, TRUE)
|
|
if(ismovable(wearer?.loc))
|
|
return wearer.loc.relaymove(wearer, direction)
|
|
else if(wearer)
|
|
ADD_TRAIT(wearer, TRAIT_FORCED_STANDING, REF(src))
|
|
addtimer(CALLBACK(src, PROC_REF(ai_fall)), AI_FALL_TIME, TIMER_UNIQUE | TIMER_OVERRIDE)
|
|
var/atom/movable/mover = wearer || src
|
|
return mover.try_step_multiz(direction)
|
|
|
|
#undef MOVE_DELAY
|
|
#undef WEARER_DELAY
|
|
#undef LONE_DELAY
|
|
#undef CHARGE_PER_STEP
|
|
|
|
/obj/item/mod/control/proc/ai_fall()
|
|
if(!wearer)
|
|
return
|
|
REMOVE_TRAIT(wearer, TRAIT_FORCED_STANDING, REF(src))
|
|
|
|
/obj/item/mod/ai_minicard
|
|
name = "AI mini-card"
|
|
desc = "A small card designed to eject dead AIs. You could use an intellicard to recover it."
|
|
icon = 'icons/obj/aicards.dmi'
|
|
icon_state = "minicard"
|
|
var/datum/weakref/stored_ai
|
|
|
|
/obj/item/mod/ai_minicard/Initialize(mapload, mob/living/silicon/ai/ai)
|
|
. = ..()
|
|
if(isnull(ai))
|
|
return
|
|
ai.controlled_equipment = null
|
|
ai.remote_control = null
|
|
ai.apply_damage(150, BURN)
|
|
INVOKE_ASYNC(ai, TYPE_PROC_REF(/mob/living/silicon/ai, death))
|
|
ai.forceMove(src)
|
|
stored_ai = WEAKREF(ai)
|
|
icon_state = "minicard-filled"
|
|
|
|
/obj/item/mod/ai_minicard/Destroy()
|
|
QDEL_NULL(stored_ai)
|
|
return ..()
|
|
|
|
/obj/item/mod/ai_minicard/examine(mob/user)
|
|
. = ..()
|
|
. += span_notice("You see [stored_ai.resolve() || "no AI"] stored inside.")
|
|
|
|
/obj/item/mod/ai_minicard/transfer_ai(interaction, mob/user, mob/living/silicon/ai/intAI, obj/item/aicard/card)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
if(interaction != AI_TRANS_TO_CARD)
|
|
return
|
|
var/mob/living/silicon/ai/ai = stored_ai.resolve()
|
|
if(!ai)
|
|
balloon_alert(user, "no ai!")
|
|
return
|
|
balloon_alert(user, "transferring to card...")
|
|
if(!do_after(user, 5 SECONDS, target = src) || !ai)
|
|
balloon_alert(user, "interrupted!")
|
|
return
|
|
icon_state = "minicard"
|
|
ai.forceMove(card)
|
|
card.AI = ai
|
|
ai.notify_revival("You have been recovered from the wreckage!", source = card)
|
|
balloon_alert(user, "ai transferred to card")
|
|
stored_ai = null
|
|
|
|
#undef AI_FALL_TIME
|