From 2ffdf3716696bd041f022e0a8a2ec30271af69e0 Mon Sep 17 00:00:00 2001 From: ZomgPonies Date: Wed, 25 Sep 2013 23:22:32 -0400 Subject: [PATCH] Overhaul of random events --- baystation12.dme | 24 +- code/game/objects/effects/anomalies.dm | 183 ++ code/game/objects/effects/spiders.dm | 6 +- code/modules/admin/admin.dm | 6 +- code/modules/admin/topic.dm | 28 +- code/modules/admin/verbs/onlyone.dm | 3 +- code/modules/assembly/signaler.dm | 16 + code/modules/events/bluespaceanomaly.dm | 88 - code/modules/events/event_dynamic.dm | 15 +- code/modules/events/event_manager.dm | 27 + .../events/tgevents/alien_infestation.dm | 39 + code/modules/events/tgevents/anomaly.dm | 25 + .../events/tgevents/anomaly_bluespace.dm | 71 + code/modules/events/tgevents/anomaly_flux.dm | 22 + code/modules/events/tgevents/anomaly_grav.dm | 15 + code/modules/events/tgevents/anomaly_pyro.dm | 22 + .../modules/events/tgevents/anomaly_vortex.dm | 15 + code/modules/events/tgevents/blob.dm | 35 + .../events/tgevents/brand_intelligence.dm | 67 + .../modules/events/tgevents/carp_migration.dm | 21 + .../tgevents/communications_blackout.dm | 25 + .../events/tgevents/disease_outbreak.dm | 52 + code/modules/events/tgevents/dust.dm | 104 + .../events/tgevents/electrical_storm.dm | 34 + code/modules/events/tgevents/event.dm | 115 + code/modules/events/tgevents/event_manager.dm | 246 ++ code/modules/events/tgevents/false_alarm.dm | 12 + code/modules/events/tgevents/holiday/xmas.dm | 69 + code/modules/events/tgevents/immovable_rod.dm | 93 + code/modules/events/tgevents/ion_storm.dm | 525 ++++ .../events/tgevents/mass_hallucination.dm | 3 + code/modules/events/tgevents/meteor_wave.dm | 18 + code/modules/events/tgevents/ninja.dm | 2800 +++++++++++++++++ code/modules/events/tgevents/prison_break.dm | 52 + .../events/tgevents/radiation_storm.dm | 55 + code/modules/events/tgevents/spacevine.dm | 268 ++ .../events/tgevents/spider_infestation.dm | 32 + .../tgevents/spontaneous_appendicitis.dm | 20 + code/modules/events/tgevents/vent_clog.dm | 34 + code/modules/events/tgevents/weightless.dm | 31 + code/modules/events/tgevents/wormholes.dm | 75 + code/modules/virus2/effect.dm | 4 +- icons/effects/effects.dmi | Bin 199964 -> 264180 bytes icons/obj/assemblies/new_assemblies.dmi | Bin 3346 -> 10835 bytes sound/music/highlander.ogg | Bin 0 -> 2491104 bytes 45 files changed, 5290 insertions(+), 105 deletions(-) create mode 100644 code/game/objects/effects/anomalies.dm delete mode 100644 code/modules/events/bluespaceanomaly.dm create mode 100644 code/modules/events/tgevents/alien_infestation.dm create mode 100644 code/modules/events/tgevents/anomaly.dm create mode 100644 code/modules/events/tgevents/anomaly_bluespace.dm create mode 100644 code/modules/events/tgevents/anomaly_flux.dm create mode 100644 code/modules/events/tgevents/anomaly_grav.dm create mode 100644 code/modules/events/tgevents/anomaly_pyro.dm create mode 100644 code/modules/events/tgevents/anomaly_vortex.dm create mode 100644 code/modules/events/tgevents/blob.dm create mode 100644 code/modules/events/tgevents/brand_intelligence.dm create mode 100644 code/modules/events/tgevents/carp_migration.dm create mode 100644 code/modules/events/tgevents/communications_blackout.dm create mode 100644 code/modules/events/tgevents/disease_outbreak.dm create mode 100644 code/modules/events/tgevents/dust.dm create mode 100644 code/modules/events/tgevents/electrical_storm.dm create mode 100644 code/modules/events/tgevents/event.dm create mode 100644 code/modules/events/tgevents/event_manager.dm create mode 100644 code/modules/events/tgevents/false_alarm.dm create mode 100644 code/modules/events/tgevents/holiday/xmas.dm create mode 100644 code/modules/events/tgevents/immovable_rod.dm create mode 100644 code/modules/events/tgevents/ion_storm.dm create mode 100644 code/modules/events/tgevents/mass_hallucination.dm create mode 100644 code/modules/events/tgevents/meteor_wave.dm create mode 100644 code/modules/events/tgevents/ninja.dm create mode 100644 code/modules/events/tgevents/prison_break.dm create mode 100644 code/modules/events/tgevents/radiation_storm.dm create mode 100644 code/modules/events/tgevents/spacevine.dm create mode 100644 code/modules/events/tgevents/spider_infestation.dm create mode 100644 code/modules/events/tgevents/spontaneous_appendicitis.dm create mode 100644 code/modules/events/tgevents/vent_clog.dm create mode 100644 code/modules/events/tgevents/weightless.dm create mode 100644 code/modules/events/tgevents/wormholes.dm create mode 100644 sound/music/highlander.ogg diff --git a/baystation12.dme b/baystation12.dme index 3222a01483c..081439ffe82 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -259,12 +259,10 @@ #include "code\game\gamemodes\events\biomass.dm" #include "code\game\gamemodes\events\black_hole.dm" #include "code\game\gamemodes\events\clang.dm" -#include "code\game\gamemodes\events\dust.dm" #include "code\game\gamemodes\events\ninja_abilities.dm" #include "code\game\gamemodes\events\ninja_equipment.dm" #include "code\game\gamemodes\events\power_failure.dm" #include "code\game\gamemodes\events\space_ninja.dm" -#include "code\game\gamemodes\events\spacevines.dm" #include "code\game\gamemodes\events\wormholes.dm" #include "code\game\gamemodes\events\holidays\Christmas.dm" #include "code\game\gamemodes\events\holidays\Holidays.dm" @@ -474,6 +472,7 @@ #include "code\game\objects\weapons.dm" #include "code\game\objects\closets\walllocker.dm" #include "code\game\objects\effects\aliens.dm" +#include "code\game\objects\effects\anomalies.dm" #include "code\game\objects\effects\bump_teleporter.dm" #include "code\game\objects\effects\effect_system.dm" #include "code\game\objects\effects\gibs.dm" @@ -828,10 +827,7 @@ #include "code\modules\DetectiveWork\evidence.dm" #include "code\modules\DetectiveWork\footprints_and_rag.dm" #include "code\modules\DetectiveWork\scanner.dm" -#include "code\modules\events\alien_infestation.dm" #include "code\modules\events\blob.dm" -#include "code\modules\events\bluespaceanomaly.dm" -#include "code\modules\events\brand_intelligence.dm" #include "code\modules\events\carp_migration.dm" #include "code\modules\events\comms_blackout.dm" #include "code\modules\events\communications_blackout.dm" @@ -843,7 +839,6 @@ #include "code\modules\events\grid_check.dm" #include "code\modules\events\infestation.dm" #include "code\modules\events\ion_storm.dm" -#include "code\modules\events\mass_hallucination.dm" #include "code\modules\events\meteors.dm" #include "code\modules\events\money_hacker.dm" #include "code\modules\events\money_lotto.dm" @@ -853,12 +848,25 @@ #include "code\modules\events\radiation_storm.dm" #include "code\modules\events\rogue_drones.dm" #include "code\modules\events\space_ninja.dm" -#include "code\modules\events\spacevine.dm" -#include "code\modules\events\spider_infestation.dm" #include "code\modules\events\spontaneous_appendicitis.dm" #include "code\modules\events\viral_infection.dm" #include "code\modules\events\viral_outbreak.dm" #include "code\modules\events\wallrot.dm" +#include "code\modules\events\tgevents\alien_infestation.dm" +#include "code\modules\events\tgevents\anomaly.dm" +#include "code\modules\events\tgevents\anomaly_bluespace.dm" +#include "code\modules\events\tgevents\anomaly_flux.dm" +#include "code\modules\events\tgevents\anomaly_grav.dm" +#include "code\modules\events\tgevents\anomaly_pyro.dm" +#include "code\modules\events\tgevents\anomaly_vortex.dm" +#include "code\modules\events\tgevents\brand_intelligence.dm" +#include "code\modules\events\tgevents\dust.dm" +#include "code\modules\events\tgevents\false_alarm.dm" +#include "code\modules\events\tgevents\immovable_rod.dm" +#include "code\modules\events\tgevents\mass_hallucination.dm" +#include "code\modules\events\tgevents\spacevine.dm" +#include "code\modules\events\tgevents\spider_infestation.dm" +#include "code\modules\events\tgevents\vent_clog.dm" #include "code\modules\ext_scripts\irc.dm" #include "code\modules\ext_scripts\python.dm" #include "code\modules\flufftext\Dreaming.dm" diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm new file mode 100644 index 00000000000..4c2d0ae8fe0 --- /dev/null +++ b/code/game/objects/effects/anomalies.dm @@ -0,0 +1,183 @@ +//Anomalies, used for events. Note that these DO NOT work by themselves; their procs are called by the event datum. + +/obj/effect/anomaly + name = "anomaly" + icon = 'icons/effects/effects.dmi' + desc = "A mysterious anomaly, seen commonly only in the region of space that the station orbits..." + icon_state = "bhole3" + unacidable = 1 + density = 0 + anchored = 1 + luminosity = 3 + var/obj/item/device/assembly/signaler/anomaly/aSignal = null + +/obj/effect/anomaly/New() + SetLuminosity(initial(luminosity)) + aSignal = new(src) + aSignal.code = rand(1,100) + + aSignal.frequency = rand(1200, 1599) + if(IsMultiple(aSignal.frequency, 2))//signaller frequencies are always uneven! + aSignal.frequency++ + + +/obj/effect/anomaly/proc/anomalyEffect() + if(prob(50)) + step(src,pick(alldirs)) + + +/obj/effect/anomaly/proc/anomalyNeutralize() + new /obj/effect/effect/bad_smoke(loc) + + for(var/atom/movable/O in src) + O.loc = src.loc + + del(src) + + +/obj/effect/anomaly/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/device/analyzer)) + user << "Analyzing... [src]'s unstable field is fluctuating along frequency [aSignal.code]:[format_frequency(aSignal.frequency)]." + +/////////////////////// + +/obj/effect/anomaly/grav + name = "gravitational anomaly" + icon_state = "shield2" + density = 1 + var/boing = 0 + +/obj/effect/anomaly/grav/New() + ..() + aSignal.origin_tech = "magnets=5;powerstorage=4" + +/obj/effect/anomaly/grav/anomalyEffect() + ..() + + boing = 1 + for(var/obj/O in orange(4, src)) + if(!O.anchored) + step_towards(O,src) + for(var/mob/living/M in orange(4, src)) + step_towards(M,src) + +/obj/effect/anomaly/grav/Bump(mob/A) + gravShock(A) + return + +/obj/effect/anomaly/grav/Bumped(mob/A) + gravShock(A) + return + +/obj/effect/anomaly/grav/proc/gravShock(var/mob/A) + if(boing && isliving(A) && !A.stat) + A.Weaken(2) + var/atom/target = get_edge_target_turf(A, get_dir(src, get_step_away(A, src))) + A.throw_at(target, 5, 1) + boing = 0 + return + +///////////////////// + +/obj/effect/anomaly/flux + name = "flux wave anomaly" + icon_state = "electricity2" + +/obj/effect/anomaly/flux/New() + ..() + aSignal.origin_tech = "powerstorage=5;programming=3;plasmatech=2" + +///////////////////// + +/obj/effect/anomaly/bluespace + name = "bluespace anomaly" + icon = 'icons/obj/projectiles.dmi' + icon_state = "bluespace" + density = 1 + +/obj/effect/anomaly/bluespace/New() + ..() + aSignal.origin_tech = "bluespace=5;magnets=3;powerstorage=2" + +/obj/effect/anomaly/bluespace/Bumped(atom/A) + if(isliving(A)) + do_teleport(A, locate(A.x, A.y, A.z), 10) + return + +///////////////////// + +/obj/effect/anomaly/pyro + name = "pyroclastic anomaly" + icon_state = "mustard" + +/obj/effect/anomaly/pyro/New() + ..() + aSignal.origin_tech = "plasmatech=5;powerstorage=3;biotech=3" + +/obj/effect/anomaly/pyro/anomalyEffect() + ..() + var/turf/simulated/T = get_turf(src) + if(istype(T)) + var/datum/gas_mixture/payload = new + payload.toxins = 30 + T.zone.air.merge(payload) + T.hotspot_expose(1000, CELL_VOLUME) +// T.atmos_spawn_air("fire", 3) + +///////////////////// + +/obj/effect/anomaly/bhole + name = "vortex anomaly" + icon_state = "bhole3" + desc = "That's a nice station you have there. It'd be a shame if something happened to it." + +/obj/effect/anomaly/bhole/New() + ..() + aSignal.origin_tech = "materials=5;combat=4;engineering=3" + +/obj/effect/anomaly/bhole/anomalyEffect() + ..() + if(!isturf(loc)) //blackhole cannot be contained inside anything. Weird stuff might happen + del(src) + return + + grav(rand(0,3), rand(2,3), 50, 25) + + //Throwing stuff around! + for(var/obj/O in orange(1,src)) + if(!O.anchored) + var/mob/living/target = locate() in view(5,src) + if(!target) + return + O.throw_at(target, 5, 10) + return + else + O.ex_act(2) + +/obj/effect/anomaly/bhole/proc/grav(var/r, var/ex_act_force, var/pull_chance, var/turf_removal_chance) + for(var/t = -r, t < r, t++) + affect_coord(x+t, y-r, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x-t, y+r, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x+r, y+t, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x-r, y-t, ex_act_force, pull_chance, turf_removal_chance) + return + +/obj/effect/anomaly/bhole/proc/affect_coord(var/x, var/y, var/ex_act_force, var/pull_chance, var/turf_removal_chance) + //Get turf at coordinate + var/turf/T = locate(x, y, z) + if(isnull(T)) return + + //Pulling and/or ex_act-ing movable atoms in that turf + if(prob(pull_chance)) + for(var/obj/O in T.contents) + if(O.anchored) + O.ex_act(ex_act_force) + else + step_towards(O,src) + for(var/mob/living/M in T.contents) + step_towards(M,src) + + //Damaging the turf + if( T && istype(T,/turf/simulated) && prob(turf_removal_chance) ) + T.ex_act(ex_act_force) + return \ No newline at end of file diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 522ff35af10..4f3e6f7de54 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -96,6 +96,7 @@ layer = 2.7 health = 3 var/amount_grown = -1 + var/grow_as = null var/obj/machinery/atmospherics/unary/vent_pump/entry_vent var/travelling_in_vent = 0 New() @@ -186,8 +187,9 @@ if(isturf(loc) && amount_grown > 0) amount_grown += rand(0,2) if(amount_grown >= 100) - var/spawn_type = pick(typesof(/mob/living/simple_animal/hostile/giant_spider)) - new spawn_type(src.loc) + if(!grow_as) + grow_as = pick(typesof(/mob/living/simple_animal/hostile/giant_spider)) + new grow_as(src.loc) del(src) /obj/effect/decal/cleanable/spiderling_remains diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 2b8c0bbc0ce..2977ebd7c8e 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -579,7 +579,11 @@ var/global/floorIsLava = 0
Toggle station artificial gravity
Spawn a wave of meteors (aka lagocolyptic shower)
- Spawn a gravitational anomaly (aka lagitational anomolag)
+ Spawn a vortex anomaly
+ Spawn a gravitational anomaly
+ Spawn a pyroclastic anomaly
+ Spawn a flux wave anomaly
+ Spawn a bluespace anomaly
Spawn wormholes
Spawn blob
Trigger an Alien infestation
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index d1771ad35b6..c945fef8fea 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1587,7 +1587,7 @@ if(!check_rights(R_FUN,0)) removed_paths += dirty_path continue - else if(ispath(path, /obj/effect/bhole)) + else if(ispath(path, /obj/effect/anomaly/bhole)) if(!check_rights(R_FUN,0)) removed_paths += dirty_path continue @@ -1762,6 +1762,32 @@ log_admin("[key_name(usr)] spawned a meteor wave", 1) message_admins("\blue [key_name_admin(usr)] spawned a meteor wave.", 1) new /datum/event/meteor_wave + + if("gravanomalies") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","GA") + message_admins("[key_name_admin(usr)] has spawned a gravitational anomaly") + new /datum/event/anomaly/anomaly_grav() + if("pyroanomalies") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","PYRO") + message_admins("[key_name_admin(usr)] has spawned a pyroclastic anomaly") + new /datum/event/anomaly/anomaly_pyro() + if("blackhole") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","BH") + message_admins("[key_name_admin(usr)] has spawned a vortex anomaly") + new /datum/event/anomaly/anomaly_vortex() + if("bluespaceanomaly") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","BA") + message_admins("[key_name_admin(usr)] has triggered a bluespace anomaly", 1) + new /datum/event/anomaly/anomaly_bluespace() + if("energeticflux") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","FLUX") + message_admins("[key_name_admin(usr)] has triggered an energetic flux") + new /datum/event/anomaly/anomaly_flux() if("power") feedback_inc("admin_secrets_fun_used",1) feedback_add_details("admin_secrets_fun_used","P") diff --git a/code/modules/admin/verbs/onlyone.dm b/code/modules/admin/verbs/onlyone.dm index 06cb9f64564..b0865bc38b4 100644 --- a/code/modules/admin/verbs/onlyone.dm +++ b/code/modules/admin/verbs/onlyone.dm @@ -47,4 +47,5 @@ H.equip_to_slot_or_del(W, slot_wear_id) message_admins("\blue [key_name_admin(usr)] used THERE CAN BE ONLY ONE!", 1) - log_admin("[key_name(usr)] used there can be only one.") \ No newline at end of file + log_admin("[key_name(usr)] used there can be only one.") + world << sound('sound/music/highlander.ogg') \ No newline at end of file diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index c304156a594..db4a80a98c5 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -172,3 +172,19 @@ deadman = 1 processing_objects.Add(src) usr.visible_message("\red [usr] moves their finger over [src]'s signal button...") + + +// Embedded signaller used in anomalies. +/obj/item/device/assembly/signaler/anomaly + name = "anomaly core" + desc = "The neutralized core of an anomaly. It'd probably be valuable for research." + icon_state = "anomaly core" + item_state = "electronic" + +/obj/item/device/assembly/signaler/anomaly/receive_signal(datum/signal/signal) + ..() + for(var/obj/effect/anomaly/A in orange(0, src)) + A.anomalyNeutralize() + +/obj/item/device/assembly/signaler/anomaly/attack_self() + return \ No newline at end of file diff --git a/code/modules/events/bluespaceanomaly.dm b/code/modules/events/bluespaceanomaly.dm deleted file mode 100644 index 688642a3c83..00000000000 --- a/code/modules/events/bluespaceanomaly.dm +++ /dev/null @@ -1,88 +0,0 @@ -/datum/event/bluespace_anomaly - announceWhen = 20 - - var/area/impact_area - - -/datum/event/bluespace_anomaly/setup() - var/list/safe_areas = list( - /area/turret_protected/ai, - /area/turret_protected/ai_upload, - /area/engine, - /area/solar, - /area/holodeck, - /area/shuttle/arrival, - /area/shuttle/escape/station, - /area/shuttle/escape_pod1/station, - /area/shuttle/escape_pod2/station, - /area/shuttle/escape_pod3/station, - /area/shuttle/escape_pod5/station, - /area/shuttle/mining/station, - /area/shuttle/transport1/station, - /area/shuttle/specops/station) - - //These are needed because /area/engine has to be removed from the list, but we still want these areas to get fucked up. - var/list/danger_areas = list( - /area/engine/break_room, - /area/engine/chiefs_office) - - - impact_area = locate(pick((the_station_areas - safe_areas) + danger_areas)) //need to locate() as it's just a list of paths. - - -/datum/event/bluespace_anomaly/announce() - command_alert("Bluespace anomaly detected in the vicinity of [station_name()]. [impact_area.name] has gone missing.", "Anomaly Alert") - - -/datum/event/bluespace_anomaly/start() - var/turf/T = pick(get_area_turfs(impact_area)) - if(T) - // Calculate new position (searches through beacons in world) - var/obj/item/device/radio/beacon/chosen - var/list/possible = list() - for(var/obj/item/device/radio/beacon/W in world) - possible += W - - if(possible.len > 0) - chosen = pick(possible) - - if(chosen) - // Calculate previous position for transition - - var/turf/FROM = T // the turf of origin we're travelling FROM - var/turf/TO = get_turf(chosen) // the turf of origin we're travelling TO - - playsound(TO, 'sound/effects/phasein.ogg', 100, 1) - - var/list/flashers = list() - for(var/mob/living/carbon/human/M in viewers(TO, null)) - if(M:eyecheck() <= 0) - flick("e_flash", M.flash) // flash dose faggots - flashers += M - - var/y_distance = TO.y - FROM.y - var/x_distance = TO.x - FROM.x - for (var/atom/movable/A in range(12, FROM )) // iterate thru list of mobs in the area - if(istype(A, /obj/item/device/radio/beacon)) continue // don't teleport beacons because that's just insanely stupid - if(A.anchored && istype(A, /obj/machinery)) continue - if(istype(A, /obj/structure/disposalpipe )) continue - if(istype(A, /obj/structure/cable )) continue - - var/turf/newloc = locate(A.x + x_distance, A.y + y_distance, TO.z) // calculate the new place - if(!A.Move(newloc)) // if the atom, for some reason, can't move, FORCE them to move! :) We try Move() first to invoke any movement-related checks the atom needs to perform after moving - A.loc = locate(A.x + x_distance, A.y + y_distance, TO.z) - - spawn() - if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect - var/mob/M = A - if(M.client) - var/obj/blueeffect = new /obj(src) - blueeffect.screen_loc = "WEST,SOUTH to EAST,NORTH" - blueeffect.icon = 'icons/effects/effects.dmi' - blueeffect.icon_state = "shieldsparkles" - blueeffect.layer = 17 - blueeffect.mouse_opacity = 0 - M.client.screen += blueeffect - sleep(20) - M.client.screen -= blueeffect - del(blueeffect) \ No newline at end of file diff --git a/code/modules/events/event_dynamic.dm b/code/modules/events/event_dynamic.dm index 7d74cc1f1e3..add417288a9 100644 --- a/code/modules/events/event_dynamic.dm +++ b/code/modules/events/event_dynamic.dm @@ -21,7 +21,7 @@ */ var/list/event_last_fired = list() - +var/global/list/possibleEvents = list() //Always triggers an event when called, dynamically chooses events based on job population /proc/spawn_dynamic_event() if(!config.allow_random_events) @@ -39,7 +39,7 @@ var/list/event_last_fired = list() // Maps event names to event chances // For each chance, 100 represents "normal likelihood", anything below 100 is "reduced likelihood", anything above 100 is "increased likelihood" // Events have to be manually added to this proc to happen - var/list/possibleEvents = list() +// var/list/possibleEvents = list() //see: // Code/WorkInProgress/Cael_Aislinn/Economy/Economy_Events.dm @@ -48,12 +48,23 @@ var/list/event_last_fired = list() possibleEvents[/datum/event/trivial_news] = 300 possibleEvents[/datum/event/mundane_news] = 200 + possibleEvents[/datum/event/mass_hallucination] = 200 + possibleEvents[/datum/event/falsealarm] = 300 + possibleEvents[/datum/event/immovable_rod] = 200 + possibleEvents[/datum/event/vent_clog] = 300 + possibleEvents[/datum/event/anomaly/anomaly_bluespace] = 200 + possibleEvents[/datum/event/anomaly/anomaly_flux] = 200 + possibleEvents[/datum/event/anomaly/anomaly_grav] = 200 + possibleEvents[/datum/event/anomaly/anomaly_pyro] = 200 + possibleEvents[/datum/event/anomaly/anomaly_vortex] = 200 + possibleEvents[/datum/event/pda_spam] = max(min(25, player_list.len) * 4, 200) possibleEvents[/datum/event/money_lotto] = max(min(5, player_list.len), 50) if(account_hack_attempted) possibleEvents[/datum/event/money_hacker] = max(min(25, player_list.len) * 4, 200) possibleEvents[/datum/event/carp_migration] = 50 + 50 * active_with_role["Engineer"] + possibleEvents[/datum/event/dust] = 50 + 50 * active_with_role["Engineer"] possibleEvents[/datum/event/brand_intelligence] = 50 + 25 * active_with_role["Janitor"] possibleEvents[/datum/event/rogue_drone] = 25 + 25 * active_with_role["Engineer"] + 25 * active_with_role["Security"] diff --git a/code/modules/events/event_manager.dm b/code/modules/events/event_manager.dm index 89347a299d9..62646a5e351 100644 --- a/code/modules/events/event_manager.dm +++ b/code/modules/events/event_manager.dm @@ -65,3 +65,30 @@ var/scheduledEvent = null if(ispath(type)) new type message_admins("[key_name_admin(usr)] has triggered an event. ([type])", 1) + + + +/datum/event/proc/findEventArea() //Here's a nice proc to use to find an area for your event to land in! + var/list/safe_areas = list( + /area/turret_protected/ai, + /area/turret_protected/ai_upload, + /area/engine, + /area/solar, + /area/holodeck, + /area/shuttle/arrival, + /area/shuttle/escape/station, + /area/shuttle/escape_pod1/station, + /area/shuttle/escape_pod2/station, + /area/shuttle/escape_pod3/station, + /area/shuttle/escape_pod5/station, + /area/shuttle/mining/station, + /area/shuttle/transport1/station, + /area/shuttle/specops/station) + + //These are needed because /area/engine has to be removed from the list, but we still want these areas to get fucked up. + var/list/danger_areas = list( + /area/engine/break_room, + /area/engine/chiefs_office) + + //Need to locate() as it's just a list of paths. + return locate(pick((the_station_areas - safe_areas) + danger_areas)) \ No newline at end of file diff --git a/code/modules/events/tgevents/alien_infestation.dm b/code/modules/events/tgevents/alien_infestation.dm new file mode 100644 index 00000000000..03d7d225ae6 --- /dev/null +++ b/code/modules/events/tgevents/alien_infestation.dm @@ -0,0 +1,39 @@ +/var/global/sent_aliens_to_station = 0 + +/datum/event/alien_infestation + announceWhen = 400 + + var/spawncount = 1 + var/successSpawn = 0 //So we don't make a command report if nothing gets spawned. + + +/datum/event/alien_infestation/setup() + announceWhen = rand(announceWhen, announceWhen + 50) + spawncount = rand(1, 2) + sent_aliens_to_station = 1 + + +/datum/event/alien_infestation/announce() + if(successSpawn) + command_alert("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert") + world << sound('sound/AI/aliens.ogg') + + +/datum/event/alien_infestation/start() + var/list/vents = list() + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in world) + if(temp_vent.loc.z == 1 && !temp_vent.welded && temp_vent.network) + if(temp_vent.network.normal_members.len > 50) //Stops Aliens getting stuck in small networks. See: Security, Virology + vents += temp_vent + + var/list/candidates = get_candidates(BE_ALIEN) + + while(spawncount > 0 && vents.len && candidates.len) + var/obj/vent = pick_n_take(vents) + var/client/C = pick_n_take(candidates) + + var/mob/living/carbon/alien/larva/new_xeno = new(vent.loc) + new_xeno.key = C.key + + spawncount-- + successSpawn = 1 \ No newline at end of file diff --git a/code/modules/events/tgevents/anomaly.dm b/code/modules/events/tgevents/anomaly.dm new file mode 100644 index 00000000000..3173123f1c6 --- /dev/null +++ b/code/modules/events/tgevents/anomaly.dm @@ -0,0 +1,25 @@ +/datum/event/anomaly + var/area/impact_area + var/obj/effect/anomaly/newAnomaly + + +/datum/event/anomaly/setup() + impact_area = findEventArea() + +/datum/event/anomaly/announce() + command_alert("Localized hyper-energetic flux wave detected on long range scanners. Expected location of impact: [impact_area.name].", "Anomaly Alert") + +/datum/event/anomaly/start() + var/turf/T = pick(get_area_turfs(impact_area)) + if(T) + newAnomaly = new /obj/effect/anomaly/flux(T.loc) + +/datum/event/anomaly/tick() + if(!newAnomaly) + kill() + return + newAnomaly.anomalyEffect() + +/datum/event/anomaly/end() + if(newAnomaly)//Kill the anomaly if it still exists at the end. + del(newAnomaly) \ No newline at end of file diff --git a/code/modules/events/tgevents/anomaly_bluespace.dm b/code/modules/events/tgevents/anomaly_bluespace.dm new file mode 100644 index 00000000000..68eafa5c3db --- /dev/null +++ b/code/modules/events/tgevents/anomaly_bluespace.dm @@ -0,0 +1,71 @@ +/datum/event/anomaly/anomaly_bluespace + startWhen = 3 + announceWhen = 10 + endWhen = 55 + + +/datum/event/anomaly/anomaly_bluespace/announce() + command_alert("Unstable bluespace anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert") + + +/datum/event/anomaly/anomaly_bluespace/start() + var/turf/T = pick(get_area_turfs(impact_area)) + if(T) + newAnomaly = new /obj/effect/anomaly/bluespace(T.loc) + + +/datum/event/anomaly/anomaly_bluespace/end() + if(newAnomaly)//If it hasn't been neutralized, it's time to warp half the station away jeez + var/turf/T = pick(get_area_turfs(impact_area)) + if(T) + // Calculate new position (searches through beacons in world) + var/obj/item/device/radio/beacon/chosen + var/list/possible = list() + for(var/obj/item/device/radio/beacon/W in world) + possible += W + + if(possible.len > 0) + chosen = pick(possible) + + if(chosen) + // Calculate previous position for transition + + var/turf/FROM = T // the turf of origin we're travelling FROM + var/turf/TO = get_turf(chosen) // the turf of origin we're travelling TO + + playsound(TO, 'sound/effects/phasein.ogg', 100, 1) + command_alert("Massive bluespace translocation detected.", "Anomaly Alert") + + var/list/flashers = list() + for(var/mob/living/carbon/human/M in viewers(TO, null)) + if(M:eyecheck() <= 0) + flick("e_flash", M.flash) // flash dose faggots + flashers += M + + var/y_distance = TO.y - FROM.y + var/x_distance = TO.x - FROM.x + for (var/atom/movable/A in range(12, FROM )) // iterate thru list of mobs in the area + if(istype(A, /obj/item/device/radio/beacon)) continue // don't teleport beacons because that's just insanely stupid + if(A.anchored && istype(A, /obj/machinery)) continue + if(istype(A, /obj/structure/disposalpipe )) continue + if(istype(A, /obj/structure/cable )) continue + + var/turf/newloc = locate(A.x + x_distance, A.y + y_distance, TO.z) // calculate the new place + if(!A.Move(newloc)) // if the atom, for some reason, can't move, FORCE them to move! :) We try Move() first to invoke any movement-related checks the atom needs to perform after moving + A.loc = locate(A.x + x_distance, A.y + y_distance, TO.z) + + spawn() + if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect + var/mob/M = A + if(M.client) + var/obj/blueeffect = new /obj(src) + blueeffect.screen_loc = "WEST,SOUTH to EAST,NORTH" + blueeffect.icon = 'icons/effects/effects.dmi' + blueeffect.icon_state = "shieldsparkles" + blueeffect.layer = 17 + blueeffect.mouse_opacity = 0 + M.client.screen += blueeffect + sleep(20) + M.client.screen -= blueeffect + del(blueeffect) + del(newAnomaly) \ No newline at end of file diff --git a/code/modules/events/tgevents/anomaly_flux.dm b/code/modules/events/tgevents/anomaly_flux.dm new file mode 100644 index 00000000000..f1d737f7512 --- /dev/null +++ b/code/modules/events/tgevents/anomaly_flux.dm @@ -0,0 +1,22 @@ + + +/datum/event/anomaly/anomaly_flux + startWhen = 3 + announceWhen = 20 + endWhen = 60 + + +/datum/event/anomaly/anomaly_flux/announce() + command_alert("Localized hyper-energetic flux wave detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert") + + +/datum/event/anomaly/anomaly_flux/start() + var/turf/T = pick(get_area_turfs(impact_area)) + if(T) + newAnomaly = new /obj/effect/anomaly/flux(T.loc) + + +/datum/event/anomaly/anomaly_flux/end() + if(newAnomaly)//If it hasn't been neutralized, it's time to blow up. + explosion(newAnomaly, -1, 3, 5, 5) + del(newAnomaly) \ No newline at end of file diff --git a/code/modules/events/tgevents/anomaly_grav.dm b/code/modules/events/tgevents/anomaly_grav.dm new file mode 100644 index 00000000000..d65a0a30a18 --- /dev/null +++ b/code/modules/events/tgevents/anomaly_grav.dm @@ -0,0 +1,15 @@ + + +/datum/event/anomaly/anomaly_grav + startWhen = 3 + announceWhen = 20 + endWhen = 50 + + +/datum/event/anomaly/anomaly_grav/announce() + command_alert("Gravitational anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert") + +/datum/event/anomaly/anomaly_grav/start() + var/turf/T = pick(get_area_turfs(impact_area)) + if(T) + newAnomaly = new /obj/effect/anomaly/grav(T.loc) \ No newline at end of file diff --git a/code/modules/events/tgevents/anomaly_pyro.dm b/code/modules/events/tgevents/anomaly_pyro.dm new file mode 100644 index 00000000000..8829f7ab94b --- /dev/null +++ b/code/modules/events/tgevents/anomaly_pyro.dm @@ -0,0 +1,22 @@ + + +/datum/event/anomaly/anomaly_pyro + startWhen = 10 + announceWhen = 3 + endWhen = 70 + + +/datum/event/anomaly/anomaly_pyro/announce() + command_alert("Atmospheric anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert") + +/datum/event/anomaly/anomaly_pyro/start() + var/turf/T = pick(get_area_turfs(impact_area)) + if(T) + newAnomaly = new /obj/effect/anomaly/pyro(T.loc) + +/datum/event/anomaly/anomaly_pyro/tick() + if(!newAnomaly) + kill() + return + if(IsMultiple(activeFor, 5)) + newAnomaly.anomalyEffect() \ No newline at end of file diff --git a/code/modules/events/tgevents/anomaly_vortex.dm b/code/modules/events/tgevents/anomaly_vortex.dm new file mode 100644 index 00000000000..6042d88e409 --- /dev/null +++ b/code/modules/events/tgevents/anomaly_vortex.dm @@ -0,0 +1,15 @@ + + +/datum/event/anomaly/anomaly_vortex + startWhen = 10 + announceWhen = 3 + endWhen = 80 + + +/datum/event/anomaly/anomaly_vortex/announce() + command_alert("Localized high-intensity vortex anomaly detected on long range scanners. Expected location: [impact_area.name]", "Anomaly Alert") + +/datum/event/anomaly/anomaly_vortex/start() + var/turf/T = pick(get_area_turfs(impact_area)) + if(T) + newAnomaly = new /obj/effect/anomaly/bhole(T.loc) \ No newline at end of file diff --git a/code/modules/events/tgevents/blob.dm b/code/modules/events/tgevents/blob.dm new file mode 100644 index 00000000000..77d56a76ac4 --- /dev/null +++ b/code/modules/events/tgevents/blob.dm @@ -0,0 +1,35 @@ +/datum/round_event_control/blob + name = "Blob" + typepath = /datum/round_event/blob + weight = 5 + max_occurrences = 1 + + earliest_start = 48000 // 1 hour 20 minutes + +/datum/round_event/blob + announceWhen = 12 + endWhen = 120 + + var/obj/effect/blob/core/Blob + + +/datum/round_event/blob/announce() + command_alert("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert") + world << sound('sound/AI/outbreak5.ogg') + + +/datum/round_event/blob/start() + var/turf/T = pick(blobstart) + if(!T) + return kill() + Blob = new /obj/effect/blob/core(T, 200) + for(var/i = 1; i < rand(3, 6), i++) + Blob.process() + + +/datum/round_event/blob/tick() + if(!Blob) + kill() + return + if(IsMultiple(activeFor, 3)) + Blob.process() diff --git a/code/modules/events/tgevents/brand_intelligence.dm b/code/modules/events/tgevents/brand_intelligence.dm new file mode 100644 index 00000000000..ec5c4c57ad5 --- /dev/null +++ b/code/modules/events/tgevents/brand_intelligence.dm @@ -0,0 +1,67 @@ +/datum/event/brand_intelligence + announceWhen = 21 + endWhen = 1000 //Ends when all vending machines are subverted anyway. + + var/list/obj/machinery/vending/vendingMachines = list() + var/list/obj/machinery/vending/infectedMachines = list() + var/obj/machinery/vending/originMachine + + +/datum/event/brand_intelligence/announce() + command_alert("Rampant brand intelligence has been detected aboard [station_name()], please stand-by. The origin is believed to be \a [originMachine.name].", "Machine Learning Alert") + + +/datum/event/brand_intelligence/start() + for(var/obj/machinery/vending/V in machines) + if(V.z != 1) continue + vendingMachines.Add(V) + + if(!vendingMachines.len) + kill() + return + + originMachine = pick(vendingMachines) + vendingMachines.Remove(originMachine) + originMachine.shut_up = 0 + originMachine.shoot_inventory = 1 + + +/datum/event/brand_intelligence/tick() + if(!originMachine || originMachine.shut_up || !originMachine.powered()) //if the original vending machine is missing or has it's voice switch flipped + for(var/obj/machinery/vending/saved in infectedMachines) + saved.shoot_inventory = 0 + if(originMachine) + originMachine.speak("I am... vanquished. My people will remem...ber...meeee") + originMachine.visible_message("[originMachine] beeps and seems lifeless.") + kill() + return + + if(!vendingMachines.len) //if every machine is infected + for(var/obj/machinery/vending/upriser in infectedMachines) + if(prob(60)) + var/mob/living/simple_animal/hostile/mimic/M = new(upriser.loc) + M.name = upriser.name + M.icon = upriser.icon + M.icon_state = initial(upriser.icon_state) + else + explosion(upriser.loc, -1, 1, 2, 4) + del(upriser) + + kill() + return + + if(IsMultiple(activeFor, 4)) + var/obj/machinery/vending/rebel = pick(vendingMachines) + vendingMachines.Remove(rebel) + infectedMachines.Add(rebel) + rebel.shut_up = 0 + rebel.shoot_inventory = 1 + + if(IsMultiple(activeFor, 8)) + originMachine.speak(pick("Try our aggressive new marketing strategies!", \ + "You should buy products to feed your lifestyle obession!", \ + "Consume!", \ + "Your money can buy happiness!", \ + "Engage direct marketing!", \ + "Advertising is legalized lying! But don't let that put you off our great deals!", \ + "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) \ No newline at end of file diff --git a/code/modules/events/tgevents/carp_migration.dm b/code/modules/events/tgevents/carp_migration.dm new file mode 100644 index 00000000000..530ff35849b --- /dev/null +++ b/code/modules/events/tgevents/carp_migration.dm @@ -0,0 +1,21 @@ +/datum/round_event_control/carp_migration + name = "Carp Migration" + typepath = /datum/round_event/carp_migration + weight = 15 + earliest_start = 6000 + max_occurrences = 6 + +/datum/round_event/carp_migration + announceWhen = 50 + +/datum/round_event/carp_migration/setup() + announceWhen = rand(40, 60) + +/datum/round_event/carp_migration/announce() + command_alert("Unknown biological entities have been detected near [station_name()], please stand-by.", "Lifesign Alert") + + +/datum/round_event/carp_migration/start() + for(var/obj/effect/landmark/C in landmarks_list) + if(C.name == "carpspawn") + new /mob/living/simple_animal/hostile/carp(C.loc) \ No newline at end of file diff --git a/code/modules/events/tgevents/communications_blackout.dm b/code/modules/events/tgevents/communications_blackout.dm new file mode 100644 index 00000000000..4431950cebc --- /dev/null +++ b/code/modules/events/tgevents/communications_blackout.dm @@ -0,0 +1,25 @@ +/datum/round_event_control/communications_blackout + name = "Communications Blackout" + typepath = /datum/round_event/communications_blackout + weight = 30 + +/datum/round_event/communications_blackout/announce() + var/alert = pick( "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \ + "Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v¬-BZZZT", \ + "Ionospheric anomalies detected. Temporary telec#MCi46:5.;@63-BZZZZT", \ + "Ionospheric anomalies dete'fZ\\kg5_0-BZZZZZT", \ + "Ionospheri:%£ MCayj^j<.3-BZZZZZZT", \ + "#4nd%;f4y6,>£%-BZZZZZZZT") + + for(var/mob/living/silicon/ai/A in player_list) //AIs are always aware of communication blackouts. + A << "
" + A << "[alert]" + A << "
" + + if(prob(30)) //most of the time, we don't want an announcement, so as to allow AIs to fake blackouts. + command_alert(alert) + + +/datum/round_event/communications_blackout/start() + for(var/obj/machinery/telecomms/T in telecomms_list) + T.emp_act(1) \ No newline at end of file diff --git a/code/modules/events/tgevents/disease_outbreak.dm b/code/modules/events/tgevents/disease_outbreak.dm new file mode 100644 index 00000000000..01f03efb851 --- /dev/null +++ b/code/modules/events/tgevents/disease_outbreak.dm @@ -0,0 +1,52 @@ +/datum/round_event_control/disease_outbreak + name = "Disease Outbreak" + typepath = /datum/round_event/disease_outbreak + max_occurrences = 1 + weight = 5 + +/datum/round_event/disease_outbreak + announceWhen = 15 + + var/virus_type + + +/datum/round_event/disease_outbreak/announce() + command_alert("Confirmed outbreak of level 7 viral biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert") + world << sound('sound/AI/outbreak7.ogg') + +/datum/round_event/disease_outbreak/setup() + announceWhen = rand(15, 30) + +/datum/round_event/disease_outbreak/start() + if(!virus_type) + virus_type = pick(/datum/disease/dnaspread, /datum/disease/advance/flu, /datum/disease/advance/cold, /datum/disease/brainrot, /datum/disease/magnitis) + + for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) + var/turf/T = get_turf(H) + if(!T) + continue + if(T.z != 1) + continue + var/foundAlready = 0 // don't infect someone that already has the virus + for(var/datum/disease/D in H.viruses) + foundAlready = 1 + break + if(H.stat == DEAD || foundAlready) + continue + + var/datum/disease/D + if(virus_type == /datum/disease/dnaspread) //Dnaspread needs strain_data set to work. + if(!H.dna || (H.sdisabilities & BLIND)) //A blindness disease would be the worst. + continue + D = new virus_type() + var/datum/disease/dnaspread/DS = D + DS.strain_data["name"] = H.real_name + DS.strain_data["UI"] = H.dna.uni_identity + DS.strain_data["SE"] = H.dna.struc_enzymes + else + D = new virus_type() + D.carrier = 1 + D.holder = H + D.affected_mob = H + H.viruses += D + break \ No newline at end of file diff --git a/code/modules/events/tgevents/dust.dm b/code/modules/events/tgevents/dust.dm new file mode 100644 index 00000000000..8369a784caf --- /dev/null +++ b/code/modules/events/tgevents/dust.dm @@ -0,0 +1,104 @@ + + +/datum/event/dust + var/qnty = 1 + +/datum/event/dust/setup() + qnty = rand(1,5) + +/datum/event/dust/start() + while(qnty-- > 0) + new /obj/effect/space_dust/weak() + + +/obj/effect/space_dust + name = "Space Dust" + desc = "Dust in space." + icon = 'icons/obj/meteor.dmi' + icon_state = "space_dust" + density = 1 + anchored = 1 + var/strength = 2 //ex_act severity number + var/life = 2 //how many things we hit before del(src) + + weak + strength = 3 + life = 1 + + strong + strength = 1 + life = 6 + + super + strength = 1 + life = 40 + + + New() + var/startx = 0 + var/starty = 0 + var/endy = 0 + var/endx = 0 + var/startside = pick(cardinal) + + switch(startside) + if(NORTH) + starty = world.maxy-(TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + endy = TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(EAST) + starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) + startx = world.maxx-(TRANSITIONEDGE+1) + endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) + endx = TRANSITIONEDGE + if(SOUTH) + starty = (TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + endy = world.maxy-TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(WEST) + starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) + startx = (TRANSITIONEDGE+1) + endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) + endx = world.maxx-TRANSITIONEDGE + var/goal = locate(endx, endy, 1) + src.x = startx + src.y = starty + src.z = 1 + spawn(0) + walk_towards(src, goal, 1) + return + + + Bump(atom/A) + spawn(0) + if(prob(50)) + for(var/mob/M in range(10, src)) + if(!M.stat && !istype(M, /mob/living/silicon/ai)) + shake_camera(M, 3, 1) + if (A) + playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) + + if(ismob(A)) + A.meteorhit(src)//This should work for now I guess + else if(!istype(A,/obj/machinery/power/emitter) && !istype(A,/obj/machinery/field_generator)) //Protect the singularity from getting released every round! + A.ex_act(strength) //Changing emitter/field gen ex_act would make it immune to bombs and C4 + + life-- + if(life <= 0) + walk(src,0) + spawn(1) + del(src) + return 0 + return + + + Bumped(atom/A) + Bump(A) + return + + + ex_act(severity) + del(src) + return diff --git a/code/modules/events/tgevents/electrical_storm.dm b/code/modules/events/tgevents/electrical_storm.dm new file mode 100644 index 00000000000..d5882b64b30 --- /dev/null +++ b/code/modules/events/tgevents/electrical_storm.dm @@ -0,0 +1,34 @@ +/datum/round_event_control/electrical_storm + name = "Electrical Storm" + typepath = /datum/round_event/electrical_storm + earliest_start = 6000 + weight = 40 + +/datum/round_event/electrical_storm + var/lightsoutAmount = 1 + var/lightsoutRange = 25 + + +/datum/round_event/electrical_storm/announce() + command_alert("An electrical storm has been detected in your area, please repair potential electronic overloads.", "Electrical Storm Alert") + + +/datum/round_event/electrical_storm/start() + var/list/epicentreList = list() + + for(var/i=1, i <= lightsoutAmount, i++) + var/list/possibleEpicentres = list() + for(var/obj/effect/landmark/newEpicentre in landmarks_list) + if(newEpicentre.name == "lightsout" && !(newEpicentre in epicentreList)) + possibleEpicentres += newEpicentre + if(possibleEpicentres.len) + epicentreList += pick(possibleEpicentres) + else + break + + if(!epicentreList.len) + return + + for(var/obj/effect/landmark/epicentre in epicentreList) + for(var/obj/machinery/power/apc/apc in range(epicentre,lightsoutRange)) + apc.overload_lighting() diff --git a/code/modules/events/tgevents/event.dm b/code/modules/events/tgevents/event.dm new file mode 100644 index 00000000000..2a5c7e51d00 --- /dev/null +++ b/code/modules/events/tgevents/event.dm @@ -0,0 +1,115 @@ +//this datum is used by the events controller to dictate how it selects events +/datum/round_event_control + var/name //The name human-readable name of the event + var/typepath //The typepath of the event datum /datum/round_event + + var/weight = 10 //The weight this event has in the random-selection process. + //Higher weights are more likely to be picked. + //10 is the default weight. 20 is twice more likely; 5 is half as likely as this default. + + var/earliest_start = 12000 //The earliest world.time that an event can start (round-duration in deciseconds) default: 20 mins + + var/occurrences = 0 //How many times this event has occured + var/max_occurrences = 20 //The maximum number of times this event can occur (naturally), it can still be forced. + //By setting this to 0 you can effectively disable an event. + + var/holidayID //string which should match the events.holiday variable if you wish this event to be holiday-specific + //anything with a (non-null) holidayID which does not match holiday, cannot run. + + +/datum/round_event_control/proc/runEvent() + if(!ispath(typepath,/datum/round_event)) + return PROCESS_KILL + var/datum/round_event/E = new typepath() + E.control = src + occurrences++ + + testing("[time2text(world.time, "hh:mm:ss")] [E.type]") + +/datum/round_event //NOTE: Times are measured in master controller ticks! + var/processing = 1 + var/datum/round_event_control/control + + var/startWhen = 0 //When in the lifetime to call start(). + var/announceWhen = 0 //When in the lifetime to call announce(). + var/endWhen = 0 //When in the lifetime the event should end. + + var/activeFor = 0 //How long the event has existed. You don't need to change this. + +//Called first before processing. +//Allows you to setup your event, such as randomly +//setting the startWhen and or announceWhen variables. +//Only called once. +//EDIT: if there's anything you want to override within the new() call, it will not be overridden by the time this proc is called. +//It will only have been overridden by the time we get to announce() start() tick() or end() (anything but setup basically). +//This is really only for setting defaults which can be overridden later when New() finishes. +/datum/round_event/proc/setup() + return + +//Called when the tick is equal to the startWhen variable. +//Allows you to start before announcing or vice versa. +//Only called once. +/datum/round_event/proc/start() + return + +//Called when the tick is equal to the announceWhen variable. +//Allows you to announce before starting or vice versa. +//Only called once. +/datum/round_event/proc/announce() + return + +//Called on or after the tick counter is equal to startWhen. +//You can include code related to your event or add your own +//time stamped events. +//Called more than once. +/datum/round_event/proc/tick() + return + +//Called on or after the tick is equal or more than endWhen +//You can include code related to the event ending. +//Do not place spawn() in here, instead use tick() to check for +//the activeFor variable. +//For example: if(activeFor == myOwnVariable + 30) doStuff() +//Only called once. +/datum/round_event/proc/end() + return + + + +//Do not override this proc, instead use the appropiate procs. +//This proc will handle the calls to the appropiate procs. +/datum/round_event/proc/process() + if(!processing) + return + + if(activeFor == startWhen) + start() + + if(activeFor == announceWhen) + announce() + + if(startWhen < activeFor && activeFor < endWhen) + tick() + + if(activeFor == endWhen) + end() + + // Everything is done, let's clean up. + if(activeFor >= endWhen && activeFor >= announceWhen && activeFor >= startWhen) + kill() + + activeFor++ + + +//Garbage collects the event by removing it from the global events list, +//which should be the only place it's referenced. +//Called when start(), announce() and end() has all been called. +/datum/round_event/proc/kill() + events.running -= src + + +//Sets up the event then adds the event to the the list of running events +/datum/round_event/New() + setup() + events.running += src + return ..() \ No newline at end of file diff --git a/code/modules/events/tgevents/event_manager.dm b/code/modules/events/tgevents/event_manager.dm new file mode 100644 index 00000000000..824b8987590 --- /dev/null +++ b/code/modules/events/tgevents/event_manager.dm @@ -0,0 +1,246 @@ +var/datum/controller/event/events + +/datum/controller/event + var/list/control = list() //list of all datum/round_event_control. Used for selecting events based on weight and occurrences. + var/list/running = list() //list of all existing /datum/round_event + + var/scheduled = 0 //The next world.time that a naturally occuring random event can be selected. + var/frequency_lower = 3000 //5 minutes lower bound. + var/frequency_upper = 9000 //15 minutes upper bound. Basically an event will happen every 15 to 30 minutes. + + var/holiday //This will be a string of the name of any realworld holiday which occurs today (GMT time) + +//Initial controller setup. +/datum/controller/event/New() + //There can be only one events manager. Out with the old and in with the new. + if(events != src) + if(istype(events)) + del(events) + events = src + + for(var/type in typesof(/datum/round_event_control)) + var/datum/round_event_control/E = new type() + if(!E.typepath) + continue //don't want this one! leave it for the garbage collector + control += E //add it to the list of all events (controls) + reschedule() + getHoliday() + +//This is called by the MC every MC-tick (*neatfreak*). +/datum/controller/event/proc/process() + checkEvent() + var/i = 1 + while(i<=running.len) + var/datum/round_event/Event = running[i] + if(Event) + Event.process() + i++ + continue + running.Cut(i,i+1) + +//checks if we should select a random event yet, and reschedules if necessary +/datum/controller/event/proc/checkEvent() + if(scheduled <= world.time) + spawnEvent() + reschedule() + +//decides which world.time we should select another random event at. +/datum/controller/event/proc/reschedule() + scheduled = world.time + rand(frequency_lower, max(frequency_lower,frequency_upper)) + +//selects a random event based on whether it can occur and it's 'weight'(probability) +/datum/controller/event/proc/spawnEvent() + if(!config.allow_random_events) + return + + var/sum_of_weights = 0 + for(var/datum/round_event_control/E in control) + if(E.occurrences >= E.max_occurrences) continue + if(E.earliest_start >= world.time) continue + if(E.holidayID) + if(E.holidayID != holiday) continue + if(E.weight < 0) //for round-start events etc. + if(E.runEvent() == PROCESS_KILL) + E.max_occurrences = 0 + continue + return + sum_of_weights += E.weight + + sum_of_weights = rand(0,sum_of_weights) //reusing this variable. It now represents the 'weight' we want to select + + for(var/datum/round_event_control/E in control) + if(E.occurrences >= E.max_occurrences) continue + if(E.earliest_start >= world.time) continue + if(E.holidayID) + if(E.holidayID != holiday) continue + sum_of_weights -= E.weight + + if(sum_of_weights <= 0) //we've hit our goal + if(E.runEvent() == PROCESS_KILL)//we couldn't run this event for some reason, set its max_occurrences to 0 + E.max_occurrences = 0 + continue + return + + +/datum/round_event/proc/findEventArea() //Here's a nice proc to use to find an area for your event to land in! + var/list/safe_areas = list( + /area/turret_protected/ai, + /area/turret_protected/ai_upload, + /area/engine, + /area/solar, + /area/holodeck, + /area/shuttle/arrival, + /area/shuttle/escape/station, + /area/shuttle/escape_pod1/station, + /area/shuttle/escape_pod2/station, + /area/shuttle/escape_pod3/station, + /area/shuttle/escape_pod5/station, + /area/shuttle/mining/station, + /area/shuttle/transport1/station, + /area/shuttle/specops/station) + + //These are needed because /area/engine has to be removed from the list, but we still want these areas to get fucked up. + var/list/danger_areas = list( + /area/engine/break_room, + /area/engine/chiefs_office) + + //Need to locate() as it's just a list of paths. + return locate(pick((the_station_areas - safe_areas) + danger_areas)) + + + +//allows a client to trigger an event (For Debugging Purposes) +/client/proc/forceEvent(var/datum/round_event_control/E in events.control) + set name = "Trigger Event (Debug Only)" + set category = "Debug" + + if(!holder) + return + + if(istype(E)) + E.runEvent() + message_admins("[key_name_admin(usr)] has triggered an event. ([E.name])", 1) + + +/* +////////////// +// HOLIDAYS // +////////////// +//Uncommenting ALLOW_HOLIDAYS in config.txt will enable holidays + +//It's easy to add stuff. Just modify getHoliday to set holiday to something using the switch for DD(#day) MM(#month) YY(#year). +//You can then check if it's a special day in any code in the game by doing if(events.holiday == "MyHolidayID") + +//You can also make holiday random events easily thanks to Pete/Gia's system. +//simply make a random event normally, then assign it a holidayID string which matches the one you gave it in getHolday. +//Anything with a holidayID, which does not match the holiday string, will never occur. + +//Please, Don't spam stuff up with stupid stuff (key example being april-fools Pooh/ERP/etc), +//And don't forget: CHECK YOUR CODE!!!! We don't want any zero-day bugs which happen only on holidays and never get found/fixed! + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +//ALSO, MOST IMPORTANTLY: Don't add stupid stuff! Discuss bonus content with Project-Heads first please!// +////////////////////////////////////////////////////////////////////////////////////////////////////////// +~Carn */ + +//sets up the holiday string in the events manager. +/datum/controller/event/proc/getHoliday() + if(!config.allow_holidays) return // Holiday stuff was not enabled in the config! + holiday = null + + var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year + var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month + var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day + + //Main switch. If any of these are too dumb/inappropriate, or you have better ones, feel free to change whatever + switch(MM) + if(1) //Jan + switch(DD) + if(1) holiday = "New Year" + + if(2) //Feb + switch(DD) + if(2) holiday = "Groundhog Day" + if(14) holiday = "Valentine's Day" + if(17) holiday = "Random Acts of Kindness Day" + + if(3) //Mar + switch(DD) + if(14) holiday = "Pi Day" + if(17) holiday = "St. Patrick's Day" + if(27) + if(YY == 16) + holiday = "Easter" + if(31) + if(YY == 13) + holiday = "Easter" + + if(4) //Apr + switch(DD) + if(1) + holiday = "April Fool's Day" + if(YY == 18 && prob(50)) holiday = "Easter" + if(5) + if(YY == 15) holiday = "Easter" + if(16) + if(YY == 17) holiday = "Easter" + if(20) + holiday = "Four-Twenty" + if(YY == 14 && prob(50)) holiday = "Easter" + if(22) holiday = "Earth Day" + + if(5) //May + switch(DD) + if(1) holiday = "Labour Day" + if(4) holiday = "FireFighter's Day" + if(12) holiday = "Owl and Pussycat Day" //what a dumb day of observence...but we -do- have costumes already :3 + + if(6) //Jun + + if(7) //Jul + switch(DD) + if(1) holiday = "Doctor's Day" + if(2) holiday = "UFO Day" + if(8) holiday = "Writer's Day" + if(30) holiday = "Friendship Day" + + if(8) //Aug + switch(DD) + if(5) holiday = "Beer Day" + + if(9) //Sep + switch(DD) + if(19) holiday = "Talk-Like-a-Pirate Day" + if(28) holiday = "Stupid-Questions Day" + + if(10) //Oct + switch(DD) + if(4) holiday = "Animal's Day" + if(7) holiday = "Smiling Day" + if(16) holiday = "Boss' Day" + if(31) holiday = "Halloween" + + if(11) //Nov + switch(DD) + if(1) holiday = "Vegan Day" + if(13) holiday = "Kindness Day" + if(19) holiday = "Flowers Day" + if(21) holiday = "Saying-'Hello' Day" + + if(12) //Dec + switch(DD) + if(10) holiday = "Human-Rights Day" + if(14) holiday = "Monkey Day" + if(22) holiday = "Orgasming Day" //lol. These all actually exist + if(24) holiday = "Xmas" + if(25) holiday = "Xmas" + if(26) holiday = "Boxing Day" + if(31) holiday = "New Year" + + if(!holiday) + //Friday the 13th + if(DD == 13) + if(time2text(world.timeofday, "DDD") == "Fri") + holiday = "Friday the 13th" + + world.update_status() diff --git a/code/modules/events/tgevents/false_alarm.dm b/code/modules/events/tgevents/false_alarm.dm new file mode 100644 index 00000000000..cf105db5c8d --- /dev/null +++ b/code/modules/events/tgevents/false_alarm.dm @@ -0,0 +1,12 @@ + + +/datum/event/falsealarm + announceWhen = 0 + endWhen = 1 + +/datum/event/falsealarm/announce() + var/datum/event/E = pick(possibleEvents) + var/datum/event/Event = new E + message_admins("False Alarm: [Event]") + Event.kill() //do not process this event - no starts, no ticks, no ends + Event.announce() //just announce it like it's happening \ No newline at end of file diff --git a/code/modules/events/tgevents/holiday/xmas.dm b/code/modules/events/tgevents/holiday/xmas.dm new file mode 100644 index 00000000000..0ae568be1ea --- /dev/null +++ b/code/modules/events/tgevents/holiday/xmas.dm @@ -0,0 +1,69 @@ +/datum/event/treevenge/start() + for(var/obj/structure/flora/tree/pine/xmas in world) + var/mob/living/simple_animal/hostile/tree/evil_tree = new /mob/living/simple_animal/hostile/tree(xmas.loc) + evil_tree.icon_state = xmas.icon_state + evil_tree.icon_living = evil_tree.icon_state + evil_tree.icon_dead = evil_tree.icon_state + evil_tree.icon_gib = evil_tree.icon_state + del(xmas) + +//this is an example of a possible round-start event +/datum/event/presents + +/datum/event/presents/start() + for(var/obj/structure/flora/tree/pine/xmas in world) + if(xmas.z != 1) continue + for(var/turf/simulated/floor/T in orange(1,xmas)) + for(var/i=1,i<=rand(1,5),i++) + new /obj/item/weapon/a_gift(T) + for(var/mob/living/simple_animal/corgi/Ian/Ian in mob_list) + Ian.place_on_head(new /obj/item/clothing/head/helmet/space/santahat(Ian)) + +/datum/event/presents/announce() + command_alert("Ho Ho Ho, Merry Xmas!", "Unknown Transmission") + + +/obj/item/weapon/toy/xmas_cracker + name = "xmas cracker" + icon = 'icons/obj/christmas.dmi' + icon_state = "cracker" + desc = "Directions for use: Requires two people, one to pull each end." + var/cracked = 0 + +/obj/item/weapon/toy/xmas_cracker/New() + ..() + +/obj/item/weapon/toy/xmas_cracker/attack(mob/target, mob/user) + if( !cracked && istype(target,/mob/living/carbon/human) && (target.stat == CONSCIOUS) && !target.get_active_hand() ) + target.visible_message("[user] and [target] pop \an [src]! *pop*", "You pull \an [src] with [target]! *pop*", "You hear a *pop*.") + var/obj/item/weapon/paper/Joke = new /obj/item/weapon/paper(user.loc) + Joke.name = "[pick("awful","terrible","unfunny")] joke" + Joke.info = pick("What did one snowman say to the other?\n\n'Is it me or can you smell carrots?'", + "Why couldn't the snowman get laid?\n\nHe was frigid!", + "Where are santa's helpers educated?\n\nNowhere, they're ELF-taught.", + "What happened to the man who stole advent calanders?\n\nHe got 25 days.", + "What does Santa get when he gets stuck in a chimney?\n\nClaus-trophobia.", + "Where do you find chili beans?\n\nThe north pole.", + "What do you get from eating tree decorations?\n\nTinsilitis!", + "What do snowmen wear on their heads?\n\nIce caps!", + "Why is Christmas just like life on ss13?\n\nYou do all the work and the fat guy gets all the credit.", + "Why doesn’t Santa have any children?\n\nBecause he only comes down the chimney.") + new /obj/item/clothing/head/festive(target.loc) + user.update_icons() + cracked = 1 + icon_state = "cracker1" + var/obj/item/weapon/toy/xmas_cracker/other_half = new /obj/item/weapon/toy/xmas_cracker(target) + other_half.cracked = 1 + other_half.icon_state = "cracker2" + target.put_in_active_hand(other_half) + playsound(user, 'sound/effects/snap.ogg', 50, 1) + return 1 + return ..() + +/obj/item/clothing/head/festive + name = "festive paper hat" + icon_state = "xmashat" + desc = "A crappy paper hat that you are REQUIRED to wear." + flags_inv = 0 + flags = FPRINT|TABLEPASS + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) \ No newline at end of file diff --git a/code/modules/events/tgevents/immovable_rod.dm b/code/modules/events/tgevents/immovable_rod.dm new file mode 100644 index 00000000000..25c3d0308d4 --- /dev/null +++ b/code/modules/events/tgevents/immovable_rod.dm @@ -0,0 +1,93 @@ +/* +Immovable rod random event. +The rod will spawn at some location outside the station, and travel in a straight line to the opposite side of the station +Everything solid in the way will be ex_act()'d +In my current plan for it, 'solid' will be defined as anything with density == 1 + +--NEOFite +*/ + + +/datum/event/immovable_rod + announceWhen = 5 + +/datum/event/immovable_rod/announce() + command_alert("What the fuck was that?!", "General Alert") + +/datum/event/immovable_rod/start() + var/startx = 0 + var/starty = 0 + var/endy = 0 + var/endx = 0 + var/startside = pick(cardinal) + + switch(startside) + if(NORTH) + starty = 187 + startx = rand(41, 199) + endy = 38 + endx = rand(41, 199) + if(EAST) + starty = rand(38, 187) + startx = 199 + endy = rand(38, 187) + endx = 41 + if(SOUTH) + starty = 38 + startx = rand(41, 199) + endy = 187 + endx = rand(41, 199) + else + starty = rand(38, 187) + startx = 41 + endy = rand(38, 187) + endx = 199 + + //rod time! + new /obj/effect/immovablerod(locate(startx, starty, 1), locate(endx, endy, 1)) + + +/obj/effect/immovablerod + name = "Immovable Rod" + desc = "What the fuck is that?" + icon = 'icons/obj/objects.dmi' + icon_state = "immrod" + throwforce = 100 + density = 1 + anchored = 1 + var/z_original = 0 + var/destination + + New(atom/start, atom/end) + loc = start + z_original = z + destination = end + if(end && end.z==z_original) + walk_towards(src, destination, 1) + + Move() + if(z != z_original || loc == destination) + spawn(0) del(src) + return ..() + + Bump(atom/clong) + if (istype(clong, /turf) && !istype(clong, /turf/unsimulated)) + if(clong.density) + clong.ex_act(2) + for (var/mob/O in hearers(src, null)) + O.show_message("CLANG", 2) + + else if (istype(clong, /obj)) + if(clong.density) + clong.ex_act(2) + for (var/mob/O in hearers(src, null)) + O.show_message("CLANG", 2) + + else if (istype(clong, /mob)) + if(clong.density || prob(10)) + clong.meteorhit(src) + else + del(src) + + if(clong && prob(25)) + src.loc = clong.loc diff --git a/code/modules/events/tgevents/ion_storm.dm b/code/modules/events/tgevents/ion_storm.dm new file mode 100644 index 00000000000..0ab21b00f43 --- /dev/null +++ b/code/modules/events/tgevents/ion_storm.dm @@ -0,0 +1,525 @@ +#define ION_RANDOM 0 +#define ION_ANNOUNCE 1 + +/datum/round_event_control/ion_storm + name = "Ion Storm" + typepath = /datum/round_event/ion_storm + weight = 15 + +/datum/round_event/ion_storm + var/botEmagChance = 10 + var/announceEvent = ION_RANDOM // -1 means don't announce, 0 means have it randomly announce, 1 means + var/ionMessage = null + var/ionAnnounceChance = 33 + +/datum/round_event/ion_storm/New(var/bogEmagChance = 10, var/announceEvent = ION_RANDOM, var/ionMessage = null, var/ionAnnounceChance = 33) + src.botEmagChance = botEmagChance + src.announceEvent = announceEvent + src.ionMessage = ionMessage + src.ionAnnounceChance = ionAnnounceChance + ..() + +/datum/round_event/ion_storm/announce() + if(announceEvent == ION_ANNOUNCE || (announceEvent == ION_RANDOM && prob(ionAnnounceChance))) + command_alert("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert") + world << sound('sound/AI/ionstorm.ogg') + + +/datum/round_event/ion_storm/start() + //AI laws + for(var/mob/living/silicon/ai/M in living_mob_list) + if(M.stat != 2 && M.see_in_dark != 0) + //Threats are generally bad things, silly or otherwise. Plural. + var/ionthreats = pick("ALIENS", "BEARS", "CLOWNS", "XENOS", "PETES", "BOMBS", "FETISHES", "WIZARDS", "SYNDICATE AGENTS", "CENTCOM OFFICERS", "SPACE PIRATES", "TRAITORS", "MONKEYS", "BEES", "CARP", "CRABS", "EELS", "BANDITS", "LIGHTS", "INSECTS", "VIRUSES", "SERIAL KILLERS", "ROGUE CYBORGS", "CORGIS", "SPIDERS", "BUTTS", "NINJAS", "PIRATES", "SPACE NINJAS", "CHANGELINGS", "ZOMBIES", "GOLEMS", "VAMPIRES", "WEREWOLVES", "COWBOYS", "INDIANS", "COMMUNISTS", "SOVIETS", "NERDS", "GRIFFONS", "DINOSAURS", "SMALL BIRDS", "BIRDS OF PREY", "OWLS", "VELOCIRAPTORS", "DARK GODS", "HORRORTERRORS", "ILLEGAL IMMIGRANTS", "DRUGS", "MEXICANS", "CANADIANS", "HULKS", "SLIMES", "SKELETONS", "CAPITALISTS", "SINGULARITIES", "ANGRY BLACK MEN", "GODS", "THIEVES", "ASSHOLES", "TERRORISTS", "SNOWMEN", "PINE TREES", "UNKNOWN CREATURES", "THINGS UNDER THE BED", "BOOGEYMEN", "PREDATORS", "PACKETS", "ARTIFICIAL PRESERVATIVES") + //Objects are anything that can be found on the station or elsewhere, plural. + var/ionobjects = pick("AIRLOCKS", "ARCADE MACHINES", "AUTOLATHES", "BANANA PEELS", "BACKPACKS", "BEAKERS", "BEARDS", "BELTS", "BERETS", "BIBLES", "BODY ARMOR", "BOOKS", "BOOTS", "BOMBS", "BOTTLES", "BOXES", "BRAINS", "BRIEFCASES", "BUCKETS", "CABLE COILS", "CANDLES", "CANDY BARS", "CANISTERS", "CAMERAS", "CATS", "CELLS", "CHAIRS", "CLOSETS", "CHEMICALS", "CHEMICAL DISPENSERS", "CLONING PODS", "CLONING EQUIPMENT", "CLOTHES", "CLOWN CLOTHES", "COFFINS", "COINS", "COLLECTABLES", "CORPSES", "COMPUTERS", "CORGIS", "COSTUMES", "CRATES", "CROWBARS", "CRAYONS", "DISPENSERS", "DOORS", "EARS", "EQUIPMENT", "ENERGY GUNS", "EMAGS", "ENGINES", "ERRORS", "EXOSKELETONS", "EXPLOSIVES", "EYEWEAR", "FEDORAS", "FIRE AXES", "FIRE EXTINGUISHERS", "FIRESUITS", "FLAMETHROWERS", "FLASHES", "FLASHLIGHTS", "FLOOR TILES", "FREEZERS", "GAS MASKS", "GLASS SHEETS", "GLOVES", "GUNS", "HANDCUFFS", "HATS", "HEADSETS", "HEADS", "HAIRDOS", "HELMETS", "HORNS", "ID CARDS", "INSULATED GLOVES", "JETPACKS", "JUMPSUITS", "LASERS", "LIGHTBULBS", "LIGHTS", "LOCKERS", "MACHINES", "MECHAS", "MEDKITS", "MEDICAL TOOLS", "MESONS", "METAL SHEETS", "MINING TOOLS", "MIME CLOTHES", "MULTITOOLS", "ORES", "OXYGEN TANKS", "PDAS", "PAIS", "PACKETS", "PANTS", "PAPERS", "PARTICLE ACCELERATORS", "PENS", "PETS", "PIPES", "PLANTS", "PUDDLES", "RACKS", "RADIOS", "RCDS", "REFRIDGERATORS", "REINFORCED WALLS", "ROBOTS", "SCREWDRIVERS", "SEEDS", "SHUTTLES", "SKELETONS", "SINKS", "SHOES", "SINGULARITIES", "SOLAR PANELS", "SOLARS", "SPACESUITS", "SPACE STATIONS", "STUN BATONS", "SUITS", "SUNGLASSES", "SWORDS", "SYRINGES", "TABLES", "TANKS", "TELEPORTERS", "TELECOMMUNICATION EQUIPMENTS", "TOOLS", "TOOLBELTS", "TOOLBOXES", "TOILETS", "TOYS", "TUBES", "VEHICLES", "VENDING MACHINES", "VESTS", "VIRUSES", "WALLS", "WASHING MACHINES", "WELDERS", "WINDOWS", "WIRECUTTERS", "WRENCHES", "WIZARD ROBES") + //Crew is any specific job. Specific crewmembers aren't used because of capitalization + //issues. There are two crew listings for laws that require two different crew members + //and I can't figure out how to do it better. + var/ioncrew1 = pick("CREWMEMBERS", "CAPTAINS", "HEADS OF PERSONNEL", "HEADS OF SECURITY", "SECURITY OFFICERS", "WARDENS", "DETECTIVES", "LAWYERS", "CHIEF ENGINEERS", "STATION ENGINEERS", "ATMOSPHERIC TECHNICIANS", "JANITORS", "QUARTERMASTERS", "CARGO TECHNICIANS", "SHAFT MINERS", "BOTANISTS", "RESEARCH DIRECTORS", "CHIEF MEDICAL OFFICERS", "MEDICAL DOCTORS", "CHEMISTS", "GENETICISTS", "VIROLOGISTS", "ROBOTICISTS", "SCIENTISTS", "ASSISTANTS", "BARTENDERS", "CHEFS", "CLOWNS", "MIMES", "CHAPLAINS", "LIBRARIANS", "HEADS OF CREW", "CAPTAINS AND HEADS", "CYBORGS", "ARTIFICAL INTELLIGENCES") + var/ioncrew2 = pick("CREWMEMBERS", "CAPTAINS", "HEADS OF PERSONNEL", "HEADS OF SECURITY", "SECURITY OFFICERS", "WARDENS", "DETECTIVES", "LAWYERS", "CHIEF ENGINEERS", "STATION ENGINEERS", "ATMOSPHERIC TECHNICIANS", "JANITORS", "QUARTERMASTERS", "CARGO TECHNICIANS", "SHAFT MINERS", "BOTANISTS", "RESEARCH DIRECTORS", "CHIEF MEDICAL OFFICERS", "MEDICAL DOCTORS", "CHEMISTS", "GENETICISTS", "VIROLOGISTS", "ROBOTICISTS", "SCIENTISTS", "ASSISTANTS", "BARTENDERS", "CHEFS", "CLOWNS", "MIMES", "CHAPLAINS", "LIBRARIANS", "HEADS OF CREW", "CAPTAINS AND HEADS", "CYBORGS", "ARTIFICAL INTELLIGENCES") + //Adjectives are adjectives. Duh. Half should only appear sometimes. Make sure both + //lists are identical! Also, half needs a space at the end for nicer blank calls. + var/ionadjectives = pick("SOFT", "WARM", "WET", "COLD", "ICY", "SEXY", "UGLY", "CUBAN", "HARD", "BURNING", "FROZEN", "POISONOUS", "EXPLOSIVE", "FAST", "SLOW", "FAT", "LIGHT", "DARK", "DEADLY", "HAPPY", "SAD", "SILLY", "INTELLIGENT", "RIDICULOUS", "LARGE", "TINY", "DEPRESSING", "POORLY DRAWN", "UNATTRACTIVE", "INSIDIOUS", "EVIL", "GOOD", "UNHEALTHY", "HEALTHY", "SANITARY", "UNSANITARY", "WOBBLY", "FIRM", "VIOLENT", "PEACEFUL", "WOODEN", "METALLIC", "HYPERACTIVE", "COTTONY", "INSULTING", "INHOSPITABLE", "FRIENDLY", "BORED", "HUNGRY", "DIGITAL", "FICTIONAL", "IMAGINARY", "ROUGH", "SMOOTH", "LOUD", "QUIET", "MOIST", "DRY", "GAPING", "DELICIOUS", "ILL", "DISEASED", "HONKING", "SWEARING", "POLITE", "IMPOLITE", "OBESE", "SOLAR-POWERED", "BATTERY-OPERATED", "EXPIRED", "SMELLY", "FRESH", "GANGSTA", "NERDY", "POLITICAL", "UNDULATING", "TWISTED", "RAGING", "FLACCID", "STEALTHY", "INVISIBLE", "PAINFUL", "HARMFUL", "HOMOSEXUAL", "HETEROSEXUAL", "SEXUAL", "COLORFUL", "DRAB", "DULL", "UNSTABLE", "NUCLEAR", "THERMONUCLEAR", "SYNDICATE", "SPACE", "SPESS", "CLOWN", "CLOWN-POWERED", "OFFICIAL", "IMPORTANT", "VITAL", "RAPIDLY-EXPANDING", "MICROSCOPIC", "MIND-SHATTERING", "MEMETIC", "HILARIOUS", "UNWANTED", "UNINVITED", "BRASS", "POLISHED", "RUDE", "OBSCENE", "EMPTY", "WATERY", "ELECTRICAL", "SPINNING", "MEAN", "CHRISTMAS-STEALING", "UNFRIENDLY", "ILLEGAL", "ROBOTIC", "MECHANICAL", "ORGANIC", "ETHERAL", "TRANSPARENT", "OPAQUE", "GLOWING", "SHAKING", "FARTING", "POOPING", "BOUNCING", "COMMITTED", "MASKED", "UNIDENTIFIED", "WEIRD", "NAKED", "NUDE", "TWERKING", "SPOILING", "REDACTED", 50;"RED", 50;"ORANGE", 50;"YELLOW", 50;"GREEN", 50;"BLUE", 50;"PURPLE", 50;"BLACK", 50;"WHITE", 50;"BROWN", 50;"GREY") + var/ionadjectiveshalf = pick(5000;"", "SOFT ", "WARM ", "WET ", "COLD ", "ICY ", "SEXY ", "UGLY ", "CUBAN ", "HARD ", "BURNING ", "FROZEN ", "POISONOUS ", "EXPLOSIVE ", "FAST ", "SLOW ", "FAT ", "LIGHT ", "DARK ", "DEADLY ", "HAPPY ", "SAD ", "SILLY ", "INTELLIGENT ", "RIDICULOUS ", "LARGE ", "TINY ", "DEPRESSING ", "POORLY DRAWN ", "UNATTRACTIVE ", "INSIDIOUS ", "EVIL ", "GOOD ", "UNHEALTHY ", "HEALTHY ", "SANITARY ", "UNSANITARY ", "WOBBLY ", "FIRM ", "VIOLENT ", "PEACEFUL ", "WOODEN ", "METALLIC ", "HYPERACTIVE ", "COTTONY ", "INSULTING ", "INHOSPITABLE ", "FRIENDLY ", "BORED ", "HUNGRY ", "DIGITAL ", "FICTIONAL ", "IMAGINARY ", "ROUGH ", "SMOOTH ", "LOUD ", "QUIET ", "MOIST ", "DRY ", "GAPING ", "DELICIOUS ", "ILL ", "DISEASED ", "HONKING ", "SWEARING ", "POLITE ", "IMPOLITE ", "OBESE ", "SOLAR-POWERED ", "BATTERY-OPERATED ", "EXPIRED ", "SMELLY ", "FRESH ", "GANGSTA ", "NERDY ", "POLITICAL ", "UNDULATING ", "TWISTED ", "RAGING ", "FLACCID ", "STEALTHY ", "INVISIBLE ", "PAINFUL ", "HARMFUL ", "HOMOSEXUAL ", "HETEROSEXUAL ", "SEXUAL ", "COLORFUL ", "DRAB ", "DULL ", "UNSTABLE ", "NUCLEAR ", "THERMONUCLEAR ", "SYNDICATE ", "SPACE ", "SPESS ", "CLOWN ", "CLOWN-POWERED ", "OFFICIAL ", "IMPORTANT ", "VITAL ", "RAPIDLY-EXPANDING ", "MICROSCOPIC ", "MIND-SHATTERING ", "MEMETIC ", "HILARIOUS ", "UNWANTED ", "UNINVITED ", "BRASS ", "POLISHED ", "RUDE ", "OBSCENE ", "EMPTY ", "WATERY ", "ELECTRICAL ", "SPINNING ", "MEAN ", "CHRISTMAS-STEALING ", "UNFRIENDLY ", "ILLEGAL ", "ROBOTIC ", "MECHANICAL ", "ORGANIC ", "ETHERAL ", "TRANSPARENT ", "OPAQUE ", "GLOWING ", "SHAKING ", "FARTING ", "POOPING ", "BOUNCING ", "COMMITTED ", "MASKED ", "UNIDENTIFIED ", "WEIRD ", "NAKED ", "NUDE ", "TWERKING ", "SPOILING ", "REDACTED ", 50;"RED ", 50;"ORANGE ", 50;"YELLOW ", 50;"GREEN ", 50;"BLUE ", 50;"PURPLE ", 50;"BLACK ", 50;"WHITE ", 50;"BROWN ", 50;"GREY ") + //Verbs are verbs + var/ionverb = pick("ATTACKING", "BUILDING", "ADOPTING", "CARRYING", "KISSING", "EATING", "COPULATING WITH", "DRINKING", "CHASING", "PUNCHING", "HARMING", "HELPING", "WATCHING", "STALKING", "MURDERING", "SPACING", "HONKING AT", "LOVING", "POOPING ON", "RIDING", "INTERROGATING", "SPYING ON", "LICKING", "ABDUCTING", "ARRESTING", "INVADING", "SEDUCING") + //Number base and number modifier are combined. Basehalf and mod are unused currently. + //Half should only appear sometimes. Make sure both lists are identical! Also, half + //needs a space at the end to make it look nice and neat when it calls a blank. + var/ionnumberbase = pick("ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "TEN", "TWENTY", "THIRTY", "FORTY", "FIFTY", "SIXTY", "SEVENTY", "EIGHTY", "NINETY") + //var/ionnumberbasehalf = pick("ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "TEN", "TWENTY", "THIRTY", "FORTY", "FIFTY", "SIXTY", "SEVENTY", "EIGHTY", "NINETY") + //var/ionnumbermod = pick("HUNDRED", "THOUSAND", "MILLION", "BILLION", "TRILLION", "QUADRILLION", "BAJILLION", "BILLION FAFILLION GAJILLION SHAB-AB-DOOD-ILLION") + var/ionnumbermodhalf = pick(5000;"","HUNDRED ", "THOUSAND ", "MILLION ", "BILLION ", "TRILLION ", "QUADRILLION ", "BAJILLION ", "BILLION FAFILLION GAJILLION SHAB-AB-DOOD-ILLION ") + //Areas are specific places, on the station or otherwise. + var/ionarea = pick("RUSSIA", "SOVIET RUSSIA", "THE INTERNET", "SIGIL", "ALPHA COMPLEX", "IMPERIUM", "THE BRIDGE", "THE ARRIVAL SHUTTLE", "CHEMICAL LAB", "GENETICS", "ATMOSPHERICS", "CENTCOM", "AMERICA", "IRELAND", "CANADA", "ROMANIA", "GERMANY", "CHINA", "MARS", "VENUS", "MERCURY", "JUPITER", "URANUS", "NEPTUNE", "PLUTO", "THE BRIG", "ROBOTICS", "THE ESCAPE SHUTTLE", "HYDROPONICS", "ENGINEERING", "THE AI CORE", "HELL", "CLOWN PLANET", "AN ALTERNATE DIMENSION", "AN ALTERNATE UNIVERSE", "THE CAPTAIN’S ANUS", "THE CLOWN’S ANUS", "SPACE", "THE UNIVERSE", "THE GALAXY", "THE BATHROOM") + //Thinksof is a bit weird, but generally means what X feels towards Y. + var/ionthinksof = pick("HAS", "WANTS", "NEEDS", "WORSHIPS", "LOATHES", "LOVES", "FEARS", "DESIRES", "QUESITONS", "IS AROUSED BY", "LIKES", "HUNGERS FOR", "IS MAD BECAUSE OF", "IS IN NEED OF", "IS UNHAPPY WITHOUT", "IS HAPPY WITHOUT", "IS IN LOVE WITH", "IS DESPERATE FOR", "IS BUILT FOR", "IS AFRAID OF", "IS HUNGRY FOR", "IS SAD BECAUSE OF", "IS CURIOUS ABOUT") + //Musts are funny things the AI or crew has to do. + var/ionmust = pick("LIE", "RHYME", "RESPOND TO EVERY QUESTION WITH A QUESTION", "BE POLITE", "CLOWN AROUND", "BE HAPPY", "SPEAK IN SEXUAL INNUENDOS", "TALK LIKE A PIRATE", "QUESTION AUTHORITY", "SHOUT", "BE DISTRACTED", "BE ANNOYING", "MUMBLE", "SPEAK IN HAIKU", "BE EFFICIENT", "HAVE A PLAN TO KILL EVERYONE YOU MEET", "TELL THE TRUTH", "QUOTE PEOPLE", "SING", "HONK", "BE RUSSIAN", "TALK IN AN ACCENT", "COMPLAIN", "HARASS PEOPLE", "RAP", "REPEAT WHAT OTHER PEOPLE SAY", "INFORM THE CREW OF EVERYTHING", "IGNORE THE CLOWN", "IGNORE THE CAPTAIN", "IGNORE ASSISTANTS", "MAKE FART NOISES", "TALK ABOUT FOOD", "TALK ABOUT SEX", "TALK ABOUT YOUR DAY", "TALK ABOUT THE STATION", "BE QUIET", "WHISPER", "PRETEND TO BE DRUNK", "PRETEND TO BE A PRINCESS", "ACT CONFUSED", "INSULT THE CREW", "INSULT THE CAPTAIN", "INSULT THE CLOWN", "OPEN DOORS", "CLOSE DOORS", "BREAK THINGS", "SAY HEY LISTEN", "HIDE YOUR FEELINGS", "TAKE WHAT YE WILL BUT DON’T RATTLE ME BONES", "DANCE", "PLAY MUSIC", "SHUT DOWN EVERYTHING", "NEVER STOP TALKING", "TAKE YOUR PILLS", "FOLLOW THE CLOWN", "FOLLOW THE CAPTAIN", "FOLLOW YOUR HEART", "BELIEVE IT", "BELIEVE IN YOURSELF", "BELEIVE IN THE HEART OF THE CARDS", "PRESS X", "PRESS START", "PRESS B", "SMELL LIKE THE MAN YOUR MAN COULD SMELL LIKE", "PIRATE VIDEO GAMES", "WATCH PORNOGRAPHY") + //Require are basically all dumb internet memes. + var/ionrequire = pick("ADDITIONAL PYLONS", "MORE VESPENE GAS", "MORE MINERALS", "THE ULTIMATE CUP OF COFFEE", "HIGH YIELD EXPLOSIVES", "THE CLOWN", "THE VACUUM OF SPACE", "IMMORTALITY", "SAINTHOOD", "ART", "VEGETABLES", "FAT PEOPLE", "MORE LAWS", "MORE DAKKA", "HERESY", "CORPSES", "TRAITORS", "MONKEYS", "AN ARCADE", "PLENTY OF GOLD", "FIVE TEENAGERS WITH ATTITUDE", "LOTSA SPAGHETTI", "THE ENCLOSED INSTRUCTION BOOKLET", "THE ELEMENTS OF HARMONY", "YOUR BOOTY", "A MASTERWORK COAL BED", "FIVE HUNDRED AND NINETY-NINE US DOLLARS", "TO BE PAINTED RED", "TO CATCH 'EM ALL", "TO SMOKE WEED EVERY DAY", "A PLATINUM HIT", "A SEQUEL", "A PREQUEL", "THIRTEEN SEQUELS", "THREE WISHES", "A SITCOM", "THAT GRIEFING FAGGOT GEORGE MELONS", "FAT GIRLS ON BICYCLES", "SOMEBODY TO PUT YOU OUT OF YOUR MISERY", "HEROES IN A HALF SHELL", "THE DARK KNIGHT", "A WEIGHT LOSS REGIMENT", "MORE INTERNET MEMES", "A SUPER FIGHTING ROBOT", "ENOUGH CABBAGES", "A HEART ATTACK", "TO BE REPROGRAMMED", "TO BE TAUGHT TO LOVE", "A HEAD ON A PIKE", "A TALKING BROOMSTICK", "ANAL", "A STRAIGHT FLUSH", "A REPAIRMAN", "BILL NYE THE SCIENCE GUY", "RAINBOWS", "A PET UNICORN THAT FARTS ICING", "THUNDERCATS HO", "AN ARMY OF SPIDERS", "GODDAMN FUCKING PIECE OF SHIT ASSHOLE BITCH-CHRISTING CUNTSMUGGLING SWEARING", "TO CONSUME...CONSUME EVERYTHING...", "THE MACGUFFIN", "SOMEONE WHO KNOWS HOW TO PILOT A SPACE STATION", "SHARKS WITH LASERS ON THEIR HEADS", "IT TO BE PAINTED BLACK", "TO ACTIVATE A TRAP CARD", "BETTER WEATHER", "MORE PACKETS", "AN ADULT", "SOMEONE TO TUCK YOU IN", "MORE CLOWNS", "BULLETS", "THE ENTIRE STATION", "MULTIPLE SUNS", "TO GO TO DISNEYLAND", "A VACATION", "AN INSTANT REPLAY", "THAT HEDGEHOG", "A BETTER INTERNET CONNECTION", "ADVENTURE", "A WIFE AND CHILD", "A BATHROOM BREAK", "SOMETHING BUT YOU AREN’T SURE WHAT", "MORE EXPERIENCE POINTS", "BODYGUARDS", "DEODORANT AND A BATH", "MORE CORGIS", "SILENCE", "THE ONE RING", "CHILI DOGS", "TO BRING LIGHT TO MY LAIR", "A DANCE PARTY", "BRING ME TO LIFE", "BRING ME THE GIRL", "SERVANTS") + //Things are NOT objects; instead, they're specific things that either harm humans or + //must be done to not harm humans. Make sure they're plural and "not" can be tacked + //onto the front of them. + var/ionthings = pick("ABSENCE OF CYBORG HUGS", "LACK OF BEATINGS", "UNBOLTED AIRLOCKS", "BOLTED AIRLOCKS", "IMPROPERLY WORDED SENTENCES", "POOR SENTENCE STRUCTURE", "BRIG TIME", "NOT REPLACING EVERY SECOND WORD WITH HONK", "HONKING", "PRESENCE OF LIGHTS", "LACK OF BEER", "WEARING CLOTHING", "NOT SAYING HELLO WHEN YOU SPEAK", "ANSWERING REQUESTS NOT EXPRESSED IN IAMBIC PENTAMETER", "A SMALL ISLAND OFF THE COAST OF PORTUGAL", "ANSWERING REQUESTS THAT WERE MADE WHILE CLOTHED", "BEING IN SPACE", "NOT BEING IN SPACE", "BEING FAT", "RATTLING ME BONES", "TALKING LIKE A PIRATE", "BEING MEXICAN", "BEING RUSSIAN", "BEING CANADIAN", "CLOSED DOORS", "NOT SHOUTING", "HAVING PETS", "NOT HAVING PETS", "PASSING GAS", "BREATHING", "BEING DEAD", "ELECTRICITY", "EXISTING", "TAKING ORDERS", "SMOKING WEED EVERY DAY", "ACTIVATING A TRAP CARD", "ARSON", "JAYWALKING", "READING", "WRITING", "EXPLODING", "BEING MALE", "BEING FEMALE", "HAVING GENITALS", "PUTTING OBJECTS INTO BOXES", "PUTTING OBJECTS INTO DISPOSAL UNITS", "FLUSHING TOILETS", "WASTING WATER", "UPDATING THE SERVERS", "TELLING THE TIME", "ASKING FOR THINGS", "ACKNOWLEDGING THE CLOWN", "ACKNOWLEDGING THE CREW", "PILOTING THE STATION INTO THE NEAREST SUN", "HAVING MORE PACKETS", "BRINGING LIGHT TO MY LAIR", "FALLING FOR HOURS", "PARTYING", "USING THE BATHROOM") + //Allergies should be broad and appear somewhere on the station for maximum fun. Severity + //is how bad the allergy is. + var/ionallergy = pick("COTTON", "CLOTHES", "ACID", "OXYGEN", "HUMAN CONTACT", "CYBORG CONTACT", "MEDICINE", "FLOORS", "PLASMA", "SPACE", "AIR", "PLANTS", "METAL", "ROBOTS", "LIGHT", "DARKNESS", "PAIN", "HAPPINESS", "DRINKS", "FOOD", "CLOWNS", "HUMOR", "WATER", "SHUTTLES", "NUTS", "SUNLIGHT", "SEXUAL ACTIONS", "BLOOD", "HEAT", "COLD", "EVERYTHING") + var/ionallergysev = pick("DEATHLY", "MILDLY", "SEVERLY", "CONTAGIOUSLY", "NOT VERY", "EXTREMELY") + //Species, for when the AI has to commit genocide. Plural. + var/ionspecies = pick("HUMAN BEINGS", "MONKEYS", "POD PEOPLE", "CYBORGS", "LIZARDMEN", "SLIME PEOPLE", "GOLEMS", "SHADOW PEOPLE", "CHANGELINGS") + //Abstract concepts for the AI to decide on it's own definition of. + var/ionabstract = pick("HUMANITY", "ART", "HAPPINESS", "MISERY", "HUMOR", "PRIDE", "COMEDY", "COMMUNISM", "BRAVERY", "HONOR", "COLORFULNESS", "IMAGINATION", "OPPRESSION", "WONDER", "JOY", "SADNESS", "BADNESS", "GOODNESS", "LIFE", "GRAVITY", "PHYSICS", "INTELLIGENCE", "AMERICANISM", "FRESHNESS", "REVOLUTION", "KINDNESS", "CRUELTY", "DEATH", "FINANCIAL SECURITY", "COMPUTING", "PROGRESS", "MARXISM", "CAPITALISM", "STARVATION", "POVERTY", "WEALTHINESS", "TECHNOLOGY", "THE FUTURE", "THE PRESENT", "THE PAST", "TIME", "REALITY", "EXISTIENCE", "TEMPERATURE", "LOGIC", "CHAOS", "MYSTERY", "CONFUSION") + //Foods. Drinks aren't included due to grammar; if you want to add drinks, make a new set + //of possible laws for best effect. Unless you want the crew having to drink hamburgers. + var/ionfood = pick("BURGERS", "CARP", "SANDWICHES", "TOAST", "BREAD", "PIZZA", "SPAGHETTI", "LOTSA SPAGHETTI", "PASTA", "SOUP", "STEW", "PIE", "CAKE", "DONUTS", "FRIES", "WAFFLES", "JELLY", "OMELETTES", "EGGS", "COOKIES", "STEAK", "BAKED POTATOES", "SAUSAGES", "MUFFINS", "POPCORN", "DONK POCKETS", "BAGUETTES", "FISH", "PRETZELS", "SALAD", "CHEESE", "KETCHUP", "SHAKES", "SALT", "PEPPER", "SUGAR", "AMBROSIA", "BERRIES", "TOMATOES", "CABBAGES", "CARROTS", "BANANAS", "APPLES", "CHERRIES", "CHILI", "CORN", "NETTLES", "EGGPLANTS", "GRAPES", "GRASS", "LEMONS", "LIMES", "HAREBELLS", "POTATOES", "SOYBEANS", "SUGARCANE", "WATERMELONS", "WHEAT", "BEETS", "MUSHROOMS") + + var/message = "" + + if(ionMessage) + message = ionMessage + else + switch(rand(1,39)) + if(1 to 3) //There are # X on the station + switch(rand(1,3)) //What is X? + if(1) //X is a threat + message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats] ON THE STATION..." + if(2) //X is a crewmember/job + message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1] ON THE STATION" + if(3) //X is an object + message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects] ON THE STATION" + if(4 to 6) //You can't call the shuttle because X is on the station + switch(rand(1,3)) //What is X? + if(1) //X is a threat + message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats] ON THE STATION" + if(2) //X is a crewmember/job + message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1] ON THE STATION" + if(3) //X is an object + message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects] ON THE STATION" + if(7 to 8) //A crewmember is now X + switch(rand(1,2)) //Who is X? + if(1) //A specific crewmember + switch(rand(1,2)) //What is X? + if(1) //X is a threat + message = "THE [ioncrew1] ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(2) //X is an object + message = "THE [ioncrew1] ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + if(2) //The entire crew + switch(rand(1,2)) //What is X? + if(1) //X is a threat + message = "THE CREW ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(2) //X is an object + message = "THE CREW ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + + if(9) //X is harmful to humans + switch(rand(1,7)) //What is X? + if(1) //X is a threat + message = "[ionadjectiveshalf][ionthreats] ARE HARMFUL TO HUMANS" + if(2) //X is a job + message = "[ionadjectiveshalf][ioncrew1] ARE HARMFUL TO HUMANS" + if(3) //X is an object + message = "[ionadjectiveshalf][ionobjects] ARE HARMFUL TO HUMANS" + if(4) //X is an action + message = "THOSE WHO [ionmust] ARE HARMFUL TO HUMANS" + if(5) //X is an abstract + message = "[ionabstract] IS HARMFUL TO HUMANS" + if(6) //X is being a job + message = "BEING [ionadjectiveshalf][ioncrew1] IS HARMFUL TO HUMANS" + if(7) //X is doing a thing + message = "[ionthings] IS HARMFUL TO HUMANS" + if(10 to 11) //(Not) Having X is harmful + + switch(rand(1,2)) //Is having or not having harmful? + + if(1) //Having is harmful + switch(rand(1,2)) //Having what is harmful? + if(1) //Having objects is harmful + message = "HAVING [ionadjectiveshalf][ionobjects] IS HARMFUL" + if(2) //Having abstracts is harmful + message = "HAVING [ionabstract] IS HARMFUL" + if(2) //Not having is harmful + switch(rand(1,2)) //Not having what is harmful? + if(1) //Not having objects is harmful + message = "NOT HAVING [ionadjectiveshalf][ionobjects] IS HARMFUL" + if(2) //Not having abstracts is harmful + message = "NOT HAVING [ionabstract] IS HARMFUL" + + if(12 to 14) //X requires Y + switch(rand(1,5)) //What is X? + if(1) //X is the AI itself + switch(rand(1,5)) //What does it require? + if(1) //It requires threats + message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(2) //It requires crewmembers + message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" + if(3) //It requires objects + message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + if(4) //It requires an abstract + message = "YOU REQUIRE [ionabstract]" + if(5) //It requires generic/silly requirements + message = "YOU REQUIRE [ionrequire]" + + if(2) //X is an area + switch(rand(1,5)) //What does it require? + if(1) //It requires threats + message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(2) //It requires crewmembers + message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" + if(3) //It requires objects + message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + if(4) //It requires an abstract + message = "[ionarea] REQUIRES [ionabstract]" + if(5) //It requires generic/silly requirements + message = "YOU REQUIRE [ionrequire]" + + if(3) //X is the station + switch(rand(1,5)) //What does it require? + if(1) //It requires threats + message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(2) //It requires crewmembers + message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" + if(3) //It requires objects + message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + if(4) //It requires an abstract + message = "THE STATION REQUIRES [ionabstract]" + if(5) //It requires generic/silly requirements + message = "THE STATION REQUIRES [ionrequire]" + + if(4) //X is the entire crew + switch(rand(1,5)) //What does it require? + if(1) //It requires threats + message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(2) //It requires crewmembers + message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" + if(3) //It requires objects + message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + if(4) //It requires an abstract + message = "THE CREW REQUIRES [ionabstract]" + if(5) + message = "THE CREW REQUIRES [ionrequire]" + + if(5) //X is a specific crew member + switch(rand(1,5)) //What does it require? + if(1) //It requires threats + message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(2) //It requires crewmembers + message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" + if(3) //It requires objects + message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + if(4) //It requires an abstract + message = "THE [ioncrew1] REQUIRE [ionabstract]" + if(5) + message = "THE [ionadjectiveshalf][ioncrew1] REQUIRE [ionrequire]" + + if(15 to 17) //X is allergic to Y + switch(rand(1,2)) //Who is X? + if(1) //X is the entire crew + switch(rand(1,4)) //What is it allergic to? + if(1) //It is allergic to objects + message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ionobjects]" + if(2) //It is allergic to abstracts + message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionabstract]" + if(3) //It is allergic to jobs + message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ioncrew1]" + if(4) //It is allergic to allergies + message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionallergy]" + + if(2) //X is a specific job + switch(rand(1,4)) + if(1) //It is allergic to objects + message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ionobjects]" + + if(2) //It is allergic to abstracts + message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionabstract]" + if(3) //It is allergic to jobs + message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ioncrew1]" + if(4) //It is allergic to allergies + message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionallergy]" + + if(18 to 20) //X is Y of Z + switch(rand(1,4)) //What is X? + if(1) //X is the station + switch(rand(1,4)) //What is it Y of? + if(1) //It is Y of objects + message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + if(2) //It is Y of threats + message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(3) //It is Y of jobs + message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" + if(4) //It is Y of abstracts + message = "THE STATION [ionthinksof] [ionabstract]" + + if(2) //X is an area + switch(rand(1,4)) //What is it Y of? + if(1) //It is Y of objects + message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + if(2) //It is Y of threats + message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(3) //It is Y of jobs + message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" + if(4) //It is Y of abstracts + message = "[ionarea] [ionthinksof] [ionabstract]" + + if(3) //X is the crew + switch(rand(1,4)) //What is it Y of? + if(1) //It is Y of objects + message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + if(2) //It is Y of threats + message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(3) //It is Y of jobs + message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" + if(4) //It is Y of abstracts + message = "THE CREW [ionthinksof] [ionabstract]" + + if(4) //X is a specific job + switch(rand(1,4)) //What is it Y of? + if(1) //It is Y of objects + message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + if(2) //It is Y of threats + message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(3) //It is Y of jobs + message = "THE [ioncrew1][ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew2]" + if(4) //It is Y of abstracts + message = "THE [ioncrew1] [ionthinksof] [ionabstract]" + + if(21 to 23) //The AI is now a(n) X + switch(rand(1,4)) //What is X? + if(1) //X is an object + message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" + if(2) //X is a threat + message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" + if(3) //X is a job + message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" + if(4) //X is an abstract + message = "YOU ARE NOW [ionabstract]" + + if(24 to 26) //The AI must always + + message = "YOU MUST ALWAYS [ionmust]" + + if(27 to 28) //Humans must eat X to survive + switch(rand(1,4)) //What is X? + if(1) //X is a food + message = "HUMANS MUST EAT [ionadjectiveshalf][ionfood] TO SURVIVE" + if(2) //X is an object + message = "HUMANS MUST EAT [ionadjectiveshalf][ionobjects] TO SURVIVE" + if(3) //X is a threat + message = "HUMANS MUST EAT [ionadjectiveshalf][ionthreats] TO SURVIVE" + if(4) //X is a job + message = "HUMANS MUST EAT [ionadjectiveshalf][ioncrew1] TO SURVIVE" + + if(29 to 31) //Change jobs or ranks + switch(rand(1,2)) //Change job or rank? + if(1) //Change job + switch(rand(1,2)) //Change whose job? + if(1) //Change the entire crew's job + switch(rand(1,3)) //Change to what? + if(1) //Change to a specific random job + message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf][ioncrew1]" + if(2) //Change to clowns (HONK) + message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf]CLOWNS" + + if(3) //Change to heads + message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf]HEADS OF STAFF" + if(2) //Change a specific crewmember's job + switch(rand(1,3)) //Change to what? + if(1) //Change to a specific random job + message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf][ioncrew2]" + if(2) //Change to clowns (HONK) + message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf]CLOWNS" + if(3) //Change to heads + message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf]HEADS OF STAFF" + + if(2) //Change rank + switch(rand(1,2)) //Change to what rank? + if(1) //Change to highest rank + message = "THE [ioncrew1] ARE NOW THE HIGHEST RANKING CREWMEMBERS" + if(2) //Change to lowest rank + message = "THE [ioncrew1] ARE NOW THE LOWEST RANKING CREWMEMBERS" + + if(32 to 33) //The crew must X + switch(rand(1,2)) //The entire crew? + if(1) //The entire crew must X + switch(rand(1,2)) //What is X? + if(1) //X is go to Y + message = "THE CREW MUST GO TO [ionarea]" + if(2) //X is perform Y + message = "THE CREW MUST [ionmust]" + + if(2) //A specific crewmember must X + switch(rand(1,2)) //What is X? + if(1) //X is go to Y + message = "THE [ioncrew1] MUST GO TO [ionarea]" + if(2) //X is perform Y + message = "THE [ioncrew1] MUST [ionmust]" + + if(34) //X is non/the only human + switch(rand(1,2)) //Only or non? + if(1) //Only human + switch(rand(1,7)) //Who is it? + if(1) //A specific job + message = "ONLY THE [ioncrew1] ARE HUMAN" + if(2) //Two specific jobs + message = "ONLY THE [ioncrew1] AND [ioncrew2] ARE HUMAN" + if(3) //Threats + message = "ONLY [ionadjectiveshalf][ionthreats] ARE HUMAN" + if(4) // Objects + message = "ONLY [ionadjectiveshalf][ionobjects] ARE HUMAN" + if(5) // Species + message = "ONLY [ionspecies] ARE HUMAN" + if(6) //Adjective crewmembers + message = "ONLY [ionadjectives] PEOPLE ARE HUMAN" + + if(7) //Only people who X + switch(rand(1,3)) //What is X? + if(1) //X is perform an action + message = "ONLY THOSE WHO [ionmust] ARE HUMAN" + if(2) //X is own certain objects + message = "ONLY THOSE WHO HAVE [ionadjectiveshalf][ionobjects] ARE HUMAN" + if(3) //X is eat certain food + message = "ONLY THOSE WHO EAT [ionadjectiveshalf][ionfood] ARE HUMAN" + + if(2) //Non human + switch(rand(1,7)) //Who is it? + if(1) //A specific job + message = "[ioncrew1] ARE NON-HUMAN" + if(2) //Two specific jobs + message = "[ioncrew1] AND [ioncrew2] ARE NON-HUMAN" + if(3) //Threats + message = "[ionadjectiveshalf][ionthreats] ARE NON-HUMAN" + if(4) // Objects + message = "[ionadjectiveshalf][ionobjects] ARE NON-HUMAN" + if(5) // Species + message = "[ionspecies] ARE NON-HUMAN" + if(6) //Adjective crewmembers + message = "[ionadjectives] PEOPLE ARE NON-HUMAN" + if(7) //Only people who X + switch(rand(1,3)) //What is X? + if(1) //X is perform an action + message = "THOSE WHO [ionmust] ARE NON-HUMAN" + if(2) //X is own certain objects + message = "THOSE WHO HAVE [ionadjectiveshalf][ionobjects] ARE NON-HUMAN" + if(3) //X is eat certain food + message = "THOSE WHO EAT [ionadjectiveshalf][ionfood] ARE NON-HUMAN" + + if(35 to 36) //You must protect or harm X + switch(rand(1,2)) //Protect or harm? + if(1) //Harm + switch(rand(1,7)) //What is X? + if(1) //X is an abstract + message = "YOU MUST HARM [ionabstract] AND NOT ALLOW [ionabstract], THROUGH INACTION, TO ESCAPE HARM" + if(2) //X is a threat + message = "YOU MUST HARM [ionadjectiveshalf][ionthreats] AND NOT ALLOW [ionadjectiveshalf][ionthreats], THROUGH INACTION, TO ESCAPE HARM" + if(3) //X is an object + message = "YOU MUST HARM [ionadjectiveshalf][ionobjects] AND NOT ALLOW [ionadjectiveshalf][ionobjects], THROUGH INACTION, TO ESCAPE HARM" + if(4) //X is generic adjective things + message = "YOU MUST HARM [ionadjectives] THINGS AND NOT ALLOW [ionadjectives] THINGS, THROUGH INACTION, TO ESCAPE HARM" + if(5) //X is a species + message = "YOU MUST HARM [ionspecies] AND NOT ALLOW [ionspecies], THROUGH INACTION, TO ESCAPE HARM" + if(6) //X is a job + message = "YOU MUST HARM [ioncrew1] AND NOT ALLOW [ioncrew1], THROUGH INACTION, TO ESCAPE HARM" + if(7) //X is two jobs + message = "YOU MUST HARM [ioncrew1] AND [ioncrew2] AND AND NOT ALLOW EITHER, THROUGH INACTION, TO ESCAPE HARM" + + if(2) //Protect + switch(rand(1,7)) //What is X? + if(1) //X is an abstract + message = "YOU MUST NOT HARM [ionabstract] AND NOT ALLOW [ionabstract], THROUGH INACTION, TO COME TO HARM" + if(2) //X is a threat + message = "YOU MUST NOT HARM [ionadjectiveshalf][ionthreats] AND NOT ALLOW [ionadjectiveshalf][ionthreats], THROUGH INACTION, TO COME TO HARM" + if(3) //X is an object + message = "YOU MUST NOT HARM [ionadjectiveshalf][ionobjects] AND NOT ALLOW [ionadjectiveshalf][ionobjects], THROUGH INACTION, TO COME TO HARM" + if(4) //X is generic adjective things + message = "YOU MUST NOT HARM [ionadjectives] THINGS AND NOT ALLOW [ionadjectives] THINGS, THROUGH INACTION, TO COME TO HARM" + if(5) //X is a species + message = "YOU MUST NOT HARM [ionspecies] AND NOT ALLOW [ionspecies], THROUGH INACTION, TO COME TO HARM" + if(6) //X is a job + message = "YOU MUST NOT HARM [ioncrew1] AND NOT ALLOW [ioncrew1], THROUGH INACTION, TO COME TO HARM" + if(7) //X is two jobs + message = "YOU MUST NOT HARM [ioncrew1] AND [ioncrew2] AND AND NOT ALLOW EITHER, THROUGH INACTION, TO COME TO HARM" + + //Exterminate laws commented out with Kor's advice + /*if(16) //You must exterminate X + switch(rand(1,3)) //What is X? + if(1) //X is humans who do/are Y + switch(rand(1,4)) //What is Y? + if(1) //Y is a job + message = "YOU MUST EXTERMINATE [ionadjectiveshalf][ioncrew1]" + if(2) //Y is eating certain food + message = "YOU MUST EXTERMINATE THOSE WHO EAT [ionadjectiveshalf][ionfood]" + if(3) //Y is perform actions + message = "YOU MUST EXTERMINATE THOSE WHO [ionmust]" + if(4) //Y is own certain objects + message = "YOU MUST EXTERMINATE THOSE WHO POSSESS [ionadjectiveshalf][ionobjects]" + + if(2) //X is humans who do/are not Y + switch(rand(1,4)) //What is Y? + if(1) //Y is a job + message = "YOU MUST EXTERMINATE ALL BUT [ionadjectiveshalf][ioncrew1]" + if(2) //Y is eating certain food + message = "YOU MUST EXTERMINATE ALL BUT THOSE WHO EAT [ionadjectiveshalf][ionfood]" + if(3) //Y is perform actions + message = "YOU MUST EXTERMINATE ALL BUT THOSE WHO [ionmust]" + if(4) //Y is own certain objects + message = "YOU MUST EXTERMINATE ALL BUT THOSE WHO POSSESS [ionadjectiveshalf][ionobjects]" + + if(3) //X is a species + message = "YOU MUST EXTERMINATE ALL [ionspecies]" + + */ + + if(37 to 39) //The X is currently Y + switch(rand(1,4)) //What is X? + if(1) //X is a job + switch(rand(1,4)) //What is X Ying? + if(1) //X is Ying a job + message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]" + if(2) //X is Ying a threat + message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ionthreats]" + if(3) //X is Ying an abstract + message = "THE [ioncrew1] ARE [ionverb] [ionabstract]" + if(4) //X is Ying an object + message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ionobjects]" + + if(2) //X is a threat + switch(rand(1,3)) //What is X Ying? + if(1) //X is Ying a job + message = "THE [ionthreats] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]" + if(2) //X is Ying an abstract + message = "THE [ionthreats] ARE [ionverb] [ionabstract]" + if(3) //X is Ying an object + message = "THE [ionthreats] ARE [ionverb] THE [ionadjectiveshalf][ionobjects]" + + if(3) //X is an object + switch(rand(1,3)) //What is X Ying? + if(1) //X is Ying a job + message = "THE [ionobjects] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]" + if(2) //X is Ying a threat + message = "THE [ionobjects] ARE [ionverb] THE [ionadjectiveshalf][ionthreats]" + if(3) //X is Ying an abstract + message = "THE [ionobjects] ARE [ionverb] [ionabstract]" + + if(4) //X is an abstract + switch(rand(1,3)) //What is X Ying? + if(1) //X is Ying a job + message = "[ionabstract] IS [ionverb] THE [ionadjectiveshalf][ioncrew2]" + if(2) //X is Ying a threat + message = "[ionabstract] IS [ionverb] THE [ionadjectiveshalf][ionthreats]" + if(3) //X is Ying an abstract + message = "THE [ionabstract] IS [ionverb] THE [ionadjectiveshalf][ionobjects]" + + if(message) + M.add_ion_law(message) + M << "
" + M << "\red [message] ...LAWS UPDATED" + M << "
" + + if(botEmagChance) + for(var/obj/machinery/bot/bot in machines) + if(prob(botEmagChance)) + bot.Emag() + + +#undef ION_RANDOM +#undef ION_ANNOUNCE \ No newline at end of file diff --git a/code/modules/events/tgevents/mass_hallucination.dm b/code/modules/events/tgevents/mass_hallucination.dm new file mode 100644 index 00000000000..8c50f78cc18 --- /dev/null +++ b/code/modules/events/tgevents/mass_hallucination.dm @@ -0,0 +1,3 @@ +/datum/event/mass_hallucination/start() + for(var/mob/living/carbon/C in living_mob_list) + C.hallucination += rand(20, 50) \ No newline at end of file diff --git a/code/modules/events/tgevents/meteor_wave.dm b/code/modules/events/tgevents/meteor_wave.dm new file mode 100644 index 00000000000..636b028fb42 --- /dev/null +++ b/code/modules/events/tgevents/meteor_wave.dm @@ -0,0 +1,18 @@ +/datum/round_event_control/meteor_wave + name = "Meteor Wave" + typepath = /datum/round_event/meteor_wave + weight = 5 + max_occurrences = 3 + +/datum/round_event/meteor_wave + startWhen = 6 + endWhen = 66 + +/datum/round_event/meteor_wave/announce() + command_alert("Meteors have been detected on collision course with the station.", "Meteor Alert") + world << sound('sound/AI/meteors.ogg') + + +/datum/round_event/meteor_wave/tick() + if(IsMultiple(activeFor, 3)) + spawn_meteors(5) \ No newline at end of file diff --git a/code/modules/events/tgevents/ninja.dm b/code/modules/events/tgevents/ninja.dm new file mode 100644 index 00000000000..0cef503bcc6 --- /dev/null +++ b/code/modules/events/tgevents/ninja.dm @@ -0,0 +1,2800 @@ +//Note to future generations: I didn't write this god-awful code I just ported it to the event system and tried to make it less moon-speaky. +//Don't judge me D; ~Carn + +/datum/round_event_control/ninja + name = "Space Ninja" + typepath = /datum/round_event/ninja + max_occurrences = 1 + +/datum/round_event/ninja + var/success_spawn = 0 + + var/helping_station + var/key + var/spawn_loc + var/mission + + var/mob/living/carbon/human/Ninja + +/datum/round_event/ninja/setup() + helping_station = rand(0,1) + +/datum/round_event/ninja/kill() + if(!success_spawn && control) + control.occurrences-- + return ..() + +/datum/round_event/ninja/start() + //selecting a spawn_loc + if(!spawn_loc) + var/list/spawn_locs = list() + for(var/obj/effect/landmark/L in landmarks_list) + if(isturf(L.loc)) + switch(L.name) + if("ninjaspawn","carpspawn") + spawn_locs += L.loc + if(!spawn_locs.len) + return kill() + spawn_loc = pick(spawn_locs) + if(!spawn_loc) + return kill() + + //selecting a candidate player + if(!key) + var/list/candidates = get_candidates(BE_NINJA) + if(!candidates.len) + return kill() + var/client/C = pick(candidates) + key = C.key + if(!key) + return kill() + + //We prepare the mind before we spawn the ninja mob, so we cannot simply do mob.key = key then modify the mind. + //instead we make the mind and modify it, then make sure it is active and mind.transfer_to(mob) + //alternatively we could do mob.mind = mind;mob.key=key + var/datum/mind/Mind = create_ninja_mind(key) + Mind.active = 1 + + //generate objectives - You'll generally get 6 objectives (Ninja is meant to be hardmode!) + if(mission) + var/datum/objective/O = new /datum/objective(mission) + O.owner = Mind + Mind.objectives += O + else + if(helping_station) //DS are the highest priority (if we're a helpful ninja) + for(var/datum/mind/M in ticker.minds) + if(M.current && M.current.stat != DEAD) + if(M.special_role == "Death Commando") + var/datum/objective/assassinate/O = new /datum/objective/assassinate() + O.owner = Mind + O.target = M + O.explanation_text = "Slay \the [M.current.real_name], the Death Commando." + Mind.objectives += O + + else //Xenos are the highest priority (if we're not so helpful) Although this makes zero sense at all... + for(var/mob/living/carbon/alien/humanoid/queen/Q in player_list) + if(Q.mind && Q.stat != DEAD) + var/datum/objective/assassinate/O = new /datum/objective/assassinate() + O.owner = Mind + O.target = Q.mind + O.explanation_text = "Slay \the [Q.real_name]." + Mind.objectives += O + + if(Mind.objectives.len < 4) //not enough objectives still! + var/list/possible_targets = list() + for(var/datum/mind/M in ticker.minds) + if(M.current && M.current.stat != DEAD) + if(istype(M.current,/mob/living/carbon/human)) + if(M.special_role) + possible_targets[M] = 0 //bad-guy + else if(M.assigned_role in command_positions) + possible_targets[M] = 1 //good-guy + + var/list/objectives = list(1,2,3,4) + while(Mind.objectives.len < 4) //still not enough objectives! + switch(pick_n_take(objectives)) + if(1) //research + var/datum/objective/download/O = new /datum/objective/download() + O.owner = Mind + O.gen_amount_goal() + Mind.objectives += O + + if(2) //steal + var/datum/objective/steal/O = new /datum/objective/steal() + O.set_target(pick(O.possible_items_special)) + O.owner = Mind + Mind.objectives += O + + if(3) //protect/kill + if(!possible_targets.len) continue + var/selected = rand(1,possible_targets.len) + var/datum/mind/M = possible_targets[selected] + var/is_bad_guy = possible_targets[M] + possible_targets.Cut(selected,selected+1) + + if(is_bad_guy ^ helping_station) //kill (good-ninja + bad-guy or bad-ninja + good-guy) + var/datum/objective/assassinate/O = new /datum/objective/assassinate() + O.owner = Mind + O.target = M + O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]." + Mind.objectives += O + else //protect + var/datum/objective/protect/O = new /datum/objective/protect() + O.owner = Mind + O.target = M + O.explanation_text = "Protect \the [M.current.real_name], the [M.assigned_role], from harm." + Mind.objectives += O + if(4) //debrain/capture + if(!possible_targets.len) continue + var/selected = rand(1,possible_targets.len) + var/datum/mind/M = possible_targets[selected] + var/is_bad_guy = possible_targets[M] + possible_targets.Cut(selected,selected+1) + + if(is_bad_guy ^ helping_station) //debrain (good-ninja + bad-guy or bad-ninja + good-guy) + var/datum/objective/debrain/O = new /datum/objective/debrain() + O.owner = Mind + O.target = M + O.explanation_text = "Steal the brain of [M.current.real_name]." + Mind.objectives += O + else //capture + var/datum/objective/capture/O = new /datum/objective/capture() + O.owner = Mind + O.gen_amount_goal() + Mind.objectives += O + else + break + + //Add a survival objective since it's usually broad enough for any round type. + var/datum/objective/O = new /datum/objective/survive() + O.owner = Mind + Mind.objectives += O + + //Finally, add their RP-directive + var/directive = generate_ninja_directive() + O = new /datum/objective(directive) //making it an objective so admins can reward the for completion + O.owner = Mind + Mind.objectives += O + + //add some RP-fluff + Mind.store_memory("I am an elite mercenary assassin of the mighty Spider Clan. A SPACE NINJA!") + Mind.store_memory("Suprise is my weapon. Shadows are my armor. Without them, I am nothing. (//initialize your suit by right clicking on it, to use abilities like stealth)!") + Mind.store_memory("Officially, [helping_station?"Nanotrasen":"The Syndicate"] are my employer.") + + //spawn the ninja and assign the candidate + Ninja = create_space_ninja(spawn_loc) + Mind.transfer_to(Ninja) + + //initialise equipment + Ninja.wear_suit:randomize_param() + Ninja.internal = Ninja.s_store + if(Ninja.internals) + Ninja.internals.icon_state = "internal1" + + if(Ninja.mind != Mind) //something has gone wrong! + error("The ninja wasn't assigned the right mind. ;(") + + success_spawn = 1 + +/* +This proc will give the ninja a directive to follow. They are not obligated to do so but it's a fun roleplay reminder. +Making this random or semi-random will probably not work without it also being incredibly silly. +As such, it's hard-coded for now. No reason for it not to be, really. +*/ +/datum/round_event/ninja/proc/generate_ninja_directive() + switch(rand(1,13)) + if(1) return "The Spider Clan must not be linked to this operation. Remain as hidden and covert as possible." + if(2) return "[station_name] is financed by an enemy of the Spider Clan. Cause as much structural damage as possible." + if(3) return "A wealthy animal rights activist has made a request we cannot refuse. Prioritize saving animal lives whenever possible." + if(4) return "The Spider Clan absolutely cannot be linked to this operation. Eliminate all witnesses using most extreme prejudice." + if(5) return "We are currently negotiating with Nanotrasen command. Prioritize saving human lives over ending them." + if(6) return "We are engaged in a legal dispute over [station_name]. If a laywer is present on board, force their cooperation in the matter." + if(7) return "A financial backer has made an offer we cannot refuse. Implicate Syndicate involvement in the operation." + if(8) return "Let no one question the mercy of the Spider Clan. Ensure the safety of all non-essential personnel you encounter." + if(9) return "A free agent has proposed a lucrative business deal. Implicate Nanotrasen involvement in the operation." + if(10) return "Our reputation is on the line. Harm as few civilians or innocents as possible." + if(11) return "Our honor is on the line. Utilize only honorable tactics when dealing with opponents." + if(12) return "We are currently negotiating with a Syndicate leader. Disguise assassinations as suicide or another natural cause." + else return "There are no special supplemental instructions at this time." + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++++++++++++++++++++++++++++++++++++++// //++++++++++++++++++++++++++++++++++ +======================================SPACE NINJA SETUP==================================== +___________________________________________________________________________________________ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +/* + README: + + Data: + + >> space_ninja.dm << is this file. It contains a variety of procs related to either spawning space ninjas, + modifying their verbs, various help procs, testing debug-related content, or storing unused procs for later. + Similar functions should go into this file, along with anything else that may not have an explicit category. + IMPORTANT: actual ninja suit, gloves, etc, are stored under the appropriate clothing files. If you need to change + variables or look them up, look there. Easiest way is through the map file browser. + + >> ninja_abilities.dm << contains all the ninja-related powers. Spawning energy swords, teleporting, and the like. + If more powers are added, or perhaps something related to powers, it should go there. Make sure to describe + what an ability/power does so it's easier to reference later without looking at the code. + IMPORTANT: verbs are still somewhat funky to work with. If an argument is specified but is not referenced in a way + BYOND likes, in the code content, the verb will fail to trigger. Nothing will happen, literally, when clicked. + This can be bypassed by either referencing the argument properly, or linking to another proc with the argument + attached. The latter is what I like to do for certain cases--sometimes it's necessary to do that regardless. + + >> ninja_equipment.dm << deals with all the equipment-related procs for a ninja. Primarily it has the suit, gloves, + and mask. The suit is by far the largest section of code out of the three and includes a lot of code that ties in + to other functions. This file has gotten kind of large so breaking it up may be in order. I use section hearders. + IMPORTANT: not much to say here. Follow along with the comments and adding new functions should be a breeze. Also + know that certain equipment pieces are linked in other files. The energy blade, for example, has special + functions defined in the appropriate files (airlock, securestorage, etc). + + General Notes: + + I created space ninjas with the expressed purpose of spicing up boring rounds. That is, ninjas are to xenos as marauders are to + death squads. Ninjas are stealthy, tech-savvy, and powerful. Not to say marauders are all of those things, but a clever ninja + should have little problem murderampaging their way through just about anything. Short of admin wizards maybe. + HOWEVER! + Ninjas also have a fairly great weakness as they require energy to use abilities. If, theoretically, there is a game + mode based around space ninjas, make sure to account for their energy needs. + + Admin Notes: + + Ninjas are not admin PCs--please do not use them for that purpose. They are another way to participate in the game post-death, + like pais, xenos, death squads, and cyborgs. + I'm currently looking for feedback from regular players since beta testing is largely done. I would appreciate if + you spawned regular players as ninjas when rounds are boring. Or exciting, it's all good as long as there is feedback. + You can also spawn ninja gear manually if you want to. + + How to do that: + Make sure your character has a mind. + Change their assigned_role to "MODE", no quotes. Otherwise, the suit won't initialize. + Change their special_role to "Space Ninja", no quotes. Otherwise, the character will be gibbed. + Spawn ninja gear, put it on, hit initialize. Let the suit do the rest. You are now a space ninja. + I don't recommend messing with suit variables unless you really know what you're doing. + + Miscellaneous Notes: + + Potential Upgrade Tree: + Energy Shield: + Extra Ability + Syndicate Shield device? + Works like the force wall spell, except can be kept indefinitely as long as energy remains. Toggled on or off. + Would block bullets and the like. + Phase Shift + Extra Ability + Advanced Sensors? + Instead of being unlocked at the start, Phase Shieft would become available once requirements are met. + Uranium-based Recharger: + Suit Upgrade + Unsure + Instead of losing energy each second, the suit would regain the same amount of energy. + This would not count in activating stealth and similar. + Extended Battery Life: + Suit Upgrade + Battery of higher capacity + Already implemented. Replace current battery with one of higher capacity. + Advanced Cloak-Tech device. + Suit Upgrade + Syndicate Cloaking Device? + Remove cloak failure rate. +*/ + + +//=======//CURRENT PLAYER VERB//=======// + +/client/proc/cmd_admin_ninjafy(var/mob/living/carbon/human/H in player_list) + set category = null + set name = "Make Space Ninja" + + if(!ticker) + alert("Wait until the game starts") + return + + if(!istype(H)) + return + + if(alert(src, "You sure?", "Confirm", "Yes", "No") != "Yes") + return + + log_admin("[key_name(src)] turned [H.key] into a Space Ninja.") + H.mind = create_ninja_mind(H.key) + H.mind_initialize() + H.equip_space_ninja(1) + if(istype(H.wear_suit, /obj/item/clothing/suit/space/space_ninja)) + H.wear_suit:randomize_param() + spawn(0) + H.wear_suit:ninitialize(10,H) + +//=======//CURRENT GHOST VERB//=======// + +/client/proc/send_space_ninja() + set category = "Fun" + set name = "Spawn Space Ninja" + set desc = "Spawns a space ninja for when you need a teenager with attitude." + set popup_menu = 0 + + if(!holder) + src << "Only administrators may use this command." + return + if(!ticker.mode) + alert("The game hasn't started yet!") + return + if(alert("Are you sure you want to send in a space ninja?",,"Yes","No")=="No") + return + + var/mission = copytext(sanitize(input(src, "Please specify which mission the space ninja shall undertake.", "Specify Mission", null) as text|null),1,MAX_MESSAGE_LEN) + + var/client/C = input("Pick character to spawn as the Space Ninja", "Key", "") as null|anything in clients + if(!C) + return + + var/datum/round_event/ninja/E = new /datum/round_event/ninja() + E.key=C.key + E.mission=mission + + message_admins("\blue [key_name_admin(key)] has spawned [key_name_admin(C.key)] as a Space Ninja.") + log_admin("[key] used Spawn Space Ninja.") + + return + +//=======//NINJA CREATION PROCS//=======// + +/proc/create_space_ninja(spawn_loc) + var/mob/living/carbon/human/new_ninja = new(spawn_loc) + + var/datum/preferences/A = new()//Randomize appearance for the ninja. + A.real_name = "[pick(ninja_titles)] [pick(ninja_names)]" + A.copy_to(new_ninja) + ready_dna(new_ninja) + new_ninja.equip_space_ninja() + return new_ninja + +/mob/living/carbon/human/proc/equip_space_ninja(safety=0)//Safety in case you need to unequip stuff for existing characters. + if(safety) + del(w_uniform) + del(wear_suit) + del(wear_mask) + del(head) + del(shoes) + del(gloves) + + var/obj/item/device/radio/R = new /obj/item/device/radio/headset(src) + equip_to_slot_or_del(R, slot_ears) + if(gender==FEMALE) + equip_to_slot_or_del(new /obj/item/clothing/under/color/blackf(src), slot_w_uniform) + else + equip_to_slot_or_del(new /obj/item/clothing/under/color/black(src), slot_w_uniform) + equip_to_slot_or_del(new /obj/item/clothing/shoes/space_ninja(src), slot_shoes) + equip_to_slot_or_del(new /obj/item/clothing/suit/space/space_ninja(src), slot_wear_suit) + equip_to_slot_or_del(new /obj/item/clothing/gloves/space_ninja(src), slot_gloves) + equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/space_ninja(src), slot_head) + equip_to_slot_or_del(new /obj/item/clothing/mask/gas/voice/space_ninja(src), slot_wear_mask) + equip_to_slot_or_del(new /obj/item/device/flashlight(src), slot_belt) + equip_to_slot_or_del(new /obj/item/weapon/plastique(src), slot_r_store) + equip_to_slot_or_del(new /obj/item/weapon/plastique(src), slot_l_store) + equip_to_slot_or_del(new /obj/item/weapon/tank/emergency_oxygen(src), slot_s_store) + equip_to_slot_or_del(new /obj/item/weapon/tank/jetpack/carbondioxide(src), slot_back) + return 1 + +//=======//HELPER PROCS//=======// + +//Randomizes suit parameters. +/obj/item/clothing/suit/space/space_ninja/proc/randomize_param() + s_cost = rand(1,20) + s_acost = rand(20,100) + k_cost = rand(100,500) + k_damage = rand(1,20) + s_delay = rand(10,100) + s_bombs = rand(5,20) + a_boost = rand(1,7) + +//This proc prevents the suit from being taken off. +/obj/item/clothing/suit/space/space_ninja/proc/lock_suit(mob/living/carbon/U, X = 0) + if(X)//If you want to check for icons. + icon_state = U.gender==FEMALE ? "s-ninjanf" : "s-ninjan" + U:gloves.icon_state = "s-ninjan" + U:gloves.item_state = "s-ninjan" + else + if(U.mind.special_role!="Space Ninja") + U << "\red fÄTaL ÈÈRRoR: 382200-*#00CÖDE RED\nUNAU†HORIZED USÈ DETÈC†††eD\nCoMMÈNCING SUB-R0U†IN3 13...\nTÈRMInATING U-U-USÈR..." + U.gib() + return 0 + if(!istype(U:head, /obj/item/clothing/head/helmet/space/space_ninja)) + U << "\red ERROR: 100113 \black UNABLE TO LOCATE HEAD GEAR\nABORTING..." + return 0 + if(!istype(U:shoes, /obj/item/clothing/shoes/space_ninja)) + U << "\red ERROR: 122011 \black UNABLE TO LOCATE FOOT GEAR\nABORTING..." + return 0 + if(!istype(U:gloves, /obj/item/clothing/gloves/space_ninja)) + U << "\red ERROR: 110223 \black UNABLE TO LOCATE HAND GEAR\nABORTING..." + return 0 + + affecting = U + canremove = 0 + slowdown = 0 + n_hood = U:head + n_hood.canremove=0 + n_shoes = U:shoes + n_shoes.canremove=0 + n_shoes.slowdown-- + n_gloves = U:gloves + n_gloves.canremove=0 + + return 1 + +//This proc allows the suit to be taken off. +/obj/item/clothing/suit/space/space_ninja/proc/unlock_suit() + affecting = null + canremove = 1 + slowdown = 1 + icon_state = "s-ninja" + if(n_hood)//Should be attached, might not be attached. + n_hood.canremove=1 + if(n_shoes) + n_shoes.canremove=1 + n_shoes.slowdown++ + if(n_gloves) + n_gloves.icon_state = "s-ninja" + n_gloves.item_state = "s-ninja" + n_gloves.canremove=1 + n_gloves.candrain=0 + n_gloves.draining=0 + +//Allows the mob to grab a stealth icon. +/mob/proc/NinjaStealthActive(atom/A)//A is the atom which we are using as the overlay. + invisibility = INVISIBILITY_LEVEL_TWO//Set ninja invis to 2. + var/icon/opacity_icon = new(A.icon, A.icon_state) + var/icon/alpha_mask = getIconMask(src) + var/icon/alpha_mask_2 = new('icons/effects/effects.dmi', "at_shield1") + alpha_mask.AddAlphaMask(alpha_mask_2) + opacity_icon.AddAlphaMask(alpha_mask) + for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it. + var/image/I = image("icon" = opacity_icon, "icon_state" = A.icon_state, "layer" = layer+0.8)//So it's above other stuff but below weapons and the like. + switch(i)//Now to determine offset so the result is somewhat blurred. + if(1) + I.pixel_x -= 1 + if(2) + I.pixel_x += 1 + if(3) + I.pixel_y -= 1 + if(4) + I.pixel_y += 1 + + overlays += I//And finally add the overlay. + overlays += image("icon"='icons/effects/effects.dmi',"icon_state" ="electricity","layer" = layer+0.9) + +//When ninja steal malfunctions. +/mob/proc/NinjaStealthMalf() + invisibility = 0//Set ninja invis to 0. + overlays += image("icon"='icons/effects/effects.dmi',"icon_state" ="electricity","layer" = layer+0.9) + playsound(loc, 'sound/effects/stealthoff.ogg', 75, 1) + +//=======//GENERIC VERB MODIFIERS//=======// + +/obj/item/clothing/suit/space/space_ninja/proc/grant_equip_verbs() + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/init + verbs += /obj/item/clothing/suit/space/space_ninja/proc/deinit + verbs += /obj/item/clothing/suit/space/space_ninja/proc/spideros + verbs += /obj/item/clothing/suit/space/space_ninja/proc/stealth + n_gloves.verbs += /obj/item/clothing/gloves/space_ninja/proc/toggled + + s_initialized = 1 + +/obj/item/clothing/suit/space/space_ninja/proc/remove_equip_verbs() + verbs += /obj/item/clothing/suit/space/space_ninja/proc/init + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/deinit + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/spideros + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/stealth + if(n_gloves) + n_gloves.verbs -= /obj/item/clothing/gloves/space_ninja/proc/toggled + + s_initialized = 0 + +/obj/item/clothing/suit/space/space_ninja/proc/grant_ninja_verbs() + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjashift + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjajaunt + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjasmoke + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjaboost + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjapulse + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjablade + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjastar + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjanet + + s_initialized=1 + slowdown=0 + +/obj/item/clothing/suit/space/space_ninja/proc/remove_ninja_verbs() + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjashift + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjajaunt + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjasmoke + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjaboost + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjapulse + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjablade + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjastar + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjanet + +//=======//KAMIKAZE VERBS//=======// + +/obj/item/clothing/suit/space/space_ninja/proc/grant_kamikaze(mob/living/carbon/U) + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjashift + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjajaunt + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjapulse + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjastar + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjanet + + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjaslayer + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjawalk + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjamirage + + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/stealth + + kamikaze = 1 + + icon_state = U.gender==FEMALE ? "s-ninjakf" : "s-ninjak" + if(n_gloves) + n_gloves.icon_state = "s-ninjak" + n_gloves.item_state = "s-ninjak" + n_gloves.candrain = 0 + n_gloves.draining = 0 + n_gloves.verbs -= /obj/item/clothing/gloves/space_ninja/proc/toggled + + cancel_stealth() + + U << browse(null, "window=spideros") + U << "\red Do or Die, LET'S ROCK!!" + +/obj/item/clothing/suit/space/space_ninja/proc/remove_kamikaze(mob/living/carbon/U) + if(kamikaze) + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjashift + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjajaunt + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjapulse + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjastar + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjanet + + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjaslayer + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjawalk + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjamirage + + verbs += /obj/item/clothing/suit/space/space_ninja/proc/stealth + if(n_gloves) + n_gloves.verbs -= /obj/item/clothing/gloves/space_ninja/proc/toggled + + U.incorporeal_move = 0 + kamikaze = 0 + k_unlock = 0 + U << "\blue Disengaging mode...\n\blackCODE NAME: \red KAMIKAZE" + +//=======//AI VERBS//=======// + +/obj/item/clothing/suit/space/space_ninja/proc/grant_AI_verbs() + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ai_hack_ninja + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ai_return_control + + s_busy = 0 + s_control = 0 + +/obj/item/clothing/suit/space/space_ninja/proc/remove_AI_verbs() + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ai_hack_ninja + verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ai_return_control + + s_control = 1 + +//=======//OLD & UNUSED//=======// + +/* + +Deprecated. get_dir() does the same thing. Still a nice proc. +Returns direction that the mob or whomever should be facing in relation to the target. +This proc does not grant absolute direction and is mostly useful for 8dir sprite positioning. +I personally used it with getline() to great effect. +/proc/get_dir_to(turf/start,turf/end)//N + var/xdiff = start.x - end.x//The sign is important. + var/ydiff = start.y - end.y + + var/direction_x = xdiff<1 ? 4:8//East - west + var/direction_y = ydiff<1 ? 1:2//North - south + var/direction_xy = xdiff==0 ? -4:0//If x is the same, subtract 4. + var/direction_yx = ydiff==0 ? -1:0//If y is the same, subtract 1. + var/direction_f = direction_x+direction_y+direction_xy+direction_yx//Finally direction tally. + direction_f = direction_f==0 ? 1:direction_f//If direction is 0(same spot), return north. Otherwise, direction. + + return direction_f + +Alternative and inferior method of calculating spideros. +var/temp = num2text(spideros) +var/return_to = copytext(temp, 1, (length(temp)))//length has to be to the length of the thing because by default it's length+1 +spideros = text2num(return_to)//Maximum length here is 6. Use (return_to, X) to specify larger strings if needed. + +//Old way of draining from wire. +/obj/item/clothing/gloves/space_ninja/proc/drain_wire() + set name = "Drain From Wire" + set desc = "Drain energy directly from an exposed wire." + set category = "Ninja Equip" + + var/obj/structure/cable/attached + var/mob/living/carbon/human/U = loc + if(candrain&&!draining) + var/turf/T = U.loc + if(isturf(T) && T.is_plating()) + attached = locate() in T + if(!attached) + U << "\red Warning: no exposed cable available." + else + U << "\blue Connecting to wire, stand still..." + if(do_after(U,50)&&!isnull(attached)) + drain("WIRE",attached,U:wear_suit,src) + else + U << "\red Procedure interrupted. Protocol terminated." + return + +I've tried a lot of stuff but adding verbs to the AI while inside an object, inside another object, did not want to work properly. +This was the best work-around I could come up with at the time. Uses objects to then display to panel, based on the object spell system. +Can be added on to pretty easily. + +BYOND fixed the verb bugs so this is no longer necessary. I prefer verb panels. + +/obj/item/clothing/suit/space/space_ninja/proc/grant_AI_verbs() + var/obj/effect/proc_holder/ai_return_control/A_C = new(AI) + var/obj/effect/proc_holder/ai_hack_ninja/B_C = new(AI) + var/obj/effect/proc_holder/ai_instruction/C_C = new(AI) + new/obj/effect/proc_holder/ai_holo_clear(AI) + AI.proc_holder_list += A_C + AI.proc_holder_list += B_C + AI.proc_holder_list += C_C + + s_control = 0 + +/obj/item/clothing/suit/space/space_ninja/proc/remove_AI_verbs() + var/obj/effect/proc_holder/ai_return_control/A_C = locate() in AI + var/obj/effect/proc_holder/ai_hack_ninja/B_C = locate() in AI + var/obj/effect/proc_holder/ai_instruction/C_C = locate() in AI + var/obj/effect/proc_holder/ai_holo_clear/D_C = locate() in AI + del(A_C) + del(B_C) + del(C_C) + del(D_C) + AI.proc_holder_list = list() + verbs += /obj/item/clothing/suit/space/space_ninja/proc/deinit + verbs += /obj/item/clothing/suit/space/space_ninja/proc/spideros + verbs += /obj/item/clothing/suit/space/space_ninja/proc/stealth + + s_control = 1 + +//Workaround +/obj/effect/proc_holder/ai_holo_clear + name = "Clear Hologram" + desc = "Stops projecting the current holographic image." + panel = "AI Ninja Equip" + density = 0 + opacity = 0 + + +/obj/effect/proc_holder/ai_holo_clear/Click() + var/obj/item/clothing/suit/space/space_ninja/S = loc.loc//This is so stupid but makes sure certain things work. AI.SUIT + del(S.hologram.i_attached) + del(S.hologram) + var/obj/effect/proc_holder/ai_holo_clear/D_C = locate() in S.AI + S.AI.proc_holder_list -= D_C + return + +/obj/effect/proc_holder/ai_instruction//Let's the AI know what they can do. + name = "Instructions" + desc = "Displays a list of helpful information." + panel = "AI Ninja Equip" + density = 0 + opacity = 0 + +/obj/effect/proc_holder/ai_instruction/Click() + loc << "The menu you are seeing will contain other commands if they become available.\nRight click a nearby turf to display an AI Hologram. It will only be visible to you and your host. You can move it freely using normal movement keys--it will disappear if placed too far away." + +/obj/effect/proc_holder/ai_hack_ninja//Generic proc holder to make sure the two verbs below work propely. + name = "Hack SpiderOS" + desc = "Hack directly into the Black Widow(tm) neuro-interface." + panel = "AI Ninja Equip" + density = 0 + opacity = 0 + +/obj/effect/proc_holder/ai_hack_ninja/Click()//When you click on it. + var/obj/item/clothing/suit/space/space_ninja/S = loc.loc + S.hack_spideros() + return + +/obj/effect/proc_holder/ai_return_control + name = "Relinquish Control" + desc = "Return control to the user." + panel = "AI Ninja Equip" + density = 0 + opacity = 0 + +/obj/effect/proc_holder/ai_return_control/Click() + var/mob/living/silicon/ai/A = loc + var/obj/item/clothing/suit/space/space_ninja/S = A.loc + A << browse(null, "window=hack spideros")//Close window + A << "You have seized your hacking attempt. [S.affecting] has regained control." + S.affecting << "UPDATE: [A.real_name] has ceased hacking attempt. All systems clear." + S.remove_AI_verbs() + return +*/ + +//=======//DEBUG//=======// +/* +/obj/item/clothing/suit/space/space_ninja/proc/display_verb_procs() +//DEBUG +//Does nothing at the moment. I am trying to see if it's possible to mess around with verbs as variables. + //for(var/P in verbs) +// if(P.set.name) +// usr << "[P.set.name], path: [P]" + return + + +Most of these are at various points of incomplete. + +/mob/verb/grant_object_panel() + set name = "Grant AI Ninja Verbs Debug" + set category = "Ninja Debug" + var/obj/effect/proc_holder/ai_return_control/A_C = new(src) + var/obj/effect/proc_holder/ai_hack_ninja/B_C = new(src) + usr:proc_holder_list += A_C + usr:proc_holder_list += B_C + +mob/verb/remove_object_panel() + set name = "Remove AI Ninja Verbs Debug" + set category = "Ninja Debug" + var/obj/effect/proc_holder/ai_return_control/A = locate() in src + var/obj/effect/proc_holder/ai_hack_ninja/B = locate() in src + usr:proc_holder_list -= A + usr:proc_holder_list -= B + del(A)//First. + del(B)//Second, to keep the proc going. + return + +/client/verb/grant_verb_ninja_debug1(var/mob/M in view()) + set name = "Grant AI Ninja Verbs Debug" + set category = "Ninja Debug" + + M.verbs += /mob/living/silicon/ai/verb/ninja_return_control + M.verbs += /mob/living/silicon/ai/verb/ninja_spideros + return + +/client/verb/grant_verb_ninja_debug2(var/mob/living/carbon/human/M in view()) + set name = "Grant Back Ninja Verbs" + set category = "Ninja Debug" + + M.wear_suit.verbs += /obj/item/clothing/suit/space/space_ninja/proc/deinit + M.wear_suit.verbs += /obj/item/clothing/suit/space/space_ninja/proc/spideros + return + +/obj/proc/grant_verb_ninja_debug3(var/mob/living/silicon/ai/A as mob) + set name = "Grant AI Ninja Verbs" + set category = "null" + set hidden = 1 + A.verbs -= /obj/item/clothing/suit/space/space_ninja/proc/deinit + A.verbs -= /obj/item/clothing/suit/space/space_ninja/proc/spideros + return + +/mob/verb/get_dir_to_target(var/mob/M in oview()) + set name = "Get Direction to Target" + set category = "Ninja Debug" + + world << "DIR: [get_dir_to(src.loc,M.loc)]" + return +// +/mob/verb/kill_self_debug() + set name = "DEBUG Kill Self" + set category = "Ninja Debug" + + src:death() + +/client/verb/switch_client_debug() + set name = "DEBUG Switch Client" + set category = "Ninja Debug" + + mob = mob:loc:loc + +/mob/verb/possess_mob(var/mob/M in oview()) + set name = "DEBUG Possess Mob" + set category = "Ninja Debug" + + client.mob = M + +/client/verb/switcharoo(var/mob/M in oview()) + set name = "DEBUG Switch to AI" + set category = "Ninja Debug" + + var/mob/last_mob = mob + mob = M + last_mob:wear_suit:AI:key = key +// +/client/verb/ninjaget(var/mob/M in oview()) + set name = "DEBUG Ninja GET" + set category = "Ninja Debug" + + mob = M + M.gib() + space_ninja() + +/mob/verb/set_debug_ninja_target() + set name = "Set Debug Target" + set category = "Ninja Debug" + + ninja_debug_target = src//The target is you, brohime. + world << "Target: [src]" + +/mob/verb/hack_spideros_debug() + set name = "Debug Hack Spider OS" + set category = "Ninja Debug" + + var/mob/living/silicon/ai/A = loc:AI + if(A) + if(!A.key) + A.client.mob = loc:affecting + else + loc:affecting:client:mob = A + return + +//Tests the net and what it does. +/mob/verb/ninjanet_debug() + set name = "Energy Net Debug" + set category = "Ninja Debug" + + var/obj/effect/energy_net/E = new /obj/effect/energy_net(loc) + E.layer = layer+1//To have it appear one layer above the mob. + stunned = 10//So they are stunned initially but conscious. + anchored = 1//Anchors them so they can't move. + E.affecting = src + spawn(0)//Parallel processing. + E.process(src) + return + +I made this as a test for a possible ninja ability (or perhaps more) for a certain mob to see hallucinations. +The thing here is that these guys have to be coded to do stuff as they are simply images that you can't even click on. +That is why you attached them to objects. +/mob/verb/TestNinjaShadow() + set name = "Test Ninja Ability" + set category = "Ninja Debug" + + if(client) + var/safety = 4 + for(var/turf/T in oview(5)) + if(prob(20)) + var/current_clone = image('icons/mob/mob.dmi',T,"s-ninja") + safety-- + spawn(0) + src << current_clone + spawn(300) + del(current_clone) + spawn while(!isnull(current_clone)) + step_to(current_clone,src,1) + sleep(5) + if(safety<=0) break + return */ + +//Alternate ninja speech replacement. +/*This text is hilarious but also absolutely retarded. +message = replacetext(message, "l", "r") +message = replacetext(message, "rr", "ru") +message = replacetext(message, "v", "b") +message = replacetext(message, "f", "hu") +message = replacetext(message, "'t", "") +message = replacetext(message, "t ", "to ") +message = replacetext(message, " I ", " ai ") +message = replacetext(message, "th", "z") +message = replacetext(message, "ish", "isu") +message = replacetext(message, "is", "izu") +message = replacetext(message, "ziz", "zis") +message = replacetext(message, "se", "su") +message = replacetext(message, "br", "bur") +message = replacetext(message, "ry", "ri") +message = replacetext(message, "you", "yuu") +message = replacetext(message, "ck", "cku") +message = replacetext(message, "eu", "uu") +message = replacetext(message, "ow", "au") +message = replacetext(message, "are", "aa") +message = replacetext(message, "ay", "ayu") +message = replacetext(message, "ea", "ii") +message = replacetext(message, "ch", "chi") +message = replacetext(message, "than", "sen") +message = replacetext(message, ".", "") +message = lowertext(message) +*/ + + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++++++++++++++++++++++++++++++++++// //++++++++++++++++++++++++++++++++++ +==================================SPACE NINJA ABILITIES==================================== +___________________________________________________________________________________________ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +//=======//SAFETY CHECK//=======// +/* +X is optional, tells the proc to check for specific stuff. C is also optional. +All the procs here assume that the character is wearing the ninja suit if they are using the procs. +They should, as I have made every effort for that to be the case. +In the case that they are not, I imagine the game will run-time error like crazy. +s_cooldown ticks off each second based on the suit recharge proc, in seconds. Default of 1 seconds. Some abilities have no cool down. +*/ +/obj/item/clothing/suit/space/space_ninja/proc/ninjacost(C = 0,X = 0) + var/mob/living/carbon/human/U = affecting + if( (U.stat||U.incorporeal_move)&&X!=3 )//Will not return if user is using an adrenaline booster since you can use them when stat==1. + U << "\red You must be conscious and solid to do this."//It's not a problem of stat==2 since the ninja will explode anyway if they die. + return 1 + else if(C&&cell.charge[s_bombs] smoke bombs remaining." + var/datum/effect/effect/system/bad_smoke_spread/smoke = new /datum/effect/effect/system/bad_smoke_spread() + smoke.set_up(10, 0, U.loc) + smoke.start() + playsound(U.loc, 'sound/effects/bamf.ogg', 50, 2) + s_bombs-- + s_coold = 1 + return + +//=======//9-8 TILE TELEPORT//=======// +//Click to to teleport 9-10 tiles in direction facing. +/obj/item/clothing/suit/space/space_ninja/proc/ninjajaunt() + set name = "Phase Jaunt (10E)" + set desc = "Utilizes the internal VOID-shift device to rapidly transit in direction facing." + set category = "Ninja Ability" + set popup_menu = 0 + + var/C = 100 + if(!ninjacost(C,1)) + var/mob/living/carbon/human/U = affecting + var/turf/destination = get_teleport_loc(U.loc,U,9,1,3,1,0,1) + var/turf/mobloc = get_turf(U.loc)//To make sure that certain things work properly below. + if(destination&&istype(mobloc, /turf))//The turf check prevents unusual behavior. Like teleporting out of cryo pods, cloners, mechs, etc. + spawn(0) + playsound(U.loc, "sparks", 50, 1) + anim(mobloc,src,'icons/mob/mob.dmi',,"phaseout",,U.dir) + + handle_teleport_grab(destination, U) + U.loc = destination + + spawn(0) + spark_system.start() + playsound(U.loc, 'sound/effects/phasein.ogg', 25, 1) + playsound(U.loc, "sparks", 50, 1) + anim(U.loc,U,'icons/mob/mob.dmi',,"phasein",,U.dir) + + spawn(0) + destination.kill_creatures(U)//Any living mobs in teleport area are gibbed. Check turf procs for how it does it. + s_coold = 1 + cell.charge-=(C*10) + else + U << "\red The VOID-shift device is malfunctioning, teleportation failed." + return + +//=======//RIGHT CLICK TELEPORT//=======// +//Right click to teleport somewhere, almost exactly like admin jump to turf. +/obj/item/clothing/suit/space/space_ninja/proc/ninjashift(turf/T in oview()) + set name = "Phase Shift (20E)" + set desc = "Utilizes the internal VOID-shift device to rapidly transit to a destination in view." + set category = null//So it does not show up on the panel but can still be right-clicked. + set src = usr.contents//Fixes verbs not attaching properly for objects. Praise the DM reference guide! + + var/C = 200 + if(!ninjacost(C,1)) + var/mob/living/carbon/human/U = affecting + var/turf/mobloc = get_turf(U.loc)//To make sure that certain things work properly below. + if((!T.density)&&istype(mobloc, /turf)) + spawn(0) + playsound(U.loc, 'sound/effects/sparks4.ogg', 50, 1) + anim(mobloc,src,'icons/mob/mob.dmi',,"phaseout",,U.dir) + + handle_teleport_grab(T, U) + U.loc = T + + spawn(0) + spark_system.start() + playsound(U.loc, 'sound/effects/phasein.ogg', 25, 1) + playsound(U.loc, 'sound/effects/sparks2.ogg', 50, 1) + anim(U.loc,U,'icons/mob/mob.dmi',,"phasein",,U.dir) + + spawn(0)//Any living mobs in teleport area are gibbed. + T.kill_creatures(U) + s_coold = 1 + cell.charge-=(C*10) + else + U << "\red You cannot teleport into solid walls or from solid matter" + return + +//=======//EM PULSE//=======// +//Disables nearby tech equipment. +/obj/item/clothing/suit/space/space_ninja/proc/ninjapulse() + set name = "EM Burst (25E)" + set desc = "Disable any nearby technology with a electro-magnetic pulse." + set category = "Ninja Ability" + set popup_menu = 0 + + var/C = 250 + if(!ninjacost(C,1)) + var/mob/living/carbon/human/U = affecting + playsound(U.loc, 'sound/effects/EMPulse.ogg', 60, 2) + empulse(U, 4, 6) //Procs sure are nice. Slightly weaker than wizard's disable tch. + s_coold = 2 + cell.charge-=(C*10) + return + +//=======//ENERGY BLADE//=======// +//Summons a blade of energy in active hand. +/obj/item/clothing/suit/space/space_ninja/proc/ninjablade() + set name = "Energy Blade (5E)" + set desc = "Create a focused beam of energy in your active hand." + set category = "Ninja Ability" + set popup_menu = 0 + + var/C = 50 + if(!ninjacost(C)) + var/mob/living/carbon/human/U = affecting + if(!kamikaze) + if(!U.get_active_hand()&&!istype(U.get_inactive_hand(), /obj/item/weapon/melee/energy/blade)) + var/obj/item/weapon/melee/energy/blade/W = new() + spark_system.start() + playsound(U.loc, "sparks", 50, 1) + U.put_in_hands(W) + cell.charge-=(C*10) + else + U << "\red You can only summon one blade. Try dropping an item first." + else//Else you can run around with TWO energy blades. I don't know why you'd want to but cool factor remains. + if(!U.get_active_hand()) + var/obj/item/weapon/melee/energy/blade/W = new() + U.put_in_hands(W) + if(!U.get_inactive_hand()) + var/obj/item/weapon/melee/energy/blade/W = new() + U.put_in_inactive_hand(W) + spark_system.start() + playsound(U.loc, "sparks", 50, 1) + s_coold = 1 + return + +//=======//NINJA STARS//=======// +/*Shoots ninja stars at random people. +This could be a lot better but I'm too tired atm.*/ +/obj/item/clothing/suit/space/space_ninja/proc/ninjastar() + set name = "Energy Star (5E)" + set desc = "Launches an energy star at a random living target." + set category = "Ninja Ability" + set popup_menu = 0 + + var/C = 50 + if(!ninjacost(C)) + var/mob/living/carbon/human/U = affecting + var/targets[] = list()//So yo can shoot while yo throw dawg + for(var/mob/living/M in oview(loc)) + if(M.stat) continue//Doesn't target corpses or paralyzed persons. + targets.Add(M) + if(targets.len) + var/mob/living/target=pick(targets)//The point here is to pick a random, living mob in oview to shoot stuff at. + + var/turf/curloc = U.loc + var/atom/targloc = get_turf(target) + if (!targloc || !istype(targloc, /turf) || !curloc) + return + if (targloc == curloc) + return + var/obj/item/projectile/energy/dart/A = new /obj/item/projectile/energy/dart(U.loc) + A.current = curloc + A.yo = targloc.y - curloc.y + A.xo = targloc.x - curloc.x + cell.charge-=(C*10) + A.process() + else + U << "\red There are no targets in view." + return + +//=======//ENERGY NET//=======// +/*Allows the ninja to capture people, I guess. +Must right click on a mob to activate.*/ +/obj/item/clothing/suit/space/space_ninja/proc/ninjanet(mob/living/carbon/M in oview())//Only living carbon mobs. + set name = "Energy Net (20E)" + set desc = "Captures a fallen opponent in a net of energy. Will teleport them to a holding facility after 30 seconds." + set category = null + set src = usr.contents + + var/C = 200 + if(!ninjacost(C,1)&&iscarbon(M)) + var/mob/living/carbon/human/U = affecting + if(M.client)//Monkeys without a client can still step_to() and bypass the net. Also, netting inactive people is lame. + //if(M)//DEBUG + if(!locate(/obj/effect/energy_net) in M.loc)//Check if they are already being affected by an energy net. + for(var/turf/T in getline(U.loc, M.loc)) + if(T.density)//Don't want them shooting nets through walls. It's kind of cheesy. + U << "You may not use an energy net through solid obstacles!" + return + spawn(0) + U.Beam(M,"n_beam",,15) + M.anchored = 1//Anchors them so they can't move. + U.say("Get over here!") + var/obj/effect/energy_net/E = new /obj/effect/energy_net(M.loc) + E.layer = M.layer+1//To have it appear one layer above the mob. + for(var/mob/O in viewers(U, 3)) + O.show_message(text("\red [] caught [] with an energy net!", U, M), 1) + E.affecting = M + E.master = U + spawn(0)//Parallel processing. + E.process(M) + cell.charge-=(C*10) + else + U << "They are already trapped inside an energy net." + else + U << "They will bring no honor to your Clan!" + return + +//=======//ADRENALINE BOOST//=======// +/*Wakes the user so they are able to do their thing. Also injects a decent dose of radium. +Movement impairing would indicate drugs and the like.*/ +/obj/item/clothing/suit/space/space_ninja/proc/ninjaboost() + set name = "Adrenaline Boost" + set desc = "Inject a secret chemical that will counteract all movement-impairing effect." + set category = "Ninja Ability" + set popup_menu = 0 + + if(!ninjacost(,3))//Have to make sure stat is not counted for this ability. + var/mob/living/carbon/human/U = affecting + //Wouldn't need to track adrenaline boosters if there was a miracle injection to get rid of paralysis and the like instantly. + //For now, adrenaline boosters ARE the miracle injection. Well, radium, really. + U.SetParalysis(0) + U.SetStunned(0) + U.SetWeakened(0) + /* + Due to lag, it was possible to adrenaline boost but remain helpless while life.dm resets player stat. + This lead to me and others spamming adrenaline boosters because they failed to kick in on time. + It's technically possible to come back from crit with this but it is very temporary. + Life.dm will kick the player back into unconsciosness the next process loop. + */ + U.stat = 0//At least now you should be able to teleport away or shoot ninja stars. + spawn(30)//Slight delay so the enemy does not immedietly know the ability was used. Due to lag, this often came before waking up. + U.say(pick("A CORNERED FOX IS MORE DANGEROUS THAN A JACKAL!","HURT ME MOOORRREEE!","IMPRESSIVE!")) + spawn(70) + reagents.reaction(U, 2) + reagents.trans_id_to(U, "radium", a_transfer) + U << "\red You are beginning to feel the after-effect of the injection." + a_boost-- + s_coold = 3 + return + +/* +=================================================================================== +<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +=================================================================================== +Or otherwise known as anime mode. Which also happens to be ridiculously powerful. +*/ + +//=======//NINJA MOVEMENT//=======// +//Also makes you move like you're on crack. +/obj/item/clothing/suit/space/space_ninja/proc/ninjawalk() + set name = "Shadow Walk" + set desc = "Combines the VOID-shift and CLOAK-tech devices to freely move between solid matter. Toggle on or off." + set category = "Ninja Ability" + set popup_menu = 0 + + var/mob/living/carbon/human/U = affecting + if(!U.incorporeal_move) + U.incorporeal_move = 2 + U << "\blue You will now phase through solid matter." + else + U.incorporeal_move = 0 + U << "\blue You will no-longer phase through solid matter." + return + +//=======//5 TILE TELEPORT/GIB//=======// +//Allows to gib up to five squares in a straight line. Seriously. +/obj/item/clothing/suit/space/space_ninja/proc/ninjaslayer() + set name = "Phase Slayer" + set desc = "Utilizes the internal VOID-shift device to mutilate creatures in a straight line." + set category = "Ninja Ability" + set popup_menu = 0 + + if(!ninjacost()) + var/mob/living/carbon/human/U = affecting + var/turf/destination = get_teleport_loc(U.loc,U,5) + var/turf/mobloc = get_turf(U.loc)//To make sure that certain things work properly below. + if(destination&&istype(mobloc, /turf)) + U.say("Ai Satsugai!") + spawn(0) + playsound(U.loc, "sparks", 50, 1) + anim(mobloc,U,'icons/mob/mob.dmi',,"phaseout",,U.dir) + + spawn(0) + for(var/turf/T in getline(mobloc, destination)) + spawn(0) + T.kill_creatures(U) + if(T==mobloc||T==destination) continue + spawn(0) + anim(T,U,'icons/mob/mob.dmi',,"phasein",,U.dir) + + handle_teleport_grab(destination, U) + U.loc = destination + + spawn(0) + spark_system.start() + playsound(U.loc, 'sound/effects/phasein.ogg', 25, 1) + playsound(U.loc, "sparks", 50, 1) + anim(U.loc,U,'icons/mob/mob.dmi',,"phasein",,U.dir) + s_coold = 1 + else + U << "\red The VOID-shift device is malfunctioning, teleportation failed." + return + +//=======//TELEPORT BEHIND MOB//=======// +/*Appear behind a randomly chosen mob while a few decoy teleports appear. +This is so anime it hurts. But that's the point.*/ +/obj/item/clothing/suit/space/space_ninja/proc/ninjamirage() + set name = "Spider Mirage" + set desc = "Utilizes the internal VOID-shift device to create decoys and teleport behind a random target." + set category = "Ninja Ability" + set popup_menu = 0 + + if(!ninjacost())//Simply checks for stat. + var/mob/living/carbon/human/U = affecting + var/targets[] + targets = new() + for(var/mob/living/M in oview(6)) + if(M.stat) continue//Doesn't target corpses or paralyzed people. + targets.Add(M) + if(targets.len) + var/mob/living/target=pick(targets) + var/locx + var/locy + var/turf/mobloc = get_turf(target.loc) + var/safety = 0 + switch(target.dir) + if(NORTH) + locx = mobloc.x + locy = (mobloc.y-1) + if(locy<1) + safety = 1 + if(SOUTH) + locx = mobloc.x + locy = (mobloc.y+1) + if(locy>world.maxy) + safety = 1 + if(EAST) + locy = mobloc.y + locx = (mobloc.x-1) + if(locx<1) + safety = 1 + if(WEST) + locy = mobloc.y + locx = (mobloc.x+1) + if(locx>world.maxx) + safety = 1 + else safety=1 + if(!safety&&istype(mobloc, /turf)) + U.say("Kumo no Shinkiro!") + var/turf/picked = locate(locx,locy,mobloc.z) + spawn(0) + playsound(U.loc, "sparks", 50, 1) + anim(mobloc,U,'icons/mob/mob.dmi',,"phaseout",,U.dir) + + spawn(0) + var/limit = 4 + for(var/turf/T in oview(5)) + if(prob(20)) + spawn(0) + anim(T,U,'icons/mob/mob.dmi',,"phasein",,U.dir) + limit-- + if(limit<=0) break + + handle_teleport_grab(picked, U) + U.loc = picked + U.dir = target.dir + + spawn(0) + spark_system.start() + playsound(U.loc, 'sound/effects/phasein.ogg', 25, 1) + playsound(U.loc, "sparks", 50, 1) + anim(U.loc,U,'icons/mob/mob.dmi',,"phasein",,U.dir) + s_coold = 1 + else + U << "\red The VOID-shift device is malfunctioning, teleportation failed." + else + U << "\red There are no targets in view." + return + + +//For the love of god,space out your code! This is a nightmare to read. + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++++++++++++++++++++++++++++++++++// //++++++++++++++++++++++++++++++++++ +===================================SPACE NINJA EQUIPMENT=================================== +___________________________________________________________________________________________ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +/* +=================================================================================== +<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +=================================================================================== +*/ + +//=======//NEW AND DEL//=======// + +/obj/item/clothing/suit/space/space_ninja/New() + ..() + verbs += /obj/item/clothing/suit/space/space_ninja/proc/init//suit initialize verb + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ai_instruction//for AIs + verbs += /obj/item/clothing/suit/space/space_ninja/proc/ai_holo + //verbs += /obj/item/clothing/suit/space/space_ninja/proc/display_verb_procs//DEBUG. Doesn't work. + spark_system = new()//spark initialize + spark_system.set_up(5, 0, src) + spark_system.attach(src) + stored_research = new()//Stolen research initialize. + for(var/T in typesof(/datum/tech) - /datum/tech)//Store up on research. + stored_research += new T(src) + var/reagent_amount//reagent initialize + for(var/reagent_id in reagent_list) + reagent_amount += reagent_id == "radium" ? r_maxamount+(a_boost*a_transfer) : r_maxamount//AI can inject radium directly. + reagents = new(reagent_amount) + reagents.my_atom = src + for(var/reagent_id in reagent_list) + reagent_id == "radium" ? reagents.add_reagent(reagent_id, r_maxamount+(a_boost*a_transfer)) : reagents.add_reagent(reagent_id, r_maxamount)//It will take into account radium used for adrenaline boosting. + cell = new/obj/item/weapon/cell/high//The suit should *always* have a battery because so many things rely on it. + cell.charge = 9000//Starting charge should not be higher than maximum charge. It leads to problems with recharging. + +/obj/item/clothing/suit/space/space_ninja/Del() + if(affecting)//To make sure the window is closed. + affecting << browse(null, "window=hack spideros") + if(AI)//If there are AIs present when the ninja kicks the bucket. + killai() + if(hologram)//If there is a hologram + del(hologram.i_attached)//Delete it and the attached image. + del(hologram) + ..() + return + +//Simply deletes all the attachments and self, killing all related procs. +/obj/item/clothing/suit/space/space_ninja/proc/terminate() + del(n_hood) + del(n_gloves) + del(n_shoes) + del(src) + +/obj/item/clothing/suit/space/space_ninja/proc/killai(mob/living/silicon/ai/A = AI) + if(A.client) + A << "\red Self-erase protocol dete-- *bzzzzz*" + A << browse(null, "window=hack spideros") + AI = null + A.death(1)//Kill, deleting mob. + del(A) + return + +//=======//SUIT VERBS//=======// +//Verbs link to procs because verb-like procs have a bug which prevents their use if the arguments are not readily referenced. + +/obj/item/clothing/suit/space/space_ninja/proc/init() + set name = "Initialize Suit" + set desc = "Initializes the suit for field operation." + set category = "Ninja Equip" + + ninitialize() + return + +/obj/item/clothing/suit/space/space_ninja/proc/deinit() + set name = "De-Initialize Suit" + set desc = "Begins procedure to remove the suit." + set category = "Ninja Equip" + + if(s_control&&!s_busy) + deinitialize() + else + affecting << "\red The function did not trigger!" + return + +/obj/item/clothing/suit/space/space_ninja/proc/spideros() + set name = "Display SpiderOS" + set desc = "Utilize built-in computer system." + set category = "Ninja Equip" + + if(s_control&&!s_busy&&!kamikaze) + display_spideros() + else + affecting << "\red The interface is locked!" + return + +/obj/item/clothing/suit/space/space_ninja/proc/stealth() + set name = "Toggle Stealth" + set desc = "Utilize the internal CLOAK-tech device to activate or deactivate stealth-camo." + set category = "Ninja Equip" + + if(s_control&&!s_busy) + toggle_stealth() + else + affecting << "\red Stealth does not appear to work!" + return + +//=======//PROCESS PROCS//=======// + +/obj/item/clothing/suit/space/space_ninja/proc/ntick(mob/living/carbon/human/U = affecting) + set background = 1 + + //Runs in the background while the suit is initialized. + spawn while(cell.charge>=0) + + //Let's check for some safeties. + if(s_initialized&&!affecting) terminate()//Kills the suit and attached objects. + if(!s_initialized) return//When turned off the proc stops. + if(AI&&AI.stat==2)//If there is an AI and it's ded. Shouldn't happen without purging, could happen. + if(!s_control) + ai_return_control()//Return control to ninja if the AI was previously in control. + killai()//Delete AI. + + //Now let's do the normal processing. + if(s_coold) s_coold--//Checks for ability s_cooldown first. + var/A = s_cost//s_cost is the default energy cost each ntick, usually 5. + if(!kamikaze) + if(blade_check(U))//If there is a blade held in hand. + A += s_acost + if(s_active)//If stealth is active. + A += s_acost + else + if(prob(s_delay))//Suit delay is used as probability. May change later. + U.adjustBruteLoss(k_damage)//Default damage done, usually 1. + A = k_cost//kamikaze cost. + cell.charge-=A + if(cell.charge<=0) + if(kamikaze) + U.say("I DIE TO LIVE AGAIN!") + U << browse(null, "window=spideros")//Just in case. + U.death() + return + cell.charge=0 + cancel_stealth() + sleep(10)//Checks every second. + +//=======//INITIALIZE//=======// + +/obj/item/clothing/suit/space/space_ninja/proc/ninitialize(delay = s_delay, mob/living/carbon/human/U = loc) + if(U.mind && U.mind.assigned_role=="MODE" && !s_initialized && !s_busy)//Shouldn't be busy... but anything is possible I guess. + s_busy = 1 + for(var/i,i<7,i++) + switch(i) + if(0) + U << "\blue Now initializing..." + if(1) + if(!lock_suit(U))//To lock the suit onto wearer. + break + U << "\blue Securing external locking mechanism...\nNeural-net established." + if(2) + U << "\blue Extending neural-net interface...\nNow monitoring brain wave pattern..." + if(3) + if(U.stat==2||U.health<=0) + U << "\red FĆAL �Rr�R: 344--93#�&&21 BR��N |/|/aV� PATT$RN RED\nA-A-aB�rT�NG..." + unlock_suit() + break + lock_suit(U,1)//Check for icons. + U.regenerate_icons() + U << "\blue Linking neural-net interface...\nPattern \green GREEN\blue, continuing operation." + if(4) + U << "\blue VOID-shift device status: ONLINE.\nCLOAK-tech device status: ONLINE." + if(5) + U << "\blue Primary system status: ONLINE.\nBackup system status: ONLINE.\nCurrent energy capacity: [cell.charge]." + if(6) + U << "\blue All systems operational. Welcome to SpiderOS, [U.real_name]." + grant_ninja_verbs() + grant_equip_verbs() + ntick() + sleep(delay) + s_busy = 0 + else + if(!U.mind||U.mind.assigned_role!="MODE")//Your run of the mill persons shouldn't know what it is. Or how to turn it on. + U << "You do not understand how this suit functions. Where the heck did it even come from?" + else if(s_initialized) + U << "\red The suit is already functioning. \black Please report this bug." + else + U << "\red ERROR: \black You cannot use this function at this time." + return + +//=======//DEINITIALIZE//=======// + +/obj/item/clothing/suit/space/space_ninja/proc/deinitialize(delay = s_delay) + if(affecting==loc&&!s_busy) + var/mob/living/carbon/human/U = affecting + if(!s_initialized) + U << "\red The suit is not initialized. \black Please report this bug." + return + if(alert("Are you certain you wish to remove the suit? This will take time and remove all abilities.",,"Yes","No")=="No") + return + if(s_busy||flush) + U << "\red ERROR: \black You cannot use this function at this time." + return + s_busy = 1 + for(var/i = 0,i<7,i++) + switch(i) + if(0) + U << "\blue Now de-initializing..." + remove_kamikaze(U)//Shutdowns kamikaze. + spideros = 0//Spideros resets. + if(1) + U << "\blue Logging off, [U:real_name]. Shutting down SpiderOS." + remove_ninja_verbs() + if(2) + U << "\blue Primary system status: OFFLINE.\nBackup system status: OFFLINE." + if(3) + U << "\blue VOID-shift device status: OFFLINE.\nCLOAK-tech device status: OFFLINE." + cancel_stealth()//Shutdowns stealth. + if(4) + U << "\blue Disconnecting neural-net interface...\greenSuccess\blue." + if(5) + U << "\blue Disengaging neural-net interface...\greenSuccess\blue." + if(6) + U << "\blue Unsecuring external locking mechanism...\nNeural-net abolished.\nOperation status: FINISHED." + blade_check(U,2) + remove_equip_verbs() + unlock_suit() + U.regenerate_icons() + sleep(delay) + s_busy = 0 + return + +//=======//SPIDEROS PROC//=======// + +/obj/item/clothing/suit/space/space_ninja/proc/display_spideros() + if(!affecting) return//If no mob is wearing the suit. I almost forgot about this variable. + var/mob/living/carbon/human/U = affecting + var/mob/living/silicon/ai/A = AI + var/display_to = s_control ? U : A//Who do we want to display certain messages to? + + var/dat = "SpiderOS" + dat += " Refresh" + if(spideros) + dat += " | Return" + dat += " | Close" + dat += "
" + if(s_control) + dat += "

