diff --git a/code/_onclick/hud/parallax/parallax.dm b/code/_onclick/hud/parallax/parallax.dm index 506226b41ead..eb8ea350f9f1 100644 --- a/code/_onclick/hud/parallax/parallax.dm +++ b/code/_onclick/hud/parallax/parallax.dm @@ -13,6 +13,7 @@ if(!length(C.parallax_layers_cached)) C.parallax_layers_cached = list() + C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/planet/unbidden(null, src) // NON-MODULE CHANGE C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_1(null, src) C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_2(null, src) C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/planet(null, src) @@ -22,7 +23,7 @@ C.parallax_layers = C.parallax_layers_cached.Copy() - if (length(C.parallax_layers) > C.parallax_layers_max) + if (length(C.parallax_layers) > C.parallax_layers_max + 1) // NON-MODULE CHANGE C.parallax_layers.len = C.parallax_layers_max C.screen |= (C.parallax_layers) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index d158a53effa4..d5982cce6566 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -133,6 +133,7 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list( /client/proc/summon_ert, /client/proc/toggle_nuke, /client/proc/toggle_random_events, + /client/proc/warfareEvent, // NON-MODULE CHANGE )) GLOBAL_PROTECT(admin_verbs_fun) GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /datum/admins/proc/podspawn_atom, /datum/admins/proc/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character, /datum/admins/proc/beaker_panel)) diff --git a/code/modules/meteors/meteor_types.dm b/code/modules/meteors/meteor_types.dm index c59a153b0223..05b7def45f4d 100644 --- a/code/modules/meteors/meteor_types.dm +++ b/code/modules/meteors/meteor_types.dm @@ -35,12 +35,20 @@ ///Used by Stray Meteor event to indicate meteor type (the type of sensor that "detected" it) in announcement var/signature = "motion" + //Used to determine if the meteor should be spinning. + var/spins = TRUE // NON-MODULE CHANGE + + //Used to determine if a player should be awarded an achievement for examining the meteor. + var/achievementworthy = TRUE // NON-MODULE CHANGE + /obj/effect/meteor/Initialize(mapload, turf/target) . = ..() z_original = z GLOB.meteor_list += src SSaugury.register_doom(src, threat) - SpinAnimation() + if(spins) // NON-MODULE CHANGE + SpinAnimation() + dest = target // NON-MODULE CHANGE chase_target(target) /obj/effect/meteor/Destroy() @@ -114,7 +122,8 @@ /obj/effect/meteor/examine(mob/user) . = ..() - check_examine_award(user) + if(achievementworthy) // NON-MODULE CHANGE + check_examine_award(user) /obj/effect/meteor/attackby(obj/item/I, mob/user, params) if(I.tool_behaviour == TOOL_MINING) diff --git a/maplestation.dme b/maplestation.dme index c8529538d58a..a20225737010 100644 --- a/maplestation.dme +++ b/maplestation.dme @@ -6093,6 +6093,7 @@ #include "maplestation_modules\code\__DEFINES\keybinding.dm" #include "maplestation_modules\code\__DEFINES\living.dm" #include "maplestation_modules\code\__DEFINES\mecha.dm" +#include "maplestation_modules\code\__DEFINES\mobfactions.dm" #include "maplestation_modules\code\__DEFINES\paperwork_defines.dm" #include "maplestation_modules\code\__DEFINES\signals.dm" #include "maplestation_modules\code\__DEFINES\spans.dm" @@ -6219,6 +6220,7 @@ #include "maplestation_modules\code\modules\admin\admin_vv.dm" #include "maplestation_modules\code\modules\admin\smites\pain_smite.dm" #include "maplestation_modules\code\modules\admin\smites\tabletide.dm" +#include "maplestation_modules\code\modules\admin\verbs\warfare_events.dm" #include "maplestation_modules\code\modules\antagonists\_common\advanced_antag.dm" #include "maplestation_modules\code\modules\antagonists\_common\advanced_objective.dm" #include "maplestation_modules\code\modules\antagonists\_common\advanced_traitor_panel.dm" @@ -6550,6 +6552,26 @@ #include "maplestation_modules\story_content\captain_equipment\code\captainclothing.dm" #include "maplestation_modules\story_content\casual_clothing\code\casualclothing.dm" #include "maplestation_modules\story_content\chaplain_equipment\code\chaplainclothing.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\artillery_shells.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\coilguns.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\curios.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\dreadactions.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\dreaditems.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\droneai.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\drones.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\kajari_beam.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\redcinematics.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\reddread.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\redeffects.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\redmaterials.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\redparts.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\redprojectiles.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\redreagents.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\redsetpieces.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\redturf.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\ship_parallax.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\shockattack.dm" +#include "maplestation_modules\story_content\deepred_warfare\code\singulo_warhead.dm" #include "maplestation_modules\story_content\grey_equipment\code\greyclothing.dm" #include "maplestation_modules\story_content\jessie_equipment\code\jessie_clothing.dm" #include "maplestation_modules\story_content\kimono\code\kimonoclothing.dm" diff --git a/maplestation_modules/code/__DEFINES/mobfactions.dm b/maplestation_modules/code/__DEFINES/mobfactions.dm new file mode 100644 index 000000000000..6a50a0d5fe4d --- /dev/null +++ b/maplestation_modules/code/__DEFINES/mobfactions.dm @@ -0,0 +1,2 @@ +/// All DEEP RED event machines. +#define FACTION_DEEPRED "deepred" diff --git a/maplestation_modules/code/modules/admin/verbs/warfare_events.dm b/maplestation_modules/code/modules/admin/verbs/warfare_events.dm new file mode 100644 index 000000000000..366294991273 --- /dev/null +++ b/maplestation_modules/code/modules/admin/verbs/warfare_events.dm @@ -0,0 +1,131 @@ +/client/proc/warfareEvent() + set name = "Warfare Module" + set desc = "Allows you to perform various actions related to warfare" + set category = "Admin.Events" + + var/datum/warfare_event/tgui = new(usr) + tgui.ui_interact(usr) + +/datum/warfare_event + var/client/holder //client of whoever is using this datum + var/list/selectedShells = list() //list of selected shells to fire (obj) + var/list/selectedNames = list() //list of selected shells to fire (but the name) + var/fireDirection = NORTH //default direction to fire shells (fires from top down) + +/datum/warfare_event/New(user)//user can either be a client or a mob due to byondcode(tm) + if (istype(user, /client)) + var/client/user_client = user + holder = user_client //if its a client, assign it to holder + else + var/mob/user_mob = user + holder = user_mob.client //if its a mob, assign the mob's client to holder + +/datum/warfare_event/ui_state(mob/user) + return GLOB.admin_state + +/datum/warfare_event/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "WarfareEvent") + ui.open() + +/datum/warfare_event/ui_data(mob/user) + var/list/data = list() + data["selectedNames"] = selectedNames + return data + +/datum/warfare_event/ui_act(action, params) + if(..()) + return + switch(action) + if("addShell") + var/selected = params["selected"] + switch(selected) + if("460mm Rocket Assisted AP") + selectedShells += /obj/effect/meteor/shell/big_ap + selectedNames += "460mm Rocket Assisted AP" + . = TRUE + if("160mm Rocket Assisted AP") + selectedShells += /obj/effect/meteor/shell/small_ap + selectedNames += "160mm Rocket Assisted AP" + . = TRUE + if("160mm HE") + selectedShells += /obj/effect/meteor/shell/small_wmd_he + selectedNames += "160mm HE" + . = TRUE + if("160mm Flak") + selectedShells += /obj/effect/meteor/shell/small_wmd_flak + selectedNames += "160mm Flak" + . = TRUE + if("160mm Cluster AP") + selectedShells += /obj/effect/meteor/shell/small_cluster_ap + selectedNames += "160mm Cluster AP" + . = TRUE + if("460mm Cluster HE") + selectedShells += /obj/effect/meteor/shell/big_cluster_wmd_he + selectedNames += "460mm Cluster HE" + . = TRUE + if("460mm Cluster Flak") + selectedShells += /obj/effect/meteor/shell/big_cluster_wmd_flak + selectedNames += "460mm Cluster Flak" + . = TRUE + if("WMD KAJARI") + selectedShells += /obj/effect/meteor/shell/kajari + selectedNames += "WMD KAJARI" + . = TRUE + if("removeShell") + var/selected = params["selected"] + switch(selected) + if("460mm Rocket Assisted AP") + selectedShells -= /obj/effect/meteor/shell/big_ap + selectedNames -= "460mm Rocket Assisted AP" + . = TRUE + if("160mm Rocket Assisted AP") + selectedShells -= /obj/effect/meteor/shell/small_ap + selectedNames -= "160mm Rocket Assisted AP" + . = TRUE + if("160mm HE") + selectedShells -= /obj/effect/meteor/shell/small_wmd_he + selectedNames -= "160mm HE" + . = TRUE + if("160mm Flak") + selectedShells -= /obj/effect/meteor/shell/small_wmd_flak + selectedNames -= "160mm Flak" + . = TRUE + if("160mm Cluster AP") + selectedShells -= /obj/effect/meteor/shell/small_cluster_ap + selectedNames -= "160mm Cluster AP" + . = TRUE + if("460mm Cluster HE") + selectedShells -= /obj/effect/meteor/shell/big_cluster_wmd_he + selectedNames -= "460mm Cluster HE" + . = TRUE + if("460mm Cluster Flak") + selectedShells -= /obj/effect/meteor/shell/big_cluster_wmd_flak + selectedNames -= "460mm Cluster Flak" + . = TRUE + if("WMD KAJARI") + selectedShells -= /obj/effect/meteor/shell/kajari + selectedNames -= "WMD KAJARI" + . = TRUE + if("changeDirection") + var/direction = params["direction"] + switch(direction) + if("North") + fireDirection = 1 + if("South") + fireDirection = 2 + if("East") + fireDirection = 4 + if("West") + fireDirection = 8 + . = TRUE + if("fireShells") + for(var/shell in selectedShells) + var/list/chosenList = list() + chosenList[shell] = 1 + spawn_meteor(chosenList, fireDirection, null) + selectedShells -= shell + for(var/name in selectedNames) + selectedNames -= name + . = TRUE diff --git a/maplestation_modules/story_content/README.md b/maplestation_modules/story_content/README.md index b0a289f26132..8bc622904685 100644 --- a/maplestation_modules/story_content/README.md +++ b/maplestation_modules/story_content/README.md @@ -8,3 +8,7 @@ All individual pieces of story-only content are to be stored in their own folder - FILE - PROC/TYPE - RELATED STORY - code\game\objects\effects\contraband.dm - proc/randomise(base_type) - Modular posters in general +- code\modules\admin\admin_verbs.dm - admin_verbs_fun - Adds the Warfare GUI to the admin panel +- code\modules\meteors\meteor_types.dm - obj/effect/meteor - Changes meteor spinning and achievement +- code\modules\meteors\meteor_types.dm - obj/effect/meteor/Initialize(mapload, turf/target) - Changes meteor spinning and fixes the destination code +- code\modules\meteors\meteor_types.dm - obj/effect/meteor/examine(mob/user) - Changes meteor achievement diff --git a/maplestation_modules/story_content/deepred_warfare/code/artillery_shells.dm b/maplestation_modules/story_content/deepred_warfare/code/artillery_shells.dm new file mode 100644 index 000000000000..befee0bdcb7d --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/artillery_shells.dm @@ -0,0 +1,279 @@ +/obj/effect/meteor/shell + name = "generic artillery shell" + desc = "A generic artillery shell." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/arty_shell.dmi' + icon_state = "rocket_ap_big" + //I'd expect shells to made out of plasteel or something. + meteordrop = list(/obj/item/stack/sheet/plasteel) + dropamt = 4 + //Mostly everything about this is the same as the meteor. + //Except for the fact that it's not a meteor. + spins = FALSE + achievementworthy = FALSE + //Shows the shell over the trail. + layer = ABOVE_ALL_MOB_LAYER + density = FALSE + +/obj/effect/meteor/shell/big_ap + name = "460mm rocket assisted AP shell" + desc = "A rocket assisted armour piercing shell, designed to cut through the heaviest of armour. You should probably get out of the way." + icon_state = "rocket_ap_big" + //Pierces a lot of hull. + hits = 8 + //Will fuck you up if you get hit by it. + hitpwr = EXPLODE_DEVASTATE + //Made of very hard materials. + meteordrop = list(/obj/item/stack/sheet/mineral/plastitanium) + //For rocket assisted shells. + var/fuel = 100 + //Adds speed to the shells. + var/speedfuel = 10 + +/obj/effect/meteor/shell/big_ap/Move() + . = ..() + if(. && fuel > 0) + //Fire trail, because it's rocket assisted. + new /obj/effect/temp_visual/fire(get_turf(src)) + fuel-- + if(. && speedfuel > 0) + //Converts speedfuel to hits. + speedfuel-- + hits++ + +/obj/effect/meteor/shell/big_ap/meteor_effect() + ..() + //Detonate fuel. + if(fuel > 0) + explosion(src, heavy_impact_range = 2, light_impact_range = 3, flash_range = 4, adminlog = FALSE) + +/obj/effect/meteor/shell/small_ap + name = "160mm rocket assisted AP shell" + desc = "A small rocket assisted armour piercing shell, designed to cut through armour. You should probably get out of the way." + icon_state = "rocket_ap_small" + hits = 4 + meteordrop = list(/obj/item/stack/sheet/mineral/plastitanium) + dropamt = 2 + var/fuel = 60 + var/speedfuel = 6 + +/obj/effect/meteor/shell/small_ap/Move() + . = ..() + if(. && fuel > 0) + new /obj/effect/temp_visual/fire(get_turf(src)) + fuel-- + if(. && speedfuel > 0) + speedfuel-- + hits++ + +/obj/effect/meteor/shell/small_ap/meteor_effect() + ..() + if(fuel > 0) + explosion(src, light_impact_range = 1, flash_range = 2, adminlog = FALSE) + +/obj/effect/meteor/shell/small_wmd_he + name = "160mm WMD singularity explosive shell" + desc = "A small WMD explosive singularity shell, designed to annihilate anything in its path. You should probably run far away." + icon_state = "he_wmd_small" + hits = 2 + hitpwr = EXPLODE_LIGHT + dropamt = 2 + heavy = TRUE + +/obj/effect/meteor/shell/small_wmd_he/meteor_effect() + ..() + new /obj/effect/temp_visual/space_explosion(get_turf(src)) + new /obj/effect/singulo_warhead(get_turf(src)) + +/obj/effect/meteor/shell/small_wmd_flak + name = "160mm tuned singularity flak shell" + desc = "A small WMD flak singularity shell, designed for explosive area denial. You should probably run far away." + icon_state = "flak_wmd_small" + hits = 2 + hitpwr = EXPLODE_LIGHT + dropamt = 2 + heavy = TRUE + +/obj/effect/meteor/shell/small_wmd_flak/meteor_effect() + ..() + new /obj/effect/temp_visual/space_explosion(get_turf(src)) + new /obj/effect/singulo_warhead/tuned(get_turf(src)) + +/obj/effect/meteor/shell/small_cluster_ap + name = "160mm cluster AP shell" + desc = "A small cluster armour piercing shell, designed to deploy a large amount of AP submunitions. You should probably watch out for submunitions." + icon_state = "cluster_ap_small" + hits = 4 + dropamt = 2 + //Fun fact: Canon is 3x this amount. + var/submunitions = 64 + //Deploys this many submunitions per step. + var/deployment_rate = 8 + +/obj/effect/meteor/shell/small_cluster_ap/Move() + var/deploys_left = deployment_rate + . = ..() + if(. && submunitions > 0) + while(deploys_left > 0) + var/start_turf = get_turf(src) + var/turf/destination = spaceDebrisStartLoc(dir, z) //This might shit submunitions all over the place if it is moving diagonally. + new /obj/effect/meteor/shell/tiny_ap_submunition(start_turf, destination) + submunitions-- + deploys_left-- + +/obj/effect/meteor/shell/tiny_ap_submunition + name = "AP submunition" + desc = "A small armour piercing submunition, designed for area denial. You should probably watch out for more." + icon_state = "sub_ap" + hits = 2 + hitpwr = EXPLODE_LIGHT + dropamt = 0 + var/start_delay = TRUE + var/actual_delay = 0 + +/obj/effect/meteor/shell/tiny_ap_submunition/Move() + if(start_delay) + start_delay = FALSE + actual_delay = rand(0, 6) + + if(actual_delay > 0) + actual_delay-- + return FALSE + + . = ..() + +/obj/effect/meteor/shell/big_cluster_wmd_he + name = "460mm cluster WMD singularity explosive shell" + desc = "A cluster WMD explosive singularity shell, designed to deploy a large amount of WMDs. You should probably watch out for submunitions." + icon_state = "cluster_wmd_big" + hits = 4 + //Fun fact: Canon is 2x this amount. + var/submunitions = 12 + //Deploys this many submunitions per step. + var/deployment_rate = 2 + +/obj/effect/meteor/shell/big_cluster_wmd_he/Move() + var/deploys_left = deployment_rate + . = ..() + if(. && submunitions > 0) + while(deploys_left > 0) + var/start_turf = get_turf(src) + var/turf/destination = spaceDebrisStartLoc(dir, z) //This might shit submunitions all over the place if it is moving diagonally. + new /obj/effect/meteor/shell/small_wmd_he_submunition(start_turf, destination) + submunitions-- + deploys_left-- + +/obj/effect/meteor/shell/small_wmd_he_submunition + name = "explosive WMD submunition" + desc = "A small explosive WMD submunition, designed to annihilate anything in its path. You should probably watch out for more." + icon_state = "sub_wmd_he" + hits = 1 + hitpwr = EXPLODE_LIGHT + dropamt = 0 + var/start_delay = TRUE + var/actual_delay = 0 + +/obj/effect/meteor/shell/small_wmd_he_submunition/Move() + if(start_delay) + start_delay = FALSE + actual_delay = rand(0, 10) + + if(actual_delay > 0) + actual_delay-- + return FALSE + + . = ..() + +/obj/effect/meteor/shell/small_wmd_he_submunition/meteor_effect() + ..() + new /obj/effect/temp_visual/space_explosion(get_turf(src)) + new /obj/effect/singulo_warhead/cluster(get_turf(src)) + +/obj/effect/meteor/shell/big_cluster_wmd_flak + name = "460mm cluster WMD singularity flak shell" + desc = "A cluster WMD flak singularity shell, designed to deploy a large amount of WMDs. You should probably watch out for submunitions." + icon_state = "cluster_wmd_big" //Deployer looks the same as the HE variant. + hits = 4 + //Fun fact: Canon is 2x this amount. + var/submunitions = 12 + //Deploys this many submunitions per step. + var/deployment_rate = 2 + +/obj/effect/meteor/shell/big_cluster_wmd_flak/Move() + var/deploys_left = deployment_rate + . = ..() + if(. && submunitions > 0) + while(deploys_left > 0) + var/start_turf = get_turf(src) + var/turf/destination = spaceDebrisStartLoc(dir, z) //This might shit submunitions all over the place if it is moving diagonally. + new /obj/effect/meteor/shell/small_wmd_flak_submunition(start_turf, destination) + submunitions-- + deploys_left-- + +/obj/effect/meteor/shell/small_wmd_flak_submunition + name = "flak WMD submunition" + desc = "A small flak WMD submunition, designed for area denial. You should probably watch out for more." + icon_state = "sub_wmd_flak" + hits = 1 + hitpwr = EXPLODE_LIGHT + dropamt = 0 + var/start_delay = TRUE + var/actual_delay = 0 + +/obj/effect/meteor/shell/small_wmd_flak_submunition/Move() + if(start_delay) + start_delay = FALSE + actual_delay = rand(0, 10) + + if(actual_delay > 0) + actual_delay-- + return FALSE + + . = ..() + +/obj/effect/meteor/shell/small_wmd_flak_submunition/meteor_effect() + ..() + new /obj/effect/temp_visual/space_explosion(get_turf(src)) + new /obj/effect/singulo_warhead/tuned_cluster(get_turf(src)) + +/obj/effect/meteor/shell/kajari + name = "460mm KAJARI WMD shell" + desc = "A KAJARI WMD shell, designed to project an incredibly destructive plasma lance. You should consider leaving while you still can." + icon_state = "kajari_big" + hits = 8 + hitpwr = EXPLODE_DEVASTATE + //How long until we fire the KAJARI beam. + var/fuse = 6 + //Holy shitcode. + var/done = FALSE + +/obj/effect/meteor/shell/kajari/Initialize(mapload, turf/target) + . = ..() + //Forces the station to red alert when this thing is fired. + var/current_sec_level = SSsecurity_level.get_current_level_as_number() + if(current_sec_level < SEC_LEVEL_RED) + SSsecurity_level.set_level(SEC_LEVEL_RED) + +/obj/effect/meteor/shell/kajari/Move() + if(done) + return + . = ..() + if(fuse <= 0) + done = TRUE + make_debris() + meteor_effect() + QDEL_IN(src, 9 SECONDS) + else + fuse-- + +/obj/effect/meteor/shell/kajari/meteor_effect() + ..() + new /obj/effect/temp_visual/space_explosion(get_turf(src)) + new /obj/effect/singulo_warhead(get_turf(src)) + addtimer(CALLBACK(src, PROC_REF(fire_beam)), 8 SECONDS) + +/obj/effect/meteor/shell/kajari/proc/fire_beam() + var/obj/projectile/A = new /obj/projectile/kajari_lance/hitscan(get_turf(src)) + A.preparePixelProjectile(dest, get_turf(src)) + A.firer = src + A.fired_from = src + A.fire(null, dest) diff --git a/maplestation_modules/story_content/deepred_warfare/code/coilguns.dm b/maplestation_modules/story_content/deepred_warfare/code/coilguns.dm new file mode 100644 index 000000000000..75d098feaa6e --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/coilguns.dm @@ -0,0 +1,460 @@ +/obj/item/gun/coilgun + name = "abstract coilgun" + desc = "You should not be seeing this." + desc_controls = "Accepts cells to refill energy, RCD compressed matter or sheets to refill matter, and coilcores to swap ammunition selections. Matter is automatically converted to ammunition and stored. Empty hand on gun to eject cell. Alt click to eject coilcore. Use in hand to switch fire modes." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/coilguns.dmi' + icon_state = "debug" + w_class = WEIGHT_CLASS_NORMAL + drop_sound = 'maplestation_modules/sound/items/drop/gun.ogg' + pickup_sound = 'maplestation_modules/sound/items/pickup/gun.ogg' + equip_sound = 'maplestation_modules/sound/items/drop/gun.ogg' + weapon_weight = WEAPON_MEDIUM + + // var/list/ammo_type = list(/obj/item/ammo_casing/coil, /obj/item/ammo_casing/coil/highvelo) // Different ammo selections (add meltdown later). + var/obj/item/coilcore/internalcore // Current core of the gun. + var/obj/item/coilcore/coretype = /obj/item/coilcore/revolver // Type of core accepted by the gun. + var/obj/item/coilcore/defaultcore = /obj/item/coilcore/revolver // Default core of the gun. + var/select = 1 // Current ammo selection. + + var/max_capacity = 10 // How many shots can be stored. + var/shots_stored = 10 // Current shots stored. + + var/max_matter = 200 // How much matter it can hold. + var/matter = 200 // Current matter. + var/matter_usage = 5 // How much matter it uses to make a shot. + var/fabricator_speed = 4 // How many seconds it takes to make a shot and put it into the stored shots. + var/fabricator_progress = 0 // How far along the fabricator is (in seconds). + + var/obj/item/stock_parts/cell/internalcell // Current cell of the gun. + var/obj/item/stock_parts/cell/defaultcell = /obj/item/stock_parts/cell/redtech // Default cell of the gun. + var/charge_sections = 10 // How many sections the charge overlay has. + var/charge_state // What sprite to use for the charge overlay. + + var/maximum_heat = 200 // How hot the gun can get. + var/dangerous_heat = 100 // When the gun starts to get dangerous. + var/current_heat = 0 // Current heat. + var/heat_dissipation = 5 // How much heat is dissipated per second (multiplied by 2x when in overcooling). + var/overcooling_speed = 6 // How many seconds it takes to overcool the gun. + var/overcooling_progress = 0 // How long the gun has been not firing (in seconds) for overcooling. + var/heat_sections = 10 // How many sections the heat overlay has. + var/heat_state // What sprite to use for the heat overlay. + + var/recoil_multiplier = 0.01 // How much recoil is multiplied by heat (IE: mult of 0.01 x 100 heat = 1 recoil added to gun). + var/heat_damage_multiplier = 0.5 // How much self damage is multiplied by heat (IE: mult of 0.5 x (200 heat - 100 dangerous heat) = 50 self damage) (always 0 self damage at exactly dangerous heat or below). + + var/load_sound = 'sound/weapons/gun/general/magazine_insert_full.ogg' // Inserting new cell sound. + var/load_sound_volume = 40 // Volume of the sound. + var/eject_sound = 'sound/weapons/gun/general/magazine_remove_full.ogg' // Ejecting cell sound. + var/eject_sound_volume = 40 // Volume of the sound. + var/regen_sound = 'sound/weapons/gun/general/magazine_insert_empty.ogg' // Regenerating sound. + var/regen_sound_volume = 40 // Volume of the sound. + var/overcooling_sound = 'sound/machines/clockcult/steam_whoosh.ogg' // Overcooling sound. + var/overcooling_sound_volume = 40 // Volume of the sound. + var/selecting_sound = 'maplestation_modules/story_content/deepred_warfare/sound/beep.ogg' // Selecting sound. + var/selecting_sound_volume = 40 // Volume of the sound. + + dry_fire_sound = 'sound/machines/terminal_error.ogg' // Sound when trying to shoot with no ammo. + +/obj/item/gun/coilgun/Initialize(mapload) + . = ..() + if(defaultcell) + internalcell = new defaultcell(src) + if(defaultcore) + internalcore = new defaultcore(src) + var/obj/item/ammo_casing/coil/shot = internalcore.ammunition_types[1] + fire_sound = shot.fire_sound + fire_delay = shot.delay + START_PROCESSING(SSobj, src) + +/obj/item/gun/coilgun/Destroy() + if (internalcell) + QDEL_NULL(internalcell) + if (internalcore) + QDEL_NULL(internalcore) + STOP_PROCESSING(SSobj, src) + + return ..() + +/obj/item/gun/coilgun/examine(mob/user) + . = ..() + + if(internalcore) + . += "It has \a [internalcore] loaded in its core slot." + . += "It has [shots_stored] slugs stored in its internal cylinder out of a maximum of [max_capacity] slugs." + else + . += "It does not have a core loaded in its core slot." + + if(internalcell) + . += "It has \a [internalcell] loaded in its cell port." + . += "It has [internalcell.charge] charge remaining." + else + . += "It does not have a cell loaded in its cell port." + + . += "It has [matter] matter stored in its matter storage out of a maximum of [max_matter] matter." + +/obj/item/gun/coilgun/add_weapon_description() + AddElement(/datum/element/weapon_description, attached_proc = PROC_REF(add_notes_coil)) + +/obj/item/gun/coilgun/proc/add_notes_coil() + var/list/readout = list() + // No core installed. + if(!internalcore) + return + // Make sure there is something to actually retrieve. + if(!internalcore.ammunition_types.len) + return + var/obj/projectile/exam_proj + readout += "\nStandard models of this projectile weapon have [span_warning("[internalcore.ammunition_types.len] mode\s")]." + readout += "Master Of None testing has shown that the average target can theoretically stay standing after..." + if(projectile_damage_multiplier <= 0) + readout += "a theoretically infinite number of shots on [span_warning("every")] mode due to esoteric or nonexistent offensive potential." + return readout.Join("\n") // Sending over the singular string, rather than the whole list + for(var/obj/item/ammo_casing/coil/for_ammo as anything in internalcore.ammunition_types) + exam_proj = for_ammo.projectile_type + if(!ispath(exam_proj)) + continue + if(initial(exam_proj.damage) > 0) // Don't divide by 0!!!!! + readout += "[span_warning("[HITS_TO_CRIT((initial(exam_proj.damage) * projectile_damage_multiplier) * for_ammo.pellets)] shot\s")] on [span_warning("[for_ammo.select_name]")] mode before collapsing from [initial(exam_proj.damage_type) == STAMINA ? "immense pain" : "their wounds"]." + if(initial(exam_proj.stamina) > 0) // In case a projectile does damage AND stamina damage (Energy Crossbow) + readout += "[span_warning("[HITS_TO_CRIT((initial(exam_proj.stamina) * projectile_damage_multiplier) * for_ammo.pellets)] shot\s")] on [span_warning("[for_ammo.select_name]")] mode before collapsing from immense pain." + else + readout += "a theoretically infinite number of shots on [span_warning("[for_ammo.select_name]")] mode." + + return readout.Join("\n") // Sending over the singular string, rather than the whole list + +/obj/item/gun/coilgun/process(seconds_per_tick) + if(matter >= matter_usage && shots_stored < max_capacity && internalcore) // Processing bullet regen. + fabricator_progress += seconds_per_tick + if(fabricator_progress >= fabricator_speed) + fabricator_progress = 0 + matter -= matter_usage + shots_stored++ + playsound(src, regen_sound, regen_sound_volume) + if(current_heat > 0) // Processing heat cooling. + if(overcooling_progress <= 0) + current_heat -= heat_dissipation * seconds_per_tick * 2 + else + current_heat -= heat_dissipation * seconds_per_tick + if(current_heat < 0) + current_heat = 0 + if(overcooling_progress > 0) // Processing overcooling. + var/previous_overcooling_progress = overcooling_progress + overcooling_progress -= seconds_per_tick + if(previous_overcooling_progress > 0 && overcooling_progress <= 0) + playsound(src, overcooling_sound, overcooling_sound_volume) + if(overcooling_progress < -1) + overcooling_progress = -1 + update_appearance() + +/obj/item/gun/coilgun/can_shoot() + if(!internalcore) + return FALSE + var/obj/item/ammo_casing/coil/shot = internalcore.ammunition_types[select] + return !QDELETED(internalcell) ? ((internalcell.charge >= shot.ammo_energy_usage) && shots_stored >= 1) : FALSE + +/obj/item/gun/coilgun/shoot_with_empty_chamber(mob/living/user as mob|obj) + if(!internalcore) + balloon_alert(user, "no ammunition core installed!") + playsound(src, dry_fire_sound, dry_fire_sound_volume, TRUE) + return + var/obj/item/ammo_casing/coil/shot = internalcore.ammunition_types[select] + if(internalcell.charge < shot.ammo_energy_usage) + balloon_alert(user, "not enough charge!") + playsound(src, dry_fire_sound, dry_fire_sound_volume, TRUE) + return + if(matter < matter_usage) + balloon_alert(user, "not enough matter in storage!") + playsound(src, dry_fire_sound, dry_fire_sound_volume, TRUE) + return + if(shots_stored < 1) + balloon_alert(user, "ammunition not fabricated!") + playsound(src, dry_fire_sound, dry_fire_sound_volume, TRUE) + return + +/obj/item/gun/coilgun/attackby(obj/item/A, mob/user, params) + . = ..() + if(.) + return + if(istype(A, /obj/item/stock_parts/cell)) + if (!internalcell) + var/obj/item/stock_parts/cell/input = A + insert_cell(user, input) + else + balloon_alert(user, "cell already loaded!") + update_appearance() + return + if(istype(A, /obj/item/coilcore)) + if (!internalcore) + if(istype(A, coretype)) + var/obj/item/coilcore/input = A + insert_core(user, input) + else + balloon_alert(user, "invalid core!") + else + balloon_alert(user, "core already installed!") + update_appearance() + return + insert_matter(A, user) + update_appearance() + +/obj/item/gun/coilgun/attack_hand(mob/user, list/modifiers) + if(loc == user && user.is_holding(src) && internalcell) + eject_cell(user) + update_appearance() + return + return ..() + +/obj/item/gun/coilgun/attack_self(mob/living/user as mob) + if(!internalcore) + balloon_alert(user, "no ammunition core installed!") + return ..() + if(internalcore.ammunition_types.len > 1) + select_fire(user) + return ..() + +/obj/item/gun/coilgun/AltClick(mob/user) + if(loc == user && user.is_holding(src) && internalcore) + eject_core(user) + update_appearance() + return + return ..() + +/obj/item/gun/coilgun/recharge_newshot() + if(!internalcore) + return + if (!internalcore.ammunition_types || !internalcell) + return + if(!chambered) + var/obj/item/ammo_casing/coil/shot = internalcore.ammunition_types[select] + if(internalcell.charge >= shot.ammo_energy_usage) + chambered = new shot(src) + if(!chambered.loaded_projectile) + chambered.newshot() + +/obj/item/gun/coilgun/handle_chamber() + if(chambered && !chambered.loaded_projectile) + var/obj/item/ammo_casing/coil/shot = chambered + internalcell.use(shot.ammo_energy_usage) + shots_stored-- + chambered = null + recharge_newshot() + +/obj/item/gun/coilgun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) + if(!chambered && can_shoot()) + process_chamber() + if(..()) + handle_heat(user) + +/obj/item/gun/coilgun/process_burst(mob/living/user, atom/target, message = TRUE, params = null, zone_override="", randomized_gun_spread = 0, randomized_bonus_spread = 0, rand_spr = 0, iteration = 0) + if(!chambered && can_shoot()) + process_chamber() + if(..()) + handle_heat(user) + +/obj/item/gun/coilgun/proc/handle_heat(mob/living/user) + if(!internalcore) + return // How the hell did you fire a shot without a core? + var/obj/item/ammo_casing/coil/shot = internalcore.ammunition_types[select] + current_heat += shot.ammo_heat_generation + if(current_heat > maximum_heat) + current_heat = maximum_heat + if(current_heat > dangerous_heat) + var/damage = heat_damage_multiplier * (current_heat - dangerous_heat) + user.adjustFireLoss(damage) + balloon_alert(user, "gun overheating!") + else if(current_heat > dangerous_heat / 2) + balloon_alert(user, "gun getting hot!") + update_heatrecoil() + overcooling_progress = overcooling_speed + update_appearance() + +/obj/item/gun/coilgun/proc/update_heatrecoil() + recoil = recoil_multiplier * current_heat + +/obj/item/gun/coilgun/proc/select_fire(mob/living/user) + select++ + if (select > internalcore.ammunition_types.len) // It should be literally impossible to reach this proc without a core. + select = 1 + var/obj/item/ammo_casing/coil/shot = internalcore.ammunition_types[select] + fire_sound = shot.fire_sound + fire_delay = shot.delay + if (shot.select_name && user) + balloon_alert(user, "set to [shot.select_name]!") + chambered = null + recharge_newshot(TRUE) + playsound(src, selecting_sound, selecting_sound_volume) + update_appearance() + +/obj/item/gun/coilgun/proc/insert_cell(mob/user, obj/item/stock_parts/cell/input) + if(user.transferItemToLoc(input, src)) + internalcell = input + balloon_alert(user, "cell reloaded!") + playsound(src, load_sound, load_sound_volume) + else + to_chat(user, span_warning("You cannot seem to get [input] out of your hands!")) + update_appearance() + +/obj/item/gun/coilgun/proc/eject_cell(mob/user) + playsound(src, eject_sound, eject_sound_volume) + internalcell.forceMove(drop_location()) + var/obj/item/stock_parts/cell/old_cell = internalcell + internalcell = null + user.put_in_hands(old_cell) + old_cell.update_appearance() + balloon_alert(user, "cell unloaded!") + update_appearance() + +/obj/item/gun/coilgun/proc/insert_core(mob/user, obj/item/coilcore/input) + if(user.transferItemToLoc(input, src)) + internalcore = input + balloon_alert(user, "core inserted!") + playsound(src, load_sound, load_sound_volume) + else + to_chat(user, span_warning("you cannot seem to get [input] out of your hands!")) + update_appearance() + +/obj/item/gun/coilgun/proc/eject_core(mob/user) + playsound(src, eject_sound, eject_sound_volume) + internalcore.forceMove(drop_location()) + var/obj/item/coilcore/old_core = internalcore + internalcore = null + user.put_in_hands(old_core) + old_core.update_appearance() + balloon_alert(user, "core ejected!") + shots_stored = 0 + update_appearance() + +/obj/item/gun/coilgun/proc/insert_matter(obj/item, mob/user) + if(istype(item, /obj/item/rcd_ammo)) + var/obj/item/rcd_ammo/ammo = item + var/load = min(ammo.ammoamt, max_matter - matter) + if(load <= 0) + balloon_alert(user, "matter storage full!") + return FALSE + ammo.ammoamt -= load + if(ammo.ammoamt <= 0) + qdel(ammo) + matter += load + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) + else if(isstack(item)) + loadwithsheets(item, user) + +/obj/item/gun/coilgun/proc/loadwithsheets(obj/item/stack/the_stack, mob/user) + if(the_stack.matter_amount <= 0) + balloon_alert(user, "invalid sheets!") + return FALSE + var/maxsheets = round((max_matter-matter) / the_stack.matter_amount) + if(maxsheets > 0) + var/amount_to_use = min(the_stack.amount, maxsheets) + the_stack.use(amount_to_use) + matter += the_stack.matter_amount * amount_to_use + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) + balloon_alert(user, "matter storage full!") + +/obj/item/gun/coilgun/proc/get_charge_ratio() + return can_shoot() ? CEILING(clamp(internalcell.charge / internalcell.maxcharge, 0, 1) * charge_sections, 1) : 0 + +/obj/item/gun/coilgun/proc/get_heat_ratio() + return CEILING(clamp(current_heat / maximum_heat, 0, 1) * heat_sections, 1) + +/obj/item/gun/coilgun/revolver + name = "Redtech 10mm coilpistol" + desc = "A Redtech 10mm coilpistol that looks like a revolver." + icon_state = "revolver" + w_class = WEIGHT_CLASS_NORMAL + + coretype = /obj/item/coilcore/revolver + defaultcore = /obj/item/coilcore/revolver + + max_capacity = 6 // Sixshooter. + shots_stored = 6 + + max_matter = 180 + matter = 180 + matter_usage = 10 + fabricator_speed = 5 + + defaultcell = /obj/item/stock_parts/cell/redtech + charge_sections = 8 + charge_state = "revolver" + + maximum_heat = 200 + dangerous_heat = 100 + heat_dissipation = 5 + overcooling_speed = 8 + heat_sections = 10 + heat_state = "revolver" + + recoil_multiplier = 0.01 + heat_damage_multiplier = 0.2 + +/obj/item/gun/coilgun/revolver/update_overlays() + . = ..() + + if(internalcell) // Has an internal cell loaded. + var/mutable_appearance/INCELL = mutable_appearance(icon, "[icon_state]_cell") + . += INCELL + + if(internalcore) // Has an internal core loaded. + var/mutable_appearance/INCORE = mutable_appearance(icon, "[icon_state]_core") + . += INCORE + + if(get_charge_ratio() != 0) // Has charge. + var/charge_overlay_state = "[charge_state]_charge" + charge_overlay_state += "_[get_charge_ratio()]" + var/mutable_appearance/CHARGE = mutable_appearance(icon, charge_overlay_state) + . += CHARGE + + if(get_heat_ratio() != 0) // Has heat. + var/heat_overlay_state = "[heat_state]_heat" + heat_overlay_state += "_[get_heat_ratio()]" + var/mutable_appearance/HEAT = mutable_appearance(icon, heat_overlay_state) + . += HEAT + +/obj/item/gun/coilgun/revolver/none + name = "Nonetech 10mm coilpistol" + desc = "A Nonetech 10mm coilpistol that looks like a revolver." + icon_state = "none_revolver" + + defaultcore = /obj/item/coilcore/revolver/none + defaultcell = /obj/item/stock_parts/cell/redtech/nonetech + +/obj/item/coilcore + name = "generic coilcore" + desc = "You should not be seeing this." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/coilguns.dmi' + icon_state = "debugcore" + w_class = WEIGHT_CLASS_SMALL + + var/list/ammunition_types = list(/obj/item/ammo_casing/coil, /obj/item/ammo_casing/coil/highvelo) // "Test" ammunition selections. + +/obj/item/coilcore/Destroy() + for (var/atom/item in ammunition_types) + qdel(item) + ammunition_types = null + + return ..() + +/obj/item/coilcore/revolver + name = "10mm standard coilcore" + desc = "A coilcore designed for 10mm revolver coilguns. Produces standard coilslugs. Good all-round ammunition." + icon_state = "revolvercore" + ammunition_types = list(/obj/item/ammo_casing/coil, /obj/item/ammo_casing/coil/highvelo, /obj/item/ammo_casing/coil/overcharge) + var/emissivetype = "revolvercore_emissives" + +/obj/item/coilcore/revolver/update_overlays() + . = ..() + . += emissive_appearance(icon, emissivetype, src, alpha = src.alpha) + +/obj/item/coilcore/revolver/none + icon_state = "none_revolvercore" + +/obj/item/coilcore/revolver/piercing + name = "10mm armour piercing coilcore" + desc = "A coilcore designed for 10mm revolver coilguns. Produces armour piercing coilslugs. Good against armoured targets and mechs." + icon_state = "revolvercore_piercing" + ammunition_types = list(/obj/item/ammo_casing/coil/piercing, /obj/item/ammo_casing/coil/highvelo/piercing, /obj/item/ammo_casing/coil/overcharge) + emissivetype = "revolvercore_emissives_piercing" + +/obj/item/coilcore/revolver/piercing/none + icon_state = "none_revolvercore_piercing" diff --git a/maplestation_modules/story_content/deepred_warfare/code/curios.dm b/maplestation_modules/story_content/deepred_warfare/code/curios.dm new file mode 100644 index 000000000000..b6605c2dce0a --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/curios.dm @@ -0,0 +1,220 @@ +// ALIAS = FAKE NAME +// NOT ALIAS = REAL NAME + +/obj/item/starblight_soot + name = "starblight soot jar" + desc = "A small jar filled with a fine, sparkling purple powder. It's sealed tight, and the label reads Starblight. The jar's glass just the faintest tinted blue and feels heavy in your hand." + + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + + icon = 'maplestation_modules/story_content/deepred_warfare/icons/curios.dmi' + icon_state = "starblight_soot" + + inhand_icon_state = "beaker" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + drop_sound = 'maplestation_modules/sound/items/drop/glass_small.ogg' + pickup_sound = 'maplestation_modules/sound/items/pickup/glass_small.ogg' + +/obj/item/starblight_soot/alias + name = "sealed soot jar" + +/obj/item/suspicious_scrap + name = "suspicious scrap" + desc = "A small chunk of componentry that looks like part of a larger device. Despite this, it's not immediately clear what it's from or what it does." + + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + + icon = 'maplestation_modules/story_content/deepred_warfare/icons/curios.dmi' + icon_state = "scrap" + + inhand_icon_state = "reverse_bear_trap" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + drop_sound = 'maplestation_modules/sound/items/drop/metal_drop.ogg' + pickup_sound = 'maplestation_modules/sound/items/pickup/device.ogg' + +/obj/item/suspicious_scrap/alt + icon_state = "scrap_alt" + drop_sound = 'maplestation_modules/sound/items/drop/card.ogg' + pickup_sound = 'maplestation_modules/sound/items/pickup/card.ogg' + +/obj/item/ammo_casing/shotgun/godslayer + name = "godslayer round" + desc = "A strange 12 gauge slug made of an unknown alloy. It's heavy and seems to be humming with energy. You feel that shooting this would be a really bad idea." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/curios.dmi' + icon_state = "godslayer" + projectile_type = /obj/projectile/bullet/godslayer + custom_materials = list(/datum/material/aerialite=SHEET_MATERIAL_AMOUNT*2) + fire_sound = 'maplestation_modules/story_content/deepred_warfare/sound/techblaster.ogg' + + var/obj/item/gun/fired_record + +/obj/item/ammo_casing/shotgun/godslayer/alias + name = "annhilator round" + +/obj/item/ammo_casing/shotgun/godslayer/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from) + if(isgun(fired_from)) + fired_record = fired_from + fired_record.fire_sound_volume = 0 + fired_record.recoil = initial(fired_record.recoil) + 3 + + . = ..() + playsound(src, fire_sound, 100, extrarange = 10) + + if(fired_record) + addtimer(CALLBACK(src, PROC_REF(reset_gunstats)), 1) + +/obj/item/ammo_casing/shotgun/godslayer/proc/reset_gunstats() + if(fired_record) + fired_record.fire_sound_volume = initial(fired_record.fire_sound_volume) + fired_record.recoil = initial(fired_record.recoil) + +/obj/item/redtech_nan_sample + name = "redtech nanite sample" + desc = "A small, hard cube that glows a deep red at its seams. It seems to move and shift geometrically in place." + + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + + icon = 'maplestation_modules/story_content/deepred_warfare/icons/curios.dmi' + icon_state = "nanite_sample" + + inhand_icon_state = "nothing" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + + drop_sound = 'maplestation_modules/sound/items/drop/ammobox.ogg' + pickup_sound = 'maplestation_modules/sound/items/pickup/ammobox.ogg' + +/obj/item/redtech_nan_sample/alias + name = "crimson nanite sample" + +/obj/item/throwing_star/needle + name = "persuasion needle" + desc = "A large, sharp needle designed for persuasion. It has a small, intricate yin-yang design etched into the side." + w_class = WEIGHT_CLASS_NORMAL + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + + icon = 'maplestation_modules/story_content/deepred_warfare/icons/curios.dmi' + icon_state = "needle" + + inhand_icon_state = "rods" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + drop_sound = 'maplestation_modules/sound/items/drop/knife_big.ogg' + pickup_sound = 'maplestation_modules/sound/items/pickup/knife_big.ogg' + +/obj/item/throwing_star/needle/alias + name = "throwing needle" + +/obj/item/yin_yang_orb + name = "yin-yang orb" + desc = "A small, smooth orb that seems all but inert now. It seems to almost be crystaline in nature and has the design of a yin-yang. Throwing this at someone would hurt quite a bit." + + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + + force = 8 + throwforce = 16 + + icon = 'maplestation_modules/story_content/deepred_warfare/icons/curios.dmi' + icon_state = "yin_yang_orb" + + inhand_icon_state = "nothing" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + + drop_sound = 'maplestation_modules/sound/items/drop/glass_small.ogg' + pickup_sound = 'maplestation_modules/sound/items/pickup/glass_small.ogg' + +/obj/item/yin_yang_orb/alias + name = "intricate orb" + +/obj/item/yin_yang_orb/Initialize(mapload) + . = ..() + AddElement(/datum/element/knockback, 1, FALSE, FALSE) + +/obj/item/snowglobe + name = "snowglobe" + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of The Collector?" + w_class = WEIGHT_CLASS_SMALL + + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + + icon = 'maplestation_modules/story_content/deepred_warfare/icons/curios.dmi' + icon_state = "snowglobe" + + inhand_icon_state = "beaker" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + + drop_sound = 'maplestation_modules/sound/items/drop/glass_small.ogg' + pickup_sound = 'maplestation_modules/sound/items/pickup/glass_small.ogg' + +/obj/item/snowglobe/reimu + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a red and white shrine maiden at an oriental shrine." + +/obj/item/snowglobe/yukari + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a tall, ribboned lady with a parasol standing on an old, abandoned train platform." + +/obj/item/snowglobe/sdm + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a misty, lakeside mansion." + +/obj/item/snowglobe/draedon + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a strange robotic figure sitting on a flying chair, flanked by a landscape of metal and machinery." + +/obj/item/snowglobe/starfarers + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of two twins, both wearing purple, starry attire. They stand on a floating island with a towering spire, surrounded by a sea of clouds." + +/obj/item/snowglobe/calamitas + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a dark skinned sorceress wearing red robes, standing in a incinerated city." + +/obj/item/snowglobe/angela + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a pale librarian holding a book, surrounded by an odd library." + +/obj/item/snowglobe/library + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a giant, tree-like library out in a barren wasteland." + +/obj/item/snowglobe/city + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a cityscape, with a large, imposing tower in the center." + +/obj/item/snowglobe/station + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of the space station you're currently on?" + +/obj/item/snowglobe/empty + desc = "A small glass globe filled with a miniature winter scene. This one is completely empty, save for the snow." + +/obj/item/snowglobe/extra + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a human, clad deep red naval captain's attire, with a large gun case slung across their back." + +/obj/item/snowglobe/extra/moon + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of Earth's moon. You didn't know it snowed on the Moon." + +/obj/item/snowglobe/extra/mars + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of Mars from the Sol system. You didn't know it snowed on Mars." + +/obj/item/snowglobe/extra/marisa + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a monochromatic, ordinary witch soaring through snowy clouds on their broom." + +/obj/item/snowglobe/extra/lunar + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a vast cityscape, built on the far side of the Moon." + +/obj/item/snowglobe/extra/needlecastle + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a large palace, suspended in the sky upside down." + +/obj/item/snowglobe/extra/astral + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a starstruck wasteland, infected by an otherworldly blight." + +/obj/item/snowglobe/extra/dog + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a titanic armoured serpent, coiled around a rift in time and space." + +/obj/item/snowglobe/extra/goozma + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a massive, gooey, tentacled creature, floating in a monsoon of slime." + +/obj/item/snowglobe/extra/arbiter + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a person wrapped in a pitch black cloak with golden, hexagonal highlights. They stand in the centre of a ruined cityscape." + +/obj/item/snowglobe/extra/limbus + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a strange, train-like bus. It has bright text written on its side, but you can't quite make it out." + +/obj/item/snowglobe/extra/silence + desc = "A small glass globe filled with a miniature winter scene. Inside is a miniature model of a man in a black suit and black gloves, with a featureless mask." diff --git a/maplestation_modules/story_content/deepred_warfare/code/dreadactions.dm b/maplestation_modules/story_content/deepred_warfare/code/dreadactions.dm new file mode 100644 index 000000000000..3ae431d5d27d --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/dreadactions.dm @@ -0,0 +1,519 @@ +// vvvvv ABILITIES THAT CAN BE USED IN ANY MODE vvvvv +/datum/action/cooldown/mob_cooldown/high_energy + name = "Activate High Energy Mode" + desc = "Activate High Energy Mode. Your cloak will need to be off to use this." + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_power" + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + cooldown_time = 10 SECONDS + melee_cooldown_time = 0 SECONDS + click_to_activate = FALSE + shared_cooldown = MOB_SHARED_COOLDOWN_1 + +/datum/action/cooldown/mob_cooldown/high_energy/Activate(atom/target) + var/mob/living/basic/redtechdread/ownercast = owner + + if(ownercast.neck) // Cloak on. + owner.balloon_alert(owner, "you cannot enter high energy mode while your cloak is on") + return FALSE + + if(ownercast.energy_level == 2) // In RL energy mode. + owner.balloon_alert(owner, "you cannot enter low energy mode while in RL energy mode") + return FALSE + + if(ownercast.energy_level == 1) // In high energy mode. + owner.balloon_alert(owner, "you slow and and enter low energy mode") + ownercast.energy_level = 0 + ownercast.update_base_stats() + StartCooldown() + var/datum/effect_system/fluid_spread/smoke/smoke = new + smoke.set_up(1, holder = owner, location = owner.loc) + smoke.start() + playsound(ownercast, 'sound/machines/clockcult/steam_whoosh.ogg', 120) + ownercast.visible_message(span_warning("[ownercast] slows down to a crawl...")) + return TRUE + + if(ownercast.RLEnergy < 0) // Negative RL energy. + owner.balloon_alert(owner, "you need to wait for your red lightning energy to recharge") + return FALSE + + owner.balloon_alert(owner, "you speed up and enter high energy mode") + ownercast.energy_level = 1 + ownercast.update_base_stats() + StartCooldown() + var/datum/effect_system/fluid_spread/smoke/smoke = new + smoke.set_up(1, holder = owner, location = owner.loc) + smoke.start() + playsound(ownercast, 'sound/mecha/hydraulic.ogg', 120) + ownercast.visible_message(span_boldwarning("[ownercast] unfolds their form and speeds up!")) + ownercast.apply_damage(-200) // Second phase moment. + return TRUE + +/datum/action/cooldown/mob_cooldown/lightning_energy + name = "Activate Red Lightning Energy Mode" + desc = "Activate Red Lightning Energy Mode. Needs to be at high energy to use this." + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_radioactive" + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + cooldown_time = 10 SECONDS + melee_cooldown_time = 0 SECONDS + click_to_activate = FALSE + shared_cooldown = MOB_SHARED_COOLDOWN_1 + +/datum/action/cooldown/mob_cooldown/lightning_energy/Activate(atom/target) + var/mob/living/basic/redtechdread/ownercast = owner + + if(ownercast.neck) // Cloak on. + owner.balloon_alert(owner, "you cannot enter red lightning energy mode while your cloak is on") + return FALSE + + if(!ownercast.back_storage) + owner.balloon_alert(owner, "you need to have a red lightning canister to enter red lightning energy mode") + return FALSE + + if(ownercast.energy_level == 0) // In low energy mode. + owner.balloon_alert(owner, "you need to be in high energy mode to enter red lightning energy mode") + return FALSE + + if(ownercast.RLEnergy < 0) // Negative RL energy. + if(ownercast.energy_level == 2) // But already in RL energy mode. + owner.balloon_alert(owner, "you slow and and enter low energy mode due to lack of red lightning energy") + ownercast.energy_level = 0 + ownercast.update_base_stats() + StartCooldown() + var/datum/effect_system/fluid_spread/smoke/smoke = new + smoke.set_up(1, holder = owner, location = owner.loc) + smoke.start() + playsound(ownercast, 'sound/machines/clockcult/steam_whoosh.ogg', 120) + ownercast.visible_message(span_warning("[ownercast] slows down to a crawl...")) + return TRUE + + owner.balloon_alert(owner, "you need to wait for your red lightning energy to recharge") + return FALSE + + if(ownercast.energy_level == 2) // In RL energy mode. + owner.balloon_alert(owner, "you cool down and enter high energy mode") + ownercast.energy_level = 1 + ownercast.update_base_stats() + StartCooldown() + var/datum/effect_system/fluid_spread/smoke/smoke = new + smoke.set_up(1, holder = owner, location = owner.loc) + smoke.start() + playsound(ownercast, 'sound/mecha/hydraulic.ogg', 120) + ownercast.visible_message(span_warning("[ownercast] cools down...")) + return TRUE + + owner.balloon_alert(owner, "you heat up and enter red lightning energy mode!") + ownercast.energy_level = 2 + ownercast.update_base_stats() + StartCooldown() + var/datum/effect_system/fluid_spread/smoke/smoke = new + smoke.set_up(1, holder = owner, location = owner.loc) + smoke.start() + playsound(ownercast, 'sound/mecha/skyfall_power_up.ogg', 120) + ownercast.visible_message(span_alertwarning("[ownercast] heats up and crackles with red lightning!")) + ownercast.apply_damage(-200) // Third phase moment. + return TRUE + +/datum/action/access_printer + name = "Access Redtech Printer" + desc = "Access the Redtech Printer to print out items." + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_info" + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + +/datum/action/access_printer/Trigger(trigger_flags) + . = ..() + if(!.) + return + + var/mob/living/basic/redtechdread/ownercast = owner + + if(!ownercast.back_storage) + owner.balloon_alert(owner, "you need to have a red lightning canister to print items") + return FALSE + + var/item_to_spawn = input("Item to fabricate (+ number to fabricate)?", "Item:", null) as text|null + + if(!item_to_spawn) + return FALSE + + if(!ownercast.belt_storage) + return FALSE + + var/list/preparsed = splittext(item_to_spawn,":") + var/path = preparsed[1] + var/amount = 1 + if(preparsed.len > 1) + amount = clamp(text2num(preparsed[2]),1,ADMIN_SPAWN_CAP) + + var/chosen = pick_closest_path(path) + if(!chosen) + return + + var/turf/T = get_turf(usr) + + if(ispath(chosen, /turf)) + T.ChangeTurf(chosen) + else + if(ispath(chosen, /obj)) + var/storage = ownercast.belt_storage + for(var/i in 1 to amount) + new chosen(storage) + else + for(var/i in 1 to amount) + new chosen(T) + +/datum/action/cooldown/mob_cooldown/dreadscan + name = "Ranged Scan" + desc = "Scan a target from a distance." + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_scan" + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + cooldown_time = 10 SECONDS + melee_cooldown_time = 0 SECONDS + click_to_activate = TRUE + shared_cooldown = NONE + +/datum/action/cooldown/mob_cooldown/dreadscan/Activate(atom/target_atom) + var/mob/living/basic/redtechdread/ownercast = owner + playsound(ownercast, 'sound/machines/clockcult/stargazer_activate.ogg', 120) + + var/mutable_appearance/scan_effect = mutable_appearance('maplestation_modules/story_content/deepred_warfare/icons/dreadscan.dmi', "scan") + ownercast.add_overlay(scan_effect) + ownercast.visible_message(span_warning("[ownercast] scans [target_atom]...")) + + StartCooldown() + if(!do_after(ownercast, 5 SECONDS)) + ownercast.balloon_alert(ownercast, "cancelled") + StartCooldown(cooldown_time * 0.2) + ownercast.cut_overlay(scan_effect) + playsound(ownercast, 'sound/machines/scanbuzz.ogg', 40) + return TRUE + + if(istype(target_atom, /mob/living)) + healthscan(ownercast, target_atom, advanced = TRUE) + ownercast.cut_overlay(scan_effect) + playsound(ownercast, 'sound/machines/ping.ogg', 40) + return TRUE + +/datum/action/cooldown/mob_cooldown/faraday_shield + name = "Activate Faraday Shield" + desc = "Activate a Faraday Shield to protect yourself from electromagnetic and thermal damage." + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_shield" + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + cooldown_time = 10 SECONDS // Cooldown time jumps if the shield is destroyed. + melee_cooldown_time = 0 SECONDS + click_to_activate = FALSE + shared_cooldown = NONE + + var/maxhits = 1 + var/hits = 0 + +/datum/action/cooldown/mob_cooldown/faraday_shield/Activate(atom/target) + var/mob/living/basic/redtechdread/ownercast = owner + + if(ownercast.RLEnergy < 0) // Negative RL energy. + if(ownercast.shielding_level == 0) // Shield not on. + owner.balloon_alert(owner, "you need to wait for your red lightning energy to recharge") + return FALSE + + if(ownercast.shielding_level > 0) // Shield on. + owner.balloon_alert(owner, "you turn off your Faraday Shield") + ownercast.updating_shield(0) // Turn off shield. + playsound(ownercast, 'sound/mecha/mech_shield_drop.ogg', 120) + ownercast.visible_message(span_warning("[ownercast] turns off their shield.")) + StartCooldown() + return TRUE + + ownercast.updating_shield(ownercast.energy_level + 1) // Turn on shield to correct level based on energy level. + playsound(ownercast, 'sound/mecha/mech_shield_raise.ogg', 120) + ownercast.visible_message(span_boldwarning("[ownercast] activates their shield!")) + StartCooldown() + return TRUE + +/datum/action/cooldown/mob_cooldown/faraday_shield/proc/update_shield_stats() + var/mob/living/basic/redtechdread/ownercast = owner + switch(ownercast.shielding_level) + if(0) + maxhits = 1 + hits = 0 + if(1) + maxhits = 1 + hits = 0 + if(2) + maxhits = 2 + hits = 0 + if(3) + maxhits = 3 + hits = 0 + +/datum/action/cooldown/mob_cooldown/faraday_shield/proc/take_hit() + var/mob/living/basic/redtechdread/ownercast = owner + hits += 1 + + if(hits < maxhits) + playsound(ownercast, 'sound/effects/glass_step.ogg', 240) + ownercast.visible_message(span_boldwarning("[ownercast]'s shielding cracks!")) + return + + break_shield() + +/datum/action/cooldown/mob_cooldown/faraday_shield/proc/break_shield() + var/mob/living/basic/redtechdread/ownercast = owner + + playsound(ownercast, 'sound/effects/glassbr3.ogg', 240) + playsound(ownercast, 'sound/mecha/mech_shield_drop.ogg', 120) + + StartCooldown((2 * maxhits) MINUTES) + + ownercast.balloon_alert(ownercast, "your Faraday Shield has been destroyed!") + ownercast.visible_message(span_boldwarning("[ownercast]'s shielding shatters like glass!")) + + hits = 0 + ownercast.adjust_RL_energy_or_damage(-20) + ownercast.updating_shield(0) + +// vvvvv ABILITIES THAT CAN BE USED IN LOW ENERGY MODE ONLY vvvvv +/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/dreadBullet + name = "Fire Volleycoil - Low Power" + desc = "Fire your head mounted coilgun. Your mask needs to be off for this to work." + cooldown_time = 10 SECONDS // Prone to change. + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_network" // Looks kinda like a revolver chamber. + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + shared_cooldown = NONE + + projectile_type = /obj/projectile/bullet/coil + projectile_sound = 'sound/weapons/gun/revolver/shot.ogg' + default_projectile_spread = 0 + shot_count = 7 // 7 barrels total. + shot_delay = 0.1 SECONDS // Prone to change. + + var/chargeup_time = 3 SECONDS + +/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/dreadBullet/Activate(atom/target_atom) + var/mob/living/basic/redtechdread/ownercast = owner + + if(ownercast.head) // Wearing mask, abort. + ownercast.balloon_alert(ownercast, "your mask needs to be off to do this!") + return FALSE + + playsound(ownercast, 'sound/mecha/skyfall_power_up.ogg', 120) + ownercast.visible_message(span_boldwarning("[ownercast] charges up their head mounted coilgun...")) + + if(!do_after(ownercast, chargeup_time)) + ownercast.balloon_alert(ownercast, "cancelled") + StartCooldown(cooldown_time * 0.2) + playsound(ownercast, 'sound/machines/scanbuzz.ogg', 120) + return TRUE + + . = ..() + +/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/dreadBullet/attack_sequence(mob/living/firer, atom/target) + for(var/i in 1 to shot_count) + playsound(get_turf(firer), projectile_sound, 30, TRUE) + shoot_projectile(firer, target, null, firer, rand(-default_projectile_spread, default_projectile_spread), null) + SLEEP_CHECK_DEATH(shot_delay, src) + +/datum/action/cooldown/mob_cooldown/charge/basic_charge/dread + name = "Charge - Low Power" + desc = "Charge at your target. Will not destroy objects." + cooldown_time = 30 SECONDS + charge_delay = 2.5 SECONDS + charge_distance = 4 + melee_cooldown_time = 0 + shake_duration = 2 SECONDS + shake_pixel_shift = 1 + recoil_duration = 0.5 SECONDS + knockdown_duration = 1 SECONDS + charge_damage = 20 + destroy_objects = FALSE + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_exclamation" + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + shared_cooldown = NONE + +/datum/action/cooldown/mob_cooldown/charge/basic_charge/dread/Activate(atom/target_atom) + var/mob/living/basic/redtechdread/ownercast = owner + playsound(ownercast, 'sound/machines/clockcult/steam_whoosh.ogg', 120) + ownercast.visible_message(span_boldwarning("[ownercast] builds up energy, ready to charge...")) + . = ..() + +/datum/action/cooldown/mob_cooldown/dreadrepair + name = "Self Repair - Efficient" + desc = "Conduct a self repair. More efficient but slower." + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_repair" + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + cooldown_time = 2 SECONDS + var/doafter_time = 10 SECONDS + melee_cooldown_time = 0 SECONDS + click_to_activate = FALSE + shared_cooldown = NONE + var/heal_amount = 100 + var/energy_cost = 50 + +/datum/action/cooldown/mob_cooldown/dreadrepair/Activate(atom/target_atom) + var/mob/living/basic/redtechdread/ownercast = owner + playsound(ownercast, 'sound/mecha/hydraulic.ogg', 120) + ownercast.visible_message(span_warning("[ownercast] conducts a self repair...")) + + if(!do_after(ownercast, doafter_time)) + ownercast.balloon_alert(ownercast, "cancelled") + StartCooldown(cooldown_time * 0.2) + playsound(ownercast, 'sound/machines/scanbuzz.ogg', 120) + return TRUE + + ownercast.adjustBruteLoss(-heal_amount) + ownercast.adjust_RL_energy_or_damage(-energy_cost) + playsound(ownercast, 'sound/machines/ping.ogg', 120) + StartCooldown(cooldown_time) + return TRUE + +/datum/action/cooldown/mob_cooldown/heatburst + name = "Purge Heat" + desc = "Purposefully vent heat from your heatsinks to deal damage to nearby enemies." + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_bomb" + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + cooldown_time = 3 MINUTES + melee_cooldown_time = 0 SECONDS + click_to_activate = FALSE + shared_cooldown = NONE + var/firesize = 2 + var/doafter_time = 2 SECONDS + +/datum/action/cooldown/mob_cooldown/heatburst/Activate(atom/target_atom) + var/mob/living/basic/redtechdread/ownercast = owner + playsound(ownercast, 'sound/machines/clockcult/stargazer_activate.ogg', 120) + ownercast.visible_message(span_alertwarning("[ownercast]'s heatsink vents blaze with to life...")) + + if(!do_after(ownercast, doafter_time)) + ownercast.balloon_alert(ownercast, "cancelled") + StartCooldown(cooldown_time * 0.2) + playsound(ownercast, 'sound/machines/scanbuzz.ogg', 120) + return TRUE + + playsound(ownercast, 'sound/machines/clockcult/ark_damage.ogg', 120) + StartCooldown(cooldown_time) + ownercast.adjust_RL_energy(50) // Return some energy. + INVOKE_ASYNC(src, PROC_REF(complete_burnoff), get_turf(owner), firesize) + +/datum/action/cooldown/mob_cooldown/heatburst/proc/complete_burnoff(atom/centre, firesize = 1) + for(var/i in 0 to firesize) + for(var/turf/nearby_turf as anything in spiral_range_turfs(i + 1, centre)) + new /obj/effect/hotspot(nearby_turf) + nearby_turf.hotspot_expose(750, 50, 1) + for(var/mob/living/fried_living in nearby_turf.contents - owner) + fried_living.apply_damage(5, BURN) + + stoplag(0.3 SECONDS) + +// vvvvv ABILITIES THAT CAN BE USED IN HIGH ENERGY MODE ONLY vvvvv +/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/dreadBullet/high + name = "Fire Volleycoil - High Power" + cooldown_time = 20 SECONDS // Prone to change. + + projectile_type = /obj/projectile/bullet/coil/highvelo + projectile_sound = 'sound/weapons/gun/sniper/shot.ogg' + default_projectile_spread = 5 + + chargeup_time = 4 SECONDS + +/datum/action/cooldown/mob_cooldown/charge/basic_charge/dread/high + name = "Charge - High Power" + desc = "Charge at your target. Will destroy objects." + charge_delay = 3 SECONDS + charge_distance = 6 + shake_duration = 2.5 SECONDS + shake_pixel_shift = 1 + recoil_duration = 1 SECONDS + knockdown_duration = 1.5 SECONDS + charge_damage = 30 + destroy_objects = TRUE + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_exclamation" + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + shared_cooldown = NONE + +/datum/action/cooldown/mob_cooldown/dreadrepair/high + name = "Self Repair - Normal" + desc = "Conduct a self repair. Normal efficiency." + cooldown_time = 10 SECONDS + doafter_time = 5 SECONDS + heal_amount = 100 + energy_cost = 50 + +/datum/action/cooldown/mob_cooldown/heatburst/high + name = "Purge Heat - Overcharge" + desc = "Purposefully overcharge your heatsinks to deal damage to nearby enemies." + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_bomb" + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + cooldown_time = 3 MINUTES + melee_cooldown_time = 0 SECONDS + click_to_activate = FALSE + shared_cooldown = NONE + firesize = 4 + doafter_time = 5 SECONDS + +// vvvvv ABILITIES THAT CAN ONLY BE USED IN RL MODE ONLY vvvvv +/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/dreadBullet/lightning + name = "Fire Volleycoil - Overcharge" + cooldown_time = 30 SECONDS // Prone to change. + + projectile_type = /obj/projectile/bullet/coil/red_lightning + projectile_sound = 'sound/weapons/gun/hmg/hmg.ogg' + default_projectile_spread = 15 + + chargeup_time = 5 SECONDS + +/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/dreadBullet/lightning/attack_sequence(mob/living/firer, atom/target) + var/mob/living/basic/redtechdread/ownercast = firer + ownercast.adjust_RL_energy_or_damage(-20) // Yummy energy. + . = ..() + +/datum/action/cooldown/mob_cooldown/charge/basic_charge/dread/lightning + name = "Charge - Overcharge" + desc = "Charge at your target with the power of red lightning. Will destroy objects and deal more damage." + charge_delay = 4 SECONDS + charge_distance = 8 + shake_duration = 3 SECONDS + shake_pixel_shift = 2 + recoil_duration = 1.5 SECONDS + knockdown_duration = 2 SECONDS + charge_damage = 40 + destroy_objects = TRUE + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_exclamation" + background_icon_state = "bg_tech" + overlay_icon_state = "bg_tech_border" + shared_cooldown = NONE + +/datum/action/cooldown/mob_cooldown/charge/basic_charge/dread/lightning/Activate(atom/target_atom) + var/mob/living/basic/redtechdread/ownercast = owner + ownercast.adjust_RL_energy_or_damage(-30) + . = ..() + +/datum/action/cooldown/mob_cooldown/dreadrepair/lightning + name = "Self Repair - Overcharge" + desc = "Conduct a self repair. Less efficient but faster." + cooldown_time = 30 SECONDS + doafter_time = 2 SECONDS + heal_amount = 100 + energy_cost = 25 + +// Godslayer fabrication and fire replacement here. diff --git a/maplestation_modules/story_content/deepred_warfare/code/dreaditems.dm b/maplestation_modules/story_content/deepred_warfare/code/dreaditems.dm new file mode 100644 index 000000000000..03e366e3764b --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/dreaditems.dm @@ -0,0 +1,64 @@ +/obj/item/storage/dread_storage + name = "Heavy Duty Storage Unit" + desc = "A heavy duty storage unit, designed to hold a large amount of items. You could carry it around, but it's too big to equip." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/dreaditems.dmi' + icon_state = "storageunit" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' // Change this to a prexisting sprite, I do not want to make a new one. + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + inhand_icon_state = "rack_parts" + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + item_flags = NO_MAT_REDEMPTION + armor_type = /datum/armor/dread_storage + storage_type = /datum/storage/dread_storage + +/datum/armor/dread_storage + fire = 100 + acid = 50 + +/datum/storage/dread_storage + max_specific_storage = WEIGHT_CLASS_BULKY + max_total_storage = 90 + max_slots = 30 + allow_big_nesting = TRUE + +/obj/item/clothing/neck/cloak/redtech_dread + name = "The Collector's Cloak" + desc = "The Collector's Cloak, a heavy duty cloak lined with heavy metals the wearer from external analysis. It's a bit bulky and was probably made for something a lot larger than you." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/dreaditems.dmi' + worn_icon = 'maplestation_modules/story_content/deepred_warfare/icons/dreadclothing.dmi' + icon_state = "cloak" + w_class = WEIGHT_CLASS_BULKY + body_parts_covered = CHEST|GROIN|LEGS|ARMS + flags_inv = HIDESUITSTORAGE + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/neck/cloak/redtech_dread/pattern + worn_icon = 'maplestation_modules/story_content/deepred_warfare/icons/dreadclothingbig.dmi' + icon_state = "cloak_pattern" + +/obj/item/clothing/mask/collector + name = "The Collector's Mask" + desc = "A strange oriental fox mask, made of heavy metal. It's half black and half white, with only one eye hole." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/dreaditems.dmi' + worn_icon = 'maplestation_modules/story_content/deepred_warfare/icons/dreadclothing.dmi' + icon_state = "mask" + w_class = WEIGHT_CLASS_BULKY + flags_inv = HIDEFACE|HIDEFACIALHAIR|HIDESNOUT + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/mask/collector/pattern + worn_icon = 'maplestation_modules/story_content/deepred_warfare/icons/dreadclothingbig.dmi' + icon_state = "mask_pattern" + +/obj/item/implant/radio/redtech + desc = "Redtech." + radio_key = /obj/item/encryptionkey/redtech + subspace_transmission = TRUE + +/obj/item/encryptionkey/redtech + channels = list(RADIO_CHANNEL_COMMAND = 1, RADIO_CHANNEL_SECURITY = 1, RADIO_CHANNEL_ENGINEERING = 1, RADIO_CHANNEL_SCIENCE = 1, RADIO_CHANNEL_MEDICAL = 1, RADIO_CHANNEL_SUPPLY = 1, RADIO_CHANNEL_SERVICE = 1, RADIO_CHANNEL_AI_PRIVATE = 1, RADIO_CHANNEL_SYNDICATE = 1) + translate_binary = TRUE + translated_language = /datum/language/machine + syndie = TRUE + greyscale_config = /datum/greyscale_config/encryptionkey_syndicate + greyscale_colors = "#171717#990000" diff --git a/maplestation_modules/story_content/deepred_warfare/code/droneai.dm b/maplestation_modules/story_content/deepred_warfare/code/droneai.dm new file mode 100644 index 000000000000..f0090609b6a9 --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/droneai.dm @@ -0,0 +1,30 @@ +/** + * The flying machinegun drone planning: + * - If too close to target, back up to keep distance. + * - If too far from target, close the distance. + * - BANG BANG BANG. + * - Find non DEEP RED faction target. + */ +/datum/ai_controller/basic_controller/rapidlightflying + blackboard = list( + BB_BASIC_MOB_CURRENT_TARGET, + BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + ) + ai_movement = /datum/ai_movement/jps + idle_behavior = /datum/idle_behavior/idle_random_walk + planning_subtrees = list( + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/maintain_distance/rapidlightflying, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/ranged_skirmish/rapidlightflying, + ) + +/datum/ai_planning_subtree/maintain_distance/rapidlightflying + minimum_distance = 3 + maximum_distance = 8 + +/datum/ai_planning_subtree/ranged_skirmish/rapidlightflying + min_range = 0 diff --git a/maplestation_modules/story_content/deepred_warfare/code/drones.dm b/maplestation_modules/story_content/deepred_warfare/code/drones.dm new file mode 100644 index 000000000000..0bda15a166fd --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/drones.dm @@ -0,0 +1,87 @@ +// Root of all drones. Is abstract and should not be spawned. +/mob/living/basic/reddrone + name = "drone error" + desc = "You should not be seeing this error." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/reddrone.dmi' + icon_state = "rapid_light_flying" + icon_living = "rapid_light_flying" + icon_dead = "rapid_light_flying_dead" + gender = NEUTER + mob_biotypes = MOB_ROBOTIC + + // Armour is optimized for thermal and energy damage. + damage_coeff = list(BRUTE = 1, BURN = 0.5, TOX = 0, STAMINA = 0, OXY = 0) + + // Most drones are ranged attackers so they don't deal much melee damage. + melee_damage_lower = 5 + melee_damage_upper = 5 + + attack_verb_continuous = "rams" + attack_verb_simple = "ram" + attack_sound = 'sound/weapons/genhit1.ogg' + attack_vis_effect = ATTACK_EFFECT_PUNCH + verb_say = "states" + verb_ask = "queries" + verb_exclaim = "declares" + verb_yell = "alarms" + bubble_icon = "machine" + + faction = list(FACTION_DEEPRED) + combat_mode = TRUE + speech_span = SPAN_ROBOT + death_sound = 'sound/voice/borg_deathsound.ogg' + death_message = "shudders, then falls to the ground, inoperable." + + unsuitable_atmos_damage = 0 + + unsuitable_cold_damage = 0 + bodytemp_heat_damage_limit = 800 + + var/ranged = TRUE + + var/ranged_cooldown = 6 SECONDS + var/projectile_type = /obj/projectile/bullet/coil + var/shoot_sound = 'sound/weapons/gun/smg/shot.ogg' + var/burst_amount = 4 + var/burst_cooldown = 0.125 SECONDS + + var/flying = TRUE + // var/list/death_loot + +/mob/living/basic/reddrone/Initialize(mapload) + . = ..() + + if(flying) + AddElement(/datum/element/simple_flying) + + // AddComponent(/datum/component/basic_mob_attack_telegraph, \telegraph_time = 0.5 SECONDS, \sound_path = 'sound/weapons/laser_crank.ogg') + + if(ranged) + AddComponent(\ + /datum/component/ranged_attacks,\ + cooldown_time = ranged_cooldown,\ + projectile_type = projectile_type,\ + projectile_sound = shoot_sound,\ + burst_shots = burst_amount,\ + burst_intervals = burst_cooldown,\ + ) + + // AddElement(/datum/element/death_drops, death_loot) + +/mob/living/basic/reddrone/rapid_flying + name = "light airborne drone" + desc = "A small, flying drone. It's equipped with a rapid-fire machinecoil." + icon_state = "rapid_light_flying" + icon_living = "rapid_light_flying" + icon_dead = "rapid_light_flying_dead" + + // Relatively weak armour. + health = 40 + maxHealth = 40 + + //Fast as fuck. + speed = 0.5 + + ai_controller = /datum/ai_controller/basic_controller/rapidlightflying + + // death_loot = list(/obj/item/stock_parts/capacitor/redtech = 1, /obj/item/stock_parts/servo/redtech = 1) diff --git a/maplestation_modules/story_content/deepred_warfare/code/kajari_beam.dm b/maplestation_modules/story_content/deepred_warfare/code/kajari_beam.dm new file mode 100644 index 000000000000..3906e4cbd5bb --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/kajari_beam.dm @@ -0,0 +1,70 @@ +/obj/projectile/kajari_lance + name = "Kajari particle lance" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/kajari.dmi' + icon_state = "kajari_beam" + pass_flags = NONE + damage = 3000 //Might be overkill, but there is no overkill for Kajari. + damage_type = BURN + hitsound = 'sound/weapons/resonator_blast.ogg' + hitsound_wall = 'sound/weapons/plasma_cutter.ogg' + range = 400 + armor_flag = ENERGY + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser //Basically doesn't matter, it will never be stopped. + ricochets_max = 0 + ricochet_chance = 0 //No. + reflectable = NONE //Hell no, this is a big ass particle beam. + wound_bonus = 80 + bare_wound_bonus = 90 + armour_penetration = 100 + projectile_piercing = ALL + hit_prone_targets = TRUE + can_hit_turfs = TRUE + parried = TRUE + + var/explosion_tile_cooldown = 6 + var/forcedust_on_hit = TRUE // Collector moment. + +/obj/projectile/kajari_lance/prehit_pierce() + return PROJECTILE_PIERCE_HIT + +/obj/projectile/kajari_lance/scan_moved_turf() + if(explosion_tile_cooldown <= 0) + explosion(src, devastation_range = 4, heavy_impact_range = 6, light_impact_range = 10, flame_range = 15, flash_range = 15, ignorecap = TRUE, adminlog = FALSE) + explosion_tile_cooldown = 3 + else + explosion_tile_cooldown-- + return ..() + +/obj/projectile/kajari_lance/singularity_pull() + return + +/obj/projectile/kajari_lance/on_hit(atom/target, blocked = 0, pierce_hit) + . = ..() + if (!QDELETED(target) && isliving(target) && forcedust_on_hit) + var/mob/living/duster = target + duster.dust(just_ash = TRUE, drop_items = FALSE, force = TRUE) + +/obj/projectile/kajari_lance/hitscan + hitscan = TRUE + muzzle_type = /obj/effect/projectile/tracer/laser/kajari + tracer_type = /obj/effect/projectile/tracer/laser/kajari + impact_type = /obj/effect/projectile/tracer/laser/kajari + impact_effect_type = null + hitscan_light_intensity = 3 + hitscan_light_range = 16 // MY EYES. + hitscan_light_color_override = COLOR_RED_LIGHT + muzzle_flash_intensity = 3 + muzzle_flash_range = 16 + muzzle_flash_color_override = COLOR_RED_LIGHT + impact_light_intensity = 3 + impact_light_range = 16 + impact_light_color_override = COLOR_RED_LIGHT + +/obj/projectile/kajari_lance/generate_hitscan_tracers(cleanup = TRUE, duration = 30 SECONDS, impacting = TRUE) + duration = 30 SECONDS + . = ..() + +/obj/effect/projectile/tracer/laser/kajari + name = "kajari lance" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/kajari.dmi' + icon_state = "kajari_beam" diff --git a/maplestation_modules/story_content/deepred_warfare/code/redcinematics.dm b/maplestation_modules/story_content/deepred_warfare/code/redcinematics.dm new file mode 100644 index 000000000000..56e92d853d40 --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/redcinematics.dm @@ -0,0 +1,48 @@ +/proc/play_unbidden_cinematic(chosen_cinematic, charge_time) + switch(chosen_cinematic) + if("Culmination Spark End") + SSsecurity_level.set_level(SEC_LEVEL_DELTA) + priority_announce( + text = "A large energy weapon buildup event has been detected in your sector. Highly recommend immediate evacuation. TIME BEFORE CRITICAL ENERGY THRESHOLD: [charge_time] MINUTES.", + title = "[command_name()] Crisis & Damage Control Center", + sound = 'sound/misc/airraid.ogg', + ) + // addtimer(CALLBACK(src, GLOBAL_PROC_REF(fire_unbidden_spark)), charge_time MINUTES) + return TRUE + return FALSE + +// Modded version of the cinematic atom to allow this to stay modular. +/atom/movable/screen/cinematic/modded + icon = 'maplestation_modules/story_content/deepred_warfare/icons/cinematics.dmi' + +/datum/cinematic/unbidden + +/datum/cinematic/unbidden/New(watcher, datum/callback/special_callback) + . = ..() + screen = new /atom/movable/screen/cinematic/modded() + +/datum/cinematic/unbidden/culmination_spark + +/datum/cinematic/unbidden/culmination_spark/play_cinematic() + flick("intro_spark", screen) + stoplag(3.5 SECONDS) + // Only intro plays, other bits seem broken (upon gib). + flick("culmination_spark", screen) + play_cinematic_sound(sound('sound/effects/explosion_distant.ogg')) // Change this later. + if(special_callback) // Prevents the code from imploding on accident. + special_callback.Invoke() + screen.icon_state = "spark_end" + +/proc/fire_unbidden_spark() + // play_cinematic(/datum/cinematic/unbidden/culmination_spark, world, CALLBACK(SSticker, TYPE_PROC_REF(/datum/controller/subsystem/ticker, station_explosion_detonation), src)) + // INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(callback_on_everyone_on_z), SSmapping.levels_by_trait(ZTRAIT_STATION), CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(nuke_gib)), src) + +/proc/remove_air_stationz() + for(var/area/station/station_area in GLOB.areas) + if(!is_station_level(station_area.z)) + continue + for(var/turf/area_turf as anything in station_area.get_turfs_from_all_zlevels()) + if(isopenturf(area_turf)) + var/turf/open/deleting_turf = area_turf + if(deleting_turf.air) + deleting_turf.remove_air(INFINITY) diff --git a/maplestation_modules/story_content/deepred_warfare/code/reddread.dm b/maplestation_modules/story_content/deepred_warfare/code/reddread.dm new file mode 100644 index 000000000000..8dcc1fe4a9b6 --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/reddread.dm @@ -0,0 +1,886 @@ +#define DREAD_TOTAL_LAYERS 2 +#define DREAD_HEAD_LAYER 1 +#define DREAD_NECK_LAYER 2 + +#define DOAFTER_SOURCE_DREAD_INTERACTION "dreadnought interaction" +#define SHIELDING_FILTER "shielding filter" + +/mob/living/basic/redtechdread + name = "Redtech Dreadnought Pattern" + desc = "A terrifying robotic multi-limbed monstrosity, covered in armour plating." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/reddreadnought64.dmi' + icon_state = "dreadnought_active" + icon_living = "dreadnought_active" + icon_dead = "dreadnought_dead" + health_doll_icon = "dreadnought_active" + pixel_x = -16 + base_pixel_x = -16 + health = 1000 // On par with Lavaland elites. + maxHealth = 1000 + unsuitable_atmos_damage = 0 + unsuitable_cold_damage = 0 + unsuitable_heat_damage = 0 + speed = 3 // Slow as balls base speed. + density = TRUE + pass_flags = NONE + sight = SEE_TURFS + status_flags = NONE + gender = PLURAL + mob_biotypes = MOB_ROBOTIC | MOB_SPECIAL + speak_emote = list("grinds") + speech_span = SPAN_ROBOT + bubble_icon = "machine" + initial_language_holder = /datum/language_holder/redtech + mob_size = MOB_SIZE_LARGE + // has_unlimited_silicon_privilege = TRUE // Change later, maybe. // Nah, The Collector can just carry around an ID. + damage_coeff = list(BRUTE = 1.2, BURN = 0.8, TOX = 0, STAMINA = 0, OXY = 0) + faction = list(FACTION_DEEPRED) + hud_type = /datum/hud/dextrous/dreadnought + layer = LARGE_MOB_LAYER + pressure_resistance = 200 + + death_sound = 'sound/machines/clockcult/ark_deathrattle.ogg' + death_message = "comes to a grinding halt, its systems shutting down with crackles of red lightning." + + move_force = MOVE_FORCE_NORMAL + move_resist = MOVE_FORCE_STRONG + pull_force = MOVE_FORCE_NORMAL + + attack_verb_continuous = "crushes" + attack_verb_simple = "crush" + + attack_sound = 'sound/effects/tableslam.ogg' + attack_vis_effect = ATTACK_EFFECT_PUNCH + + melee_damage_upper = 15 + melee_damage_lower = 10 + obj_damage = 20 + armour_penetration = 0 + + melee_attack_cooldown = (CLICK_CD_MELEE/2) // Many limbs, fast attacking. + + var/heavy_emp_damage = 80 // If the EMP is heavy, the pattern is damaged by this value. + var/light_emp_damage = 40 // The pattern is damaged by this value when hit by an EMP. + + var/hands = 4 + + var/list/dread_overlays[DREAD_TOTAL_LAYERS] + + var/obj/item/back_storage // Only used to hold the red lightning container. + + var/obj/item/default_storage = /obj/item/reagent_containers/cup/beaker/redlightning/filled + + var/obj/item/belt_storage // Can hold anything (but typically holds the storage module). + + var/obj/item/default_belt = /obj/item/storage/dread_storage + + var/obj/item/neck // Only used to hold the cloak. + + var/obj/item/default_neckwear = /obj/item/clothing/neck/cloak/redtech_dread + + var/obj/item/head // Multipurpose for masks and hats. (but usually the mask) + + var/obj/item/default_headwear = /obj/item/clothing/mask/collector + + /// Actions to grant on spawn (persistent slots) + var/static/list/actions_to_add = list( + /datum/action/cooldown/mob_cooldown/high_energy = null, + /datum/action/cooldown/mob_cooldown/lightning_energy = null, + /datum/action/access_printer = null, + /datum/action/cooldown/mob_cooldown/dreadscan = null, + ) + + var/RLEnergy = 100 // Red lightning reserves (-100 to 100). + var/RL_energy_regen = 2 // How much red lightning energy is regenerated per second. + + var/energy_level = 0 // Used for the red lightning system. + + var/shielding_level = 0 // Used for the faraday shielding system. + + var/datum/action/cooldown/mob_cooldown/faraday_shield/internalshield // Used to hold the shielding system. + + var/datum/action/dynamicSlot1 // Used to hold various projectile attacks. + + var/datum/action/dynamicSlot2 // Used to hold various AOE melee attacks. + + var/datum/action/dynamicSlot3 // Used to hold repair abilities. + + var/datum/action/dynamicSlot4 // Used to hold various AOE special attacks. + +/datum/language_holder/redtech // Literally just the TG silicon language list (so far). + understood_languages = list( + /datum/language/common = list(LANGUAGE_ATOM), + /datum/language/uncommon = list(LANGUAGE_ATOM), + /datum/language/machine = list(LANGUAGE_ATOM), + /datum/language/draconic = list(LANGUAGE_ATOM), + /datum/language/moffic = list(LANGUAGE_ATOM), + /datum/language/calcic = list(LANGUAGE_ATOM), + /datum/language/voltaic = list(LANGUAGE_ATOM), + /datum/language/nekomimetic = list(LANGUAGE_ATOM), + ) + spoken_languages = list( + /datum/language/common = list(LANGUAGE_ATOM), + /datum/language/uncommon = list(LANGUAGE_ATOM), + /datum/language/machine = list(LANGUAGE_ATOM), + /datum/language/draconic = list(LANGUAGE_ATOM), + /datum/language/moffic = list(LANGUAGE_ATOM), + /datum/language/calcic = list(LANGUAGE_ATOM), + /datum/language/voltaic = list(LANGUAGE_ATOM), + /datum/language/nekomimetic = list(LANGUAGE_ATOM), + ) + +/mob/living/basic/redtechdread/get_speech_sounds(sound_type) // Hopefully works. + return string_assoc_list(list('goon/sound/voice/radio_ai.ogg' = 100)) + +/mob/living/basic/redtechdread/Initialize(mapload) + . = ..() + AddElement(/datum/element/dextrous, hud_type = hud_type) + AddComponent(/datum/component/basic_inhands, y_offset = 0) + change_number_of_hands(hands) // Cring. + // AddComponent(/datum/component/simple_access, SSid_access.get_region_access_list(list(REGION_ALL_GLOBAL))) // I'm a dumbass. + AddComponent(/datum/component/personal_crafting) + + AddComponent(/datum/component/regenerator, regeneration_delay = 10 MINUTES, brute_per_second = 10, outline_colour = NONE) // Highly suggested to use the self repair option, but this will kick in after 10 minutes if you are too lazy. + + add_traits(list(TRAIT_NO_PLASMA_TRANSFORM, // Just in case of Icebox. + TRAIT_KNOW_ROBO_WIRES, // Big brain. + TRAIT_MADNESS_IMMUNE, // Doesn't go insanae due to SM. + TRAIT_NO_SOUL, // Only useful in case of revenant, but it's a nice thematic thing. + TRAIT_PLANT_SAFE, // Metallic hands prevent plant exposure. + TRAIT_QUICKER_CARRY, // Can't carry yet (oops). + TRAIT_STRONG_GRABBER, // Many limbs provide strong grabbing. + TRAIT_SURGEON, // Certified:tm: medical robotics. + TRAIT_REAGENT_SCANNER, // Look at reagents to see what it is. + TRAIT_GOOD_HEARING, // I HEARD YOU WHISPERING. + TRAIT_FASTMED, // Certified:tm: medical robotics. + TRAIT_NOFIRE, // Don't want the cloak and stuff to burn. + TRAIT_PUSHIMMUNE, // Literal 3m tall tank of a robot. + TRAIT_FIST_MINING, // Just in case of Icebox. + TRAIT_NEGATES_GRAVITY, // Grabby grabby hands. + TRAIT_LITERATE, // Basically needed. + TRAIT_KNOW_ENGI_WIRES, // Big brain. + TRAIT_ADVANCEDTOOLUSER, // Also basically needed + TRAIT_CAN_STRIP), INNATE_TRAIT) // Also also basically needed. + + if(default_storage) + var/obj/item/storage = new default_storage(src) + equip_to_slot_or_del(storage, ITEM_SLOT_BACK) + + if(default_belt) + var/obj/item/storage = new default_belt(src) + equip_to_slot_or_del(storage, ITEM_SLOT_BELT) + + if(default_neckwear) + var/obj/item/storage = new default_neckwear(src) + equip_to_slot_or_del(storage, ITEM_SLOT_NECK) + + if(default_headwear) + var/obj/item/storage = new default_headwear(src) + equip_to_slot_or_del(storage, ITEM_SLOT_HEAD) + + grant_actions_by_list(actions_to_add) + + internalshield = new /datum/action/cooldown/mob_cooldown/faraday_shield(src) + internalshield.Grant(src) + + // AddComponent(/datum/component/seethrough_mob) + RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack)) + RegisterSignal(src, COMSIG_LIVING_LIFE, PROC_REF(on_life)) + + var/obj/item/implant/radio/redtech/comms = new(src) + comms.implant(src) + + update_base_stats() // This proc basically makes everything else redundant. + + shielding_level = 0 + updating_shield(0) + // Maybe fixes the EMP thing. + +/datum/hud/dextrous/dreadnought/New(mob/owner) + ..() + var/atom/movable/screen/inventory/inv_box + + inv_box = new /atom/movable/screen/inventory(null, src) + inv_box.name = "internal red lightning storage" + inv_box.icon = ui_style + inv_box.icon_state = "back" + inv_box.screen_loc = ui_back + inv_box.slot_id = ITEM_SLOT_BACK + static_inventory += inv_box + + inv_box = new /atom/movable/screen/inventory(null, src) + inv_box.name = "internal belt storage" + inv_box.icon = ui_style + inv_box.icon_state = "belt" + inv_box.screen_loc = ui_belt + inv_box.slot_id = ITEM_SLOT_BELT + static_inventory += inv_box + + inv_box = new /atom/movable/screen/inventory(null, src) + inv_box.name = "cloak" + inv_box.icon = ui_style + inv_box.icon_state = "neck" + inv_box.screen_loc = ui_id + inv_box.slot_id = ITEM_SLOT_NECK + static_inventory += inv_box + + inv_box = new /atom/movable/screen/inventory(null, src) + inv_box.name = "head/mask" + inv_box.icon = ui_style + inv_box.icon_state = "mask" + inv_box.screen_loc = ui_sstore1 + inv_box.slot_id = ITEM_SLOT_HEAD + static_inventory += inv_box + +/datum/hud/dextrous/dreadnought/persistent_inventory_update() + if(!mymob) + return + var/mob/living/basic/redtechdread/drone = mymob + + if(hud_shown) + if(!isnull(drone.back_storage)) + drone.back_storage.screen_loc = ui_back + drone.client.screen += drone.back_storage + if(!isnull(drone.belt_storage)) + drone.belt_storage.screen_loc = ui_belt + drone.client.screen += drone.belt_storage + if(!isnull(drone.neck)) + drone.neck.screen_loc = ui_id + drone.client.screen += drone.neck + if(!isnull(drone.head)) + drone.head.screen_loc = ui_sstore1 + drone.client.screen += drone.head + else + drone.back_storage?.screen_loc = null + drone.belt_storage?.screen_loc = null + drone.neck?.screen_loc = null + drone.head?.screen_loc = null + ..() + +/mob/living/basic/redtechdread/doUnEquip(obj/item/item, force, newloc, no_move, invdrop = TRUE, silent = FALSE) + if(..()) + update_held_items() + if(item == head) + head = null + update_worn_head() + if(item == neck) + neck = null + update_worn_neck() + if(item == belt_storage) + belt_storage = null + update_worn_belt() + if(item == back_storage) + back_storage = null + update_worn_back() + return TRUE + return FALSE + +/mob/living/basic/redtechdread/can_equip(obj/item/item, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, ignore_equipped = FALSE, indirect_action = FALSE) + switch(slot) + if(ITEM_SLOT_HEAD) + if(head) + return FALSE + if(!((item.slot_flags & ITEM_SLOT_HEAD) || (item.slot_flags & ITEM_SLOT_MASK))) + return FALSE + if(!(istype(item, /obj/item/clothing/mask/collector))) + return FALSE + return TRUE + if(ITEM_SLOT_NECK) + if(neck) + return FALSE + if(energy_level != 0) + return FALSE + if(!(istype(item, /obj/item/clothing/neck/cloak/redtech_dread))) + return FALSE + return TRUE + if(ITEM_SLOT_BELT) + if(belt_storage) + return FALSE + return TRUE + if(ITEM_SLOT_BACK) + if(back_storage) + return FALSE + if(!(istype(item, /obj/item/reagent_containers/cup/beaker/redlightning))) + return FALSE + return TRUE + ..() + +/mob/living/basic/redtechdread/get_item_by_slot(slot_id) + switch(slot_id) + if(ITEM_SLOT_HEAD) + return head + if(ITEM_SLOT_NECK) + return neck + if(ITEM_SLOT_BELT) + return belt_storage + if(ITEM_SLOT_BACK) + return back_storage + + return ..() + +/mob/living/basic/redtechdread/get_slot_by_item(obj/item/looking_for) + if(back_storage == looking_for) + return ITEM_SLOT_BACK + if(belt_storage == looking_for) + return ITEM_SLOT_BELT + if(neck == looking_for) + return ITEM_SLOT_NECK + if(head == looking_for) + return ITEM_SLOT_HEAD + return ..() + +/mob/living/basic/redtechdread/equip_to_slot(obj/item/equipping, slot, initial = FALSE, redraw_mob = FALSE, indirect_action = FALSE) + if(!slot) + return + if(!istype(equipping)) + return + + var/index = get_held_index_of_item(equipping) + if(index) + held_items[index] = null + update_held_items() + + if(equipping.pulledby) + equipping.pulledby.stop_pulling() + + equipping.screen_loc = null // will get moved if inventory is visible + equipping.forceMove(src) + SET_PLANE_EXPLICIT(equipping, ABOVE_HUD_PLANE, src) + + switch(slot) + if(ITEM_SLOT_HEAD) + head = equipping + update_worn_head() + if(ITEM_SLOT_NECK) + neck = equipping + update_worn_neck() + if(ITEM_SLOT_BELT) + belt_storage = equipping + update_worn_belt() + if(ITEM_SLOT_BACK) + back_storage = equipping + update_worn_back() + else + to_chat(src, span_danger("You are trying to equip this item to an unsupported inventory slot. Report this to a coder!")) + return + + //Call back for item being equipped to drone + equipping.on_equipped(src, slot) + +/mob/living/basic/redtechdread/update_clothing(slot_flags) + if(slot_flags & ITEM_SLOT_HEAD) + update_worn_head() + if(slot_flags & ITEM_SLOT_MASK) + update_worn_mask() + if(slot_flags & ITEM_SLOT_NECK) + update_worn_neck() + if(slot_flags & ITEM_SLOT_BELT) + update_worn_belt() + if(slot_flags & ITEM_SLOT_HANDS) + update_held_items() + if(slot_flags & (ITEM_SLOT_HANDS|ITEM_SLOT_BACKPACK|ITEM_SLOT_BACK)) + update_worn_back() + +/mob/living/basic/redtechdread/update_held_items() + SHOULD_CALL_PARENT(FALSE) // Get overridden nerd. + if(isnull(client) || isnull(hud_used) || hud_used.hud_version == HUD_STYLE_NOHUD) + return + var/turf/our_turf = get_turf(src) + for(var/obj/item/held in held_items) + var/index = get_held_index_of_item(held) + SET_PLANE(held, ABOVE_HUD_PLANE, our_turf) + held.screen_loc = ui_hand_position(index) + client.screen |= held + +/mob/living/basic/redtechdread/proc/apply_overlay(cache_index) + if((. = dread_overlays[cache_index])) + add_overlay(.) + +/mob/living/basic/redtechdread/proc/remove_overlay(cache_index) + var/overlay = dread_overlays[cache_index] + if(overlay) + cut_overlay(overlay) + dread_overlays[cache_index] = null + +/mob/living/basic/redtechdread/update_worn_head() + remove_overlay(DREAD_HEAD_LAYER) + + if(head) + if(client && hud_used?.hud_shown) + head.screen_loc = ui_sstore1 + client.screen += head + var/used_head_icon = 'maplestation_modules/story_content/deepred_warfare/icons/dreadclothing.dmi' + //if(istype(head, /obj/item/clothing/mask)) + //used_head_icon = 'icons/mob/clothing/mask.dmi' + var/obj/item/force_mask = new /obj/item/clothing/mask/collector/pattern + var/mutable_appearance/head_overlay = force_mask.build_worn_icon(default_layer = DREAD_HEAD_LAYER, default_icon_file = used_head_icon) + // head_overlay.pixel_y -= 15 + + dread_overlays[DREAD_HEAD_LAYER] = head_overlay + + apply_overlay(DREAD_HEAD_LAYER) + update_naming_status() + +/mob/living/basic/redtechdread/update_worn_mask() + update_worn_head() + +/mob/living/basic/redtechdread/update_worn_neck() + remove_overlay(DREAD_NECK_LAYER) + + if(neck) + if(client && hud_used?.hud_shown) + neck.screen_loc = ui_id + client.screen += neck + var/used_neck_icon = 'maplestation_modules/story_content/deepred_warfare/icons/dreadclothing.dmi' + var/obj/item/force_cloak = new /obj/item/clothing/neck/cloak/redtech_dread/pattern + var/mutable_appearance/neck_overlay = force_cloak.build_worn_icon(default_layer = DREAD_NECK_LAYER, default_icon_file = used_neck_icon) + // head_overlay.pixel_y -= 15 + + dread_overlays[DREAD_NECK_LAYER] = neck_overlay + + apply_overlay(DREAD_NECK_LAYER) + update_naming_status() + +/mob/living/basic/redtechdread/update_worn_back() + if(back_storage && client && hud_used?.hud_shown) + back_storage.screen_loc = ui_back + client.screen += back_storage + +/mob/living/basic/redtechdread/update_worn_belt() + if(belt_storage && client && hud_used?.hud_shown) + belt_storage.screen_loc = ui_belt + client.screen += belt_storage + +/mob/living/basic/redtechdread/regenerate_icons() + // FUCK. + update_held_items() + update_worn_head() + update_worn_neck() + update_worn_belt() + update_worn_back() + +/mob/living/basic/redtechdread/proc/update_naming_status() + if(neck) + if(head) + name = "The Collector" + desc = "An enigmatic and imposing mechanical figure. They are wearing an oriental fox mask, with only one eye hole on the light side of their face." + return + name = "The Collector" + desc = "An enigmatic and imposing mechanical figure. Their face can only be described as half sensor array, half volley gun." + return + name = "Redtech Dreadnought Pattern" + desc = "A terrifying robotic multi-limbed monstrosity, covered in armour plating. By looking at their face, you are staring down almost a dozen barrels." + +/mob/living/basic/redtechdread/emp_reaction(severity) + if(EMP_PROTECT_SELF && shielding_level > 0) + playsound(src, 'sound/mecha/mech_shield_deflect.ogg', 120) + src.visible_message(span_warning("[src]'s shield absorbs the EMP!")) + new /obj/effect/temp_visual/faraday/emp(get_turf(src)) + return + + if(health <= 0) + return + + visible_message(span_danger("[src] shudders for a moment.")) + Shake(duration = 1 SECONDS) + switch(severity) + if(EMP_LIGHT) + apply_damage(light_emp_damage) + to_chat(src, span_danger("EMP DETECTED: DAMAGE TO HARDWARE")) + adjust_RL_energy_or_damage(-(light_emp_damage / 4)) + if(EMP_HEAVY) + apply_damage(heavy_emp_damage) + to_chat(src, span_userdanger("WARNING: HEAVY DAMAGE TO HARDWARE")) + adjust_RL_energy_or_damage(-(heavy_emp_damage / 4)) + +/mob/living/basic/redtechdread/electrocute_act(shock_damage, source, siemens_coeff, flags = NONE) + return FALSE + +/mob/living/basic/redtechdread/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25) + if(affect_silicon) + return ..() + +/mob/living/basic/redtechdread/examine(mob/user) + . = list("This is [icon2html(src, user)] \a [src]!") + + //desc + . += "[src.desc]" + + //Hands + for(var/obj/item/held_thing in held_items) + if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM)) + continue + . += "They have [held_thing.examine_title(user)] in their [get_held_index_name(get_held_index_of_item(held_thing))]." + + if(!neck) // If the cloak is not worn, show the internal storage. + //Back storage + if(back_storage && !(back_storage.item_flags & ABSTRACT)) + . += "They are holding [back_storage.examine_title(user)] in their reactor port." + + //Belt storage + if(belt_storage && !(belt_storage.item_flags & ABSTRACT)) + . += "They are holding [belt_storage.examine_title(user)] in their storage compartment." + + //Neckwear + if(neck && !(neck.item_flags & ABSTRACT)) + . += "They are wearing [neck.examine_title(user)]." + + //Cosmetic hat - provides no function other than looks + if(head && !(head.item_flags & ABSTRACT)) + . += "They are wearing [head.examine_title(user)] on their head." + + //Braindead + if(!client && stat != DEAD) + . += "They appear to be currently disconnected." + + //Damaged + if(health != maxHealth) // Note to self, change description if it is wearing a cloak. + if(health > maxHealth * 0.5) + if(neck) + . += span_warning("They make a metallic grinding noise when they move.") + else + . += span_warning("Their armour plates are slightly damaged.") + else //otherwise, below about 50% + if(neck) + . += span_warning("Occasional sparks fly from out under their cloak.") + else + . += span_boldwarning("Their armour plates are heavily damaged!") + + //Dead + if(stat == DEAD) + if(client) + . += span_deadsay("Their systems have been shut down.") + else + . += span_deadsay("They are about as useful as a heap of scrap metal now.") + . += "" + +/mob/living/basic/redtechdread/get_status_tab_items() + . = ..() + switch(energy_level) + if(0) + . += "In low energy mode." + if(1) + . += "In high energy mode." + if(2) + . += "In red lightning mode." + + . += "Red lightning reserves: [RLEnergy]" + +/datum/movespeed_modifier/high_energy + multiplicative_slowdown = -3 + +/datum/movespeed_modifier/RL_energy + multiplicative_slowdown = -3.5 + +/mob/living/basic/redtechdread/proc/update_base_stats() + switch(energy_level) + if(0) + remove_movespeed_modifier(/datum/movespeed_modifier/high_energy) + remove_movespeed_modifier(/datum/movespeed_modifier/RL_energy) + + RemoveElement(/datum/element/wall_tearer, tear_time = 2 SECONDS, reinforced_multiplier = 3, do_after_key = DOAFTER_SOURCE_DREAD_INTERACTION) + RemoveElement(/datum/element/door_pryer, pry_time = 2 SECONDS, interaction_key = DOAFTER_SOURCE_DREAD_INTERACTION) + RemoveElement(/datum/element/wall_tearer, tear_time = 4 SECONDS, reinforced_multiplier = 3, do_after_key = DOAFTER_SOURCE_DREAD_INTERACTION) + RemoveElement(/datum/element/door_pryer, pry_time = 4 SECONDS, interaction_key = DOAFTER_SOURCE_DREAD_INTERACTION) + + AddElement(/datum/element/door_pryer, pry_time = 8 SECONDS, interaction_key = DOAFTER_SOURCE_DREAD_INTERACTION) + + RemoveElement(/datum/element/footstep, FOOTSTEP_MOB_HEAVY, 1, sound_vary = TRUE) + AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW, sound_vary = TRUE) + + RL_energy_regen = 2 + + pixel_x = -16 + base_pixel_x = -16 + + move_force = MOVE_FORCE_NORMAL + move_resist = MOVE_FORCE_STRONG + pull_force = MOVE_FORCE_NORMAL + + attack_verb_continuous = "crushes" + attack_verb_simple = "crush" + + attack_sound = 'sound/effects/tableslam.ogg' + attack_vis_effect = ATTACK_EFFECT_PUNCH + + melee_damage_upper = 15 + melee_damage_lower = 10 + obj_damage = 20 + armour_penetration = 0 + + RemoveElement(/datum/element/shockattack, stun_on_hit = FALSE, shock_damage = 15) + + RemoveElement(/datum/element/effect_trail, /obj/effect/temp_visual/red_lightning_trail) + + if(shielding_level > 0) + updating_shield(1) + else + updating_shield(0) + + if(dynamicSlot1) + dynamicSlot1.Remove(src) + dynamicSlot1 = new /datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/dreadBullet(src) + dynamicSlot1.Grant(src) + + if(dynamicSlot2) + dynamicSlot2.Remove(src) + dynamicSlot2 = new /datum/action/cooldown/mob_cooldown/charge/basic_charge/dread(src) + dynamicSlot2.Grant(src) + + if(dynamicSlot3) + dynamicSlot3.Remove(src) + dynamicSlot3 = new /datum/action/cooldown/mob_cooldown/dreadrepair(src) + dynamicSlot3.Grant(src) + + if(dynamicSlot4) + dynamicSlot4.Remove(src) + dynamicSlot4 = new /datum/action/cooldown/mob_cooldown/heatburst(src) + dynamicSlot4.Grant(src) + + if(1) + remove_movespeed_modifier(/datum/movespeed_modifier/RL_energy) + add_movespeed_modifier(/datum/movespeed_modifier/high_energy) + + RemoveElement(/datum/element/wall_tearer, tear_time = 2 SECONDS, reinforced_multiplier = 3, do_after_key = DOAFTER_SOURCE_DREAD_INTERACTION) + RemoveElement(/datum/element/door_pryer, pry_time = 2 SECONDS, interaction_key = DOAFTER_SOURCE_DREAD_INTERACTION) + RemoveElement(/datum/element/door_pryer, pry_time = 8 SECONDS, interaction_key = DOAFTER_SOURCE_DREAD_INTERACTION) + + AddElement(/datum/element/wall_tearer, tear_time = 4 SECONDS, reinforced_multiplier = 3, do_after_key = DOAFTER_SOURCE_DREAD_INTERACTION) + AddElement(/datum/element/door_pryer, pry_time = 4 SECONDS, interaction_key = DOAFTER_SOURCE_DREAD_INTERACTION) + + RemoveElement(/datum/element/footstep, FOOTSTEP_MOB_HEAVY, 1, sound_vary = TRUE) + RemoveElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW, sound_vary = TRUE) + AddElement(/datum/element/footstep, FOOTSTEP_MOB_HEAVY, 1, sound_vary = TRUE) + + RL_energy_regen = 1 + + pixel_x = -16 + base_pixel_x = -16 + + move_force = MOVE_FORCE_STRONG + move_resist = MOVE_FORCE_STRONG + pull_force = MOVE_FORCE_STRONG + + attack_verb_continuous = "pulverizes" + attack_verb_simple = "pulverize" + + attack_sound = 'sound/effects/meteorimpact.ogg' + attack_vis_effect = ATTACK_EFFECT_SMASH + + melee_damage_upper = 30 + melee_damage_lower = 25 + obj_damage = 40 + armour_penetration = 20 + + RemoveElement(/datum/element/shockattack, stun_on_hit = FALSE, shock_damage = 15) + + RemoveElement(/datum/element/effect_trail, /obj/effect/temp_visual/red_lightning_trail) + + if(shielding_level > 0) + updating_shield(2) + else + updating_shield(0) + + if(dynamicSlot1) + dynamicSlot1.Remove(src) + dynamicSlot1 = new /datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/dreadBullet/high(src) + dynamicSlot1.Grant(src) + + if(dynamicSlot2) + dynamicSlot2.Remove(src) + dynamicSlot2 = new /datum/action/cooldown/mob_cooldown/charge/basic_charge/dread/high(src) + dynamicSlot2.Grant(src) + + if(dynamicSlot3) + dynamicSlot3.Remove(src) + dynamicSlot3 = new /datum/action/cooldown/mob_cooldown/dreadrepair/high(src) + dynamicSlot3.Grant(src) + + if(dynamicSlot4) + dynamicSlot4.Remove(src) + dynamicSlot4 = new /datum/action/cooldown/mob_cooldown/heatburst/high(src) + dynamicSlot4.Grant(src) + + if(2) + remove_movespeed_modifier(/datum/movespeed_modifier/high_energy) + add_movespeed_modifier(/datum/movespeed_modifier/RL_energy) + + RemoveElement(/datum/element/wall_tearer, tear_time = 4 SECONDS, reinforced_multiplier = 3, do_after_key = DOAFTER_SOURCE_DREAD_INTERACTION) + RemoveElement(/datum/element/door_pryer, pry_time = 4 SECONDS, interaction_key = DOAFTER_SOURCE_DREAD_INTERACTION) + RemoveElement(/datum/element/door_pryer, pry_time = 8 SECONDS, interaction_key = DOAFTER_SOURCE_DREAD_INTERACTION) + + AddElement(/datum/element/wall_tearer, tear_time = 2 SECONDS, reinforced_multiplier = 3, do_after_key = DOAFTER_SOURCE_DREAD_INTERACTION) + AddElement(/datum/element/door_pryer, pry_time = 2 SECONDS, interaction_key = DOAFTER_SOURCE_DREAD_INTERACTION) + + RemoveElement(/datum/element/footstep, FOOTSTEP_MOB_HEAVY, 1, sound_vary = TRUE) + RemoveElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW, sound_vary = TRUE) + AddElement(/datum/element/footstep, FOOTSTEP_MOB_HEAVY, 1, sound_vary = TRUE) + + RL_energy_regen = -0.5 + + pixel_x = -16 + base_pixel_x = -16 + + move_force = MOVE_FORCE_VERY_STRONG + move_resist = MOVE_FORCE_VERY_STRONG + pull_force = MOVE_FORCE_VERY_STRONG + + attack_verb_continuous = "annihilates" + attack_verb_simple = "annihilate" + + attack_sound = 'sound/effects/meteorimpact.ogg' + attack_vis_effect = ATTACK_EFFECT_SMASH + + melee_damage_upper = 35 // Plus shock damage. + melee_damage_lower = 30 // Plus shock damage. + obj_damage = 60 + armour_penetration = 40 + + AddElement(/datum/element/shockattack, stun_on_hit = FALSE, shock_damage = 15) + + AddElement(/datum/element/effect_trail, /obj/effect/temp_visual/red_lightning_trail) + + if(shielding_level > 0) + updating_shield(3) + else + updating_shield(0) + + if(dynamicSlot1) + dynamicSlot1.Remove(src) + dynamicSlot1 = new /datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/dreadBullet/lightning(src) + dynamicSlot1.Grant(src) + + if(dynamicSlot2) + dynamicSlot2.Remove(src) + dynamicSlot2 = new /datum/action/cooldown/mob_cooldown/charge/basic_charge/dread/lightning(src) + dynamicSlot2.Grant(src) + + if(dynamicSlot3) + dynamicSlot3.Remove(src) + dynamicSlot3 = new /datum/action/cooldown/mob_cooldown/dreadrepair/lightning(src) + dynamicSlot3.Grant(src) + + if(dynamicSlot4) + dynamicSlot4.Remove(src) + + update_naming_status() + +/mob/living/basic/redtechdread/proc/pre_attack(mob/living/source, atom/target) + SIGNAL_HANDLER + if (target == src) + return COMPONENT_HOSTILE_NO_ATTACK // Easy to misclick yourself, let's not. + if (DOING_INTERACTION(source, DOAFTER_SOURCE_DREAD_INTERACTION)) + balloon_alert(source, "busy!") + return COMPONENT_HOSTILE_NO_ATTACK + return + +/mob/living/basic/redtechdread/proc/adjust_RL_energy(amount) + RLEnergy += amount + if(RLEnergy < -100) + RLEnergy = clamp(RLEnergy, -100, 100) + kick_out_of_RL() + return + RLEnergy = clamp(RLEnergy, -100, 100) + +/mob/living/basic/redtechdread/proc/adjust_RL_energy_or_damage(amount) + if(RLEnergy < 0) + apply_damage(-amount) + return + adjust_RL_energy(amount) + +/mob/living/basic/redtechdread/proc/on_life(seconds_per_tick, times_fired) + adjust_RL_energy(RL_energy_regen) + +/mob/living/basic/redtechdread/proc/kick_out_of_RL() + if(energy_level == 2) + src.balloon_alert(src, "you run out of RL energy!") + energy_level = 0 + playsound(src, 'sound/machines/clockcult/steam_whoosh.ogg', 120) + update_base_stats() + +/mob/living/basic/redtechdread/proc/updating_shield(newlevel) + shielding_level = newlevel + + switch(shielding_level) + if(0) + src.remove_filter(SHIELDING_FILTER) + damage_coeff = list(BRUTE = 1.2, BURN = 0.8, TOX = 0, STAMINA = 0, OXY = 0) // 1250 EHP vs BURN (800 EHP vs BRUTE) + + RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + if(1) + src.remove_filter(SHIELDING_FILTER) + src.add_filter(SHIELDING_FILTER, 2, list("type" = "outline", "color" = COLOR_RED, "alpha" = 160, "size" = 1)) + damage_coeff = list(BRUTE = 1, BURN = 0.5, TOX = 0, STAMINA = 0, OXY = 0) // 2000 EHP vs BURN + + RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + if(2) + src.remove_filter(SHIELDING_FILTER) + src.add_filter(SHIELDING_FILTER, 2, list("type" = "outline", "color" = COLOR_RED, "alpha" = 255, "size" = 1)) + damage_coeff = list(BRUTE = 1, BURN = 0.25, TOX = 0, STAMINA = 0, OXY = 0) // 4000 EHP vs BURN + + RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + if(3) + src.remove_filter(SHIELDING_FILTER) + src.add_filter(SHIELDING_FILTER, 2, list("type" = "outline", "color" = COLOR_RED, "alpha" = 255, "size" = 2)) + damage_coeff = list(BRUTE = 1, BURN = 0.1, TOX = 0, STAMINA = 0, OXY = 0) // 10000 EHP vs BURN (could theoretically tank the KAJARI if it wasn't for the explosive damage) + + RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + + internalshield.update_shield_stats() + +/mob/living/basic/redtechdread/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, required_bodytype) + if(shielding_level > 0) // If the shielding is even on. + if(amount >= 12) // If the brute damage is 12 or more, shield takes damage but deflects the attack. + internalshield.take_hit() + new /obj/effect/temp_visual/faraday/shatter(get_turf(src)) + return 0 + else // Not enough damage to damage the shield, damage is deflected. + playsound(src, 'sound/mecha/mech_shield_deflect.ogg', 120) + src.visible_message(span_warning("[src]'s shield struggles to deflect the impact!")) + new /obj/effect/temp_visual/faraday(get_turf(src)) + return 0 + . = ..() + +/mob/living/basic/redtechdread/adjustFireLoss(amount, updating_health = TRUE, forced = FALSE, required_bodytype) + . = ..() + if(shielding_level > 0) // Shielding is on. + playsound(src, 'sound/mecha/mech_shield_deflect.ogg', 120) + src.visible_message(span_warning("[src]'s shield dampens the thermal energy!")) // This is basically handled by the EHP system but I added this for flavour anyways. + new /obj/effect/temp_visual/faraday/energy(get_turf(src)) + +/mob/living/basic/redtechdread/death(gibbed) + ..(gibbed) + if(back_storage) + dropItemToGround(back_storage) + if(belt_storage) + dropItemToGround(belt_storage) + if(neck) + dropItemToGround(neck) + if(head) + dropItemToGround(head) + +/mob/living/basic/redtechdread/ex_act(severity, target) + if(shielding_level > 0) + switch(severity) + if(EXPLODE_DEVASTATE) + playsound(src, 'sound/mecha/mech_shield_deflect.ogg', 120) + src.visible_message(span_warning("[src]'s shield shatters as it tries deflect the explosion!")) + internalshield.break_shield() + severity = EXPLODE_HEAVY + if(EXPLODE_HEAVY) + playsound(src, 'sound/mecha/mech_shield_deflect.ogg', 120) + src.visible_message(span_warning("[src]'s shield shatters as it tries deflect the explosion!")) + internalshield.break_shield() + severity = EXPLODE_LIGHT + if(EXPLODE_LIGHT) + playsound(src, 'sound/mecha/mech_shield_deflect.ogg', 120) + src.visible_message(span_warning("[src]'s shield struggles to deflect the explosion!")) + internalshield.take_hit() + severity = EXPLODE_NONE + new /obj/effect/temp_visual/faraday/shatter(get_turf(src)) + return ..() + +#undef DOAFTER_SOURCE_DREAD_INTERACTION diff --git a/maplestation_modules/story_content/deepred_warfare/code/redeffects.dm b/maplestation_modules/story_content/deepred_warfare/code/redeffects.dm new file mode 100644 index 000000000000..db6dfa374dd0 --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/redeffects.dm @@ -0,0 +1,33 @@ +/obj/effect/temp_visual/red_lightning_trail + name = "red lightning trail" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/redeffects.dmi' + icon_state = "trail" + duration = 15 + + light_on = TRUE + light_range = 2 + light_power = 2 + light_color = COLOR_RED + light_system = STATIC_LIGHT + +/obj/effect/temp_visual/faraday + name = "faraday block" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/redeffects64.dmi' + icon_state = "block" + layer = LARGE_MOB_LAYER + anchored = TRUE + duration = 2 + pixel_x = -16 + pixel_y = -16 + +/obj/effect/temp_visual/faraday/energy + name = "faraday absorb" + icon_state = "absorb" + +/obj/effect/temp_visual/faraday/emp + name = "faraday emp" + icon_state = "emp" + +/obj/effect/temp_visual/faraday/shatter + name = "faraday shatter" + icon_state = "shatter" diff --git a/maplestation_modules/story_content/deepred_warfare/code/redmaterials.dm b/maplestation_modules/story_content/deepred_warfare/code/redmaterials.dm new file mode 100644 index 000000000000..b916b31af790 --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/redmaterials.dm @@ -0,0 +1,98 @@ +/obj/item/stack/sheet/mineral/aerialite + name = "alloyed aerialite" + desc = "An alloyed blue metal. It shimmers with the power of the skies and cosmos." + singular_name = "alloyed aerialite bar" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/redmaterials.dmi' + lefthand_file = 'icons/mob/inhands/items/sheets_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items/sheets_righthand.dmi' + icon_state = "sheet-aerialite" + inhand_icon_state = "sheet-adamantine" // ADD SPRITES. + mats_per_unit = list(/datum/material/aerialite=SHEET_MATERIAL_AMOUNT) + grind_results = list(/datum/reagent/gravitum/aerialite = 20) + merge_type = /obj/item/stack/sheet/mineral/aerialite + material_type = /datum/material/aerialite + armor_type = /datum/armor/sheet_aerialite + // FUN FACT: This this actually an aerialite --> cosmilite alloy. + +/datum/armor/sheet_aerialite + fire = 100 + acid = 80 + +/datum/material/aerialite + name = "alloyed aerialite" + desc = "Alloyed Aerialite" + color = "#00d0ff" + greyscale_colors = "#00d0ff" + strength_modifier = 3 // You must be a maniac to forge something out of this in the first place. + categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + sheet_type = /obj/item/stack/sheet/mineral/aerialite + value_per_unit = 6000 / SHEET_MATERIAL_AMOUNT // Giga valuable. + beauty_modifier = 1.5 // I think aerialite / cosmilite looks nice. + armor_modifiers = list(MELEE = 2.5, BULLET = 2.5, LASER = 1.5, ENERGY = 1.5, BOMB = 2.5, BIO = 1, FIRE = 1.5, ACID = 1.5) + +/datum/material/aerialite/on_applied_obj(obj/o, amount, material_flags) + . = ..() + o.AddElement(/datum/element/forced_gravity, 0) + +/datum/material/aerialite/on_removed_obj(obj/o, amount, material_flags) + . = ..() + o.RemoveElement(/datum/element/forced_gravity, 0) + +/obj/item/stack/sheet/mineral/resmythril + name = "resonant mythril" + desc = "A resonant turquoise metal. It shimmers with the power of souls and essences." + singular_name = "resonant mythril bar" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/redmaterials.dmi' + lefthand_file = 'icons/mob/inhands/items/sheets_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items/sheets_righthand.dmi' + icon_state = "sheet-resonant" + inhand_icon_state = "sheet-adamantine"// ADD SPRITES. + mats_per_unit = list(/datum/material/resmythril=SHEET_MATERIAL_AMOUNT) + grind_results = list(/datum/reagent/resmythril = 20) + merge_type = /obj/item/stack/sheet/mineral/resmythril + material_type = /datum/material/resmythril + armor_type = /datum/armor/sheet_resmythril + // FUN FACT: I chose mythril for the scanner module because it's used in the codebreaker's scanner. + +/datum/armor/sheet_resmythril + fire = 100 + acid = 80 + +/datum/material/resmythril + name = "resonant mythril" + desc = "Resonant Mythril" + color = "#14747c" + greyscale_colors = "#14747c" + strength_modifier = 2 + categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + sheet_type = /obj/item/stack/sheet/mineral/resmythril + value_per_unit = 6000 / SHEET_MATERIAL_AMOUNT + beauty_modifier = 2 + armor_modifiers = list(MELEE = 1.5, BULLET = 1.5, LASER = 2.5, ENERGY = 2.5, BOMB = 1.5, BIO = 1, FIRE = 2.5, ACID = 1.5) + // We already have mythril in the code, so this is resonant. + +/obj/item/stack/sheet/mineral/miracle_matter + name = "Miracle Matter" + desc = "Its amorphous form contains untold destructive potential. Wish upon a star." + singular_name = "Miracle Matter" + icon = "" // ADD SPRITES. + lefthand_file = 'icons/mob/inhands/items/sheets_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items/sheets_righthand.dmi' + icon_state = "sheet-runite" + mats_per_unit = list(/datum/material/miracle=SHEET_MATERIAL_AMOUNT) + grind_results = list(/datum/reagent/miracle = 1) + merge_type = /obj/item/stack/sheet/mineral/miracle_matter + material_type = /datum/material/miracle + +/datum/material/miracle + name = "Miracle Matter" + desc = "Miracle Matter" + color = "#e6a6e0" + greyscale_colors = "#e6a6e0" + strength_modifier = 20 // UNTOLD DESTRUCTIVE POTENTIAL. + categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + sheet_type = /obj/item/stack/sheet/mineral/miracle_matter + value_per_unit = 60000 / SHEET_MATERIAL_AMOUNT + beauty_modifier = 10 + armor_modifiers = list(MELEE = 10, BULLET = 10, LASER = 10, ENERGY = 10, BOMB = 10, BIO = 10, FIRE = 10, ACID = 10) + // The Miracle Matter. This material code will probably never show up, but if you make a toolbox out of Miracle Matter, you are going to oneshot everything. diff --git a/maplestation_modules/story_content/deepred_warfare/code/redparts.dm b/maplestation_modules/story_content/deepred_warfare/code/redparts.dm new file mode 100644 index 000000000000..f99d2ec18fde --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/redparts.dm @@ -0,0 +1,202 @@ +// Need to override this so that T5 parts can be used without crashing. +/datum/stock_part/energy_rating() + switch (tier) + if (1) + return 1 + if (2) + return 3 + if (3) + return 5 + if (4) + return 10 + if (5) + return 20 + else + CRASH("Invalid level given to energy_rating: [tier]") + +/obj/item/stock_parts/cell/redtech + name = "processed red power cell" + desc = "A processed power cell. Its design is unlike anything you've seen before. It seems to be EMP resistant." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/redparts.dmi' + icon_state = "redcell" + // connector_type = "redcellconnector" + charge_light_type = null + rating = 4 + + maxcharge = STANDARD_CELL_CHARGE * 50 + chargerate = STANDARD_CELL_CHARGE * 5 + + // Useful for scrapping. + custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT, /datum/material/plasma=SHEET_MATERIAL_AMOUNT, /datum/material/bluespace=SHEET_MATERIAL_AMOUNT) + grind_results = list(/datum/reagent/iron = 15, /datum/reagent/bluespace = 15, /datum/reagent/consumable/liquidelectricity/auric = 15) + +/obj/item/stock_parts/cell/redtech/empty + empty = TRUE + +/obj/item/stock_parts/cell/redtech/Initialize(mapload) + AddElement(/datum/element/empprotection, EMP_PROTECT_SELF) + return ..() + +/obj/item/stock_parts/cell/redtech/update_overlays() + . = ..() + . += emissive_appearance(icon, "redcellemissive", src, alpha = src.alpha) + +/obj/item/stock_parts/cell/redtech/nonetech + name = "Nonetech power cell" + desc = "A Nonetech power cell. Its design is oddly familiar. It seems to be EMP resistant." + icon_state = "nonecell" + +/datum/stock_part/matter_bin/tier5 + tier = 5 + physical_object_type = /obj/item/stock_parts/cell/redtech + +/obj/item/stock_parts/servo/redtech + name = "alloyed red servo" + desc = "An alloyed servo module. Its design is unlike anything you've seen before." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/redparts.dmi' + icon_state = "redservo" + + rating = 5 + energy_rating = 20 + + // Useful for scrapping. + custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT, /datum/material/titanium=SHEET_MATERIAL_AMOUNT, /datum/material/diamond=SHEET_MATERIAL_AMOUNT) + grind_results = list(/datum/reagent/iron = 15, /datum/reagent/carbon = 15, /datum/reagent/gravitum/aerialite = 15) + +/obj/item/stock_parts/servo/redtech/update_overlays() + . = ..() + . += emissive_appearance(icon, "redservoemissive", src, alpha = src.alpha) + +/obj/item/stock_parts/servo/redtech/Initialize(mapload) + . = ..() + update_appearance() + +/datum/stock_part/servo/tier5 + tier = 5 + physical_object_type = /obj/item/stock_parts/servo/redtech + +// Basically have to override the entire thing to allow T5 servos to dispense emagged reagents. +/obj/machinery/chem_dispenser/RefreshParts() + . = ..() + recharge_amount = initial(recharge_amount) + var/newpowereff = 0.0666666 + var/parts_rating = 0 + for(var/obj/item/stock_parts/cell/stock_cell in component_parts) + cell = stock_cell + for(var/datum/stock_part/matter_bin/matter_bin in component_parts) + newpowereff += 0.0166666666 * matter_bin.tier + parts_rating += matter_bin.tier + for(var/datum/stock_part/capacitor/capacitor in component_parts) + recharge_amount *= capacitor.tier + parts_rating += capacitor.tier + for(var/datum/stock_part/servo/servo in component_parts) + if (servo.tier > 3) + dispensable_reagents |= upgrade_reagents + else + dispensable_reagents -= upgrade_reagents + // Tier 5 servo parts are special and allow for emagged reagents. + if (servo.tier > 4) + dispensable_reagents |= emagged_reagents + else + dispensable_reagents -= emagged_reagents + parts_rating += servo.tier + powerefficiency = round(newpowereff, 0.01) + +/obj/item/stock_parts/capacitor/redtech + name = "processed red capacitor" + desc = "A processed capacitor module. Its design is unlike anything you've seen before." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/redparts.dmi' + icon_state = "redcapacitor" + + rating = 5 + energy_rating = 20 + + // Useful for scrapping. + custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT, /datum/material/titanium=SHEET_MATERIAL_AMOUNT, /datum/material/bluespace=SHEET_MATERIAL_AMOUNT) + grind_results = list(/datum/reagent/iron = 15, /datum/reagent/bluespace = 15, /datum/reagent/consumable/liquidelectricity/auric = 15) + +/obj/item/stock_parts/capacitor/redtech/update_overlays() + . = ..() + . += emissive_appearance(icon, "redcapacitoremissive", src, alpha = src.alpha) + +/obj/item/stock_parts/capacitor/redtech/Initialize(mapload) + . = ..() + update_appearance() + +/datum/stock_part/capacitor/tier5 + tier = 5 + physical_object_type = /obj/item/stock_parts/capacitor/redtech + +/obj/item/stock_parts/scanning_module/redtech + name = "resonant red scanning module" + desc = "A resonant scanning module. Its design is unlike anything you've seen before." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/redparts.dmi' + icon_state = "redscanner" + + rating = 5 + energy_rating = 20 + + // Useful for scrapping. + custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT, /datum/material/gold=SHEET_MATERIAL_AMOUNT, /datum/material/bluespace=SHEET_MATERIAL_AMOUNT) + grind_results = list(/datum/reagent/gold = 15, /datum/reagent/bluespace = 15, /datum/reagent/resmythril = 15) + +/obj/item/stock_parts/scanning_module/redtech/update_overlays() + . = ..() + . += emissive_appearance(icon, "redscanneremissive", src, alpha = src.alpha) + +/obj/item/stock_parts/scanning_module/redtech/Initialize(mapload) + . = ..() + update_appearance() + +/datum/stock_part/scanning_module/tier5 + tier = 5 + physical_object_type = /obj/item/stock_parts/scanning_module/redtech + +/obj/item/stock_parts/micro_laser/redtech + name = "crystalline red micro laser" + desc = "A crystalline laser module. Its design is unlike anything you've seen before." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/redparts.dmi' + icon_state = "redlaser" + + rating = 5 + energy_rating = 20 + + // Useful for scrapping. + custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT, /datum/material/uranium=SHEET_MATERIAL_AMOUNT, /datum/material/bluespace=SHEET_MATERIAL_AMOUNT) + grind_results = list(/datum/reagent/uranium = 15, /datum/reagent/bluespace = 15, /datum/reagent/exodust = 15) + +/obj/item/stock_parts/micro_laser/redtech/update_overlays() + . = ..() + . += emissive_appearance(icon, "redlaseremissive", src, alpha = src.alpha) + +/obj/item/stock_parts/micro_laser/redtech/Initialize(mapload) + . = ..() + update_appearance() + +/datum/stock_part/micro_laser/tier5 + tier = 5 + physical_object_type = /obj/item/stock_parts/micro_laser/redtech +/obj/item/stock_parts/matter_bin/redtech + name = "condensed red matter bin" + desc = "A condensed matter bin. Its design is unlike anything you've seen before." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/redparts.dmi' + icon_state = "redmatterbin" + + rating = 5 + energy_rating = 20 + + // Useful for scrapping. + custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT, /datum/material/titanium=SHEET_MATERIAL_AMOUNT, /datum/material/bluespace=SHEET_MATERIAL_AMOUNT) + grind_results = list(/datum/reagent/iron = 15, /datum/reagent/bluespace = 15, /datum/reagent/darkplasma = 15) + +/obj/item/stock_parts/matter_bin/redtech/update_overlays() + . = ..() + . += emissive_appearance(icon, "redmatterbinemissive", src, alpha = src.alpha) + +/obj/item/stock_parts/matter_bin/redtech/Initialize(mapload) + . = ..() + update_appearance() + +/datum/stock_part/matter_bin/tier5 + tier = 5 + physical_object_type = /obj/item/stock_parts/matter_bin/redtech diff --git a/maplestation_modules/story_content/deepred_warfare/code/redprojectiles.dm b/maplestation_modules/story_content/deepred_warfare/code/redprojectiles.dm new file mode 100644 index 000000000000..728439bf71b5 --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/redprojectiles.dm @@ -0,0 +1,322 @@ +/obj/projectile/bullet/coil + name ="low velocity 10mm coilslug" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "coilslug" + range = 60 + damage = 30 + armour_penetration = 20 + dismemberment = 0 + damage_falloff_tile = 0 + wound_bonus = 10 + wound_falloff_tile = -1 + embed_falloff_tile = -1 + +/obj/item/ammo_casing/coil + name = "internal low velocity 10mm coilslug" + desc = "You should not be seeing this." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/coilguns.dmi' + icon_state = "debug" + projectile_type = /obj/projectile/bullet/coil + + fire_sound = 'maplestation_modules/story_content/deepred_warfare/sound/coilshoot.ogg' + delay = 0 // How long it takes to fire the ammo? + + var/select_name = "low velocity" + var/ammo_energy_usage = 1000 // How much power it takes to fire the ammo (note, redtech cells have 50000 energy). + var/ammo_heat_generation = 20 // How much heat it generates. + +/obj/projectile/bullet/coil/highvelo + name ="10mm coilslug" + icon_state = "high_velo" + speed = 0.4 // Twice as fast as normal. + range = 80 + damage = 40 + armour_penetration = 50 + dismemberment = 10 + wound_bonus = 20 + +/obj/item/ammo_casing/coil/highvelo + name = "internal 10mm coilslug" + projectile_type = /obj/projectile/bullet/coil/highvelo + + select_name = "high velocity" + ammo_energy_usage = 2000 + ammo_heat_generation = 30 + +/obj/projectile/bullet/coil/overcharge + name = "overcharged slag" + icon_state = "overcharge" + damage_type = BURN + reflectable = NONE + parried = TRUE + damage = 5 + range = 10 + armour_penetration = 0 + weak_against_armour = TRUE + dismemberment = 5 + wound_bonus = 5 + bare_wound_bonus = 5 + wound_falloff_tile = -1 + embed_falloff_tile = -1 + damage_falloff_tile = -1 + +/obj/item/ammo_casing/coil/overcharge + name = "internal overcharge slag" + projectile_type = /obj/projectile/bullet/coil/overcharge + fire_sound = 'maplestation_modules/story_content/deepred_warfare/sound/coilbang.ogg' + + select_name = "overcharge" + ammo_energy_usage = 5000 + ammo_heat_generation = 60 + pellets = 12 + variance = 30 + +/obj/projectile/bullet/coil/piercing + name ="low velocity 10mm armour piercing coilslug" + damage = 20 + armour_penetration = 50 + + var/object_damage = 30 + var/mecha_damage = 20 + +/obj/projectile/bullet/coil/piercing/on_hit(atom/target, blocked = 0, pierce_hit) + if(isobj(target) && (blocked != 100)) + var/obj/thing_to_break = target + var/damage_to_deal = object_damage + if(ismecha(thing_to_break) && mecha_damage) + damage_to_deal += mecha_damage + if(damage_to_deal) + thing_to_break.take_damage(damage_to_deal, BRUTE, BULLET, FALSE) + return ..() + +/obj/item/ammo_casing/coil/piercing + name = "internal low velocity 10mm armour piercing coilslug" + projectile_type = /obj/projectile/bullet/coil/piercing + select_name = "low velocity AP" + +/obj/projectile/bullet/coil/highvelo/piercing + name ="10mm armour piercing coilslug" + damage = 30 + armour_penetration = 80 + projectile_piercing = PASSMOB + + var/object_damage = 40 + var/mecha_damage = 30 + +/obj/projectile/bullet/coil/highvelo/piercing/on_hit(atom/target, blocked = 0, pierce_hit) + if(isobj(target) && (blocked != 100)) + var/obj/thing_to_break = target + var/damage_to_deal = object_damage + if(ismecha(thing_to_break) && mecha_damage) + damage_to_deal += mecha_damage + if(damage_to_deal) + thing_to_break.take_damage(damage_to_deal, BRUTE, BULLET, FALSE) + return ..() + +/obj/item/ammo_casing/coil/highvelo/piercing + name = "internal 10mm armour piercing coilslug" + projectile_type = /obj/projectile/bullet/coil/highvelo/piercing + + select_name = "high velocity AP" + ammo_heat_generation = 40 + +/obj/projectile/bullet/coil/red_lightning + name = "charged 10mm coilslug" + icon_state = "red_lightning" + range = 80 + damage = 30 + armour_penetration = 60 + + var/extra_damage = 30 // Damage done by shock aftereffect. + var/emp_radius = 1 // Radius of EMP effect. + + muzzle_type = /obj/effect/projectile/muzzle/RLcoil + tracer_type = /obj/effect/projectile/tracer/RLcoil + impact_type = /obj/effect/projectile/impact/RLcoil + hitscan = TRUE + impact_effect_type = null + hitscan_light_intensity = 3 + hitscan_light_range = 0.75 + hitscan_light_color_override = COLOR_RED_LIGHT + muzzle_flash_intensity = 5 + muzzle_flash_range = 1 + muzzle_flash_color_override = COLOR_RED_LIGHT + impact_light_intensity = 5 + impact_light_range = 1 + impact_light_color_override = COLOR_RED_LIGHT + +/obj/projectile/bullet/coil/red_lightning/on_hit(atom/target, blocked = 0, pierce_hit) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/shocked_fellow = target + shocked_fellow.electrocute_act(extra_damage, src, 1, SHOCK_NOSTUN | SHOCK_NOGLOVES) + else + empulse(target, emp_radius, emp_radius) + new /obj/effect/temp_visual/impact_effect/ion(get_turf(target)) + +/obj/effect/projectile/muzzle/RLcoil + name = "charged coilslug muzzle flash" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "rl_coil_muzzle" + +/obj/effect/projectile/tracer/RLcoil + name = "charged coilslug tracer" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "rl_coil_tracer" + +/obj/effect/projectile/impact/RLcoil + name = "charged coilslug impact" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "rl_coil_impact" + +/obj/projectile/bullet/coil/red_lightning/generate_hitscan_tracers(cleanup = TRUE, duration = 2 SECONDS, impacting = TRUE) + duration = 2 SECONDS + . = ..() + +/obj/projectile/bullet/godslayer + name = "annihilator round" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "godslayer_tracer" + range = 120 + damage = 80 + armour_penetration = 100 + + dismemberment = 10 + catastropic_dismemberment = TRUE + parried = TRUE + + projectile_piercing = PASSTABLE|PASSGLASS|PASSGRILLE|PASSBLOB|PASSMOB|PASSMACHINE|PASSSTRUCTURE|PASSFLAPS|PASSDOORS|PASSVEHICLE + + muzzle_type = /obj/effect/projectile/muzzle/godslayer + tracer_type = /obj/effect/projectile/tracer/godslayer + impact_type = /obj/effect/projectile/impact/godslayer + hitscan = TRUE + impact_effect_type = null + hitscan_light_intensity = 3 + hitscan_light_range = 0.75 + hitscan_light_color_override = COLOR_BLUE_LIGHT + muzzle_flash_intensity = 5 + muzzle_flash_range = 1 + muzzle_flash_color_override = COLOR_BLUE_LIGHT + impact_light_intensity = 5 + impact_light_range = 1 + impact_light_color_override = COLOR_BLUE_LIGHT + + var/extra_damage = 500 // Extra damage dealt to non-mob, non-vehicle objects (secondary shot will not trigger). + + var/datum/marked_target + + var/warp_sound = 'maplestation_modules/story_content/deepred_warfare/sound/techpowerup.ogg' + var/fire_sound = 'maplestation_modules/story_content/deepred_warfare/sound/techblaster.ogg' + +/obj/projectile/bullet/godslayer/fire(angle, atom/direct_target, make_sound) + if(make_sound != null) + playsound(firer, fire_sound, 100, extrarange = 10) + . = ..() + INVOKE_ASYNC(src, PROC_REF(fire_warp)) + +/obj/projectile/bullet/godslayer/on_hit(atom/target, blocked = 0, pierce_hit) + . = ..() + if(marked_target != null) + return + if(isliving(target) || isvehicle(target)) + marked_target = target + else + target.take_damage(extra_damage) + +/obj/projectile/bullet/godslayer/proc/fire_warp() + sleep(35) + if(marked_target == null || QDELETED(marked_target) || QDELETED(firer)) + return + playsound(firer, warp_sound, 100, extrarange = 10) + sleep(25) + + if(marked_target != null && !QDELETED(marked_target) && !QDELETED(firer)) + var/obj/projectile/A = new /obj/projectile/bullet/supergodslayer(get_turf(firer)) + A.preparePixelProjectile(marked_target, get_turf(firer)) + A.firer = firer + A.fired_from = firer + A.fire(null, marked_target) + +/obj/effect/projectile/muzzle/godslayer + name = "annihilator muzzle flash" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "godslayer_muzzle" + +/obj/effect/projectile/tracer/godslayer + name = "annihilator tracer" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "godslayer_tracer" + +/obj/effect/projectile/impact/godslayer + name = "annihilator warp site" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "godslayer_impact" + +/obj/projectile/bullet/godslayer/generate_hitscan_tracers(cleanup = TRUE, duration = 4 SECONDS, impacting = TRUE) + duration = 4 SECONDS + . = ..() + +/obj/projectile/bullet/supergodslayer + name = "supercharged annihilator round" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "godslayer_tracer" + range = 120 + damage = 800 + armour_penetration = 100 + + dismemberment = 10 + catastropic_dismemberment = TRUE + parried = TRUE + + muzzle_type = /obj/effect/projectile/muzzle/supergodslayer + tracer_type = /obj/effect/projectile/tracer/supergodslayer + impact_type = /obj/effect/projectile/impact/supergodslayer + hitscan = TRUE + impact_effect_type = null + hitscan_light_intensity = 3 + hitscan_light_range = 0.75 + hitscan_light_color_override = COLOR_BLUE_LIGHT + muzzle_flash_intensity = 5 + muzzle_flash_range = 1 + muzzle_flash_color_override = COLOR_BLUE_LIGHT + impact_light_intensity = 5 + impact_light_range = 1 + impact_light_color_override = COLOR_BLUE_LIGHT + + var/supercharge_sound = 'maplestation_modules/story_content/deepred_warfare/sound/techexplosion.ogg' + +/obj/effect/projectile/muzzle/supergodslayer + name = "supercharged annihilator warp site" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "supergodslayer_muzzle" + +/obj/effect/projectile/tracer/supergodslayer + name = "supercharged annihilator tracer" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "supergodslayer_tracer" + +/obj/effect/projectile/impact/supergodslayer + name = "supercharged annihilator impact" + icon = 'maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi' + icon_state = "supergodslayer_impact" + +/obj/projectile/bullet/supergodslayer/generate_hitscan_tracers(cleanup = TRUE, duration = 5 SECONDS, impacting = TRUE) + duration = 5 SECONDS + . = ..() + +/obj/projectile/bullet/supergodslayer/fire(angle, atom/direct_target) + playsound(firer, supercharge_sound, 100, extrarange = 10) + . = ..() + +/obj/projectile/bullet/supergodslayer/on_hit(atom/target, blocked = 0, pierce_hit) + . = ..() + if (!QDELETED(target) && isliving(target)) + var/mob/living/duster = target + if(duster.client) + var/client/hopper = duster.client + hopper << link("byond://terry.tgstation13.org:3336") + duster.dust(just_ash = TRUE, drop_items = FALSE, force = TRUE) + if(. == BULLET_ACT_HIT && !pierce_hit) + explosion(src, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 2, flame_range = 2, flash_range = 3, adminlog = FALSE) + new /obj/effect/temp_visual/cosmic_explosion(get_turf(src)) diff --git a/maplestation_modules/story_content/deepred_warfare/code/redreagents.dm b/maplestation_modules/story_content/deepred_warfare/code/redreagents.dm new file mode 100644 index 000000000000..0bfded8c4506 --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/redreagents.dm @@ -0,0 +1,269 @@ +/datum/reagent/consumable/liquidelectricity/auric + name = "Processed Auric Tesla" + description = "A processed metallic gel that seems to spark and crackle with electricity. It is unlike anything you've seen before." + color = "#fff870" + taste_description = "absolute power" + var/shock_timer = 0 + var/shock_speed = 20 + creation_purity = 1 + +/datum/reagent/consumable/liquidelectricity/auric/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) + . = ..() + shock_timer++ + if(shock_timer >= rand(0, 50 - shock_speed)) + shock_timer = 0 + affected_mob.electrocute_act(rand(10, 30), "Auric Tesla in their body", 1, SHOCK_NOGLOVES) + playsound(affected_mob, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + + if(affected_mob?.mana_pool) + affected_mob.adjust_personal_mana(10) // HOLY JESUS, BATMAN. + +/datum/chemical_reaction/auricelectrolysis + results = list(/datum/reagent/oxygen = 10, /datum/reagent/hydrogen = 20) + required_reagents = list(/datum/reagent/water = 10) + required_catalysts = list(/datum/reagent/consumable/liquidelectricity/auric = 1) + reaction_tags = REACTION_TAG_UNIQUE | REACTION_TAG_CHEMICAL + mix_message = "the reaction zaps suddenly!" + mix_sound = 'sound/effects/supermatter.ogg' + +/datum/reagent/gravitum/aerialite + name = "Alloyed Aerialite" + description = "A powdered alloy of a strange blue metal that seems to defy the laws of gravity. It is unlike anything you've seen before." + color = "#00aaff" + taste_description = "the boundless sky" + chemical_flags = null + taste_mult = 1 + reagent_state = SOLID + +/datum/reagent/resmythril + name = "Resonant Mythril" + description = "A powdered turquoise metal that seems to resonate with electromagnetic waves. It is unlike anything you've seen before." + color = "#14747c" + taste_description = "resonance" + reagent_state = SOLID + +/datum/chemical_reaction/resmythril_emp + required_reagents = list(/datum/reagent/uranium = 1, /datum/reagent/resmythril = 1) + reaction_tags = REACTION_TAG_UNIQUE | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS + mix_message = "the reaction resonates!" + mix_sound = 'sound/machines/defib_zap.ogg' + +/datum/chemical_reaction/resmythril_emp/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) + var/location = get_turf(holder.my_atom) + empulse(location, round(created_volume / 12), round(created_volume / 7), 1) + holder.clear_reagents() + +/datum/reagent/exodust + name = "Crystalline ExoPrism" + description = "A pulverized crystalline dust that seems to be unusually energized. It is unlike anything you've seen before." + color = "#d3d1ed" + taste_description = "a forge of a bygone era" + reagent_state = SOLID + +/datum/chemical_reaction/exo_stabilizer + results = list(/datum/reagent/exotic_stabilizer = 1) + required_reagents = list(/datum/reagent/exodust = 1,/datum/reagent/stabilizing_agent = 1) + reaction_tags = REACTION_TAG_UNIQUE | REACTION_TAG_CHEMICAL + +/datum/reagent/darkplasma + name = "Condensed Dark Plasma" + description = "A swirling dark liquid that seems to dissipate any light around it. It is unlike anything you've seen before." + color = "#0e0033" + taste_description = "an endless void" + metabolization_rate = 4 * REAGENTS_METABOLISM + +/datum/reagent/darkplasma/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) + . = ..() + if(affected_mob.damage_random_bodypart(10*REM*seconds_per_tick)) // Health rework compatibility. + // if(affected_mob.take_bodypart_damage(10*REM*seconds_per_tick, 0)) // Original code. + return UPDATE_MOB_HEALTH + + if(affected_mob?.mana_pool) + affected_mob.adjust_personal_mana(-10) + +/datum/chemical_reaction/plasma_vortex + required_reagents = list(/datum/reagent/darkplasma = 1) + required_temp = 474 + reaction_tags = REACTION_TAG_UNIQUE | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS + mix_message = "the reaction destabilizes!" + mix_sound = 'sound/magic/cosmic_energy.ogg' + +/datum/chemical_reaction/plasma_vortex/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) + var/turf/T = get_turf(holder.my_atom) + var/range = clamp(sqrt(created_volume), 1, 6) + goonchem_vortex(T, 0, range) + +/datum/reagent/miracle + name = "Prototype Miracle Matter" + description = "A shifting web of fractal energies, it seems to shift to be a solid, liquid, or gas. It is unlike anything you've seen before." + color = "#e6a6e0" + taste_description = "a universe far, far away" + metabolization_rate = 0.05 * REAGENTS_METABOLISM + +/datum/reagent/miracle/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) + . = ..() + radiation_pulse(affected_mob, max_range = 1, threshold = 0.1, chance = 80) + if(affected_mob.adjustToxLoss(10 * seconds_per_tick * REM, updating_health = FALSE)) + return UPDATE_MOB_HEALTH + +/datum/reagent/miracle/expose_turf(turf/exposed_turf) + . = ..() + if(isspaceturf(exposed_turf)) + return + + radiation_pulse(holder, max_range = 4, threshold = 0.1, chance = 80) + +/datum/reagent/miracle/expose_obj(obj/exposed_obj) + . = ..() + radiation_pulse(holder, max_range = 4, threshold = 0.1, chance = 80) + +/datum/reagent/miracle/expose_mob(mob/living/exposed_mob, methods=TOUCH) + . = ..() + radiation_pulse(exposed_mob, max_range = 4, threshold = 0.1, chance = 80) + +/datum/chemical_reaction/miracle_creation + results = list(/datum/reagent/miracle = 1) + required_reagents = list(/datum/reagent/consumable/liquidelectricity/auric = 30, /datum/reagent/gravitum/aerialite = 30, /datum/reagent/resmythril = 30, /datum/reagent/exodust = 30, /datum/reagent/darkplasma = 30) + reaction_tags = REACTION_TAG_UNIQUE | REACTION_TAG_CHEMICAL + mix_message = "the reaction fractalizes!" + mix_sound = 'sound/magic/cosmic_expansion.ogg' + +/datum/chemical_reaction/miracle_creation/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) + radiation_pulse(holder, max_range = 6, threshold = 0.1, chance = 80) + +/datum/reagent/aggregation_agent + name = "Aggregation Agent" + description = "A specially designed agent used to solidify metals. This does not work on everything." + reagent_state = LIQUID + color = "#00ff08" + taste_description = "metal" + chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + +/datum/chemical_reaction/aggregation_creation + results = list(/datum/reagent/aggregation_agent = 1) + required_reagents = list(/datum/reagent/toxin/plasma = 1, /datum/reagent/liquid_dark_matter = 1, /datum/reagent/iron = 5) + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_CHEMICAL + +/datum/chemical_reaction/silver_aggregation + required_reagents = list(/datum/reagent/aggregation_agent = 5, /datum/reagent/silver = 20) + mob_react = FALSE + reaction_flags = REACTION_INSTANT + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_OTHER + +/datum/chemical_reaction/silver_aggregation/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) + var/location = get_turf(holder.my_atom) + for(var/i in 1 to created_volume) + new /obj/item/stack/sheet/mineral/silver(location) + +/datum/chemical_reaction/gold_aggregation + required_reagents = list(/datum/reagent/aggregation_agent = 5, /datum/reagent/gold = 20) + mob_react = FALSE + reaction_flags = REACTION_INSTANT + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_OTHER + +/datum/chemical_reaction/gold_aggregation/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) + var/location = get_turf(holder.my_atom) + for(var/i in 1 to created_volume) + new /obj/item/stack/sheet/mineral/gold(location) + +/datum/chemical_reaction/uranium_aggregation + required_reagents = list(/datum/reagent/aggregation_agent = 5, /datum/reagent/uranium = 20) + mob_react = FALSE + reaction_flags = REACTION_INSTANT + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_OTHER + +/datum/chemical_reaction/uranium_aggregation/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) + var/location = get_turf(holder.my_atom) + for(var/i in 1 to created_volume) + new /obj/item/stack/sheet/mineral/uranium(location) + +/datum/chemical_reaction/bs_aggregation + required_reagents = list(/datum/reagent/aggregation_agent = 5, /datum/reagent/bluespace = 20) + mob_react = FALSE + reaction_flags = REACTION_INSTANT + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_OTHER + +/datum/chemical_reaction/bs_aggregation/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) + var/location = get_turf(holder.my_atom) + for(var/i in 1 to created_volume) + new /obj/item/stack/sheet/bluespace_crystal(location) + +/datum/chemical_reaction/aerialite_aggregation + required_reagents = list(/datum/reagent/aggregation_agent = 5, /datum/reagent/gravitum/aerialite = 20) + mob_react = FALSE + reaction_flags = REACTION_INSTANT + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_OTHER + +/datum/chemical_reaction/aerialite_aggregation/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) + var/location = get_turf(holder.my_atom) + for(var/i in 1 to created_volume) + new /obj/item/stack/sheet/mineral/aerialite(location) + +/datum/chemical_reaction/resmythril_aggregation + required_reagents = list(/datum/reagent/aggregation_agent = 5, /datum/reagent/resmythril = 20) + mob_react = FALSE + reaction_flags = REACTION_INSTANT + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_OTHER + +/datum/chemical_reaction/resmythril_aggregation/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) + var/location = get_turf(holder.my_atom) + for(var/i in 1 to created_volume) + new /obj/item/stack/sheet/mineral/resmythril(location) + +/datum/reagent/consumable/liquidelectricity/auric/redlightning + name = "Liquid Red Lightning" + description = "A liquid lightning that seems to sputter with explosive power. It is unlike anything you've seen before." + color = "#ff4545" + taste_description = "godlike power" + shock_speed = 40 // YOU WILL HAVE A VERY BAD TIME DRINKING THIS. + +/obj/item/reagent_containers/cup/beaker/redlightning + name = "red lightning container" + desc = "A strange, heavy-duty electromagnetic stasis container, powered by unknown technology. Can hold up to 300 units." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/redcanister.dmi' + icon_state = "redlightning" + custom_materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT * 2, /datum/material/plasma = SHEET_MATERIAL_AMOUNT, /datum/material/diamond = SHEET_MATERIAL_AMOUNT, /datum/material/bluespace = SHEET_MATERIAL_AMOUNT) + volume = 300 + amount_per_transfer_from_this = 10 + possible_transfer_amounts = list(5,10,15,20,25,30,50,100,300) + // spillable = FALSE + reagent_flags = OPENCONTAINER | NO_REACT + fill_icon = 'maplestation_modules/story_content/deepred_warfare/icons/redfillings.dmi' + fill_icon_state = "redlightning" + fill_icon_thresholds = list(0, 1, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300) // For some reason the fill icon doesn't work properly. + w_class = WEIGHT_CLASS_BULKY + +/obj/item/reagent_containers/cup/beaker/redlightning/update_overlays() + . = ..() + . += emissive_appearance(icon, "redlightningemissive", src, alpha = src.alpha) + +/obj/item/reagent_containers/cup/beaker/redlightning/Initialize(mapload) + . = ..() + update_appearance() + +/obj/item/reagent_containers/cup/beaker/redlightning/filled + list_reagents = list(/datum/reagent/consumable/liquidelectricity/auric/redlightning = 300) + +/datum/reagent/aggregation_agent/advanced + name = "Advanced Aggregation Agent" + description = "An advanced agent used solely to produce stable miracle matter. Use with caution." + reagent_state = LIQUID + color = "#ff8400" + taste_description = "otherworldly space" + chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + +/datum/chemical_reaction/advanced_aggregation_creation + results = list(/datum/reagent/aggregation_agent/advanced = 1) + required_reagents = list(/datum/reagent/aggregation_agent = 1, /datum/reagent/consumable/liquidelectricity/auric/redlightning = 2) + reaction_tags = REACTION_TAG_UNIQUE | REACTION_TAG_EXPLOSIVE | REACTION_TAG_CHEMICAL + +/datum/chemical_reaction/true_miracle + required_reagents = list(/datum/reagent/aggregation_agent = 150, /datum/reagent/miracle = 1) + mob_react = FALSE + reaction_flags = REACTION_INSTANT + reaction_tags = REACTION_TAG_UNIQUE | REACTION_TAG_OTHER + +/datum/chemical_reaction/true_miracle/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) + var/location = get_turf(holder.my_atom) + for(var/i in 1 to created_volume) + new /obj/item/stack/sheet/mineral/miracle_matter(location) // Replace with miracle matter. diff --git a/maplestation_modules/story_content/deepred_warfare/code/redsetpieces.dm b/maplestation_modules/story_content/deepred_warfare/code/redsetpieces.dm new file mode 100644 index 000000000000..173e71c30cca --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/redsetpieces.dm @@ -0,0 +1,32 @@ +/obj/structure/redtech_indestructable + name = "redtech setpiece" + desc = "Report this to a coder." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/setpieces.dmi' + icon_state = "server_off" // Temp sprite KEKW. + density = TRUE + anchored = TRUE + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/structure/redtech_indestructable/server_off + name = "redtech server" + desc = "A large quantum computing server machine, which seems to be deactivated at the moment. Its construction looks nigh-indestructable." + icon_state = "server_off" + +/obj/structure/redtech_indestructable/server_off/alias + name = "crimson server" + +/obj/structure/redtech_indestructable/heatsink_off + name = "redtech heatsink" + desc = "Ab excessively large heatsink, which thankfully seems to be deactivated at the moment. Its construction looks nigh-indestructable." + icon_state = "heatsink_off" + +/obj/structure/redtech_indestructable/heatsink_off/alias + name = "crimson heatsink" + +/obj/structure/redtech_indestructable/inert_AV + name = "inert Anti Void" + desc = "Something has gone terribly wrong." + icon_state = "inert_AV" + +/obj/structure/redtech_indestructable/inert_AV/worse + icon_state = "inert_AV_worse" diff --git a/maplestation_modules/story_content/deepred_warfare/code/redturf.dm b/maplestation_modules/story_content/deepred_warfare/code/redturf.dm new file mode 100644 index 000000000000..2c0b0b63c375 --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/redturf.dm @@ -0,0 +1,57 @@ +/turf/open/indestructible/redtech + name = "redtech plating" + desc = "Fairly smooth, redtech floor plating. Its strange construction seems to be currently inactive." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/turfs.dmi' + icon_state = "offplating" + footstep = FOOTSTEP_PLATING + +/turf/open/indestructible/redtech/alias + name = "crimson plating" + desc = "Fairly smooth, metallic floor plating. Its strange construction seems to be currently inactive." + +/turf/open/indestructible/redtech/alt + icon_state = "offplating_alt" + +/turf/open/indestructible/redtech/alias/alt + icon_state = "offplating_alt" + +/turf/open/indestructible/redtech/altalt + icon_state = "offplating_altalt" + +/turf/open/indestructible/redtech/alias/altalt + icon_state = "offplating_altalt" + +/turf/open/indestructible/redtech/active + name = "active redtech plating" + desc = "Fairly smooth, redtech floor plating. Its strange construction seems to be currently active." + icon_state = "onplating" + +/turf/open/indestructible/redtech/active/alias + name = "active crimson plating" + desc = "Fairly smooth, metallic floor plating. Its strange construction seems to be currently active." + +/turf/open/indestructible/redtech/active/alt + icon_state = "onplating_alt" + +/turf/open/indestructible/redtech/active/alias/alt + icon_state = "onplating_alt" + +/turf/open/indestructible/redtech/active/altalt + icon_state = "onplating_altalt" + +/turf/open/indestructible/redtech/active/alias/altalt + icon_state = "onplating_altalt" + +/turf/closed/indestructible/redtech + name = "redtech hull" + desc = "A huge, impervious construction of redtech plating. It seems to be currently inactive." + icon = 'maplestation_modules/story_content/deepred_warfare/icons/offwalls.dmi' + icon_state = "redwalloff-0" + base_icon_state = "redwalloff" + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_WALLS + +/turf/closed/indestructible/redtech/alias + name = "crimson hull" + desc = "A huge, impervious construction of metallic plating. It seems to be currently inactive." diff --git a/maplestation_modules/story_content/deepred_warfare/code/ship_parallax.dm b/maplestation_modules/story_content/deepred_warfare/code/ship_parallax.dm new file mode 100644 index 000000000000..d7ac6303343a --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/ship_parallax.dm @@ -0,0 +1,72 @@ +GLOBAL_VAR_INIT(unbidden_distance, 0) // 0 = empty, 1 = very far, 2 = far, 3 = close +GLOBAL_VAR_INIT(unbidden_ringed, TRUE) // TRUE = ringed, FALSE = not ringed +GLOBAL_LIST_EMPTY(collective_unbiddens) // A list of all unbidden parallax layers. + +/atom/movable/screen/parallax_layer/planet/unbidden + icon = 'maplestation_modules/story_content/deepred_warfare/icons/unbidden_parallax.dmi' + icon_state = "unbidden_empty" + blend_mode = BLEND_OVERLAY + absolute = TRUE + speed = 5 + layer = 40 + + var/distance = 0 + var/ringed = TRUE + +/atom/movable/screen/parallax_layer/planet/unbidden/Initialize(mapload) + . = ..() + GLOB.collective_unbiddens += src + distance = GLOB.unbidden_distance + ringed = GLOB.unbidden_ringed + full_update() + +/atom/movable/screen/parallax_layer/planet/unbidden/Destroy() + GLOB.collective_unbiddens -= src + return ..() + +/atom/movable/screen/parallax_layer/planet/unbidden/proc/get_random_look() + return + +/atom/movable/screen/parallax_layer/planet/unbidden/proc/change_distance() + switch(GLOB.unbidden_distance) + if(0) + icon_state = "unbidden_empty" + distance = 0 + if(1) + if(ringed) + icon_state = "unbidden_very_far_ring" + else + icon_state = "unbidden_very_far" + distance = 1 + speed = 3 + if(2) + if(ringed) + icon_state = "unbidden_far_ring" + else + icon_state = "unbidden_far" + distance = 2 + speed = 4 + if(3) + if(ringed) + icon_state = "unbidden_ring" + else + icon_state = "unbidden" + distance = 3 + speed = 5 + +/atom/movable/screen/parallax_layer/planet/unbidden/proc/full_update() + if(GLOB.unbidden_ringed) + ringed = TRUE + else + ringed = FALSE + change_distance() + +/proc/update_unbidden(new_distance, new_ringed) + GLOB.unbidden_distance = new_distance + GLOB.unbidden_ringed = new_ringed + for(var/atom/movable/screen/parallax_layer/planet/unbidden/R as anything in GLOB.collective_unbiddens) + R.full_update() + +/atom/movable/screen/parallax_layer/planet/unbidden/on_z_change(mob/source) + . = ..() + full_update() diff --git a/maplestation_modules/story_content/deepred_warfare/code/shockattack.dm b/maplestation_modules/story_content/deepred_warfare/code/shockattack.dm new file mode 100644 index 000000000000..651e039cf315 --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/shockattack.dm @@ -0,0 +1,31 @@ +/datum/element/shockattack + element_flags = ELEMENT_BESPOKE + argument_hash_start_idx = 2 + // If the attack stuns on hit. + var/stun_on_hit + // How much damage the attack does. + var/shock_damage + +/datum/element/shockattack/Attach(datum/target, stun_on_hit = FALSE, shock_damage, thrown_effect = FALSE) + . = ..() + src.stun_on_hit = stun_on_hit + src.shock_damage = shock_damage + target.AddComponent(\ + /datum/component/on_hit_effect,\ + on_hit_callback = CALLBACK(src, PROC_REF(do_shocking)),\ + thrown_effect = thrown_effect,\ + ) + +/datum/element/shockattack/Detach(datum/target) + qdel(target.GetComponent(/datum/component/on_hit_effect)) + return ..() + +/datum/element/shockattack/proc/do_shocking(datum/element_owner, mob/living/owner, mob/living/target) + if(!istype(target)) + return + if(target.stat == DEAD) + return + if(stun_on_hit) + target.electrocute_act(shock_damage, owner, 1, SHOCK_NOGLOVES) + return + target.electrocute_act(shock_damage, owner, 1, SHOCK_NOSTUN | SHOCK_NOGLOVES) diff --git a/maplestation_modules/story_content/deepred_warfare/code/singulo_warhead.dm b/maplestation_modules/story_content/deepred_warfare/code/singulo_warhead.dm new file mode 100644 index 000000000000..25d20622c06a --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/code/singulo_warhead.dm @@ -0,0 +1,73 @@ +/obj/effect/singulo_warhead + name = "active bluespace singularity warhead" + desc = "An active bluespace singularity warhead. You probably should be running instead of looking at this." + icon = 'icons/effects/anomalies.dmi' + icon_state = "dimensional" + opacity = TRUE + anchored = TRUE + layer = ABOVE_ALL_MOB_LAYER + plane = ABOVE_GAME_PLANE + + light_range = 8 + light_power = 5 + light_on = TRUE + color = COLOR_RED + light_color = COLOR_RED + + COOLDOWN_DECLARE(detonate_cooldown) + var/detonate_delay = 1 SECONDS + + //Lifetime of the singularity, 4 seconds by default for untuned warheads. + var/lifetime = 8 SECONDS + + //Determines if the warhead should be in area denial mode. + var/tuned = FALSE + + //The cluster version is less destructive. + var/cluster = FALSE + + //Spooky. + var/atom/movable/warp_effect/warp + +/obj/effect/singulo_warhead/Initialize(mapload) + . = ..() + warp = new(src) + vis_contents += warp + if(tuned) + START_PROCESSING(SSobj, src) + addtimer(CALLBACK(src, PROC_REF(detonate)), lifetime) + +/obj/effect/singulo_warhead/Destroy() + vis_contents -= warp + QDEL_NULL(warp) + return ..() + +/obj/effect/singulo_warhead/proc/detonate() + if(!tuned) + if(cluster) + explosion(src, devastation_range = 4, heavy_impact_range = 8, light_impact_range = 12, flame_range = 12, flash_range = 15, ignorecap = TRUE, adminlog = FALSE) + else + explosion(src, devastation_range = 5, heavy_impact_range = 10, light_impact_range = 15, flame_range = 15, flash_range = 20, ignorecap = TRUE, adminlog = FALSE) + qdel(src) + +/obj/effect/singulo_warhead/process(seconds_per_tick) + if(!COOLDOWN_FINISHED(src, detonate_cooldown)) + return + COOLDOWN_START(src, detonate_cooldown, detonate_delay) + if(cluster) + explosion(src, light_impact_range = 3, flame_range = 10, flash_range = 12, ignorecap = TRUE, adminlog = FALSE) + else + explosion(src, light_impact_range = 4, flame_range = 12, flash_range = 16, ignorecap = TRUE, adminlog = FALSE) + +/obj/effect/singulo_warhead/tuned + tuned = TRUE + lifetime = 16 SECONDS + +/obj/effect/singulo_warhead/cluster + cluster = TRUE + lifetime = 6 SECONDS + +/obj/effect/singulo_warhead/tuned_cluster + tuned = TRUE + cluster = TRUE + lifetime = 12 SECONDS diff --git a/maplestation_modules/story_content/deepred_warfare/icons/arty_shell.dmi b/maplestation_modules/story_content/deepred_warfare/icons/arty_shell.dmi new file mode 100644 index 000000000000..7711110cfad2 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/arty_shell.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/cinematics.dmi b/maplestation_modules/story_content/deepred_warfare/icons/cinematics.dmi new file mode 100644 index 000000000000..c05c2794c7a6 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/cinematics.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/coilguns.dmi b/maplestation_modules/story_content/deepred_warfare/icons/coilguns.dmi new file mode 100644 index 000000000000..09792597a5e2 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/coilguns.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/curios.dmi b/maplestation_modules/story_content/deepred_warfare/icons/curios.dmi new file mode 100644 index 000000000000..fe549866086f Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/curios.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/dreadclothing.dmi b/maplestation_modules/story_content/deepred_warfare/icons/dreadclothing.dmi new file mode 100644 index 000000000000..5200be0e5453 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/dreadclothing.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/dreadclothingbig.dmi b/maplestation_modules/story_content/deepred_warfare/icons/dreadclothingbig.dmi new file mode 100644 index 000000000000..0c14215cba45 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/dreadclothingbig.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/dreaditems.dmi b/maplestation_modules/story_content/deepred_warfare/icons/dreaditems.dmi new file mode 100644 index 000000000000..12cc97e45456 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/dreaditems.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/dreadscan.dmi b/maplestation_modules/story_content/deepred_warfare/icons/dreadscan.dmi new file mode 100644 index 000000000000..661d02bddcbb Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/dreadscan.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/kajari.dmi b/maplestation_modules/story_content/deepred_warfare/icons/kajari.dmi new file mode 100644 index 000000000000..989d7edd7ef1 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/kajari.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/offwalls.dmi b/maplestation_modules/story_content/deepred_warfare/icons/offwalls.dmi new file mode 100644 index 000000000000..b7e5a7ad373e Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/offwalls.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi b/maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi new file mode 100644 index 000000000000..f6b5874acd4c Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/projectiles.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/redcanister.dmi b/maplestation_modules/story_content/deepred_warfare/icons/redcanister.dmi new file mode 100644 index 000000000000..1b1f2f65053d Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/redcanister.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/reddreadnought64.dmi b/maplestation_modules/story_content/deepred_warfare/icons/reddreadnought64.dmi new file mode 100644 index 000000000000..7d4bf8af57c2 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/reddreadnought64.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/reddrone.dmi b/maplestation_modules/story_content/deepred_warfare/icons/reddrone.dmi new file mode 100644 index 000000000000..6ef13cccd206 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/reddrone.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/redeffects.dmi b/maplestation_modules/story_content/deepred_warfare/icons/redeffects.dmi new file mode 100644 index 000000000000..7e6ca12412da Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/redeffects.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/redeffects64.dmi b/maplestation_modules/story_content/deepred_warfare/icons/redeffects64.dmi new file mode 100644 index 000000000000..fb40392ecf02 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/redeffects64.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/redfillings.dmi b/maplestation_modules/story_content/deepred_warfare/icons/redfillings.dmi new file mode 100644 index 000000000000..e799e98a5ea1 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/redfillings.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/redmaterials.dmi b/maplestation_modules/story_content/deepred_warfare/icons/redmaterials.dmi new file mode 100644 index 000000000000..17da28d00f5d Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/redmaterials.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/redparts.dmi b/maplestation_modules/story_content/deepred_warfare/icons/redparts.dmi new file mode 100644 index 000000000000..b0ff20aafb44 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/redparts.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/setpieces.dmi b/maplestation_modules/story_content/deepred_warfare/icons/setpieces.dmi new file mode 100644 index 000000000000..6d2c203ac253 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/setpieces.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/turfs.dmi b/maplestation_modules/story_content/deepred_warfare/icons/turfs.dmi new file mode 100644 index 000000000000..a9eefdc696a6 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/turfs.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/icons/unbidden_parallax.dmi b/maplestation_modules/story_content/deepred_warfare/icons/unbidden_parallax.dmi new file mode 100644 index 000000000000..f7ed3364dac0 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/icons/unbidden_parallax.dmi differ diff --git a/maplestation_modules/story_content/deepred_warfare/sound/attribution.txt b/maplestation_modules/story_content/deepred_warfare/sound/attribution.txt new file mode 100644 index 000000000000..f1b9806a6802 --- /dev/null +++ b/maplestation_modules/story_content/deepred_warfare/sound/attribution.txt @@ -0,0 +1,3 @@ +godslayer.ogg, warp.ogg, and supercharge.ogg are samples taken from Xtrullor's Supernova under CC BY 3.0. + +techblaster.ogg, techexplosion.ogg, techpowerup.ogg, beep.ogg, coilshoot.ogg, and coilbang.ogg are sound effects taken from the Terraria Calamity mod under CC BY-NC-SA 3.0. \ No newline at end of file diff --git a/maplestation_modules/story_content/deepred_warfare/sound/beep.ogg b/maplestation_modules/story_content/deepred_warfare/sound/beep.ogg new file mode 100644 index 000000000000..7a33b242da69 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/sound/beep.ogg differ diff --git a/maplestation_modules/story_content/deepred_warfare/sound/coilbang.ogg b/maplestation_modules/story_content/deepred_warfare/sound/coilbang.ogg new file mode 100644 index 000000000000..90c2d697c1e4 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/sound/coilbang.ogg differ diff --git a/maplestation_modules/story_content/deepred_warfare/sound/coilshoot.ogg b/maplestation_modules/story_content/deepred_warfare/sound/coilshoot.ogg new file mode 100644 index 000000000000..ecafe40711c6 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/sound/coilshoot.ogg differ diff --git a/maplestation_modules/story_content/deepred_warfare/sound/godslayer.ogg b/maplestation_modules/story_content/deepred_warfare/sound/godslayer.ogg new file mode 100644 index 000000000000..b103f154ce37 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/sound/godslayer.ogg differ diff --git a/maplestation_modules/story_content/deepred_warfare/sound/supercharge.ogg b/maplestation_modules/story_content/deepred_warfare/sound/supercharge.ogg new file mode 100644 index 000000000000..56a971129400 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/sound/supercharge.ogg differ diff --git a/maplestation_modules/story_content/deepred_warfare/sound/techblaster.ogg b/maplestation_modules/story_content/deepred_warfare/sound/techblaster.ogg new file mode 100644 index 000000000000..18d01a8b41a5 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/sound/techblaster.ogg differ diff --git a/maplestation_modules/story_content/deepred_warfare/sound/techexplosion.ogg b/maplestation_modules/story_content/deepred_warfare/sound/techexplosion.ogg new file mode 100644 index 000000000000..86c6d0034a51 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/sound/techexplosion.ogg differ diff --git a/maplestation_modules/story_content/deepred_warfare/sound/techpowerup.ogg b/maplestation_modules/story_content/deepred_warfare/sound/techpowerup.ogg new file mode 100644 index 000000000000..da1cf4968882 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/sound/techpowerup.ogg differ diff --git a/maplestation_modules/story_content/deepred_warfare/sound/warp.ogg b/maplestation_modules/story_content/deepred_warfare/sound/warp.ogg new file mode 100644 index 000000000000..7c0eb3ef2f64 Binary files /dev/null and b/maplestation_modules/story_content/deepred_warfare/sound/warp.ogg differ diff --git a/tgui/packages/tgui/interfaces/WarfareEvent.jsx b/tgui/packages/tgui/interfaces/WarfareEvent.jsx new file mode 100644 index 000000000000..470741c7e48f --- /dev/null +++ b/tgui/packages/tgui/interfaces/WarfareEvent.jsx @@ -0,0 +1,122 @@ +import { useBackend } from '../backend'; +import { Button, Dropdown, Flex, Section, Stack } from '../components'; +import { Window } from '../layouts'; + +export const WarfareEvent = (props, context) => { + const { act, data } = useBackend(context); + const { selectedNames } = data; + + const shellList = [ + '160mm Rocket Assisted AP', + '160mm HE', + '160mm Flak', + '160mm Cluster AP', + '460mm Rocket Assisted AP', + '460mm Cluster HE', + '460mm Cluster Flak', + 'WMD KAJARI', + ]; + + return ( + + +
+ + + act('fireShells')} + textAlign="center" + > + {'Fire Volley'} + + + + + act('changeDirection', { direction: value }) + } + /> + + +
+ + +
+ + {selectedNames.map((currentShell) => ( + + + + {currentShell} + + +
+
+ +
+ + {shellList.map((currentShell) => ( + + + + {currentShell} + + +
+
+
+
+
+
+
+ ); +};