mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-18 11:36:24 +01:00
A small pile of borg combat changes (#75819)
## About The Pull Request This PR rebalances borg combat. It is first part of broader set of changes that I talked about with armhullen, which was then discussed in discord player project channel. Ultimately arm decided to rewrite the complete rebalance and grab combat, but to help with testing, and to measure reaction to that changes, I wrote this much smaller change, that should be usable as a TMd test. Main points that changed: - Flashing cyborgs for the first time blinds them, and scrambles their movement. Flashing them while they are blinded immobilizes them as it used to, but still allows them to talk, and use radio. - Emping cyborgs still turns them completely off, but the code behind is _slightly_ changed, because of how utterly incomprehensible cyborg code is - Borg stunarm deals 60 stamina damage, costs 200 energy (SUBJECT TO CHANGE AS SOON AS I DECIDE ON HOW TO), and applies confusion, stuttering, and jittering. - Robot consoles used by humans can now lock down only one borg at a time, they exhibit increased power draw while doing so. The borg is also informed of the location of the console. If left alone, the borg will unlock itself in 10 minutes, to avoid leaving it permanently locked down in some faraway place. If the console gets destroyed (or depowered), the borg will get unlocked - laser pointer only blinds borgs, without the paralyze component. If you flash a borg blinded this way it will paralyze it, so comboing it is possible. - Throwing things at borgs slows them down Not planned, but possible if testing shows its required: - Changing borg health to malfunctions - Whatever people convince me to add - Portable lockdown solution for sec, or a way for them to do basic check on borg. - Usage of bucket/something else to restrict radio usage of cyborg If you have ideas what would make this change better please use [discord channel](https://discord.com/channels/326822144233439242/1113145741788065924), or comments. Almost everything that I wrote here is subject to change, as requested or suggested. ### DISCLAIMER I had to change few functions in background of how borgs work. This WILL have changes I couldn't predict, since some of them aren't linked in any obvious way in code. I am aware of that, and will try fixing what needs fixing when pointed out. PS: Borg code is a nightmare ## Why It's Good For The Game Borg combat sucks. It is absolutely binary, there is 0 capacity for talking, because both sides fear losing in one click. Sec can't really feasibly ask the borg to state laws while flashed, so their only solution is to kill it and revive it. - Turning most instastun solutions into less lethal versions should lessen the pressure, since it reduces the chance that person that reacts first survives. - It causes minor reduction in traitor's capacity to emag borgs with only a flash, but EMPs still hardstun, and they are plentiful both in uplink, and in ghetto form, so that shouldn't be a problem. - Since it allows borgs to scream for help while being flashed, it also increases the potential of AI helping it, or at least noticing its death. - Lockdown console changes reduce its capability in completely turning off malf ai, and leave it still highly capable of being an useful tool in stopping emagged cyborgs. ## Changelog 🆑 balance: Flashing borgs requires two consecutive flashes to fully immobilize balance: Flashed borgs can speak balance: Remote lockdown on cyborgs lasts 180 seconds balance: Cyborg stun arm works like normal baton, and costs less energy /🆑 --------- Co-authored-by: ATH1909 <42606352+ATH1909@users.noreply.github.com> Co-authored-by: Jacquerel <hnevard@gmail.com>
This commit is contained in:
@@ -6,6 +6,9 @@
|
||||
req_access = list(ACCESS_ROBOTICS)
|
||||
circuit = /obj/item/circuitboard/computer/robotics
|
||||
light_color = LIGHT_COLOR_PINK
|
||||
var/mob/living/silicon/robot/locked_down_borg = null
|
||||
active_power_usage = 10000 //no idea how much is it
|
||||
|
||||
|
||||
/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R)
|
||||
. = FALSE
|
||||
@@ -88,21 +91,37 @@
|
||||
if(allowed(usr))
|
||||
var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
|
||||
if(can_control(usr, R) && !..())
|
||||
if(isAI(usr) && (R.ai_lockdown && R.lockcharge || !R.lockcharge) || !isAI(usr))
|
||||
R.ai_lockdown = FALSE
|
||||
if(isAI(usr) && !R.lockcharge)
|
||||
if(isAI(usr))
|
||||
if(R.lockcharge)
|
||||
if(R.ai_lockdown)
|
||||
R.ai_lockdown = FALSE
|
||||
lock_unlock_borg(R)
|
||||
else
|
||||
to_chat(usr, span_danger("Cyborg locked by an user with superior permissions."))
|
||||
else
|
||||
R.ai_lockdown = TRUE
|
||||
message_admins(span_notice("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!"))
|
||||
log_silicon("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!")
|
||||
log_combat(usr, R, "[!R.lockcharge ? "locked down" : "released"] cyborg")
|
||||
R.SetLockdown(!R.lockcharge)
|
||||
to_chat(R, !R.lockcharge ? span_notice("Your lockdown has been lifted!") : span_alert("You have been locked down!"))
|
||||
if(R.connected_ai)
|
||||
to_chat(R.connected_ai, "[!R.lockcharge ? span_notice("NOTICE - Cyborg lockdown lifted") : span_alert("ALERT - Cyborg lockdown detected")]: <a href='?src=[REF(R.connected_ai)];track=[html_encode(R.name)]'>[R.name]</a><br>")
|
||||
lock_unlock_borg(R)
|
||||
else
|
||||
to_chat(usr, span_danger("Cyborg locked by an user with superior permissions."))
|
||||
if(isnull(locked_down_borg) && !R.lockcharge) //If there is no borg locked down by the console yet
|
||||
lock_unlock_borg(R, src.loc.loc.name)
|
||||
R.ai_lockdown = FALSE //Just in case I'm stupid
|
||||
locked_down_borg = R
|
||||
RegisterSignal(R, COMSIG_QDELETING, PROC_REF(borg_destroyed))
|
||||
else if(locked_down_borg == R) //If the borg locked down by the console is the same as the one we're trying to unlock
|
||||
lock_unlock_borg(R)
|
||||
else if(R.lockcharge&&R.ai_lockdown)
|
||||
R.ai_lockdown = FALSE
|
||||
lock_unlock_borg(R)
|
||||
else if(R.lockcharge&&locked_down_borg!=R)
|
||||
to_chat(usr, span_danger("The cyborg was locked by a different console."))
|
||||
else
|
||||
to_chat(usr, span_danger("You can lock down only one cyborg at a time."))
|
||||
else
|
||||
to_chat(usr, span_danger("Access Denied."))
|
||||
if(!isnull(locked_down_borg))
|
||||
use_power = ACTIVE_POWER_USE
|
||||
else
|
||||
use_power = IDLE_POWER_USE
|
||||
|
||||
if("killbot") //Malf AIs, and AIs with a combat upgrade, can detonate their cyborgs remotely.
|
||||
if(!isAI(usr))
|
||||
@@ -142,3 +161,44 @@
|
||||
drone.visible_message(span_danger("\the [drone] self-destructs!"))
|
||||
drone.investigate_log("has been gibbed by a robotics console.", INVESTIGATE_DEATHS)
|
||||
drone.gib()
|
||||
|
||||
|
||||
// I feel like this should be changed, but I have no idea in what way exactly, so I just extracted it to make the code less of a mess
|
||||
/obj/machinery/computer/robotics/proc/lock_unlock_borg(mob/living/silicon/robot/R, console_location = null)
|
||||
if(R.lockcharge && locked_down_borg == R)
|
||||
UnregisterSignal(locked_down_borg, COMSIG_QDELETING)
|
||||
locked_down_borg = null
|
||||
message_admins(span_notice("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!"))
|
||||
log_silicon("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!")
|
||||
log_combat(usr, R, "[!R.lockcharge ? "locked down" : "released"] cyborg")
|
||||
R.SetLockdown(!R.lockcharge)
|
||||
to_chat(R, !R.lockcharge ? span_notice("Your lockdown has been lifted!") : span_alert("You have been locked down!"))
|
||||
if(!isnull(console_location))
|
||||
to_chat(R, span_alert("The approximate location of the console that is keeping you locked down is [console_location]"))
|
||||
if(R.connected_ai)
|
||||
to_chat(R.connected_ai, "[!R.lockcharge ? span_notice("NOTICE - Cyborg lockdown lifted") : span_alert("ALERT - Cyborg lockdown detected")]: <a href='?src=[REF(R.connected_ai)];track=[html_encode(R.name)]'>[R.name]</a><br>")
|
||||
|
||||
/obj/machinery/computer/robotics/proc/borg_destroyed()
|
||||
SIGNAL_HANDLER
|
||||
locked_down_borg = null
|
||||
|
||||
/obj/machinery/computer/robotics/on_set_machine_stat(old_value) //depowering the console unlocks the borg
|
||||
if(!isnull(locked_down_borg))
|
||||
if(machine_stat & (NOPOWER|BROKEN|MAINT) && locked_down_borg.lockcharge)
|
||||
src.lock_unlock_borg(locked_down_borg)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/robotics/atom_break() // This shouldnt be needed, but hitting console doesnt trigger destroy apparently
|
||||
if(!isnull(locked_down_borg))
|
||||
lock_unlock_borg(locked_down_borg)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/robotics/Destroy()
|
||||
if(!isnull(locked_down_borg))
|
||||
lock_unlock_borg(locked_down_borg)
|
||||
return ..()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
log_combat(user, S, "shone in the sensors", src)
|
||||
//chance to actually hit the eyes depends on internal component
|
||||
if(prob(effectchance * diode.rating) && S.flash_act(affect_silicon = TRUE))
|
||||
S.Paralyze(rand(100,200))
|
||||
S.set_temp_blindness_if_lower(5 SECONDS)
|
||||
to_chat(S, span_danger("Your sensors were overloaded by a laser!"))
|
||||
outmsg = span_notice("You overload [S] by shining [src] at [S.p_their()] sensors.")
|
||||
else
|
||||
|
||||
@@ -15,10 +15,17 @@
|
||||
/obj/item/borg/stun
|
||||
name = "electrically-charged arm"
|
||||
icon_state = "elecarm"
|
||||
var/stamina_damage = 60 //Same as normal batong
|
||||
/// Cost to use the stun arm
|
||||
var/charge_cost = 1000
|
||||
var/charge_cost = 200
|
||||
var/cooldown_check = 0
|
||||
/// cooldown between attacks
|
||||
var/cooldown = 4 SECONDS // same as baton
|
||||
|
||||
/obj/item/borg/stun/attack(mob/living/attacked_mob, mob/living/user)
|
||||
if(cooldown_check > world.time)
|
||||
user.balloon_alert(user, "still recharging!")
|
||||
return
|
||||
if(ishuman(attacked_mob))
|
||||
var/mob/living/carbon/human/human = attacked_mob
|
||||
if(human.check_shields(src, 0, "[attacked_mob]'s [name]", MELEE_ATTACK))
|
||||
@@ -30,14 +37,20 @@
|
||||
return
|
||||
|
||||
user.do_attack_animation(attacked_mob)
|
||||
attacked_mob.Paralyze(100)
|
||||
attacked_mob.adjust_stutter(10 SECONDS)
|
||||
|
||||
attacked_mob.visible_message(span_danger("[user] prods [attacked_mob] with [src]!"), \
|
||||
attacked_mob.adjustStaminaLoss(stamina_damage)
|
||||
attacked_mob.set_confusion_if_lower(5 SECONDS)
|
||||
attacked_mob.adjust_stutter(20 SECONDS)
|
||||
attacked_mob.set_jitter_if_lower(5 SECONDS)
|
||||
if(issilicon(attacked_mob))
|
||||
attacked_mob.emp_act(EMP_HEAVY)
|
||||
attacked_mob.visible_message(span_danger("[user] shocks [attacked_mob] with [src]!"), \
|
||||
span_userdanger("[user] shocks you with [src]!"))
|
||||
else
|
||||
attacked_mob.visible_message(span_danger("[user] prods [attacked_mob] with [src]!"), \
|
||||
span_userdanger("[user] prods you with [src]!"))
|
||||
|
||||
playsound(loc, 'sound/weapons/egloves.ogg', 50, TRUE, -1)
|
||||
|
||||
cooldown_check = world.time + cooldown
|
||||
log_combat(user, attacked_mob, "stunned", src, "(Combat mode: [user.combat_mode ? "On" : "Off"])")
|
||||
|
||||
/obj/item/borg/cyborghug
|
||||
|
||||
@@ -243,12 +243,18 @@
|
||||
var/mob/living/silicon/robot/flashed_borgo = M
|
||||
log_combat(user, flashed_borgo, "flashed", src)
|
||||
update_icon(ALL, TRUE)
|
||||
if(!flashed_borgo.flash_act(affect_silicon = TRUE))
|
||||
if(flashed_borgo.flash_act(affect_silicon = TRUE))
|
||||
if(flashed_borgo.is_blind())
|
||||
var/flash_duration = rand(8,12) SECONDS
|
||||
flashed_borgo.Paralyze(flash_duration)
|
||||
flashed_borgo.set_temp_blindness_if_lower(flash_duration)
|
||||
user.visible_message(span_warning("[user] overloads [flashed_borgo]'s sensors and computing with the flash!"), span_danger("You overload [flashed_borgo]'s sensors and computing with the flash!"))
|
||||
else
|
||||
user.visible_message(span_warning("[user] blinds [flashed_borgo] with the flash!"), span_danger("You blind [flashed_borgo] with the flash!"))
|
||||
flashed_borgo.set_temp_blindness_if_lower( (rand(5,15) SECONDS))
|
||||
flashed_borgo.set_confusion_if_lower(5 SECONDS * CONFUSION_STACK_MAX_MULTIPLIER)
|
||||
else
|
||||
user.visible_message(span_warning("[user] fails to blind [flashed_borgo] with the flash!"), span_warning("You fail to blind [flashed_borgo] with the flash!"))
|
||||
return
|
||||
flashed_borgo.Paralyze(rand(80,120))
|
||||
flashed_borgo.set_confusion_if_lower(5 SECONDS * CONFUSION_STACK_MAX_MULTIPLIER)
|
||||
user.visible_message(span_warning("[user] overloads [flashed_borgo]'s sensors with the flash!"), span_danger("You overload [flashed_borgo]'s sensors with the flash!"))
|
||||
return
|
||||
|
||||
user.visible_message(span_warning("[user] fails to blind [M] with the flash!"), span_warning("You fail to blind [M] with the flash!"))
|
||||
|
||||
@@ -411,12 +411,25 @@
|
||||
// They stay locked down if their wire is cut.
|
||||
if(wires?.is_cut(WIRE_LOCKDOWN))
|
||||
state = TRUE
|
||||
else if(!ai_lockdown)
|
||||
lockdown_timer = addtimer(CALLBACK(src,PROC_REF(lockdown_override), FALSE), 10 MINUTES, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_DELETE_ME | TIMER_STOPPABLE)
|
||||
to_chat(src, "<br><br>[span_alert("ALERT - Remote system lockdown engaged. Trying to hack the lockdown subsystem...")]<br>")
|
||||
if(state)
|
||||
throw_alert(ALERT_HACKED, /atom/movable/screen/alert/locked)
|
||||
else
|
||||
deltimer(lockdown_timer)
|
||||
clear_alert(ALERT_HACKED)
|
||||
set_lockcharge(state)
|
||||
|
||||
/// Allows the borg to unlock themselves after a lenghty period of time.
|
||||
/mob/living/silicon/robot/proc/lockdown_override()
|
||||
if(ai_lockdown)
|
||||
to_chat(src, "<br><br>[span_alert("ALERT - Remote system lockdown override failed.")]<br>")
|
||||
return
|
||||
set_lockcharge(FALSE)
|
||||
to_chat(src, "<br><br>[span_notice("ALERT - Remote system lockdown override successful.")]<br>")
|
||||
if(connected_ai)
|
||||
to_chat(connected_ai, "<br><br>[span_notice("ALERT - Cyborg [name] succesfully overriden the lockdown system")]<br>")
|
||||
|
||||
///Reports the event of the change in value of the lockcharge variable.
|
||||
/mob/living/silicon/robot/proc/set_lockcharge(new_lockcharge)
|
||||
@@ -658,10 +671,6 @@
|
||||
death()
|
||||
toggle_headlamp(1)
|
||||
return
|
||||
if(HAS_TRAIT(src, TRAIT_KNOCKEDOUT) || IsStun() || IsKnockdown() || IsParalyzed())
|
||||
set_stat(UNCONSCIOUS)
|
||||
else
|
||||
set_stat(CONSCIOUS)
|
||||
diag_hud_set_status()
|
||||
diag_hud_set_health()
|
||||
diag_hud_set_aishell()
|
||||
@@ -677,6 +686,7 @@
|
||||
builtInCamera.toggle_cam(src, 0)
|
||||
if(full_heal_flags & HEAL_ADMIN)
|
||||
locked = TRUE
|
||||
src.set_stat(CONSCIOUS)
|
||||
notify_ai(AI_NOTIFICATION_NEW_BORG)
|
||||
toggle_headlamp(FALSE, TRUE) //This will reenable borg headlamps if doomsday is currently going on still.
|
||||
return TRUE
|
||||
|
||||
@@ -320,9 +320,17 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
|
||||
return
|
||||
switch(severity)
|
||||
if(1)
|
||||
Stun(160)
|
||||
emp_knockout(16 SECONDS)
|
||||
if(2)
|
||||
Stun(60)
|
||||
emp_knockout(6 SECONDS)
|
||||
|
||||
/mob/living/silicon/robot/proc/emp_knockout(deciseconds)
|
||||
set_stat(UNCONSCIOUS)
|
||||
addtimer(CALLBACK(src, PROC_REF(wake_from_emp)), deciseconds, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_DELETE_ME)
|
||||
|
||||
/mob/living/silicon/robot/proc/wake_from_emp()
|
||||
set_stat(CONSCIOUS)
|
||||
update_stat()
|
||||
|
||||
/mob/living/silicon/robot/emag_act(mob/user, obj/item/card/emag/emag_card)
|
||||
if(user == src)//To prevent syndieborgs from emagging themselves
|
||||
@@ -430,3 +438,12 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
|
||||
updatehealth()
|
||||
if(prob(75) && Proj.damage > 0)
|
||||
spark_system.start()
|
||||
|
||||
/mob/living/silicon/on_hit(obj/projectile/P)
|
||||
if(!has_movespeed_modifier(/datum/movespeed_modifier/borg_throw))
|
||||
add_movespeed_modifier(/datum/movespeed_modifier/borg_throw)
|
||||
addtimer(CALLBACK(src, TYPE_PROC_REF(/mob/living/silicon, clear_throw_slowdown)), (P.throwforce / 10) SECONDS)
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/proc/clear_throw_slowdown()
|
||||
src.remove_movespeed_modifier(/datum/movespeed_modifier/borg_throw)
|
||||
|
||||
@@ -100,6 +100,8 @@
|
||||
var/lockcharge = FALSE
|
||||
///Boolean of whether the borg was locked by its AI or nothing
|
||||
var/ai_lockdown = FALSE
|
||||
///Timer that allows the borg to self-unlock after a set amount of time
|
||||
var/lockdown_timer = null
|
||||
///Random serial number generated for each cyborg upon its initialization
|
||||
var/ident = 0
|
||||
var/locked = TRUE
|
||||
|
||||
@@ -276,6 +276,7 @@
|
||||
flick("[cyborg_base_icon]_transform", cyborg)
|
||||
cyborg.notransform = TRUE
|
||||
if(locked_transform)
|
||||
cyborg.ai_lockdown = TRUE
|
||||
cyborg.SetLockdown(TRUE)
|
||||
cyborg.set_anchored(TRUE)
|
||||
cyborg.logevent("Chassis model has been set to [name].")
|
||||
@@ -284,6 +285,7 @@
|
||||
playsound(cyborg, pick('sound/items/drill_use.ogg', 'sound/items/jaws_cut.ogg', 'sound/items/jaws_pry.ogg', 'sound/items/welder.ogg', 'sound/items/ratchet.ogg'), 80, TRUE, -1)
|
||||
sleep(0.7 SECONDS)
|
||||
cyborg.SetLockdown(FALSE)
|
||||
cyborg.ai_lockdown = FALSE
|
||||
cyborg.setDir(SOUTH)
|
||||
cyborg.set_anchored(FALSE)
|
||||
cyborg.notransform = FALSE
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
/mob/living/silicon/emp_act(severity)
|
||||
. = ..()
|
||||
to_chat(src, span_danger("Warning: Electromagnetic pulse detected."))
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
if(. & EMP_PROTECT_SELF || QDELETED(src))
|
||||
return
|
||||
switch(severity)
|
||||
if(1)
|
||||
|
||||
@@ -84,6 +84,9 @@
|
||||
/datum/movespeed_modifier/shove
|
||||
multiplicative_slowdown = SHOVE_SLOWDOWN_STRENGTH
|
||||
|
||||
/datum/movespeed_modifier/borg_throw
|
||||
multiplicative_slowdown = 0.9
|
||||
|
||||
/datum/movespeed_modifier/human_carry
|
||||
multiplicative_slowdown = HUMAN_CARRY_SLOWDOWN
|
||||
blacklisted_movetypes = FLOATING
|
||||
|
||||
Reference in New Issue
Block a user