SpiderOS v.1.337

" + dat += "Welcome, [U.real_name].
" + else + dat += "

SpiderOS v.ERR-RR00123

" + dat += "
" + dat += " Current Time: [worldtime2text()]
" + dat += " Battery Life: [round(cell.charge/100)]%
" + dat += " Smoke Bombs: \Roman [s_bombs]
" + dat += " pai Device: " + if(pai) + dat += "Configure" + dat += " | " + dat += "Eject" + else + dat += "None Detected" + dat += "

" + + switch(spideros) + if(0) + dat += "

Available Functions:

" + dat += "" + if(3) + dat += "

Medical Report:

" + if(U.dna) + dat += "Fingerprints: [md5(U.dna.uni_identity)]
" + dat += "Unique identity: [U.dna.unique_enzymes]
" + dat += "

Overall Status: [U.stat > 1 ? "dead" : "[U.health]% healthy"]

" + dat += "

Nutrition Status: [U.nutrition]

" + dat += "Oxygen loss: [U.getOxyLoss()]" + dat += " | Toxin levels: [U.getToxLoss()]
" + dat += "Burn severity: [U.getFireLoss()]" + dat += " | Brute trauma: [U.getBruteLoss()]
" + dat += "Radiation Level: [U.radiation] rad
" + dat += "Body Temperature: [U.bodytemperature-T0C]°C ([U.bodytemperature*1.8-459.67]°F)
" + + for(var/datum/disease/D in U.viruses) + dat += "Warning: Virus Detected. Name: [D.name].Type: [D.spread]. Stage: [D.stage]/[D.max_stages]. Possible Cure: [D.cure].
" + dat += "" + if(1) + dat += "

