diff --git a/code/__DEFINES/dcs/signals/atom/signals_obj.dm b/code/__DEFINES/dcs/signals/atom/signals_obj.dm index c870a55ed746..50a08e418fa8 100644 --- a/code/__DEFINES/dcs/signals/atom/signals_obj.dm +++ b/code/__DEFINES/dcs/signals/atom/signals_obj.dm @@ -30,6 +30,14 @@ /// from /obj/proc/afterbuckle() #define COSMIG_OBJ_AFTER_BUCKLE "signal_obj_after_buckle" +/// from /datum/cm_objective/retrieve_data/disk/process() +#define COMSIG_INTEL_DISK_LOST_POWER "intel_disk_lost_power" + +/// from /datum/cm_objective/retrieve_data/disk/complete() +#define COMSIG_INTEL_DISK_COMPLETED "intel_disk_completed" + +/// from /obj/vehicle/multitile/arc/toggle_antenna() +#define COMSIG_ARC_ANTENNA_TOGGLED "arc_antenna_toggled" /// from /obj/structure/machinery/cryopod/go_out() #define COMSIG_CRYOPOD_GO_OUT "cryopod_go_out" diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 8d77920a59cc..71fc9162dcaa 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -16,7 +16,8 @@ return FALSE /atom/movable/attackby(obj/item/W, mob/living/user) - if(W) + . = ..() + if(W && !.) if(!(W.flags_item & NOBLUDGEON)) visible_message(SPAN_DANGER("[src] has been hit by [user] with [W]."), null, null, 5, CHAT_TYPE_MELEE_HIT) user.animation_attack_on(src) diff --git a/code/datums/ammo/bullet/arc.dm b/code/datums/ammo/bullet/arc.dm new file mode 100644 index 000000000000..5e74508e04b2 --- /dev/null +++ b/code/datums/ammo/bullet/arc.dm @@ -0,0 +1,14 @@ +/datum/ammo/bullet/re700 + name = "rotary cannon bullet" + icon_state = "autocannon" + damage_falloff = 0 + flags_ammo_behavior = AMMO_BALLISTIC + + accuracy = HIT_ACCURACY_TIER_7 + scatter = 0 + damage = 30 + damage_var_high = PROJECTILE_VARIANCE_TIER_8 + penetration = ARMOR_PENETRATION_TIER_2 + accurate_range = 10 + max_range = 12 + shell_speed = AMMO_SPEED_TIER_6 diff --git a/code/datums/components/disk_reader.dm b/code/datums/components/disk_reader.dm new file mode 100644 index 000000000000..2b74ec35423b --- /dev/null +++ b/code/datums/components/disk_reader.dm @@ -0,0 +1,87 @@ +/datum/component/disk_reader + dupe_mode = COMPONENT_DUPE_UNIQUE + /// Ref to the inserted disk + var/obj/item/disk/objective/disk + +/datum/component/disk_reader/Initialize() + . = ..() + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + +/datum/component/disk_reader/Destroy(force, silent) + handle_qdel() + return ..() + +/datum/component/disk_reader/RegisterWithParent() + ..() + RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, PROC_REF(on_disk_insert)) + RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(handle_qdel)) + RegisterSignal(parent, COMSIG_INTEL_DISK_COMPLETED, PROC_REF(on_disk_complete)) + RegisterSignal(parent, COMSIG_INTEL_DISK_LOST_POWER, PROC_REF(on_power_lost)) + +/datum/component/disk_reader/UnregisterFromParent() + ..() + handle_qdel() + +/datum/component/disk_reader/proc/handle_qdel() + SIGNAL_HANDLER + QDEL_NULL(disk) + +/datum/component/disk_reader/proc/on_disk_insert(datum/source, obj/item/disk/objective/potential_disk, mob/living/inserter, params) + SIGNAL_HANDLER + + if(!istype(potential_disk) || !potential_disk.objective) + return + + if(disk) + to_chat(inserter, SPAN_WARNING("There's already a disk inside [parent], wait for it to finish first!")) + return COMPONENT_NO_AFTERATTACK + + if(potential_disk.objective.state == OBJECTIVE_COMPLETE) + to_chat(inserter, SPAN_WARNING("The reader displays a message stating this disk has already been read and refuses to accept it.")) + return COMPONENT_NO_AFTERATTACK + + INVOKE_ASYNC(src, PROC_REF(handle_disk_insert), potential_disk, inserter) + return COMPONENT_NO_AFTERATTACK + +/datum/component/disk_reader/proc/handle_disk_insert(obj/item/disk/objective/potential_disk, mob/living/inserter) + if(tgui_input_text(inserter, "Enter the encryption key", "Decrypting [potential_disk]", "") != potential_disk.objective.decryption_password) + to_chat(inserter, SPAN_WARNING("The reader buzzes, ejecting the disk.")) + return + + if(disk) + to_chat(inserter, SPAN_WARNING("There's already a disk inside [parent], wait for it to finish first!")) + return + + if(!(potential_disk in inserter.contents)) + return + + potential_disk.objective.activate() + + inserter.drop_inv_item_to_loc(potential_disk, parent) + disk = potential_disk + to_chat(inserter, SPAN_NOTICE("You insert [potential_disk] and enter the decryption key.")) + inserter.count_niche_stat(STATISTICS_NICHE_DISK) + +/datum/component/disk_reader/proc/on_disk_complete(datum/source) + SIGNAL_HANDLER + var/atom/atom_parent = parent + + atom_parent.visible_message("[atom_parent] pings softly as the upload finishes and ejects [disk].") + playsound(atom_parent, 'sound/machines/screen_output1.ogg', 25, 1) + disk.forceMove(get_turf(atom_parent)) + disk.name = "[disk.name] (complete)" + disk.objective.award_points() + disk.retrieve_objective.state = OBJECTIVE_ACTIVE + disk.retrieve_objective.activate() + disk = null + +/datum/component/disk_reader/proc/on_power_lost(datum/source) + SIGNAL_HANDLER + var/atom/atom_parent = parent + + atom_parent.visible_message(SPAN_WARNING("\The [atom_parent] powers down mid-operation as the area loses power.")) + playsound(atom_parent, 'sound/machines/terminal_shutdown.ogg', 25, 1) + SSobjectives.stop_processing_objective(src) + disk.forceMove(get_turf(atom_parent)) + disk = null diff --git a/code/datums/elements/bullet_trait/iff.dm b/code/datums/elements/bullet_trait/iff.dm index ab48b29f4812..cee36acbed80 100644 --- a/code/datums/elements/bullet_trait/iff.dm +++ b/code/datums/elements/bullet_trait/iff.dm @@ -46,7 +46,7 @@ // The cache is reset when the user drops their ID /datum/element/bullet_trait_iff/proc/get_user_iff_group(mob/living/carbon/human/user) if(!ishuman(user)) - return user.faction_group + return user?.faction_group var/iff_group = LAZYACCESS(iff_group_cache, user) if(isnull(iff_group)) diff --git a/code/datums/skills/uscm.dm b/code/datums/skills/uscm.dm index 8a6d2fd2c8c2..323a7270f33b 100644 --- a/code/datums/skills/uscm.dm +++ b/code/datums/skills/uscm.dm @@ -239,7 +239,8 @@ COMMAND STAFF SKILL_JTAC = SKILL_JTAC_MASTER, SKILL_SPEC_WEAPONS = SKILL_SPEC_ALL, SKILL_EXECUTION = SKILL_EXECUTION_TRAINED, //can BE people - SKILL_INTEL = SKILL_INTEL_EXPERT + SKILL_INTEL = SKILL_INTEL_EXPERT, + SKILL_VEHICLE = SKILL_VEHICLE_LARGE, ) /datum/skills/commander @@ -261,7 +262,8 @@ COMMAND STAFF SKILL_JTAC = SKILL_JTAC_MASTER, SKILL_EXECUTION = SKILL_EXECUTION_TRAINED, //can BE people SKILL_INTEL = SKILL_INTEL_EXPERT, - SKILL_NAVIGATIONS = SKILL_NAVIGATIONS_TRAINED //can change ship alt + SKILL_NAVIGATIONS = SKILL_NAVIGATIONS_TRAINED, //can change ship alt + SKILL_VEHICLE = SKILL_VEHICLE_LARGE, ) /datum/skills/XO @@ -282,6 +284,7 @@ COMMAND STAFF SKILL_JTAC = SKILL_JTAC_MASTER, SKILL_INTEL = SKILL_INTEL_EXPERT, SKILL_NAVIGATIONS = SKILL_NAVIGATIONS_TRAINED, + SKILL_VEHICLE = SKILL_VEHICLE_LARGE, ) /datum/skills/SO @@ -298,6 +301,7 @@ COMMAND STAFF SKILL_POWERLOADER = SKILL_POWERLOADER_TRAINED, SKILL_JTAC = SKILL_JTAC_EXPERT, SKILL_INTEL = SKILL_INTEL_TRAINED, + SKILL_VEHICLE = SKILL_VEHICLE_LARGE, ) /datum/skills/SEA diff --git a/code/datums/supply_packs/vehicle_ammo.dm b/code/datums/supply_packs/vehicle_ammo.dm index 5dad91d27ed4..43ce36ec2b64 100644 --- a/code/datums/supply_packs/vehicle_ammo.dm +++ b/code/datums/supply_packs/vehicle_ammo.dm @@ -148,3 +148,15 @@ containertype = /obj/structure/closet/crate/ammo containername = "M-87F Flare Launcher ammo crate" group = "Vehicle Ammo" + +/datum/supply_packs/ammo_arcsentry + name = "RE700 Rotary Cannon magazines (x3)" + contains = list( + /obj/item/ammo_magazine/hardpoint/arc_sentry, + /obj/item/ammo_magazine/hardpoint/arc_sentry, + /obj/item/ammo_magazine/hardpoint/arc_sentry, + ) + cost = 20 + containertype = /obj/structure/closet/crate/ammo + containername = "RE700 Rotary Cannon ammo crate" + group = "Vehicle Ammo" diff --git a/code/datums/supply_packs/vehicle_equipment.dm b/code/datums/supply_packs/vehicle_equipment.dm new file mode 100644 index 000000000000..df106761d467 --- /dev/null +++ b/code/datums/supply_packs/vehicle_equipment.dm @@ -0,0 +1,9 @@ +/datum/supply_packs/arcsentry_replacement + name = "Replacement RE700 Rotary Cannon (x1)" + contains = list( + /obj/item/hardpoint/primary/arc_sentry, + ) + cost = 25 + containertype = /obj/structure/closet/crate/weapon + containername = "RE700 Rotary Cannon crate" + group = "Vehicle Equipment" diff --git a/code/datums/vehicles.dm b/code/datums/vehicles.dm index 36ac96938c6b..67070dd04c0b 100644 --- a/code/datums/vehicles.dm +++ b/code/datums/vehicles.dm @@ -37,3 +37,7 @@ /datum/map_template/interior/van name = "Van" interior_id = "van" + +/datum/map_template/interior/arc + name = "ARC" + interior_id = "arc" diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 21f7b6b0a9be..52a35b715b1a 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -45,7 +45,10 @@ if(orbiting) orbiting.end_orbit(src) orbiting = null - vis_contents.Cut() + + vis_locs = null //clears this atom out of all viscontents + if(length(vis_contents)) + vis_contents.Cut() . = ..() moveToNullspace() //so we move into null space. Must be after ..() b/c atom's Dispose handles deleting our lighting stuff diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index c33517796271..f6efe6edb5e2 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -126,7 +126,7 @@ src.attack_alien(user) return src.attack_hand(user) - return + return ..() /obj/structure/machinery/computer/attack_hand() . = ..() diff --git a/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm b/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm index 6877c2b4b5b3..0586f4b72fa5 100644 --- a/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm +++ b/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm @@ -76,6 +76,9 @@ else display_list = GLOB.cm_vending_vehicle_crew_tank_spare + else if(selected_vehicle == "ARC") + display_list = GLOB.cm_vending_vehicle_crew_arc + else if(selected_vehicle == "APC") if(available_categories) display_list = GLOB.cm_vending_vehicle_crew_apc @@ -245,6 +248,11 @@ GLOBAL_LIST_INIT(cm_vending_vehicle_crew_apc_spare, list( list("WHEELS", 0, null, null, null), list("APC Wheels", 200, /obj/item/hardpoint/locomotion/apc_wheels, null, VENDOR_ITEM_REGULAR))) +GLOBAL_LIST_INIT(cm_vending_vehicle_crew_arc, list( + list("STARTING KIT SELECTION:", 0, null, null, null), + + list("WHEELS", 0, null, null, null), + list("Replacement ARC Wheels", 0, /obj/item/hardpoint/locomotion/arc_wheels, VEHICLE_TREADS_AVAILABLE, VENDOR_ITEM_MANDATORY))) //------------WEAPONS RACK--------------- diff --git a/code/game/supplyshuttle.dm b/code/game/supplyshuttle.dm index a059d080e8ee..af746ceb20a2 100644 --- a/code/game/supplyshuttle.dm +++ b/code/game/supplyshuttle.dm @@ -410,6 +410,7 @@ GLOBAL_DATUM_INIT(supply_controller, /datum/controller/supply, new()) "Operations", "Weapons", "Vehicle Ammo", + "Vehicle Equipment", "Attachments", "Ammo", "Weapons Specialist Ammo", @@ -1365,6 +1366,13 @@ GLOBAL_DATUM_INIT(supply_controller, /datum/controller/supply, new()) name = "Barebones M577 Armored Personal Carrier" ordered_vehicle = /obj/effect/vehicle_spawner/apc/unarmed/broken +/datum/vehicle_order/arc + name = "M540 Armored Recon Carrier" + ordered_vehicle = /obj/effect/vehicle_spawner/arc + +/datum/vehicle_order/arc/has_vehicle_lock() + return + /obj/structure/machinery/computer/supplycomp/vehicle/Initialize() . = ..() @@ -1401,12 +1409,13 @@ GLOBAL_DATUM_INIT(supply_controller, /datum/controller/supply, new()) if(!SSshuttle.vehicle_elevator) return - dat += "Platform position: " - if (SSshuttle.vehicle_elevator.timeLeft()) + dat += "Platform position:
" + if (SSshuttle.vehicle_elevator.mode != SHUTTLE_IDLE) dat += "Moving" else if(is_mainship_level(SSshuttle.vehicle_elevator.z)) - dat += "Raised" + dat += "Raised
" + dat += "\[Lower\]" else dat += "Lowered" dat += "

