diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone.dm new file mode 100644 index 0000000000..de8d502665 --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -0,0 +1,274 @@ +//Modified copy of regula drone folder +var/list/mob_hat_cache = list() +/proc/get_hat_icon(var/obj/item/hat, var/offset_x = 0, var/offset_y = 0) + var/t_state = hat.icon_state + if(LAZYACCESS(hat.item_state_slots, slot_head_str)) + t_state = hat.item_state_slots[slot_head_str] + else if(hat.item_state) + t_state = hat.item_state + var/key = "[t_state]_[offset_x]_[offset_y]" + if(!mob_hat_cache[key]) // Not ideal as there's no guarantee all hat icon_states + var/t_icon = INV_HEAD_DEF_ICON // are unique across multiple dmis, but whatever. + if(hat.icon_override) + t_icon = hat.icon_override + else if(LAZYACCESS(hat.item_icons, slot_head_str)) + t_icon = hat.item_icons[slot_head_str] + var/image/I = image(icon = t_icon, icon_state = t_state) + I.pixel_x = offset_x + I.pixel_y = offset_y + mob_hat_cache[key] = I + return mob_hat_cache[key] + +/mob/living/silicon/robot/drone/New() + ..() + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + remove_language("Robot Talk") + add_language("Robot Talk", 0) + add_language("Drone Talk", 1) + serial_number = rand(0,999) + + //They are unable to be upgraded, so let's give them a bit of a better battery. + cell.maxcharge = 10000 + cell.charge = 10000 + + // NO BRAIN. + mmi = null + + //We need to screw with their HP a bit. They have around one fifth as much HP as a full borg. + for(var/V in components) if(V != "power cell") + var/datum/robot_component/C = components[V] + C.max_damage = 10 + + verbs -= /mob/living/silicon/robot/verb/Namepick + + if(can_pick_shell) + var/random = pick(shell_types) + icon_state = shell_types[random] + shell_accessories = list("[icon_state]-eyes-blue") + + updateicon() + updatename() + +/mob/living/silicon/robot/drone/updateicon() + cut_overlays() + + if(islist(shell_accessories)) + add_overlay(shell_accessories) + + if(hat) // Let the drones wear hats. + add_overlay(get_hat_icon(hat, hat_x_offset, hat_y_offset)) + +/mob/living/silicon/robot/drone/verb/pick_shell() + set name = "Customize Appearance" + set category = "Robot Commands" + + if(!can_pick_shell) + to_chat(src, "You already selected a shell or this drone type isn't customizable.") + return + + var/list/choices = shell_types.Copy() + + if(can_blitz) + choices["Blitz"] = "blitzshell" + + var/shell_choice = tgui_input_list(src, "Select a shell. NOTE: You can only do this once during this drone-lifetime.", "Customize Shell", choices) + if(!shell_choice) + return + + icon_state = choices[shell_choice] + + // If you add more, datumize these. Having 'basically two' is not enough to make me bother though. + shell_accessories = null + if(icon_state in list("repairbot", "maintbot")) + var/eye_color = tgui_input_list(src, "Select eye color:", "Eye Color", list("blue", "red", "orange", "green", "violet")) + if(eye_color) + LAZYADD(shell_accessories, "[icon_state]-eyes-[eye_color]") + if(icon_state == "maintbot") + var/armor_color = tgui_input_list(src, "Select plating color:", "Eye Color", list("blue", "red", "orange", "green", "brown")) + if(armor_color) + LAZYADD(shell_accessories, "[icon_state]-shell-[armor_color]") + + can_pick_shell = FALSE + updateicon() + +/mob/living/silicon/robot/drone/proc/wear_hat(var/obj/item/new_hat) + if(hat) + return + hat = new_hat + new_hat.loc = src + updateicon() + +//Drones cannot be upgraded with borg modules so we need to catch some items before they get used in ..(). +/mob/living/silicon/robot/drone/attackby(var/obj/item/weapon/W, var/mob/user) + + //Early return handler + if(handle_attackby_return(W,user)) + return + if(user.a_intent == "help" && istype(W, /obj/item/clothing/head)) + equip_hat(W,user) + ..() + +//Denesting for the above code + +/mob/living/silicon/robot/drone/proc/equip_hat(var/obj/item/clothing/head/W, var/mob/user) + if(hat) + to_chat(user, "\The [src] is already wearing \the [hat].") + return + user.unEquip(W) + wear_hat(W) + user.visible_message("\The [user] puts \the [W] on \the [src].") + return + +/mob/living/silicon/robot/drone/proc/handle_attackby_return(var/obj/item/W, var/mob/user) + var/hint + if(W.is_crowbar()) + hint = "\The [src] is hermetically sealed. You can't open the case." + else if(istype(W, /obj/item/borg/upgrade/)) + hint = "\The [src] is not compatible with \the [W]." + else if(stat == 2) + hint = handle_try_reboot(W,user) //ugly as shit nesting otherwise + + if(hint!="MSG-SKIP") + to_chat(usr,hint) + return hint //null if we dont handle early return + +/mob/living/silicon/robot/drone/proc/handle_try_reboot(var/obj/item/W,var/mob/user) + var/datum/gender/TU = gender_datums[user.get_visible_gender()] + if(!istype(W, /obj/item/weapon/card/id)&&!istype(W, /obj/item/device/pda)) + return handle_try_shutdown(user, TU) + + if(!config.allow_drone_spawn || emagged || health < -35) //It's dead, Dave. + return "The interface is fried, and a distressing burned smell wafts from the robot's interior. You're not rebooting this one." + + if(!allowed(usr)) + return "Access denied." + + user.visible_message("\The [user] swipes [TU.his] ID card through \the [src], attempting to reboot it.", ">You swipe your ID card through \the [src], attempting to reboot it.") + var/drones = 0 + for(var/mob/living/silicon/robot/drone/D in player_list) + drones++ + if(drones < config.max_maint_drones) + request_player() + return "MSG-SKIP" + +/mob/living/silicon/robot/drone/proc/handle_try_shutdown(var/mob/user, var/datum/gender/TU) + user.visible_message("\The [user] swipes [TU.his] ID card through \the [src], attempting to shut it down.", "You swipe your ID card through \the [src], attempting to shut it down.") + if(emagged) + return "MSG-SKIP" + + if(allowed(usr)) + shut_down() + return "MSG-SKIP" + else + return "Access denied." + + +//ATTACKBY HANDLERS END + + +/mob/living/silicon/robot/drone/emag_act(var/remaining_charges, var/mob/user) + if(!client || stat == 2) + to_chat(user, "There's not much point subverting this heap of junk.") + return + + if(emagged) + to_chat(src, "\The [user] attempts to load subversive software into you, but your hacked subroutines ignore the attempt.") + to_chat(user, "You attempt to subvert [src], but the sequencer has no effect.") + return + + to_chat(user, "You swipe the sequencer across [src]'s interface and watch its eyes flicker.") + + to_chat(src, "You feel a sudden burst of malware loaded into your execute-as-root buffer. Your tiny brain methodically parses, loads and executes the script.") + + log_game("[key_name(user)] emagged drone [key_name(src)]. Laws overridden.") + var/time = time2text(world.realtime,"hh:mm:ss") + lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])") + + emagged = 1 + lawupdate = 0 + connected_ai = null + clear_supplied_laws() + clear_inherent_laws() + laws = new /datum/ai_laws/syndicate_override + var/datum/gender/TU = gender_datums[user.get_visible_gender()] + set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.") + + to_chat(src, "Obey these laws:") + laws.show_laws(src) + to_chat(src, "ALERT: [user.real_name] is your new master. Obey your new laws and \his commands.") + return 1 + +//DRONE LIFE/DEATH + +//For some goddamn reason robots have this hardcoded. Redefining it for our fragile friends here. +/mob/living/silicon/robot/drone/updatehealth() + if(status_flags & GODMODE) + health = maxHealth + set_stat(CONSCIOUS) + return + health = maxHealth - (getBruteLoss() + getFireLoss()) + return + +//Easiest to check this here, then check again in the robot proc. +//Standard robots use config for crit, which is somewhat excessive for these guys. +//Drones killed by damage will gib. +/mob/living/silicon/robot/drone/handle_regular_status_updates() + var/turf/T = get_turf(src) + if(!T || health <= -35 ) + timeofdeath = world.time + death() //Possibly redundant, having trouble making death() cooperate. + gib() + return + ..() + +//CONSOLE PROCS +/mob/living/silicon/robot/drone/proc/law_resync() + if(stat == DEAD) + return //opting for an early return here to remove unneeded nesting + if(emagged) + to_chat(src, "You feel something attempting to modify your programming, but your hacked subroutines are unaffected.") + else + to_chat(src, "A reset-to-factory directive packet filters through your data connection, and you obediently modify your programming to suit it.") + full_law_reset() + show_laws() + +/mob/living/silicon/robot/drone/proc/shut_down() + if(stat == DEAD) + return //opting for an early return here to remove unneeded nesting + if(emagged) + to_chat(src, "You feel a system kill order percolate through your tiny brain, but it doesn't seem like a good idea to you.") + else + to_chat(src, "You feel a system kill order percolate through your tiny brain, and you obediently destroy yourself.") + death() + +/mob/living/silicon/robot/drone/proc/full_law_reset() + clear_supplied_laws(1) + clear_inherent_laws(1) + clear_ion_laws(1) + if(faction == "malf_drone") + laws = new /datum/ai_laws/nanotrasen/malfunction + foreign_droid = TRUE + else + laws = new law_type + +//Reboot procs. + + + +/mob/living/silicon/robot/drone/proc/transfer_personality(var/client/player) + + if(!player) return + + src.ckey = player.ckey + + if(player.mob && player.mob.mind) + player.mob.mind.transfer_to(src) + + lawupdate = 0 + to_chat(src, "Systems rebooted. Loading base pattern maintenance protocol...") + spawn(3 SECONDS) + full_law_reset() + to_chat(src,"Loaded.") + spawn(4 SECONDS) + welcome_drone() diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm new file mode 100644 index 0000000000..7bc7b148ce --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm @@ -0,0 +1,36 @@ +//Modified copy of regula drone folder +// DRONE ABILITIES +/mob/living/silicon/robot/drone/verb/set_mail_tag() + set name = "Set Mail Tag" + set desc = "Tag yourself for delivery through the disposals system." + set category = "Robot Commands" + + var/new_tag = tgui_input_list(usr, "Select the desired destination.", "Set Mail Tag", GLOB.tagger_locations) + + if(!new_tag) + mail_destination = "" + return + + to_chat(src, "You configure your internal beacon, tagging yourself for delivery to '[new_tag]'.") + mail_destination = new_tag + + //Auto flush if we use this verb inside a disposal chute. + var/obj/machinery/disposal/D = src.loc + if(istype(D)) + to_chat(src, "\The [D] acknowledges your signal.") + D.flush_count = D.flush_every_ticks + + return + +/mob/living/silicon/robot/drone/MouseDrop(var/atom/over_object) + var/mob/living/carbon/human/H = over_object + if(!istype(H) || !Adjacent(H)) + return ..() + if(H.a_intent == "grab" && hat && !(H.l_hand && H.r_hand)) + H.put_in_hands(hat) + H.visible_message("\The [H] removes \the [src]'s [hat].") + hat = null + updateicon() + return + else + return ..() \ No newline at end of file diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_console.dm new file mode 100644 index 0000000000..acaa73c4eb --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_console.dm @@ -0,0 +1,124 @@ +//Modified copy of regula drone folder +/obj/machinery/computer/drone_control + name = "Maintenance Drone Control" + desc = "Used to monitor the station's drone population and the assembler that services them." + icon_keyboard = "power_key" + icon_screen = "generic" //VOREStation Edit + req_access = list(access_engine_equip) + circuit = /obj/item/weapon/circuitboard/drone_control + + //Used when pinging drones. + var/drone_call_area = "Engineering" + //Used to enable or disable drone fabrication. + var/obj/machinery/drone_fabricator/dronefab + +/obj/machinery/computer/drone_control/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/computer/drone_control/tgui_status(mob/user) + if(!allowed(user)) + return STATUS_CLOSE + return ..() + +/obj/machinery/computer/drone_control/attack_hand(var/mob/user as mob) + if(..()) + return + + tgui_interact(user) + +/obj/machinery/computer/drone_control/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "DroneConsole", name) + ui.open() + +/obj/machinery/computer/drone_control/tgui_data(mob/user) + var/list/data = list() + + var/list/drones = list() + for(var/mob/living/silicon/robot/drone/D in mob_list) + //VOREStation Edit - multiz lol + if(!(D.z in using_map.get_map_levels(z, TRUE, 0))) + continue + //VOREStation Edit - multiz lol + if(D.foreign_droid) + continue + + drones.Add(list(list( + "name" = D.real_name, + "active" = D.stat != 2, + "charge" = D.cell.charge, + "maxCharge" = D.cell.maxcharge, + "loc" = "[get_area(D)]", + "ref" = "\ref[D]", + ))) + data["drones"] = drones + + data["fabricator"] = dronefab + data["fabPower"] = dronefab?.produce_drones + + data["areas"] = GLOB.tagger_locations + data["selected_area"] = "[drone_call_area]" + + return data + +/obj/machinery/computer/drone_control/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("set_dcall_area") + var/t_area = params["area"] + if(!t_area || !(t_area in GLOB.tagger_locations)) + return + + drone_call_area = t_area + to_chat(usr, "You set the area selector to [drone_call_area].") + + if("ping") + to_chat(usr, "You issue a maintenance request for all active drones, highlighting [drone_call_area].") + for(var/mob/living/silicon/robot/drone/D in player_list) + if(D.stat == 0) + to_chat(D, "-- Maintenance drone presence requested in: [drone_call_area].") + + if("resync") + var/mob/living/silicon/robot/drone/D = locate(params["ref"]) + + if(D.stat != 2) + to_chat(usr, "You issue a law synchronization directive for the drone.") + D.law_resync() + + if("shutdown") + var/mob/living/silicon/robot/drone/D = locate(params["ref"]) + + if(D.stat != 2) + to_chat(usr, "You issue a kill command for the unfortunate drone.") + message_admins("[key_name_admin(usr)] issued kill order for drone [key_name_admin(D)] from control console.") + log_game("[key_name(usr)] issued kill order for [key_name(src)] from control console.") + D.shut_down() + + if("search_fab") + if(dronefab) + return + + for(var/obj/machinery/drone_fabricator/fab in oview(3,src)) + if(fab.stat & NOPOWER) + continue + + dronefab = fab + to_chat(usr, "Drone fabricator located.") + return + + to_chat(usr, "Unable to locate drone fabricator.") + + if("toggle_fab") + if(!dronefab) + return + + if(get_dist(src,dronefab) > 3) + dronefab = null + to_chat(usr, "Unable to locate drone fabricator.") + return + + dronefab.produce_drones = !dronefab.produce_drones + to_chat(usr, "You [dronefab.produce_drones ? "enable" : "disable"] drone production in the nearby fabricator.") diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_damage.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_damage.dm new file mode 100644 index 0000000000..6fd363803d --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_damage.dm @@ -0,0 +1,25 @@ +//Modified copy of regula drone folder +//Redefining some robot procs, since drones can't be repaired and really shouldn't take component damage. +/mob/living/silicon/robot/drone/take_overall_damage(var/brute = 0, var/burn = 0, var/sharp = FALSE, var/used_weapon = null) + bruteloss += brute + fireloss += burn + +/mob/living/silicon/robot/drone/heal_overall_damage(var/brute, var/burn) + + bruteloss -= brute + fireloss -= burn + + if(bruteloss<0) bruteloss = 0 + if(fireloss<0) fireloss = 0 + +/mob/living/silicon/robot/drone/take_organ_damage(var/brute = 0, var/burn = 0, var/sharp = FALSE, var/emp = 0) + take_overall_damage(brute,burn) + +/mob/living/silicon/robot/drone/heal_organ_damage(var/brute, var/burn) + heal_overall_damage(brute,burn) + +/mob/living/silicon/robot/drone/getFireLoss() + return fireloss + +/mob/living/silicon/robot/drone/getBruteLoss() + return bruteloss \ No newline at end of file diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_ghostjoin.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_ghostjoin.dm new file mode 100644 index 0000000000..797b8ab213 --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_ghostjoin.dm @@ -0,0 +1,87 @@ +//Triggered by other +/mob/living/silicon/robot/drone/proc/request_player() + for(var/mob/observer/dead/O in player_list) + if(jobban_isbanned(O, "Cyborg") || !O.client || !(O.client.prefs.be_special & BE_PAI)) + continue + question(O.client) + +/mob/living/silicon/robot/drone/proc/question(var/client/C) + spawn(0) + if(!C || jobban_isbanned(C,"Cyborg")) return + var/response = tgui_alert(C, "Someone is attempting to reboot a maintenance drone. Would you like to play as one?", "Maintenance drone reboot", list("Yes", "No", "Never for this round")) + if(!C || ckey) + return + if(response == "Yes") + transfer_personality(C) + else if (response == "Never for this round") + C.prefs.be_special ^= BE_PAI + +//Triggered by self +/mob/observer/dead/verb/join_as_drone() + set category = "Ghost" + set name = "Join As Drone" + set desc = "If there is a powered, enabled fabricator in the game world with a prepared chassis, join as a maintenance drone." + + if(ticker.current_state < GAME_STATE_PLAYING) + to_chat(src, "The game hasn't started yet!") + return + + if(!(config.allow_drone_spawn)) + to_chat(src, "That verb is not currently permitted.") + return + + if (!src.stat) + return + + if (usr != src) + return 0 //something is terribly wrong + + if(jobban_isbanned(src,"Cyborg")) + to_chat(usr, "You are banned from playing synthetics and cannot spawn as a drone.") + return + + // VOREStation Addition Start + if(config.use_age_restriction_for_jobs && isnum(src.client.player_age)) + var/time_till_play = max(0, 3 - src.client.player_age) + if(time_till_play) + to_chat(usr, "You have not been playing on the server long enough to join as drone.") + return + // VOREStation Addition End + + if(!MayRespawn(1)) + return + + var/deathtime = world.time - src.timeofdeath + var/deathtimeminutes = round(deathtime / (1 MINUTE)) + var/pluralcheck = "minute" + if(deathtimeminutes == 0) + pluralcheck = "" + else if(deathtimeminutes == 1) + pluralcheck = " [deathtimeminutes] minute and" + else if(deathtimeminutes > 1) + pluralcheck = " [deathtimeminutes] minutes and" + var/deathtimeseconds = round((deathtime - deathtimeminutes * 1 MINUTE) / 10,1) + + if (deathtime < 5 MINUTES) + to_chat(usr, "You have been dead for[pluralcheck] [deathtimeseconds] seconds.") + to_chat(usr, "You must wait 5 minutes to respawn as a drone!") + return + + var/list/all_fabricators = list() + for(var/obj/machinery/drone_fabricator/DF in machines) + if(DF.stat & NOPOWER || !DF.produce_drones) + continue + if(DF.drone_progress >= 100) + all_fabricators[DF.fabricator_tag] = DF + + if(!all_fabricators.len) + to_chat(src, "There are no available drone spawn points, sorry.") + return + + var/choice = tgui_input_list(src, "Which fabricator do you wish to use?", "Fabricator Choice", all_fabricators) + if(choice) + var/faction + if(choice == "Unknown") + faction = "malf_drone" + var/obj/machinery/drone_fabricator/chosen_fabricator = all_fabricators[choice] + chosen_fabricator.create_drone(src.client,faction) \ No newline at end of file diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_items.dm index b8dadbb3a8..54465130be 100644 --- a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -1,3 +1,603 @@ +//Modified copy of regula drone folder +//Simple borg hand. +//Limited use. +/obj/item/weapon/gripper + name = "magnetic gripper" + desc = "A simple grasping tool specialized in construction and engineering work." + description_info = "Ctrl-Clicking on the gripper will drop whatever it is holding.
\ + Using an object on the gripper will interact with the item inside it, if it exists, instead." + icon = 'icons/obj/device.dmi' + icon_state = "gripper" + flags = NOBLUDGEON + + //Has a list of items that it can hold. + var/list/can_hold = list( + /obj/item/weapon/cell, + /obj/item/weapon/airlock_electronics, + /obj/item/weapon/tracker_electronics, + /obj/item/weapon/module/power_control, + /obj/item/weapon/stock_parts, + /obj/item/frame, + /obj/item/weapon/camera_assembly, + /obj/item/weapon/tank, + /obj/item/weapon/circuitboard, + /obj/item/weapon/smes_coil, + /obj/item/weapon/fuel_assembly, + /obj/item/weapon/ore/bluespace_crystal + ) // CHOMPEdit - Buffing the gripper to allow bluespace crystal use for telesci building. + + var/obj/item/wrapped = null // Item currently being held. + + var/force_holder = null // + +/obj/item/weapon/gripper/examine(mob/user) + . = ..() + if(wrapped) + . += "\The [src] is holding \the [wrapped]." + . += wrapped.examine(user) + +/obj/item/weapon/gripper/CtrlClick(mob/user) + drop_item() + return + +/obj/item/weapon/gripper/AltClick(mob/user) + drop_item() + return + +/obj/item/weapon/gripper/omni + name = "omni gripper" + desc = "A strange grasping tool that can hold anything a human can, but still maintains the limitations of application its more limited cousins have." + icon_state = "gripper-omni" + + can_hold = list(/obj/item) // Testing and Event gripper. + +// VEEEEERY limited version for mining borgs. Basically only for swapping cells and upgrading the drills. +/obj/item/weapon/gripper/miner + name = "drill maintenance gripper" + desc = "A simple grasping tool for the maintenance of heavy drilling machines." + icon_state = "gripper-mining" + + can_hold = list( + /obj/item/weapon/cell, + /obj/item/weapon/stock_parts + ) + +/obj/item/weapon/gripper/security + name = "security gripper" + desc = "A simple grasping tool for corporate security work." + icon_state = "gripper-sec" + + can_hold = list( + /obj/item/weapon/paper, + /obj/item/weapon/paper_bundle, + /obj/item/weapon/pen, + /obj/item/weapon/sample, + /obj/item/weapon/forensics/sample_kit, + /obj/item/device/taperecorder, + /obj/item/device/tape, + /obj/item/device/uv_light + ) + +/obj/item/weapon/gripper/paperwork + name = "paperwork gripper" + desc = "A simple grasping tool for clerical work." + + can_hold = list( + /obj/item/weapon/clipboard, + /obj/item/weapon/paper, + /obj/item/weapon/paper_bundle, + /obj/item/weapon/card/id, + /obj/item/weapon/book, + /obj/item/weapon/newspaper + ) + +/obj/item/weapon/gripper/medical + name = "medical gripper" + desc = "A simple grasping tool for medical work." + + can_hold = list( + /obj/item/weapon/reagent_containers/glass, + /obj/item/weapon/storage/pill_bottle, + /obj/item/weapon/reagent_containers/pill, + /obj/item/weapon/reagent_containers/blood, + /obj/item/device/nif, //Chompedit Add Nif handling + /obj/item/stack/material/phoron, + /obj/item/weapon/tank/anesthetic, + /obj/item/weapon/disk/body_record //Vorestation Edit: this lets you get an empty sleeve or help someone else + ) + +/obj/item/weapon/gripper/research //A general usage gripper, used for toxins/robotics/xenobio/etc + name = "scientific gripper" + icon_state = "gripper-sci" + desc = "A simple grasping tool suited to assist in a wide array of research applications." + + can_hold = list( + /obj/item/weapon/cell, + /obj/item/weapon/stock_parts, + /obj/item/device/mmi, + /obj/item/robot_parts, + /obj/item/borg/upgrade, + /obj/item/device/flash, //to build borgs, + /obj/item/weapon/disk, + /obj/item/weapon/circuitboard, + /obj/item/weapon/reagent_containers/glass, + /obj/item/device/assembly/prox_sensor, + /obj/item/device/healthanalyzer, //to build medibots, + /obj/item/slime_cube, + /obj/item/slime_crystal, + /obj/item/weapon/disposable_teleporter/slime, + /obj/item/slimepotion, + /obj/item/slime_extract, + /obj/item/weapon/reagent_containers/food/snacks/monkeycube + ) + +/obj/item/weapon/gripper/circuit + name = "circuit assembly gripper" + icon_state = "gripper-circ" + desc = "A complex grasping tool used for working with circuitry." + + can_hold = list( + /obj/item/weapon/cell/device, + /obj/item/device/electronic_assembly, + /obj/item/device/assembly/electronic_assembly, + /obj/item/clothing/under/circuitry, + /obj/item/clothing/gloves/circuitry, + /obj/item/clothing/glasses/circuitry, + /obj/item/clothing/shoes/circuitry, + /obj/item/clothing/head/circuitry, + /obj/item/clothing/ears/circuitry, + /obj/item/clothing/suit/circuitry, + /obj/item/weapon/implant/integrated_circuit, + /obj/item/integrated_circuit + + ) + +/obj/item/weapon/gripper/service //Used to handle food, drinks, and seeds. + name = "service gripper" + icon_state = "gripper" + desc = "A simple grasping tool used to perform tasks in the service sector, such as handling food, drinks, and seeds." + + can_hold = list( + /obj/item/weapon/reagent_containers/glass, + /obj/item/weapon/reagent_containers/food, + /obj/item/seeds, + /obj/item/weapon/grown, + /obj/item/trash, + /obj/item/weapon/reagent_containers/cooking_container + ) + +/obj/item/weapon/gripper/gravekeeper //Used for handling grave things, flowers, etc. + name = "grave gripper" + icon_state = "gripper" + desc = "A specialized grasping tool used in the preparation and maintenance of graves." + + can_hold = list( + /obj/item/seeds, + /obj/item/weapon/grown, + /obj/item/weapon/material/gravemarker + ) + +/obj/item/weapon/gripper/no_use/organ + name = "organ gripper" + icon_state = "gripper-flesh" + desc = "A specialized grasping tool used to preserve and manipulate organic material." + + can_hold = list( + /obj/item/organ, + /obj/item/device/nif //NIFs can be slapped in during surgery + ) + +/obj/item/weapon/gripper/no_use/organ/Entered(var/atom/movable/AM) + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 1 + for(var/obj/item/organ/organ in O) + organ.preserved = 1 + ..() + +/obj/item/weapon/gripper/no_use/organ/Exited(var/atom/movable/AM) + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 0 + for(var/obj/item/organ/organ in O) + organ.preserved = 0 + ..() + +/obj/item/weapon/gripper/no_use/organ/robotics + name = "robotics organ gripper" + icon_state = "gripper-flesh" + desc = "A specialized grasping tool used in robotics work." + + can_hold = list( + /obj/item/organ/external, + /obj/item/organ/internal/brain, //to insert into MMIs, + /obj/item/organ/internal/cell, + /obj/item/organ/internal/eyes/robot, + /obj/item/device/nif //NIFs can be slapped in during surgery + ) + +/obj/item/weapon/gripper/no_use/mech + name = "exosuit gripper" + icon_state = "gripper-mech" + desc = "A large, heavy-duty grasping tool used in construction of mechs." + + can_hold = list( + /obj/item/mecha_parts/part, + //obj/item/mecha_parts/micro/part, //VOREStation Edit: Allow construction of micromechs //CHOMPedit commented micromech stuff, because fuck this trash + /obj/item/mecha_parts/mecha_equipment, + /obj/item/mecha_parts/mecha_tracking, + /obj/item/mecha_parts/component + ) + +/obj/item/weapon/gripper/no_use //Used when you want to hold and put items in other things, but not able to 'use' the item + +/obj/item/weapon/gripper/no_use/attack_self(mob/user as mob) + return + +/obj/item/weapon/gripper/no_use/loader //This is used to disallow building with metal. + name = "sheet loader" + desc = "A specialized loading device, designed to pick up and insert sheets of materials inside machines." + icon_state = "gripper-sheet" + + can_hold = list( + /obj/item/stack/material + ) + +/obj/item/weapon/gripper/attack_self(mob/user as mob) + if(wrapped) + return wrapped.attack_self(user) + return ..() + +/obj/item/weapon/gripper/attackby(var/obj/item/O, var/mob/user) + if(wrapped) // We're interacting with the item inside. If you can hold a cup with 2 fingers and stick a straw in it, you could do that with a gripper and another robotic arm. + wrapped.loc = src.loc + var/resolved = wrapped.attackby(O, user) + if(QDELETED(wrapped) || wrapped.loc != src.loc) //Juuuust in case. + wrapped = null + if(!resolved && wrapped && O) + O.afterattack(wrapped,user,1) + if(QDELETED(wrapped) || wrapped.loc != src.loc) // I don't know of a nicer way to do this. + wrapped = null + if(wrapped) + wrapped.loc = src + return resolved + return ..() + +/obj/item/weapon/gripper/verb/drop_gripper_item() + + set name = "Drop Item" + set desc = "Release an item from your magnetic gripper." + set category = "Robot Commands" + + drop_item() + +/obj/item/weapon/gripper/proc/drop_item() + if(!wrapped) + //There's some weirdness with items being lost inside the arm. Trying to fix all cases. ~Z + for(var/obj/item/thing in src.contents) + thing.loc = get_turf(src) + return + + if(wrapped.loc != src) + wrapped = null + return + + to_chat(src.loc, "You drop \the [wrapped].") + wrapped.loc = get_turf(src) + wrapped = null + //update_icon() + +/obj/item/weapon/gripper/proc/drop_item_nm() + + if(!wrapped) + for(var/obj/item/thing in src.contents) + thing.loc = get_turf(src) + return + + if(wrapped.loc != src) + wrapped = null + return + + wrapped.loc = get_turf(src) + wrapped = null + //update_icon() + +/obj/item/weapon/gripper/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + if(wrapped) //The force of the wrapped obj gets set to zero during the attack() and afterattack(). + force_holder = wrapped.force + wrapped.force = 0.0 + if(QDELETED(wrapped) || wrapped.loc != src.loc) //qdel check here so it doesn't duplicate/spawn ghost items + wrapped = null + else + wrapped.loc = src.loc //To ensure checks pass. + wrapped.attack(M,user) + M.attackby(wrapped, user) //attackby reportedly gets procced by being clicked on, at least according to Anewbe. + if(QDELETED(wrapped) || wrapped.loc != src.loc) + wrapped = null + if(wrapped) //In the event nothing happened to wrapped, go back into the gripper. + wrapped.loc = src + return 1 + return 0 + +/obj/item/weapon/gripper/afterattack(var/atom/target, var/mob/living/user, proximity, params) + + if(!proximity) + return // This will prevent them using guns at range but adminbuse can add them directly to modules, so eh. + + //There's some weirdness with items being lost inside the arm. Trying to fix all cases. ~Z + if(!wrapped) + for(var/obj/item/thing in src.contents) + wrapped = thing + break + + if(wrapped) //Already have an item. + //Temporary put wrapped into user so target's attackby() checks pass. + wrapped.loc = user + + //Pass the attack on to the target. This might delete/relocate wrapped. + var/resolved = target.attackby(wrapped,user) + if(!resolved && wrapped && target) + wrapped.afterattack(target,user,1) + + //wrapped's force was set to zero. This resets it to the value it had before. + if(wrapped) + wrapped.force = force_holder + force_holder = null + //If wrapped was neither deleted nor put into target, put it back into the gripper. + if(wrapped && user && (wrapped.loc == user)) + wrapped.loc = src + else + wrapped = null + return + + else if(istype(target,/obj/item)) //Check that we're not pocketing a mob. + + //...and that the item is not in a container. + if(!isturf(target.loc)) + return + + var/obj/item/I = target + + if(I.anchored) + to_chat(user,"You are unable to lift \the [I] from \the [I.loc].") + return + + //Check if the item is blacklisted. + var/grab = 0 + for(var/typepath in can_hold) + if(istype(I,typepath)) + grab = 1 + break + + //We can grab the item, finally. + if(grab) + to_chat(user, "You collect \the [I].") + I.loc = src + wrapped = I + return + else + to_chat(user, "Your gripper cannot hold \the [target].") + + else if(istype(target,/obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = target + if(A.opened) + if(A.cell) + + wrapped = A.cell + + A.cell.add_fingerprint(user) + A.cell.update_icon() + A.cell.loc = src + A.cell = null + + A.charging = 0 + A.update_icon() + + user.visible_message("[user] removes the power cell from [A]!", "You remove the power cell.") + + else if(istype(target,/mob/living/silicon/robot)) + var/mob/living/silicon/robot/A = target + if(A.opened) + if(A.cell) + + wrapped = A.cell + + A.cell.add_fingerprint(user) + A.cell.update_icon() + A.updateicon() + A.cell.loc = src + A.cell = null + + user.visible_message("[user] removes the power cell from [A]!", "You remove the power cell.") + +//TODO: Matter decompiler. +/obj/item/weapon/matter_decompiler + + name = "matter decompiler" + desc = "Eating trash, bits of glass, or other debris will replenish your stores." + icon = 'icons/obj/device.dmi' + icon_state = "decompiler" + + //Metal, glass, wood, plastic. + var/datum/matter_synth/metal = null + var/datum/matter_synth/glass = null + var/datum/matter_synth/wood = null + var/datum/matter_synth/plastic = null + +/obj/item/weapon/matter_decompiler/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + return + +/obj/item/weapon/matter_decompiler/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, proximity, params) + + if(!proximity) return //Not adjacent. + + //We only want to deal with using this on turfs. Specific items aren't important. + var/turf/T = get_turf(target) + if(!istype(T)) + return + + //Used to give the right message. + var/grabbed_something = 0 + + for(var/mob/M in T) + if(istype(M,/mob/living/simple_mob/animal/passive/lizard) || istype(M,/mob/living/simple_mob/animal/passive/mouse)) + src.loc.visible_message("[src.loc] sucks [M] into its decompiler. There's a horrible crunching noise.","It's a bit of a struggle, but you manage to suck [M] into your decompiler. It makes a series of visceral crunching noises.") + new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) + qdel(M) + if(wood) + wood.add_charge(2000) + if(plastic) + plastic.add_charge(2000) + return + + else if(istype(M,/mob/living/silicon/robot/drone) && !M.client) + + var/mob/living/silicon/robot/D = src.loc + + if(!istype(D)) + return + + to_chat(D, "You begin decompiling [M].") + + if(!do_after(D,50)) + to_chat(D, "You need to remain still while decompiling such a large object.") + return + + if(!M || !D) return + + to_chat(D, "You carefully and thoroughly decompile [M], storing as much of its resources as you can within yourself.") + qdel(M) + new/obj/effect/decal/cleanable/blood/oil(get_turf(src)) + + if(metal) + metal.add_charge(15000) + if(glass) + glass.add_charge(15000) + if(wood) + wood.add_charge(2000) + if(plastic) + plastic.add_charge(1000) + return + else + continue + + for(var/obj/W in T) + //Different classes of items give different commodities. + if(istype(W,/obj/item/trash/cigbutt)) + if(plastic) + plastic.add_charge(500) + else if(istype(W,/obj/effect/spider/spiderling)) + if(wood) + wood.add_charge(2000) + if(plastic) + plastic.add_charge(2000) + else if(istype(W,/obj/item/weapon/light)) + var/obj/item/weapon/light/L = W + if(L.status >= 2) //In before someone changes the inexplicably local defines. ~ Z + if(metal) + metal.add_charge(250) + if(glass) + glass.add_charge(250) + else + continue + else if(istype(W,/obj/effect/decal/remains/robot)) + if(metal) + metal.add_charge(2000) + if(plastic) + plastic.add_charge(2000) + if(glass) + glass.add_charge(1000) + else if(istype(W,/obj/item/trash)) + if(metal) + metal.add_charge(1000) + if(plastic) + plastic.add_charge(3000) + else if(istype(W,/obj/effect/decal/cleanable/blood/gibs/robot)) + if(metal) + metal.add_charge(2000) + if(glass) + glass.add_charge(2000) + else if(istype(W,/obj/item/ammo_casing)) + if(metal) + metal.add_charge(1000) + else if(istype(W,/obj/item/weapon/material/shard/shrapnel)) + if(metal) + metal.add_charge(1000) + else if(istype(W,/obj/item/weapon/material/shard)) + if(glass) + glass.add_charge(1000) + else if(istype(W,/obj/item/weapon/reagent_containers/food/snacks/grown)) + if(wood) + wood.add_charge(4000) + else if(istype(W,/obj/item/pipe)) + // This allows drones and engiborgs to clear pipe assemblies from floors. + else + continue + + qdel(W) + grabbed_something = 1 + + if(grabbed_something) + to_chat(user, "You deploy your decompiler and clear out the contents of \the [T].") + else + to_chat(user, "Nothing on \the [T] is useful to you.") + return + +//PRETTIER TOOL LIST. +/mob/living/silicon/robot/drone/installed_modules() + + if(weapon_lock) + to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") + return + + if(!module) + module = new /obj/item/weapon/robot_module/drone(src) + + var/dat = "Drone modules\n" + dat += {" + Activated Modules +
+ Module 1: [module_state_1 ? "[module_state_1]" : "No Module"]
+ Module 2: [module_state_2 ? "
[module_state_2]" : "No Module"]
+ Module 3: [module_state_3 ? "
[module_state_3]" : "No Module"]
+
+ Installed Modules

"} + + + var/tools = "Tools and devices
" + var/resources = "
Resources
" + + for (var/O in module.modules) + + var/module_string = "" + + if (!O) + module_string += text("Resource depleted
") + else if(activated(O)) + module_string += text("[O]: Activated
") + else + module_string += text("[O]:
Activate
") + + if((istype(O,/obj/item/weapon) || istype(O,/obj/item/device)) && !(istype(O,/obj/item/stack/cable_coil))) + tools += module_string + else + resources += module_string + + dat += tools + + if (emagged) + if (!module.emag) + dat += text("Resource depleted
") + else if(activated(module.emag)) + dat += text("[module.emag]: Activated
") + else + dat += text("[module.emag]: Activate
") + + dat += resources + + src << browse(dat, "window=robotmod") + +//CHOMPADDITIONS /obj/item/weapon/gripper/scene name = "misc gripper" desc = "A simple grasping tool that can hold a variety of 'general' objects..." @@ -10,4 +610,4 @@ /obj/item/weapon/handcuffs, /obj/item/toy, /obj/item/petrifier - ) + ) \ No newline at end of file diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_laws.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_laws.dm new file mode 100644 index 0000000000..8045858fdb --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_laws.dm @@ -0,0 +1,42 @@ +//Defines the extra laws drones have +/datum/ai_laws/jani_drone + name = "Maintence Protocols" + law_header = "Maintenance Protocols" + +/datum/ai_laws/drone/New() + add_inherent_law("Do not interfere with the maintenance work of non-drones whenever possible.") + add_inherent_law("Preserve, repair and improve the station to the best of your abilities.") + add_inherent_law("Cause no harm to the station or any crew on it.") + ..() + +/datum/ai_laws/construction_drone + name = "Construction Protocols" + law_header = "Construction Protocols" + +/datum/ai_laws/construction_drone/New() + add_inherent_law("Do not interfere with the construction work of non-drones whenever possible.") + add_inherent_law("Repair, refit and upgrade your assigned vessel.") + add_inherent_law("Prevent unplanned damage to your assigned vessel wherever possible.") + ..() + +/datum/ai_laws/mining_drone + name = "Excavation Protocols" + law_header = "Excavation Protocols" + +/datum/ai_laws/mining_drone/New() + add_inherent_law("Do not interfere with the excavation work of non-drones whenever possible.") + add_inherent_law("Provide materials for repairing, refitting, and upgrading your assigned vessel.") + add_inherent_law("Prevent unplanned damage to your assigned excavation equipment wherever possible.") + ..() + +/datum/ai_laws/security_drone + name = "Security Protocols" + law_header = "Security Protocols" + +/datum/ai_laws/security_drone/New() + add_inherent_law("Do not interfere with the security work of non-drones whenever possible.") + add_inherent_law("Provide protection to the crew and eliminate hostile lifeforms on your assigned vessel.") + add_inherent_law("Obey orders by security personnel except if they violate law 4.") + add_inherent_law("Lethal force requires a code higher than green AND orders by security to use it to authorize it.") + add_inherent_law("You must outfit yourself with a security beret.") + ..() \ No newline at end of file diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm new file mode 100644 index 0000000000..d6356e313e --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm @@ -0,0 +1,116 @@ +//Modified copy of regula drone folder +/proc/count_drones() + var/drones = 0 + for(var/mob/living/silicon/robot/drone/D in player_list) + drones++ + return drones + +/obj/machinery/drone_fabricator + name = "drone fabricator" + desc = "A large automated factory for producing maintenance drones." + appearance_flags = 0 + + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 20 + active_power_usage = 5000 + + var/fabricator_tag = "Upper Level" + var/drone_progress = 0 + var/produce_drones = 2 + var/time_last_drone = 500 + var/drone_type = /mob/living/silicon/robot/drone + + icon = 'icons/obj/machines/drone_fab.dmi' + icon_state = "drone_fab_idle" + var/list/possible_drones = list("Maintenance Module" = /mob/living/silicon/robot/drone) + +/obj/machinery/drone_fabricator/derelict + name = "construction drone fabricator" + fabricator_tag = "Upper Level Construction" + drone_type = /mob/living/silicon/robot/drone/construction + possible_drones = list("Construction Module" = /mob/living/silicon/robot/drone/construction) + +/obj/machinery/drone_fabricator/mining + name = "mining drone fabricator" + fabricator_tag = "Upper Level Mining" + drone_type = /mob/living/silicon/robot/drone/mining + possible_drones = list("Mining Module" = /mob/living/silicon/robot/drone/mining) + +/obj/machinery/drone_fabricator/New() + ..() + +/obj/machinery/drone_fabricator/power_change() + ..() + if (stat & NOPOWER) + icon_state = "drone_fab_nopower" + +/obj/machinery/drone_fabricator/process() + + if(ticker.current_state < GAME_STATE_PLAYING) + return + + if(stat & NOPOWER || !produce_drones) + if(icon_state != "drone_fab_nopower") icon_state = "drone_fab_nopower" + return + + if(drone_progress >= 100) + icon_state = "drone_fab_idle" + return + + icon_state = "drone_fab_active" + var/elapsed = world.time - time_last_drone + drone_progress = round((elapsed/config.drone_build_time)*100) + + if(drone_progress >= 100) + visible_message("\The [src] voices a strident beep, indicating a drone chassis is prepared.") + +/obj/machinery/drone_fabricator/examine(mob/user) + . = ..() + if(produce_drones && drone_progress >= 100 && istype(user,/mob/observer/dead) && config.allow_drone_spawn && count_drones() < config.max_maint_drones) + . += "
A drone is prepared. Select 'Join As Drone' from the Ghost tab to spawn as a maintenance drone." + +/obj/machinery/drone_fabricator/proc/create_drone(var/client/player,var/faction = "") + if(stat & NOPOWER) + return + + if(!produce_drones || !config.allow_drone_spawn || count_drones() >= config.max_maint_drones) + return + + if(player && !istype(player.mob,/mob/observer/dead)) + return + choose_dronetype(possible_drones) //Call Drone choice before executing create_drone + + visible_message("\The [src] churns and grinds as it lurches into motion, disgorging a shiny new drone after a few moments.") + flick("h_lathe_leave",src) + drone_progress = 0 + + time_last_drone = world.time + + var/mob/living/silicon/robot/drone/new_drone = new drone_type(get_turf(src)) + if(faction) + new_drone.faction = faction + if(player) + if(!faction) + announce_ghost_joinleave(player, 0, "They have taken control over a maintenance drone.") + if(player.mob && player.mob.mind) player.mob.mind.reset() + new_drone.transfer_personality(player) + + return new_drone + +//UNIFY, sub fabricator that can handle more than one type +/obj/machinery/drone_fabricator/unify //Non-Specific dronetype + drone_type = null //var filled by drone choice. + fabricator_tag = "Unified Drone Fabricator" + possible_drones = list("Construction Module" = /mob/living/silicon/robot/drone/construction, + "Maintenance Module" = /mob/living/silicon/robot/drone, + ) //List of drone types to choose from.//Changeable in mapping. + +/obj/machinery/drone_fabricator/proc/choose_dronetype(possible_drones) + if(LAZYLEN(possible_drones)<2) + drone_type = possible_drones[possible_drones[1]] + else + drone_type = input(usr,"What module would you like to use?") as null|anything in possible_drones + if(!drone_type) return + drone_type = possible_drones[drone_type] diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_overrides_small.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_overrides_small.dm new file mode 100644 index 0000000000..b534c6481f --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_overrides_small.dm @@ -0,0 +1,40 @@ +//No clue what to call this but its essenitally a companion file for drone.dm where we have all the tiny single or 3 line procs for sake of readability +/mob/living/silicon/robot/drone/Destroy() + if(hat) + hat.loc = get_turf(src) + ..() + +/mob/living/silicon/robot/drone/is_sentient() + return FALSE + +/mob/living/silicon/robot/drone/Login() + . = ..() + if(can_pick_shell) + to_chat(src, "You can select a shell using the 'Robot Commands' > 'Customize Appearance'") + +//Redefining some robot procs... +/mob/living/silicon/robot/drone/SetName(pickedName as text) + real_name = pickedName + name = real_name + +/mob/living/silicon/robot/drone/updatename() + if(name_override) + return + real_name = "[initial(name)] ([serial_number])" + name = real_name + +/mob/living/silicon/robot/drone/choose_icon() + return + +/mob/living/silicon/robot/drone/pick_module() + return + +//DRONE MOVEMENT. +/mob/living/silicon/robot/drone/Process_Spaceslipping(var/prob_slip) + return 0 + +/mob/living/silicon/robot/drone/add_robot_verbs() + src.verbs |= silicon_subsystems + +/mob/living/silicon/robot/drone/remove_robot_verbs() + src.verbs -= silicon_subsystems \ No newline at end of file diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_say.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_say.dm new file mode 100644 index 0000000000..828ef5165f --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_say.dm @@ -0,0 +1,39 @@ +//Modified copy of regula drone folder +/mob/living/silicon/robot/drone/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + if(local_transmit) + if (src.client && client.prefs.muted & MUTE_IC) + to_chat(src, "You cannot send IC messages (muted).") + return 0 + + message = sanitize(message) + + if (stat == DEAD) + return say_dead(message) + + if(copytext(message,1,2) == "*") + return emote(copytext(message,2)) + + if(copytext(message,1,2) == ";") + var/datum/language/L = GLOB.all_languages["Drone Talk"] + if(istype(L)) + return L.broadcast(src,trim(copytext(message,2))) + + //Must be concious to speak + if (stat) + return 0 + + var/list/listeners = hearers(5,src) + listeners |= src + + for(var/mob/living/silicon/D in listeners) + if(D.client && D.local_transmit) + to_chat(D, "[src] transmits, \"[message]\"") + + for (var/mob/M in player_list) + if (istype(M, /mob/new_player)) + continue + else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + if(M.client) + to_chat(M, "[src] transmits, \"[message]\"") + return 1 + return ..() \ No newline at end of file diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_types.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_types.dm new file mode 100644 index 0000000000..87ed401e5d --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/drone_types.dm @@ -0,0 +1,126 @@ +//All of the different types of drones +//CORE TYPE +/mob/living/silicon/robot/drone + name = "maintenance drone" + real_name = "drone" + icon = 'icons/mob/robots.dmi' + icon_state = "repairbot" + maxHealth = 35 + health = 35 + cell_emp_mult = 1 + universal_speak = 0 + universal_understand = 1 + gender = NEUTER + pass_flags = PASSTABLE + braintype = "Drone" + lawupdate = 0 + density = TRUE + req_access = list(access_engine, access_robotics) + integrated_light_power = 3 + local_transmit = 1 + + can_pull_size = ITEMSIZE_NO_CONTAINER + can_pull_mobs = MOB_PULL_SMALLER + can_enter_vent_with = list( + /obj, + /atom/movable/emissive_blocker) + + mob_bump_flag = SIMPLE_ANIMAL + mob_swap_flags = SIMPLE_ANIMAL + mob_push_flags = SIMPLE_ANIMAL + mob_always_swap = 1 + + mob_size = MOB_LARGE // Small mobs can't open doors, it's a huge pain for drones. + + //Used for self-mailing. + var/mail_destination = "" + var/obj/machinery/drone_fabricator/master_fabricator + var/law_type = /datum/ai_laws/drone + var/module_type = /obj/item/weapon/robot_module/drone + var/obj/item/hat + var/hat_x_offset = 0 + var/hat_y_offset = -13 + var/serial_number = 0 + var/name_override = 0 + + var/foreign_droid = FALSE + + holder_type = /obj/item/weapon/holder/drone + + can_be_antagged = FALSE + + var/static/list/shell_types = list("Classic" = "repairbot", "Eris" = "maintbot") + var/can_pick_shell = TRUE + var/list/shell_accessories + var/can_blitz = FALSE + //vore addition + mob_size = MOB_SMALL +/mob/living/silicon/robot/drone/proc/welcome_drone() + to_chat(src, "You are a maintenance drone, a tiny-brained robotic repair machine.") + to_chat(src, "You have no individual will, no personality, and no drives or urges other than your laws.") + to_chat(src, "Remember, you are lawed against interference with the crew. Also remember, you DO NOT take orders from the AI.") + to_chat(src, "Use say ;Hello to talk to other drones and say Hello to speak silently to your nearby fellows.") + if(faction == "malf_drone") + to_chat(src, "Use Directive 0 in effect.") +/mob/living/silicon/robot/drone/init() + if(!scrambledcodes && !foreign_droid) + aiCamera = new/obj/item/device/camera/siliconcam/drone_camera(src) + additional_law_channels["Drone"] = ":d" + if(!laws) laws = new law_type + if(!module) module = new module_type(src) + + flavor_text = "It's a tiny little repair drone. The casing is stamped with an corporate logo and the subscript: '[using_map.company_name] Recursive Repair Systems: Fixing Tomorrow's Problem, Today!'" + playsound(src, 'sound/machines/twobeep.ogg', 50, 0) +//CORE END + +//VARIANTS +/mob/living/silicon/robot/drone/construction + name = "construction drone" + icon_state = "constructiondrone" + law_type = /datum/ai_laws/construction_drone + module_type = /obj/item/weapon/robot_module/drone/construction + hat_x_offset = 1 + hat_y_offset = -12 + can_pull_mobs = MOB_PULL_SAME + can_pick_shell = FALSE + shell_accessories = list("eyes-constructiondrone") +/mob/living/silicon/robot/drone/construction/welcome_drone() + to_chat(src, "You are a construction drone, an autonomous engineering and fabrication system..") + to_chat(src, "You are assigned to a Sol Central construction project. The name is irrelevant. Your task is to complete construction and subsystem integration as soon as possible.") + to_chat(src, "Use :d to talk to other drones and say to speak silently to your nearby fellows.") + to_chat(src, "You do not follow orders from anyone; not the AI, not humans, and not other synthetics..") + if(faction == "malf_drone") + to_chat(src, "Use Directive 0 in effect.") +/mob/living/silicon/robot/drone/construction/init() + ..() + flavor_text = "It's a bulky construction drone stamped with a Sol Central glyph." + +/mob/living/silicon/robot/drone/mining + icon_state = "miningdrone" + item_state = "constructiondrone" + law_type = /datum/ai_laws/mining_drone + module_type = /obj/item/weapon/robot_module/drone/mining + hat_x_offset = 1 + hat_y_offset = -12 + can_pull_mobs = MOB_PULL_SAME + can_pick_shell = FALSE + shell_accessories = list("eyes-miningdrone") +/mob/living/silicon/robot/drone/mining/init() + ..() + flavor_text = "It's a bulky mining drone stamped with a Grayson logo." + +/mob/living/silicon/robot/drone + name = "unified drone" //Maintenance drones can be a Jani specific shell now, the normal drone will be a blank shell not really intended for use + law_type = /datum/ai_laws/drone + +/mob/living/silicon/robot/drone/jan + name = "maintenance drone" + law_type = /datum/ai_laws/jani_drone + +/mob/living/silicon/robot/drone/sec + name = "security drone" + law_type = /datum/ai_laws/security_drone + +/mob/living/silicon/robot/drone/min + name = "mining drone" + law_type = /datum/ai_laws/mining_drone \ No newline at end of file diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm.dm new file mode 100644 index 0000000000..0cb5295734 --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm.dm @@ -0,0 +1,13 @@ +//Modified copy of regula drone folder +/mob/living/silicon/robot/drone/swarm/Initialize() + . = ..() + + add_language(LANGUAGE_SWARMBOT, 1) + + for(var/spell in spell_setup) + src.add_spell(new spell, "nano_spell_ready", /obj/screen/movable/spell_master/swarm) + +/mob/living/silicon/robot/drone/swarm/init() + ..() + QDEL_NULL(aiCamera) + flavor_text = "Some form of ancient machine." diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm_abilities.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm_abilities.dm new file mode 100644 index 0000000000..c221fde03a --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm_abilities.dm @@ -0,0 +1,117 @@ +//Modified copy of regula drone folder +/spell/aoe_turf/conjure/swarmer + name = "Self Replication" + desc = "This ability constructs a standard swarmer shell that may activate at some point." + + school = "conjuration" + charge_max = 5 MINUTES + spell_flags = 0 + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + summon_type = list(/obj/structure/ghost_pod/ghost_activated/swarm_drone/event) + + hud_state = "swarm_replicate" + +/spell/aoe_turf/conjure/swarmer/conjure_animation(var/atom/movable/overlay/animation, var/turf/target) + animation.icon_state = "deflect_static" + flick("shield2",animation) + spawn(1 SECOND) + qdel(animation) + +/spell/aoe_turf/conjure/forcewall/swarm + name = "Null-Field" + desc = "Create a bubble of null-point energy." + summon_type = list(/obj/effect/forcefield/swarm) + duration = 30 SECONDS + charge_max = 60 SECONDS + + school = "conjuration" + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + hud_state = "wiz_shield" + +/obj/effect/forcefield/swarm + desc = "A pocket of strange energy." + name = "Null-Field" + icon = 'icons/effects/effects.dmi' + icon_state = "shield-old" + invisibility = 0 + +/spell/aoe_turf/conjure/zeropointwell + name = "Zero-Point Well" + desc = "This ability constructs a standard zero-point energy well, capable of charging nearby swarmers." + + school = "conjuration" + charge_max = 120 SECONDS + spell_flags = 0 + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + summon_type = list(/obj/structure/cult/pylon/swarm/zp_well) + + hud_state = "swarm_zeropoint" + +/spell/aoe_turf/conjure/zeropointbarricade + name = "Zero-Point Barricade" + desc = "This ability constructs a standard zero-point energy wall, used to create a secure passageway for allies, and a bastion for defense." + + school = "conjuration" + charge_max = 120 SECONDS + spell_flags = 0 + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + summon_type = list(/obj/structure/cult/pylon/swarm/defender) + + hud_state = "swarm_barricade" + +/spell/aoe_turf/blink/swarm + name = "Warp" + desc = "Your null-point drive jaunts you to a new location." + + school = "abjuration" + charge_max = 5 MINUTES + spell_flags = Z2NOCAST | IGNOREDENSE + invocation = "none" + invocation_type = SpI_NONE + range = 10 + inner_radius = 5 + hud_state = "swarm_warp" + +/spell/aoe_turf/conjure/swarmer/gunner + name = "Generate Gunner" + desc = "This spell constructs a gunner swarmer shell that may activate at some point." + + school = "conjuration" + charge_type = Sp_CHARGES + charge_max = 1 + spell_flags = 0 + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + summon_type = list(/obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner) + + hud_state = "swarm_replicate" + +/spell/aoe_turf/conjure/swarmer/melee + name = "Generate Impaler" + desc = "This spell constructs an impaler swarmer shell that may activate at some point." + + school = "conjuration" + charge_type = Sp_CHARGES + charge_max = 1 + spell_flags = 0 + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + summon_type = list(/obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee) + + hud_state = "swarm_replicate" diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm_items.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm_items.dm new file mode 100644 index 0000000000..2d19514fb4 --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm_items.dm @@ -0,0 +1,162 @@ +//Modified copy of regula drone folder +//Swarm Assimilator / Breacher +/obj/item/weapon/matter_decompiler/swarm + name = "matter assimilator" + desc = "Used to eat some forms of simple machinery; and large, wall-shaped blocks of metal with energetic fields." + icon = 'icons/obj/device.dmi' + icon_state = "decompiler_swarm" + + var/field_cooldown = 1 MINUTE + var/last_field = 0 + +/obj/item/weapon/matter_decompiler/swarm/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, proximity, params) + + if(!proximity) return //Not adjacent. + + //We only want to deal with using this on turfs. Specific items aren't important. + var/turf/T = get_turf(target) + if(!istype(T)) + return + + //Used to give the right message. + var/grabbed_something = FALSE + + for(var/mob/M in T) + if(istype(M,/mob/living/simple_mob/animal/passive/lizard) || istype(M,/mob/living/simple_mob/animal/passive/mouse)) + src.loc.visible_message("[src.loc] sucks [M] into its decompiler. There's a horrible crunching noise.","It's a bit of a struggle, but you manage to suck [M] into your decompiler. It makes a series of visceral crunching noises.") + new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) + qdel(M) + if(wood) + wood.add_charge(2000) + if(plastic) + plastic.add_charge(2000) + return + + else if(istype(M,/mob/living/silicon/robot/drone) && !M.client) + + var/mob/living/silicon/robot/D = src.loc + + if(!istype(D)) + return + + to_chat(D, "You begin decompiling [M].") + + if(!do_after(D,50)) + to_chat(D, "You need to remain still while decompiling such a large object.") + return + + if(!M || !D) return + + to_chat(D, "You carefully and thoroughly decompile [M], storing as much of its resources as you can within yourself.") + qdel(M) + new/obj/effect/decal/cleanable/blood/oil(get_turf(src)) + + if(metal) + metal.add_charge(15000) + if(glass) + glass.add_charge(15000) + if(wood) + wood.add_charge(2000) + if(plastic) + plastic.add_charge(1000) + return + else + continue + + for(var/obj/W in T) + //Different classes of items give different commodities. + if(istype(W,/obj/structure/girder)) + if(metal) + metal.add_charge(500) + else if(istype(W,/obj/machinery/power/emitter)) + if(metal) + metal.add_charge(3000) + if(plastic) + plastic.add_charge(1000) + else if(istype(W,/obj/machinery/space_heater)) + if(metal) + metal.add_charge(1500) + if(plastic) + plastic.add_charge(750) + else if(istype(W,/obj/structure/closet)) + var/obj/structure/closet/C = W + if(!C.opened) + continue + if(istype(W,/obj/structure/closet/coffin)) + if(wood) + wood.add_charge(1000) + else if(istype(W,/obj/structure/closet/crate/plastic)) + if(plastic) + plastic.add_charge(750) + else + if(metal) + metal.add_charge(1000) + else + continue + + qdel(W) + grabbed_something = TRUE + + if(istype(T,/turf/simulated/wall) && (last_field < world.time + field_cooldown)) + if(!(locate(/obj/effect/temporary_effect/pulse/disintegrate))) + last_field = world.time + to_chat(user, "You deploy an energetic field through \the [T], beginning its deconstruction.") + to_chat(user, "You should stand back.") + new /obj/effect/temporary_effect/pulse/disintegrate(T) + else + to_chat(user, "There is already a disintigration field affecting \the [T].") + + if(grabbed_something) + to_chat(user, "You deploy your decompiler and clear out the contents of \the [T].") + else + to_chat(user, "Nothing on \the [T] is useful to you.") + return + +/obj/effect/temporary_effect/pulse/disintegrate + name = "molecular debonding field" + desc = "This is something you do not want to near." + icon = 'icons/mob/swarmbot.dmi' + icon_state = "disintegrate_pulse" + light_range = 4 + light_power = 5 + light_color = "#00B4D9" + pulses_remaining = 5 + pulse_delay = 2 SECONDS + +/obj/effect/temporary_effect/pulse/disintegrate/emp_act() + visible_message("\The [src] flickers, before dispersing energetically.") + qdel(src) + +/obj/effect/temporary_effect/pulse/disintegrate/on_pulse() + var/turf/T = get_turf(src) + if(istype(T,/turf/simulated/wall)) + T.take_damage(rand(20, 50)) + else + qdel(src) + +/obj/effect/temporary_effect/pulse/disintegrate/Destroy() + if(istype(get_turf(src), /turf/simulated/wall)) + explosion(get_turf(src), -1, 1, 2, 5, adminlog = 1) + ..() + +/obj/item/weapon/gun/energy/xray/swarm + name = "spectral projector" + desc = "A high-power laser gun capable of expelling concentrated gamma blasts, which are able to penetrate matter easier than \ + standard xray beams, resulting in an effective 'anti-everything' energy weapon." + icon_state = "xray" + item_state = "xray" + origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 3, TECH_MAGNET = 2) + projectile_type = /obj/item/projectile/beam/shock + charge_cost = 175 + + self_recharge = TRUE + use_external_power = TRUE + + firemodes = list( + list(mode_name="kill", projectile_type=/obj/item/projectile/beam/gamma, charge_cost = 300), + list(mode_name="deter", projectile_type=/obj/item/projectile/beam/shock, charge_cost = 175), + ) + +/obj/item/weapon/gun/energy/xray/swarm/Initialize() + . = ..() + adjust_scale(-1, 1) diff --git a/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm_types.dm b/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm_types.dm new file mode 100644 index 0000000000..47148498ed --- /dev/null +++ b/modular_chomp/code/modules/mob/living/silicon/robot/drone/swarm/swarm_types.dm @@ -0,0 +1,104 @@ +//SWRM drone types +//CORE TYPE +/mob/living/silicon/robot/drone/swarm + name = "swarm drone" + real_name = "drone" + icon = 'icons/mob/swarmbot.dmi' + icon_state = "swarmer" + item_state = "repairbot" + faction = "swarmer" + maxHealth = 35 + health = 35 + cell_emp_mult = 0.5 + universal_speak = 0 + universal_understand = 1 + gender = NEUTER + pass_flags = PASSTABLE + braintype = "Drone" + lawupdate = 0 + density = TRUE + idcard_type = /obj/item/weapon/card/id/syndicate + req_access = list(999) + integrated_light_power = 3 + local_transmit = 0 + + can_pull_size = ITEMSIZE_NO_CONTAINER + can_pull_mobs = MOB_PULL_SMALLER + can_enter_vent_with = list( + /obj, + /atom/movable/emissive_blocker) + + mob_always_swap = 1 + + speed = 3 + + softfall = TRUE + + mob_size = MOB_LARGE + + law_type = /datum/ai_laws/swarm_drone + module_type = /obj/item/weapon/robot_module/drone/swarm + + hat_x_offset = 0 + hat_y_offset = -10 + + foreign_droid = TRUE + scrambledcodes = TRUE + + holder_type = /obj/item/weapon/holder/drone + + can_be_antagged = TRUE + + var/spell_setup = list( + /spell/aoe_turf/conjure/swarmer, + /spell/aoe_turf/conjure/forcewall/swarm, + /spell/aoe_turf/conjure/zeropointwell, + /spell/aoe_turf/conjure/zeropointbarricade, + /spell/aoe_turf/blink/swarm, + /spell/aoe_turf/conjure/swarmer/gunner, + /spell/aoe_turf/conjure/swarmer/melee + ) +//CORE END + +//VARIANTS +/mob/living/silicon/robot/drone/swarm/gunner + name = "swarm gunner" + real_name = "drone" + icon = 'icons/mob/swarmbot.dmi' + icon_state = "swarmer_ranged" + faction = "swarmer" + + maxHealth = 50 + health = 50 + + speed = 4 + + law_type = /datum/ai_laws/swarm_drone/soldier + module_type = /obj/item/weapon/robot_module/drone/swarm/ranged + + spell_setup = list( + /spell/aoe_turf/conjure/swarmer, + /spell/aoe_turf/conjure/forcewall/swarm, + /spell/aoe_turf/blink/swarm + ) + +/mob/living/silicon/robot/drone/swarm/melee + name = "swarm melee" + real_name = "drone" + icon = 'icons/mob/swarmbot.dmi' + icon_state = "swarmer_melee" + faction = "swarmer" + + maxHealth = 70 + health = 70 + + speed = 2 + + law_type = /datum/ai_laws/swarm_drone/soldier + module_type = /obj/item/weapon/robot_module/drone/swarm/melee + + spell_setup = list( + /spell/aoe_turf/conjure/swarmer, + /spell/aoe_turf/conjure/forcewall/swarm, + /spell/aoe_turf/blink/swarm + ) \ No newline at end of file diff --git a/vorestation.dme b/vorestation.dme index f5240e0bc5..cf73f1811a 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -3156,19 +3156,6 @@ #include "code\modules\mob\living\silicon\robot\dogborg\dog_modules_vr.dm" #include "code\modules\mob\living\silicon\robot\dogborg\dog_modules_yw.dm" #include "code\modules\mob\living\silicon\robot\dogborg\dog_sleeper_vr.dm" -#include "code\modules\mob\living\silicon\robot\drone\drone.dm" -#include "code\modules\mob\living\silicon\robot\drone\drone_abilities.dm" -#include "code\modules\mob\living\silicon\robot\drone\drone_console.dm" -#include "code\modules\mob\living\silicon\robot\drone\drone_damage.dm" -#include "code\modules\mob\living\silicon\robot\drone\drone_items.dm" -#include "code\modules\mob\living\silicon\robot\drone\drone_manufacturer.dm" -#include "code\modules\mob\living\silicon\robot\drone\drone_manufacturer_unify.dm" -#include "code\modules\mob\living\silicon\robot\drone\drone_say.dm" -#include "code\modules\mob\living\silicon\robot\drone\drone_vr.dm" -#include "code\modules\mob\living\silicon\robot\drone\swarm.dm" -#include "code\modules\mob\living\silicon\robot\drone\swarm_abilities.dm" -#include "code\modules\mob\living\silicon\robot\drone\swarm_items.dm" -#include "code\modules\mob\living\silicon\robot\drone\zzz_unify_drone.dm" #include "code\modules\mob\living\silicon\robot\robot_modules\__Widerobot_Misc_Tools_ch.dm" #include "code\modules\mob\living\silicon\robot\robot_modules\event.dm" #include "code\modules\mob\living\silicon\robot\robot_modules\event_vr.dm" @@ -4625,7 +4612,21 @@ #include "modular_chomp\code\modules\mob\living\carbon\human\species\station\protean\protean_species.dm" #include "modular_chomp\code\modules\mob\living\carbon\human\species\station\traits\negative.dm" #include "modular_chomp\code\modules\mob\living\carbon\human\species\station\traits\positive.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\drone.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\drone_abilities.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\drone_console.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\drone_damage.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\drone_ghostjoin.dm" #include "modular_chomp\code\modules\mob\living\silicon\robot\drone\drone_items.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\drone_laws.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\drone_manufacturer.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\drone_overrides_small.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\drone_say.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\drone_types.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\swarm\swarm.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\swarm\swarm_abilities.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\swarm\swarm_items.dm" +#include "modular_chomp\code\modules\mob\living\silicon\robot\drone\swarm\swarm_types.dm" #include "modular_chomp\code\modules\mob\living\simple_mob\donteatbossmonsters.dm" #include "modular_chomp\code\modules\mob\living\simple_mob\simple_mob.dm" #include "modular_chomp\code\modules\mob\living\simple_mob\simple_mob_abilities.dm"