mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 12:41:09 +01:00
## About The Pull Request New changes for Justice mech - traitor mech that can be created on emaged fabricator(you need robo skillchip to emag it) 1. Charge attack refactor Charged attack is now used not by pressing the action button but on holding right click  While you press right click scope will focus on tile mouse looking for and when you release right button you will charge on this tile (cooldown 5 seconds as before). if your mouse look at position you unable to charge your mouse icon will become red 2. New Justice block Now Justice don't have 60% chance to block any damage. Instead of this rng, justice has 3 engines that orbit around him  Engines can be active and disabled. While all engines active they will block any range projictiles back.  To deactivate engine you need to melee hit mech. When all engines deactivated mech loses its ability to deflect for 10 seconds, after which all cores are recharged and the cycle continues again. Justice can also recharge one engine when pilot hits someone who not in crit.  3. Some balance changes: Mech hp now 300 Mech melee armor now 50 Mech bullet and laser armor now 30 Mech now don't have speed change when you switch safety mod. Instead mech becomes faster in invisibility and slower when holding the right mouse button to charge a charge attack. Invisibility mode now no longer temporary. You can be invisible as long as you want until you attack/ bump with someone/ take damage or turn it off yourself AoE attack from invisibility is now x2 faster  ## Why It's Good For The Game Lots of changes from recived feedback. Basically the most annoying thing was the block chances. Praying on luck to not die from laser spam turned out to be not a very interesting mechanic. Now mech justice requires more strategic actions to destroy it and the mechanics create more fun for both the mech user and those who fight with it. You need to get close to the mech to hit it and only then will you be able to shoot the laser and user of justice has the ability to recharge his cores by attacking enemies (if you don't attack, then you won't be able to recharge the cores (well, only if your other cores are broken)). This all creates more risk reward elements and not just rng. Accordance with block chance changes i also increased HP and armor of Justice to balance the block changes a bit. AoE attack speed was needed simply because 100/100 cases everyone said that the aoe attack is weak and useless because it is almost very easy to get away from it. Changing the charge attack work mechanic is necessary to improve the maneuverability of the mech. pressing the charge button and then getting messages that you chose the wrong direction is very inconvenient and deprives comfortable gameplay and pleasant use of the mech. ## Changelog 🆑 add: Justice mech: New block and charge system! Build and try it now! qol: Justice mech: charge attack is now much more pleasant and convenient to use! balance: Justice mech: Increased HP and armor. balance: Justice mech: Invisibility is now infinite. balance: Justice mech: stealth aoe attack is now 2x faster. refactor: Justice mech: Some code clenup and mechanic improvements. /🆑 --------- Co-authored-by: tgstation-server-ci[bot] <161980869+tgstation-server-ci[bot]@users.noreply.github.com> Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com> Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
230 lines
8.9 KiB
Plaintext
230 lines
8.9 KiB
Plaintext
/obj/vehicle/sealed/mecha/mob_try_enter(mob/M)
|
|
if(!ishuman(M)) // no silicons or drones in mechas.
|
|
return
|
|
if(HAS_TRAIT(M, TRAIT_PRIMITIVE)) //no lavalizards either.
|
|
to_chat(M, span_warning("The knowledge to use this device eludes you!"))
|
|
return
|
|
log_message("[M] tried to move into [src].", LOG_MECHA)
|
|
if(dna_lock && M.has_dna())
|
|
var/mob/living/carbon/entering_carbon = M
|
|
if(entering_carbon.dna.unique_enzymes != dna_lock)
|
|
to_chat(M, span_warning("Access denied. [name] is secured with a DNA lock."))
|
|
log_message("Permission denied (DNA LOCK).", LOG_MECHA)
|
|
return
|
|
if((mecha_flags & ID_LOCK_ON) && !allowed(M))
|
|
to_chat(M, span_warning("Access denied. Insufficient operation keycodes."))
|
|
log_message("Permission denied (No keycode).", LOG_MECHA)
|
|
return
|
|
. = ..()
|
|
if(.)
|
|
moved_inside(M)
|
|
|
|
/obj/vehicle/sealed/mecha/enter_checks(mob/M)
|
|
if(M.incapacitated)
|
|
return FALSE
|
|
if(atom_integrity <= 0)
|
|
to_chat(M, span_warning("You cannot get in the [src], it has been destroyed!"))
|
|
return FALSE
|
|
if(M.buckled)
|
|
to_chat(M, span_warning("You can't enter the exosuit while buckled."))
|
|
log_message("Permission denied (Buckled).", LOG_MECHA)
|
|
return FALSE
|
|
if(M.has_buckled_mobs())
|
|
to_chat(M, span_warning("You can't enter the exosuit with other creatures attached to you!"))
|
|
log_message("Permission denied (Attached mobs).", LOG_MECHA)
|
|
return FALSE
|
|
|
|
for(var/obj/item/thing in M.held_items)
|
|
if(!(thing.item_flags & (ABSTRACT|HAND_ITEM)))
|
|
to_chat(M, span_warning("You can't enter the exosuit while your hands are occupied!"))
|
|
return FALSE
|
|
|
|
return ..()
|
|
|
|
///proc called when a new non-mmi mob enters this mech
|
|
/obj/vehicle/sealed/mecha/proc/moved_inside(mob/living/newoccupant)
|
|
if(!(newoccupant?.client))
|
|
return FALSE
|
|
if(ishuman(newoccupant) && !Adjacent(newoccupant))
|
|
return FALSE
|
|
mecha_flags &= ~PANEL_OPEN //Close panel if open
|
|
newoccupant.forceMove(src)
|
|
newoccupant.update_mouse_pointer()
|
|
add_fingerprint(newoccupant)
|
|
log_message("[newoccupant] moved in as pilot.", LOG_MECHA)
|
|
setDir(SOUTH)
|
|
playsound(src, 'sound/machines/windowdoor.ogg', 50, TRUE)
|
|
set_mouse_pointer()
|
|
if(!internal_damage)
|
|
SEND_SOUND(newoccupant, sound('sound/vehicles/mecha/nominal.ogg',volume=50))
|
|
return TRUE
|
|
|
|
///proc called when a new mmi mob tries to enter this mech
|
|
/obj/vehicle/sealed/mecha/proc/mmi_move_inside(obj/item/mmi/brain_obj, mob/user)
|
|
if(!(mecha_flags & MMI_COMPATIBLE))
|
|
to_chat(user, span_warning("This mecha is not compatible with MMIs!"))
|
|
return FALSE
|
|
if(!brain_obj.brain_check(user))
|
|
return FALSE
|
|
var/mob/living/brain/brain_mob = brain_obj.brainmob
|
|
if(LAZYLEN(occupants) >= max_occupants)
|
|
to_chat(user, span_warning("It's full!"))
|
|
return FALSE
|
|
if(dna_lock && (!brain_mob.stored_dna || (dna_lock != brain_mob.stored_dna.unique_enzymes)))
|
|
to_chat(user, span_warning("Access denied. [name] is secured with a DNA lock."))
|
|
return FALSE
|
|
|
|
visible_message(span_notice("[user] starts to insert an MMI into [name]."))
|
|
|
|
if(!do_after(user, 4 SECONDS, target = src))
|
|
to_chat(user, span_notice("You stop inserting the MMI."))
|
|
return FALSE
|
|
if(LAZYLEN(occupants) < max_occupants)
|
|
return mmi_moved_inside(brain_obj, user)
|
|
to_chat(user, span_warning("Maximum occupants exceeded!"))
|
|
return FALSE
|
|
|
|
///proc called when a new mmi mob enters this mech
|
|
/obj/vehicle/sealed/mecha/proc/mmi_moved_inside(obj/item/mmi/brain_obj, mob/user)
|
|
if(!(Adjacent(brain_obj) && Adjacent(user)))
|
|
return FALSE
|
|
if(!brain_obj.brain_check(user))
|
|
return FALSE
|
|
|
|
var/mob/living/brain/brain_mob = brain_obj.brainmob
|
|
if(!user.transferItemToLoc(brain_obj, src))
|
|
to_chat(user, span_warning("[brain_obj] is stuck to your hand, you cannot put it in [src]!"))
|
|
return FALSE
|
|
|
|
brain_obj.set_mecha(src)
|
|
add_occupant(brain_mob)//Note this forcemoves the brain into the mech to allow relaymove
|
|
mecha_flags &= ~PANEL_OPEN //Close panel if open
|
|
mecha_flags |= SILICON_PILOT
|
|
brain_mob.reset_perspective(src)
|
|
brain_mob.remote_control = src
|
|
brain_mob.update_mouse_pointer()
|
|
setDir(SOUTH)
|
|
log_message("[brain_obj] moved in as pilot.", LOG_MECHA)
|
|
if(!internal_damage)
|
|
SEND_SOUND(brain_obj, sound('sound/vehicles/mecha/nominal.ogg',volume=50))
|
|
user.log_message("has put the MMI/posibrain of [key_name(brain_mob)] into [src]", LOG_GAME)
|
|
brain_mob.log_message("was put into [src] by [key_name(user)]", LOG_GAME, log_globally = FALSE)
|
|
return TRUE
|
|
|
|
/obj/vehicle/sealed/mecha/mob_exit(mob/M, silent = FALSE, randomstep = FALSE, forced = FALSE)
|
|
var/atom/movable/mob_container
|
|
var/turf/newloc = get_turf(src)
|
|
if(ishuman(M))
|
|
mob_container = M
|
|
else if(isbrain(M))
|
|
var/mob/living/brain/brain = M
|
|
mob_container = brain.container
|
|
else if(isAI(M))
|
|
var/mob/living/silicon/ai/AI = M
|
|
mob_container = AI
|
|
//stop listening to this signal, as the static update is now handled by the eyeobj's setLoc
|
|
AI.eyeobj?.UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
|
|
AI.eyeobj?.forceMove(newloc) //kick the eye out as well
|
|
AI.controlled_equipment = null
|
|
AI.remote_control = null
|
|
if(forced)
|
|
if(!AI.linked_core) //if the victim AI has no core
|
|
if (!AI.can_shunt || !length(AI.hacked_apcs))
|
|
AI.investigate_log("has been gibbed by being forced out of their mech.", INVESTIGATE_DEATHS)
|
|
/// If an AI with no core (and no shunting abilities) gets forced out of their mech
|
|
/// (in a way that isn't handled by the normal handling of their mech being destroyed)
|
|
/// we gib 'em here, too.
|
|
AI.gib(DROP_ALL_REMAINS)
|
|
AI = null
|
|
mecha_flags &= ~SILICON_PILOT
|
|
return ..()
|
|
else
|
|
var/obj/machinery/power/apc/emergency_shunt_apc = pick(AI.hacked_apcs)
|
|
emergency_shunt_apc.malfoccupy(AI) //get shunted into a random APC (you don't get to choose which)
|
|
AI = null
|
|
mecha_flags &= ~SILICON_PILOT
|
|
return ..()
|
|
if(!forced && !silent)
|
|
to_chat(AI, span_notice("Returning to core..."))
|
|
mecha_flags &= ~SILICON_PILOT
|
|
newloc = get_turf(AI.linked_core)
|
|
qdel(AI.linked_core)
|
|
AI.forceMove(newloc)
|
|
if(forced)
|
|
to_chat(AI, span_danger("ZZUZULU.ERR--ERRR-NEUROLOG-- PERCEP--- DIST-B**@"))
|
|
for(var/count in 1 to 5)
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(do_sparks), rand(10, 20), FALSE, AI), count SECONDS)
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(empulse), get_turf(AI), /*heavy_range = */10, /*light_range = */20), 10 SECONDS)
|
|
return ..()
|
|
else if(isliving(M))
|
|
mob_container = M
|
|
else
|
|
return ..()
|
|
var/mob/living/ejector = M
|
|
mob_container.forceMove(newloc)//ejecting mob container
|
|
log_message("[mob_container] moved out.", LOG_MECHA)
|
|
SStgui.close_user_uis(M, src)
|
|
if(istype(mob_container, /obj/item/mmi))
|
|
var/obj/item/mmi/mmi = mob_container
|
|
if(mmi.brainmob)
|
|
ejector.forceMove(mmi)
|
|
ejector.reset_perspective()
|
|
remove_occupant(ejector)
|
|
mmi.set_mecha(null)
|
|
mmi.update_appearance()
|
|
setDir(SOUTH)
|
|
SEND_SIGNAL(src, COMSIG_MECHA_MOB_EXIT)
|
|
return ..()
|
|
|
|
/obj/vehicle/sealed/mecha/add_occupant(mob/driver, control_flags)
|
|
RegisterSignal(driver, COMSIG_MOB_CLICKON, PROC_REF(on_mouseclick), TRUE)
|
|
RegisterSignal(driver, COMSIG_MOB_SAY, PROC_REF(display_speech_bubble), TRUE)
|
|
RegisterSignal(driver, COMSIG_MOVABLE_KEYBIND_FACE_DIR, PROC_REF(on_turn), TRUE)
|
|
RegisterSignal(driver, COMSIG_MOB_ALTCLICKON, PROC_REF(on_click_alt))
|
|
. = ..()
|
|
update_appearance()
|
|
|
|
/obj/vehicle/sealed/mecha/remove_occupant(mob/driver)
|
|
UnregisterSignal(driver, list(
|
|
COMSIG_MOB_CLICKON,
|
|
COMSIG_MOB_SAY,
|
|
COMSIG_MOVABLE_KEYBIND_FACE_DIR,
|
|
COMSIG_MOB_ALTCLICKON,
|
|
))
|
|
driver.clear_alert(ALERT_CHARGE)
|
|
driver.clear_alert(ALERT_MECH_DAMAGE)
|
|
if(driver.client)
|
|
driver.update_mouse_pointer()
|
|
driver.client.view_size.resetToDefault()
|
|
zoom_mode = FALSE
|
|
. = ..()
|
|
update_appearance()
|
|
|
|
/obj/vehicle/sealed/mecha/container_resist_act(mob/living/user)
|
|
if(isAI(user))
|
|
var/mob/living/silicon/ai/AI = user
|
|
if(!AI.linked_core)
|
|
to_chat(AI, span_userdanger("Inactive core destroyed. Unable to return."))
|
|
if(!AI.can_shunt || !AI.hacked_apcs.len)
|
|
to_chat(AI, span_warning("[AI.can_shunt ? "No hacked APCs available." : "No shunting capabilities."]"))
|
|
return
|
|
var/confirm = tgui_alert(AI, "Shunt to a random APC? You won't have anywhere else to go!", "Confirm Emergency Shunt", list("Yes", "No"))
|
|
if(confirm == "Yes")
|
|
/// Mechs with open cockpits can have the pilot shot by projectiles, or EMPs may destroy the AI inside
|
|
/// Alternatively, destroying the mech will shunt the AI if they can shunt, or a deadeye wizard can hit
|
|
/// them with a teleportation bolt
|
|
if (AI.stat == DEAD || AI.loc != src)
|
|
return
|
|
mob_exit(AI, forced = TRUE)
|
|
return
|
|
to_chat(user, span_notice("You begin the ejection procedure. Equipment is disabled during this process. Hold still to finish ejecting."))
|
|
is_currently_ejecting = TRUE
|
|
if(do_after(user, has_gravity() ? exit_delay : 0 , target = src))
|
|
to_chat(user, span_notice("You exit the mech."))
|
|
if(cabin_sealed)
|
|
set_cabin_seal(user, FALSE)
|
|
mob_exit(user, silent = TRUE)
|
|
else
|
|
to_chat(user, span_notice("You stop exiting the mech. Weapons are enabled again."))
|
|
is_currently_ejecting = FALSE
|