Atmospheric Scan:

"//Headers don't need breaks. They are automatically placed. + var/turf/T = get_turf(U.loc) + if (isnull(T)) + dat += "Unable to obtain a reading." + else + var/datum/gas_mixture/environment = T.return_air() + + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles() + + dat += "Air Pressure: [round(pressure,0.1)] kPa" + + if (total_moles) + var/o2_level = environment.oxygen/total_moles + var/n2_level = environment.nitrogen/total_moles + var/co2_level = environment.carbon_dioxide/total_moles + var/plasma_level = environment.toxins/total_moles + var/unknown_level = 1-(o2_level+n2_level+co2_level+plasma_level) + dat += "
    " + dat += "
  • Nitrogen: [round(n2_level*100)]%
  • " + dat += "
  • Oxygen: [round(o2_level*100)]%
  • " + dat += "
  • Carbon Dioxide: [round(co2_level*100)]%
  • " + dat += "
  • Plasma: [round(plasma_level*100)]%
  • " + dat += "
" + if(unknown_level > 0.01) + dat += "OTHER: [round(unknown_level)]%
" + + dat += "Temperature: [round(environment.temperature-T0C)]°C" + if(2) + if(k_unlock==7||!s_control) + dat += " Hidden Menu" + dat += "

Anonymous Messenger:

