diff --git a/code/__DEFINES/cooldowns.dm b/code/__DEFINES/cooldowns.dm index 11c65df87f0..deee90e4424 100644 --- a/code/__DEFINES/cooldowns.dm +++ b/code/__DEFINES/cooldowns.dm @@ -31,7 +31,7 @@ //Mecha cooldowns #define COOLDOWN_MECHA_MESSAGE "mecha_message" -#define COOLDOWN_MECHA_EQUIPMENT "mecha_equipment" +#define COOLDOWN_MECHA_EQUIPMENT(type) ("mecha_equip_[type]") #define COOLDOWN_MECHA_ARMOR "mecha_armor" #define COOLDOWN_MECHA_MELEE_ATTACK "mecha_melee" #define COOLDOWN_MECHA_SMOKE "mecha_smoke" diff --git a/code/__DEFINES/mecha.dm b/code/__DEFINES/mecha.dm index 80dff1870a9..9e698604b8d 100644 --- a/code/__DEFINES/mecha.dm +++ b/code/__DEFINES/mecha.dm @@ -23,11 +23,26 @@ #define MECHA_MELEE (1 << 0) #define MECHA_RANGED (1 << 1) -#define MECHA_FRONT_ARMOUR 1 -#define MECHA_SIDE_ARMOUR 2 -#define MECHA_BACK_ARMOUR 3 +#define MECHA_FRONT_ARMOUR "mechafront" +#define MECHA_SIDE_ARMOUR "mechaside" +#define MECHA_BACK_ARMOUR "mechaback" + +#define MECHA_WEAPON "mecha_weapon" //l and r arm weapon type +#define MECHA_L_ARM "mecha_l_arm" +#define MECHA_R_ARM "mecha_r_arm" +#define MECHA_UTILITY "mecha_utility" +#define MECHA_POWER "mecha_power" +#define MECHA_ARMOR "mecha_armor" #define MECHA_LOCKED 0 #define MECHA_SECURE_BOLTS 1 #define MECHA_LOOSE_BOLTS 2 #define MECHA_OPEN_HATCH 3 + +// Some mechs must (at least for now) use snowflake handling of their UI elements, these defines are for that +// when changing MUST update the same-named tsx file constants +#define MECHA_SNOWFLAKE_ID_SLEEPER "sleeper_snowflake" +#define MECHA_SNOWFLAKE_ID_SYRINGE "syringe_snowflake" +#define MECHA_SNOWFLAKE_ID_MODE "mode_snowflake" +#define MECHA_SNOWFLAKE_ID_EXTINGUISHER "extinguisher_snowflake" +#define MECHA_SNOWFLAKE_ID_EJECTOR "ejector_snowflake" diff --git a/code/modules/asset_cache/assets/mecha.dm b/code/modules/asset_cache/assets/mecha.dm new file mode 100644 index 00000000000..ced0fcf234b --- /dev/null +++ b/code/modules/asset_cache/assets/mecha.dm @@ -0,0 +1,5 @@ +/datum/asset/spritesheet/mechaarmor + name = "mechaarmor" + +/datum/asset/spritesheet/mechaarmor/create_spritesheets() + InsertAll("", 'icons/ui_icons/mecha/armor.dmi') diff --git a/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm b/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm index f9390fe6900..d68c5b38764 100644 --- a/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm +++ b/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm @@ -74,7 +74,7 @@ targets_from = WEAKREF(M) allow_movement_on_non_turfs = TRUE //duh var/do_ranged = 0 - for(var/equip in mecha.equipment) + for(var/equip in mecha.flat_equipment) var/obj/item/mecha_parts/mecha_equipment/ME = equip if(ME.range & MECHA_RANGED) do_ranged = 1 @@ -136,7 +136,7 @@ /mob/living/simple_animal/hostile/syndicate/mecha_pilot/proc/mecha_reload() if(mecha) - for(var/equip in mecha.equipment) + for(var/equip in mecha.flat_equipment) var/obj/item/mecha_parts/mecha_equipment/ME = equip if(ME.needs_rearm()) ME.rearm() @@ -145,7 +145,7 @@ /mob/living/simple_animal/hostile/syndicate/mecha_pilot/proc/get_mecha_equip_by_flag(flag = MECHA_RANGED) . = list() if(mecha) - for(var/equip in mecha.equipment) + for(var/equip in mecha.flat_equipment) var/obj/item/mecha_parts/mecha_equipment/ME = equip if((ME.range & flag) && ME.action_checks(ME)) //this looks weird, but action_checks() just needs any atom, so I spoofed it here . += ME diff --git a/code/modules/research/designs/mecha_designs.dm b/code/modules/research/designs/mecha_designs.dm index 7b4eb5c8e45..0652e33e801 100644 --- a/code/modules/research/designs/mecha_designs.dm +++ b/code/modules/research/designs/mecha_designs.dm @@ -392,32 +392,22 @@ construction_time = 100 category = list("Exosuit Equipment") -/datum/design/mech_energy_relay - name = "Exosuit Module (Tesla Energy Relay)" - desc = "Tesla Energy Relay" - id = "mech_energy_relay" - build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay - materials = list(/datum/material/iron=10000,/datum/material/glass = 2000,/datum/material/gold=2000,/datum/material/silver=3000) - construction_time = 100 - category = list("Exosuit Equipment") - /datum/design/mech_ccw_armor name = "Exosuit Module (Reactive Armor Booster Module)" - desc = "Exosuit-mounted armor booster." + desc = "Exosuit-mounted melee armor booster." id = "mech_ccw_armor" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster + build_path = /obj/item/mecha_parts/mecha_equipment/armor/anticcw_armor_booster materials = list(/datum/material/iron=20000,/datum/material/silver=5000) construction_time = 100 category = list("Exosuit Equipment") /datum/design/mech_proj_armor name = "Exosuit Module (Reflective Armor Booster Module)" - desc = "Exosuit-mounted armor booster." + desc = "Exosuit-mounted ranged armor booster." id = "mech_proj_armor" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster + build_path = /obj/item/mecha_parts/mecha_equipment/armor/antiproj_armor_booster materials = list(/datum/material/iron=20000,/datum/material/gold=5000) construction_time = 100 category = list("Exosuit Equipment") diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index bfadc98b247..25c8633a06a 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -1804,7 +1804,6 @@ prereq_ids = list("adv_mecha", "bluespace_power") design_ids = list( "mech_ccw_armor", - "mech_energy_relay", "mech_proj_armor", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) diff --git a/code/modules/uplink/uplink_items/job.dm b/code/modules/uplink/uplink_items/job.dm index 45df56e2379..364594955e8 100644 --- a/code/modules/uplink/uplink_items/job.dm +++ b/code/modules/uplink/uplink_items/job.dm @@ -241,16 +241,6 @@ cost = 15 restricted_roles = list(JOB_CLOWN) -/datum/uplink_item/role_restricted/concealed_weapon_bay - name = "Concealed Weapon Bay" - desc = "A modification for non-combat mechas that allows them to equip one piece of equipment designed for combat mechs. \ - It also hides the equipped weapon from plain sight. \ - Only one can fit on a mecha." - progression_minimum = 30 MINUTES - item = /obj/item/mecha_parts/concealed_weapon_bay - cost = 3 - restricted_roles = list(JOB_ROBOTICIST, JOB_RESEARCH_DIRECTOR) - /datum/uplink_item/role_restricted/clowncar name = "Clown Car" desc = "The Clown Car is the ultimate transportation method for any worthy clown! \ diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm index 29269963665..fe14aaf1988 100644 --- a/code/modules/vehicles/mecha/_mecha.dm +++ b/code/modules/vehicles/mecha/_mecha.dm @@ -33,6 +33,7 @@ light_on = FALSE light_range = 8 generic_canpass = FALSE + hud_possible = list(DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) ///What direction will the mech face when entered/powered on? Defaults to South. var/dir_in = SOUTH ///How much energy the mech will consume each time it moves. This variable is a backup for when leg actuators affect the energy drain. @@ -43,10 +44,8 @@ var/melee_energy_drain = 15 ///The minimum amount of energy charge consumed by leg overload var/overload_step_energy_drain_min = 100 - ///chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. - var/deflect_chance = 10 - ///Modifiers for directional armor - var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5) + ///Modifiers for directional damage reduction + var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 0.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 1.5) ///if we cant use our equipment(such as due to EMP) var/equipment_disabled = FALSE /// Keeps track of the mech's cell @@ -89,10 +88,17 @@ var/list/trackers = list() var/max_temperature = 25000 - ///health percentage below which internal damage is possible - var/internal_damage_threshold = 50 + ///Bitflags for internal damage var/internal_damage = NONE + /// damage amount above which we can take internal damages + var/internal_damage_threshold = 15 + /// % chance for internal damage to occur + var/internal_damage_probability = 20 + /// list of possibly dealt internal damage for this mech type + var/possible_int_damage = MECHA_INT_FIRE|MECHA_INT_TEMP_CONTROL|MECHA_INT_TANK_BREACH|MECHA_INT_CONTROL_LOST|MECHA_INT_SHORT_CIRCUIT + /// damage threshold above which we take component damage + var/component_damage_threshold = 10 ///required access level for mecha operation var/list/operation_req_access = list() @@ -102,11 +108,22 @@ ///Typepath for the wreckage it spawns when destroyed var/wreckage - var/list/equipment = list() - ///Current active equipment - var/obj/item/mecha_parts/mecha_equipment/selected - ///Maximum amount of equipment we can have - var/max_equip = 3 + ///assoc list: key-typepathlist before init, key-equipmentlist after + var/list/equip_by_category = list( + MECHA_L_ARM = null, + MECHA_R_ARM = null, + MECHA_UTILITY = list(), + MECHA_POWER = list(), + MECHA_ARMOR = list(), + ) + ///assoc list: max equips for non-arm modules key-count + var/list/max_equip_by_category = list( + MECHA_UTILITY = 0, + MECHA_POWER = 1, + MECHA_ARMOR = 0, + ) + ///flat equipment for iteration + var/list/flat_equipment ///Whether our steps are silent due to no gravity var/step_silent = FALSE @@ -167,14 +184,19 @@ ///Bool for whether this mech can only be used on lavaland var/lavaland_only = FALSE - - hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) + /// Ui size, so you can make the UI bigger if you let it load a lot of stuff + var/ui_x = 1100 + /// Ui size, so you can make the UI bigger if you let it load a lot of stuff + var/ui_y = 600 + /// ref to screen object that displays in the middle of the UI + var/atom/movable/screen/mech_view/ui_view /obj/item/radio/mech //this has to go somewhere subspace_transmission = TRUE /obj/vehicle/sealed/mecha/Initialize(mapload) . = ..() + ui_view = new(null, src) if(enclosed) internal_tank = new (src) RegisterSignal(src, COMSIG_MOVABLE_PRE_MOVE , .proc/disconnect_air) @@ -215,19 +237,31 @@ AddElement(/datum/element/atmos_sensitive, mapload) become_hearing_sensitive(trait_source = ROUNDSTART_TRAIT) ADD_TRAIT(src, TRAIT_ASHSTORM_IMMUNE, ROUNDSTART_TRAIT) //protects pilots from ashstorms. + for(var/key in equip_by_category) + if(key == MECHA_L_ARM || key == MECHA_R_ARM) + var/path = equip_by_category[key] + if(!path) + continue + var/obj/item/mecha_parts/mecha_equipment/thing = new path + thing.attach(src, key == MECHA_R_ARM) + continue + for(var/path in equip_by_category[key]) + var/obj/item/mecha_parts/mecha_equipment/thing = new path + thing.attach(src, FALSE) + equip_by_category[key] -= path /obj/vehicle/sealed/mecha/Destroy() for(var/ejectee in occupants) mob_exit(ejectee, TRUE, TRUE) - if(LAZYLEN(equipment)) - for(var/obj/item/mecha_parts/mecha_equipment/equip as anything in equipment) + if(LAZYLEN(flat_equipment)) + for(var/obj/item/mecha_parts/mecha_equipment/equip as anything in flat_equipment) equip.detach(loc) qdel(equip) radio = null STOP_PROCESSING(SSobj, src) - LAZYCLEARLIST(equipment) + LAZYCLEARLIST(flat_equipment) QDEL_NULL(cell) QDEL_NULL(scanmod) @@ -236,6 +270,7 @@ QDEL_NULL(cabin_air) QDEL_NULL(spark_system) QDEL_NULL(smoke_system) + QDEL_NULL(ui_view) GLOB.mechas_list -= src //global mech list for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) @@ -361,12 +396,9 @@ . += "It's heavily damaged." else . += "It's falling apart." - var/hide_weapon = locate(/obj/item/mecha_parts/concealed_weapon_bay) in contents - var/hidden_weapon = hide_weapon ? (locate(/obj/item/mecha_parts/mecha_equipment/weapon) in equipment) : null - var/list/visible_equipment = equipment - hidden_weapon - if(length(visible_equipment)) + if(LAZYLEN(flat_equipment)) . += "It's equipped with:" - for(var/obj/item/mecha_parts/mecha_equipment/ME in visible_equipment) + for(var/obj/item/mecha_parts/mecha_equipment/ME as anything in flat_equipment) . += "[icon2html(ME, user)] \A [ME]." if(enclosed) return @@ -548,26 +580,32 @@ if(internal_damage & MECHA_INT_CONTROL_LOST) target = pick(view(3,target)) var/mob/living/livinguser = user - if(selected) - if(!(livinguser in return_controllers_with_flag(VEHICLE_CONTROL_EQUIPMENT))) - balloon_alert(user, "wrong seat for equipment!") + if(!(livinguser in return_controllers_with_flag(VEHICLE_CONTROL_EQUIPMENT))) + balloon_alert(user, "wrong seat for equipment!") + return + var/obj/item/mecha_parts/mecha_equipment/selected + if(LAZYACCESS(modifiers, RIGHT_CLICK)) + selected = equip_by_category[MECHA_R_ARM] + else + selected = equip_by_category[MECHA_L_ARM] + if(!selected) + return + if(!Adjacent(target) && (selected.range & MECHA_RANGED)) + if(HAS_TRAIT(livinguser, TRAIT_PACIFISM) && selected.harmful) + to_chat(livinguser, span_warning("You don't want to harm other living beings!")) return - if(!Adjacent(target) && (selected.range & MECHA_RANGED)) - if(HAS_TRAIT(livinguser, TRAIT_PACIFISM) && selected.harmful) - to_chat(livinguser, span_warning("You don't want to harm other living beings!")) - return - if(SEND_SIGNAL(src, COMSIG_MECHA_EQUIPMENT_CLICK, livinguser, target) & COMPONENT_CANCEL_EQUIPMENT_CLICK) - return - INVOKE_ASYNC(selected, /obj/item/mecha_parts/mecha_equipment.proc/action, user, target, modifiers) + if(SEND_SIGNAL(src, COMSIG_MECHA_EQUIPMENT_CLICK, livinguser, target) & COMPONENT_CANCEL_EQUIPMENT_CLICK) return - if((selected.range & MECHA_MELEE) && Adjacent(target)) - if(isliving(target) && selected.harmful && HAS_TRAIT(livinguser, TRAIT_PACIFISM)) - to_chat(livinguser, span_warning("You don't want to harm other living beings!")) - return - if(SEND_SIGNAL(src, COMSIG_MECHA_EQUIPMENT_CLICK, livinguser, target) & COMPONENT_CANCEL_EQUIPMENT_CLICK) - return - INVOKE_ASYNC(selected, /obj/item/mecha_parts/mecha_equipment.proc/action, user, target, modifiers) + INVOKE_ASYNC(selected, /obj/item/mecha_parts/mecha_equipment.proc/action, user, target, modifiers) + return + if((selected.range & MECHA_MELEE) && Adjacent(target)) + if(isliving(target) && selected.harmful && HAS_TRAIT(livinguser, TRAIT_PACIFISM)) + to_chat(livinguser, span_warning("You don't want to harm other living beings!")) return + if(SEND_SIGNAL(src, COMSIG_MECHA_EQUIPMENT_CLICK, livinguser, target) & COMPONENT_CANCEL_EQUIPMENT_CLICK) + return + INVOKE_ASYNC(selected, /obj/item/mecha_parts/mecha_equipment.proc/action, user, target, modifiers) + return if(!(livinguser in return_controllers_with_flag(VEHICLE_CONTROL_MELEE))) to_chat(livinguser, span_warning("You're in the wrong seat to interact with your hands.")) return @@ -750,23 +788,53 @@ //////// Internal damage //////// /////////////////////////////////// -/obj/vehicle/sealed/mecha/proc/check_for_internal_damage(list/possible_int_damage,ignore_threshold=null) - if(!islist(possible_int_damage) || !length(possible_int_damage)) +/// tries to repair any internal damage and plays fluff for it +/obj/vehicle/sealed/mecha/proc/try_repair_int_damage(mob/user, flag_to_heal) + balloon_alert(user, get_int_repair_fluff_start(flag_to_heal)) + log_message("[key_name(user)] starting internal damage repair for flag [flag_to_heal]", LOG_MECHA) + if(!do_after(user, 10 SECONDS, src)) + balloon_alert(user, get_int_repair_fluff_fail(flag_to_heal)) + log_message("Internal damage repair for flag [flag_to_heal] failed.", LOG_MECHA, color="red") return - if(prob(20)) - if(ignore_threshold || atom_integrity*100/max_integrity < internal_damage_threshold) - for(var/T in possible_int_damage) - if(internal_damage & T) - possible_int_damage -= T - if (length(possible_int_damage)) - var/int_dam_flag = pick(possible_int_damage) - if(int_dam_flag) - set_internal_damage(int_dam_flag) - if(prob(5)) - if(ignore_threshold || atom_integrity*100/max_integrity < internal_damage_threshold) - if(LAZYLEN(equipment)) - var/obj/item/mecha_parts/mecha_equipment/ME = pick(equipment) - qdel(ME) + clear_internal_damage(flag_to_heal) + balloon_alert(user, get_int_repair_fluff_end(flag_to_heal)) + log_message("Finished internal damage repair for flag [flag_to_heal]", LOG_MECHA) + +///gets the starting balloon alert flufftext +/obj/vehicle/sealed/mecha/proc/get_int_repair_fluff_start(flag) + switch(flag) + if(MECHA_INT_FIRE) + return "activating internal fire supression..." + if(MECHA_INT_TEMP_CONTROL) + return "resetting temperature module..." + if(MECHA_INT_TANK_BREACH) + return "activating tank sealant..." + if(MECHA_INT_CONTROL_LOST) + return "recalibrating coordination system..." + +///gets the successful finish balloon alert flufftext +/obj/vehicle/sealed/mecha/proc/get_int_repair_fluff_end(flag) + switch(flag) + if(MECHA_INT_FIRE) + return "internal fire supressed" + if(MECHA_INT_TEMP_CONTROL) + return "temperature chip reactivated" + if(MECHA_INT_TANK_BREACH) + return "air tank sealed" + if(MECHA_INT_CONTROL_LOST) + return "coordination re-established" + +///gets the on-fail balloon alert flufftext +/obj/vehicle/sealed/mecha/proc/get_int_repair_fluff_fail(flag) + switch(flag) + if(MECHA_INT_FIRE) + return "fire supression canceled" + if(MECHA_INT_TEMP_CONTROL) + return "reset aborted" + if(MECHA_INT_TANK_BREACH) + return "sealant deactivated" + if(MECHA_INT_CONTROL_LOST) + return "recalibration failed" /obj/vehicle/sealed/mecha/proc/set_internal_damage(int_dam_flag) internal_damage |= int_dam_flag @@ -966,7 +1034,6 @@ /obj/vehicle/sealed/mecha/generate_actions() initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_eject) initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_internals, VEHICLE_CONTROL_SETTINGS) - initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_cycle_equip, VEHICLE_CONTROL_EQUIPMENT) initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_lights, VEHICLE_CONTROL_SETTINGS) initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_view_stats, VEHICLE_CONTROL_SETTINGS) initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/strafe, VEHICLE_CONTROL_DRIVE) @@ -1089,8 +1156,7 @@ mecha_flags &= ~SILICON_PILOT mob_container.forceMove(newloc)//ejecting mob container log_message("[mob_container] moved out.", LOG_MECHA) - ejector << browse(null, "window=exosuit") - + SStgui.close_user_uis(M, src) if(istype(mob_container, /obj/item/mmi)) var/obj/item/mmi/mmi = mob_container if(mmi.brainmob) @@ -1148,17 +1214,10 @@ return (get_charge()>=amount) /obj/vehicle/sealed/mecha/proc/get_charge() - for(var/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/R in equipment) - var/relay_charge = R.get_charge() - if(relay_charge) - return relay_charge - if(cell) - return max(0, cell.charge) + return cell?.charge /obj/vehicle/sealed/mecha/proc/use_power(amount) - if(get_charge() && cell.use(amount)) - return TRUE - return FALSE + return (get_charge() && cell.use(amount)) /obj/vehicle/sealed/mecha/proc/give_power(amount) if(!isnull(get_charge())) @@ -1208,7 +1267,7 @@ return FALSE var/ammo_needed var/found_gun - for(var/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/gun in equipment) + for(var/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/gun in flat_equipment) ammo_needed = 0 if(!istype(gun, /obj/item/mecha_parts/mecha_equipment/weapon/ballistic) && gun.ammo_type == A.ammo_type) diff --git a/code/modules/vehicles/mecha/combat/combat.dm b/code/modules/vehicles/mecha/combat/combat.dm index 6e4cf7a8d58..7f6265fd714 100644 --- a/code/modules/vehicles/mecha/combat/combat.dm +++ b/code/modules/vehicles/mecha/combat/combat.dm @@ -1,7 +1,6 @@ /obj/vehicle/sealed/mecha/combat force = 30 internals_req_access = list(ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY) - internal_damage_threshold = 50 armor = list(MELEE = 30, BULLET = 30, LASER = 15, ENERGY = 20, BOMB = 20, BIO = 0, FIRE = 100, ACID = 100) mouse_pointer = 'icons/effects/mouse_pointers/mecha_mouse.dmi' destruction_sleep_duration = 40 @@ -11,9 +10,8 @@ mouse_pointer = 'icons/effects/mouse_pointers/mecha_mouse.dmi' return ..() - /obj/vehicle/sealed/mecha/combat/proc/max_ammo() //Max the ammo stored for Nuke Ops mechs, or anyone else that calls this - for(var/obj/item/I in equipment) - if(istype(I, /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/)) + for(var/obj/item/I as anything in flat_equipment) + if(istype(I, /obj/item/mecha_parts/mecha_equipment/weapon/ballistic)) var/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/gun = I gun.projectiles_cache = gun.projectiles_cache_max diff --git a/code/modules/vehicles/mecha/combat/durand.dm b/code/modules/vehicles/mecha/combat/durand.dm index fb0b34ae2aa..862f4fcdb8c 100644 --- a/code/modules/vehicles/mecha/combat/durand.dm +++ b/code/modules/vehicles/mecha/combat/durand.dm @@ -6,11 +6,15 @@ movedelay = 4 dir_in = 1 //Facing North. max_integrity = 400 - deflect_chance = 20 armor = list(MELEE = 40, BULLET = 35, LASER = 15, ENERGY = 10, BOMB = 20, BIO = 0, FIRE = 100, ACID = 100) max_temperature = 30000 force = 40 wreckage = /obj/structure/mecha_wreckage/durand + max_equip_by_category = list( + MECHA_UTILITY = 1, + MECHA_POWER = 1, + MECHA_ARMOR = 3, + ) var/obj/durand_shield/shield diff --git a/code/modules/vehicles/mecha/combat/gygax.dm b/code/modules/vehicles/mecha/combat/gygax.dm index e9fa0c1a24e..43824350ee0 100644 --- a/code/modules/vehicles/mecha/combat/gygax.dm +++ b/code/modules/vehicles/mecha/combat/gygax.dm @@ -7,14 +7,16 @@ movedelay = 3 dir_in = 1 //Facing North. max_integrity = 250 - deflect_chance = 5 armor = list(MELEE = 25, BULLET = 20, LASER = 30, ENERGY = 15, BOMB = 0, BIO = 0, FIRE = 100, ACID = 100) max_temperature = 25000 leg_overload_coeff = 80 force = 25 wreckage = /obj/structure/mecha_wreckage/gygax - internal_damage_threshold = 35 - max_equip = 3 + max_equip_by_category = list( + MECHA_UTILITY = 1, + MECHA_POWER = 1, + MECHA_ARMOR = 2, + ) step_energy_drain = 3 /obj/vehicle/sealed/mecha/combat/gygax/generate_actions() @@ -50,7 +52,6 @@ icon_state = "darkgygax" base_icon_state = "darkgygax" max_integrity = 300 - deflect_chance = 20 armor = list(MELEE = 40, BULLET = 40, LASER = 50, ENERGY = 35, BOMB = 20, BIO = 0, FIRE = 100, ACID = 100) max_temperature = 35000 leg_overload_coeff = 70 @@ -58,21 +59,22 @@ operation_req_access = list(ACCESS_SYNDICATE) internals_req_access = list(ACCESS_SYNDICATE) wreckage = /obj/structure/mecha_wreckage/gygax/dark - max_equip = 5 + max_equip_by_category = list( + MECHA_UTILITY = 2, + MECHA_POWER = 1, + MECHA_ARMOR = 3, + ) + equip_by_category = list( + MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot, + MECHA_R_ARM = null, + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/thrusters/ion), + MECHA_POWER = list(), + MECHA_ARMOR = list(/obj/item/mecha_parts/mecha_equipment/armor/anticcw_armor_booster, /obj/item/mecha_parts/mecha_equipment/armor/antiproj_armor_booster), + ) destruction_sleep_duration = 20 /obj/vehicle/sealed/mecha/combat/gygax/dark/loaded/Initialize(mapload) . = ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/thrusters/ion(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay - ME.attach(src) max_ammo() /obj/vehicle/sealed/mecha/combat/gygax/dark/add_cell(obj/item/stock_parts/cell/C=null) diff --git a/code/modules/vehicles/mecha/combat/honker.dm b/code/modules/vehicles/mecha/combat/honker.dm index 3f94eebfcef..b3b5aa3bc9d 100644 --- a/code/modules/vehicles/mecha/combat/honker.dm +++ b/code/modules/vehicles/mecha/combat/honker.dm @@ -5,140 +5,24 @@ base_icon_state = "honker" movedelay = 3 max_integrity = 140 - deflect_chance = 60 - internal_damage_threshold = 60 armor = list(MELEE = -20, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 100, ACID = 100) max_temperature = 25000 operation_req_access = list(ACCESS_THEATRE) internals_req_access = list(ACCESS_MECH_SCIENCE, ACCESS_THEATRE) wreckage = /obj/structure/mecha_wreckage/honker mecha_flags = CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE - max_equip = 3 + max_equip_by_category = list( + MECHA_UTILITY = 2, + MECHA_POWER = 1, + MECHA_ARMOR = 0, + ) var/squeak = TRUE -/obj/vehicle/sealed/mecha/combat/honker/get_stats_part(mob/user) - var/integrity = atom_integrity/max_integrity*100 - var/cell_charge = get_charge() - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - var/tank_pressure = internal_tank ? round(int_tank_air.return_pressure(),0.01) : "None" - var/tank_temperature = internal_tank ? int_tank_air.temperature : "Unknown" - var/cabin_pressure = round(return_pressure(),0.01) - return {"[report_internal_damage()] - [integrity<30?"DAMAGE LEVEL CRITICAL
":null] - [internal_damage&MECHA_INT_TEMP_CONTROL?"CLOWN SUPPORT SYSTEM MALFUNCTION
":null] - [internal_damage&MECHA_INT_TANK_BREACH?"GAS TANK HONK
":null] - [internal_damage&MECHA_INT_CONTROL_LOST?"HONK-A-DOODLE - Recalibrate
":null] - IntegriHONK: [integrity]%
- PowerHONK charge: [isnull(cell_charge)?"No power cell installed":"[cell.percent()]%"]
- Air source: [use_internal_tank?"Internal Airtank":"Environment"]
- AirHONK pressure: [tank_pressure]kPa
- AirHONK temperature: [tank_temperature]°K|[tank_temperature - T0C]°C
- HONK pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa
- HONK temperature: [return_temperature()]°K|[return_temperature() - T0C]°C
- Lights: [(mecha_flags & LIGHTS_ON)?"on":"off"]
- [dna_lock?"DNA-locked:
[dna_lock] \[Reset\]
":null] - "} - -/obj/vehicle/sealed/mecha/combat/honker/get_ssticker_function() - . = {" - function SSticker() { - setInterval(function(){ - window.location='byond://?src=[REF(src)]&update_content=1'; - document.body.style.color = get_rand_color_string(); - document.body.style.background = get_rand_color_string(); - }, 1000); - } - - function get_rand_color_string() { - var color = new Array; - for(var i=0;i<3;i++){ - color.push(Math.floor(Math.random()*255)); - } - return "rgb("+color.toString()+")"; - } - "} - -/obj/vehicle/sealed/mecha/combat/honker/get_commands() - var/output = {"
-
Sounds of HONK:
- -
- "} - output += ..() - return output - - -/obj/vehicle/sealed/mecha/combat/honker/get_equipment_list() - if(!LAZYLEN(equipment)) - return - var/output = "Honk-ON-Systems:
" - for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) - output += "
[MT.get_equip_info()]
" - output += "
" - return output - -/obj/vehicle/sealed/mecha/combat/honker/get_equipment_menu() //outputs mecha html equipment menu - . = {" -
-
Honk-ON-Systems
- -
"} - /obj/vehicle/sealed/mecha/combat/honker/play_stepsound() if(squeak) playsound(src, SFX_CLOWN_STEP, 70, 1) squeak = !squeak -/obj/vehicle/sealed/mecha/combat/honker/Topic(href, href_list) - ..() - if (href_list["play_sound"]) - switch(href_list["play_sound"]) - if("sadtrombone") - playsound(src, 'sound/misc/sadtrombone.ogg', 50) - if("bikehorn") - playsound(src, 'sound/items/bikehorn.ogg', 50) - if("airhorn2") - playsound(src, 'sound/items/airhorn2.ogg', 40) //soundfile has higher than average volume - if("carhorn") - playsound(src, 'sound/items/carhorn.ogg', 80) //soundfile has lower than average volume - if("party_horn") - playsound(src, 'sound/items/party_horn.ogg', 50) - if("reee") - playsound(src, 'sound/effects/reee.ogg', 50) - if("weeoo1") - playsound(src, 'sound/items/weeoo1.ogg', 50) - if("hiss1") - playsound(src, 'sound/voice/hiss1.ogg', 50) - if("armbomb") - playsound(src, 'sound/weapons/armbomb.ogg', 50) - if("saberon") - playsound(src, 'sound/weapons/saberon.ogg', 50) - if("airlock_alien_prying") - playsound(src, 'sound/machines/airlock_alien_prying.ogg', 50) - if("lightningbolt") - playsound(src, 'sound/magic/lightningbolt.ogg', 50) - if("explosionfar") - playsound(src, 'sound/effects/explosionfar.ogg', 50) - //DARK H.O.N.K. @@ -147,13 +31,25 @@ name = "\improper Dark H.O.N.K" icon_state = "darkhonker" max_integrity = 300 - deflect_chance = 15 armor = list(MELEE = 40, BULLET = 40, LASER = 50, ENERGY = 35, BOMB = 20, BIO = 0, FIRE = 100, ACID = 100) max_temperature = 35000 operation_req_access = list(ACCESS_SYNDICATE) internals_req_access = list(ACCESS_SYNDICATE) wreckage = /obj/structure/mecha_wreckage/honker/dark - max_equip = 4 + max_equip_by_category = list( + MECHA_UTILITY = 1, + MECHA_POWER = 1, + MECHA_ARMOR = 3, + ) + +/obj/vehicle/sealed/mecha/combat/honker/dark/loaded + equip_by_category = list( + MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/honker, + MECHA_R_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana, + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/thrusters/ion), + MECHA_POWER = list(), + MECHA_ARMOR = list(), + ) /obj/vehicle/sealed/mecha/combat/honker/dark/add_cell(obj/item/stock_parts/cell/C) if(C) @@ -162,17 +58,6 @@ return cell = new /obj/item/stock_parts/cell/hyper(src) -/obj/vehicle/sealed/mecha/combat/honker/dark/loaded/Initialize(mapload) - . = ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/thrusters/ion(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/honker() - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana()//Needed more offensive weapons. - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache()//The mousetrap mortar was not up-to-snuff. - ME.attach(src) - /obj/structure/mecha_wreckage/honker/dark name = "\improper Dark H.O.N.K wreckage" icon_state = "darkhonker-broken" diff --git a/code/modules/vehicles/mecha/combat/marauder.dm b/code/modules/vehicles/mecha/combat/marauder.dm index 5d0f4da0a62..b0cfd022e0c 100644 --- a/code/modules/vehicles/mecha/combat/marauder.dm +++ b/code/modules/vehicles/mecha/combat/marauder.dm @@ -5,7 +5,6 @@ base_icon_state = "marauder" movedelay = 5 max_integrity = 500 - deflect_chance = 25 armor = list(MELEE = 50, BULLET = 55, LASER = 40, ENERGY = 30, BOMB = 30, BIO = 0, FIRE = 100, ACID = 100) max_temperature = 60000 resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF @@ -13,9 +12,12 @@ internals_req_access = list(ACCESS_CENT_SPECOPS) wreckage = /obj/structure/mecha_wreckage/marauder mecha_flags = CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE - internal_damage_threshold = 25 force = 45 - max_equip = 5 + max_equip_by_category = list( + MECHA_UTILITY = 3, + MECHA_POWER = 2, + MECHA_ARMOR = 3, + ) bumpsmash = TRUE /obj/vehicle/sealed/mecha/combat/marauder/generate_actions() @@ -23,19 +25,14 @@ initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_smoke) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_zoom) -/obj/vehicle/sealed/mecha/combat/marauder/loaded/Initialize(mapload) - . = ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/thrusters/ion(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/pulse(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster(src) - ME.attach(src) - max_ammo() +/obj/vehicle/sealed/mecha/combat/marauder/loaded + equip_by_category = list( + MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/energy/pulse, + MECHA_R_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack, + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/thrusters/ion), + MECHA_POWER = list(), + MECHA_ARMOR = list(/obj/item/mecha_parts/mecha_equipment/armor/antiproj_armor_booster), + ) /obj/vehicle/sealed/mecha/combat/marauder/add_cell(obj/item/stock_parts/cell/C=null) if(C) @@ -84,25 +81,19 @@ movedelay = 3 max_integrity = 550 wreckage = /obj/structure/mecha_wreckage/seraph - internal_damage_threshold = 20 force = 55 - max_equip = 6 - -/obj/vehicle/sealed/mecha/combat/marauder/seraph/Initialize(mapload) - . = ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/thrusters/ion(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/pulse(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/teleporter(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster(src) - ME.attach(src) - max_ammo() + max_equip_by_category = list( + MECHA_UTILITY = 3, + MECHA_POWER = 2, + MECHA_ARMOR = 3, + ) + equip_by_category = list( + MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/energy/pulse, + MECHA_R_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack, + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/thrusters/ion), + MECHA_POWER = list(), + MECHA_ARMOR = list(/obj/item/mecha_parts/mecha_equipment/armor/antiproj_armor_booster), + ) /obj/vehicle/sealed/mecha/combat/marauder/mauler desc = "Heavy-duty, combat exosuit, developed off of the existing Marauder model." @@ -112,26 +103,26 @@ operation_req_access = list(ACCESS_SYNDICATE) internals_req_access = list(ACCESS_SYNDICATE) wreckage = /obj/structure/mecha_wreckage/mauler - max_equip = 6 + max_equip_by_category = list( + MECHA_UTILITY = 3, + MECHA_POWER = 2, + MECHA_ARMOR = 4, + ) + equip_by_category = list( + MECHA_L_ARM = null, + MECHA_R_ARM = null, + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/thrusters/ion), + MECHA_POWER = list(), + MECHA_ARMOR = list(), + ) destruction_sleep_duration = 20 -/obj/vehicle/sealed/mecha/combat/marauder/mauler/Initialize(mapload) - . = ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/thrusters/ion(src) - ME.attach(src) - -/obj/vehicle/sealed/mecha/combat/marauder/mauler/loaded/Initialize(mapload) - . = ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay(src) - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster(src) - ME.attach(src) - max_ammo() - +/obj/vehicle/sealed/mecha/combat/marauder/mauler/loaded + equip_by_category = list( + MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg, + MECHA_R_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack, + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/thrusters/ion), + MECHA_POWER = list(), + MECHA_ARMOR = list(/obj/item/mecha_parts/mecha_equipment/armor/antiproj_armor_booster), + ) diff --git a/code/modules/vehicles/mecha/combat/phazon.dm b/code/modules/vehicles/mecha/combat/phazon.dm index 16c83ec730d..16dc0ce3555 100644 --- a/code/modules/vehicles/mecha/combat/phazon.dm +++ b/code/modules/vehicles/mecha/combat/phazon.dm @@ -7,13 +7,15 @@ dir_in = 2 //Facing South. step_energy_drain = 3 max_integrity = 200 - deflect_chance = 30 armor = list(MELEE = 30, BULLET = 30, LASER = 30, ENERGY = 30, BOMB = 30, BIO = 0, FIRE = 100, ACID = 100) max_temperature = 25000 wreckage = /obj/structure/mecha_wreckage/phazon - internal_damage_threshold = 25 force = 15 - max_equip = 3 + max_equip_by_category = list( + MECHA_UTILITY = 1, + MECHA_POWER = 1, + MECHA_ARMOR = 2, + ) phase_state = "phazon-phase" /obj/vehicle/sealed/mecha/combat/phazon/generate_actions() diff --git a/code/modules/vehicles/mecha/combat/reticence.dm b/code/modules/vehicles/mecha/combat/reticence.dm index b838d1296bb..afb91667b31 100644 --- a/code/modules/vehicles/mecha/combat/reticence.dm +++ b/code/modules/vehicles/mecha/combat/reticence.dm @@ -6,21 +6,25 @@ movedelay = 2 dir_in = 1 //Facing North. max_integrity = 100 - deflect_chance = 3 armor = list(MELEE = 25, BULLET = 20, LASER = 30, ENERGY = 15, BOMB = 0, BIO = 0, FIRE = 100, ACID = 100) max_temperature = 15000 wreckage = /obj/structure/mecha_wreckage/reticence operation_req_access = list(ACCESS_THEATRE) internals_req_access = list(ACCESS_MECH_SCIENCE, ACCESS_THEATRE) mecha_flags = CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS | QUIET_STEPS | QUIET_TURNS | MMI_COMPATIBLE - internal_damage_threshold = 25 - max_equip = 2 + max_equip_by_category = list( + MECHA_UTILITY = 1, + MECHA_POWER = 1, + MECHA_ARMOR = 1, + ) step_energy_drain = 3 color = "#87878715" -/obj/vehicle/sealed/mecha/combat/reticence/loaded/Initialize(mapload) - . = ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/silenced - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/rcd //HAHA IT MAKES WALLS GET IT - ME.attach(src) +/obj/vehicle/sealed/mecha/combat/reticence/loaded + equip_by_category = list( + MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/silenced, + MECHA_R_ARM = /obj/item/mecha_parts/mecha_equipment/rcd, + MECHA_UTILITY = list(), + MECHA_POWER = list(), + MECHA_ARMOR = list(), + ) diff --git a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm index 7a3152b593c..c50314d7a60 100644 --- a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm +++ b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm @@ -22,12 +22,15 @@ mecha_flags = ADDING_ACCESS_POSSIBLE | CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS movedelay = 3 max_integrity = 450 //really tanky, like damn - deflect_chance = 25 armor = list(MELEE = 45, BULLET = 40, LASER = 30, ENERGY = 30, BOMB = 40, BIO = 0, FIRE = 100, ACID = 100) max_temperature = 30000 wreckage = /obj/structure/mecha_wreckage/savannah_ivanov - internal_damage_threshold = 25 max_occupants = 2 + max_equip_by_category = list( + MECHA_UTILITY = 1, + MECHA_POWER = 1, + MECHA_ARMOR = 3, + ) //no tax on flying, since the power cost is in the leap itself. phasing_energy_drain = 0 diff --git a/code/modules/vehicles/mecha/equipment/mecha_equipment.dm b/code/modules/vehicles/mecha/equipment/mecha_equipment.dm index 8da111ec7d1..a25178ca6c7 100644 --- a/code/modules/vehicles/mecha/equipment/mecha_equipment.dm +++ b/code/modules/vehicles/mecha/equipment/mecha_equipment.dm @@ -8,6 +8,8 @@ icon_state = "mecha_equip" force = 5 max_integrity = 300 + /// Determines what "slot" this attachment will try to attach to on a mech + var/equipment_slot = MECHA_WEAPON ///Cooldown in ticks required between activations of the equipment var/equip_cooldown = 0 ///used for equipment that can be turned on/off, boolean @@ -29,58 +31,43 @@ ///Sound file: Sound to play when this equipment is destroyed while still attached to the mech var/destroy_sound = 'sound/mecha/critdestr.ogg' -///Updates chassis equipment list html menu -/obj/item/mecha_parts/mecha_equipment/proc/update_chassis_page() - SHOULD_CALL_PARENT(TRUE) - send_byjax(chassis.occupants,"exosuit.browser","eq_list", chassis.get_equipment_list()) - send_byjax(chassis.occupants,"exosuit.browser","equipment_menu", chassis.get_equipment_menu(),"dropdowns") - return TRUE - -///Updates chassis equipment list html menu with custom data -/obj/item/mecha_parts/mecha_equipment/proc/update_equip_info() - if(!chassis) - return - send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",get_equip_info()) - return TRUE - /obj/item/mecha_parts/mecha_equipment/Destroy() if(chassis) - LAZYREMOVE(chassis.equipment, src) - if(chassis.selected == src) - chassis.selected = null - update_chassis_page() + detach(get_turf(src)) log_message("[src] is destroyed.", LOG_MECHA) if(LAZYLEN(chassis.occupants)) to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_danger("[src] is destroyed!")]") playsound(chassis, destroy_sound, 50) - if(!detachable) //If we're a built-in nondetachable equipment, let's lock up the slot that we were in. - chassis.max_equip-- chassis = null return ..() -/obj/item/mecha_parts/mecha_equipment/try_attach_part(mob/user, obj/vehicle/sealed/mecha/mech) - if(!can_attach(mech)) - to_chat(user, span_warning("You are unable to attach [src] to [mech]!")) - return FALSE - if(!user.temporarilyRemoveItemFromInventory(src)) - return FALSE - attach(mech) - user.visible_message(span_notice("[user] attaches [src] to [mech]."), span_notice("You attach [src] to [mech].")) - return TRUE +/obj/item/mecha_parts/mecha_equipment/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M, attach_right = FALSE) + if(can_attach(M, attach_right)) + if(!user.temporarilyRemoveItemFromInventory(src)) + return FALSE + attach(M, attach_right) + user.visible_message(span_notice("[user] attaches [src] to [M]."), span_notice("You attach [src] to [M].")) + return TRUE + to_chat(user, span_warning("You are unable to attach [src] to [M]!")) + return FALSE -///fetches and returns a html formatted string with equippability status -/obj/item/mecha_parts/mecha_equipment/proc/get_equip_info() - if(!chassis) - return - var/txt = "* " - if(chassis.selected == src) - txt += "[src]" - else if(selectable) - txt += "[src]" - else - txt += "[src]" - - return txt +/obj/item/mecha_parts/mecha_equipment/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + switch(action) + if("detach") + detach(get_turf(src)) + return TRUE + if("toggle") + activated = !activated + return TRUE + if("repair") + ui.close() // allow watching for baddies and the ingame effects + chassis.balloon_alert(usr, "starting repair") + while(do_after(usr, 1 SECONDS, chassis) && get_integrity() < max_integrity) + repair_damage(30) + if(get_integrity() == max_integrity) + balloon_alert(usr, "repair complete") + return FALSE /** * Checks whether this mecha equipment can be activated @@ -95,20 +82,19 @@ return FALSE if(!activated) return FALSE - if(energy_drain && !chassis.has_charge(energy_drain)) + if(energy_drain && !chassis?.has_charge(energy_drain)) return FALSE if(chassis.is_currently_ejecting) return FALSE if(chassis.equipment_disabled) to_chat(chassis.occupants, span_warning("Error -- Equipment control unit is unresponsive.")) return FALSE - if(TIMER_COOLDOWN_CHECK(chassis, COOLDOWN_MECHA_EQUIPMENT)) + if(TIMER_COOLDOWN_CHECK(chassis, COOLDOWN_MECHA_EQUIPMENT(type))) return FALSE return TRUE /obj/item/mecha_parts/mecha_equipment/proc/action(mob/source, atom/target, list/modifiers) - TIMER_COOLDOWN_START(chassis, COOLDOWN_MECHA_EQUIPMENT, equip_cooldown)//Cooldown is on the MECH so people dont bypass it by switching equipment - send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]", get_equip_info()) + TIMER_COOLDOWN_START(chassis, COOLDOWN_MECHA_EQUIPMENT(type), equip_cooldown)//Cooldown is on the MECH so people dont bypass it by switching equipment chassis.use_power(energy_drain) return TRUE @@ -125,7 +111,7 @@ return chassis.use_power(energy_drain) . = do_after(user, equip_cooldown, target=target, interaction_key = interaction_key) - if(!chassis || src != chassis.selected || !(get_dir(chassis, target) & chassis.dir)) + if(!chassis || !(get_dir(chassis, target) & chassis.dir)) return FALSE ///Do after wrapper for mecha equipment @@ -133,45 +119,72 @@ if(!chassis) return . = do_after(user, delay, target=target) - if(!chassis || src != chassis.selected || !(get_dir(chassis, target) & chassis.dir)) + if(!chassis || !(get_dir(chassis, target)&chassis.dir)) return FALSE -///Returns TRUE if equipment should be allowed to attach to the targetted necha -/obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/vehicle/sealed/mecha/M) - return LAZYLEN(M.equipment) < M.max_equip +/obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) + return default_can_attach(M, attach_right) -///Attaches equipment and updates relevant equipment trackers -/obj/item/mecha_parts/mecha_equipment/proc/attach(obj/vehicle/sealed/mecha/M) - LAZYADD(M.equipment, src) +/obj/item/mecha_parts/mecha_equipment/proc/default_can_attach(obj/vehicle/sealed/mecha/mech, attach_right = FALSE) + if(equipment_slot == MECHA_WEAPON) + if(attach_right) + if(mech.equip_by_category[MECHA_R_ARM]) + return FALSE + else + if(mech.equip_by_category[MECHA_L_ARM]) + return FALSE + return TRUE + return length(mech.equip_by_category[equipment_slot]) < mech.max_equip_by_category[equipment_slot] + +/obj/item/mecha_parts/mecha_equipment/proc/attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) + LAZYADD(M.flat_equipment, src) + var/to_equip_slot = equipment_slot + if(equipment_slot == MECHA_WEAPON) + if(attach_right) + to_equip_slot = MECHA_R_ARM + else + to_equip_slot = MECHA_L_ARM + if(islist(M.equip_by_category[to_equip_slot])) + M.equip_by_category[to_equip_slot] += src + else + M.equip_by_category[to_equip_slot] = src chassis = M forceMove(M) - log_message("[src] attached.", LOG_MECHA) - update_chassis_page() + log_message("[src] initialized.", LOG_MECHA) -///Detaches equipment and updates relevant equipment trackers. Optional argument of atom to forcemove to once detached /obj/item/mecha_parts/mecha_equipment/proc/detach(atom/moveto) - SHOULD_CALL_PARENT(TRUE) moveto = moveto || get_turf(chassis) - forceMove(moveto) - LAZYREMOVE(chassis.equipment, src) - if(chassis.selected == src) - chassis.selected = null - update_chassis_page() - log_message("[src] removed from equipment.", LOG_MECHA) - chassis = null - -/obj/item/mecha_parts/mecha_equipment/Topic(href,href_list) - . = ..() - if(.) - return - if(href_list["detach"]) - detach() + if(src.Move(moveto)) + LAZYREMOVE(chassis.flat_equipment, src) + var/to_unequip_slot = equipment_slot + if(equipment_slot == MECHA_WEAPON) + if(chassis.equip_by_category[MECHA_R_ARM] == src) + to_unequip_slot = MECHA_R_ARM + else + to_unequip_slot = MECHA_L_ARM + if(islist(chassis.equip_by_category[to_unequip_slot])) + chassis.equip_by_category[to_unequip_slot] -= src + else + chassis.equip_by_category[to_unequip_slot] = null + log_message("[src] removed from equipment.", LOG_MECHA) + chassis = null + return /obj/item/mecha_parts/mecha_equipment/log_message(message, message_type=LOG_GAME, color=null, log_globally) if(chassis) return chassis.log_message("ATTACHMENT: [src] [message]", message_type, color) return ..() +/** + * ## get_snowflake_data + * handles the returning of snowflake data required by the UI of the mecha + * not the prettiest of procs honeslty + * returns: + * * an assoc list + * * must include an list("snowflake_id" = snowflake_id) + */ +/obj/item/mecha_parts/mecha_equipment/proc/get_snowflake_data() + return list() /** * Proc for reloading weapons from HTML UI or by AI diff --git a/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm b/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm index 518cbcfb5f0..27c901a55c3 100644 --- a/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm +++ b/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm @@ -11,7 +11,7 @@ STOP_PROCESSING(SSobj, src) return ..() -/obj/item/mecha_parts/mecha_equipment/medical/can_attach(obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/mecha_equipment/medical/can_attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) . = ..() if(!ismedicalmecha(M)) return FALSE @@ -45,6 +45,30 @@ AM.forceMove(get_turf(src)) return ..() +/obj/item/mecha_parts/mecha_equipment/medical/sleeper/get_snowflake_data() + var/list/data = list("snowflake_id" = MECHA_SNOWFLAKE_ID_SLEEPER) + if(!patient) + return data + data["patient"] = list( + "patientname" = patient.name, + "is_dead" = patient.stat == DEAD, + "patient_health" = patient.health/patient.maxHealth, + ) + return data + +/obj/item/mecha_parts/mecha_equipment/medical/sleeper/ui_act(action, list/params) + . = ..() + if(.) + return + switch(action) + if("eject") + go_out() + return TRUE + if("view_stats") + usr << browse(get_patient_stats(),"window=msleeper") + onclose(usr, "msleeper") + return FALSE + /obj/item/mecha_parts/mecha_equipment/medical/sleeper/Exit(atom/movable/leaving, direction) return FALSE //prevents them from leaving without being forcemoved I guess @@ -60,14 +84,13 @@ chassis.visible_message(span_warning("[chassis] starts putting [target] into \the [src].")) if(!do_after(source, equip_cooldown, target=target)) return - if(!chassis || src != chassis.selected || !(get_dir(chassis, target)&chassis.dir)) + if(!chassis || !(get_dir(chassis, target) & chassis.dir)) return if(!patient_insertion_check(target, source)) return target.forceMove(src) patient = target START_PROCESSING(SSobj, src) - update_equip_info() to_chat(source, "[icon2html(src, source)][span_notice("[target] successfully loaded into [src]. Life support functions engaged.")]") chassis.visible_message(span_warning("[chassis] loads [target] into [src].")) log_message("[target] loaded. Life support functions engaged.", LOG_MECHA) @@ -93,7 +116,6 @@ log_message("[patient] ejected. Life support functions disabled.", LOG_MECHA) STOP_PROCESSING(SSobj, src) patient = null - update_equip_info() /obj/item/mecha_parts/mecha_equipment/medical/sleeper/detach() if(patient) @@ -102,26 +124,12 @@ STOP_PROCESSING(SSobj, src) return ..() -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/get_equip_info() - var/output = ..() - if(output) - var/temp = "" - if(patient) - temp = "
\[Occupant: [patient] ([patient.stat > 1 ? "*DECEASED*" : "Health: [patient.health]%"])\]
View stats|Eject" - return "[output] [temp]" - /obj/item/mecha_parts/mecha_equipment/medical/sleeper/Topic(href,href_list) . = ..() if(.) return if(!(usr in chassis.occupants)) return - if(href_list["eject"]) - go_out() - if(href_list["view_stats"]) - usr << browse(get_patient_stats(),"window=msleeper") - onclose(usr, "msleeper") - return if(href_list["inject"]) var/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/SG = locate() in chassis var/datum/reagent/R = locate(href_list["inject"]) in SG.reagents.reagent_list @@ -203,7 +211,7 @@ /obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/inject_reagent(datum/reagent/R,obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/SG) - if(!R || !patient || !SG || !(SG in chassis.equipment)) + if(!R || !patient || !SG || !(SG in chassis.flat_equipment)) return var/to_inject = min(R.volume, inject_amount) if(to_inject && patient.reagents.get_reagent_amount(R.type) + to_inject <= inject_amount*2) @@ -211,14 +219,6 @@ log_message("Injecting [patient] with [to_inject] units of [R.name].", LOG_MECHA) log_combat(chassis.occupants, patient, "injected", "[name] ([R] - [to_inject] units)") SG.reagents.trans_id_to(patient,R.type,to_inject) - update_equip_info() - -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/update_equip_info() - . = ..() - if(. && patient) - send_byjax(chassis.occupants,"msleeper.browser","lossinfo",get_patient_dam()) - send_byjax(chassis.occupants,"msleeper.browser","reagents",get_patient_reagents()) - send_byjax(chassis.occupants,"msleeper.browser","injectwith",get_available_reagents()) /obj/item/mecha_parts/mecha_equipment/medical/sleeper/container_resist_act(mob/living/user) go_out() @@ -240,7 +240,6 @@ log_message("[patient] no longer detected - Life support functions disabled.", LOG_MECHA) STOP_PROCESSING(SSobj, src) patient = null - update_equip_info() if(M.health > 0) M.adjustOxyLoss(-0.5 * delta_time) M.AdjustStun(-40 * delta_time) @@ -251,7 +250,6 @@ if(M.reagents.get_reagent_amount(/datum/reagent/medicine/epinephrine) < 5) M.reagents.add_reagent(/datum/reagent/medicine/epinephrine, 5) chassis.use_power(energy_drain) - update_equip_info() @@ -308,15 +306,31 @@ STOP_PROCESSING(SSobj, src) return ..() -/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/can_attach(obj/vehicle/sealed/mecha/medical/M) +/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/can_attach(obj/vehicle/sealed/mecha/medical/M, attach_right = FALSE) . = ..() if(!istype(M)) return FALSE -/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/get_equip_info() - var/output = ..()// no . = here to avoid obfuscation - if(output) - return "[output] \[[mode? "Analyze" : "Launch"]\]
\[Syringes: [LAZYLEN(syringes)]/[max_syringes] | Reagents: [reagents.total_volume]/[reagents.maximum_volume]\]
Reagents list" +/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/get_snowflake_data() + return list( + "snowflake_id" = MECHA_SNOWFLAKE_ID_SYRINGE, + "mode" = mode == FIRE_SYRINGE_MODE ? "Launch" : "Analyze", + "syringe" = LAZYLEN(syringes), + "max_syringe" = max_syringes, + "reagents" = reagents.total_volume, + "total_reagents" = reagents.maximum_volume, + ) + +/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/ui_act(action, list/params) + . = ..() + if(.) + return + if(action == "change_mode") + mode = !mode + return TRUE + else if(action == "show_reagents") + usr << browse(get_reagents_page(),"window=msyringegun") + return FALSE /obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/action(mob/source, atom/target, list/modifiers) if(!action_checks(target)) @@ -348,7 +362,6 @@ ..() if (href_list["toggle_mode"]) mode = !mode - update_equip_info() return if (href_list["select_reagents"]) LAZYCLEARLIST(processed_reagents) @@ -369,11 +382,6 @@ to_chat(usr, "[icon2html(src, usr)][span_notice("Reagent processing started.")]") log_message("Reagent processing started.", LOG_MECHA) return - if (href_list["show_reagents"]) - if(!(usr in chassis.occupants)) - return - usr << browse(get_reagents_page(),"window=msyringegun") - return if (href_list["purge_reagent"]) var/reagent = href_list["purge_reagent"] if(!reagent) @@ -454,7 +462,6 @@ S.forceMove(src) LAZYADD(syringes,S) to_chat(user, "[icon2html(src, user)][span_notice("Syringe loaded.")]") - update_equip_info() return TRUE /obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/proc/analyze_reagents(atom/A, mob/user) @@ -479,16 +486,11 @@ known_reagents[r_id] = r_name return TRUE -/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/update_equip_info() - . = ..() - if(.) - send_byjax(chassis.occupants,"msyringegun.browser","reagents",get_current_reagents()) - send_byjax(chassis.occupants,"msyringegun.browser","reagents_form",get_reagents_form()) - /// Updates the equipment info list when the reagents change. Eats signal args. /obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/proc/on_reagent_change(datum/reagents/holder, ...) SIGNAL_HANDLER - update_equip_info() + send_byjax(chassis.occupants,"msyringegun.browser","reagents",get_current_reagents()) + send_byjax(chassis.occupants,"msyringegun.browser","reagents_form",get_reagents_form()) return NONE diff --git a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm index c80147b51a9..70ea103f73f 100644 --- a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm +++ b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm @@ -102,11 +102,11 @@ /obj/item/mecha_parts/mecha_equipment/drill/proc/move_ores() - if(locate(/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp) in chassis.equipment && istype(chassis, /obj/vehicle/sealed/mecha/working/ripley)) + if(locate(/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp) in chassis.flat_equipment && istype(chassis, /obj/vehicle/sealed/mecha/working/ripley)) var/obj/vehicle/sealed/mecha/working/ripley/R = chassis //we could assume that it's a ripley because it has a clamp, but that's ~unsafe~ and ~bad practice~ R.collect_ore() -/obj/item/mecha_parts/mecha_equipment/drill/can_attach(obj/vehicle/sealed/mecha/M as obj) +/obj/item/mecha_parts/mecha_equipment/drill/can_attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) if(..()) if(istype(M, /obj/vehicle/sealed/mecha/working) || istype(M, /obj/vehicle/sealed/mecha/combat)) return TRUE @@ -166,6 +166,7 @@ icon_state = "mecha_analyzer" selectable = 0 equip_cooldown = 15 + equipment_slot = MECHA_UTILITY var/scanning_time = 0 mech_flags = EXOSUIT_MODULE_WORKING @@ -173,7 +174,7 @@ . = ..() START_PROCESSING(SSfastprocess, src) -/obj/item/mecha_parts/mecha_equipment/mining_scanner/can_attach(obj/vehicle/sealed/mecha/M as obj) +/obj/item/mecha_parts/mecha_equipment/mining_scanner/can_attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) if(..()) if(istype(M, /obj/vehicle/sealed/mecha/working)) return TRUE diff --git a/code/modules/vehicles/mecha/equipment/tools/other_tools.dm b/code/modules/vehicles/mecha/equipment/tools/other_tools.dm index d069da3366d..b4a51c52ff7 100644 --- a/code/modules/vehicles/mecha/equipment/tools/other_tools.dm +++ b/code/modules/vehicles/mecha/equipment/tools/other_tools.dm @@ -97,19 +97,16 @@ return movable_target = target to_chat(source, "[icon2html(src, source)][span_notice("locked on [target].")]") - send_byjax(source,"exosuit.browser","[REF(src)]", get_equip_info()) else if(target!=movable_target) if(movable_target in view(chassis)) var/turf/targ = get_turf(target) var/turf/orig = get_turf(movable_target) movable_target.throw_at(target, 14, 1.5) movable_target = null - send_byjax(source,"exosuit.browser","[REF(src)]", get_equip_info()) log_game("[key_name(source)] used a Gravitational Catapult to throw [movable_target] (From [AREACOORD(orig)]) at [target] ([AREACOORD(targ)]).") return ..() movable_target = null to_chat(source, "[icon2html(src, source)][span_notice("Lock on [movable_target] disengaged.")]") - send_byjax(source,"exosuit.browser","[REF(src)]", get_equip_info()) if(GRAVPUSH_MODE) var/list/atomstothrow = list() @@ -134,57 +131,62 @@ var/delay = 2 SSmove_manager.move_away(scatter, target, delay = delay, timeout = delay * dist, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) -/obj/item/mecha_parts/mecha_equipment/gravcatapult/get_equip_info() - return "[..()] [mode==1?"([movable_target||"Nothing"])":null] \[S|P\]" - -/obj/item/mecha_parts/mecha_equipment/gravcatapult/Topic(href, href_list) - ..() - if(href_list["mode"]) - mode = text2num(href_list["mode"]) - send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",src.get_equip_info()) +/obj/item/mecha_parts/mecha_equipment/gravcatapult/get_snowflake_data() + return list( + "snowflake_id" = MECHA_SNOWFLAKE_ID_MODE, + "name" = "Gravity catapult", + "mode" = mode == GRAVPUSH_MODE ? "Push" : "Sling", + ) +/obj/item/mecha_parts/mecha_equipment/gravcatapult/ui_act(action, list/params) + . = ..() + if(.) + return + if(action == "change_mode") + mode++ + if(mode > GRAVPUSH_MODE) + mode = GRAVSLING_MODE + return TRUE #undef GRAVSLING_MODE #undef GRAVPUSH_MODE //////////////////////////// ARMOR BOOSTER MODULES ////////////////////////////////////////////////////////// +/obj/item/mecha_parts/mecha_equipment/armor + equipment_slot = MECHA_ARMOR + ///short protection name to display in the UI + var/protect_name = "you're mome" + ///icon in armor.dmi that shows in the UI + var/iconstate_name + //how much the armor of the mech is modified by + var/list/armor_mod = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 0, ACID = 0) +/obj/item/mecha_parts/mecha_equipment/armor/attach(obj/vehicle/sealed/mecha/M, attach_right) + . = ..() + chassis.armor.modifyRating(arglist(armor_mod)) -/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster //what is that noise? A BAWWW from TK mutants. +/obj/item/mecha_parts/mecha_equipment/armor/detach(atom/moveto) + var/list/removed_armor = armor_mod.Copy() + for(var/armor_type in removed_armor) + removed_armor[armor_type] = -removed_armor[armor_type] + chassis.armor.modifyRating(arglist(removed_armor)) + return ..() + +/obj/item/mecha_parts/mecha_equipment/armor/anticcw_armor_booster name = "armor booster module (Close Combat Weaponry)" - desc = "Boosts exosuit armor against armed melee attacks. Requires energy to operate." + desc = "Boosts exosuit armor against melee attacks" icon_state = "mecha_abooster_ccw" - equip_cooldown = 10 - energy_drain = 50 - range = 0 - var/deflect_coeff = 1.15 - var/damage_coeff = 0.8 - selectable = FALSE + iconstate_name = "melee" + protect_name = "Melee Armor" + armor = list(MELEE = 15, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 0, ACID = 0) -/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/proc/attack_react() - if(energy_drain && !chassis.has_charge(energy_drain)) - return FALSE - TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_ARMOR, equip_cooldown) - return TRUE - - - -/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster +/obj/item/mecha_parts/mecha_equipment/armor/antiproj_armor_booster name = "armor booster module (Ranged Weaponry)" - desc = "Boosts exosuit armor against ranged attacks. Completely blocks taser shots. Requires energy to operate." + desc = "Boosts exosuit armor against ranged attacks. Completely blocks taser shots." icon_state = "mecha_abooster_proj" - equip_cooldown = 10 - energy_drain = 50 - range = 0 - var/deflect_coeff = 1.15 - var/damage_coeff = 0.8 - selectable = FALSE - -/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/proc/projectile_react() - if(energy_drain && !chassis.has_charge(energy_drain)) - return FALSE - TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_ARMOR, equip_cooldown) - return TRUE + iconstate_name = "range" + protect_name = "Ranged Armor" + armor = list(MELEE = 0, BULLET = 10, LASER = 10, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 0, ACID = 0) ////////////////////////////////// REPAIR DROID ////////////////////////////////////////////////// @@ -196,7 +198,8 @@ icon_state = "repair_droid" energy_drain = 50 range = 0 - + activated = FALSE + equipment_slot = MECHA_UTILITY /// Repaired health per second var/health_boost = 0.5 var/icon/droid_overlay @@ -208,7 +211,7 @@ chassis?.cut_overlay(droid_overlay) return ..() -/obj/item/mecha_parts/mecha_equipment/repair_droid/attach(obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/mecha_equipment/repair_droid/attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) . = ..() droid_overlay = new(src.icon, icon_state = "repair_droid") M.add_overlay(droid_overlay) @@ -218,27 +221,20 @@ STOP_PROCESSING(SSobj, src) return ..() -/obj/item/mecha_parts/mecha_equipment/repair_droid/get_equip_info() - return "*  [src] - [activated?"Deactivate":"Activate"]" - - -/obj/item/mecha_parts/mecha_equipment/repair_droid/Topic(href, href_list) - ..() - if(!href_list["toggle_repairs"]) +/obj/item/mecha_parts/mecha_equipment/repair_droid/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(action != "toggle") return chassis.cut_overlay(droid_overlay) - activated = !activated //now set to FALSE and active, so update the UI - update_equip_info() if(activated) START_PROCESSING(SSobj, src) - droid_overlay = new(icon, icon_state = "repair_droid_a") + droid_overlay = new(src.icon, icon_state = "repair_droid_a") log_message("Activated.", LOG_MECHA) else STOP_PROCESSING(SSobj, src) - droid_overlay = new(icon, icon_state = "repair_droid") + droid_overlay = new(src.icon, icon_state = "repair_droid") log_message("Deactivated.", LOG_MECHA) chassis.add_overlay(droid_overlay) - send_byjax(chassis.occupants,"exosuit.browser", "[REF(src)]", get_equip_info()) /obj/item/mecha_parts/mecha_equipment/repair_droid/process(delta_time) @@ -260,100 +256,26 @@ repaired = TRUE if(repaired) if(!chassis.use_power(energy_drain)) + activated = FALSE return PROCESS_KILL else //no repair needed, we turn off chassis.cut_overlay(droid_overlay) droid_overlay = new(src.icon, icon_state = "repair_droid") chassis.add_overlay(droid_overlay) + activated = FALSE return PROCESS_KILL - - -/////////////////////////////////// TESLA ENERGY RELAY //////////////////////////////////////////////// - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay - name = "exosuit energy relay" - desc = "An exosuit module that wirelessly drains energy from any available power channel in area. The performance index is quite low." - icon_state = "tesla" - energy_drain = 0 - range = 0 - var/coeff = 100 - var/list/use_channels = list(AREA_USAGE_EQUIP,AREA_USAGE_ENVIRON,AREA_USAGE_LIGHT) - selectable = FALSE - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/detach() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_charge() - if(activated) //disabled - return - var/pow_chan = get_chassis_area_power(get_area(chassis)) - if(pow_chan) - return 1000 //making magic - - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_chassis_area_power(area/A) - if(!A) - return - var/pow_chan = 0 - for(var/c in use_channels) - if(!A.powered(c)) - continue - pow_chan = c - break - return pow_chan - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/Topic(href, href_list) - ..() - if(href_list["toggle_relay"]) - activated = !activated //now set to FALSE and active, so update the UI - update_equip_info() - if(activated) //inactive - START_PROCESSING(SSobj, src) - log_message("Activated.", LOG_MECHA) - else - STOP_PROCESSING(SSobj, src) - log_message("Deactivated.", LOG_MECHA) - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/get_equip_info() - if(!chassis) - return - return "*  [src.name] - [activated?"Deactivate":"Activate"]" - - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/process(delta_time) - if(!chassis || chassis.internal_damage & MECHA_INT_SHORT_CIRCUIT) - return PROCESS_KILL - var/cur_charge = chassis.get_charge() - if(isnull(cur_charge) || !chassis.cell) - to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("No power cell detected.")]") - return PROCESS_KILL - if(cur_charge >= chassis.cell.maxcharge) - return - var/area/A = get_area(chassis) - var/pow_chan = get_chassis_area_power(A) - if(pow_chan) - var/delta = min(10 * delta_time, chassis.cell.maxcharge-cur_charge) - chassis.give_power(delta) - A.use_power(delta*coeff, pow_chan) - - - - /////////////////////////////////////////// GENERATOR ///////////////////////////////////////////// /obj/item/mecha_parts/mecha_equipment/generator - name = "exosuit plasma converter" + name = "plasma engine" desc = "An exosuit module that generates power using solid plasma as fuel. Pollutes the environment." icon_state = "tesla" range = MECHA_MELEE + equipment_slot = MECHA_POWER + activated = FALSE var/coeff = 100 var/obj/item/stack/sheet/fuel var/max_fuel = 150000 @@ -377,31 +299,31 @@ /obj/item/mecha_parts/mecha_equipment/generator/detach() STOP_PROCESSING(SSobj, src) + activated = FALSE return ..() -/obj/item/mecha_parts/mecha_equipment/generator/Topic(href, href_list) - ..() - if(href_list["toggle"]) - activated = !activated //now set to FALSE and active, so update the UI - update_equip_info() - if(activated) //inactive +/obj/item/mecha_parts/mecha_equipment/generator/get_snowflake_data() + return list( + "active" = activated, + "fuel" = fuel.amount, + ) + +/obj/item/mecha_parts/mecha_equipment/generator/ui_act(action, list/params) + . = ..() + if(action == "toggle") + if(activated) + to_chat(usr, "[icon2html(src, usr)][span_warning("Power generation enabled.")]") START_PROCESSING(SSobj, src) log_message("Activated.", LOG_MECHA) else + to_chat(usr, "[icon2html(src, usr)][span_warning("Power generation disabled.")]") STOP_PROCESSING(SSobj, src) log_message("Deactivated.", LOG_MECHA) + return TRUE -/obj/item/mecha_parts/mecha_equipment/generator/get_equip_info() - var/output = ..() - if(output) - return "[output] \[[fuel]: [round(fuel.amount*MINERAL_MATERIAL_AMOUNT,0.1)] cm3\] - [activated?"Deactivate":"Activate"]" - -/obj/item/mecha_parts/mecha_equipment/generator/action(mob/source, atom/movable/target, list/modifiers) - if(!chassis) - return - if(load_fuel(target, source)) - send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",src.get_equip_info()) - return ..() +/obj/item/mecha_parts/mecha_equipment/generator/attackby(weapon, mob/user, params) + . = ..() + load_fuel(weapon, user) /obj/item/mecha_parts/mecha_equipment/generator/proc/load_fuel(obj/item/stack/sheet/P, mob/user) if(P.type == fuel.type && P.amount > 0) @@ -424,13 +346,16 @@ /obj/item/mecha_parts/mecha_equipment/generator/process(delta_time) if(!chassis) + activated = FALSE return PROCESS_KILL if(fuel.amount<=0) + activated = FALSE log_message("Deactivated - no fuel.", LOG_MECHA) to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("Fuel reserves depleted.")]") return PROCESS_KILL var/cur_charge = chassis.get_charge() if(isnull(cur_charge)) + activated = FALSE to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("No power cell detected.")]") log_message("Deactivated.", LOG_MECHA) return PROCESS_KILL @@ -439,7 +364,6 @@ use_fuel = fuelrate_active chassis.give_power(rechargerate * delta_time) fuel.amount -= min(delta_time * use_fuel / MINERAL_MATERIAL_AMOUNT, fuel.amount) - update_equip_info() /////////////////////////////////////////// THRUSTERS ///////////////////////////////////////////// @@ -448,16 +372,17 @@ desc = "A generic set of thrusters, from an unknown source. Uses not-understood methods to propel exosuits seemingly for free." icon_state = "thrusters" selectable = FALSE + equipment_slot = MECHA_UTILITY var/effect_type = /obj/effect/particle_effect/sparks -/obj/item/mecha_parts/mecha_equipment/thrusters/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M) - for(var/obj/item/I in M.equipment) +/obj/item/mecha_parts/mecha_equipment/thrusters/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M, attach_right) + for(var/obj/item/I in M.equip_by_category[MECHA_UTILITY]) if(istype(I, src)) to_chat(user, span_warning("[M] already has this thruster package!")) return FALSE return ..() -/obj/item/mecha_parts/mecha_equipment/thrusters/attach(obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/mecha_equipment/thrusters/attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) M.active_thrusters = src //Enable by default return ..() @@ -471,11 +396,9 @@ chassis.active_thrusters = null return ..() -/obj/item/mecha_parts/mecha_equipment/thrusters/Topic(href,href_list) - ..() - if(href_list["toggle"]) - activated = !activated //now set to FALSE and active, so update the UI - update_equip_info() +/obj/item/mecha_parts/mecha_equipment/thrusters/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(params["toggle"]) if(activated) //inactive START_PROCESSING(SSobj, src) enable() @@ -497,11 +420,6 @@ chassis.active_thrusters = null to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("[src] disabled.")]") -/obj/item/mecha_parts/mecha_equipment/thrusters/get_equip_info() - var/output = ..() - if(output) - return "[output] [activated?"Deactivate":"Activate"]" - /obj/item/mecha_parts/mecha_equipment/thrusters/proc/thrust(movement_dir) if(!chassis) return FALSE @@ -521,7 +439,7 @@ effect_type = /obj/effect/particle_effect/smoke var/move_cost = 20 //moles per step -/obj/item/mecha_parts/mecha_equipment/thrusters/gas/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/mecha_equipment/thrusters/gas/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M, attach_right = FALSE) if(!M.internal_tank) to_chat(user, span_warning("[M] does not have an internal tank and cannot support this upgrade!")) return FALSE @@ -539,8 +457,6 @@ generate_effect(movement_dir) return TRUE - - /obj/item/mecha_parts/mecha_equipment/thrusters/ion //for mechs with built-in thrusters, should never really exist un-attached to a mech name = "Ion thruster package" desc = "A set of thrusters that allow for exosuit movement in zero-gravity environments." diff --git a/code/modules/vehicles/mecha/equipment/tools/weapon_bay.dm b/code/modules/vehicles/mecha/equipment/tools/weapon_bay.dm deleted file mode 100644 index 87c5b949d26..00000000000 --- a/code/modules/vehicles/mecha/equipment/tools/weapon_bay.dm +++ /dev/null @@ -1,14 +0,0 @@ -/obj/item/mecha_parts/concealed_weapon_bay - name = "concealed weapon bay" - desc = "A compartment that allows a non-combat mecha to equip one weapon while hiding the weapon from plain sight." - icon = 'icons/mecha/mecha_equipment.dmi' - icon_state = "mecha_weapon_bay" - -/obj/item/mecha_parts/concealed_weapon_bay/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M) - if(istype(M, /obj/vehicle/sealed/mecha/combat)) - to_chat(user, span_warning("[M] can already hold weapons!")) - return - if(locate(/obj/item/mecha_parts/concealed_weapon_bay) in M.contents) - to_chat(user, span_warning("[M] already has a concealed weapon bay!")) - return - return ..() diff --git a/code/modules/vehicles/mecha/equipment/tools/work_tools.dm b/code/modules/vehicles/mecha/equipment/tools/work_tools.dm index 248661a6ceb..b7aa0d438b4 100644 --- a/code/modules/vehicles/mecha/equipment/tools/work_tools.dm +++ b/code/modules/vehicles/mecha/equipment/tools/work_tools.dm @@ -24,7 +24,7 @@ ///Audio for using the hydraulic clamp var/clampsound = 'sound/mecha/hydraulic.ogg' -/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/can_attach(obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/can_attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) . = ..() if(!.) return @@ -215,10 +215,14 @@ return extinguish(source.target) -/obj/item/mecha_parts/mecha_equipment/extinguisher/get_equip_info() - return "[..()] \[[src.reagents.total_volume]\]" +/obj/item/mecha_parts/mecha_equipment/extinguisher/get_snowflake_data() + return list( + "snowflake_id" = MECHA_SNOWFLAKE_ID_EXTINGUISHER, + "reagents" = reagents.total_volume, + "total_reagents" = reagents.maximum_volume, + ) -/obj/item/mecha_parts/mecha_equipment/extinguisher/can_attach(obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/mecha_equipment/extinguisher/can_attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) . = ..() if(!.) return @@ -249,6 +253,45 @@ GLOB.rcd_list -= src return ..() +/obj/item/mecha_parts/mecha_equipment/rcd/get_snowflake_data() + return list( + "snowflake_id" = MECHA_SNOWFLAKE_ID_MODE, + "name" = "RCD control", + "mode" = get_mode_name(), + ) + +/// fetches the mode name to display in the UI +/obj/item/mecha_parts/mecha_equipment/rcd/proc/get_mode_name() + switch(mode) + if(MODE_DECONSTRUCT) + return "Deconstruct" + if(MODE_WALL) + return "Build wall" + if(MODE_AIRLOCK) + return "Build Airlock" + else + return "Someone didnt set this" + +/obj/item/mecha_parts/mecha_equipment/rcd/ui_act(action, list/params) + . = ..() + if(.) + return + if(action == "change_mode") + mode++ + if(mode > MODE_AIRLOCK) + mode = MODE_DECONSTRUCT + switch(mode) + if(MODE_DECONSTRUCT) + to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("Switched RCD to Deconstruct.")]") + energy_drain = initial(energy_drain) + if(MODE_WALL) + to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("Switched RCD to Construct Walls and Flooring.")]") + energy_drain = 2*initial(energy_drain) + if(MODE_AIRLOCK) + to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("Switched RCD to Construct Airlock.")]") + energy_drain = 2*initial(energy_drain) + return TRUE + /obj/item/mecha_parts/mecha_equipment/rcd/action(mob/source, atom/target, list/modifiers) if(!isturf(target) && !istype(target, /obj/machinery/door/airlock)) target = get_turf(target) @@ -298,24 +341,6 @@ playsound(target, 'sound/items/deconstruct.ogg', 50, TRUE) return ..() -/obj/item/mecha_parts/mecha_equipment/rcd/Topic(href,href_list) - ..() - if(href_list["mode"]) - mode = text2num(href_list["mode"]) - switch(mode) - if(MODE_DECONSTRUCT) - to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("Switched RCD to Deconstruct.")]") - energy_drain = initial(energy_drain) - if(MODE_WALL) - to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("Switched RCD to Construct Walls and Flooring.")]") - energy_drain = 2*initial(energy_drain) - if(MODE_AIRLOCK) - to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("Switched RCD to Construct Airlock.")]") - energy_drain = 2*initial(energy_drain) - -/obj/item/mecha_parts/mecha_equipment/rcd/get_equip_info() - return "[..()] \[D|C|A\]" - #undef MODE_DECONSTRUCT #undef MODE_WALL #undef MODE_AIRLOCK @@ -327,7 +352,7 @@ icon_state = "ripleyupgrade" mech_flags = EXOSUIT_MODULE_RIPLEY -/obj/item/mecha_parts/mecha_equipment/ripleyupgrade/can_attach(obj/vehicle/sealed/mecha/working/ripley/M) +/obj/item/mecha_parts/mecha_equipment/ripleyupgrade/can_attach(obj/vehicle/sealed/mecha/working/ripley/M, attach_right = FALSE) if(M.type != /obj/vehicle/sealed/mecha/working/ripley) to_chat(loc, span_warning("This conversion kit can only be applied to APLU MK-I models.")) return FALSE @@ -345,7 +370,7 @@ return FALSE return TRUE -/obj/item/mecha_parts/mecha_equipment/ripleyupgrade/attach(obj/vehicle/sealed/mecha/markone) +/obj/item/mecha_parts/mecha_equipment/ripleyupgrade/attach(obj/vehicle/sealed/mecha/markone, attach_right = FALSE) var/obj/vehicle/sealed/mecha/working/ripley/mk2/marktwo = new (get_turf(markone),1) if(!marktwo) return @@ -365,12 +390,10 @@ markone.capacitor.forceMove(marktwo) markone.capacitor = null marktwo.update_part_values() - for(var/obj/item/mecha_parts/equipment in markone.contents) - if(istype(equipment, /obj/item/mecha_parts/concealed_weapon_bay)) //why is the bay not just a variable change who did this - equipment.forceMove(marktwo) - for(var/obj/item/mecha_parts/mecha_equipment/equipment in markone.equipment) //Move the equipment over... + for(var/obj/item/mecha_parts/mecha_equipment/equipment in markone.flat_equipment) //Move the equipment over... + var/righthandgun = markone.equip_by_category[MECHA_R_ARM] == equipment equipment.detach(marktwo) - equipment.attach(marktwo) + equipment.attach(marktwo, righthandgun) marktwo.dna_lock = markone.dna_lock marktwo.mecha_flags = markone.mecha_flags marktwo.strafe = markone.strafe diff --git a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm index 5221b479266..4e622482877 100644 --- a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm +++ b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm @@ -1,6 +1,7 @@ /obj/item/mecha_parts/mecha_equipment/weapon name = "mecha weapon" range = MECHA_RANGED + equipment_slot = MECHA_WEAPON destroy_sound = 'sound/mecha/weapdestr.ogg' var/projectile var/fire_sound @@ -12,13 +13,11 @@ var/kickback = TRUE //Will using this weapon in no grav push mecha back. mech_flags = EXOSUIT_MODULE_COMBAT -/obj/item/mecha_parts/mecha_equipment/weapon/can_attach(obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/mecha_equipment/weapon/can_attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) if(!..()) return FALSE if(istype(M, /obj/vehicle/sealed/mecha/combat)) return TRUE - if((locate(/obj/item/mecha_parts/concealed_weapon_bay) in M.contents) && !(locate(/obj/item/mecha_parts/mecha_equipment/weapon) in M.equipment)) - return TRUE return FALSE /obj/item/mecha_parts/mecha_equipment/weapon/action(mob/source, atom/target, list/modifiers) @@ -128,10 +127,10 @@ fire_sound = 'sound/weapons/plasma_cutter.ogg' harmful = TRUE -/obj/item/mecha_parts/mecha_equipment/weapon/energy/plasma/can_attach(obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/mecha_equipment/weapon/energy/plasma/can_attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) if(..()) //combat mech return TRUE - else if(LAZYLEN(M.equipment) < M.max_equip) + else if(default_can_attach(M, attach_right)) return TRUE return FALSE @@ -147,11 +146,11 @@ harmful = TRUE //attachable to all mechas, like the plasma cutter -/obj/item/mecha_parts/mecha_equipment/weapon/energy/mecha_kineticgun/can_attach(obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/mecha_equipment/weapon/energy/mecha_kineticgun/can_attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) . = ..() if(.) //combat mech return - if(LAZYLEN(M.equipment) < M.max_equip) + if(default_can_attach(M, attach_right)) return TRUE /obj/item/mecha_parts/mecha_equipment/weapon/energy/taser @@ -174,7 +173,7 @@ kickback = FALSE mech_flags = EXOSUIT_MODULE_HONK -/obj/item/mecha_parts/mecha_equipment/weapon/honker/can_attach(obj/vehicle/sealed/mecha/mecha) +/obj/item/mecha_parts/mecha_equipment/weapon/honker/can_attach(obj/vehicle/sealed/mecha/mecha, attach_right = FALSE) . = ..() if(!.) return @@ -220,7 +219,6 @@ var/projectiles var/projectiles_cache //ammo to be loaded in, if possible. var/projectiles_cache_max - var/projectile_energy_cost var/disabledreload //For weapons with no cache (like the rockets) which are reloaded by hand var/ammo_type @@ -231,51 +229,35 @@ return FALSE return TRUE -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/get_equip_info() - return "[..()] \[[projectiles][projectiles_cache_max &&!projectile_energy_cost?"/[projectiles_cache]":""]\][!disabledreload &&(src.projectiles < initial(src.projectiles))?" - Rearm":null]" - +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(action == "reload") + rearm() + return TRUE /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/rearm() if(projectiles < initial(projectiles)) var/projectiles_to_add = initial(projectiles) - projectiles - - if(projectile_energy_cost) - while(chassis.get_charge() >= projectile_energy_cost && projectiles_to_add) - projectiles++ - projectiles_to_add-- - chassis.use_power(projectile_energy_cost) - + if(!projectiles_cache) + return FALSE + if(projectiles_to_add <= projectiles_cache) + projectiles = projectiles + projectiles_to_add + projectiles_cache = projectiles_cache - projectiles_to_add else - if(!projectiles_cache) - return FALSE - if(projectiles_to_add <= projectiles_cache) - projectiles = projectiles + projectiles_to_add - projectiles_cache = projectiles_cache - projectiles_to_add - else - projectiles = projectiles + projectiles_cache - projectiles_cache = 0 - - send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",src.get_equip_info()) + projectiles = projectiles + projectiles_cache + projectiles_cache = 0 log_message("Rearmed [src].", LOG_MECHA) return TRUE - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/needs_rearm() return projectiles <= 0 - -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/Topic(href, href_list) - ..() - if (href_list["rearm"]) - rearm() - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/action(mob/source, atom/target, list/modifiers) - if(..()) - projectiles -= projectiles_per_shot - send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",src.get_equip_info()) - return ..() - + . = ..() + if(!.) + return + projectiles -= projectiles_per_shot /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/carbine name = "\improper FNX-99 \"Hades\" Carbine" @@ -297,7 +279,7 @@ equip_cooldown = 30 projectile = /obj/projectile/bullet/mime projectiles = 6 - projectile_energy_cost = 50 + projectiles_cache = 999 harmful = TRUE /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot @@ -419,11 +401,11 @@ fire_sound = 'sound/items/bikehorn.ogg' projectiles = 15 missile_speed = 1.5 - projectile_energy_cost = 100 + projectiles_cache = 999 equip_cooldown = 20 mech_flags = EXOSUIT_MODULE_HONK -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/can_attach(obj/vehicle/sealed/mecha/combat/honker/M) +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/can_attach(obj/vehicle/sealed/mecha/combat/honker/M, attach_right = FALSE) if(..()) if(istype(M)) return 1 @@ -437,11 +419,11 @@ fire_sound = 'sound/items/bikehorn.ogg' projectiles = 15 missile_speed = 1.5 - projectile_energy_cost = 100 + projectiles_cache = 999 equip_cooldown = 10 mech_flags = EXOSUIT_MODULE_HONK -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/mousetrap_mortar/can_attach(obj/vehicle/sealed/mecha/combat/honker/M) +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/mousetrap_mortar/can_attach(obj/vehicle/sealed/mecha/combat/honker/M, attach_right = FALSE) if(..()) if(istype(M)) return 1 @@ -463,40 +445,34 @@ projectile = /obj/item/punching_glove fire_sound = 'sound/items/bikehorn.ogg' projectiles = 10 - projectile_energy_cost = 500 + projectiles_cache = 999 harmful = TRUE diags_first = TRUE /// Damage done by the glove on contact. Also used to determine throw distance (damage / 5) var/punch_damage = 35 - /// TRUE - Can toggle between lethal and non-lethal || FALSE - Cannot toggle - var/can_toggle_lethal = TRUE mech_flags = EXOSUIT_MODULE_HONK -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/punching_glove/can_attach(obj/vehicle/sealed/mecha/combat/honker/M) +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/punching_glove/can_attach(obj/vehicle/sealed/mecha/combat/honker/M, attach_right = FALSE) if(..()) if(istype(M)) return 1 return 0 -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/punching_glove/get_equip_info() - if(!chassis) - return +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/punching_glove/get_snowflake_data() + return list( + "snowflake_id" = MECHA_SNOWFLAKE_ID_MODE, + "mode" = harmful ? "LETHAL FISTING" : "Cuddles", + ) - if(can_toggle_lethal) - return "[..()]   [harmful?"Punch":"Pat"] mode" - else - return ..() - -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/punching_glove/Topic(href, href_list) - ..() - if(href_list["lethalPunch"]) +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/punching_glove/ui_act(action, list/params) + . = ..() + if(action == "toggle") harmful = !harmful - if(!chassis) - return if(harmful) to_chat(usr, "[icon2html(src, usr)][span_warning("Lethal Fisting Enabled.")]") else to_chat(usr, "[icon2html(src, usr)][span_warning("Lethal Fisting Disabled.")]") + return TRUE /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/punching_glove/action(mob/source, atom/target, list/modifiers) . = ..() @@ -535,9 +511,9 @@ icon_state = "mecha_bananamrtr" projectile = /obj/item/grown/bananapeel/bombanana projectiles = 8 - projectile_energy_cost = 1000 + projectiles_cache = 999 -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana/can_attach(obj/vehicle/sealed/mecha/combat/honker/M) +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana/can_attach(obj/vehicle/sealed/mecha/combat/honker/M, attach_right = FALSE) if(..()) if(istype(M)) return TRUE @@ -551,11 +527,11 @@ fire_sound = 'sound/weapons/gun/general/grenade_launch.ogg' projectiles = 6 missile_speed = 1.5 - projectile_energy_cost = 800 + projectiles_cache = 999 equip_cooldown = 60 det_time = 20 -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache/can_attach(obj/vehicle/sealed/mecha/combat/honker/M) +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache/can_attach(obj/vehicle/sealed/mecha/combat/honker/M, attach_right = FALSE) if(..()) if(istype(M)) return TRUE diff --git a/code/modules/vehicles/mecha/mech_melee_attack.dm b/code/modules/vehicles/mecha/mech_melee_attack.dm index ffd9b6b3662..7b1261b6f4a 100644 --- a/code/modules/vehicles/mecha/mech_melee_attack.dm +++ b/code/modules/vehicles/mecha/mech_melee_attack.dm @@ -9,18 +9,12 @@ */ /atom/proc/mech_melee_attack(obj/vehicle/sealed/mecha/mecha_attacker, mob/living/user) SHOULD_CALL_PARENT(TRUE) - var/mech_damtype = mecha_attacker.damtype - if(mecha_attacker.selected) - mech_damtype = mecha_attacker.selected.damtype - log_combat(user, src, "attacked", mecha_attacker, "(COMBAT MODE: [uppertext(user.combat_mode)] (DAMTYPE: [uppertext(mech_damtype)])") + log_combat(user, src, "attacked", mecha_attacker, "(COMBAT MODE: [uppertext(user.combat_mode)] (DAMTYPE: [uppertext(mecha_attacker.damtype)])") return 0 /turf/closed/wall/mech_melee_attack(obj/vehicle/sealed/mecha/mecha_attacker, mob/living/user) mecha_attacker.do_attack_animation(src) - var/mech_damtype = mecha_attacker.damtype - if(mecha_attacker.selected) - mech_damtype = mecha_attacker.selected.damtype - switch(mech_damtype) + switch(mecha_attacker.damtype) if(BRUTE) playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE) if(BURN) @@ -38,10 +32,7 @@ /obj/mech_melee_attack(obj/vehicle/sealed/mecha/mecha_attacker, mob/living/user) mecha_attacker.do_attack_animation(src) - var/mech_damtype = mecha_attacker.damtype - if(mecha_attacker.selected) - mech_damtype = mecha_attacker.selected.damtype - switch(mech_damtype) + switch(mecha_attacker.damtype) if(BRUTE) playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE) if(BURN) @@ -50,7 +41,7 @@ return 0 mecha_attacker.visible_message(span_danger("[mecha_attacker] hits [src]!"), span_danger("You hit [src]!"), null, COMBAT_MESSAGE_RANGE) ..() - return take_damage(mecha_attacker.force * 3, mech_damtype, "melee", mecha_attacker.selected, get_dir(src, mecha_attacker)) // multiplied by 3 so we can hit objs hard but not be overpowered against mobs. + return take_damage(mecha_attacker.force * 3, mecha_attacker.damtype, "melee", FALSE, get_dir(src, mecha_attacker)) // multiplied by 3 so we can hit objs hard but not be overpowered against mobs. /obj/structure/window/mech_melee_attack(obj/vehicle/sealed/mecha/mecha_attacker, mob/living/user) if(!can_be_reached()) diff --git a/code/modules/vehicles/mecha/mecha_actions.dm b/code/modules/vehicles/mecha/mecha_actions.dm index a96baa0f5ac..266a84a51f9 100644 --- a/code/modules/vehicles/mecha/mecha_actions.dm +++ b/code/modules/vehicles/mecha/mecha_actions.dm @@ -40,52 +40,6 @@ chassis.log_message("Now taking air from [chassis.use_internal_tank?"internal airtank":"environment"].", LOG_MECHA) UpdateButtonIcon() -/datum/action/vehicle/sealed/mecha/mech_cycle_equip - name = "Cycle Equipment" - button_icon_state = "mech_cycle_equip_off" - -/datum/action/vehicle/sealed/mecha/mech_cycle_equip/Trigger(trigger_flags) - if(!owner || !chassis || !(owner in chassis.occupants)) - return - - var/list/available_equipment = list() - for(var/e in chassis.equipment) - var/obj/item/mecha_parts/mecha_equipment/equipment = e - if(equipment.selectable) - available_equipment += equipment - - if(available_equipment.len == 0) - chassis.balloon_alert(owner, "no equipment available") - playsound(chassis,'sound/machines/terminal_error.ogg', 40, FALSE) - return - if(!chassis.selected) - chassis.selected = available_equipment[1] - chassis.balloon_alert(owner, "[chassis.selected] selected") - send_byjax(chassis.occupants,"exosuit.browser","eq_list",chassis.get_equipment_list()) - button_icon_state = "mech_cycle_equip_on" - playsound(chassis,'sound/machines/piston_raise.ogg', 40, TRUE) - UpdateButtonIcon() - return - var/number = 0 - for(var/equipment in available_equipment) - number++ - if(equipment != chassis.selected) - continue - if(available_equipment.len == number) - chassis.selected = null - chassis.balloon_alert(owner, "switched to no equipment") - button_icon_state = "mech_cycle_equip_off" - playsound(chassis,'sound/machines/piston_lower.ogg', 40, TRUE) - else - chassis.selected = available_equipment[number+1] - chassis.balloon_alert(owner, "switched to [chassis.selected]") - button_icon_state = "mech_cycle_equip_on" - playsound(chassis,'sound/machines/piston_raise.ogg', 40, TRUE) - send_byjax(chassis.occupants,"exosuit.browser","eq_list",chassis.get_equipment_list()) - UpdateButtonIcon() - return - - /datum/action/vehicle/sealed/mecha/mech_toggle_lights name = "Toggle Lights" button_icon_state = "mech_lights_off" @@ -116,9 +70,7 @@ if(!owner || !chassis || !(owner in chassis.occupants)) return - var/datum/browser/popup = new(owner , "exosuit") - popup.set_content(chassis.get_stats_html(owner)) - popup.open() + chassis.ui_interact(owner) /datum/action/vehicle/sealed/mecha/strafe diff --git a/code/modules/vehicles/mecha/mecha_control_console.dm b/code/modules/vehicles/mecha/mecha_control_console.dm index e5e70fb47e8..b52cd69a879 100644 --- a/code/modules/vehicles/mecha/mecha_control_console.dm +++ b/code/modules/vehicles/mecha/mecha_control_console.dm @@ -31,7 +31,6 @@ airtank = M.internal_tank ? M.return_pressure() : null, pilot = M.return_drivers(), location = get_area_name(M, TRUE), - active_equipment = M.selected, emp_recharging = MT.recharging, tracker_ref = REF(MT) ) @@ -98,8 +97,7 @@ Cell Charge: [isnull(cell_charge) ? "Not Found":"[chassis.cell.percent()]%"]
Airtank: [chassis.internal_tank ? "[round(chassis.return_pressure(), 0.01)]" : "Not Equipped"] kPa
Pilot: [chassis.return_drivers() || "None"]
- Location: [get_area_name(chassis, TRUE) || "Unknown"]
- Active Equipment: [chassis.selected || "None"]"} + Location: [get_area_name(chassis, TRUE) || "Unknown"]"} if(istype(chassis, /obj/vehicle/sealed/mecha/working/ripley)) var/obj/vehicle/sealed/mecha/working/ripley/RM = chassis answer += "
Used Cargo Space: [round((LAZYLEN(RM.cargo) / RM.cargo_capacity * 100), 0.01)]%" @@ -118,7 +116,7 @@ chassis = null return ..() -/obj/item/mecha_parts/mecha_tracking/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/mecha_tracking/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M, attach_right = FALSE) if(!..()) return M.trackers += src diff --git a/code/modules/vehicles/mecha/mecha_defense.dm b/code/modules/vehicles/mecha/mecha_defense.dm index c0c32b3c5d4..61411b077cd 100644 --- a/code/modules/vehicles/mecha/mecha_defense.dm +++ b/code/modules/vehicles/mecha/mecha_defense.dm @@ -1,3 +1,19 @@ +/*! + * # Mecha defence explanation + * Mechs focus is on a more heavy-but-slower damage approach + * For this they have the following mechanics + * + * ## Backstab + * Basically the tldr is that mechs are less flexible so we encourage good positioning, pretty simple + * ## Armor modules + * Pretty simple, adds armor, you can choose against what + * ## Internal damage + * When taking damage will force you to take some time to repair, encourages improvising in a fight + * Targetting different def zones will damage them to encurage a more strategic approach to fights + * where they target the "dangerous" modules + */ + +/// returns a number for the damage multiplier for this relative angle/dir /obj/vehicle/sealed/mecha/proc/get_armour_facing(relative_dir) switch(relative_dir) if(180) // BACKSTAB! @@ -6,50 +22,52 @@ return facing_modifiers[MECHA_FRONT_ARMOUR] return facing_modifiers[MECHA_SIDE_ARMOUR] //if its not a front hit or back hit then assume its from the side +///tries to deal internal damaget depending on the damage amount +/obj/vehicle/sealed/mecha/proc/try_deal_internal_damage(damage) + if(damage < internal_damage_threshold) + return + if(!prob(internal_damage_probability)) + return + var/internal_damage_to_deal = possible_int_damage + internal_damage_to_deal &= ~mecha_flags + if(internal_damage_to_deal) + set_internal_damage(pick(bitfield_to_list(internal_damage_to_deal))) + +/// tries to damage mech equipment depending on damage and where is being targetted +/obj/vehicle/sealed/mecha/proc/try_damage_component(damage, def_zone) + if(damage < component_damage_threshold) + return + var/obj/item/mecha_parts/mecha_equipment/gear + switch(def_zone) + if(BODY_ZONE_L_ARM) + gear = equip_by_category[MECHA_L_ARM] + if(BODY_ZONE_R_ARM) + gear = equip_by_category[MECHA_R_ARM] + if(!gear) + return + var/brokenstatus = gear.get_integrity() + // always leave at least 1 health + brokenstatus-- + var/damage_to_deal = min(brokenstatus, damage) + if(!damage_to_deal) + return + gear.take_damage(damage_to_deal) + /obj/vehicle/sealed/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) . = ..() if(. && atom_integrity > 0) spark_system.start() - switch(damage_flag) - if(FIRE) - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL)) - if(MELEE) - check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - else - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT)) + try_deal_internal_damage(.) if(. >= 5 || prob(33)) to_chat(occupants, "[icon2html(src, occupants)][span_userdanger("Taking damage!")]") - log_message("Took [damage_amount] points of damage. Damage type: [damage_type]", LOG_MECHA) + log_message("Took [.] points of damage. Damage type: [damage_type]", LOG_MECHA) -/obj/vehicle/sealed/mecha/run_atom_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) +/obj/vehicle/sealed/mecha/run_atom_armor(damage_amount, damage_type, damage_flag = 0, attack_dir, armour_penentration) . = ..() - if(!damage_amount) - return 0 - var/booster_deflection_modifier = 1 - var/booster_damage_modifier = 1 - if(damage_flag == BULLET || damage_flag == LASER || damage_flag == ENERGY) - for(var/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/B in equipment) - if(B.projectile_react()) - booster_deflection_modifier = B.deflect_coeff - booster_damage_modifier = B.damage_coeff - break - else if(damage_flag == MELEE) - for(var/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/B in equipment) - if(B.attack_react()) - booster_deflection_modifier *= B.deflect_coeff - booster_damage_modifier *= B.damage_coeff - break - if(attack_dir) var/facing_modifier = get_armour_facing(abs(dir2angle(dir) - dir2angle(attack_dir))) - booster_damage_modifier /= facing_modifier - booster_deflection_modifier *= facing_modifier - if(prob(deflect_chance * booster_deflection_modifier)) - visible_message(span_danger("[src]'s armour deflects the attack!")) - log_message("Armor saved.", LOG_MECHA) - return 0 - if(.) - . *= booster_damage_modifier + if(.) + . *= facing_modifier /obj/vehicle/sealed/mecha/attack_hand(mob/living/user, list/modifiers) . = ..() @@ -58,15 +76,15 @@ user.changeNext_move(CLICK_CD_MELEE) // Ugh. Ideally we shouldn't be setting cooldowns outside of click code. user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) playsound(loc, 'sound/weapons/tap.ogg', 40, TRUE, -1) - user.visible_message(span_danger("[user] hits [name]. Nothing happens."), null, null, COMBAT_MESSAGE_RANGE) - log_message("Attack by hand/paw. Attacker - [user].", LOG_MECHA, color="red") + user.visible_message(span_danger("[user] hits [src]. Nothing happens."), null, null, COMBAT_MESSAGE_RANGE) + log_message("Attack by hand/paw (no damage). Attacker - [user].", LOG_MECHA, color="red") /obj/vehicle/sealed/mecha/attack_paw(mob/user, list/modifiers) return attack_hand(user, modifiers) /obj/vehicle/sealed/mecha/attack_alien(mob/living/user, list/modifiers) log_message("Attack by alien. Attacker - [user].", LOG_MECHA, color="red") - playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE) + playsound(loc, 'sound/weapons/slash.ogg', 100, TRUE) attack_generic(user, rand(user.melee_damage_lower, user.melee_damage_upper), BRUTE, MELEE, 0) /obj/vehicle/sealed/mecha/attack_animal(mob/living/simple_animal/user, list/modifiers) @@ -106,22 +124,20 @@ /obj/vehicle/sealed/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper log_message("Hit by [AM].", LOG_MECHA, color="red") - . = ..() + return ..() -/obj/vehicle/sealed/mecha/bullet_act(obj/projectile/Proj) //wrapper - if(!enclosed && LAZYLEN(occupants) && !(mecha_flags & SILICON_PILOT) && !Proj.force_hit && (Proj.def_zone == BODY_ZONE_HEAD || Proj.def_zone == BODY_ZONE_CHEST)) //allows bullets to hit the pilot of open-canopy mechs - for(var/m in occupants) - var/mob/living/hitmob = m - hitmob.bullet_act(Proj) //If the sides are open, the occupant can be hit +/obj/vehicle/sealed/mecha/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit) //wrapper + if(!enclosed && LAZYLEN(occupants) && !(mecha_flags & SILICON_PILOT) && (hitting_projectile.def_zone == BODY_ZONE_HEAD || hitting_projectile.def_zone == BODY_ZONE_CHEST)) //allows bullets to hit the pilot of open-canopy mechs + for(var/mob/living/hitmob as anything in occupants) + hitmob.bullet_act(hitting_projectile, def_zone, piercing_hit) //If the sides are open, the occupant can be hit return BULLET_ACT_HIT - log_message("Hit by projectile. Type: [Proj.name]([Proj.armor_flag]).", LOG_MECHA, color="red") - . = ..() + log_message("Hit by projectile. Type: [hitting_projectile]([hitting_projectile.damage_type]).", LOG_MECHA, color="red") + // yes we *have* to run the armor calc proc here I love tg projectile code too + try_damage_component(run_atom_armor(hitting_projectile.damage, hitting_projectile.damage_type, hitting_projectile.damage_type, 0, REVERSE_DIR(hitting_projectile.dir), hitting_projectile.armour_penetration), hitting_projectile.def_zone) + return ..() /obj/vehicle/sealed/mecha/ex_act(severity, target) log_message("Affected by explosion of severity: [severity].", LOG_MECHA, color="red") - if(prob(deflect_chance)) - severity-- - log_message("Armor saved, changing severity to [severity]", LOG_MECHA) return ..() /obj/vehicle/sealed/mecha/contents_explosion(severity, target) @@ -129,29 +145,29 @@ switch(severity) if(EXPLODE_DEVASTATE) - if(equipment) - SSexplosions.high_mov_atom += equipment + if(flat_equipment) + SSexplosions.high_mov_atom += flat_equipment if(trackers) SSexplosions.high_mov_atom += trackers if(occupants) SSexplosions.high_mov_atom += occupants if(EXPLODE_HEAVY) - if(equipment) - SSexplosions.med_mov_atom += equipment + if(flat_equipment) + SSexplosions.med_mov_atom += flat_equipment if(trackers) SSexplosions.med_mov_atom += trackers if(occupants) SSexplosions.med_mov_atom += occupants if(EXPLODE_LIGHT) - if(equipment) - SSexplosions.low_mov_atom += equipment + if(flat_equipment) + SSexplosions.low_mov_atom += flat_equipment if(trackers) SSexplosions.low_mov_atom += trackers if(occupants) SSexplosions.low_mov_atom += occupants /obj/vehicle/sealed/mecha/handle_atom_del(atom/A) - if(A in occupants) + if(A in occupants) //todo does not work and in wrong file LAZYREMOVE(occupants, A) icon_state = initial(icon_state)+"-open" setDir(dir_in) @@ -165,15 +181,15 @@ take_damage(30 / severity, BURN, ENERGY, 1) log_message("EMP detected", LOG_MECHA, color="red") - if(istype(src, /obj/vehicle/sealed/mecha/combat)) + if(istype(src, /obj/vehicle/sealed/mecha/combat)) //todo this stupid mouse icon should be a flag mouse_pointer = 'icons/effects/mouse_pointers/mecha_mouse-disable.dmi' for(var/occus in occupants) var/mob/living/occupant = occus occupant.update_mouse_pointer() if(!equipment_disabled && LAZYLEN(occupants)) //prevent spamming this message with back-to-back EMPs to_chat(occupants, span_warning("Error -- Connection to equipment control unit has been lost.")) - addtimer(CALLBACK(src, /obj/vehicle/sealed/mecha/proc/restore_equipment), 3 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) - equipment_disabled = 1 + addtimer(CALLBACK(src, /obj/vehicle/sealed/mecha.proc/restore_equipment), 3 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) + equipment_disabled = TRUE /obj/vehicle/sealed/mecha/should_atmos_process(datum/gas_mixture/air, exposed_temperature) return exposed_temperature > max_temperature @@ -182,8 +198,14 @@ log_message("Exposed to dangerous temperature.", LOG_MECHA, color="red") take_damage(5, BURN, 0, 1) -/obj/vehicle/sealed/mecha/attackby(obj/item/W, mob/user, params) +/obj/vehicle/sealed/mecha/attackby_secondary(obj/item/weapon, mob/user, params) + if(istype(weapon, /obj/item/mecha_parts)) + var/obj/item/mecha_parts/parts = weapon + parts.try_attach_part(user, src, TRUE) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + return ..() +/obj/vehicle/sealed/mecha/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/mmi)) if(mmi_move_inside(W,user)) to_chat(user, span_notice("[src]-[W] interface initialized successfully.")) @@ -198,13 +220,7 @@ if(W.GetID()) if((mecha_flags & ADDING_ACCESS_POSSIBLE) || (mecha_flags & ADDING_MAINT_ACCESS_POSSIBLE)) if(internals_access_allowed(user)) - var/obj/item/card/id/id_card - if(istype(W, /obj/item/card/id)) - id_card = W - else - var/obj/item/pda/pda = W - id_card = pda.id - output_maintenance_dialog(id_card, user) + ui_interact(user) return to_chat(user, span_warning("Invalid ID: Access denied.")) return @@ -253,22 +269,15 @@ to_chat(user, span_warning("There's already a capacitor installed!")) return - if(istype(W, /obj/item/stack/cable_coil)) - if(construction_state == MECHA_OPEN_HATCH && (internal_damage & MECHA_INT_SHORT_CIRCUIT)) - var/obj/item/stack/cable_coil/CC = W - if(CC.use(2)) - clear_internal_damage(MECHA_INT_SHORT_CIRCUIT) - to_chat(user, span_notice("You replace the fused wires.")) - else - to_chat(user, span_warning("You need two lengths of cable to fix this mech!")) - return - if(istype(W, /obj/item/mecha_parts)) var/obj/item/mecha_parts/P = W - P.try_attach_part(user, src) + P.try_attach_part(user, src, FALSE) return - log_message("Attacked by [W]. Attacker - [user]", LOG_MECHA) - return ..() + . = ..() + log_message("Attacked by [W]. Attacker - [user], Damage - [.]", LOG_MECHA) + if(isliving(user)) + var/mob/living/living_user = user + try_damage_component(., living_user.zone_selected) /obj/vehicle/sealed/mecha/wrench_act(mob/living/user, obj/item/I) ..() @@ -292,25 +301,11 @@ construction_state = MECHA_LOOSE_BOLTS to_chat(user, span_notice("You close the hatch to the power unit.")) -/obj/vehicle/sealed/mecha/screwdriver_act(mob/living/user, obj/item/I) - ..() - . = TRUE - if(internal_damage & MECHA_INT_TEMP_CONTROL) - clear_internal_damage(MECHA_INT_TEMP_CONTROL) - to_chat(user, span_notice("You repair the damaged temperature controller.")) - return - /obj/vehicle/sealed/mecha/welder_act(mob/living/user, obj/item/W) . = ..() if(user.combat_mode) return . = TRUE - if(internal_damage & MECHA_INT_TANK_BREACH) - if(!W.use_tool(src, user, 0, volume=50, amount=1)) - return - clear_internal_damage(MECHA_INT_TANK_BREACH) - to_chat(user, span_notice("You repair the damaged gas tank.")) - return if(atom_integrity < max_integrity) if(!W.use_tool(src, user, 0, volume=50, amount=1)) return @@ -319,7 +314,7 @@ if(atom_integrity == max_integrity) to_chat(user, span_notice("It looks to be fully repaired now.")) return - to_chat(user, span_warning("The [name] is at full integrity!")) + to_chat(user, span_warning("[src] is at full integrity!")) /obj/vehicle/sealed/mecha/proc/full_repair(charge_cell) atom_integrity = max_integrity @@ -340,15 +335,12 @@ emp_act(EMP_HEAVY) /obj/vehicle/sealed/mecha/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect) - if(!no_effect) - if(selected) - used_item = selected - else if(!visual_effect_icon) - visual_effect_icon = ATTACK_EFFECT_SMASH - if(damtype == BURN) - visual_effect_icon = ATTACK_EFFECT_MECHFIRE - else if(damtype == TOX) - visual_effect_icon = ATTACK_EFFECT_MECHTOXIN + if(!no_effect && !visual_effect_icon) + visual_effect_icon = ATTACK_EFFECT_SMASH + if(damtype == BURN) + visual_effect_icon = ATTACK_EFFECT_MECHFIRE + else if(damtype == TOX) + visual_effect_icon = ATTACK_EFFECT_MECHTOXIN ..() /obj/vehicle/sealed/mecha/atom_destruction() @@ -362,7 +354,7 @@ continue AI = crew var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI) - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + for(var/obj/item/mecha_parts/mecha_equipment/E in flat_equipment) if(E.detachable && prob(30)) WR.crowbar_salvage += E E.detach(WR) //detaches from src into WR diff --git a/code/modules/vehicles/mecha/mecha_parts.dm b/code/modules/vehicles/mecha/mecha_parts.dm index b2282065cf3..9950e78311b 100644 --- a/code/modules/vehicles/mecha/mecha_parts.dm +++ b/code/modules/vehicles/mecha/mecha_parts.dm @@ -9,14 +9,14 @@ w_class = WEIGHT_CLASS_GIGANTIC flags_1 = CONDUCT_1 -/obj/item/mecha_parts/proc/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M) //For attaching parts to a finished mech +/obj/item/mecha_parts/proc/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M, attach_right = FALSE) //For attaching parts to a finished mech if(!user.transferItemToLoc(src, M)) to_chat(user, span_warning("\The [src] is stuck to your hand, you cannot put it in \the [M]!")) return FALSE user.visible_message(span_notice("[user] attaches [src] to [M]."), span_notice("You attach [src] to [M].")) return TRUE -/obj/item/mecha_parts/part/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/part/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M, attach_right = FALSE) return /obj/item/mecha_parts/chassis diff --git a/code/modules/vehicles/mecha/mecha_topic.dm b/code/modules/vehicles/mecha/mecha_topic.dm deleted file mode 100644 index 3039d2b8f49..00000000000 --- a/code/modules/vehicles/mecha/mecha_topic.dm +++ /dev/null @@ -1,428 +0,0 @@ - -//////////////////////////////////// -///// Rendering stats window /////// -//////////////////////////////////// - -/obj/vehicle/sealed/mecha/proc/get_stats_html(mob/user) - . = {" - - - [name] data - - - - -
- [get_stats_part(user)] -
-
- [get_equipment_list()] -
-
-
- [get_commands()] -
-
- [get_equipment_menu()] -
- - "} - -//Returns the autoupdate javascript script functions for the mecha ui. -/obj/vehicle/sealed/mecha/proc/get_ssticker_function() - . = {" - function SSticker() { - setInterval(function(){ - window.location='byond://?src=[REF(src)]&update_content=1'; - }, 1000); - } - "} - -///Returns the status of the mech. -/obj/vehicle/sealed/mecha/proc/get_stats_part(mob/user) - var/integrity = atom_integrity/max_integrity*100 - var/cell_charge = get_charge() - var/datum/gas_mixture/int_tank_air = 0 - var/tank_pressure = 0 - var/tank_temperature = 0 - var/cabin_pressure = 0 - if (internal_tank) - int_tank_air = internal_tank.return_air() - tank_pressure = internal_tank ? round(int_tank_air.return_pressure(),0.01) : "None" - tank_temperature = internal_tank ? int_tank_air.temperature : "Unknown" - cabin_pressure = round(return_pressure(),0.01) - . = {"[report_internal_damage()] - [integrity<30?"[span_userdanger("DAMAGE LEVEL CRITICAL")]
":null] - Integrity: [integrity]%
- Power cell charge: [isnull(cell_charge)?"No power cell installed":"[cell.percent()]%"]
- Air source: [internal_tank?"[use_internal_tank?"Internal Airtank":"Environment"]":"Environment"]
- Airtank pressure: [internal_tank?"[tank_pressure]kPa":"N/A"]
- Airtank temperature: [internal_tank?"[tank_temperature]°K|[tank_temperature - T0C]°C":"N/A"]
- Cabin pressure: [internal_tank?"[cabin_pressure>WARNING_HIGH_PRESSURE ? span_danger("[cabin_pressure]"): cabin_pressure]kPa":"N/A"]
- Cabin temperature: [internal_tank?"[return_temperature()]°K|[return_temperature() - T0C]°C":"N/A"]
- [dna_lock?"DNA-locked:
[dna_lock] \[Reset\]
":""]
"} - . += "[get_actions(user)]
" - -///Returns HTML for mech actions. Ideally, this proc would be empty for the base mecha. Segmented for easy refactoring. -/obj/vehicle/sealed/mecha/proc/get_actions(mob/user) - . = "" - . += "[LAZYACCESSASSOC(occupant_actions, user, /datum/action/vehicle/sealed/mecha/mech_defense_mode) ? "Defense Mode: [defense_mode ? "Enabled" : "Disabled"]
" : ""]" - . += "[LAZYACCESSASSOC(occupant_actions, user, /datum/action/vehicle/sealed/mecha/mech_overload_mode) ? "Leg Actuators Overload: [leg_overload_mode ? "Enabled" : "Disabled"]
" : ""]" - . += "[LAZYACCESSASSOC(occupant_actions, user, /datum/action/vehicle/sealed/mecha/mech_smoke) ? "Smoke Charges remaining: [smoke_charges]
" : ""]" - . += "[LAZYACCESSASSOC(occupant_actions, user, /datum/action/vehicle/sealed/mecha/mech_zoom) ? "Zoom: [zoom_mode ? "Enabled" : "Disabled"]
" : ""]" - . += "[LAZYACCESSASSOC(occupant_actions, user, /datum/action/vehicle/sealed/mecha/mech_switch_damtype) ? "Damtype: [damtype]
" : ""]" - . += "[LAZYACCESSASSOC(occupant_actions, user, /datum/action/vehicle/sealed/mecha/mech_toggle_phasing) ? "Phase Modulator: [phasing ? "Enabled" : "Disabled"]
" : ""]" - -///HTML for internal damage. -/obj/vehicle/sealed/mecha/proc/report_internal_damage() - . = "" - var/list/dam_reports = list( - "[MECHA_INT_FIRE]" = span_userdanger("INTERNAL FIRE"), - "[MECHA_INT_TEMP_CONTROL]" = span_userdanger("LIFE SUPPORT SYSTEM MALFUNCTION"), - "[MECHA_INT_TANK_BREACH]" = span_userdanger("GAS TANK BREACH"), - "[MECHA_INT_CONTROL_LOST]" = "[span_userdanger("COORDINATION SYSTEM CALIBRATION FAILURE")] - Recalibrate", - "[MECHA_INT_SHORT_CIRCUIT]" = span_userdanger("SHORT CIRCUIT") - ) - for(var/tflag in dam_reports) - var/intdamflag = text2num(tflag) - if(internal_damage & intdamflag) - . += dam_reports[tflag] - . += "
" - if(return_pressure() > WARNING_HIGH_PRESSURE) - . += "[span_userdanger("DANGEROUSLY HIGH CABIN PRESSURE")]
" - -///HTML for list of equipment. -/obj/vehicle/sealed/mecha/proc/get_equipment_list() //outputs mecha equipment list in html - if(!LAZYLEN(equipment)) - return - . = "Equipment:
" - for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) - . += "
[MT.get_equip_info()]
" - . += "
" - -///HTML for commands. -/obj/vehicle/sealed/mecha/proc/get_commands() - . = {" -
-
Electronics
- -
-
-
Permissions & Logging
- -
"} - - -/obj/vehicle/sealed/mecha/proc/get_equipment_menu() //outputs mecha html equipment menu - . = {" -
-
Equipment
- -
"} - -/obj/vehicle/sealed/mecha/proc/output_access_dialog(obj/item/card/id/id_card, mob/user) - if(!id_card || !user) - return - . = {" - - - - - -

Following keycodes are present in this system:

"} - for(var/a in operation_req_access) - . += "[SSid_access.get_access_desc(a)] - Delete
" - . += "

Following keycodes were detected on portable device:

" - for(var/a in id_card.access) - if(a in operation_req_access) - continue - var/a_name = SSid_access.get_access_desc(a) - if(!a_name) - continue //there's some strange access without a name - . += "[a_name] - Add
" - . +={"
Lock ID panel
- [span_danger("(Warning! The ID upload panel can be unlocked only through Exosuit Interface.)")] - - "} - user << browse(., "window=exosuit_add_access") - onclose(user, "exosuit_add_access") - - -/obj/vehicle/sealed/mecha/proc/output_maintenance_dialog(obj/item/card/id/id_card,mob/user) - if(!id_card || !user) - return - . = {" - - - - - - [(mecha_flags & ADDING_ACCESS_POSSIBLE)?"Edit operation keycodes":null] - [(mecha_flags & ADDING_MAINT_ACCESS_POSSIBLE)?"[(construction_state > MECHA_LOCKED) ? "Terminate" : "Initiate"] maintenance protocol":null] - [(construction_state == MECHA_OPEN_HATCH) ?"--------------------
":null] - [(construction_state == MECHA_OPEN_HATCH) ?"[cell?"Drop power cell":"No cell installed
"]":null] - [(construction_state == MECHA_OPEN_HATCH) ?"[scanmod?"Drop scanning module":"No scanning module installed
"]":null] - [(construction_state == MECHA_OPEN_HATCH) ?"[capacitor?"Drop capacitor":"No capacitor installed
"]":null] - [(construction_state == MECHA_OPEN_HATCH) ?"--------------------
":null] - [(construction_state > MECHA_LOCKED) ?"Set Cabin Air Pressure":null] - - "} - user << browse(., "window=exosuit_maint_console") - onclose(user, "exosuit_maint_console") - - - - -///////////////// -///// Topic ///// -///////////////// - -/obj/vehicle/sealed/mecha/Topic(href, href_list) - ..() - - if(!usr) - return - - if(href_list["close"]) - return - - if(usr.incapacitated()) - return - - if(in_range(src, usr)) - //Start of ID requirements. - if(href_list["id_card"]) - var/obj/item/card/id/id_card - id_card = locate(href_list["id_card"]) - if(!istype(id_card)) - return - - if(href_list["req_access"]) - if(!(mecha_flags & ADDING_ACCESS_POSSIBLE)) - return - output_access_dialog(id_card,usr) - return - - if(href_list["maint_access"]) - if(!(mecha_flags & ADDING_MAINT_ACCESS_POSSIBLE)) - return - if(construction_state == MECHA_LOCKED) - construction_state = MECHA_SECURE_BOLTS - to_chat(usr, span_notice("The securing bolts are now exposed.")) - else if(construction_state == MECHA_SECURE_BOLTS) - construction_state = MECHA_LOCKED - to_chat(usr, span_notice("The securing bolts are now hidden.")) - output_maintenance_dialog(id_card,usr) - return - if(href_list["drop_cell"]) - if(construction_state == MECHA_OPEN_HATCH) - cell.forceMove(get_turf(src)) - cell = null - output_maintenance_dialog(id_card,usr) - return - if(href_list["drop_scanmod"]) - if(construction_state == MECHA_OPEN_HATCH) - scanmod.forceMove(get_turf(src)) - scanmod = null - output_maintenance_dialog(id_card,usr) - return - if(href_list["drop_cap"]) - if(construction_state == MECHA_OPEN_HATCH) - capacitor.forceMove(get_turf(src)) - capacitor = null - output_maintenance_dialog(id_card,usr) - return - - if(href_list["add_req_access"]) - if(!(mecha_flags & ADDING_ACCESS_POSSIBLE)) - return - operation_req_access += text2num(href_list["add_req_access"]) - output_access_dialog(id_card,usr) - return - - if(href_list["del_req_access"]) - if(!(mecha_flags & ADDING_ACCESS_POSSIBLE)) - return - operation_req_access -= text2num(href_list["del_req_access"]) - output_access_dialog(id_card, usr) - return - return //Here end everything requiring an ID. - - //Here ID access stuff goes to die. - if(href_list["finish_req_access"]) - mecha_flags &= ~ADDING_ACCESS_POSSIBLE - usr << browse(null,"window=exosuit_add_access") - return - - //Set pressure. - if(href_list["set_internal_tank_valve"] && construction_state) - var/new_pressure = input(usr,"Input new output pressure","Pressure setting",internal_tank_valve) as num|null - if(isnull(new_pressure) || usr.incapacitated() || !construction_state) - return - internal_tank_valve = new_pressure - to_chat(usr, span_notice("The internal pressure valve has been set to [internal_tank_valve]kPa.")) - return - - //Start of all internal topic stuff. - if(!locate(usr) in occupants) - return - - if(href_list["update_content"]) - send_byjax(usr,"exosuit.browser","content", get_stats_part()) - return - - //Selects the mech equipment/weapon. - if(href_list["select_equip"]) - var/obj/item/mecha_parts/mecha_equipment/equip = locate(href_list["select_equip"]) in src - if(!equip || !equip.selectable) - return - selected = equip - to_chat(occupants, "[icon2html(src, occupants)][span_notice("You switch to [equip].")]") - visible_message(span_notice("[src] raises [equip].")) - send_byjax(usr, "exosuit.browser", "eq_list", get_equipment_list()) - playsound(src,'sound/machines/piston_raise.ogg', 40, TRUE) - return - - //Toggles radio broadcasting - if(href_list["rmictoggle"]) - radio.set_broadcasting(!radio.get_broadcasting()) - send_byjax(usr,"exosuit.browser","rmicstate",(radio.get_broadcasting()?"Engaged":"Disengaged")) - return - - //Toggles radio listening - if(href_list["rspktoggle"]) - radio.set_listening(!radio.get_listening()) - send_byjax(usr,"exosuit.browser","rspkstate",(radio.get_listening()?"Engaged":"Disengaged")) - return - - //Changes radio freqency. - if(href_list["rfreq"]) - var/new_frequency = radio.get_frequency() + text2num(href_list["rfreq"]) - radio.set_frequency(sanitize_frequency(new_frequency, radio.freerange)) - send_byjax(usr,"exosuit.browser","rfreq","[format_frequency(radio.get_frequency())]") - return - - //Changes the exosuit name. - if(href_list["change_name"]) - var/userinput = tgui_input_text(usr, "Choose a new exosuit name", "Rename exosuit", max_length = MAX_NAME_LEN) - if(!userinput || !locate(usr) in occupants || usr.incapacitated()) - return - name = userinput - return - - //Toggles ID upload. - if (href_list["toggle_id_upload"]) - mecha_flags ^= ADDING_ACCESS_POSSIBLE - send_byjax(usr,"exosuit.browser","t_id_upload","[(mecha_flags & ADDING_ACCESS_POSSIBLE)?"L":"Unl"]ock ID upload panel") - return - - //Toggles main access. - if(href_list["toggle_maint_access"]) - if(construction_state) - to_chat(occupants, "[icon2html(src, occupants)][span_danger("Maintenance protocols in effect")]") - return - mecha_flags ^= ADDING_MAINT_ACCESS_POSSIBLE - send_byjax(usr,"exosuit.browser","t_maint_access","[(mecha_flags & ADDING_MAINT_ACCESS_POSSIBLE)?"Forbid":"Permit"] maintenance protocols") - return - - //Toggles connection port. - if (href_list["toggle_port_connection"]) - if(internal_tank.connected_port) - if(internal_tank.disconnect()) - to_chat(occupants, "[icon2html(src, occupants)][span_notice("Disconnected from the air system port.")]") - log_message("Disconnected from gas port.", LOG_MECHA) - else - to_chat(occupants, "[icon2html(src, occupants)][span_warning("Unable to disconnect from the air system port!")]") - return - else - var/obj/machinery/atmospherics/components/unary/portables_connector/possible_port = locate() in loc - if(internal_tank.connect(possible_port)) - to_chat(occupants, "[icon2html(src, occupants)][span_notice("Connected to the air system port.")]") - log_message("Connected to gas port.", LOG_MECHA) - else - to_chat(occupants, "[icon2html(src, occupants)][span_warning("Unable to connect with air system port!")]") - return - send_byjax(occupants,"exosuit.browser","t_port_connection","[internal_tank.connected_port?"Disconnect from":"Connect to"] gas port") - return - - //Turns on the DNA lock - if(href_list["dna_lock"]) - var/mob/living/carbon/user = usr - if(!istype(user) || !user.dna) - to_chat(user, "[icon2html(src, occupants)][span_notice("You can't create a DNA lock with no DNA!.")]") - return - dna_lock = user.dna.unique_enzymes - to_chat(user, "[icon2html(src, occupants)][span_notice("You feel a prick as the needle takes your DNA sample.")]") - return - - //Resets the DNA lock - if(href_list["reset_dna"]) - dna_lock = null - return - - //Repairs internal damage - if(href_list["repair_int_control_lost"]) - to_chat(occupants, "[icon2html(src, occupants)][span_notice("Recalibrating coordination system...")]") - log_message("Recalibration of coordination system started.", LOG_MECHA) - addtimer(CALLBACK(src, .proc/stationary_repair, loc), 100, TIMER_UNIQUE) - -///Repairs internal damage if the mech hasn't moved. -/obj/vehicle/sealed/mecha/proc/stationary_repair(location) - if(location == loc) - clear_internal_damage(MECHA_INT_CONTROL_LOST) - to_chat(occupants, "[icon2html(src, occupants)][span_notice("Recalibration successful.")]") - log_message("Recalibration of coordination system finished with 0 errors.", LOG_MECHA) - else - to_chat(occupants, "[icon2html(src, occupants)][span_warning("Recalibration failed!")]") - log_message("Recalibration of coordination system failed with 1 error.", LOG_MECHA, color="red") diff --git a/code/modules/vehicles/mecha/mecha_ui.dm b/code/modules/vehicles/mecha/mecha_ui.dm new file mode 100644 index 00000000000..85530636130 --- /dev/null +++ b/code/modules/vehicles/mecha/mecha_ui.dm @@ -0,0 +1,319 @@ +/// A preview of the mech for the UI +/atom/movable/screen/mech_view + name = "mechview" + del_on_map_removal = FALSE + layer = OBJ_LAYER + plane = GAME_PLANE + + /// The body that is displayed + var/obj/vehicle/sealed/mecha/owner + ///list of plane masters to apply to owners + var/list/plane_masters = list() + +/atom/movable/screen/mech_view/Initialize(mapload, obj/vehicle/sealed/mecha/newowner) + . = ..() + owner = newowner + assigned_map = "mech_view_[REF(owner)]" + set_position(1, 1) + for(var/plane_master_type in subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/blackness) + var/atom/movable/screen/plane_master/plane_master = new plane_master_type() + plane_master.screen_loc = "[assigned_map]:CENTER" + plane_masters += plane_master + +/atom/movable/screen/mech_view/Destroy() + QDEL_LIST(plane_masters) + owner = null + return ..() + +/obj/vehicle/sealed/mecha/ui_close(mob/user) + . = ..() + user.client?.screen -= ui_view.plane_masters + user.client?.clear_map(ui_view.assigned_map) + +/obj/vehicle/sealed/mecha/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Mecha", name, ui_x, ui_y) + ui.open() + user.client?.screen |= ui_view.plane_masters + user.client?.register_map_obj(ui_view) + +/obj/vehicle/sealed/mecha/ui_status(mob/user) + if(contains(user)) + return UI_INTERACTIVE + return min( + ui_status_user_is_abled(user, src), + ui_status_user_has_free_hands(user, src), + ui_status_user_is_advanced_tool_user(user), + ui_status_only_living(user), + max( + ui_status_user_is_adjacent(user, src), + ui_status_silicon_has_access(user, src), + ) + ) + +/obj/vehicle/sealed/mecha/ui_assets(mob/user) + return list(get_asset_datum(/datum/asset/spritesheet/mechaarmor)) + +/obj/vehicle/sealed/mecha/ui_static_data(mob/user) + var/list/data = list() + data["cabin_dangerous_highpressure"] = WARNING_HIGH_PRESSURE + data["mineral_material_amount"] = MINERAL_MATERIAL_AMOUNT + //map of relevant flags to check tgui side, not every flag needs to be here + data["mechflag_keys"] = list( + "ADDING_ACCESS_POSSIBLE" = ADDING_ACCESS_POSSIBLE, + "ADDING_MAINT_ACCESS_POSSIBLE" = ADDING_MAINT_ACCESS_POSSIBLE, + "LIGHTS_ON" = LIGHTS_ON, + "HAS_LIGHTS" = HAS_LIGHTS, + ) + data["internal_damage_keys"] = list( + "MECHA_INT_FIRE" = MECHA_INT_FIRE, + "MECHA_INT_TEMP_CONTROL" = MECHA_INT_TEMP_CONTROL, + "MECHA_INT_TANK_BREACH" = MECHA_INT_TANK_BREACH, + "MECHA_INT_CONTROL_LOST" = MECHA_INT_CONTROL_LOST, + ) + data["mech_electronics"] = list( + "minfreq" = MIN_FREE_FREQ, + "maxfreq" = MAX_FREE_FREQ, + ) + return data + +/obj/vehicle/sealed/mecha/ui_data(mob/user) + var/list/data = list() + var/isoperator = (user.loc == src) //maintenance mode outside of mech + data["isoperator"] = isoperator + if(!isoperator) + data["name"] = name + data["mecha_flags"] = mecha_flags + data["internal_tank_valve"] = internal_tank_valve + data["cell"] = cell?.name + data["scanning"] = scanmod?.name + data["capacitor"] = capacitor?.name + data["operation_req_access"] = list() + data["idcard_access"] = list() + for(var/code in operation_req_access) + data["operation_req_access"] += list(list("name" = SSid_access.get_access_desc(code), "number" = code)) + if(!isliving(user)) + return data + var/mob/living/living_user = user + var/obj/item/card/id/card = living_user.get_idcard(TRUE) + if(!card) + return data + for(var/idcode in card.access) + if(idcode in operation_req_access) + continue + var/accessname = SSid_access.get_access_desc(idcode) + if(!accessname) + continue //there's some strange access without a name + data["idcard_access"] += list(list("name" = accessname, "number" = idcode)) + return data + ui_view.appearance = appearance + var/datum/gas_mixture/int_tank_air= internal_tank?.return_air() + data["name"] = name + data["integrity"] = atom_integrity/max_integrity + data["power_level"] = cell?.charge + data["power_max"] = cell?.maxcharge + data["mecha_flags"] = mecha_flags + data["internal_damage"] = internal_damage + data["air_source"] = use_internal_tank ? "Internal Airtank" : "Environment" + data["airtank_pressure"] = int_tank_air ? round(int_tank_air.return_pressure(), 0.01) : null + data["airtank_temp"] = int_tank_air?.temperature + data["port_connected"] = internal_tank?.connected_port ? TRUE : FALSE + data["cabin_pressure"] = round(return_pressure(), 0.01) + data["cabin_temp"] = return_temperature() + data["dna_lock"] = dna_lock + data["mech_view"] = ui_view.assigned_map + if(radio) + data["mech_electronics"] = list( + "microphone" = radio.get_broadcasting(), + "speaker" = radio.get_listening(), + "frequency" = radio.get_frequency(), + ) + if(equip_by_category[MECHA_L_ARM]) + var/obj/item/mecha_parts/mecha_equipment/l_gun = equip_by_category[MECHA_L_ARM] + var/isballisticweapon = istype(l_gun, /obj/item/mecha_parts/mecha_equipment/weapon/ballistic) + data["left_arm_weapon"] = list( + "name" = l_gun.name, + "desc" = l_gun.desc, + "ref" = REF(l_gun), + "integrity" = (l_gun.get_integrity()/l_gun.max_integrity), + "isballisticweapon" = isballisticweapon, + "energy_per_use" = l_gun.energy_drain, + "snowflake" = l_gun.get_snowflake_data(), + ) + if(isballisticweapon) + var/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/weapon = l_gun + data["left_arm_weapon"] += list( + "projectiles" = weapon.projectiles, + "max_magazine" = initial(weapon.projectiles), + "projectiles_cache" = weapon.projectiles_cache, + "projectiles_cache_max" = weapon.projectiles_cache_max, + "disabledreload" = weapon.disabledreload, + "ammo_type" = weapon.ammo_type, + ) + if(equip_by_category[MECHA_R_ARM]) + var/obj/item/mecha_parts/mecha_equipment/r_gun = equip_by_category[MECHA_R_ARM] + var/isballisticweapon = istype(r_gun, /obj/item/mecha_parts/mecha_equipment/weapon/ballistic) + data["right_arm_weapon"] = list( + "name" = r_gun.name, + "desc" = r_gun.desc, + "ref" = REF(r_gun), + "integrity" = (r_gun.get_integrity()/r_gun.max_integrity), + "isballisticweapon" = isballisticweapon, + "energy_per_use" = r_gun.energy_drain, + "snowflake" = r_gun.get_snowflake_data(), + ) + if(isballisticweapon) + var/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/weapon = r_gun + data["right_arm_weapon"] += list( + "projectiles" = weapon.projectiles, + "max_magazine" = initial(weapon.projectiles), + "projectiles_cache" = weapon.projectiles_cache, + "projectiles_cache_max" = weapon.projectiles_cache_max, + "disabledreload" = weapon.disabledreload, + "ammo_type" = weapon.ammo_type, + ) + data["mech_equipment"] = list("utility" = list(), "power" = list(), "armor" = list()) + for(var/obj/item/mecha_parts/mecha_equipment/utility as anything in equip_by_category[MECHA_UTILITY]) + data["mech_equipment"]["utility"] += list(list( + "name" = utility.name, + "activated" = utility.activated, + "snowflake" = utility.get_snowflake_data(), + "ref" = REF(utility), + )) + for(var/obj/item/mecha_parts/mecha_equipment/power as anything in equip_by_category[MECHA_POWER]) + data["mech_equipment"]["power"] += list(list( + "name" = power.name, + "activated" = power.activated, + "snowflake" = power.get_snowflake_data(), + "ref" = REF(power), + )) + for(var/obj/item/mecha_parts/mecha_equipment/armor/armor as anything in equip_by_category[MECHA_ARMOR]) + data["mech_equipment"]["armor"] += list(list( + "protect_name" = armor.protect_name, + "iconstate_name" = armor.iconstate_name, + "ref" = REF(armor), + )) + return data + +/obj/vehicle/sealed/mecha/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + if(usr.loc != src) + switch(action) + if("stopmaint") + mecha_flags &= ~ADDING_MAINT_ACCESS_POSSIBLE + ui.close() + return FALSE + if("togglemaint") + if(!(mecha_flags & ADDING_MAINT_ACCESS_POSSIBLE)) + return FALSE + if(construction_state == MECHA_LOCKED) + construction_state = MECHA_SECURE_BOLTS + to_chat(usr, span_notice("The securing bolts are now exposed.")) + else if(construction_state == MECHA_SECURE_BOLTS) + construction_state = MECHA_LOCKED + to_chat(usr, span_notice("The securing bolts are now hidden.")) + if("drop_cell") + if(construction_state != MECHA_OPEN_HATCH) + return + cell.forceMove(get_turf(src)) + cell = null + if("drop_scanning") + if(construction_state == MECHA_OPEN_HATCH) + return + scanmod.forceMove(get_turf(src)) + scanmod = null + if("drop_capacitor") + if(construction_state == MECHA_OPEN_HATCH) + return + capacitor.forceMove(get_turf(src)) + capacitor = null + if("set_pressure") + var/new_pressure = tgui_input_number(usr, "Enter new pressure", "Cabin pressure change", internal_tank_valve) + if(isnull(new_pressure) || !construction_state) + return + internal_tank_valve = new_pressure + to_chat(usr, span_notice("The internal pressure valve has been set to [internal_tank_valve]kPa.")) + if("add_req_access") + if(!(mecha_flags & ADDING_ACCESS_POSSIBLE)) + return + if(!(params["added_access"] == "all")) + operation_req_access += params["added_access"] + else + var/mob/living/living_user = usr + var/obj/item/card/id/card = living_user.get_idcard(TRUE) + operation_req_access += card.access + if("del_req_access") + if(!(mecha_flags & ADDING_ACCESS_POSSIBLE)) + return + if(!(params["removed_access"] == "all")) + operation_req_access -= params["removed_access"] + else + operation_req_access = list() + if("lock_req_edit") + mecha_flags &= ~ADDING_ACCESS_POSSIBLE + return TRUE + if(!(usr in occupants)) + return + switch(action) + if("changename") + var/userinput = tgui_input_text(usr, "Choose a new exosuit name", "Rename exosuit", max_length = MAX_NAME_LEN) + if(!userinput) + return + if(is_ic_filtered(userinput) || is_soft_ic_filtered(userinput)) + tgui_alert(usr, "You cannot set a name that contains a word prohibited in IC chat!") + return + name = userinput + if("dna_lock") + var/mob/living/carbon/user = usr + if(!istype(user) || !user.dna) + to_chat(user, "[icon2html(src, occupants)][span_notice("You can't create a DNA lock with no DNA!.")]") + return + dna_lock = user.dna.unique_enzymes + to_chat(user, "[icon2html(src, occupants)][span_notice("You feel a prick as the needle takes your DNA sample.")]") + if("reset_dna") + dna_lock = null + if("view_dna") + tgui_alert(usr, "Enzymes detected: " + dna_lock) + return FALSE + if("toggle_airsource") + use_internal_tank = !use_internal_tank + balloon_alert(usr, "taking air from [use_internal_tank ? "internal airtank" : "environment"]") + log_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].", LOG_MECHA) + if("toggle_port") + if(internal_tank.connected_port) + if(internal_tank.disconnect()) + to_chat(occupants, "[icon2html(src, occupants)][span_notice("Disconnected from the air system port.")]") + log_message("Disconnected from gas port.", LOG_MECHA) + return TRUE + to_chat(occupants, "[icon2html(src, occupants)][span_warning("Unable to disconnect from the air system port!")]") + return + var/obj/machinery/atmospherics/components/unary/portables_connector/possible_port = locate() in loc + if(internal_tank.connect(possible_port)) + to_chat(occupants, "[icon2html(src, occupants)][span_notice("Connected to the air system port.")]") + log_message("Connected to gas port.", LOG_MECHA) + return TRUE + to_chat(occupants, "[icon2html(src, occupants)][span_warning("Unable to connect with air system port!")]") + if("toggle_maintenance") + if(construction_state) + to_chat(occupants, "[icon2html(src, occupants)][span_danger("Maintenance protocols in effect")]") + return + mecha_flags ^= ADDING_MAINT_ACCESS_POSSIBLE + if("toggle_id_panel") + mecha_flags ^= ADDING_ACCESS_POSSIBLE + if("toggle_microphone") + radio.set_broadcasting(!radio.get_broadcasting()) + if("toggle_speaker") + radio.set_listening(!radio.get_listening()) + if("set_frequency") + radio.set_frequency(sanitize_frequency(params["new_frequency"], radio.freerange)) + if("repair_int_damage") + ui.close() //if doing this you're likely want to watch for bad people so close the UI + try_repair_int_damage(usr, params["flag"]) + return FALSE + if("equip_act") + var/obj/item/mecha_parts/mecha_equipment/gear = locate(params["ref"]) in flat_equipment + return gear?.ui_act(params["gear_action"], params, ui, state) + return TRUE diff --git a/code/modules/vehicles/mecha/medical/odysseus.dm b/code/modules/vehicles/mecha/medical/odysseus.dm index 11629500afa..fbf4960e6c9 100644 --- a/code/modules/vehicles/mecha/medical/odysseus.dm +++ b/code/modules/vehicles/mecha/medical/odysseus.dm @@ -8,8 +8,6 @@ max_temperature = 15000 max_integrity = 120 wreckage = /obj/structure/mecha_wreckage/odysseus - internal_damage_threshold = 35 - deflect_chance = 15 step_energy_drain = 6 internals_req_access = list(ACCESS_MECH_SCIENCE, ACCESS_MECH_MEDICAL) diff --git a/code/modules/vehicles/mecha/working/clarke.dm b/code/modules/vehicles/mecha/working/clarke.dm index ba901f0f2ed..49454ce7402 100644 --- a/code/modules/vehicles/mecha/working/clarke.dm +++ b/code/modules/vehicles/mecha/working/clarke.dm @@ -9,10 +9,20 @@ movedelay = 1.25 resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF lights_power = 7 - deflect_chance = 10 step_energy_drain = 15 //slightly higher energy drain since you movin those wheels FAST armor = list(MELEE = 20, BULLET = 10, LASER = 20, ENERGY = 10, BOMB = 60, BIO = 0, FIRE = 100, ACID = 100) //low armor to compensate for fire protection and speed - max_equip = 7 + equip_by_category = list( + MECHA_L_ARM = null, + MECHA_R_ARM = null, + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/orebox_manager), + MECHA_POWER = list(), + MECHA_ARMOR = list(), + ) + max_equip_by_category = list( + MECHA_UTILITY = 3, + MECHA_POWER = 1, + MECHA_ARMOR = 1, + ) wreckage = /obj/structure/mecha_wreckage/clarke enter_delay = 40 mecha_flags = ADDING_ACCESS_POSSIBLE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE | OMNIDIRECTIONAL_ATTACKS @@ -21,8 +31,6 @@ /obj/vehicle/sealed/mecha/working/clarke/Initialize(mapload) . = ..() box = new(src) - var/obj/item/mecha_parts/mecha_equipment/orebox_manager/ME = new(src) - ME.attach(src) /obj/vehicle/sealed/mecha/working/clarke/Destroy() INVOKE_ASYNC(box, /obj/structure/ore_box/proc/dump_box_contents) @@ -40,12 +48,13 @@ desc = "An automated ore box management device." icon = 'icons/obj/mining.dmi' icon_state = "bin" + equipment_slot = MECHA_UTILITY selectable = FALSE detachable = FALSE /// Var to avoid istype checking every time the topic button is pressed. This will only work inside Clarke mechs. var/obj/vehicle/sealed/mecha/working/clarke/hostmech -/obj/item/mecha_parts/mecha_equipment/orebox_manager/attach(obj/vehicle/sealed/mecha/M) +/obj/item/mecha_parts/mecha_equipment/orebox_manager/attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE) . = ..() if(istype(M, /obj/vehicle/sealed/mecha/working/clarke)) hostmech = M @@ -54,14 +63,11 @@ hostmech = null //just in case return ..() -/obj/item/mecha_parts/mecha_equipment/orebox_manager/Topic(href,href_list) +/obj/item/mecha_parts/mecha_equipment/orebox_manager/ui_act(action, list/params) . = ..() - if(!hostmech || !hostmech.box) - return - hostmech.box.dump_box_contents() - -/obj/item/mecha_parts/mecha_equipment/orebox_manager/get_equip_info() - return "[..()] [hostmech?.box ? "Unload Cargo" : "Error"]" + if(action == "toggle") + hostmech.box?.dump_box_contents() + activated = TRUE #define SEARCH_COOLDOWN 1 MINUTES diff --git a/code/modules/vehicles/mecha/working/ripley.dm b/code/modules/vehicles/mecha/working/ripley.dm index 1b993806f60..c716ca373cc 100644 --- a/code/modules/vehicles/mecha/working/ripley.dm +++ b/code/modules/vehicles/mecha/working/ripley.dm @@ -5,30 +5,42 @@ base_icon_state = "ripley" silicon_icon_state = "ripley-empty" movedelay = 1.5 //Move speed, lower is faster. - /// How fast the mech is in low pressure - var/fast_pressure_step_in = 1.5 - /// How fast the mech is in normal pressure - var/slow_pressure_step_in = 2 max_temperature = 20000 max_integrity = 200 + ui_x = 1200 lights_power = 7 - deflect_chance = 15 armor = list(MELEE = 40, BULLET = 20, LASER = 10, ENERGY = 20, BOMB = 40, BIO = 0, FIRE = 100, ACID = 100) - max_equip = 6 + max_equip_by_category = list( + MECHA_UTILITY = 2, + MECHA_POWER = 1, + MECHA_ARMOR = 1, + ) wreckage = /obj/structure/mecha_wreckage/ripley + possible_int_damage = MECHA_INT_FIRE|MECHA_INT_CONTROL_LOST|MECHA_INT_SHORT_CIRCUIT internals_req_access = list(ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_MINING) enclosed = FALSE //Normal ripley has an open cockpit design enter_delay = 10 //can enter in a quarter of the time of other mechs exit_delay = 10 + /// Custom Ripley step and turning sounds (from TGMC) + stepsound = 'sound/mecha/powerloader_step.ogg' + turnsound = 'sound/mecha/powerloader_turn2.ogg' + equip_by_category = list( + MECHA_L_ARM = null, + MECHA_R_ARM = null, + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/ejector), + MECHA_POWER = list(), + MECHA_ARMOR = list(), + ) /// Amount of Goliath hides attached to the mech var/hides = 0 /// List of all things in Ripley's Cargo Compartment var/list/cargo /// How much things Ripley can carry in their Cargo Compartment var/cargo_capacity = 15 - /// Custom Ripley step and turning sounds (from TGMC) - stepsound = 'sound/mecha/powerloader_step.ogg' - turnsound = 'sound/mecha/powerloader_turn2.ogg' + /// How fast the mech is in low pressure + var/fast_pressure_step_in = 1.5 + /// How fast the mech is in normal pressure + var/slow_pressure_step_in = 2 /obj/vehicle/sealed/mecha/working/ripley/Move() . = ..() @@ -36,16 +48,10 @@ /obj/vehicle/sealed/mecha/working/ripley/generate_actions() //isnt allowed to have internal air initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_eject) - initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_cycle_equip) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_lights) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_view_stats) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/strafe) -/obj/vehicle/sealed/mecha/working/ripley/check_for_internal_damage(list/possible_int_damage, ignore_threshold = FALSE) - if(!enclosed) //if we don't even have an air tank, these two doesn't make a ton of sense. - possible_int_damage -= (MECHA_INT_TEMP_CONTROL + MECHA_INT_TANK_BREACH) - return ..() - /obj/vehicle/sealed/mecha/working/ripley/Initialize(mapload) . = ..() AddComponent(/datum/component/armor_plate,3,/obj/item/stack/sheet/animalhide/goliath_hide,list(MELEE = 10, BULLET = 5, LASER = 5)) @@ -68,6 +74,7 @@ movedelay = 3 //SKYRAT EDIT - Movedelay is now 3, from 4. max_temperature = 30000 max_integrity = 250 + possible_int_damage = MECHA_INT_FIRE|MECHA_INT_TEMP_CONTROL|MECHA_INT_TANK_BREACH|MECHA_INT_CONTROL_LOST|MECHA_INT_SHORT_CIRCUIT armor = list(MELEE = 40, BULLET = 30, LASER = 30, ENERGY = 30, BOMB = 60, BIO = 0, FIRE = 100, ACID = 100) wreckage = /obj/structure/mecha_wreckage/ripley/mk2 enclosed = TRUE @@ -77,7 +84,6 @@ /obj/vehicle/sealed/mecha/working/ripley/mk2/generate_actions() initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_eject) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_internals) - initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_cycle_equip) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_lights) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_view_stats) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/strafe) @@ -96,23 +102,23 @@ enclosed = TRUE enter_delay = 40 silicon_icon_state = null - -/obj/vehicle/sealed/mecha/working/ripley/deathripley/Initialize(mapload) - . = ..() - var/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/kill/fake/clamper = new(loc) - clamper.attach(src) + equip_by_category = list( + MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/kill/fake, + MECHA_R_ARM = null, + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/ejector), + MECHA_POWER = list(), + MECHA_ARMOR = list(), + ) /obj/vehicle/sealed/mecha/working/ripley/deathripley/real desc = "OH SHIT IT'S THE DEATHSQUAD WE'RE ALL GONNA DIE. FOR REAL" - -/obj/vehicle/sealed/mecha/working/ripley/deathripley/real/Initialize(mapload) - . = ..() - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - E.detach() - qdel(E) - LAZYCLEARLIST(equipment) - var/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/kill/clamper = new(loc) - clamper.attach(src) + equip_by_category = list( + MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/kill, + MECHA_R_ARM = null, + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/ejector), + MECHA_POWER = list(), + MECHA_ARMOR = list(), + ) /obj/vehicle/sealed/mecha/working/ripley/mining desc = "An old, dusty mining Ripley." @@ -140,7 +146,7 @@ //Attach hydraulic clamp var/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/HC = new - HC.attach(src) + HC.attach(src, TRUE) var/obj/item/mecha_parts/mecha_equipment/mining_scanner/scanner = new scanner.attach(src) @@ -149,7 +155,6 @@ name = "\improper APLU \"Big Bess\"" icon_state = "hauler" base_icon_state = "hauler" - max_equip = 2 max_integrity = 100 //Has half the health of a normal RIPLEY mech, so it's harder to use as a weapon. /obj/vehicle/sealed/mecha/working/ripley/cargo/Initialize(mapload) @@ -168,19 +173,6 @@ return FALSE return ..() -/obj/vehicle/sealed/mecha/working/ripley/Topic(href, href_list) - ..() - if(href_list["drop_from_cargo"]) - var/obj/cargoobj = locate(href_list["drop_from_cargo"]) in cargo - if(cargoobj) - to_chat(occupants, "[icon2html(src, occupants)][span_notice("You unload [cargoobj].")]") - cargoobj.forceMove(drop_location()) - LAZYREMOVE(cargo, cargoobj) - if(cargoobj == box) - box = null - log_message("Unloaded [cargoobj]. Cargo compartment capacity: [cargo_capacity - LAZYLEN(cargo)]", LOG_MECHA) - - /obj/vehicle/sealed/mecha/working/ripley/contents_explosion(severity, target) for(var/i in cargo) var/obj/cargoobj = i @@ -189,16 +181,38 @@ cargoobj.forceMove(drop_location()) return ..() -/obj/vehicle/sealed/mecha/working/ripley/get_stats_part() - var/output = ..() - output += "Cargo Compartment Contents:
" - if(LAZYLEN(cargo)) - for(var/obj/O in cargo) - output += "Unload : [O]
" - else - output += "Nothing" - output += "
" - return output +/obj/item/mecha_parts/mecha_equipment/ejector + name = "Cargo compartment" + equipment_slot = MECHA_UTILITY + detachable = FALSE + +/obj/item/mecha_parts/mecha_equipment/ejector/get_snowflake_data() + var/list/data = list("snowflake_id" = MECHA_SNOWFLAKE_ID_EJECTOR, "cargo" = list()) + var/obj/vehicle/sealed/mecha/working/ripley/miner = chassis + for(var/obj/crate in miner.cargo) + data["cargo"] += list(list( + "name" = crate.name, + "ref" = REF(crate), + )) + return data + +/obj/item/mecha_parts/mecha_equipment/ejector/ui_act(action, list/params) + . = ..() + if(.) + return TRUE + if(action == "eject") + var/obj/vehicle/sealed/mecha/working/ripley/miner = chassis + var/obj/crate = locate(params["cargoref"]) in miner.cargo + if(!crate) + return FALSE + to_chat(miner.occupants, "[icon2html(src, miner.occupants)][span_notice("You unload [crate].")]") + crate.forceMove(drop_location()) + LAZYREMOVE(miner.cargo, crate) + if(crate == miner.box) + miner.box = null + log_message("Unloaded [crate]. Cargo compartment capacity: [miner.cargo_capacity - LAZYLEN(miner.cargo)]", LOG_MECHA) + return TRUE + /obj/vehicle/sealed/mecha/working/ripley/relay_container_resist_act(mob/living/user, obj/O) to_chat(user, span_notice("You lean on the back of [O] and start pushing so it falls out of [src].")) @@ -222,10 +236,10 @@ if(lavaland_equipment_pressure_check(T)) movedelay = fast_pressure_step_in - for(var/obj/item/mecha_parts/mecha_equipment/drill/drill in equipment) + for(var/obj/item/mecha_parts/mecha_equipment/drill/drill in flat_equipment) drill.equip_cooldown = initial(drill.equip_cooldown) * 0.5 else movedelay = slow_pressure_step_in - for(var/obj/item/mecha_parts/mecha_equipment/drill/drill in equipment) + for(var/obj/item/mecha_parts/mecha_equipment/drill/drill in flat_equipment) drill.equip_cooldown = initial(drill.equip_cooldown) diff --git a/code/modules/vehicles/mecha/working/working.dm b/code/modules/vehicles/mecha/working/working.dm index 8e630c91a58..3138fc0121c 100644 --- a/code/modules/vehicles/mecha/working/working.dm +++ b/code/modules/vehicles/mecha/working/working.dm @@ -1,5 +1,4 @@ /obj/vehicle/sealed/mecha/working - internal_damage_threshold = 60 allow_diagonal_movement = TRUE /// Handles an internal ore box for working mechs var/obj/structure/ore_box/box @@ -21,7 +20,7 @@ /obj/vehicle/sealed/mecha/working/proc/collect_ore() if(!box) return - if(!(locate(/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp) in equipment) && !(locate(/obj/item/mecha_parts/mecha_equipment/orebox_manager) in equipment)) + if(!(locate(/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp) in flat_equipment) && !(locate(/obj/item/mecha_parts/mecha_equipment/orebox_manager) in equip_by_category[MECHA_UTILITY])) return for(var/obj/item/stack/ore/ore in range(1, src)) if(ore.Adjacent(src) && ((get_dir(src, ore) & dir) || ore.loc == loc)) //we can reach it and it's in front of us? grab it! diff --git a/icons/ui_icons/mecha/armor.dmi b/icons/ui_icons/mecha/armor.dmi new file mode 100644 index 00000000000..665e3f665eb Binary files /dev/null and b/icons/ui_icons/mecha/armor.dmi differ diff --git a/modular_skyrat/modules/firefighter/code/firefighter.dm b/modular_skyrat/modules/firefighter/code/firefighter.dm index c52b583a930..0b355b3d408 100644 --- a/modular_skyrat/modules/firefighter/code/firefighter.dm +++ b/modular_skyrat/modules/firefighter/code/firefighter.dm @@ -16,7 +16,6 @@ /obj/vehicle/sealed/mecha/working/ripley/firefighter/generate_actions() initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_eject) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_internals) - initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_cycle_equip) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_lights) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_view_stats) initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/strafe) @@ -82,12 +81,6 @@ markone.capacitor.forceMove(mkf) markone.capacitor = null mkf.update_part_values() - for(var/obj/item/mecha_parts/equipment in markone.contents) - if(istype(equipment, /obj/item/mecha_parts/concealed_weapon_bay)) //why is the bay not just a variable change who did this - equipment.forceMove(mkf) - for(var/obj/item/mecha_parts/mecha_equipment/equipment in markone.equipment) //Move the equipment over... - equipment.detach(mkf) - equipment.attach(mkf) mkf.dna_lock = markone.dna_lock mkf.mecha_flags = markone.mecha_flags mkf.strafe = markone.strafe diff --git a/tgstation.dme b/tgstation.dme index 50b0087aa23..6060b1485ec 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2277,6 +2277,7 @@ #include "code\modules\asset_cache\assets\language.dm" #include "code\modules\asset_cache\assets\lobby.dm" #include "code\modules\asset_cache\assets\mafia.dm" +#include "code\modules\asset_cache\assets\mecha.dm" #include "code\modules\asset_cache\assets\moods.dm" #include "code\modules\asset_cache\assets\notes.dm" #include "code\modules\asset_cache\assets\orbit.dm" @@ -4233,7 +4234,7 @@ #include "code\modules\vehicles\mecha\mecha_control_console.dm" #include "code\modules\vehicles\mecha\mecha_defense.dm" #include "code\modules\vehicles\mecha\mecha_parts.dm" -#include "code\modules\vehicles\mecha\mecha_topic.dm" +#include "code\modules\vehicles\mecha\mecha_ui.dm" #include "code\modules\vehicles\mecha\mecha_wreckage.dm" #include "code\modules\vehicles\mecha\combat\combat.dm" #include "code\modules\vehicles\mecha\combat\durand.dm" @@ -4247,7 +4248,6 @@ #include "code\modules\vehicles\mecha\equipment\tools\medical_tools.dm" #include "code\modules\vehicles\mecha\equipment\tools\mining_tools.dm" #include "code\modules\vehicles\mecha\equipment\tools\other_tools.dm" -#include "code\modules\vehicles\mecha\equipment\tools\weapon_bay.dm" #include "code\modules\vehicles\mecha\equipment\tools\work_tools.dm" #include "code\modules\vehicles\mecha\equipment\weapons\mecha_ammo.dm" #include "code\modules\vehicles\mecha\equipment\weapons\weapons.dm" diff --git a/tgui/packages/tgui/interfaces/ExosuitControlConsole.js b/tgui/packages/tgui/interfaces/ExosuitControlConsole.js index 108660f86b3..8ca786f8180 100644 --- a/tgui/packages/tgui/interfaces/ExosuitControlConsole.js +++ b/tgui/packages/tgui/interfaces/ExosuitControlConsole.js @@ -86,9 +86,6 @@ export const ExosuitControlConsole = (props, context) => { {mech.location || 'Unknown'} - - {mech.active_equipment || 'None'} - {mech.cargo_space >= 0 && ( { + const { act, data } = useBackend(context); + const { + internal_damage, + internal_damage_keys, + } = data; + return ( + + {Object.keys(internal_damage_keys).map(t => ( + + + + + {(internal_damage & internal_damage_keys[t]) + ?InternalDamageToDamagedDesc[t]:InternalDamageToNormalDesc[t]} + + + + + + + + ))} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Mecha/ArmPane.tsx b/tgui/packages/tgui/interfaces/Mecha/ArmPane.tsx new file mode 100644 index 00000000000..429da85366a --- /dev/null +++ b/tgui/packages/tgui/interfaces/Mecha/ArmPane.tsx @@ -0,0 +1,261 @@ +import { toFixed } from 'common/math'; +import { useBackend } from '../../backend'; +import { Button, LabeledList, Stack, Section, ProgressBar } from '../../components'; +import { MechWeapon, OperatorData } from './data'; + +export const ArmPane=(props:{weapon:MechWeapon}, context) => { + const { act, data } = useBackend(context); + const { + name, + desc, + integrity, + energy_per_use, + } = props.weapon; + const { + power_level, + } = data; + return ( + + + {name} + + + + + + + + + + )} + + {ammo_type} + + + ); + } +}; + +const MECHA_SNOWFLAKE_ID_SLEEPER = "sleeper_snowflake"; +const MECHA_SNOWFLAKE_ID_SYRINGE = "syringe_snowflake"; +const MECHA_SNOWFLAKE_ID_MODE = "mode_snowflake"; +const MECHA_SNOWFLAKE_ID_EXTINGUISHER = "extinguisher_snowflake"; + +// Handles all the snowflake buttons and whatever +const Snowflake = (props: {weapon: MechWeapon}, context) => { + const { + snowflake, + } = props.weapon; + switch (snowflake["snowflake_id"]) { + case MECHA_SNOWFLAKE_ID_SLEEPER: + return ; + case MECHA_SNOWFLAKE_ID_SYRINGE: + return ; + case MECHA_SNOWFLAKE_ID_EXTINGUISHER: + return ; + case MECHA_SNOWFLAKE_ID_MODE: + return ; + default: + return null; + } +}; + +const SnowflakeSleeper = (props: {weapon: MechWeapon}, context) => { + const { act, data } = useBackend(context); + const { + patient, + } = props.weapon.snowflake; + if (!patient) { + return null; + } else { + return ( +
+ + + {patient.isdead ? "DECEASED" : "ALIVE"} + + + + + +
+ ); + } +}; + +const SnowflakeSyringe = (props: {weapon: MechWeapon}, context) => { + const { act, data } = useBackend(context); + const { + mode, + syringe, + max_syringe, + reagents, + total_reagents, + } = props.weapon.snowflake; + return ( +
+ + + {syringe}/{max_syringe} + + + {reagents}/{total_reagents} + + +
+ ); +}; + +const SnowflakeExtinguisher = (props: {weapon: MechWeapon}, context) => { + const { + reagents, + total_reagents, + } = props.weapon.snowflake; + return ( +
+ + + {reagents}/{total_reagents} + + +
+ ); +}; + +const SnowflakeMode = (props: {weapon: MechWeapon}, context) => { + const { act, data } = useBackend(context); + const { + mode, + name, + } = props.weapon.snowflake; + return ( +
+ + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Mecha/ArmorPane.tsx b/tgui/packages/tgui/interfaces/Mecha/ArmorPane.tsx new file mode 100644 index 00000000000..d963b656c88 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Mecha/ArmorPane.tsx @@ -0,0 +1,40 @@ +import { useBackend } from '../../backend'; +import { Stack, Button, Box } from '../../components'; +import { OperatorData } from './data'; +import { classes } from 'common/react'; + +export const ArmorPane = (props, context) => { + const { act, data } = useBackend(context); + const { + mech_equipment, + } = data; + return ( + + {mech_equipment["armor"].map((module, i) => ( + + + + {module.protect_name} + + + + + + + )} /> + + +
+ + + + + + + + +
+
+ +
+ + + + + + cabin_dangerous_highpressure?"red":null}> + {cabin_pressure} kPa + + + + + {GetTempFormat(cabin_temp)} + + + + +
+
+ +
+ +
+
+ +
+ + + + + + + + + + + +
+
+
+ ); +}; + +const GetTempFormat = (temp) => { + return toFixed(temp, 1) +"°K\n"+ toFixed(temp-KelvinZeroCelcius, 1) + "°C"; +}; + + +const EnviromentalAir = (props, context) => { + const { act, data } = useBackend(context); + const { + airtank_pressure, + airtank_temp, + } = data; + if (airtank_temp === null) { + return (No air tank detected); + } else { + return ( + <> + + {airtank_pressure} kPa + + + {GetTempFormat(airtank_temp)} + + + ); + } +}; + +const DNABody = (props, context) => { + const { act, data } = useBackend(context); + const { + dna_lock, + } = data; + if (dna_lock === null) { + return ( + + + + + + ); + } else { + return ( + + + + + + + + + + + + ); + } +}; + +const PowerBar = (props, context) => { + const { act, data } = useBackend(context); + const { + power_level, + power_max, + } = data; + if (power_max === null) { + return (); + } else { + return ( + + ); + } +}; diff --git a/tgui/packages/tgui/interfaces/Mecha/OperatorMode.tsx b/tgui/packages/tgui/interfaces/Mecha/OperatorMode.tsx new file mode 100644 index 00000000000..2bb5a0b164a --- /dev/null +++ b/tgui/packages/tgui/interfaces/Mecha/OperatorMode.tsx @@ -0,0 +1,89 @@ +import { useBackend } from '../../backend'; +import { ByondUi, Stack, Section } from '../../components'; +import { OperatorData } from './data'; +import { RadioPane } from './RadioPane'; +import { AlertPane } from './AlertPane'; +import { MechStatPane } from './MechStatPane'; +import { ArmorPane } from './ArmorPane'; +import { UtilityModulesPane } from './UtilityModulesPane'; +import { PowerModulesPane } from './PowerModulesPane'; +import { ArmPane } from './ArmPane'; + +export const OperatorMode = (props, context) => { + const { act, data } = useBackend(context); + const { + left_arm_weapon, + right_arm_weapon, + mech_view, + } = data; + return ( + + + + +
+ {left_arm_weapon ? : null} +
+
+ +
+ +
+
+ +
+ +
+
+
+
+ + + + + + +
+ +
+
+
+
+ + + +
+ {right_arm_weapon ? : null} +
+
+ +
+ +
+
+ +
+ +
+
+
+
+ + + +
+ +
+
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Mecha/PowerModulesPane.tsx b/tgui/packages/tgui/interfaces/Mecha/PowerModulesPane.tsx new file mode 100644 index 00000000000..8e4181dc6c7 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Mecha/PowerModulesPane.tsx @@ -0,0 +1,33 @@ +import { useBackend } from '../../backend'; +import { Button, LabeledList } from '../../components'; +import { OperatorData } from './data'; +import { toFixed } from 'common/math'; + +export const PowerModulesPane = (props, context) => { + const { act, data } = useBackend(context); + const { + mech_equipment, + mineral_material_amount, + } = data; + return ( + + {mech_equipment["power"].map((module, i) => ( + + + + + + + + toFixed(value, 1)} + width="80px" + onDrag={(e, value) => act('set_frequency', { + new_frequency: value, + })} /> + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Mecha/UtilityModulesPane.tsx b/tgui/packages/tgui/interfaces/Mecha/UtilityModulesPane.tsx new file mode 100644 index 00000000000..8a3451cbb04 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Mecha/UtilityModulesPane.tsx @@ -0,0 +1,74 @@ +import { useBackend } from '../../backend'; +import { Button, LabeledList } from '../../components'; +import { OperatorData, MechaUtility } from './data'; + +export const UtilityModulesPane = (props, context) => { + const { act, data } = useBackend(context); + const { + mech_equipment, + } = data; + return ( + + {mech_equipment["utility"].map((module, i) => ( + + {module.snowflake.snowflake_id ? ( + ) : ( + <> + + + ))} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Mecha/data.ts b/tgui/packages/tgui/interfaces/Mecha/data.ts new file mode 100644 index 00000000000..fb47ded5013 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Mecha/data.ts @@ -0,0 +1,93 @@ +export const KelvinZeroCelcius = 273.15; + +export const InternalDamageToDamagedDesc = { + "MECHA_INT_FIRE": "Internal fire detected", + "MECHA_INT_TEMP_CONTROL": "Temperature control inactive", + "MECHA_INT_TANK_BREACH": "Air tank breach detected", + "MECHA_INT_CONTROL_LOST": "Control module damaged", +}; + +export const InternalDamageToNormalDesc = { + "MECHA_INT_FIRE": "No internal fires detected", + "MECHA_INT_TEMP_CONTROL": "Temperature control active", + "MECHA_INT_TANK_BREACH": "Air tank intact", + "MECHA_INT_CONTROL_LOST": "Control module active", +}; + +export type AccessData = { + name: string; + number: number; +} + +type MechElectronics = { + microphone: boolean; + speaker: boolean; + frequency: number; + minfreq: number; + maxfreq: number; +} + +export type MechWeapon = { + name: string; + desc: string; + ref: string; + isballisticweapon: boolean; + integrity: number; + energy_per_use: number; + // null when not ballistic weapon + disabledreload: boolean | null; + projectiles: number | null; + max_magazine: number | null; + projectiles_cache: number | null; + projectiles_cache_max: number | null; + ammo_type: string | null; + // first entry is always "snowflake_id"=snowflake_id if snowflake + snowflake: any; +} + +export type MainData = { + isoperator: boolean; +}; + +export type MaintData = { + name: string; + mecha_flags: number; + mechflag_keys: string[]; + internal_tank_valve: number; + cell: string; + scanning: string; + capacitor: string; + operation_req_access: AccessData[]; + idcard_access: AccessData[]; +}; + +export type OperatorData = { + name: string; + integrity: number; + power_level: number | null; + power_max: number | null; + mecha_flags: number; + internal_damage: number; + internal_damage_keys: string[]; + air_source: string; + mechflag_keys: string[]; + cabin_dangerous_highpressure: number; + airtank_pressure: number | null; + airtank_temp: number | null; + port_connected: boolean | null; + cabin_pressure: number; + cabin_temp: number; + dna_lock: string | null; + mech_electronics: MechElectronics; + right_arm_weapon: MechWeapon | null; + left_arm_weapon: MechWeapon | null; + mech_equipment: string[]; + mech_view: string; + mineral_material_amount: number; +}; + +export type MechaUtility = { + name: string; + ref: string; + snowflake: any; +} diff --git a/tgui/packages/tgui/interfaces/Mecha/index.tsx b/tgui/packages/tgui/interfaces/Mecha/index.tsx new file mode 100644 index 00000000000..fdd8e9c9f11 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Mecha/index.tsx @@ -0,0 +1,25 @@ +import { useBackend } from '../../backend'; +import { Window } from '../../layouts'; +import { MainData } from './data'; +import { MaintMode } from './MaintMode'; +import { OperatorMode } from './OperatorMode'; + +export const Mecha = (props, context) => { + const { data } = useBackend(context); + if (data.isoperator) { + return ( + + + + + + ); + } + return ( + + + + + + ); +}; diff --git a/tgui/packages/tgui/styles/interfaces/Mecha.scss b/tgui/packages/tgui/styles/interfaces/Mecha.scss new file mode 100644 index 00000000000..ba910f392fb --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/Mecha.scss @@ -0,0 +1,43 @@ +@use '../base.scss'; +@use '../colors.scss'; +@use '../functions.scss' as *; + +$color-danger: #c92020 !default; + +.Mecha__displayBox { + background-color: #002003; + border: 0.167em inset #E8E4C9; + color: #03e017; + font-size: 2em; + font-family: monospace; + padding: 0.25em; +} + +.Mecha__displayBoxRed { + background-color: #002003; + border: 0.167em inset #E8E4C9; + color: #f54b4b; + font-size: 2em; + font-family: monospace; + padding: 0.25em; +} + +.Mecha__Button { + outline-width: 0.25rem !important; + border-width: 0.65rem !important; + padding-left: 0 !important; + padding-right: 0 !important; +} + +.Mecha__ButtonDanger { + background-color: $color-danger !important; + border-color: $color-danger !important; + outline-width: 0.25rem !important; + border-width: 0.65rem !important; + padding-left: 0 !important; + padding-right: 0 !important; + &:hover { + background-color: lighten($color-danger, 15%) !important; + border-color: lighten($color-danger, 15%) !important; + } +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 6e564433e7f..8e0afb9f8bf 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -56,6 +56,7 @@ @include meta.load-css('./interfaces/HellishRunes.scss'); @include meta.load-css('./interfaces/Hypertorus.scss'); @include meta.load-css('./interfaces/NuclearBomb.scss'); +@include meta.load-css('./interfaces/Mecha.scss'); @include meta.load-css('./interfaces/Paper.scss'); @include meta.load-css('./interfaces/PreferencesMenu.scss'); @include meta.load-css('./interfaces/Roulette.scss');