#define METEOR_TEMPERATURE /var/meteor_wave_delay = 300 //Default wait between waves in tenths of seconds /var/meteors_in_wave = 10 //Default absolute size /var/meteor_wave_active = 0 /var/max_meteor_size = 0 //One for small waves, two for big waves, three for massive waves, four for boss waves /var/chosen_dir = 1 //Call above constants to change /proc/meteor_wave(var/number = meteors_in_wave, var/max_size = 0, var/list/types = null, var/offset_origin = 0, var/offset_dest = 0) if(!ticker || meteor_wave_active) return meteor_wave_active = 1 meteor_wave_delay = (rand(30, 45)) * 10 //Between 30 and 45 seconds, engineers need time to shuffle in relative safety chosen_dir = pick(cardinal) //Pick a direction max_meteor_size = max_size //Generate a name for our wave var/greek_alphabet = list("Alpha", "Beta", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", \ "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega") var/wave_final_name = "[number > 25 ? "Major":"Minor"] Meteor [pick("Wave", "Cluster", "Group")] [pick(greek_alphabet)]-[rand(1, 999)]" output_information(meteor_wave_delay, chosen_dir, max_size, number, wave_final_name) spawn(meteor_wave_delay) for(var/i = 0 to number) sleep(rand(1, 3)) //0.1 to 0.3 seconds between meteors var/meteor_type = null if(types != null) meteor_type = pick(types) spawn_meteor(chosen_dir, meteor_type, offset_origin, offset_dest) sleep(50) //Five seconds for the chat to scroll meteor_wave_active = 0 return chosen_dir //A bunch of information to be used by the bhangmeter (doubles as a meteor monitoring computer), and sent to the admins otherwise /proc/output_information(var/meteor_delay, var/wave_dir, var/meteor_size, var/wave_size, var/wave_name) var/meteor_l_size = "unknown" switch(meteor_size) if(1) meteor_l_size = "small" if(2) meteor_l_size = "medium" if(3) meteor_l_size = "large" if(4) meteor_l_size = "apocalyptic" else meteor_l_size = "unknown" var/wave_l_dir = "north" switch(wave_dir) if(1) wave_l_dir = "north" if(2) wave_l_dir = "south" if(4) wave_l_dir = "east" if(8) wave_l_dir = "west" message_admins("[wave_name], containing [wave_size] objects up to [meteor_l_size] size and incoming from the [wave_l_dir], will strike in [meteor_delay/10] seconds.") //Send to all Bhangmeters for(var/obj/machinery/computer/bhangmeter/bhangmeter in doppler_arrays) if(bhangmeter && !bhangmeter.stat) bhangmeter.say("Detected: [wave_name], containing [wave_size] objects up to [meteor_l_size] size and incoming from the [wave_l_dir], will strike in [meteor_delay/10] seconds.") /proc/spawn_meteor(var/chosen_dir, var/meteorpath = null, var/offset_origin = 0, var/offset_dest = 0) var/startx var/starty var/endx var/endy var/turf/pickedstart var/turf/pickedgoal var/max_i = 5 //Try only five times maximum do switch(chosen_dir) if(1) //North, along the y = max edge starty = world.maxy - (TRANSITIONEDGE + 2) startx = rand((TRANSITIONEDGE + 2 + offset_origin), world.maxx - (TRANSITIONEDGE + 2 + offset_origin)) endy = TRANSITIONEDGE endx = rand(TRANSITIONEDGE + offset_dest, world.maxx - TRANSITIONEDGE - offset_dest) if(2) //South, along the y = 0 edge starty = (TRANSITIONEDGE + 2) startx = rand((TRANSITIONEDGE + 2 + offset_origin), world.maxx - (TRANSITIONEDGE + 2 + offset_origin)) endy = world.maxy - (TRANSITIONEDGE + 2) endx = rand(TRANSITIONEDGE + offset_dest, world.maxx - TRANSITIONEDGE - offset_dest) if(4) //East, along the x = max edge starty = rand((TRANSITIONEDGE + 2 + offset_origin), world.maxy - (TRANSITIONEDGE + 2 + offset_origin)) startx = world.maxx - (TRANSITIONEDGE + 2) endy = rand(TRANSITIONEDGE + offset_dest, world.maxy - TRANSITIONEDGE - offset_dest) endx = (TRANSITIONEDGE + 2) if(8) //West, along the x = 0 edge starty = rand((TRANSITIONEDGE + 2 + offset_origin), world.maxy - (TRANSITIONEDGE + 2 + offset_origin)) startx = (TRANSITIONEDGE + 2) endy = rand(TRANSITIONEDGE + offset_dest, world.maxy - TRANSITIONEDGE - offset_dest) endx = world.maxx - (TRANSITIONEDGE + 2) pickedstart = locate(startx, starty, 1) pickedgoal = locate(endx, endy, 1) max_i-- if(max_i <= 0) return while(!istype(pickedstart, /turf/space)) if(meteorpath) return new meteorpath(pickedstart, pickedgoal) else var/list/possible_meteors = list() if(!max_meteor_size || max_meteor_size >= 1) //Small waves possible_meteors[/obj/item/projectile/meteor/small] = 80 possible_meteors[/obj/item/projectile/meteor/small/flash] = 8 if(!max_meteor_size || max_meteor_size >= 2) //Medium waves possible_meteors[/obj/item/projectile/meteor] = 100 possible_meteors[/obj/item/projectile/meteor/radioactive] = 10 if(!max_meteor_size || max_meteor_size >= 3) //Big waves possible_meteors[/obj/item/projectile/meteor/big] = 10 possible_meteors[/obj/item/projectile/meteor/big/cluster] = 1 var/chosen = pick(possible_meteors) return new chosen(pickedstart, pickedgoal) /* * Below are all meteor types */ /obj/item/projectile/meteor name = "meteor" icon = 'icons/obj/meteor.dmi' icon_state = "medium" density = 1 anchored = 1 //You can't push or pull it to prevent exploiting grillepasschance = 0 mouse_opacity = 1 /obj/item/projectile/meteor/New(atom/start, atom/end) ..() if(end) throw_at(end) /obj/item/projectile/meteor/throw_at(atom/end) original = end starting = loc current = loc OnFired() yo = target.y - y xo = target.x - x process() //Since meteors explode on impact, we won't allow chain reactions like this //Maybe one day I wil code explosive recoil, but in the meantime who bombs meteor waves anyways ? /obj/item/projectile/meteor/ex_act() return //We don't want meteors to bump into eachother and explode, so they pass through eachother //Reflection on bumping would be better, but I would reckon I'm not sure on how to achieve it /obj/item/projectile/meteor/Cross(atom/movable/mover, turf/target, height = 1.5, air_group = 0) if(istype(mover, /obj/item/projectile/meteor)) return 1 //Just move through it, no questions asked if(isliving(mover)) return 0 //Collision else return ..() //Refer to atom/proc/Cross /obj/item/projectile/meteor/to_bump(atom/A) if(loc == null) return explosion(get_turf(src), 2, 4, 6, 8, 0, 1, 0) //Medium meteor, medium boom qdel(src) /obj/item/projectile/meteor/process_step() if(z != starting.z) qdel(src) return ..() /obj/item/projectile/meteor/radioactive name = "radioactive meteor" desc = "The Engineer's bane" icon_state = "medium_radioactive" /obj/item/projectile/meteor/radioactive/to_bump(atom/a) if(loc == null) return for(var/mob/living/M in viewers(src, null)) M.apply_radiation(rand(5, 10), RAD_EXTERNAL) ..() /obj/item/projectile/meteor/small name = "small meteor" desc = "The mineral version of armed C4, coming right for your walls." icon_state = "small" pass_flags = PASSTABLE /obj/item/projectile/meteor/small/to_bump(atom/A) if(loc == null) return explosion(get_turf(src), -1, 1, 3, 4, 0, 1, 0) //Tiny meteor doesn't cause too much damage qdel(src) /obj/item/projectile/meteor/small/flash name = "flash meteor" desc = "A absolutely stunning rock specimen of blinding beauty." icon_state = "small_flash" /obj/item/projectile/meteor/small/flash/to_bump(atom/A) if(loc == null) return //Adjusted from flashbangs, should be its own global proc visible_message("BANG") playsound(src, 'sound/effects/bang.ogg', 25, 1) for(var/mob/living/M in viewers(src, null)) //Checking for protections var/eye_safety = 0 var/ear_safety = 0 if(iscarbon(M)) var/mob/living/carbon/C = M eye_safety = C.eyecheck() if(ishuman(C)) var/mob/living/carbon/human/H = C if(H.earprot()) ear_safety += 2 if(M_HULK in H.mutations) ear_safety += 1 if(istype(H.head, /obj/item/clothing/head/helmet)) ear_safety += 1 //Flashing everyone if(eye_safety < 2) M.flash_eyes(visual = 1) switch(eye_safety) if(1) M.Stun(2) if(0) M.Stun(4) M.Knockdown(10) if(-1) M.Stun(7) M.Knockdown(15) if(ear_safety < 2) switch(ear_safety) if(1) M.ear_damage += rand(0, 3) if(0) M.ear_damage += rand(5, 15) M.ear_deaf = max(M.ear_deaf, 10) //Shouldn't have to do this here, this is what life.dm and organ checks are for //Not even going to bother with eye damage if(prob(M.ear_damage - 10 + 5)) to_chat(M, "You can't hear anything!") M.sdisabilities |= DEAF explosion(get_turf(src), -1, 1, 3, 4, 0, 1, 0) //Tiny meteor doesn't cause too much damage qdel(src) /obj/item/projectile/meteor/piercing name = "piercing meteor" desc = "Takes a page out of armor-piercing rounds, blowing its way through cover once, and then blowing up normally." icon_state = "medium_piercing" var/pierce_health = 1 //When 0, piercing meteor explodes like normal /obj/item/projectile/meteor/piercing/to_bump(atom/A) if(loc == null) return if(pierce_health) explosion(get_turf(A), 1, 0, 0, 0, 0, 1, 0) //Blow up the resisting object pierce_health-- else explosion(get_turf(src), 2, 4, 6, 8, 0, 1, 0) //Blow ourselves up, in glory qdel(src) /obj/item/projectile/meteor/big name = "large meteor" desc = "It might look large, but it is only a small splinter of a much bigger thing." icon_state = "big" /obj/item/projectile/meteor/big/to_bump(atom/A) if(loc == null) return explosion(get_turf(src), 4, 6, 8, 8, 0, 1, 0) //You have been visited by the nuclear meteor qdel(src) /obj/item/projectile/meteor/big/cluster name = "cluster meteor" desc = "Makes up for its lack of explosiveness by splitting into multiple, fairly explosive meteors." icon_state = "big_cluster" /obj/item/projectile/meteor/big/cluster/to_bump(atom/A) if(loc == null) return explosion(get_turf(A), 1, 0, 0, 0, 0, 1, 0) //Enough to destroy whatever was in the way for(var/i = 0, i < 3, i++) var/c_endx = rand(TRANSITIONEDGE, world.maxx - TRANSITIONEDGE) var/c_endy = rand(TRANSITIONEDGE, world.maxy - TRANSITIONEDGE) var/c_pickedgoal = locate(c_endx, c_endy, 1) if(c_pickedgoal) new /obj/item/projectile/meteor(get_turf(src), c_pickedgoal) qdel(src) //Placeholder for actual meteors of this kind, will be included SOON /obj/item/projectile/meteor/boss name = "apocalytic meteor" desc = "And behold, a white meteor. And on that meteor..." /obj/item/projectile/meteor/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/weapon/pickaxe)) //Yeah, you can totally do that qdel(src) return ..() /obj/item/projectile/meteor/Destroy() ..() /obj/item/projectile/meteor/gib //non explosive meteor, appears to be a corpse spinning in space before impacting something and spraying gibs everywhere name = "human corpse" icon_state = "human" /obj/item/projectile/meteor/gib/to_bump(atom/A) if(loc == null) return new /obj/effect/gibspawner/human(src.loc) qdel(src) /obj/item/projectile/meteor/blob name = "Blob" icon = 'icons/obj/meteor_64x64.dmi' icon_state = "meteorblob" pixel_x = -16 * PIXEL_MULTIPLIER pixel_y = -16 * PIXEL_MULTIPLIER /obj/item/projectile/meteor/blob/to_bump(atom/A) if(!loc) return if(ismob(A)) src.forceMove(A.loc) A.blob_act() return playsound(loc, get_sfx("explosion"), 50) for (var/mob/M in player_list) if(M && M.client) var/turf/M_turf = get_turf(M) if(M_turf && (M_turf.z == loc.z)) var/dist = get_dist(M_turf, loc) if(dist <= round(world.view + 10, 1)) shake_camera(M, 3, 2) M.playsound_local(loc, 'sound/effects/explosionfar.ogg') var/turf/T = get_turf(A) for(var/atom/AT in T) AT.blob_act(1) T.blob_act(1) var/obj/effect/blob/is_there_a_blob = (locate(/obj/effect/blob) in T) if(is_there_a_blob) do_blob_stuff(loc) else do_blob_stuff(T) qdel(src) /obj/item/projectile/meteor/blob/proc/do_blob_stuff(var/turf/T) new/obj/effect/blob/normal(T, no_morph = 1) /obj/item/projectile/meteor/blob/node name = "Blob Node" icon = 'icons/obj/meteor_64x64.dmi' icon_state = "meteornode" /obj/item/projectile/meteor/blob/node/do_blob_stuff(var/turf/T) new/obj/effect/blob/node(T, no_morph = 1) var/list/blob_candidates = list() /obj/item/projectile/meteor/blob/core name = "Blob Core" icon = 'icons/obj/meteor_64x64.dmi' icon_state = "meteorcore" var/client/blob_candidate = null /obj/item/projectile/meteor/blob/core/proc/AssignMob(var/mob/M) blob_candidate = M.client if(blob_candidate) blob_candidate.perspective = EYE_PERSPECTIVE blob_candidate.eye = src blob_candidate.mob.see_invisible = SEE_INVISIBLE_MINIMUM /obj/item/projectile/meteor/blob/core/Destroy() if(blob_candidate) blob_candidate.perspective = MOB_PERSPECTIVE blob_candidate.eye = blob_candidate.mob blob_candidates -= blob_candidate blob_candidate = null ..() /obj/item/projectile/meteor/blob/core/do_blob_stuff(var/turf/T) if(blob_candidate && istype(blob_candidate.mob, /mob/dead/observer)) new/obj/effect/blob/core(T, new_overmind = blob_candidate, no_morph = 1) else new/obj/effect/blob/core(T, no_morph = 1) log_admin("Blob core meteor impacted at [formatJumpTo(loc)] controlled by [key_name(blob_candidate)].") message_admins("Blob core meteor impacted at [formatJumpTo(loc)] controlled by [key_name(blob_candidate)].") //It's a tool to debug and test stuff, ok? Pls don't hand them out to players unless you just want to set the world on fire. /obj/item/weapon/meteor_gun name = "Meteor Gun" desc = "Jesus fucking christ." icon = 'icons/obj/gun.dmi' icon_state = "meteorgun" item_state = "gun" flags = FPRINT siemens_coefficient = 1 slot_flags = SLOT_BELT w_class = W_CLASS_MEDIUM throwforce = 5 throw_speed = 4 throw_range = 5 force = 5.0 var/projectile_type = /obj/item/projectile/meteor /obj/item/weapon/meteor_gun/afterattack(atom/A, mob/living/user, flag, params, struggle = 0) if(flag) return //we're placing gun on a table or in backpack user.visible_message( "[user] fires the [src]!", "You fire the [src]!") playsound(user, 'sound/weapons/rocket.ogg', 100) var/obj/item/projectile/meteor/in_chamber = new projectile_type(get_turf(src), get_turf(A)) add_logs(user,A,"fired \the [src] (proj:[in_chamber.name]) at ",addition="([A.x],[A.y],[A.z])") /obj/item/weapon/meteor_gun/attack_self(mob/user as mob) projectile_type = input(user, "Pick a meteor type.", "Projectile Choice") in typesof(/obj/item/projectile/meteor) /obj/item/projectile/meteor/firework name = "firework" desc = "Oooh! Aaaaah" icon_state = "firework" /obj/item/projectile/meteor/firework/New() ..() var/list/colors = list( list(1, 0, 0, 1), list(0, 1, 0, 1), list(0, 0, 1, 1), list(1, 1, 0, 1), list(0, 1, 1, 1), list(1, 0, 1, 1) ) var/list/cl = list(0,0,0,0) for(var/x = 1 to 4) cl = pick(colors) + cl color = cl /obj/item/projectile/meteor/firework/to_bump(atom/A) if(!loc) return explosion(get_turf(src),0,0,0) new /obj/effect/overlay/firework_sparkle(get_turf(src)) qdel(src) /obj/effect/overlay/firework_sparkle name = "pretty lights" desc = "Shiny." icon = 'icons/obj/meteor_64x64.dmi' icon_state = "firework_sparkle" pixel_x = -16 * PIXEL_MULTIPLIER pixel_y = -16 * PIXEL_MULTIPLIER plane = LIGHTING_PLANE layer = ABOVE_LIGHTING_LAYER /obj/effect/overlay/firework_sparkle/New() ..() var/list/colors = list( list(1, 0, 0, 1), list(0, 1, 0, 1), list(0, 0, 1, 1), list(1, 1, 0, 1), list(0, 1, 1, 1), list(1, 0, 1, 1) ) var/list/cl = list(0,0,0,0) for(var/x = 1 to 4) cl = pick(colors) + cl color = cl set_light(12,12) var/matrix/M = src.transform if(!istype(M)) M = matrix() var/scaleby = rand(1,2) M.Scale(scaleby, scaleby) animate(src, transform = M, alpha = 0, time = 12) spawn(12) qdel(src)