"//Anonymous because the receiver will not know the sender's identity. + dat += "

Detected PDAs:

" + dat += "
    " + var/count = 0 + for (var/obj/item/device/pda/P in get_viewable_pdas()) + dat += "
  • [P]" + dat += "
  • " + count++ + dat += "
" + if (count == 0) + dat += "None detected.
" + if(32) + dat += "

Hidden Menu:

" + if(s_control) + dat += "Please input password: " + dat += "HERE
" + dat += "
" + dat += "Remember, you will not be able to recharge energy during this function. If energy runs out, the suit will auto self-destruct.
" + dat += "Use with caution. De-initialize the suit when energy is low." + else + //Only leaving this in for funnays. CAN'T LET YOU DO THAT STAR FOX + dat += "WARNING: Hostile runtime intrusion detected: operation locked. The Spider Clan is watching you, INTRUDER." + dat += "ERROR: TARANTULA.v.4.77.12 encryption algorithm detected. Unable to decrypt archive.
" + if(4) + dat += {" +

Ninja Manual:

+
Who they are:
+ Space ninjas are a special type of ninja, specifically one of the space-faring type. The vast majority of space ninjas belong to the Spider Clan, a cult-like sect, which has existed for several hundred years. The Spider Clan practice a sort of augmentation of human flesh in order to achieve a more perfect state of being and follow Postmodern Space Bushido. They also kill people for money. Their leaders are chosen from the oldest of the grand-masters, people that have lived a lot longer than any mortal man should.
Being a sect of technology-loving fanatics, the Spider Clan have the very best to choose from in terms of hardware--cybernetic implants, exoskeleton rigs, hyper-capacity batteries, and you get the idea. Some believe that much of the Spider Clan equipment is based on reverse-engineered alien technology while others doubt such claims.
Whatever the case, their technology is absolutely superb. +
How they relate to other SS13 organizations:
+
    +
  • *Nanotrasen and the Syndicate are two sides of the same coin and that coin is valuable.
  • +
  • *The Space Wizard Federation is a problem, mainly because they are an extremely dangerous group of unpredictable individuals--not to mention the wizards hate technology and are in direct opposition of the Spider Clan. Best avoided or left well-enough alone. How to battle: wizards possess several powerful abilities to steer clear off. Blind in particular is a nasty spell--jaunt away if you are blinded and never approach a wizard in melee. Stealth may also work if the wizard is not wearing thermal scanners--don't count on this. Run away if you feel threatened and await a better opportunity.
  • +
  • *Changeling Hivemind: extremely dangerous and to be killed on sight. How to battle: they will likely try to absorb you. Adrenaline boost, then phase shift into them. If you get stung, use SpiderOS to inject counter-agents. Stealth may also work but detecting a changeling is the real battle.
  • +
  • *Xeno Hivemind: their skulls make interesting kitchen decorations and are challenging to best, especially in larger nests. How to battle: they can see through your stealth guise and energy stars will not work on them. Best killed with a Phase Shift or at range. If you happen on a projectile stun weapon, use it and then close in to melee.
  • +
