New Traitor mech: Justice. (#84097)

## About The Pull Request

Adds new mech that can be created from emaged fabricator. To craft this
mech you need:
1. Emag Fabricator to recive Justice disgine(done this, fabricator will
emit a text and sound signal that can reveal you).
2. You need to craft it like any other mech but you also need 8
telecrystals to finish constraction.
3. To board a mech you need syndicate access (traitor can get it by
purchasing an agent card).
About Mech:
Video:
https://youtu.be/QwJfO_lbLK0
Text:
Health 200
Damage 60
Only melee attacks(can't attach range weapons(can't attach anything in
his arms at all))
Block chanse 60
Speed - in safty mode 2.5 in combat mode 4.5
Attack effects:
In addition to damage, it has a 50 percent chance of cutting off an arm
or leg (does not put you to sleep or give a knockdown
effect when hit like regular combat mechs do).
If a target in crit or stun makes a finishing attack instead of a
regular attack, the mech says an epic phrase and after a second cuts off
the target's head by dashing through the target.

Special actions(can't be used if mech in safty mode):

Invisability - Makes the mech invisible for 20 seconds. If the mech is
pushed or collides with someone or if the pilot presses the invisibility
button again, the mech will no longer be invisible and will go into a 5
second cooldown (the same will happen after 20 seconds). If you attack
in invisibility, then instead of a normal attack the mech will make an
AoE 3x3 attack 1 second after pressing (the attack will be accompanied
by a sound and visual effect and for a second the mech will not be able
to move, which gives enemies time to run away from the attack). Upon
completion AoE attack, everyone( except peoples in crit and in stunned
condition) in a 3x3 radius will receive 35 damage and a 25 chance of
having one of their limbs cut off.

Charge Attack - Mech makes a dash towards the cursor at a distance of up
to 7 cells, breaking all obstacles in its path. If the obstacle is a
living person (and he is not in crit and is not stunned) then he will
receive 35 damage and a 25 chance of cutting off a limb. Cooldown 5
Seconds.
P.S.
Also added emag_act on fabricator so you can put any new secret traitor
mechs on it.
Also made the sounds of mech melee attacks to vars so that it would be
more convenient to change them.

Also fix a runtime error where if you set something in the mech's
max_equip_by_category to 0, the mech UI panel doesn't load.

## Why It's Good For The Game

Adds a traitor mech with fun and interesting mechanics. This is a new
mech with new unique abilities that turns the battle with you into a
boss fight.

## Changelog

🆑
add: New traitor mech: Justice. Emag fabricator to get it.
/🆑

---------

Co-authored-by: Jacquerel <hnevard@gmail.com>
This commit is contained in:
Xackii
2024-07-04 12:01:42 -07:00
committed by GitHub
co-authored by Jacquerel
parent 6784ac1d8d
commit 82f92cb983
30 changed files with 849 additions and 42 deletions
+3 -1
View File
@@ -35,10 +35,12 @@
#define EXOSUIT_MODULE_MARAUDER (1<<10)
/// Module is compatible with Paddy models
#define EXOSUIT_MODULE_PADDY (1<<11)
/// Module is compatible with Justice models
#define EXOSUIT_MODULE_JUSTICE (1<<12)
/// Module is compatible with "Working" Exosuit models - Ripley and Clarke
#define EXOSUIT_MODULE_WORKING (EXOSUIT_MODULE_RIPLEY | EXOSUIT_MODULE_CLARKE)
/// Module is compatible with "Combat" Exosuit models - Gygax, H.O.N.K, Durand and Phazon, or any Exosuit with an empty Concealed Weapon Bay
#define EXOSUIT_MODULE_COMBAT (EXOSUIT_MODULE_GYGAX | EXOSUIT_MODULE_HONK | EXOSUIT_MODULE_DURAND | EXOSUIT_MODULE_PHAZON | EXOSUIT_MODULE_SAVANNAH | EXOSUIT_MODULE_RETICENCE | EXOSUIT_MODULE_MARAUDER | EXOSUIT_MODULE_PADDY | EXOSUIT_MODULE_CONCEALED_WEP_BAY)
#define EXOSUIT_MODULE_COMBAT (EXOSUIT_MODULE_GYGAX | EXOSUIT_MODULE_HONK | EXOSUIT_MODULE_DURAND | EXOSUIT_MODULE_PHAZON | EXOSUIT_MODULE_SAVANNAH | EXOSUIT_MODULE_RETICENCE | EXOSUIT_MODULE_MARAUDER | EXOSUIT_MODULE_PADDY | EXOSUIT_MODULE_JUSTICE | EXOSUIT_MODULE_CONCEALED_WEP_BAY)
/// Module is compatible with "Medical" Exosuit modelsm - Odysseus
#define EXOSUIT_MODULE_MEDICAL EXOSUIT_MODULE_ODYSSEUS
@@ -134,6 +134,7 @@
#define RND_CATEGORY_MECHFAB_PHAZON "/Phazon"
#define RND_CATEGORY_MECHFAB_CLARKE "/Clarke"
#define RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV "/Savannah-Ivanov"
#define RND_CATEGORY_MECHFAB_JUSTICE "/Justice"
#define RND_SUBCATEGORY_MECHFAB_CHASSIS "/Chassis"
#define RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT "/Supported Equipment"
#define RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES "/Control Interfaces"
@@ -709,3 +709,29 @@
flags = ANIMATION_RELATIVE,
)
return ..()
/obj/effect/temp_visual/mech_sparks
name = "mech sparks"
icon_state = "mech_sparks"
duration = 0.4 SECONDS
/obj/effect/temp_visual/mech_sparks/Initialize(mapload, set_color)
. = ..()
pixel_x = rand(-16, 16)
pixel_y = rand(-8, 8)
/obj/effect/temp_visual/mech_attack_aoe_charge
name = "mech attack aoe charge"
icon = 'icons/effects/96x96.dmi'
icon_state = "mech_attack_aoe_charge"
duration = 1 SECONDS
pixel_x = -32
pixel_y = -32
/obj/effect/temp_visual/mech_attack_aoe_attack
name = "mech attack aoe attack"
icon = 'icons/effects/96x96.dmi'
icon_state = "mech_attack_aoe_attack"
duration = 0.5 SECONDS
pixel_x = -32
pixel_y = -32
@@ -907,6 +907,104 @@
RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_CHASSIS
)
//Justice (emaged only)
/datum/design/justice_chassis
name = "Exosuit Chassis (\"Justice\")"
id = "justice_chassis"
build_type = MECHFAB
build_path = /obj/item/mecha_parts/chassis/justice
materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT*20)
construction_time = 10 SECONDS
category = list(
RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
)
/datum/design/justice_torso
name = "Exosuit Torso (\"Justice\")"
id = "justice_torso"
build_type = MECHFAB
build_path = /obj/item/mecha_parts/part/justice_torso
materials = list(
/datum/material/iron = SHEET_MATERIAL_AMOUNT * 50,
/datum/material/silver = SHEET_MATERIAL_AMOUNT * 5,
)
construction_time = 30 SECONDS
category = list(
RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
)
/datum/design/justice_left_arm
name = "Exosuit Left Arm (\"Justice\")"
id = "justice_left_arm"
build_type = MECHFAB
build_path = /obj/item/mecha_parts/part/justice_left_arm
materials = list(
/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5,
/datum/material/silver = SHEET_MATERIAL_AMOUNT * 2,
)
construction_time = 10 SECONDS
category = list(
RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
)
/datum/design/justice_right_arm
name = "Exosuit Right Arm (\"Justice\")"
id = "justice_right_arm"
build_type = MECHFAB
build_path = /obj/item/mecha_parts/part/justice_right_arm
materials = list(
/datum/material/iron=SHEET_MATERIAL_AMOUNT*5,
/datum/material/silver=SHEET_MATERIAL_AMOUNT*2,
)
construction_time = 10 SECONDS
category = list(
RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
)
/datum/design/justice_left_leg
name = "Exosuit Left Leg (\"Justice\")"
id = "justice_left_leg"
build_type = MECHFAB
build_path = /obj/item/mecha_parts/part/justice_left_leg
materials = list(
/datum/material/iron=SHEET_MATERIAL_AMOUNT*5,
/datum/material/titanium=SHEET_MATERIAL_AMOUNT*2,
)
construction_time = 10 SECONDS
category = list(
RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
)
/datum/design/justice_right_leg
name = "Exosuit Right Leg (\"Justice\")"
id = "justice_right_leg"
build_type = MECHFAB
build_path = /obj/item/mecha_parts/part/justice_right_leg
materials = list(
/datum/material/iron=SHEET_MATERIAL_AMOUNT*5,
/datum/material/titanium=SHEET_MATERIAL_AMOUNT*2,
)
construction_time = 10 SECONDS
category = list(
RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
)
/datum/design/justice_armor
name = "Exosuit Armor (\"Justice\")"
id = "justice_armor"
build_type = MECHFAB
build_path = /obj/item/mecha_parts/part/justice_armor
materials = list(
/datum/material/silver=SHEET_MATERIAL_AMOUNT*10,
/datum/material/titanium=SHEET_MATERIAL_AMOUNT*10,
/datum/material/plastic=SHEET_MATERIAL_AMOUNT*5,
/datum/material/diamond=SHEET_MATERIAL_AMOUNT*1,
)
construction_time = 20 SECONDS
category = list(
RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
)
//Clarke
/datum/design/clarke_chassis
name = "Exosuit Chassis (\"Clarke\")"
@@ -40,6 +40,8 @@
var/list/experiments_to_unlock = list()
/// Whether or not this node should show on the wiki
var/show_on_wiki = TRUE
/// Hidden Mech nodes unlocked when mech fabricator emaged.
var/illegal_mech_node = FALSE
/datum/techweb_node/error_node
id = "ERROR"
@@ -194,6 +194,22 @@
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
/datum/techweb_node/justice
id = "mecha_justice"
display_name = "EXOSUIT: Justice"
description = "Justice exosuit designs"
design_ids = list(
"justice_armor",
"justice_chassis",
"justice_left_arm",
"justice_left_leg",
"justice_right_arm",
"justice_right_leg",
"justice_torso",
)
hidden = TRUE
illegal_mech_node = TRUE
/datum/techweb_node/mech_energy_guns
id = TECHWEB_NODE_MECH_ENERGY_GUNS
display_name = "Mech Energy Guns"
+1
View File
@@ -390,3 +390,4 @@
restricted = TRUE
refundable = FALSE
purchasable_from = parent_type::purchasable_from & ~UPLINK_SPY
+19 -1
View File
@@ -137,6 +137,15 @@
var/stepsound = 'sound/mecha/mechstep.ogg'
///Sound played when the mech walks
var/turnsound = 'sound/mecha/mechturn.ogg'
///Sounds for types of melee attack
var/brute_attack_sound = 'sound/weapons/punch4.ogg'
var/burn_attack_sound = 'sound/items/welder.ogg'
var/tox_attack_sound = 'sound/effects/spray2.ogg'
///Sound on wall destroying
var/destroy_wall_sound = 'sound/effects/meteorimpact.ogg'
///Melee attack verb
var/list/attack_verbs = list("hit", "hits", "hitting")
///Cooldown duration between melee punches
var/melee_cooldown = CLICK_CD_SLOW
@@ -151,6 +160,8 @@
var/is_currently_ejecting = FALSE
///Safety for weapons. Won't fire if enabled, and toggled by middle click.
var/weapons_safety = FALSE
///Don't play standard sound when set safety if TRUE.
var/safety_sound_custom = FALSE
var/datum/effect_system/fluid_spread/smoke/smoke_system
@@ -365,7 +376,8 @@
*/
/obj/vehicle/sealed/mecha/proc/set_safety(mob/user)
weapons_safety = !weapons_safety
SEND_SOUND(user, sound('sound/machines/beep.ogg', volume = 25))
if(!safety_sound_custom)
SEND_SOUND(user, sound('sound/machines/beep.ogg', volume = 25))
balloon_alert(user, "equipment [weapons_safety ? "safe" : "ready"]")
set_mouse_pointer()
SEND_SIGNAL(src, COMSIG_MECH_SAFETIES_TOGGLE, user, weapons_safety)
@@ -905,3 +917,9 @@
act.button_icon_state = "mech_lights_off"
balloon_alert(occupant, "lights [mecha_flags & LIGHTS_ON ? "on":"off"]")
act.build_all_button_icons()
/obj/vehicle/sealed/mecha/proc/melee_attack_effect(mob/living/victim, heavy)
if(heavy)
victim.Unconscious(2 SECONDS)
else
victim.Knockdown(4 SECONDS)
@@ -0,0 +1,535 @@
#define DISMEMBER_CHANCE_HIGH 50
#define DISMEMBER_CHANCE_LOW 25
#define MOVEDELAY_ANGRY 4.5
#define MOVEDELAY_SAFETY 2.5
/obj/vehicle/sealed/mecha/justice
name = "\improper Justice"
desc = "Black and red syndicate mech designed for execution orders. \
For safety reasons, the syndicate advises against standing too close."
icon_state = "justice"
base_icon_state = "justice"
movedelay = MOVEDELAY_SAFETY // fast
max_integrity = 200 // but weak
accesses = list(ACCESS_SYNDICATE)
armor_type = /datum/armor/mecha_justice
max_temperature = 40000
force = 60 // dangerous in melee
damtype = BRUTE
destruction_sleep_duration = 10
exit_delay = 10
wreckage = /obj/structure/mecha_wreckage/justice
mech_type = EXOSUIT_MODULE_JUSTICE
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
mecha_flags = ID_LOCK_ON | QUIET_STEPS | QUIET_TURNS | CAN_STRAFE | HAS_LIGHTS | MMI_COMPATIBLE | IS_ENCLOSED
destroy_wall_sound = 'sound/mecha/mech_blade_break_wall.ogg'
brute_attack_sound = 'sound/mecha/mech_blade_attack.ogg'
attack_verbs = list("cut", "cuts", "cutting")
weapons_safety = TRUE
safety_sound_custom = TRUE
max_equip_by_category = list(
MECHA_L_ARM = null,
MECHA_R_ARM = null,
MECHA_UTILITY = 3,
MECHA_POWER = 1,
MECHA_ARMOR = 2,
)
step_energy_drain = 2
/datum/armor/mecha_justice
melee = 30
bullet = 20
laser = 20
energy = 30
fire = 100
acid = 100
/obj/vehicle/sealed/mecha/justice/Initialize(mapload, built_manually)
. = ..()
RegisterSignal(src, COMSIG_MECHA_MELEE_CLICK, PROC_REF(justice_fatality)) //We do not hit those who are in crit or stun. We are finishing them.
transform = transform.Scale(1.04, 1.04)
/obj/vehicle/sealed/mecha/justice/generate_actions()
. = ..()
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/invisibility)
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/charge_attack)
/obj/vehicle/sealed/mecha/justice/update_icon_state()
. = ..()
if(LAZYLEN(occupants))
icon_state = weapons_safety ? "[base_icon_state]" : "[base_icon_state]-angry"
if(!has_gravity())
icon_state = "[icon_state]-fly"
/obj/vehicle/sealed/mecha/justice/set_safety(mob/user)
. = ..()
if(weapons_safety)
movedelay = MOVEDELAY_SAFETY
else
movedelay = MOVEDELAY_ANGRY
playsound(src, 'sound/mecha/mech_blade_safty.ogg', 75, FALSE) //everyone need to hear this sound
update_appearance(UPDATE_ICON_STATE)
/obj/vehicle/sealed/mecha/justice/Move(newloc, dir)
if(HAS_TRAIT(src, TRAIT_IMMOBILIZED))
return
. = ..()
update_appearance(UPDATE_ICON_STATE)
/// Says 1 of 3 epic phrases before attacking and make a finishing blow to targets in stun or crit after 1 SECOND.
/obj/vehicle/sealed/mecha/justice/proc/justice_fatality(datum/source, mob/living/pilot, atom/target, on_cooldown, is_adjacent)
SIGNAL_HANDLER
if(!ishuman(target))
return FALSE
var/mob/living/carbon/human/live_or_dead = target
if(live_or_dead.stat < UNCONSCIOUS && live_or_dead.getStaminaLoss() < 100)
return FALSE
var/obj/item/bodypart/check_head = live_or_dead.get_bodypart(BODY_ZONE_HEAD)
if(!check_head)
return FALSE
INVOKE_ASYNC(src, PROC_REF(finish_him), src, pilot, live_or_dead)
return TRUE
/**
* ## finish_him
*
* Target's head is cut off (if it has one)
* Attack from invisibility and charged attack have higher priority.
* Arguments:
* * finisher - Mech pilot who makes an attack.
* * him - Target at which the mech makes an attack.
*/
/obj/vehicle/sealed/mecha/justice/proc/finish_him(obj/vehicle/sealed/mecha/my_mech, mob/finisher, mob/living/him)
say(pick("Take my Justice-Slash!", "A falling leaf...", "Justice is quite a lonely path"), forced = "Justice Mech")
playsound(src, 'sound/mecha/mech_stealth_pre_attack.ogg', 75, FALSE)
if(!do_after(finisher, 1 SECONDS, him))
return
if(QDELETED(finisher))
return
if(QDELETED(him))
return
if(QDELETED(my_mech))
return
if(!LAZYLEN(my_mech.occupants))
return
var/turf/finish_turf = get_step(him, get_dir(my_mech, him))
var/turf/for_line_turf = get_turf(my_mech)
var/obj/item/bodypart/in_your_head = him.get_bodypart(BODY_ZONE_HEAD)
in_your_head?.dismember(BRUTE)
playsound(src, brute_attack_sound, 75, FALSE)
for_line_turf.Beam(src, icon_state = "mech_charge", time = 8)
forceMove(finish_turf)
/obj/vehicle/sealed/mecha/justice/melee_attack_effect(mob/living/victim, heavy)
if(!heavy)
victim.Knockdown(4 SECONDS)
return
if(!prob(DISMEMBER_CHANCE_HIGH))
return
var/obj/item/bodypart/cut_bodypart = victim.get_bodypart(pick(BODY_ZONE_R_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG))
cut_bodypart?.dismember(BRUTE)
/obj/vehicle/sealed/mecha/justice/mob_exit(mob/M, silent, randomstep, forced)
. = ..()
if(alpha == 255)
return
animate(src, alpha = 255, time = 0.5 SECONDS)
playsound(src, 'sound/mecha/mech_stealth_effect.ogg' , 75, FALSE)
/obj/vehicle/sealed/mecha/justice/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
if(LAZYLEN(occupants))
if(prob(60))
new /obj/effect/temp_visual/mech_sparks(get_turf(src))
playsound(src, 'sound/mecha/mech_stealth_effect.ogg' , 75, FALSE)
return
return ..()
/datum/action/vehicle/sealed/mecha/invisibility
name = "Invisibility"
button_icon_state = "mech_stealth_off"
/// Is invisibility activated.
var/on = FALSE
/// Recharge check.
var/charge = TRUE
/// Varset for invisibility timer
var/invisibility_timer
/// Energy cost to become invisibile
var/energy_cost = 200
/// Aoe pre attack sound.
var/stealth_pre_attack_sound = 'sound/mecha/mech_stealth_pre_attack.ogg'
/// Aoe attack sound.
var/stealth_attack_sound = 'sound/mecha/mech_stealth_attack.ogg'
/datum/action/vehicle/sealed/mecha/invisibility/set_chassis(passed_chassis)
. = ..()
RegisterSignal(chassis, COMSIG_MECH_SAFETIES_TOGGLE, PROC_REF(on_toggle_safety))
/// update button icon when toggle safety.
/datum/action/vehicle/sealed/mecha/invisibility/proc/on_toggle_safety()
SIGNAL_HANDLER
build_all_button_icons(UPDATE_BUTTON_STATUS)
/datum/action/vehicle/sealed/mecha/invisibility/Trigger(trigger_flags)
. = ..()
if(!.)
return
on = !on
if(on)
invisibility_on()
else
invisibility_off()
/datum/action/vehicle/sealed/mecha/invisibility/IsAvailable(feedback)
. = ..()
if(!.)
return FALSE
if(!chassis.has_charge(energy_cost))
if(feedback)
owner.balloon_alert(owner, "not enough energy!")
return FALSE
if(chassis.weapons_safety)
if(feedback)
owner.balloon_alert(owner, "safety is on!")
return FALSE
if(!charge)
if(feedback)
owner.balloon_alert(owner, "recharging!")
return FALSE
return TRUE
///Called when invisibility activated.
/datum/action/vehicle/sealed/mecha/invisibility/proc/invisibility_on()
new /obj/effect/temp_visual/mech_sparks(get_turf(chassis))
playsound(chassis, 'sound/mecha/mech_stealth_effect.ogg' , 75, FALSE)
check_charge_attack()
animate(chassis, alpha = 0, time = 0.5 SECONDS)
button_icon_state = "mech_stealth_on"
invisibility_timer = addtimer(CALLBACK(src, PROC_REF(end_stealth)), 20 SECONDS)
RegisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK, PROC_REF(stealth_attack_aoe))
RegisterSignal(chassis, COMSIG_MOVABLE_BUMP, PROC_REF(bumb_on))
RegisterSignal(chassis, COMSIG_ATOM_BUMPED, PROC_REF(bumbed_on))
RegisterSignal(chassis, COMSIG_ATOM_TAKE_DAMAGE, PROC_REF(take_damage))
chassis.use_energy(energy_cost)
build_all_button_icons()
///Called when invisibility deactivated.
/datum/action/vehicle/sealed/mecha/invisibility/proc/invisibility_off()
new /obj/effect/temp_visual/mech_sparks(get_turf(chassis))
playsound(chassis, 'sound/mecha/mech_stealth_effect.ogg' , 75, FALSE)
invisibility_timer = null
charge = FALSE
addtimer(CALLBACK(src, PROC_REF(charge)), 5 SECONDS)
button_icon_state = "mech_stealth_cooldown"
animate(chassis, alpha = 255, time = 0.5 SECONDS)
UnregisterSignal(chassis, list(
COMSIG_MECHA_MELEE_CLICK,
COMSIG_MOVABLE_BUMP,
COMSIG_ATOM_BUMPED,
COMSIG_ATOM_TAKE_DAMAGE
))
build_all_button_icons()
///Check if mech use charge attack and deactivate it when we activate invisibility.
/datum/action/vehicle/sealed/mecha/invisibility/proc/check_charge_attack()
for(var/mob/living/occupant in chassis.occupants)
var/datum/action/vehicle/sealed/mecha/charge_attack/charge_action = LAZYACCESSASSOC(chassis.occupant_actions, occupant, /datum/action/vehicle/sealed/mecha/charge_attack)
if(charge_action?.on)
charge_action.on = !on
charge_action.charge_attack_off()
/**
* ## end_stealth
*
* Called when mech runs out of invisibility time.
*/
/datum/action/vehicle/sealed/mecha/invisibility/proc/end_stealth()
make_visible()
/**
* ## bumb_on
*
* Called when mech bumb on somthing. If is living somthing shutdown mech invisibility.
*/
/datum/action/vehicle/sealed/mecha/invisibility/proc/bumb_on(obj/vehicle/sealed/mecha/our_mech, atom/obstacle)
SIGNAL_HANDLER
if(!isliving(obstacle))
return
make_visible()
/**
* ## bumbed_on
*
* Called when somthing bumbed on mech. If is living somthing shutdown mech invisibility.
*/
/datum/action/vehicle/sealed/mecha/invisibility/proc/bumbed_on(obj/vehicle/sealed/mecha/our_mech, atom/movable/bumped_atom)
SIGNAL_HANDLER
if(!isliving(bumped_atom))
return
make_visible()
/**
* ## take_damage
*
* Called when mech take damage. Shutdown mech invisibility.
*/
/datum/action/vehicle/sealed/mecha/invisibility/proc/take_damage(obj/vehicle/sealed/mecha/our_mech)
SIGNAL_HANDLER
make_visible()
/**
* ## make_visible
*
* Called when somthing force invisibility shutdown.
*/
/datum/action/vehicle/sealed/mecha/invisibility/proc/make_visible()
if(!on)
return
on = !on
invisibility_off()
/**
* Proc makes an AOE attack after 1 SECOND.
* Called by the mech pilot when he is in stealth mode and wants to attack.
* During this, mech cannot move.
*/
/datum/action/vehicle/sealed/mecha/invisibility/proc/stealth_attack_aoe(datum/source, mob/living/pilot, atom/target, on_cooldown, is_adjacent)
SIGNAL_HANDLER
if(!charge)
return FALSE
if(chassis.alpha != 0)
UnregisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK)
return FALSE
UnregisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK)
new /obj/effect/temp_visual/mech_attack_aoe_charge(get_turf(chassis))
ADD_TRAIT(chassis, TRAIT_IMMOBILIZED, REF(src))
playsound(chassis, stealth_pre_attack_sound, 75, FALSE)
addtimer(CALLBACK(src, PROC_REF(attack_in_aoe), pilot), 1 SECONDS)
return TRUE
/**
* ## attack_in_aoe
*
* Brings mech out of invisibility.
* Deal everyone in range 3x3 35 damage and 25 chanse to cut off limb.
* Arguments:
* * pilot - occupant inside mech.
*/
/datum/action/vehicle/sealed/mecha/invisibility/proc/attack_in_aoe(mob/living/pilot)
invisibility_off()
new /obj/effect/temp_visual/mech_attack_aoe_attack(get_turf(chassis))
for(var/mob/living/something_living in range(1, get_turf(chassis)))
if(something_living.stat >= UNCONSCIOUS)
continue
if(something_living.getStaminaLoss() >= 100)
continue
if(something_living == pilot)
continue
if(prob(DISMEMBER_CHANCE_LOW))
var/obj/item/bodypart/cut_bodypart = something_living.get_bodypart(pick(BODY_ZONE_R_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG))
cut_bodypart?.dismember(BRUTE)
something_living.apply_damage(35, BRUTE)
playsound(chassis, stealth_attack_sound, 75, FALSE)
REMOVE_TRAIT(chassis, TRAIT_IMMOBILIZED, REF(src))
on = !on
charge = FALSE
button_icon_state = "mech_stealth_cooldown"
build_all_button_icons()
addtimer(CALLBACK(src, PROC_REF(charge)), 5 SECONDS)
/**
* ## charge
*
* Recharge invisibility action after 5 SECONDS.
*/
/datum/action/vehicle/sealed/mecha/invisibility/proc/charge()
button_icon_state = "mech_stealth_off"
charge = TRUE
build_all_button_icons()
/datum/action/vehicle/sealed/mecha/charge_attack
name = "Charge Attack"
button_icon_state = "mech_charge_off"
/// Is charge attack activated.
var/on = FALSE
/// Recharge check.
var/charge = TRUE
/// Energy cost to perform charge attack
var/energy_cost = 400
/// Maximum range of charge attack.
var/max_charge_range = 7
/// Sound when mech do charge attack.
var/charge_attack_sound = 'sound/mecha/mech_charge_attack.ogg'
/datum/action/vehicle/sealed/mecha/charge_attack/set_chassis(passed_chassis)
. = ..()
RegisterSignal(chassis, COMSIG_MECH_SAFETIES_TOGGLE, PROC_REF(on_toggle_safety))
/// update button icon when toggle safety.
/datum/action/vehicle/sealed/mecha/charge_attack/proc/on_toggle_safety()
SIGNAL_HANDLER
build_all_button_icons(UPDATE_BUTTON_STATUS)
/datum/action/vehicle/sealed/mecha/charge_attack/Trigger(trigger_flags)
. = ..()
if(!.)
return
on = !on
if(on)
charge_attack_on()
else
charge_attack_off()
/datum/action/vehicle/sealed/mecha/charge_attack/IsAvailable(feedback)
. = ..()
if(!.)
return FALSE
if(!chassis.has_charge(energy_cost))
if(feedback)
owner.balloon_alert(owner, "not enough energy!")
return FALSE
if(chassis.weapons_safety)
if(feedback)
owner.balloon_alert(owner, "safety is on!")
return FALSE
if(!charge)
if(feedback)
owner.balloon_alert(owner, "recharging!")
return FALSE
return TRUE
///Called when charge attack activated
/datum/action/vehicle/sealed/mecha/charge_attack/proc/charge_attack_on()
check_visability()
button_icon_state = "mech_charge_on"
RegisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK, PROC_REF(click_try_charge))
build_all_button_icons()
///Called when charge attack deactivated
/datum/action/vehicle/sealed/mecha/charge_attack/proc/charge_attack_off()
button_icon_state = "mech_charge_off"
UnregisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK)
build_all_button_icons()
///Check if mech use invisibility and deactivate it when we activate charge attack.
/datum/action/vehicle/sealed/mecha/charge_attack/proc/check_visability()
for(var/who_inside in chassis.occupants)
var/mob/living/occupant = who_inside
var/datum/action/vehicle/sealed/mecha/invisibility/stealth_action = LAZYACCESSASSOC(chassis.occupant_actions, occupant, /datum/action/vehicle/sealed/mecha/invisibility)
if(stealth_action?.on)
stealth_action.make_visible()
///Called when mech attacks with charge attack enabled.
/datum/action/vehicle/sealed/mecha/charge_attack/proc/click_try_charge(datum/source, mob/living/pilot, atom/target, on_cooldown, is_adjacent)
SIGNAL_HANDLER
var/turf = get_turf(target)
if(!on)
UnregisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK)
return FALSE
if(isnull(turf))
pilot.balloon_alert(pilot, "invalid direction!")
return FALSE
if(!charge)
pilot.balloon_alert(pilot, "recharging!")
return FALSE
else
if(charge_attack(pilot, turf))
return TRUE
return FALSE
/**
* ## charge_attack
*
* Deal everyone in line for mech location to mouse location 35 damage and 25 chanse to cut off limb.
* Teleport mech to the end of line.
* Arguments:
* * charger - occupant inside mech.
* * target - occupant inside mech.
*/
/datum/action/vehicle/sealed/mecha/charge_attack/proc/charge_attack(mob/living/charger, turf/target)
var/turf/start_charge_here = get_turf(charger)
var/charge_range = min(get_dist_euclidean(start_charge_here, target), max_charge_range)
var/turf/but_we_gonna_here = get_ranged_target_turf(start_charge_here, get_dir(start_charge_here, target), floor(charge_range))
var/turf/here_we_go = start_charge_here
for(var/turf/line_turf in get_line(get_step(start_charge_here, get_dir(start_charge_here, target)), but_we_gonna_here))
if(get_turf(charger) == get_turf(line_turf))
continue
if(isclosedturf(line_turf))
break
var/obj/machinery/power/supermatter_crystal/funny_crystal = locate() in line_turf
if(funny_crystal)
funny_crystal.Bumped(chassis)
break
var/obj/machinery/door/airlock/like_a_wall = locate() in line_turf
if(like_a_wall?.density)
break
if(locate(/obj/structure/window) in line_turf)
break
for(var/mob/living/something_living in line_turf.contents)
if(something_living.stat >= UNCONSCIOUS || something_living.getStaminaLoss() >= 100 || something_living == charger)
continue
if(prob(DISMEMBER_CHANCE_LOW))
var/obj/item/bodypart/cut_bodypart = something_living.get_bodypart(pick(BODY_ZONE_R_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG, BODY_ZONE_HEAD))
cut_bodypart?.dismember(BRUTE)
something_living.apply_damage(35, BRUTE)
here_we_go = line_turf
// If the mech didn't move, it didn't charge
if(here_we_go == start_charge_here)
charger.balloon_alert(charger, "invalid direction!")
return FALSE
chassis.forceMove(here_we_go)
start_charge_here.Beam(chassis, icon_state = "mech_charge", time = 8)
playsound(chassis, charge_attack_sound, 75, FALSE)
on = !on
chassis.use_energy(energy_cost)
UnregisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK)
charge = FALSE
button_icon_state = "mech_charge_cooldown"
build_all_button_icons()
addtimer(CALLBACK(src, PROC_REF(charge)), 5 SECONDS)
return TRUE
/**
* ## charge
*
* Recharge charge attack action after 5 SECONDS.
*/
/datum/action/vehicle/sealed/mecha/charge_attack/proc/charge()
charge = TRUE
button_icon_state = "mech_charge_off"
build_all_button_icons()
/obj/vehicle/sealed/mecha/justice/loaded
equip_by_category = list(
MECHA_L_ARM = null,
MECHA_R_ARM = null,
MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/radio, /obj/item/mecha_parts/mecha_equipment/air_tank/full, /obj/item/mecha_parts/mecha_equipment/thrusters/ion),
MECHA_POWER = list(),
MECHA_ARMOR = list(),
)
/obj/vehicle/sealed/mecha/justice/loaded/populate_parts()
cell = new /obj/item/stock_parts/power_store/cell/bluespace(src)
scanmod = new /obj/item/stock_parts/scanning_module/triphasic(src)
capacitor = new /obj/item/stock_parts/capacitor/quadratic(src)
servo = new /obj/item/stock_parts/servo/femto(src)
update_part_values()
#undef DISMEMBER_CHANCE_HIGH
#undef DISMEMBER_CHANCE_LOW
#undef MOVEDELAY_ANGRY
#undef MOVEDELAY_SAFETY
+25 -1
View File
@@ -52,10 +52,14 @@
//looping sound for printing items
var/datum/looping_sound/lathe_print/print_sound
/// Local designs that only this mechfab have(using when mechfab emaged so it's illegal designs).
var/list/datum/design/illegal_local_designs
/obj/machinery/mecha_part_fabricator/Initialize(mapload)
print_sound = new(src, FALSE)
rmat = AddComponent(/datum/component/remote_materials, mapload && link_on_init)
cached_designs = list()
illegal_local_designs = list()
RefreshParts() //Recalculating local material sizes if the fab isn't linked
return ..()
@@ -142,6 +146,26 @@
balloon_alert(user, "rotated to [dir2text(dir)].")
return CLICK_ACTION_SUCCESS
/obj/machinery/mecha_part_fabricator/emag_act(mob/user, obj/item/card/emag/emag_card)
if(obj_flags & EMAGGED)
return FALSE
if(user.job != JOB_ROBOTICIST)
to_chat(user, span_warning("You clicking and typing but dont understand what to do with it"))
return FALSE
obj_flags |= EMAGGED
for(var/found_illegal_mech_nods in SSresearch.techweb_nodes)
var/datum/techweb_node/illegal_mech_node = SSresearch.techweb_nodes[found_illegal_mech_nods]
if(!illegal_mech_node?.illegal_mech_node)
continue
for(var/id in illegal_mech_node.design_ids)
var/datum/design/illegal_mech_design = SSresearch.techweb_design_by_id(id)
illegal_local_designs |= illegal_mech_design
cached_designs |= illegal_mech_design
say("R$c!i&ed ERROR de#i$ns. C@n%ec$%ng to ~NULL~ se%ve$s.")
playsound(src, 'sound/machines/uplinkerror.ogg', 50, TRUE)
update_static_data_for_all_viewers()
return TRUE
/**
* Updates the `final_sets` and `buildable_parts` for the current mecha fabricator.
*/
@@ -418,7 +442,7 @@
if(!istext(design_id))
continue
if(!stored_research.researched_designs.Find(design_id))
if(!(stored_research.researched_designs.Find(design_id) || is_type_in_list(SSresearch.techweb_design_by_id(design_id), illegal_local_designs)))
continue
var/datum/design/design = SSresearch.techweb_design_by_id(design_id)
@@ -21,15 +21,15 @@
mecha_attacker.do_attack_animation(src)
switch(mecha_attacker.damtype)
if(BRUTE)
playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE)
playsound(src, mecha_attacker.brute_attack_sound, 50, TRUE)
if(BURN)
playsound(src, 'sound/items/welder.ogg', 50, TRUE)
playsound(src, mecha_attacker.burn_attack_sound, 50, TRUE)
else
return 0
mecha_attacker.visible_message(span_danger("[mecha_attacker] hits [src]!"), span_danger("You hit [src]!"), null, COMBAT_MESSAGE_RANGE)
if(prob(hardness + mecha_attacker.force) && mecha_attacker.force > 20)
dismantle_wall(1)
playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
playsound(src, mecha_attacker.destroy_wall_sound, 100, TRUE)
else
add_dent(WALL_DENT_HIT)
..()
@@ -58,9 +58,9 @@
mecha_attacker.do_attack_animation(src)
switch(mecha_attacker.damtype)
if(BRUTE)
playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE)
playsound(src, mecha_attacker.brute_attack_sound, 50, TRUE)
if(BURN)
playsound(src, 'sound/items/welder.ogg', 50, TRUE)
playsound(src, mecha_attacker.burn_attack_sound, 50, TRUE)
else
return 0
mecha_attacker.visible_message(span_danger("[mecha_attacker] hits [src]!"), span_danger("You hit [src]!"), null, COMBAT_MESSAGE_RANGE)
@@ -88,29 +88,34 @@
if(mecha_attacker.damtype == BRUTE)
step_away(src, mecha_attacker, 15)
var/obj/item/bodypart/selected_zone = get_bodypart(pick(BODY_ZONE_CHEST, BODY_ZONE_CHEST, BODY_ZONE_CHEST, BODY_ZONE_HEAD))
if(selected_zone)
var/dmg = rand(mecha_attacker.force * 0.5, mecha_attacker.force)
switch(mecha_attacker.damtype)
if(BRUTE)
if(mecha_attacker.force > 35) // durand and other heavy mechas
Unconscious(20)
else if(mecha_attacker.force > 20 && !IsKnockdown()) // lightweight mechas like gygax
Knockdown(40)
var/dmg = rand(mecha_attacker.force * 0.5, mecha_attacker.force)
switch(mecha_attacker.damtype)
if(BRUTE)
if(mecha_attacker.force > 35) // durand and other heavy mechas
mecha_attacker.melee_attack_effect(src, heavy = TRUE)
else if(mecha_attacker.force > 20 && !IsKnockdown()) // lightweight mechas like gygax
mecha_attacker.melee_attack_effect(src, heavy = FALSE)
if(selected_zone)
selected_zone.receive_damage(dmg, 0, updating_health = TRUE)
playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE)
if(FIRE)
selected_zone.receive_damage(0, dmg, updating_health = TRUE)
playsound(src, 'sound/items/welder.ogg', 50, TRUE)
if(TOX)
playsound(src, 'sound/effects/spray2.ogg', 50, TRUE)
if((reagents.get_reagent_amount(/datum/reagent/cryptobiolin) + mecha_attacker.force) < mecha_attacker.force*2)
reagents.add_reagent(/datum/reagent/cryptobiolin, mecha_attacker.force/2)
if((reagents.get_reagent_amount(/datum/reagent/toxin) + mecha_attacker.force) < mecha_attacker.force*2)
reagents.add_reagent(/datum/reagent/toxin, mecha_attacker.force/2.5)
else
return 0
. = dmg
visible_message(span_danger("[mecha_attacker.name] hits [src]!"), \
span_userdanger("[mecha_attacker.name] hits you!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, list(mecha_attacker))
to_chat(mecha_attacker, span_danger("You hit [src]!"))
apply_damage(dmg, BRUTE)
playsound(src, mecha_attacker.brute_attack_sound, 50, TRUE)
if(FIRE)
if(selected_zone)
selected_zone.receive_damage(0, dmg, updating_health = TRUE)
else
apply_damage(dmg, BURN)
playsound(src, mecha_attacker.burn_attack_sound, 50, TRUE)
if(TOX)
playsound(src, mecha_attacker.tox_attack_sound, 50, TRUE)
if((reagents.get_reagent_amount(/datum/reagent/cryptobiolin) + mecha_attacker.force) < mecha_attacker.force*2)
reagents.add_reagent(/datum/reagent/cryptobiolin, mecha_attacker.force/2)
if((reagents.get_reagent_amount(/datum/reagent/toxin) + mecha_attacker.force) < mecha_attacker.force*2)
reagents.add_reagent(/datum/reagent/toxin, mecha_attacker.force/2.5)
else
return 0
. = dmg
visible_message(span_danger("[mecha_attacker.name] [mecha_attacker.attack_verbs[1]] [src]!"), \
span_userdanger("[mecha_attacker.name] [mecha_attacker.attack_verbs[2]] you!"), span_hear("You hear a sickening sound of flesh [mecha_attacker.attack_verbs[3]] flesh!"), COMBAT_MESSAGE_RANGE, list(mecha_attacker))
to_chat(mecha_attacker, span_danger("You [mecha_attacker.attack_verbs[1]] [src]!"))
..()
@@ -819,3 +819,39 @@
outer_plating = /obj/item/stack/sheet/plasteel
outer_plating_amount = 5
//Justice
/datum/component/construction/unordered/mecha_chassis/justice
result = /datum/component/construction/mecha/justice
steps = list(
/obj/item/mecha_parts/part/justice_torso,
/obj/item/mecha_parts/part/justice_left_arm,
/obj/item/mecha_parts/part/justice_right_arm,
/obj/item/mecha_parts/part/justice_left_leg,
/obj/item/mecha_parts/part/justice_right_leg
)
/datum/component/construction/mecha/justice
result = /obj/vehicle/sealed/mecha/justice
base_icon = "justice"
inner_plating = /obj/item/stack/telecrystal
inner_plating_amount = 8
outer_plating = /obj/item/mecha_parts/part/justice_armor
outer_plating_amount = 1
/datum/component/construction/mecha/justice/get_circuit_steps()
return list()
/datum/component/construction/mecha/justice/get_inner_plating_steps()
return list(
list(
"key" = inner_plating,
"amount" = inner_plating_amount,
"back_key" = TOOL_SCREWDRIVER,
"desc" = "The power cell is secured, and [inner_plating_amount] <b>telecrystals</b> can be added.",
"forward_message" = "added telecrystal",
"backward_message" = "unsecured power cell"
)
)
@@ -332,6 +332,42 @@
desc="Savannah-Ivanov armor plates. They are uniquely shaped and reinforced to deal with the stresses of two pilots, grandiose leaps, and missiles."
icon_state = "savannah_ivanov_armor"
// Justice
/obj/item/mecha_parts/chassis/justice
name = "\improper Justice chassis"
construct_type = /datum/component/construction/unordered/mecha_chassis/justice
/obj/item/mecha_parts/part/justice_torso
name="\improper Justice torso"
desc="A Justice torso part."
icon_state = "justice_torso"
/obj/item/mecha_parts/part/justice_left_arm
name="\improper Justice left arm"
desc="A Justice left arm."
icon_state = "justice_l_arm"
/obj/item/mecha_parts/part/justice_right_arm
name="\improper Justice right arm"
desc="A Justice left arm."
icon_state = "justice_r_arm"
/obj/item/mecha_parts/part/justice_left_leg
name="\improper Justice left leg"
desc="A Justice left leg."
icon_state = "justice_l_leg"
/obj/item/mecha_parts/part/justice_right_leg
name="\improper Justice right leg"
desc="A Justice left leg."
icon_state = "justice_r_leg"
/obj/item/mecha_parts/part/justice_armor
name="Justice armor"
desc="Justice armor plates."
icon_state = "justice_armor"
///////// Circuitboards
/obj/item/circuitboard/mecha
+12 -11
View File
@@ -111,16 +111,17 @@
var/module_index = 0
for(var/category in max_equip_by_category)
var/max_per_category = max_equip_by_category[category]
for(var/i = 1 to max_per_category)
var/equipment = equip_by_category[category]
var/is_slot_free = islist(equipment) ? i > length(equipment) : isnull(equipment)
if(is_slot_free)
data += list(list(
"slot" = category
))
if(ui_selected_module_index == module_index)
ui_selected_module_index = null
else
if(max_per_category)
for(var/i = 1 to max_per_category)
var/equipment = equip_by_category[category]
var/is_slot_free = islist(equipment) ? i > length(equipment) : isnull(equipment)
if(is_slot_free)
data += list(list(
"slot" = category
))
if(ui_selected_module_index == module_index)
ui_selected_module_index = null
continue
var/obj/item/mecha_parts/mecha_equipment/module = islist(equipment) ? equipment[i] : equipment
data += list(list(
"slot" = category,
@@ -140,7 +141,7 @@
))
if(isnull(ui_selected_module_index))
ui_selected_module_index = module_index
module_index++
module_index++
return data
/obj/vehicle/sealed/mecha/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
@@ -245,3 +245,8 @@
/obj/item/mecha_parts/part/odysseus_right_arm,
/obj/item/mecha_parts/part/odysseus_left_leg,
/obj/item/mecha_parts/part/odysseus_right_leg)
/obj/structure/mecha_wreckage/justice
name = "\improper Justice wreckage"
icon_state = "justice-broken"
welder_salvage = list(/obj/item/stack/sheet/iron, /obj/item/stack/rods)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 904 KiB

After

Width:  |  Height:  |  Size: 904 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -6101,6 +6101,7 @@
#include "code\modules\vehicles\mecha\combat\durand.dm"
#include "code\modules\vehicles\mecha\combat\gygax.dm"
#include "code\modules\vehicles\mecha\combat\honker.dm"
#include "code\modules\vehicles\mecha\combat\justice.dm"
#include "code\modules\vehicles\mecha\combat\marauder.dm"
#include "code\modules\vehicles\mecha\combat\phazon.dm"
#include "code\modules\vehicles\mecha\combat\reticence.dm"