" @@ -1442,15 +1451,11 @@ GLOBAL_DATUM_INIT(supply_controller, /datum/controller/supply, new()) world.log << "## ERROR: Eek. The supply/elevator datum is missing somehow." return - if(!should_block_game_interaction(SSshuttle.vehicle_elevator)) - to_chat(usr, SPAN_WARNING("The elevator needs to be in the cargo bay dock to call a vehicle up. Ask someone to send it away.")) - return - if(isturf(loc) && ( in_range(src, usr) || isSilicon(usr) ) ) usr.set_interaction(src) if(href_list["get_vehicle"]) - if(is_mainship_level(SSshuttle.vehicle_elevator.z)) + if(is_mainship_level(SSshuttle.vehicle_elevator.z) || SSshuttle.vehicle_elevator.mode != SHUTTLE_IDLE) return // dunno why the +1 is needed but the vehicles spawn off-center var/turf/middle_turf = get_turf(SSshuttle.vehicle_elevator) @@ -1458,9 +1463,11 @@ GLOBAL_DATUM_INIT(supply_controller, /datum/controller/supply, new()) var/obj/vehicle/multitile/ordered_vehicle var/datum/vehicle_order/VO = locate(href_list["get_vehicle"]) + if(!(VO in vehicles)) + return - if(!VO) return - if(VO.has_vehicle_lock()) return + if(VO?.has_vehicle_lock()) + return spent = TRUE ordered_vehicle = new VO.ordered_vehicle(middle_turf) @@ -1470,5 +1477,11 @@ GLOBAL_DATUM_INIT(supply_controller, /datum/controller/supply, new()) SEND_GLOBAL_SIGNAL(COMSIG_GLOB_VEHICLE_ORDERED, ordered_vehicle) + else if(href_list["lower_elevator"]) + if(!is_mainship_level(SSshuttle.vehicle_elevator.z)) + return + + SSshuttle.vehicle_elevator.request(SSshuttle.getDock("adminlevel vehicle")) + add_fingerprint(usr) updateUsrDialog() diff --git a/code/modules/almayer/machinery.dm b/code/modules/almayer/machinery.dm index 74ce9a81eb88..9411c229d2f3 100644 --- a/code/modules/almayer/machinery.dm +++ b/code/modules/almayer/machinery.dm @@ -73,7 +73,7 @@ /obj/structure/machinery/prop/almayer/CICmap name = "map table" - desc = "A table that displays a map of the current target location" + desc = "A table that displays a map of the current operation location." icon = 'icons/obj/structures/machinery/computer.dmi' icon_state = "maptable" anchored = TRUE @@ -103,6 +103,11 @@ map.tgui_interact(user) +/obj/structure/machinery/prop/almayer/CICmap/computer + name = "map terminal" + desc = "A terminal that displays a map of the current operation location." + icon_state = "security" + /obj/structure/machinery/prop/almayer/CICmap/upp minimap_type = MINIMAP_FLAG_UPP faction = FACTION_UPP diff --git a/code/modules/cm_marines/overwatch.dm b/code/modules/cm_marines/overwatch.dm index e68ef467faa9..0fbfca556498 100644 --- a/code/modules/cm_marines/overwatch.dm +++ b/code/modules/cm_marines/overwatch.dm @@ -865,6 +865,9 @@ /obj/structure/machinery/computer/overwatch/almayer/broken name = "Broken Overwatch Console" +/obj/structure/machinery/computer/overwatch/almayer/small + icon_state = "engineering_terminal" + /obj/structure/machinery/computer/overwatch/clf faction = FACTION_CLF /obj/structure/machinery/computer/overwatch/upp diff --git a/code/modules/cm_tech/techs/marine/tier1/arc.dm b/code/modules/cm_tech/techs/marine/tier1/arc.dm new file mode 100644 index 000000000000..1ea5a0afb6c1 --- /dev/null +++ b/code/modules/cm_tech/techs/marine/tier1/arc.dm @@ -0,0 +1,40 @@ +/datum/tech/arc + name = "M540 Armored Recon Carrier" + desc = "Purchase an M540 Armored Recon Carrier, specialized in assisting groundside command. Able to be driven by Staff Officers, Executive Officers, and Commanding Officers." + icon_state = "upgrade" + + required_points = 5 + + tier = /datum/tier/one + + announce_name = "M540 ARC ACQUIRED" + announce_message = "An M540 Armored Recon Carrier has been authorized and will be delivered in the vehicle bay." + + flags = TREE_FLAG_MARINE + +/datum/tech/arc/on_unlock() + . = ..() + + var/obj/structure/machinery/computer/supplycomp/vehicle/comp = GLOB.VehicleElevatorConsole + var/obj/structure/machinery/cm_vending/gear/vehicle_crew/gearcomp = GLOB.VehicleGearConsole + + if(!comp || !gearcomp) + return FALSE + + comp.spent = FALSE + QDEL_NULL_LIST(comp.vehicles) + comp.vehicles = list( + new /datum/vehicle_order/arc() + ) + comp.allowed_roles = list(JOB_SYNTH, JOB_SEA, JOB_SO, JOB_XO, JOB_CO, JOB_GENERAL) + comp.req_access = list(ACCESS_MARINE_COMMAND) + comp.req_one_access = list() + comp.spent = FALSE + + gearcomp.req_access = list(ACCESS_MARINE_COMMAND) + gearcomp.req_one_access = list() + gearcomp.vendor_role = list() + gearcomp.selected_vehicle = "ARC" + gearcomp.available_categories = VEHICLE_ALL_AVAILABLE + + return TRUE diff --git a/code/modules/mob/living/carbon/human/human_abilities.dm b/code/modules/mob/living/carbon/human/human_abilities.dm index a568e93df5c0..2d7f472952cc 100644 --- a/code/modules/mob/living/carbon/human/human_abilities.dm +++ b/code/modules/mob/living/carbon/human/human_abilities.dm @@ -605,3 +605,26 @@ CULT var/mob/living/carbon/human/human_user = owner SEND_SIGNAL(human_user, COMSIG_MOB_MG_EXIT) + +/datum/action/human_action/toggle_arc_antenna + name = "Toggle Sensor Antenna" + action_icon_state = "recoil_compensation" + +/datum/action/human_action/toggle_arc_antenna/give_to(mob/user) + . = ..() + RegisterSignal(user, COMSIG_MOB_RESET_VIEW, PROC_REF(remove_from)) + +/datum/action/human_action/toggle_arc_antenna/remove_from(mob/user) + . = ..() + UnregisterSignal(user, COMSIG_MOB_RESET_VIEW) + +/datum/action/human_action/toggle_arc_antenna/action_activate() + if(!can_use_action()) + return + + var/mob/living/carbon/human/human_user = owner + if(istype(human_user.buckled, /obj/structure/bed/chair/comfy/vehicle)) + var/obj/structure/bed/chair/comfy/vehicle/vehicle_chair = human_user.buckled + if(istype(vehicle_chair.vehicle, /obj/vehicle/multitile/arc)) + var/obj/vehicle/multitile/arc/vehicle = vehicle_chair.vehicle + vehicle.toggle_antenna(human_user) diff --git a/code/modules/objectives/data_retrieval.dm b/code/modules/objectives/data_retrieval.dm index f66c578f48fb..ee5053f26c4b 100644 --- a/code/modules/objectives/data_retrieval.dm +++ b/code/modules/objectives/data_retrieval.dm @@ -117,29 +117,14 @@ /datum/cm_objective/retrieve_data/disk/process() var/obj/structure/machinery/computer/disk_reader/reader = disk.loc if(!reader.powered()) - reader.visible_message(SPAN_WARNING("\The [reader] powers down mid-operation as the area looses power.")) - playsound(reader, 'sound/machines/terminal_shutdown.ogg', 25, 1) - SSobjectives.stop_processing_objective(src) - disk.forceMove(reader.loc) - reader.disk = null + SEND_SIGNAL(reader, COMSIG_INTEL_DISK_LOST_POWER) return ..() /datum/cm_objective/retrieve_data/disk/complete() state = OBJECTIVE_COMPLETE - var/obj/structure/machinery/computer/disk_reader/reader = disk.loc - reader.visible_message("\The [reader] pings softly as the upload finishes and ejects the disk.") - playsound(reader, 'sound/machines/screen_output1.ogg', 25, 1) - disk.forceMove(reader.loc) - disk.name = "[disk.name] (complete)" - reader.disk = null - award_points() - - // Now enable the objective to store this disk in the lab. - disk.retrieve_objective.state = OBJECTIVE_ACTIVE - disk.retrieve_objective.activate() - + SEND_SIGNAL(disk.loc, COMSIG_INTEL_DISK_COMPLETED) ..() /datum/cm_objective/retrieve_data/disk/get_tgui_data() @@ -295,34 +280,6 @@ unslashable = TRUE unacidable = TRUE -/obj/structure/machinery/computer/disk_reader/attack_hand(mob/living/user) - if(isxeno(user)) - return - if(disk) - to_chat(user, SPAN_NOTICE("[disk] is currently being uploaded to ARES.")) - -/obj/structure/machinery/computer/disk_reader/attackby(obj/item/W, mob/living/user) - if(istype(W, /obj/item/disk/objective)) - if(istype(disk)) - to_chat(user, SPAN_WARNING("There is a disk in the drive being uploaded already!")) - return FALSE - var/obj/item/disk/objective/newdisk = W - if(newdisk.objective.state == OBJECTIVE_COMPLETE) - to_chat(user, SPAN_WARNING("The reader displays a message stating this disk has already been read and refuses to accept it.")) - return FALSE - if(input(user,"Enter the encryption key","Decrypting [newdisk]","") != newdisk.objective.decryption_password) - to_chat(user, SPAN_WARNING("The reader asks for the encryption key for this disk, not having the correct key you eject the disk.")) - return FALSE - if(istype(disk)) - to_chat(user, SPAN_WARNING("There is a disk in the drive being uploaded already!")) - return FALSE - - if(!(newdisk in user.contents)) - return FALSE - - newdisk.objective.activate() - - user.drop_inv_item_to_loc(W, src) - disk = W - to_chat(user, SPAN_NOTICE("You insert \the [W] and enter the decryption key.")) - user.count_niche_stat(STATISTICS_NICHE_DISK) +/obj/structure/machinery/computer/disk_reader/Initialize() + . = ..() + AddComponent(/datum/component/disk_reader) diff --git a/code/modules/objectives/objective_memory_storage.dm b/code/modules/objectives/objective_memory_storage.dm index 161c78d4d1ba..de2ab30691cc 100644 --- a/code/modules/objectives/objective_memory_storage.dm +++ b/code/modules/objectives/objective_memory_storage.dm @@ -218,6 +218,15 @@ GLOBAL_DATUM_INIT(intel_system, /datum/intel_system, new()) GLOB.intel_system.store_single_objective(O) return 1 +/obj/structure/machinery/computer/intel/disk_reader // ARC computer to save on tile space + name = "\improper SIGINT terminal" + desc = "An USCM computer capable of uploading data to the intelligence database. It has a disk reader slot built into the bottom, as well." + icon_state = "terminal" + +/obj/structure/machinery/computer/intel/disk_reader/Initialize() + . = ..() + AddComponent(/datum/component/disk_reader) + // -------------------------------------------- // *** View objectives with the computer *** // -------------------------------------------- diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 3ed10129f4d6..8b189266c9cb 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -341,14 +341,7 @@ SEND_SIGNAL(src, COMSIG_BULLET_TERMINAL) // Check we can reach the turf at all based on pathed grid - var/proj_dir = get_dir(current_turf, next_turf) - if((proj_dir & (proj_dir - 1)) && !current_turf.Adjacent(next_turf)) - ammo.on_hit_turf(current_turf, src) - current_turf.bullet_act(src) - return TRUE - - // Check for hits that would occur when moving to turf, such as a blocking cade - if(scan_a_turf(next_turf, proj_dir)) + if(check_canhit(current_turf, next_turf)) return TRUE // Actually move @@ -516,7 +509,8 @@ else direct_hit = TRUE - SEND_SIGNAL(firer, COMSIG_BULLET_DIRECT_HIT, L) + if(firer) + SEND_SIGNAL(firer, COMSIG_BULLET_DIRECT_HIT, L) // At present, Xenos have no inherent effects or localized damage stemming from limb targeting // Therefore we exempt the shooter from direct hit accuracy penalties as well, @@ -583,6 +577,19 @@ if(SEND_SIGNAL(src, COMSIG_BULLET_POST_HANDLE_MOB, L, .) & COMPONENT_BULLET_PASS_THROUGH) return FALSE +/obj/projectile/proc/check_canhit(turf/current_turf, turf/next_turf) + var/proj_dir = get_dir(current_turf, next_turf) + if((proj_dir & (proj_dir - 1)) && !current_turf.Adjacent(next_turf)) + ammo.on_hit_turf(current_turf, src) + current_turf.bullet_act(src) + return TRUE + + // Check for hits that would occur when moving to turf, such as a blocking cade + if(scan_a_turf(next_turf, proj_dir)) + return TRUE + + return FALSE + //---------------------------------------------------------- // \\ // HITTING THE TARGET \\ diff --git a/code/modules/vehicles/apc/apc_command.dm b/code/modules/vehicles/apc/apc_command.dm index e0862ae4f2ab..54647279ec3b 100644 --- a/code/modules/vehicles/apc/apc_command.dm +++ b/code/modules/vehicles/apc/apc_command.dm @@ -43,8 +43,6 @@ return ..() /obj/vehicle/multitile/apc/command/process() - . = ..() - var/turf/apc_turf = get_turf(src) if(health == 0 || !visible_in_tacmap || !is_ground_level(apc_turf.z)) return diff --git a/code/modules/vehicles/arc/arc.dm b/code/modules/vehicles/arc/arc.dm new file mode 100644 index 000000000000..c491cc35314b --- /dev/null +++ b/code/modules/vehicles/arc/arc.dm @@ -0,0 +1,263 @@ +/obj/vehicle/multitile/arc + name = "M540-A Armored Recon Carrier" + desc = "An M540-A Armored Recon Carrier. A lightly armored reconnaissance and intelligence vehicle. Entrances on the sides." + + icon = 'icons/obj/vehicles/arc.dmi' + icon_state = "arc_base" + pixel_x = -48 + pixel_y = -48 + + bound_width = 96 + bound_height = 96 + + bound_x = -32 + bound_y = -32 + + health = 800 + + interior_map = /datum/map_template/interior/arc + + passengers_slots = 3 + xenos_slots = 5 + + entrances = list( + "left" = list(2, 0), + "right" = list(-2, 0), + ) + + entrance_speed = 0.5 SECONDS + + required_skill = SKILL_VEHICLE_LARGE + + movement_sound = 'sound/vehicles/tank_driving.ogg' + + luminosity = 7 + + hardpoints_allowed = list( + /obj/item/hardpoint/locomotion/arc_wheels, + /obj/item/hardpoint/primary/arc_sentry, + /obj/item/hardpoint/support/arc_antenna, + ) + + seats = list( + VEHICLE_DRIVER = null, + ) + + active_hp = list( + VEHICLE_DRIVER = null, + ) + + vehicle_flags = VEHICLE_CLASS_LIGHT + + mob_size_required_to_hit = MOB_SIZE_XENO + + dmg_multipliers = list( + "all" = 1, + "acid" = 1.8, + "slash" = 1.1, + "bullet" = 0.6, + "explosive" = 0.8, + "blunt" = 0.8, + "abstract" = 1, + ) + + move_max_momentum = 2.2 + move_momentum_build_factor = 1.5 + move_turn_momentum_loss_factor = 0.8 + + vehicle_ram_multiplier = VEHICLE_TRAMPLE_DAMAGE_APC_REDUCTION + + /// If the ARC has its antenna up, making it unable to move but enabling the turret and sensor wallhack + var/antenna_deployed = FALSE + /// How long it takes to deploy or retract the antenna + var/antenna_toggle_time = 10 SECONDS + /// Range of the ARC's xenomorph wallhacks + var/sensor_radius = 45 + /// weakrefs of xenos temporarily added to the marine minimap + var/list/minimap_added = list() + +/obj/vehicle/multitile/arc/Initialize() + . = ..() + + var/turf/gotten_turf = get_turf(src) + if(gotten_turf?.z) + SSminimaps.add_marker(src, gotten_turf.z, MINIMAP_FLAG_USCM, "arc", 'icons/ui_icons/map_blips_large.dmi') + + RegisterSignal(src, COMSIG_ARC_ANTENNA_TOGGLED, PROC_REF(on_antenna_toggle)) + +/obj/vehicle/multitile/arc/crew_mousedown(datum/source, atom/object, turf/location, control, params) + return + + +/obj/vehicle/multitile/arc/get_examine_text(mob/user) + . = ..() + if(!isxeno(user)) + return + + if(health > 0) + . += SPAN_XENO("[src] can be crawled under once destroyed.") + else + . += SPAN_XENO("[src] can be crawled under by dragging our sprite to it.") + +/obj/vehicle/multitile/arc/proc/on_antenna_toggle(datum/source) + SIGNAL_HANDLER + + if(antenna_deployed) + START_PROCESSING(SSslowobj, src) + + else + STOP_PROCESSING(SSslowobj, src) + +/obj/vehicle/multitile/arc/process() + var/turf/arc_turf = get_turf(src) + if((health <= 0) || !visible_in_tacmap || !is_ground_level(arc_turf.z)) + return + + var/obj/item/hardpoint/support/arc_antenna/antenna = locate() in hardpoints + if(!antenna || (antenna.health <= 0)) + for(var/datum/weakref/xeno as anything in minimap_added) + SSminimaps.remove_marker(xeno.resolve()) + minimap_added.Remove(xeno) + return + + for(var/mob/living/carbon/xenomorph/current_xeno as anything in GLOB.living_xeno_list) + var/turf/xeno_turf = get_turf(current_xeno) + if(!is_ground_level(xeno_turf.z)) + continue + + if(get_dist(src, current_xeno) <= sensor_radius) + if(WEAKREF(current_xeno) in minimap_added) + continue + + SSminimaps.remove_marker(current_xeno) + current_xeno.add_minimap_marker(MINIMAP_FLAG_USCM|MINIMAP_FLAG_XENO) + minimap_added += WEAKREF(current_xeno) + else + if(WEAKREF(current_xeno) in minimap_added) + SSminimaps.remove_marker(current_xeno) + current_xeno.add_minimap_marker() + minimap_added -= WEAKREF(current_xeno) + +/obj/vehicle/multitile/arc/relaymove(mob/user, direction) + if(antenna_deployed) + return FALSE + + return ..() + +/obj/vehicle/multitile/arc/load_role_reserved_slots() + var/datum/role_reserved_slots/RRS = new + RRS.category_name = "CIC Officer" + RRS.roles = list(JOB_SO, JOB_SEA, JOB_XO, JOB_CO, JOB_GENERAL) + RRS.total = 2 + role_reserved_slots += RRS + + RRS = new + RRS.category_name = "Intelligence Officer" + RRS.roles = list(JOB_INTEL) + RRS.total = 1 + role_reserved_slots += RRS + +/obj/vehicle/multitile/arc/set_seated_mob(seat, mob/living/M) + . = ..() + if(!.) + return + + give_action(M, /datum/action/human_action/toggle_arc_antenna) + +/obj/vehicle/multitile/arc/add_seated_verbs(mob/living/M, seat) + if(!M.client) + return + add_verb(M.client, list( + /obj/vehicle/multitile/proc/get_status_info, + //obj/vehicle/multitile/proc/open_controls_guide, + /obj/vehicle/multitile/proc/toggle_door_lock, + /obj/vehicle/multitile/proc/activate_horn, + /obj/vehicle/multitile/proc/name_vehicle, + /obj/vehicle/multitile/arc/proc/toggle_antenna, + )) + +/obj/vehicle/multitile/arc/remove_seated_verbs(mob/living/M, seat) + if(!M.client) + return + remove_verb(M.client, list( + /obj/vehicle/multitile/proc/get_status_info, + //obj/vehicle/multitile/proc/open_controls_guide, + /obj/vehicle/multitile/proc/toggle_door_lock, + /obj/vehicle/multitile/proc/activate_horn, + /obj/vehicle/multitile/proc/name_vehicle, + /obj/vehicle/multitile/arc/proc/toggle_antenna, + )) + SStgui.close_user_uis(M, src) + +/obj/vehicle/multitile/arc/initialize_cameras(change_tag = FALSE) + if(!camera) + camera = new /obj/structure/machinery/camera/vehicle(src) + if(change_tag) + camera.c_tag = "#[rand(1,100)] M540-A \"[nickname]\" ARC" + if(camera_int) + camera_int.c_tag = camera.c_tag + " interior" + else + camera.c_tag = "#[rand(1,100)] 540-A ARC" + if(camera_int) + camera_int.c_tag = camera.c_tag + " interior" + +/obj/vehicle/multitile/arc/MouseDrop_T(mob/M, mob/user) + . = ..() + if((M != user) || !isxeno(user)) + return + + if(health > 0) + to_chat(user, SPAN_XENO("We can't go under [src] until it is destroyed!")) + return + + var/turf/current_turf = get_turf(user) + var/dir_to_go = get_dir(current_turf, src) + for(var/i in 1 to 3) + current_turf = get_step(current_turf, dir_to_go) + if(!(current_turf in locs)) + break + + if(current_turf.density) + to_chat(user, SPAN_XENO("The path under [src] is obstructed!")) + return + + // Now we check to make sure the turf on the other side of the ARC isn't dense too + current_turf = get_step(current_turf, dir_to_go) + if(current_turf.density) + to_chat(user, SPAN_XENO("The path under [src] is obstructed!")) + return + + to_chat(user, SPAN_XENO("We begin to crawl under [src]...")) + if(!do_after(user, 3 SECONDS, INTERRUPT_ALL, BUSY_ICON_HOSTILE)) + to_chat(user, SPAN_XENO("We stop crawling under [src].")) + return + + user.forceMove(current_turf) + +/* +** PRESETS SPAWNERS +*/ +/obj/effect/vehicle_spawner/arc + name = "ARC Transport Spawner" + icon = 'icons/obj/vehicles/apc.dmi' + icon_state = "apc_base" + pixel_x = -48 + pixel_y = -48 + +/obj/effect/vehicle_spawner/arc/Initialize() + . = ..() + spawn_vehicle() + qdel(src) + +/obj/effect/vehicle_spawner/arc/spawn_vehicle() + var/obj/vehicle/multitile/arc/ARC = new (loc) + + load_misc(ARC) + load_hardpoints(ARC) + handle_direction(ARC) + ARC.update_icon() + +/obj/effect/vehicle_spawner/arc/load_hardpoints(obj/vehicle/multitile/arc/vehicle) + vehicle.add_hardpoint(new /obj/item/hardpoint/locomotion/arc_wheels) + vehicle.add_hardpoint(new /obj/item/hardpoint/primary/arc_sentry) + vehicle.add_hardpoint(new /obj/item/hardpoint/support/arc_antenna) diff --git a/code/modules/vehicles/arc/verbs.dm b/code/modules/vehicles/arc/verbs.dm new file mode 100644 index 000000000000..9b4409d16994 --- /dev/null +++ b/code/modules/vehicles/arc/verbs.dm @@ -0,0 +1,121 @@ +/obj/vehicle/multitile/arc/proc/toggle_antenna(mob/toggler) + set name = "Toggle Sensor Antenna" + set desc = "Raises or lowers the external sensor antenna. While raised, the ARC cannot move." + set category = "Vehicle" + + var/mob/user = toggler || usr + if(!user || !istype(user)) + return + + var/obj/vehicle/multitile/arc/vehicle = user.interactee + if(!istype(vehicle)) + return + + var/seat + for(var/vehicle_seat in vehicle.seats) + if(vehicle.seats[vehicle_seat] == user) + seat = vehicle_seat + break + + if(!seat) + return + + if(vehicle.health < initial(vehicle.health) * 0.5) + to_chat(user, SPAN_WARNING("[vehicle]'s hull is too damaged to operate!")) + return + + var/obj/item/hardpoint/support/arc_antenna/antenna = locate() in vehicle.hardpoints + if(!antenna) + to_chat(user, SPAN_WARNING("[vehicle] has no antenna mounted!")) + return + + if(antenna.deploying) + return + + if(antenna.health <= 0) + to_chat(user, SPAN_WARNING("[antenna] is broken!")) + return + + if(vehicle.antenna_deployed) + to_chat(user, SPAN_NOTICE("You begin to retract [antenna]...")) + antenna.deploying = TRUE + if(!do_after(user, max(vehicle.antenna_toggle_time - antenna.deploy_animation_time, 1 SECONDS), target = vehicle)) + to_chat(user, SPAN_NOTICE("You stop retracting [antenna].")) + antenna.deploying = FALSE + return + + antenna.retract_antenna() + addtimer(CALLBACK(vehicle, PROC_REF(finish_antenna_retract), vehicle, user), antenna.deploy_animation_time) + + else + to_chat(user, SPAN_NOTICE("You begin to extend [antenna]...")) + antenna.deploying = TRUE + if(!do_after(user, max(vehicle.antenna_toggle_time - antenna.deploy_animation_time, 1 SECONDS), target = vehicle)) + to_chat(user, SPAN_NOTICE("You stop extending [antenna].")) + antenna.deploying = FALSE + return + + antenna.deploy_antenna() + addtimer(CALLBACK(vehicle, PROC_REF(finish_antenna_deploy), vehicle, user), antenna.deploy_animation_time) + +/obj/vehicle/multitile/arc/proc/finish_antenna_retract(obj/vehicle/multitile/arc/vehicle, mob/user) + var/obj/item/hardpoint/support/arc_antenna/antenna = locate() in vehicle.hardpoints + if(!antenna) + antenna.deploying = FALSE + return + + STOP_PROCESSING(SSslowobj, vehicle) + to_chat(user, SPAN_NOTICE("You retract [antenna], enabling the ARC to move again.")) + playsound(user, 'sound/machines/hydraulics_2.ogg', 80, TRUE) + vehicle.antenna_deployed = !vehicle.antenna_deployed + antenna.deploying = FALSE + vehicle.update_icon() + SEND_SIGNAL(vehicle, COMSIG_ARC_ANTENNA_TOGGLED) + +/obj/vehicle/multitile/arc/proc/finish_antenna_deploy(obj/vehicle/multitile/arc/vehicle, mob/user) + var/obj/item/hardpoint/support/arc_antenna/antenna = locate() in vehicle.hardpoints + if(!antenna) + antenna.deploying = FALSE + return + + START_PROCESSING(SSslowobj, vehicle) + to_chat(user, SPAN_NOTICE("You extend [antenna], locking the ARC in place.")) + playsound(user, 'sound/machines/hydraulics_2.ogg', 80, TRUE) + vehicle.antenna_deployed = !vehicle.antenna_deployed + antenna.deploying = FALSE + vehicle.update_icon() + SEND_SIGNAL(vehicle, COMSIG_ARC_ANTENNA_TOGGLED) + +/obj/vehicle/multitile/arc/open_controls_guide() + set name = "Vehicle Controls Guide" + set desc = "MANDATORY FOR FIRST PLAY AS VEHICLE CREWMAN OR AFTER UPDATES." + set category = "Vehicle" + + var/mob/user = usr + if(!istype(user)) + return + + var/obj/vehicle/multitile/arc/vehicle = user.interactee + if(!istype(vehicle)) + return + + var/seat + for(var/vehicle_seat in vehicle.seats) + if(vehicle.seats[vehicle_seat] == user) + seat = vehicle_seat + break + + if(!seat) + return + + var/dat = "Common verbs:
\ + 1. \"G: Name Vehicle\" - used to add a custom name to the vehicle. Single use. 26 characters maximum.
\ + 2. \"I: Get Status Info\" - brings up \"Vehicle Status Info\" window with all available information about your vehicle.
\ + 3. \"G: Toggle Sensor Antenna\" - extend or retract the ARC's sensor antenna. While extended, all unknown lifeforms within a large range can be seen by all on the tacmap, but the ARC cannot move. Additionally enables the automated RE700 cannon.\ + Driver verbs:
1. \"G: Activate Horn\" - activates vehicle horn. Keep in mind, that vehicle horn is very loud and can be heard from afar by both allies and foes.
\ + 2. \"G: Toggle Door Locks\" - toggles vehicle's access restrictions. Crewman, Brig and Command accesses bypass these restrictions.
\ + Driver shortcuts:
1. \"CTRL + Click\" - activates vehicle horn.
" + + show_browser(user, dat, "Vehicle Controls Guide", "vehicle_help", "size=900x500") + onclose(user, "vehicle_help") + return diff --git a/code/modules/vehicles/hardpoints/hardpoint.dm b/code/modules/vehicles/hardpoints/hardpoint.dm index 21e3e4b29f89..dff08004ba95 100644 --- a/code/modules/vehicles/hardpoints/hardpoint.dm +++ b/code/modules/vehicles/hardpoints/hardpoint.dm @@ -581,7 +581,6 @@ /// Wrapper proc for the autofire system to ensure the important args aren't null. /obj/item/hardpoint/proc/fire_wrapper(atom/target, mob/living/user, params) - SHOULD_NOT_OVERRIDE(TRUE) if(!target) target = src.target if(!user) diff --git a/code/modules/vehicles/hardpoints/hardpoint_ammo/arc_sentry_ammo.dm b/code/modules/vehicles/hardpoints/hardpoint_ammo/arc_sentry_ammo.dm new file mode 100644 index 000000000000..f9c28e151514 --- /dev/null +++ b/code/modules/vehicles/hardpoints/hardpoint_ammo/arc_sentry_ammo.dm @@ -0,0 +1,16 @@ +/obj/item/ammo_magazine/hardpoint/arc_sentry + name = "\improper RE700 Rotary Cannon Magazine" + desc = "A magazine for RE700 Rotary Cannon filled with 20mm rounds. Supports IFF." + caliber = "20mm" + icon_state = "ace_autocannon" + w_class = SIZE_LARGE + default_ammo = /datum/ammo/bullet/re700 + max_rounds = 500 + gun_type = /obj/item/hardpoint/primary/arc_sentry + +/obj/item/ammo_magazine/hardpoint/arc_sentry/update_icon() + if(current_rounds > 0) + icon_state = "ace_autocannon" + else + icon_state = "ace_autocannon_empty" + diff --git a/code/modules/vehicles/hardpoints/primary/arc_sentry.dm b/code/modules/vehicles/hardpoints/primary/arc_sentry.dm new file mode 100644 index 000000000000..c0b18f33708e --- /dev/null +++ b/code/modules/vehicles/hardpoints/primary/arc_sentry.dm @@ -0,0 +1,305 @@ +// APC cannons +/obj/item/hardpoint/primary/arc_sentry + name = "\improper RE700 Rotary Cannon" + desc = "A primary two-barrel cannon for the ARC that shoots 12.7mm IFF-compatible rounds." + icon = 'icons/obj/vehicles/hardpoints/arc.dmi' + + icon_state = "autocannon" + disp_icon = "arc" + disp_icon_state = "autocannon" + activation_sounds = list('sound/weapons/gun_m60.ogg') + + damage_multiplier = 0.1 + health = 125 + + origins = list(0, 0) + + ammo = new /obj/item/ammo_magazine/hardpoint/arc_sentry + max_clips = 2 + + use_muzzle_flash = TRUE + angle_muzzleflash = FALSE + muzzleflash_icon_state = "muzzle_flash_double" + + muzzle_flash_pos = list( + "1" = list(1, 0), + "2" = list(1, -25), + "4" = list(16, -4), + "8" = list(-16, -4) + ) + gun_firemode = GUN_FIREMODE_BURSTFIRE + gun_firemode_list = list( + GUN_FIREMODE_BURSTFIRE, + ) + burst_delay = 2 + burst_amount = 3 + + /// Potential targets the turret can shoot at + var/list/targets = list() + /// The currently focused sentry target + var/atom/movable/sentry_target = null + /// The range that this turret can shoot at the furthest + var/turret_range = 5 + /// What factions this sentry is aligned with + var/faction_group = FACTION_LIST_MARINE + +/obj/item/hardpoint/primary/arc_sentry/on_install(obj/vehicle/multitile/V) + . = ..() + RegisterSignal(owner, COMSIG_ARC_ANTENNA_TOGGLED, PROC_REF(toggle_processing)) + +/obj/item/hardpoint/primary/arc_sentry/on_uninstall(obj/vehicle/multitile/V) + . = ..() + UnregisterSignal(owner, COMSIG_ARC_ANTENNA_TOGGLED) + START_PROCESSING(SSfastobj, src) + +/obj/item/hardpoint/primary/arc_sentry/Destroy() + STOP_PROCESSING(SSfastobj, src) + sentry_target = null + return ..() + +/obj/item/hardpoint/primary/arc_sentry/proc/toggle_processing() + SIGNAL_HANDLER + if(!owner) + return + + var/obj/vehicle/multitile/arc/vehicle = owner + if(vehicle.antenna_deployed) + START_PROCESSING(SSfastobj, src) + + else + STOP_PROCESSING(SSfastobj, src) + +/obj/item/hardpoint/primary/arc_sentry/process() + for(var/mob/living/in_range_mob in range(turret_range, owner)) + targets |= in_range_mob + + if(!length(targets)) + return FALSE + + if(!sentry_target && length(targets)) + sentry_target = pick(targets) + + get_target(sentry_target) + return TRUE + +/obj/item/hardpoint/primary/arc_sentry/set_bullet_traits() + ..() + LAZYADD(traits_to_give, list( + BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_iff) + )) + +/obj/item/hardpoint/primary/arc_sentry/fire_wrapper(atom/target, mob/living/user, params) + if(!target) + target = src.target + if(!target) + return NONE + + return try_fire(target, null, params) + +/obj/item/hardpoint/primary/arc_sentry/clear_los() + var/turf/muzzle_turf = get_origin_turf() + + var/turf/checking_turf = muzzle_turf + while(!(owner in checking_turf)) + // Dense turfs block LoS + if(checking_turf.density) + return FALSE + + // Ensure that we can pass over all objects in the turf + for(var/obj/object in checking_turf) + // Since vehicles are multitile the + if(object == owner) + continue + + // Non-dense objects are irrelevant + if(!object.density) + continue + + // Make sure we can pass object from all directions + if(!HAS_FLAG(object.pass_flags.flags_can_pass_all, PASS_OVER_THROW_ITEM)) + if(!HAS_FLAG(object.flags_atom, ON_BORDER)) + return FALSE + //If we're behind the object, check the behind pass flags + else if(dir == object.dir && !HAS_FLAG(object.pass_flags.flags_can_pass_behind, PASS_OVER_THROW_ITEM)) + return FALSE + //If we're in front, check front pass flags + else if(dir == turn(object.dir, 180) && !HAS_FLAG(object.pass_flags.flags_can_pass_front, PASS_OVER_THROW_ITEM)) + return FALSE + + // Trace back towards the vehicle + checking_turf = get_step(checking_turf, turn(dir,180)) + + return TRUE + +/obj/item/hardpoint/primary/arc_sentry/handle_fire(atom/target, mob/living/user, params) + var/turf/origin_turf = get_origin_turf() + + var/obj/projectile/arc_sentry/new_bullet = generate_bullet(origin_turf) + ammo.current_rounds-- + SEND_SIGNAL(new_bullet, COMSIG_BULLET_USER_EFFECTS, user) + new_bullet.runtime_iff_group = faction_group // Technically shouldn't be directly modifying this, but sue me + + // turf-targeted projectiles are fired without scatter, because proc would raytrace them further away + var/ammo_flags = new_bullet.ammo.flags_ammo_behavior | new_bullet.projectile_override_flags + if(!HAS_FLAG(ammo_flags, AMMO_HITS_TARGET_TURF) && !HAS_FLAG(ammo_flags, AMMO_EXPLOSIVE)) //AMMO_EXPLOSIVE is also a turf-targeted projectile + new_bullet.scatter = scatter + target = simulate_scatter(new_bullet, target, origin_turf, get_turf(target), user) + + INVOKE_ASYNC(new_bullet, TYPE_PROC_REF(/obj/projectile, fire_at), target, user, src, new_bullet.ammo.max_range, new_bullet.ammo.shell_speed) + new_bullet = null + + shots_fired++ + play_firing_sounds() + if(use_muzzle_flash) + muzzle_flash(Get_Angle(origin_turf, target)) + + set_fire_cooldown(gun_firemode) + return AUTOFIRE_CONTINUE + + +/obj/item/hardpoint/primary/arc_sentry/generate_bullet(turf/origin_turf) + var/obj/projectile/arc_sentry/new_proj = new(origin_turf, create_cause_data(initial(name), owner)) + new_proj.generate_bullet(new ammo.default_ammo) + new_proj.permutated += owner + // Apply bullet traits from gun + for(var/entry in traits_to_give) + var/list/trait_list + // Check if this is an ID'd bullet trait + if(istext(entry)) + trait_list = traits_to_give[entry].Copy() + else + // Prepend the bullet trait to the list + trait_list = list(entry) + traits_to_give[entry] + new_proj.apply_bullet_trait(trait_list) + return new_proj + +/obj/item/hardpoint/primary/arc_sentry/start_fire(datum/source, atom/object, turf/location, control, params) + if(istype(object, /atom/movable/screen)) + return + + if(QDELETED(object)) + return + + if(!auto_firing && !burst_firing && !COOLDOWN_FINISHED(src, fire_cooldown)) + if(max(fire_delay, burst_delay + extra_delay) >= 2.0 SECONDS) //filter out guns with high firerate to prevent message spam. + to_chat(source, SPAN_WARNING("You need to wait [SPAN_HELPFUL(COOLDOWN_SECONDSLEFT(src, fire_cooldown))] seconds before [name] can be used again.")) + return + + set_target(object) + + if(gun_firemode == GUN_FIREMODE_SEMIAUTO) + var/fire_return = try_fire(object, source, params) + //end-of-fire, show ammo (if changed) + if(fire_return == AUTOFIRE_CONTINUE) + reset_fire() + display_ammo(source) + else + SEND_SIGNAL(src, COMSIG_GUN_FIRE) + +/obj/item/hardpoint/primary/arc_sentry/proc/get_target(atom/movable/new_target) + if(!islist(targets)) + return + if(!targets.Find(new_target)) + targets.Add(new_target) + + if(!length(targets)) + return + + var/list/conscious_targets = list() + var/list/unconscious_targets = list() + + for(var/atom/movable/movable as anything in targets) // orange allows sentry to fire through gas and darkness + if(isliving(movable)) + var/mob/living/living_mob = movable + if(living_mob.stat & DEAD) + if(movable == sentry_target) + sentry_target = null + targets.Remove(movable) + continue + + if(living_mob.get_target_lock(faction_group) || living_mob.invisibility || HAS_TRAIT(living_mob, TRAIT_ABILITY_BURROWED)) + if(living_mob == sentry_target) + sentry_target = null + targets.Remove(living_mob) + continue + + var/list/turf/path = get_line(get_turf(src), movable) + if(!length(path)|| get_dist(get_turf(src), movable) > turret_range) + if(movable == sentry_target) + sentry_target = null + targets.Remove(movable) + continue + + var/blocked = FALSE + for(var/turf/tile as anything in path) + if(tile.density || tile.opacity) + blocked = TRUE + break + + for(var/obj/structure/struct in tile) + if(struct.opacity) + blocked = TRUE + break + + for(var/obj/vehicle/multitile/vehicle in tile) + if(vehicle == owner) // Some of the tiles will inevitably be the ARC itself + continue + blocked = TRUE + break + + if(locate(/obj/effect/particle_effect/smoke) in tile) + blocked = TRUE + break + + if(blocked) + if(movable == sentry_target) + sentry_target = null + targets.Remove(movable) + continue + + if(isliving(movable)) + var/mob/living/living_mob = movable + if(living_mob.stat & UNCONSCIOUS) + unconscious_targets += living_mob + else + conscious_targets += living_mob + + if((sentry_target in conscious_targets) || (sentry_target in unconscious_targets)) + sentry_target = sentry_target + + else if(length(conscious_targets)) + sentry_target = pick(conscious_targets) + + else if(length(unconscious_targets)) + sentry_target = pick(unconscious_targets) + + if(!sentry_target) //No targets, don't bother firing + return + + start_fire(object = sentry_target) + +/obj/projectile/arc_sentry/Initialize(mapload, datum/cause_data/cause_data) + . = ..() + RegisterSignal(src, COMSIG_BULLET_POST_HANDLE_OBJ, PROC_REF(check_passthrough)) + +/obj/projectile/arc_sentry/check_canhit(turf/current_turf, turf/next_turf) + var/proj_dir = get_dir(current_turf, next_turf) + var/obj/item/hardpoint/arc_sentry = shot_from + if(!(arc_sentry.owner in current_turf) && !(arc_sentry.owner in next_turf) && (proj_dir & (proj_dir - 1)) && !current_turf.Adjacent(next_turf)) + ammo.on_hit_turf(current_turf, src) + current_turf.bullet_act(src) + return TRUE + + // Check for hits that would occur when moving to turf, such as a blocking cade + if(scan_a_turf(next_turf, proj_dir)) + return TRUE + + return FALSE + +/obj/projectile/arc_sentry/proc/check_passthrough(datum/source, obj/hit_obj, bool) + if(!istype(shot_from, /obj/item/hardpoint)) + return + + var/obj/item/hardpoint/sentry = shot_from + if(sentry.owner == hit_obj) + return COMPONENT_BULLET_PASS_THROUGH diff --git a/code/modules/vehicles/hardpoints/support/antenna.dm b/code/modules/vehicles/hardpoints/support/antenna.dm new file mode 100644 index 000000000000..38b08656c067 --- /dev/null +++ b/code/modules/vehicles/hardpoints/support/antenna.dm @@ -0,0 +1,72 @@ +/obj/item/hardpoint/support/arc_antenna + name = "\improper U-56 Radar Antenna" + desc = "A heavy-duty antenna built for the ARC." + icon = 'icons/obj/vehicles/hardpoints/arc.dmi' + + icon_state = "antenna" + disp_icon = "arc" + disp_icon_state = "antenna" + + damage_multiplier = 0.1 + + health = 500 + + /// How long the antenna deploy/retract animation is, keep accurate to the sprite in the dmi + var/deploy_animation_time = 1.7 SECONDS + /// If the antenna is already deploying + var/deploying = FALSE + +/obj/item/hardpoint/support/arc_antenna/proc/deploy_antenna() + set waitfor = FALSE + + disp_icon_state = "" + if(owner) + owner.update_icon() + var/obj/dummy_obj = new() + dummy_obj.icon = 'icons/obj/vehicles/arc.dmi' + dummy_obj.icon_state = "antenna_cover_0" + dummy_obj.dir = owner.dir + dummy_obj.vis_flags = VIS_INHERIT_ID | VIS_INHERIT_LAYER | VIS_INHERIT_PLANE + owner.vis_contents += dummy_obj + flick("antenna_extending", dummy_obj) + sleep(deploy_animation_time) + qdel(dummy_obj) + disp_icon_state = initial(disp_icon_state) + +/obj/item/hardpoint/support/arc_antenna/proc/retract_antenna() + set waitfor = FALSE + + disp_icon_state = "" + if(owner) + owner.update_icon() + var/obj/dummy_obj = new() + dummy_obj.icon = 'icons/obj/vehicles/arc.dmi' + dummy_obj.icon_state = "antenna_cover_0" + dummy_obj.dir = owner.dir + dummy_obj.vis_flags = VIS_INHERIT_ID | VIS_INHERIT_LAYER | VIS_INHERIT_PLANE + owner.vis_contents += dummy_obj + flick("antenna_retracting", dummy_obj) + sleep(deploy_animation_time) + qdel(dummy_obj) + disp_icon_state = initial(disp_icon_state) + +/obj/item/hardpoint/support/arc_antenna/get_icon_image(x_offset, y_offset, new_dir) + var/is_broken = health <= 0 + var/antenna_extended = FALSE + if(istype(owner, /obj/vehicle/multitile/arc)) + var/obj/vehicle/multitile/arc/arc_owner = owner + antenna_extended = arc_owner.antenna_deployed + + var/image/I = image(icon = disp_icon, icon_state = "[disp_icon_state]_[antenna_extended ? "extended" : "cover"]_[is_broken ? "1" : "0"]", pixel_x = x_offset, pixel_y = y_offset, dir = new_dir) + switch(round((health / initial(health)) * 100)) + if(0 to 20) + I.color = "#4e4e4e" + if(21 to 40) + I.color = "#6e6e6e" + if(41 to 60) + I.color = "#8b8b8b" + if(61 to 80) + I.color = "#bebebe" + else + I.color = null + return I diff --git a/code/modules/vehicles/hardpoints/wheels/arc_wheels.dm b/code/modules/vehicles/hardpoints/wheels/arc_wheels.dm new file mode 100644 index 000000000000..9bb6c31746e0 --- /dev/null +++ b/code/modules/vehicles/hardpoints/wheels/arc_wheels.dm @@ -0,0 +1,17 @@ +/obj/item/hardpoint/locomotion/arc_wheels + name = "ARC Wheels" + desc = "Integral to the movement of the ARC." + icon = 'icons/obj/vehicles/hardpoints/arc.dmi' + + damage_multiplier = 0.15 + + icon_state = "tires" + disp_icon = "arc" + disp_icon_state = "arc_wheels" + + health = 500 + + move_delay = VEHICLE_SPEED_SUPERFAST + move_max_momentum = 2 + move_momentum_build_factor = 1.5 + move_turn_momentum_loss_factor = 0.5 diff --git a/code/modules/vehicles/interior/areas.dm b/code/modules/vehicles/interior/areas.dm index 254bcb6b26ea..399e55e11450 100644 --- a/code/modules/vehicles/interior/areas.dm +++ b/code/modules/vehicles/interior/areas.dm @@ -29,5 +29,9 @@ name = "van interior" icon_state = "van" +/area/interior/vehicle/arc + name = "\improper ARC interior" + icon_state = "arc" + /area/interior/fancylocker name = "closet interior" diff --git a/code/modules/vehicles/multitile/multitile.dm b/code/modules/vehicles/multitile/multitile.dm index f3b7be510b08..18dade67b834 100644 --- a/code/modules/vehicles/multitile/multitile.dm +++ b/code/modules/vehicles/multitile/multitile.dm @@ -334,11 +334,12 @@ // Checked here because we want to be able to null the mob in a seat if(!istype(M)) - return + return FALSE M.set_interaction(src) M.reset_view(src) give_action(M, /datum/action/human_action/vehicle_unbuckle) + return TRUE /// Get crewmember of seat. /obj/vehicle/multitile/proc/get_seat_mob(seat) diff --git a/code/modules/vehicles/multitile/multitile_hardpoints.dm b/code/modules/vehicles/multitile/multitile_hardpoints.dm index 2a6f97dda06f..078cfc4b199a 100644 --- a/code/modules/vehicles/multitile/multitile_hardpoints.dm +++ b/code/modules/vehicles/multitile/multitile_hardpoints.dm @@ -149,7 +149,7 @@ hps += H var/chosen_hp = tgui_input_list(usr, "Select a hardpoint to remove", "Hardpoint Removal", (hps + "Cancel")) - if(chosen_hp == "Cancel" || !chosen_hp || !in_range(src, user)) + if(chosen_hp == "Cancel" || !chosen_hp || (get_dist(src, user) > 2)) //get_dist uses 2 because the vehicle is 3x3 return var/obj/item/hardpoint/old = chosen_hp diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm index 014452426a3c..76107a4a1776 100644 --- a/code/modules/vehicles/vehicle.dm +++ b/code/modules/vehicles/vehicle.dm @@ -159,10 +159,11 @@ // Checked here because we want to be able to null the mob in a seat if(!istype(M)) - return + return FALSE M.forceMove(src) M.set_interaction(src) + return TRUE /obj/vehicle/proc/turn_on() if(stat) diff --git a/colonialmarines.dme b/colonialmarines.dme index b509aa695847..fd7f5833e345 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -379,6 +379,7 @@ #include "code\datums\ammo\rocket.dm" #include "code\datums\ammo\shrapnel.dm" #include "code\datums\ammo\xeno.dm" +#include "code\datums\ammo\bullet\arc.dm" #include "code\datums\ammo\bullet\bullet.dm" #include "code\datums\ammo\bullet\lever_action.dm" #include "code\datums\ammo\bullet\pistol.dm" @@ -401,6 +402,7 @@ #include "code\datums\components\cluster_stack.dm" #include "code\datums\components\connect_mob_behalf.dm" #include "code\datums\components\crate_tag.dm" +#include "code\datums\components\disk_reader.dm" #include "code\datums\components\footstep.dm" #include "code\datums\components\healing_reduction.dm" #include "code\datums\components\id_lock.dm" @@ -670,6 +672,7 @@ #include "code\datums\supply_packs\restricted_equipment.dm" #include "code\datums\supply_packs\spec_ammo.dm" #include "code\datums\supply_packs\vehicle_ammo.dm" +#include "code\datums\supply_packs\vehicle_equipment.dm" #include "code\datums\supply_packs\weapons.dm" #include "code\datums\tutorial\_tutorial.dm" #include "code\datums\tutorial\_tutorial_menu.dm" @@ -1672,6 +1675,7 @@ #include "code\modules\cm_tech\resources\resource.dm" #include "code\modules\cm_tech\techs\abstract\repeatable.dm" #include "code\modules\cm_tech\techs\abstract\transitory.dm" +#include "code\modules\cm_tech\techs\marine\tier1\arc.dm" #include "code\modules\cm_tech\techs\marine\tier1\points.dm" #include "code\modules\cm_tech\techs\marine\tier2\orbital_ammo.dm" #include "code\modules\cm_tech\techs\marine\tier3\cryo_spec.dm" @@ -2375,6 +2379,8 @@ #include "code\modules\vehicles\apc\apc_command.dm" #include "code\modules\vehicles\apc\apc_medical.dm" #include "code\modules\vehicles\apc\interior.dm" +#include "code\modules\vehicles\arc\arc.dm" +#include "code\modules\vehicles\arc\verbs.dm" #include "code\modules\vehicles\hardpoints\hardpoint.dm" #include "code\modules\vehicles\hardpoints\armor\armor.dm" #include "code\modules\vehicles\hardpoints\armor\ballistic.dm" @@ -2382,6 +2388,7 @@ #include "code\modules\vehicles\hardpoints\armor\concussive.dm" #include "code\modules\vehicles\hardpoints\armor\paladin.dm" #include "code\modules\vehicles\hardpoints\armor\snowplow.dm" +#include "code\modules\vehicles\hardpoints\hardpoint_ammo\arc_sentry_ammo.dm" #include "code\modules\vehicles\hardpoints\hardpoint_ammo\autocannon_ammo.dm" #include "code\modules\vehicles\hardpoints\hardpoint_ammo\cupola_ammo.dm" #include "code\modules\vehicles\hardpoints\hardpoint_ammo\dualcannon_ammo.dm" @@ -2397,6 +2404,7 @@ #include "code\modules\vehicles\hardpoints\hardpoint_ammo\tow_ammo.dm" #include "code\modules\vehicles\hardpoints\holder\holder.dm" #include "code\modules\vehicles\hardpoints\holder\tank_turret.dm" +#include "code\modules\vehicles\hardpoints\primary\arc_sentry.dm" #include "code\modules\vehicles\hardpoints\primary\autocannon.dm" #include "code\modules\vehicles\hardpoints\primary\dual_cannon.dm" #include "code\modules\vehicles\hardpoints\primary\flamer.dm" @@ -2411,12 +2419,14 @@ #include "code\modules\vehicles\hardpoints\secondary\tow.dm" #include "code\modules\vehicles\hardpoints\special\firing_port_weapon.dm" #include "code\modules\vehicles\hardpoints\special\special.dm" +#include "code\modules\vehicles\hardpoints\support\antenna.dm" #include "code\modules\vehicles\hardpoints\support\artillery.dm" #include "code\modules\vehicles\hardpoints\support\flare.dm" #include "code\modules\vehicles\hardpoints\support\iwsa.dm" #include "code\modules\vehicles\hardpoints\support\overdrive.dm" #include "code\modules\vehicles\hardpoints\support\support.dm" #include "code\modules\vehicles\hardpoints\wheels\apc_wheels.dm" +#include "code\modules\vehicles\hardpoints\wheels\arc_wheels.dm" #include "code\modules\vehicles\hardpoints\wheels\locomotion.dm" #include "code\modules\vehicles\hardpoints\wheels\treads.dm" #include "code\modules\vehicles\hardpoints\wheels\van_wheels.dm" diff --git a/icons/obj/vehicles/arc.dmi b/icons/obj/vehicles/arc.dmi new file mode 100644 index 000000000000..b44cf45b3a5d Binary files /dev/null and b/icons/obj/vehicles/arc.dmi differ diff --git a/icons/obj/vehicles/hardpoints/arc.dmi b/icons/obj/vehicles/hardpoints/arc.dmi new file mode 100644 index 000000000000..72854adf2382 Binary files /dev/null and b/icons/obj/vehicles/hardpoints/arc.dmi differ diff --git a/icons/turf/areas_interiors.dmi b/icons/turf/areas_interiors.dmi index 47a95da322ea..6fc30badd763 100644 Binary files a/icons/turf/areas_interiors.dmi and b/icons/turf/areas_interiors.dmi differ diff --git a/icons/ui_icons/map_blips_large.dmi b/icons/ui_icons/map_blips_large.dmi index 0cf41b52a4dc..f03433c8c8ef 100644 Binary files a/icons/ui_icons/map_blips_large.dmi and b/icons/ui_icons/map_blips_large.dmi differ diff --git a/maps/interiors/arc.dmm b/maps/interiors/arc.dmm new file mode 100644 index 000000000000..4da63cbff383 --- /dev/null +++ b/maps/interiors/arc.dmm @@ -0,0 +1,296 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"aD" = ( +/obj/effect/landmark/interior/spawn/weapons_loader, +/turf/open/shuttle/vehicle{ + icon_state = "floor_3_12" + }, +/area/interior/vehicle/arc) +"be" = ( +/obj/structure/interior_wall/apc{ + icon_state = "door_back" + }, +/turf/open/void/vehicle, +/area/space) +"cJ" = ( +/obj/structure/interior_wall/apc{ + icon_state = "rear_1" + }, +/turf/open/void/vehicle, +/area/space) +"dM" = ( +/obj/structure/interior_wall/apc{ + icon_state = "rear_2" + }, +/turf/open/void/vehicle, +/area/space) +"dU" = ( +/obj/effect/landmark/interior/spawn/vehicle_driver_seat/armor{ + dir = 4 + }, +/obj/structure/machinery/cm_vending/sorted/medical/wall_med/vehicle{ + pixel_x = 6; + pixel_y = 28 + }, +/obj/item/device/megaphone, +/turf/open/shuttle/vehicle{ + icon_state = "floor_3_9_1" + }, +/area/interior/vehicle/arc) +"jb" = ( +/turf/open/shuttle/vehicle{ + icon_state = "floor_3_11" + }, +/area/interior/vehicle/arc) +"ml" = ( +/obj/structure/interior_wall/apc{ + icon_state = "wall" + }, +/obj/effect/landmark/interior/spawn/telephone, +/turf/open/void/vehicle, +/area/space) +"mR" = ( +/obj/structure/interior_wall/apc{ + icon_state = "front_wheel_R" + }, +/turf/open/void/vehicle, +/area/space) +"ro" = ( +/obj/structure/interior_wall/apc{ + alpha = 100; + icon_state = "wall_door_front"; + layer = 5.2; + pixel_y = 32 + }, +/turf/open/void/vehicle, +/area/space) +"ru" = ( +/turf/open/shuttle/vehicle{ + icon_state = "floor_1_6" + }, +/area/interior/vehicle/arc) +"tD" = ( +/obj/structure/interior_wall/apc{ + icon_state = "rear_6" + }, +/turf/open/void/vehicle, +/area/space) +"vt" = ( +/obj/structure/interior_wall/apc{ + icon_state = "rear_wheel_L" + }, +/turf/open/void/vehicle, +/area/space) +"we" = ( +/obj/structure/interior_wall/apc{ + alpha = 100; + icon_state = "door_front"; + layer = 5.2; + pixel_y = 32 + }, +/turf/open/void/vehicle, +/area/space) +"wK" = ( +/obj/structure/interior_wall/apc{ + icon_state = "wheel_front_top_1"; + pixel_x = 1; + pixel_y = -4 + }, +/turf/open/void/vehicle, +/area/space) +"ym" = ( +/obj/structure/interior_wall/apc{ + icon_state = "rear_wheel_R"; + opacity = 0 + }, +/turf/open/void/vehicle, +/area/space) +"yX" = ( +/turf/open/shuttle/vehicle{ + icon_state = "floor_3_8_1" + }, +/area/interior/vehicle/arc) +"CB" = ( +/obj/structure/interior_wall/apc{ + icon_state = "wall"; + opacity = 0 + }, +/turf/open/void/vehicle, +/area/space) +"Dn" = ( +/obj/structure/machinery/prop/almayer/CICmap/computer{ + dir = 4; + pixel_y = 8 + }, +/obj/structure/surface/table/reinforced/prison{ + pixel_y = -3 + }, +/obj/structure/machinery/computer/overwatch/almayer/small{ + dir = 4; + layer = 3.01; + pixel_y = -5 + }, +/turf/open/shuttle/vehicle{ + icon_state = "floor_3_10_1" + }, +/area/interior/vehicle/arc) +"Gy" = ( +/obj/structure/interior_wall/apc{ + icon_state = "front_1" + }, +/turf/open/void/vehicle, +/area/space) +"HB" = ( +/obj/structure/interior_wall/apc{ + icon_state = "corner_small_L" + }, +/obj/structure/interior_wall/apc{ + icon_state = "rear_5" + }, +/turf/open/void/vehicle, +/area/space) +"HJ" = ( +/obj/structure/interior_wall/apc{ + icon_state = "front_6" + }, +/turf/open/void/vehicle, +/area/space) +"Jc" = ( +/obj/structure/interior_wall/apc{ + icon_state = "corner_small_R" + }, +/obj/structure/interior_wall/apc{ + icon_state = "front_5" + }, +/turf/open/void/vehicle, +/area/space) +"Ng" = ( +/obj/structure/interior_wall/apc{ + icon_state = "wall" + }, +/turf/open/void/vehicle, +/area/space) +"NS" = ( +/obj/structure/bed/chair/comfy{ + dir = 8 + }, +/turf/open/shuttle/vehicle{ + icon_state = "floor_3_5" + }, +/area/interior/vehicle/arc) +"Ol" = ( +/obj/effect/landmark/interior/spawn/entrance{ + alpha = 50; + exit_type = /obj/structure/interior_exit/vehicle/apc; + name = "Right APC door"; + tag = "right" + }, +/obj/effect/landmark/interior/spawn/interior_viewport{ + dir = 8; + pixel_x = 5; + pixel_y = 1 + }, +/turf/open/shuttle/vehicle{ + icon_state = "floor_1_12" + }, +/area/interior/vehicle/arc) +"Po" = ( +/obj/structure/surface/table/reinforced/prison{ + pixel_y = -3 + }, +/obj/structure/machinery/computer/intel/disk_reader{ + dir = 4; + pixel_y = 6 + }, +/obj/structure/machinery/computer/groundside_operations{ + dir = 4; + pixel_y = -7 + }, +/turf/open/shuttle/vehicle{ + icon_state = "floor_3_8_1" + }, +/area/interior/vehicle/arc) +"Rb" = ( +/turf/open/void/vehicle, +/area/space) +"Rf" = ( +/obj/structure/interior_wall/apc{ + icon_state = "front_2" + }, +/turf/open/void/vehicle, +/area/space) +"Zs" = ( +/obj/structure/bed/chair/comfy{ + dir = 8 + }, +/turf/open/shuttle/vehicle{ + icon_state = "floor_3_8_1" + }, +/area/interior/vehicle/arc) +"ZF" = ( +/obj/effect/landmark/interior/spawn/interior_viewport{ + dir = 8; + pixel_x = 5; + pixel_y = 8 + }, +/obj/effect/landmark/interior/spawn/entrance{ + alpha = 50; + dir = 1; + exit_type = /obj/structure/interior_exit/vehicle/apc; + name = "Left APC door"; + pixel_y = 32; + tag = "left" + }, +/turf/open/shuttle/vehicle{ + icon_state = "floor_1_11" + }, +/area/interior/vehicle/arc) + +(1,1,1) = {" +tD +HB +dM +cJ +Rb +"} +(2,1,1) = {" +Ng +Po +Dn +vt +Rb +"} +(3,1,1) = {" +ml +Zs +NS +ym +Rb +"} +(4,1,1) = {" +Ng +jb +yX +aD +ro +"} +(5,1,1) = {" +be +ZF +ru +Ol +we +"} +(6,1,1) = {" +wK +CB +dU +mR +Rb +"} +(7,1,1) = {" +HJ +Jc +Rf +Gy +Rb +"} diff --git a/strings/xenotips.txt b/strings/xenotips.txt index 08f56bf1aa8e..8cc44753d802 100644 --- a/strings/xenotips.txt +++ b/strings/xenotips.txt @@ -37,3 +37,5 @@ You can filter out the Xenomorphs displayed in hive status by health, allowing y Each xeno has their own ‘tackle counter’ on a marine. The range to successfully tackle can be anywhere from two to six tackles based on caste. If a marine gets stunned or knocked over by other means it will reset everyone's tackle counters and they may get up! As a Xenomorph, the list of available tunnels is sorted by their distance to the player! As a Xenomorph, pay attention to what a marine is wearing. If they have no helmet, aiming for the head will be significantly more damaging, if they aren't wearing gloves, then think about going for their hands. +The M540-A Armored Recon Carrier can be crawled under as a xenomorph when destroyed. +The M540-A Armored Recon Carrier is very vulnerable to acid damage.