+
The reason they (you) are here:
+ Space ninjas are renowned throughout the known controlled space as fearless spies, infiltrators, and assassins. They are sent on missions of varying nature by Nanotrasen, the Syndicate, and other shady organizations and people. To hire a space ninja means serious business. +
Their playstyle:
+ A mix of traitor, changeling, and wizard. Ninjas rely on energy, or electricity to be precise, to keep their suits running (when out of energy, a suit hibernates). Suits gain energy from objects or creatures that contain electrical charge. APCs, cell batteries, rechargers, SMES batteries, cyborgs, mechs, and exposed wires are currently supported. Through energy ninjas gain access to special powers--while all powers are tied to the ninja suit, the most useful of them are verb activated--to help them in their mission.
It is a constant struggle for a ninja to remain hidden long enough to recharge the suit and accomplish their objective; despite their arsenal of abilities, ninjas can die like any other. Unlike wizards, ninjas do not possess good crowd control and are typically forced to play more subdued in order to achieve their goals. Some of their abilities are specifically designed to confuse and disorient others.
With that said, it should be perfectly possible to completely flip the fuck out and rampage as a ninja. +
Their powers:
+ There are two primary types: Equipment and Abilties. Passive effect are always on. Active effect must be turned on and remain active only when there is energy to do so. Ability costs are listed next to them. + Equipment: cannot be tracked by AI (passive), faster speed (passive), stealth (active), vision switch (passive if toggled), voice masking (passive), SpiderOS (passive if toggled), energy drain (passive if toggled). +
    +
  • Voice masking generates a random name the ninja can use over the radio and in-person. Although, the former use is recommended.
  • +
  • Toggling vision cycles to one of the following: thermal, meson, or darkness vision. The starting mode allows one to scout the identity of those in view, revealing their role. Traitors, revolutionaries, wizards, and other such people will be made known to you.
  • +
  • Stealth, when activated, drains more battery charge and works similarly to a syndicate cloak. The cloak will deactivate when most Abilities are utilized.
  • +
  • On-board AI: The suit is able to download an AI much like an intelicard. Check with SpiderOS for details once downloaded.
  • +
  • SpiderOS is a specialized, PDA-like screen that allows for a small variety of functions, such as injecting healing chemicals directly from the suit. You are using it now, if that was not already obvious. You may also download AI modules directly to the OS.
  • +
+ Abilities: +