mirror of
https://github.com/Yawn-Wider/YWPolarisVore.git
synced 2026-08-24 05:28:16 +01:00
Whitespace Standardization [MDB IGNORE] (#15748)
* Update settings * Whitespace changes * Comment out merger hooks in gitattributes Corrupt maps would have to be resolved in repo before hooks could be updated * Revert "Whitespace changes" This reverts commit afbdd1d8442973f5d570c30920d9d865b5acd479. * Whitespace again minus example * Gitignore example changelog * Restore changelog merge setting * Keep older dmi hook attribute until hooks can be updated * update vscode settings too * Renormalize remaining * Revert "Gitignore example changelog" This reverts commit de22ad375d3ee4d5930c550da2fd23a29a86e616. * Attempt to normalize example.yml (and another file I guess) * Try again
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
#define EVENT_CHAOS_THRESHOLD_HIGH_IMPACT 25
|
||||
#define EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT 50
|
||||
#define EVENT_CHAOS_THRESHOLD_HIGH_IMPACT 25
|
||||
#define EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT 50
|
||||
#define EVENT_CHAOS_THRESHOLD_LOW_IMPACT 100
|
||||
@@ -1,240 +1,240 @@
|
||||
// This object holds the code that is needed to execute an event.
|
||||
// Code for judging whether doing that event is a good idea or not belongs inside its meta event object.
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Important: DO NOT `sleep()` in any of the procs here, or the GM will get stuck. Use callbacks insead.
|
||||
Also please don't use spawn(), but use callbacks instead.
|
||||
|
||||
Note that there is an important distinction between an event being ended, and an event being finished.
|
||||
- Ended is for when the actual event is over, regardless of whether an announcement happened or not.
|
||||
- Finished is for when both the event itself is over, and it was announced. The event will stop being
|
||||
processed after it is finished.
|
||||
|
||||
For an event to finish, it must have done two things:
|
||||
- Go through its entire cycle, of start() -> end(), and
|
||||
- Have the event be announced.
|
||||
If an event has ended, but the announcement didn't happen, the event will not be finished.
|
||||
This allows for events that have their announcement happen after the end itself.
|
||||
|
||||
*/
|
||||
|
||||
//
|
||||
/datum/event2/event
|
||||
var/announced = FALSE // Is set to TRUE when `announce()` is called by `process()`.
|
||||
var/started = FALSE // Is set to TRUE when `start()` is called by `process()`.
|
||||
var/ended = FALSE // Is set to TRUE when `end()` is called by `process()`.
|
||||
var/finished = FALSE // Is set to TRUE when `ended` and `announced` are TRUE.
|
||||
|
||||
// `world.time`s when this event started, and finished, for bookkeeping.
|
||||
var/time_started = null
|
||||
var/time_finished = null
|
||||
|
||||
// If these are set, the announcement will be delayed by a random time between the lower and upper bounds.
|
||||
// If the upper bound is not defined, then it will use the lower bound instead.
|
||||
// Note that this is independant of the event itself, so you can have the announcement happen long after the event ended.
|
||||
// This may not work if should_announce() is overrided.
|
||||
var/announce_delay_lower_bound = null
|
||||
var/announce_delay_upper_bound = null
|
||||
|
||||
// If these are set, the event will be delayed by a random time between the lower and upper bounds.
|
||||
// If the upper bound is not defined, then it will use the lower bound instead.
|
||||
// This may not work if should_start() is overrided.
|
||||
var/start_delay_lower_bound = null
|
||||
var/start_delay_upper_bound = null
|
||||
|
||||
// If these are set, the event will automatically end at a random time between the lower and upper bounds.
|
||||
// If the upper bound is not defined, then it will use the lower bound instead.
|
||||
// This may not work if should_end() is overrided.
|
||||
var/length_lower_bound = null
|
||||
var/length_upper_bound = null
|
||||
|
||||
// Set automatically, don't touch.
|
||||
var/time_to_start = null
|
||||
var/time_to_announce = null
|
||||
var/time_to_end = null
|
||||
|
||||
// These are also set automatically, and are provided for events to know what RNG decided for the various durations.
|
||||
var/start_delay = null
|
||||
var/announce_delay = null
|
||||
var/length = null
|
||||
|
||||
// Returns the name of where the event is taking place.
|
||||
// In the future this might be handy for off-station events.
|
||||
/datum/event2/event/proc/location_name()
|
||||
return station_name()
|
||||
|
||||
// Returns the z-levels that are involved with the event.
|
||||
// In the future this might be handy for off-station events.
|
||||
/datum/event2/event/proc/get_location_z_levels(space_only = FALSE)
|
||||
. = using_map.station_levels.Copy()
|
||||
if(space_only)
|
||||
for(var/z_level in .)
|
||||
if(is_planet_z_level(z_level))
|
||||
. -= z_level
|
||||
|
||||
|
||||
/datum/event2/event/proc/is_planet_z_level(z_level)
|
||||
var/datum/planet/P = LAZYACCESS(SSplanets.z_to_planet, z_level)
|
||||
if(!istype(P))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
// Returns a list of empty turfs in the same area.
|
||||
/datum/event2/event/proc/find_random_turfs(minimum_free_space = 5, list/specific_areas = list(), ignore_occupancy = FALSE)
|
||||
var/list/area/grand_list_of_areas = find_random_areas(specific_areas)
|
||||
|
||||
if(!LAZYLEN(grand_list_of_areas))
|
||||
return list()
|
||||
|
||||
for(var/list/A as anything in grand_list_of_areas)
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in A)
|
||||
if(!T.check_density())
|
||||
turfs += T
|
||||
|
||||
if(turfs.len < minimum_free_space)
|
||||
continue // Not enough free space.
|
||||
return turfs
|
||||
|
||||
return list()
|
||||
|
||||
/datum/event2/event/proc/find_random_areas(list/specific_areas = list(), ignore_occupancy = FALSE)
|
||||
if(!LAZYLEN(specific_areas))
|
||||
specific_areas = global.the_station_areas.Copy()
|
||||
|
||||
var/list/area/grand_list_of_areas = get_all_existing_areas_of_types(specific_areas)
|
||||
. = list()
|
||||
for(var/area/A as anything in shuffle(grand_list_of_areas))
|
||||
if(A.forbid_events)
|
||||
continue
|
||||
if(!(A.z in get_location_z_levels()))
|
||||
continue
|
||||
if(!ignore_occupancy && is_area_occupied(A))
|
||||
continue // Occupied.
|
||||
. += A
|
||||
|
||||
|
||||
// Starts the event.
|
||||
/datum/event2/event/proc/execute()
|
||||
time_started = world.time
|
||||
|
||||
if(announce_delay_lower_bound)
|
||||
announce_delay = rand(announce_delay_lower_bound, announce_delay_upper_bound ? announce_delay_upper_bound : announce_delay_lower_bound)
|
||||
time_to_announce = world.time + announce_delay
|
||||
|
||||
if(start_delay_lower_bound)
|
||||
start_delay = rand(start_delay_lower_bound, start_delay_upper_bound ? start_delay_upper_bound : start_delay_lower_bound)
|
||||
time_to_start = world.time + start_delay
|
||||
|
||||
if(length_lower_bound)
|
||||
var/starting_point = time_to_start ? time_to_start : world.time
|
||||
length = rand(length_lower_bound, length_upper_bound ? length_upper_bound : length_lower_bound)
|
||||
time_to_end = starting_point + length
|
||||
|
||||
set_up()
|
||||
|
||||
// Called at the very end of the event's lifecycle, or when aborted.
|
||||
// Don't override this, use `end()` for cleanup instead.
|
||||
/datum/event2/event/proc/finish()
|
||||
finished = TRUE
|
||||
time_finished = world.time
|
||||
|
||||
// Called by admins wanting to stop an event immediately.
|
||||
/datum/event2/event/proc/abort()
|
||||
if(!announced)
|
||||
announce()
|
||||
if(!ended) // `end()` generally has cleanup procs, so call that.
|
||||
end()
|
||||
finish()
|
||||
|
||||
// Called by the GM processer.
|
||||
/datum/event2/event/process()
|
||||
// Handle announcement track.
|
||||
if(!announced && should_announce())
|
||||
announced = TRUE
|
||||
announce()
|
||||
|
||||
// Handle event track.
|
||||
if(!started)
|
||||
if(should_start())
|
||||
started = TRUE
|
||||
start()
|
||||
else
|
||||
wait_tick()
|
||||
|
||||
if(started && !ended)
|
||||
if(should_end())
|
||||
ended = TRUE
|
||||
end()
|
||||
else
|
||||
event_tick()
|
||||
|
||||
// In order to be finished, the event needs to end, and be announced.
|
||||
if(ended && announced)
|
||||
finish()
|
||||
|
||||
/datum/event2/event/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG))
|
||||
message_admins("[usr] has attempted to manipulate an event without sufficent privilages.")
|
||||
return
|
||||
|
||||
if(href_list["abort"])
|
||||
abort()
|
||||
message_admins("Event '[type]' was aborted by [usr.key].")
|
||||
|
||||
// SSgame_master.interact(usr) // To refresh the UI. // VOREStation Edit - We don't use SSgame_master yet.
|
||||
|
||||
/*
|
||||
* Procs to Override
|
||||
*/
|
||||
|
||||
// Override this for code to be ran before the event is started.
|
||||
/datum/event2/event/proc/set_up()
|
||||
|
||||
// Called every tick from the GM system, and determines if the announcement should happen.
|
||||
// Override this for special logic on when it should be announced, e.g. after `ended` is set to TRUE,
|
||||
// however be aware that the event cannot finish until this returns TRUE at some point.
|
||||
/datum/event2/event/proc/should_announce()
|
||||
if(!time_to_announce)
|
||||
return TRUE
|
||||
return time_to_announce <= world.time
|
||||
|
||||
// Override this for code that alerts the crew that the event is happening in some form, e.g. a centcom announcement or some other message.
|
||||
// If you want them to not know, you can just not override it.
|
||||
/datum/event2/event/proc/announce()
|
||||
|
||||
// Override for code that runs every few seconds, while the event is waiting for `should_start()` to return TRUE.
|
||||
// Note that events that have `should_start()` return TRUE at the start will never have this proc called.
|
||||
/datum/event2/event/proc/wait_tick()
|
||||
|
||||
// Called every tick from the GM system, and determines if the event should offically start.
|
||||
// Override this for special logic on when it should start.
|
||||
/datum/event2/event/proc/should_start()
|
||||
if(!time_to_start)
|
||||
return TRUE
|
||||
return time_to_start <= world.time
|
||||
|
||||
// Override this for code to do the actual event.
|
||||
/datum/event2/event/proc/start()
|
||||
|
||||
|
||||
// Override for code that runs every few seconds, while the event is waiting for `should_end()` to return TRUE.
|
||||
// Note that events that have `should_end()` return TRUE at the start will never have this proc called.
|
||||
/datum/event2/event/proc/event_tick()
|
||||
|
||||
|
||||
// Called every tick from the GM system, and determines if the event should end.
|
||||
// If this returns TRUE at the very start, then the event ends instantly and `tick()` will never be called.
|
||||
// Override this for special logic on when it should end, e.g. blob core has to die before event ends.
|
||||
/datum/event2/event/proc/should_end()
|
||||
if(!time_to_end)
|
||||
return TRUE
|
||||
return time_to_end <= world.time
|
||||
|
||||
// Override this for code to run when the event is over, e.g. cleanup.
|
||||
/datum/event2/event/proc/end()
|
||||
// This object holds the code that is needed to execute an event.
|
||||
// Code for judging whether doing that event is a good idea or not belongs inside its meta event object.
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Important: DO NOT `sleep()` in any of the procs here, or the GM will get stuck. Use callbacks insead.
|
||||
Also please don't use spawn(), but use callbacks instead.
|
||||
|
||||
Note that there is an important distinction between an event being ended, and an event being finished.
|
||||
- Ended is for when the actual event is over, regardless of whether an announcement happened or not.
|
||||
- Finished is for when both the event itself is over, and it was announced. The event will stop being
|
||||
processed after it is finished.
|
||||
|
||||
For an event to finish, it must have done two things:
|
||||
- Go through its entire cycle, of start() -> end(), and
|
||||
- Have the event be announced.
|
||||
If an event has ended, but the announcement didn't happen, the event will not be finished.
|
||||
This allows for events that have their announcement happen after the end itself.
|
||||
|
||||
*/
|
||||
|
||||
//
|
||||
/datum/event2/event
|
||||
var/announced = FALSE // Is set to TRUE when `announce()` is called by `process()`.
|
||||
var/started = FALSE // Is set to TRUE when `start()` is called by `process()`.
|
||||
var/ended = FALSE // Is set to TRUE when `end()` is called by `process()`.
|
||||
var/finished = FALSE // Is set to TRUE when `ended` and `announced` are TRUE.
|
||||
|
||||
// `world.time`s when this event started, and finished, for bookkeeping.
|
||||
var/time_started = null
|
||||
var/time_finished = null
|
||||
|
||||
// If these are set, the announcement will be delayed by a random time between the lower and upper bounds.
|
||||
// If the upper bound is not defined, then it will use the lower bound instead.
|
||||
// Note that this is independant of the event itself, so you can have the announcement happen long after the event ended.
|
||||
// This may not work if should_announce() is overrided.
|
||||
var/announce_delay_lower_bound = null
|
||||
var/announce_delay_upper_bound = null
|
||||
|
||||
// If these are set, the event will be delayed by a random time between the lower and upper bounds.
|
||||
// If the upper bound is not defined, then it will use the lower bound instead.
|
||||
// This may not work if should_start() is overrided.
|
||||
var/start_delay_lower_bound = null
|
||||
var/start_delay_upper_bound = null
|
||||
|
||||
// If these are set, the event will automatically end at a random time between the lower and upper bounds.
|
||||
// If the upper bound is not defined, then it will use the lower bound instead.
|
||||
// This may not work if should_end() is overrided.
|
||||
var/length_lower_bound = null
|
||||
var/length_upper_bound = null
|
||||
|
||||
// Set automatically, don't touch.
|
||||
var/time_to_start = null
|
||||
var/time_to_announce = null
|
||||
var/time_to_end = null
|
||||
|
||||
// These are also set automatically, and are provided for events to know what RNG decided for the various durations.
|
||||
var/start_delay = null
|
||||
var/announce_delay = null
|
||||
var/length = null
|
||||
|
||||
// Returns the name of where the event is taking place.
|
||||
// In the future this might be handy for off-station events.
|
||||
/datum/event2/event/proc/location_name()
|
||||
return station_name()
|
||||
|
||||
// Returns the z-levels that are involved with the event.
|
||||
// In the future this might be handy for off-station events.
|
||||
/datum/event2/event/proc/get_location_z_levels(space_only = FALSE)
|
||||
. = using_map.station_levels.Copy()
|
||||
if(space_only)
|
||||
for(var/z_level in .)
|
||||
if(is_planet_z_level(z_level))
|
||||
. -= z_level
|
||||
|
||||
|
||||
/datum/event2/event/proc/is_planet_z_level(z_level)
|
||||
var/datum/planet/P = LAZYACCESS(SSplanets.z_to_planet, z_level)
|
||||
if(!istype(P))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
// Returns a list of empty turfs in the same area.
|
||||
/datum/event2/event/proc/find_random_turfs(minimum_free_space = 5, list/specific_areas = list(), ignore_occupancy = FALSE)
|
||||
var/list/area/grand_list_of_areas = find_random_areas(specific_areas)
|
||||
|
||||
if(!LAZYLEN(grand_list_of_areas))
|
||||
return list()
|
||||
|
||||
for(var/list/A as anything in grand_list_of_areas)
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in A)
|
||||
if(!T.check_density())
|
||||
turfs += T
|
||||
|
||||
if(turfs.len < minimum_free_space)
|
||||
continue // Not enough free space.
|
||||
return turfs
|
||||
|
||||
return list()
|
||||
|
||||
/datum/event2/event/proc/find_random_areas(list/specific_areas = list(), ignore_occupancy = FALSE)
|
||||
if(!LAZYLEN(specific_areas))
|
||||
specific_areas = global.the_station_areas.Copy()
|
||||
|
||||
var/list/area/grand_list_of_areas = get_all_existing_areas_of_types(specific_areas)
|
||||
. = list()
|
||||
for(var/area/A as anything in shuffle(grand_list_of_areas))
|
||||
if(A.forbid_events)
|
||||
continue
|
||||
if(!(A.z in get_location_z_levels()))
|
||||
continue
|
||||
if(!ignore_occupancy && is_area_occupied(A))
|
||||
continue // Occupied.
|
||||
. += A
|
||||
|
||||
|
||||
// Starts the event.
|
||||
/datum/event2/event/proc/execute()
|
||||
time_started = world.time
|
||||
|
||||
if(announce_delay_lower_bound)
|
||||
announce_delay = rand(announce_delay_lower_bound, announce_delay_upper_bound ? announce_delay_upper_bound : announce_delay_lower_bound)
|
||||
time_to_announce = world.time + announce_delay
|
||||
|
||||
if(start_delay_lower_bound)
|
||||
start_delay = rand(start_delay_lower_bound, start_delay_upper_bound ? start_delay_upper_bound : start_delay_lower_bound)
|
||||
time_to_start = world.time + start_delay
|
||||
|
||||
if(length_lower_bound)
|
||||
var/starting_point = time_to_start ? time_to_start : world.time
|
||||
length = rand(length_lower_bound, length_upper_bound ? length_upper_bound : length_lower_bound)
|
||||
time_to_end = starting_point + length
|
||||
|
||||
set_up()
|
||||
|
||||
// Called at the very end of the event's lifecycle, or when aborted.
|
||||
// Don't override this, use `end()` for cleanup instead.
|
||||
/datum/event2/event/proc/finish()
|
||||
finished = TRUE
|
||||
time_finished = world.time
|
||||
|
||||
// Called by admins wanting to stop an event immediately.
|
||||
/datum/event2/event/proc/abort()
|
||||
if(!announced)
|
||||
announce()
|
||||
if(!ended) // `end()` generally has cleanup procs, so call that.
|
||||
end()
|
||||
finish()
|
||||
|
||||
// Called by the GM processer.
|
||||
/datum/event2/event/process()
|
||||
// Handle announcement track.
|
||||
if(!announced && should_announce())
|
||||
announced = TRUE
|
||||
announce()
|
||||
|
||||
// Handle event track.
|
||||
if(!started)
|
||||
if(should_start())
|
||||
started = TRUE
|
||||
start()
|
||||
else
|
||||
wait_tick()
|
||||
|
||||
if(started && !ended)
|
||||
if(should_end())
|
||||
ended = TRUE
|
||||
end()
|
||||
else
|
||||
event_tick()
|
||||
|
||||
// In order to be finished, the event needs to end, and be announced.
|
||||
if(ended && announced)
|
||||
finish()
|
||||
|
||||
/datum/event2/event/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG))
|
||||
message_admins("[usr] has attempted to manipulate an event without sufficent privilages.")
|
||||
return
|
||||
|
||||
if(href_list["abort"])
|
||||
abort()
|
||||
message_admins("Event '[type]' was aborted by [usr.key].")
|
||||
|
||||
// SSgame_master.interact(usr) // To refresh the UI. // VOREStation Edit - We don't use SSgame_master yet.
|
||||
|
||||
/*
|
||||
* Procs to Override
|
||||
*/
|
||||
|
||||
// Override this for code to be ran before the event is started.
|
||||
/datum/event2/event/proc/set_up()
|
||||
|
||||
// Called every tick from the GM system, and determines if the announcement should happen.
|
||||
// Override this for special logic on when it should be announced, e.g. after `ended` is set to TRUE,
|
||||
// however be aware that the event cannot finish until this returns TRUE at some point.
|
||||
/datum/event2/event/proc/should_announce()
|
||||
if(!time_to_announce)
|
||||
return TRUE
|
||||
return time_to_announce <= world.time
|
||||
|
||||
// Override this for code that alerts the crew that the event is happening in some form, e.g. a centcom announcement or some other message.
|
||||
// If you want them to not know, you can just not override it.
|
||||
/datum/event2/event/proc/announce()
|
||||
|
||||
// Override for code that runs every few seconds, while the event is waiting for `should_start()` to return TRUE.
|
||||
// Note that events that have `should_start()` return TRUE at the start will never have this proc called.
|
||||
/datum/event2/event/proc/wait_tick()
|
||||
|
||||
// Called every tick from the GM system, and determines if the event should offically start.
|
||||
// Override this for special logic on when it should start.
|
||||
/datum/event2/event/proc/should_start()
|
||||
if(!time_to_start)
|
||||
return TRUE
|
||||
return time_to_start <= world.time
|
||||
|
||||
// Override this for code to do the actual event.
|
||||
/datum/event2/event/proc/start()
|
||||
|
||||
|
||||
// Override for code that runs every few seconds, while the event is waiting for `should_end()` to return TRUE.
|
||||
// Note that events that have `should_end()` return TRUE at the start will never have this proc called.
|
||||
/datum/event2/event/proc/event_tick()
|
||||
|
||||
|
||||
// Called every tick from the GM system, and determines if the event should end.
|
||||
// If this returns TRUE at the very start, then the event ends instantly and `tick()` will never be called.
|
||||
// Override this for special logic on when it should end, e.g. blob core has to die before event ends.
|
||||
/datum/event2/event/proc/should_end()
|
||||
if(!time_to_end)
|
||||
return TRUE
|
||||
return time_to_end <= world.time
|
||||
|
||||
// Override this for code to run when the event is over, e.g. cleanup.
|
||||
/datum/event2/event/proc/end()
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/datum/event2/meta/shipping_error
|
||||
name = "shipping error"
|
||||
departments = list(DEPARTMENT_CARGO)
|
||||
chaos = -10 // A helpful event.
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/shipping_error
|
||||
|
||||
/datum/event2/meta/shipping_error/get_weight()
|
||||
return (metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm)) * 30
|
||||
|
||||
/datum/event2/event/shipping_error/start()
|
||||
var/datum/supply_order/O = new /datum/supply_order()
|
||||
O.ordernum = SSsupply.ordernum
|
||||
O.object = SSsupply.supply_pack[pick(SSsupply.supply_pack)]
|
||||
O.ordered_by = random_name(pick(MALE,FEMALE), species = "Human")
|
||||
SSsupply.shoppinglist += O
|
||||
/datum/event2/meta/shipping_error
|
||||
name = "shipping error"
|
||||
departments = list(DEPARTMENT_CARGO)
|
||||
chaos = -10 // A helpful event.
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/shipping_error
|
||||
|
||||
/datum/event2/meta/shipping_error/get_weight()
|
||||
return (metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm)) * 30
|
||||
|
||||
/datum/event2/event/shipping_error/start()
|
||||
var/datum/supply_order/O = new /datum/supply_order()
|
||||
O.ordernum = SSsupply.ordernum
|
||||
O.object = SSsupply.supply_pack[pick(SSsupply.supply_pack)]
|
||||
O.ordered_by = random_name(pick(MALE,FEMALE), species = "Human")
|
||||
SSsupply.shoppinglist += O
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
/datum/event2/meta/manifest_malfunction
|
||||
name = "manifest_malfunction"
|
||||
departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/manifest_malfunction
|
||||
|
||||
/datum/event2/meta/manifest_malfunction/get_weight()
|
||||
var/security = metric.count_people_in_department(DEPARTMENT_SECURITY)
|
||||
|
||||
if(!security || !data_core)
|
||||
return 0
|
||||
|
||||
var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain)
|
||||
var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC)
|
||||
var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (synths + security + command) // So they don't get counted twice.
|
||||
|
||||
return (security * 10) + (synths * 20) + (command * 20) + (everyone * 5)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/manifest_malfunction
|
||||
announce_delay_lower_bound = 5 MINUTES
|
||||
announce_delay_upper_bound = 10 MINUTES
|
||||
var/records_to_delete = 2
|
||||
var/record_class_to_delete = null
|
||||
|
||||
/datum/event2/event/manifest_malfunction/set_up()
|
||||
record_class_to_delete = pickweight(list("medical" = 10, "security" = 30))
|
||||
|
||||
/datum/event2/event/manifest_malfunction/announce()
|
||||
if(prob(30))
|
||||
var/message = null
|
||||
var/author = null
|
||||
var/rng = rand(1, 2)
|
||||
switch(rng)
|
||||
if(1)
|
||||
author = "Data Breach Alert"
|
||||
message = "The [record_class_to_delete] record database has suffered from an attack by one or more hackers. \
|
||||
They appear to have wiped several records, before disconnecting."
|
||||
if(2)
|
||||
author = "Downtime Alert"
|
||||
message = "The [record_class_to_delete] record database server has suffered a hardware failure, and is no longer functional. \
|
||||
A temporary replacement server has been activated, containing recovered data from the main server. \
|
||||
A few records became corrupted, and could not be transferred."
|
||||
command_announcement.Announce(message, author)
|
||||
|
||||
/datum/event2/event/manifest_malfunction/start()
|
||||
for(var/i = 1 to records_to_delete)
|
||||
var/datum/data/record/R
|
||||
|
||||
switch(record_class_to_delete)
|
||||
if("security")
|
||||
R = safepick(data_core.security)
|
||||
|
||||
if("medical")
|
||||
R = safepick(data_core.medical)
|
||||
|
||||
if(R)
|
||||
log_debug("Manifest malfunction event is now deleting [R.fields["name"]]'s [record_class_to_delete] record.")
|
||||
qdel(R)
|
||||
/datum/event2/meta/manifest_malfunction
|
||||
name = "manifest_malfunction"
|
||||
departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/manifest_malfunction
|
||||
|
||||
/datum/event2/meta/manifest_malfunction/get_weight()
|
||||
var/security = metric.count_people_in_department(DEPARTMENT_SECURITY)
|
||||
|
||||
if(!security || !data_core)
|
||||
return 0
|
||||
|
||||
var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain)
|
||||
var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC)
|
||||
var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (synths + security + command) // So they don't get counted twice.
|
||||
|
||||
return (security * 10) + (synths * 20) + (command * 20) + (everyone * 5)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/manifest_malfunction
|
||||
announce_delay_lower_bound = 5 MINUTES
|
||||
announce_delay_upper_bound = 10 MINUTES
|
||||
var/records_to_delete = 2
|
||||
var/record_class_to_delete = null
|
||||
|
||||
/datum/event2/event/manifest_malfunction/set_up()
|
||||
record_class_to_delete = pickweight(list("medical" = 10, "security" = 30))
|
||||
|
||||
/datum/event2/event/manifest_malfunction/announce()
|
||||
if(prob(30))
|
||||
var/message = null
|
||||
var/author = null
|
||||
var/rng = rand(1, 2)
|
||||
switch(rng)
|
||||
if(1)
|
||||
author = "Data Breach Alert"
|
||||
message = "The [record_class_to_delete] record database has suffered from an attack by one or more hackers. \
|
||||
They appear to have wiped several records, before disconnecting."
|
||||
if(2)
|
||||
author = "Downtime Alert"
|
||||
message = "The [record_class_to_delete] record database server has suffered a hardware failure, and is no longer functional. \
|
||||
A temporary replacement server has been activated, containing recovered data from the main server. \
|
||||
A few records became corrupted, and could not be transferred."
|
||||
command_announcement.Announce(message, author)
|
||||
|
||||
/datum/event2/event/manifest_malfunction/start()
|
||||
for(var/i = 1 to records_to_delete)
|
||||
var/datum/data/record/R
|
||||
|
||||
switch(record_class_to_delete)
|
||||
if("security")
|
||||
R = safepick(data_core.security)
|
||||
|
||||
if("medical")
|
||||
R = safepick(data_core.medical)
|
||||
|
||||
if(R)
|
||||
log_debug("Manifest malfunction event is now deleting [R.fields["name"]]'s [record_class_to_delete] record.")
|
||||
qdel(R)
|
||||
|
||||
@@ -1,110 +1,110 @@
|
||||
/datum/event2/meta/money_hacker
|
||||
name = "money hacker"
|
||||
departments = list(DEPARTMENT_COMMAND)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/money_hacker
|
||||
|
||||
/datum/event2/meta/money_hacker/get_weight()
|
||||
var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain)
|
||||
|
||||
if(!command)
|
||||
return 0
|
||||
return 30 + (command * 20) + (all_money_accounts.len * 5)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/money_hacker
|
||||
length_lower_bound = 8 MINUTES
|
||||
length_upper_bound = 12 MINUTES
|
||||
var/datum/money_account/targeted_account = null
|
||||
|
||||
/datum/event2/event/money_hacker/set_up()
|
||||
if(LAZYLEN(all_money_accounts))
|
||||
targeted_account = pick(all_money_accounts)
|
||||
|
||||
if(!targeted_account)
|
||||
log_debug("Money hacker event could not find an account to hack. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/money_hacker/announce()
|
||||
var/message = "A brute force hack has been detected (in progress since [stationtime2text()]). The target of the attack is: Financial account #[targeted_account.account_number], \
|
||||
without intervention this attack will succeed in approximately 10 minutes. Required intervention: temporary suspension of affected accounts until the attack has ceased. \
|
||||
Notifications will be sent as updates occur."
|
||||
var/my_department = "[location_name()] Firewall Subroutines"
|
||||
|
||||
for(var/obj/machinery/message_server/MS in machines)
|
||||
if(!MS.active)
|
||||
continue
|
||||
MS.send_rc_message("Head of Personnel's Desk", my_department, "[message]<br>", "", "", 2)
|
||||
|
||||
// Nobody reads the requests consoles so lets use the radio as well.
|
||||
global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND)
|
||||
|
||||
/datum/event2/event/money_hacker/end()
|
||||
var/message = null
|
||||
if(targeted_account && !targeted_account.suspended) // Hacker wins.
|
||||
message = "The hack attempt has succeeded."
|
||||
hack_account(targeted_account)
|
||||
log_debug("Money hacker event managed to hack the targeted account.")
|
||||
|
||||
else // Crew wins.
|
||||
message = "The attack has ceased, the affected accounts can now be brought online."
|
||||
log_debug("Money hacker event failed to hack the targeted account due to intervention by the crew.")
|
||||
|
||||
var/my_department = "[location_name()] Firewall Subroutines"
|
||||
|
||||
for(var/obj/machinery/message_server/MS in machines)
|
||||
if(!MS.active) continue
|
||||
MS.send_rc_message("Head of Personnel's Desk", my_department, message, "", "", 2)
|
||||
|
||||
global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND)
|
||||
|
||||
/datum/event2/event/money_hacker/proc/hack_account(datum/money_account/A)
|
||||
// Subtract the money.
|
||||
var/lost = A.money * 0.8 + (rand(2,4) - 2) / 10
|
||||
A.money -= lost
|
||||
|
||||
// Create a taunting log entry.
|
||||
var/datum/transaction/T = new()
|
||||
T.target_name = pick(list(
|
||||
"",
|
||||
"yo brotha from anotha motha",
|
||||
"el Presidente",
|
||||
"chieF smackDowN",
|
||||
"Nobody"
|
||||
))
|
||||
|
||||
T.purpose = pick(list(
|
||||
"Ne$ ---ount fu%ds init*&lisat@*n",
|
||||
"PAY BACK YOUR MUM",
|
||||
"Funds withdrawal",
|
||||
"pWnAgE",
|
||||
"l33t hax",
|
||||
"liberationez",
|
||||
"Hit",
|
||||
"Nothing"
|
||||
))
|
||||
|
||||
T.amount = pick(list(
|
||||
"",
|
||||
"([rand(0,99999)])",
|
||||
"alla money",
|
||||
"9001$",
|
||||
"HOLLA HOLLA GET DOLLA",
|
||||
"([lost])",
|
||||
"69,420t"
|
||||
))
|
||||
|
||||
var/date1 = "1 January 1970" // Unix epoch.
|
||||
var/date2 = "[num2text(rand(1,31))] [pick("January","February","March","April","May","June","July","August","September","October","November","December")], [rand(1000,3000)]"
|
||||
T.date = pick("", current_date_string, date1, date2,"Nowhen")
|
||||
|
||||
var/time1 = rand(0, 99999999)
|
||||
var/time2 = "[round(time1 / 36000)+12]:[(time1 / 600 % 60) < 10 ? add_zero(time1 / 600 % 60, 1) : time1 / 600 % 60]"
|
||||
T.time = pick("", stationtime2text(), time2, "Never")
|
||||
|
||||
T.source_terminal = pick("","[pick("Biesel","New Gibson")] GalaxyNet Terminal #[rand(111,999)]","your mums place","nantrasen high CommanD","Angessa's Pearl","Nowhere")
|
||||
|
||||
A.transaction_log.Add(T)
|
||||
/datum/event2/meta/money_hacker
|
||||
name = "money hacker"
|
||||
departments = list(DEPARTMENT_COMMAND)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/money_hacker
|
||||
|
||||
/datum/event2/meta/money_hacker/get_weight()
|
||||
var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain)
|
||||
|
||||
if(!command)
|
||||
return 0
|
||||
return 30 + (command * 20) + (all_money_accounts.len * 5)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/money_hacker
|
||||
length_lower_bound = 8 MINUTES
|
||||
length_upper_bound = 12 MINUTES
|
||||
var/datum/money_account/targeted_account = null
|
||||
|
||||
/datum/event2/event/money_hacker/set_up()
|
||||
if(LAZYLEN(all_money_accounts))
|
||||
targeted_account = pick(all_money_accounts)
|
||||
|
||||
if(!targeted_account)
|
||||
log_debug("Money hacker event could not find an account to hack. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/money_hacker/announce()
|
||||
var/message = "A brute force hack has been detected (in progress since [stationtime2text()]). The target of the attack is: Financial account #[targeted_account.account_number], \
|
||||
without intervention this attack will succeed in approximately 10 minutes. Required intervention: temporary suspension of affected accounts until the attack has ceased. \
|
||||
Notifications will be sent as updates occur."
|
||||
var/my_department = "[location_name()] Firewall Subroutines"
|
||||
|
||||
for(var/obj/machinery/message_server/MS in machines)
|
||||
if(!MS.active)
|
||||
continue
|
||||
MS.send_rc_message("Head of Personnel's Desk", my_department, "[message]<br>", "", "", 2)
|
||||
|
||||
// Nobody reads the requests consoles so lets use the radio as well.
|
||||
global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND)
|
||||
|
||||
/datum/event2/event/money_hacker/end()
|
||||
var/message = null
|
||||
if(targeted_account && !targeted_account.suspended) // Hacker wins.
|
||||
message = "The hack attempt has succeeded."
|
||||
hack_account(targeted_account)
|
||||
log_debug("Money hacker event managed to hack the targeted account.")
|
||||
|
||||
else // Crew wins.
|
||||
message = "The attack has ceased, the affected accounts can now be brought online."
|
||||
log_debug("Money hacker event failed to hack the targeted account due to intervention by the crew.")
|
||||
|
||||
var/my_department = "[location_name()] Firewall Subroutines"
|
||||
|
||||
for(var/obj/machinery/message_server/MS in machines)
|
||||
if(!MS.active) continue
|
||||
MS.send_rc_message("Head of Personnel's Desk", my_department, message, "", "", 2)
|
||||
|
||||
global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND)
|
||||
|
||||
/datum/event2/event/money_hacker/proc/hack_account(datum/money_account/A)
|
||||
// Subtract the money.
|
||||
var/lost = A.money * 0.8 + (rand(2,4) - 2) / 10
|
||||
A.money -= lost
|
||||
|
||||
// Create a taunting log entry.
|
||||
var/datum/transaction/T = new()
|
||||
T.target_name = pick(list(
|
||||
"",
|
||||
"yo brotha from anotha motha",
|
||||
"el Presidente",
|
||||
"chieF smackDowN",
|
||||
"Nobody"
|
||||
))
|
||||
|
||||
T.purpose = pick(list(
|
||||
"Ne$ ---ount fu%ds init*&lisat@*n",
|
||||
"PAY BACK YOUR MUM",
|
||||
"Funds withdrawal",
|
||||
"pWnAgE",
|
||||
"l33t hax",
|
||||
"liberationez",
|
||||
"Hit",
|
||||
"Nothing"
|
||||
))
|
||||
|
||||
T.amount = pick(list(
|
||||
"",
|
||||
"([rand(0,99999)])",
|
||||
"alla money",
|
||||
"9001$",
|
||||
"HOLLA HOLLA GET DOLLA",
|
||||
"([lost])",
|
||||
"69,420t"
|
||||
))
|
||||
|
||||
var/date1 = "1 January 1970" // Unix epoch.
|
||||
var/date2 = "[num2text(rand(1,31))] [pick("January","February","March","April","May","June","July","August","September","October","November","December")], [rand(1000,3000)]"
|
||||
T.date = pick("", current_date_string, date1, date2,"Nowhen")
|
||||
|
||||
var/time1 = rand(0, 99999999)
|
||||
var/time2 = "[round(time1 / 36000)+12]:[(time1 / 600 % 60) < 10 ? add_zero(time1 / 600 % 60, 1) : time1 / 600 % 60]"
|
||||
T.time = pick("", stationtime2text(), time2, "Never")
|
||||
|
||||
T.source_terminal = pick("","[pick("Biesel","New Gibson")] GalaxyNet Terminal #[rand(111,999)]","your mums place","nantrasen high CommanD","Angessa's Pearl","Nowhere")
|
||||
|
||||
A.transaction_log.Add(T)
|
||||
|
||||
@@ -1,96 +1,96 @@
|
||||
/datum/event2/meta/raise_funds
|
||||
name = "local funding drive"
|
||||
enabled = FALSE // There isn't really any suitable way for the crew to generate thalers right now, if that gets fixed feel free to turn this event on.
|
||||
departments = list(DEPARTMENT_COMMAND, DEPARTMENT_CARGO)
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/raise_funds
|
||||
|
||||
/datum/event2/meta/raise_funds/get_weight()
|
||||
var/command = metric.count_people_in_department(DEPARTMENT_COMMAND)
|
||||
if(!command) // Need someone to read the centcom message.
|
||||
return 0
|
||||
|
||||
var/cargo = metric.count_people_in_department(DEPARTMENT_CARGO)
|
||||
return (command * 20) + (cargo * 20)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/raise_funds
|
||||
length_lower_bound = 30 MINUTES
|
||||
length_upper_bound = 45 MINUTES
|
||||
var/money_at_start = 0
|
||||
|
||||
/datum/event2/event/raise_funds/announce()
|
||||
var/message = "Due to [pick("recent", "unfortunate", "possible future")] budget \
|
||||
[pick("changes", "issues")], in-system stations are now advised to increase funding income."
|
||||
|
||||
send_command_report("Budget Advisement", message)
|
||||
|
||||
/datum/event2/event/raise_funds/start()
|
||||
// Note that the event remembers the amount of money when it started. If an issue develops where people try to scam centcom by
|
||||
// taking out loads of money before the event, then depositing it back in after the event fires, feel free to make this check for
|
||||
// roundstart money instead.
|
||||
money_at_start = count_money()
|
||||
log_debug("Funding Drive event logged a sum of [money_at_start] thalers in all station accounts at the start of the event.")
|
||||
|
||||
/datum/event2/event/raise_funds/end()
|
||||
var/money_at_end = count_money()
|
||||
log_debug("Funding Drive event logged a sum of [money_at_end] thalers in all station accounts at the end of the event, compared \
|
||||
to [money_at_start] thalers. A difference of [money_at_end / money_at_start] was calculated.")
|
||||
|
||||
// A number above 1 indicates money was made, while below 1 does the opposite.
|
||||
var/budget_shift = money_at_end / money_at_start
|
||||
|
||||
// Centcom will say different things based on if they gained or lost money.
|
||||
var/message = null
|
||||
switch(budget_shift)
|
||||
if(0 to 0.02) // Abyssmal response.
|
||||
message = "We are very interested in learning where [round(money_at_start, 1000)] thaler went in \
|
||||
just half an hour. We highly recommend rectifying this issue before the end of the shift, otherwise a \
|
||||
discussion regarding your future employment prospects will occur.<br><br>\
|
||||
Your facility's current balance of requisition tokens has been revoked."
|
||||
SSsupply.points = 0
|
||||
log_debug("Funding Drive event ended with an abyssmal response, and the loss of all cargo points.")
|
||||
|
||||
if(0.02 to 0.98) // Bad response.
|
||||
message = "We're very disappointed that \the [location_name()] has ran a deficit since our request. \
|
||||
As such, we will be taking away some requisition tokens to cover the cost of operating your facility."
|
||||
var/points_lost = round(SSsupply.points * rand(0.5, 0.8))
|
||||
SSsupply.points -= points_lost
|
||||
log_debug("Funding Drive event ended with a bad response, and [points_lost] cargo points was taken away.")
|
||||
|
||||
if(0.98 to 1.02) // Neutral response.
|
||||
message = "It is unfortunate that \the [location_name()]'s finances remain at a standstill, however \
|
||||
that is still preferred over having a decicit. We hope that in the future, your facility will be able to be \
|
||||
more profitable."
|
||||
log_debug("Funding Drive event ended with a neutral response.")
|
||||
|
||||
if(1.02 to INFINITY) // Good response.
|
||||
message = "We appreciate the efforts made by \the [location_name()] to run at a surplus. \
|
||||
Together, along with the other facilities present in the [using_map.starsys_name] system, \
|
||||
the company is expected to meet the quota.<br><br>\
|
||||
We will allocate additional requisition tokens for the cargo department as a reward."
|
||||
|
||||
// If cargo is ever made to use station funds instead of cargo points, then a new kind of reward will be needed.
|
||||
// Otherwise it would be weird for centcom to go 'thanks for not spending money, your reward is money to spend'.
|
||||
var/point_reward = rand(100, 200)
|
||||
SSsupply.points += point_reward
|
||||
log_debug("Funding Drive event ended with a good response and a bonus of [point_reward] cargo points.")
|
||||
|
||||
send_command_report("Budget Followup", message)
|
||||
|
||||
|
||||
|
||||
// Returns the sum of the station account and all the departmental accounts.
|
||||
/datum/event2/event/raise_funds/proc/count_money()
|
||||
. = 0
|
||||
. += station_account.money
|
||||
for(var/i = 1 to SSjob.department_datums.len)
|
||||
var/datum/money_account/account = LAZYACCESS(department_accounts, SSjob.department_datums[i])
|
||||
if(istype(account))
|
||||
. += account.money
|
||||
|
||||
/datum/event2/event/raise_funds/proc/send_command_report(title, message)
|
||||
post_comm_message(title, message)
|
||||
to_world(span("danger", "New [using_map.company_name] Update available at all communication consoles."))
|
||||
SEND_SOUND(world, 'sound/AI/commandreport.ogg')
|
||||
/datum/event2/meta/raise_funds
|
||||
name = "local funding drive"
|
||||
enabled = FALSE // There isn't really any suitable way for the crew to generate thalers right now, if that gets fixed feel free to turn this event on.
|
||||
departments = list(DEPARTMENT_COMMAND, DEPARTMENT_CARGO)
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/raise_funds
|
||||
|
||||
/datum/event2/meta/raise_funds/get_weight()
|
||||
var/command = metric.count_people_in_department(DEPARTMENT_COMMAND)
|
||||
if(!command) // Need someone to read the centcom message.
|
||||
return 0
|
||||
|
||||
var/cargo = metric.count_people_in_department(DEPARTMENT_CARGO)
|
||||
return (command * 20) + (cargo * 20)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/raise_funds
|
||||
length_lower_bound = 30 MINUTES
|
||||
length_upper_bound = 45 MINUTES
|
||||
var/money_at_start = 0
|
||||
|
||||
/datum/event2/event/raise_funds/announce()
|
||||
var/message = "Due to [pick("recent", "unfortunate", "possible future")] budget \
|
||||
[pick("changes", "issues")], in-system stations are now advised to increase funding income."
|
||||
|
||||
send_command_report("Budget Advisement", message)
|
||||
|
||||
/datum/event2/event/raise_funds/start()
|
||||
// Note that the event remembers the amount of money when it started. If an issue develops where people try to scam centcom by
|
||||
// taking out loads of money before the event, then depositing it back in after the event fires, feel free to make this check for
|
||||
// roundstart money instead.
|
||||
money_at_start = count_money()
|
||||
log_debug("Funding Drive event logged a sum of [money_at_start] thalers in all station accounts at the start of the event.")
|
||||
|
||||
/datum/event2/event/raise_funds/end()
|
||||
var/money_at_end = count_money()
|
||||
log_debug("Funding Drive event logged a sum of [money_at_end] thalers in all station accounts at the end of the event, compared \
|
||||
to [money_at_start] thalers. A difference of [money_at_end / money_at_start] was calculated.")
|
||||
|
||||
// A number above 1 indicates money was made, while below 1 does the opposite.
|
||||
var/budget_shift = money_at_end / money_at_start
|
||||
|
||||
// Centcom will say different things based on if they gained or lost money.
|
||||
var/message = null
|
||||
switch(budget_shift)
|
||||
if(0 to 0.02) // Abyssmal response.
|
||||
message = "We are very interested in learning where [round(money_at_start, 1000)] thaler went in \
|
||||
just half an hour. We highly recommend rectifying this issue before the end of the shift, otherwise a \
|
||||
discussion regarding your future employment prospects will occur.<br><br>\
|
||||
Your facility's current balance of requisition tokens has been revoked."
|
||||
SSsupply.points = 0
|
||||
log_debug("Funding Drive event ended with an abyssmal response, and the loss of all cargo points.")
|
||||
|
||||
if(0.02 to 0.98) // Bad response.
|
||||
message = "We're very disappointed that \the [location_name()] has ran a deficit since our request. \
|
||||
As such, we will be taking away some requisition tokens to cover the cost of operating your facility."
|
||||
var/points_lost = round(SSsupply.points * rand(0.5, 0.8))
|
||||
SSsupply.points -= points_lost
|
||||
log_debug("Funding Drive event ended with a bad response, and [points_lost] cargo points was taken away.")
|
||||
|
||||
if(0.98 to 1.02) // Neutral response.
|
||||
message = "It is unfortunate that \the [location_name()]'s finances remain at a standstill, however \
|
||||
that is still preferred over having a decicit. We hope that in the future, your facility will be able to be \
|
||||
more profitable."
|
||||
log_debug("Funding Drive event ended with a neutral response.")
|
||||
|
||||
if(1.02 to INFINITY) // Good response.
|
||||
message = "We appreciate the efforts made by \the [location_name()] to run at a surplus. \
|
||||
Together, along with the other facilities present in the [using_map.starsys_name] system, \
|
||||
the company is expected to meet the quota.<br><br>\
|
||||
We will allocate additional requisition tokens for the cargo department as a reward."
|
||||
|
||||
// If cargo is ever made to use station funds instead of cargo points, then a new kind of reward will be needed.
|
||||
// Otherwise it would be weird for centcom to go 'thanks for not spending money, your reward is money to spend'.
|
||||
var/point_reward = rand(100, 200)
|
||||
SSsupply.points += point_reward
|
||||
log_debug("Funding Drive event ended with a good response and a bonus of [point_reward] cargo points.")
|
||||
|
||||
send_command_report("Budget Followup", message)
|
||||
|
||||
|
||||
|
||||
// Returns the sum of the station account and all the departmental accounts.
|
||||
/datum/event2/event/raise_funds/proc/count_money()
|
||||
. = 0
|
||||
. += station_account.money
|
||||
for(var/i = 1 to SSjob.department_datums.len)
|
||||
var/datum/money_account/account = LAZYACCESS(department_accounts, SSjob.department_datums[i])
|
||||
if(istype(account))
|
||||
. += account.money
|
||||
|
||||
/datum/event2/event/raise_funds/proc/send_command_report(title, message)
|
||||
post_comm_message(title, message)
|
||||
to_world(span("danger", "New [using_map.company_name] Update available at all communication consoles."))
|
||||
SEND_SOUND(world, 'sound/AI/commandreport.ogg')
|
||||
|
||||
@@ -1,105 +1,105 @@
|
||||
/datum/event2/meta/airlock_failure
|
||||
event_class = "airlock failure"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_MEDICAL)
|
||||
chaos = 15
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/airlock_failure
|
||||
var/needs_medical = FALSE
|
||||
|
||||
/datum/event2/meta/airlock_failure/emag
|
||||
name = "airlock failure - emag"
|
||||
event_type = /datum/event2/event/airlock_failure/emag
|
||||
|
||||
/datum/event2/meta/airlock_failure/door_crush
|
||||
name = "airlock failure - crushing"
|
||||
event_type = /datum/event2/event/airlock_failure/door_crush
|
||||
needs_medical = TRUE
|
||||
|
||||
/datum/event2/meta/airlock_failure/shock
|
||||
name = "airlock failure - shock"
|
||||
chaos = 30
|
||||
event_type = /datum/event2/event/airlock_failure/shock
|
||||
needs_medical = TRUE
|
||||
|
||||
|
||||
/datum/event2/meta/airlock_failure/get_weight()
|
||||
var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING)
|
||||
|
||||
// Synths are good both for fixing the doors and getting blamed for the doors zapping people.
|
||||
var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC)
|
||||
if(!engineering && !synths) // Nobody's around to fix the door.
|
||||
return 0
|
||||
|
||||
// Medical might be needed for some of the more violent airlock failures.
|
||||
var/medical = metric.count_people_in_department(DEPARTMENT_MEDICAL)
|
||||
if(!medical && needs_medical)
|
||||
return 0
|
||||
|
||||
return (engineering * 20) + (medical * 20) + (synths * 20)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/airlock_failure
|
||||
announce_delay_lower_bound = 20 SECONDS
|
||||
announce_delay_upper_bound = 40 SECONDS
|
||||
var/announce_odds = 0
|
||||
var/doors_to_break = 1
|
||||
var/list/affected_areas = list()
|
||||
|
||||
/datum/event2/event/airlock_failure/emag
|
||||
announce_odds = 10 // To make people wonder if the emagged door was from a baddie or from this event.
|
||||
doors_to_break = 2 // Replacing emagged doors really sucks for engineering so don't overdo it.
|
||||
|
||||
/datum/event2/event/airlock_failure/door_crush
|
||||
announce_odds = 30
|
||||
doors_to_break = 5
|
||||
|
||||
/datum/event2/event/airlock_failure/shock
|
||||
announce_odds = 70
|
||||
|
||||
/datum/event2/event/airlock_failure/start()
|
||||
var/list/areas = find_random_areas()
|
||||
if(!LAZYLEN(areas))
|
||||
log_debug("Airlock Failure event could not find any areas. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
while(areas.len)
|
||||
var/area/area = pick(areas)
|
||||
areas -= area
|
||||
|
||||
for(var/obj/machinery/door/airlock/door in area.contents)
|
||||
if(can_break_door(door))
|
||||
addtimer(CALLBACK(src, PROC_REF(break_door), door), 1) // Emagging proc is actually a blocking proc and that's bad for the ticker.
|
||||
door.visible_message(span("danger", "\The [door]'s panel sparks!"))
|
||||
playsound(door, "sparks", 50, 1)
|
||||
log_debug("Airlock Failure event has broken \the [door] airlock in [area].")
|
||||
affected_areas |= area
|
||||
doors_to_break--
|
||||
|
||||
if(doors_to_break <= 0)
|
||||
return
|
||||
|
||||
/datum/event2/event/airlock_failure/announce()
|
||||
if(prob(announce_odds))
|
||||
command_announcement.Announce("An electrical issue has been detected in the airlock grid at [english_list(affected_areas)]. \
|
||||
Some airlocks may require servicing by a qualified technician.", "Electrical Alert")
|
||||
|
||||
|
||||
/datum/event2/event/airlock_failure/proc/can_break_door(obj/machinery/door/airlock/door)
|
||||
if(istype(door, /obj/machinery/door/airlock/lift))
|
||||
return FALSE
|
||||
return door.arePowerSystemsOn()
|
||||
|
||||
// Override this for door busting.
|
||||
/datum/event2/event/airlock_failure/proc/break_door(obj/machinery/door/airlock/door)
|
||||
|
||||
/datum/event2/event/airlock_failure/emag/break_door(obj/machinery/door/airlock/door)
|
||||
door.emag_act(1)
|
||||
|
||||
/datum/event2/event/airlock_failure/door_crush/break_door(obj/machinery/door/airlock/door)
|
||||
door.normalspeed = FALSE
|
||||
door.safe = FALSE
|
||||
|
||||
/datum/event2/event/airlock_failure/shock/break_door(obj/machinery/door/airlock/door)
|
||||
door.electrify(-1)
|
||||
/datum/event2/meta/airlock_failure
|
||||
event_class = "airlock failure"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_MEDICAL)
|
||||
chaos = 15
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/airlock_failure
|
||||
var/needs_medical = FALSE
|
||||
|
||||
/datum/event2/meta/airlock_failure/emag
|
||||
name = "airlock failure - emag"
|
||||
event_type = /datum/event2/event/airlock_failure/emag
|
||||
|
||||
/datum/event2/meta/airlock_failure/door_crush
|
||||
name = "airlock failure - crushing"
|
||||
event_type = /datum/event2/event/airlock_failure/door_crush
|
||||
needs_medical = TRUE
|
||||
|
||||
/datum/event2/meta/airlock_failure/shock
|
||||
name = "airlock failure - shock"
|
||||
chaos = 30
|
||||
event_type = /datum/event2/event/airlock_failure/shock
|
||||
needs_medical = TRUE
|
||||
|
||||
|
||||
/datum/event2/meta/airlock_failure/get_weight()
|
||||
var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING)
|
||||
|
||||
// Synths are good both for fixing the doors and getting blamed for the doors zapping people.
|
||||
var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC)
|
||||
if(!engineering && !synths) // Nobody's around to fix the door.
|
||||
return 0
|
||||
|
||||
// Medical might be needed for some of the more violent airlock failures.
|
||||
var/medical = metric.count_people_in_department(DEPARTMENT_MEDICAL)
|
||||
if(!medical && needs_medical)
|
||||
return 0
|
||||
|
||||
return (engineering * 20) + (medical * 20) + (synths * 20)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/airlock_failure
|
||||
announce_delay_lower_bound = 20 SECONDS
|
||||
announce_delay_upper_bound = 40 SECONDS
|
||||
var/announce_odds = 0
|
||||
var/doors_to_break = 1
|
||||
var/list/affected_areas = list()
|
||||
|
||||
/datum/event2/event/airlock_failure/emag
|
||||
announce_odds = 10 // To make people wonder if the emagged door was from a baddie or from this event.
|
||||
doors_to_break = 2 // Replacing emagged doors really sucks for engineering so don't overdo it.
|
||||
|
||||
/datum/event2/event/airlock_failure/door_crush
|
||||
announce_odds = 30
|
||||
doors_to_break = 5
|
||||
|
||||
/datum/event2/event/airlock_failure/shock
|
||||
announce_odds = 70
|
||||
|
||||
/datum/event2/event/airlock_failure/start()
|
||||
var/list/areas = find_random_areas()
|
||||
if(!LAZYLEN(areas))
|
||||
log_debug("Airlock Failure event could not find any areas. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
while(areas.len)
|
||||
var/area/area = pick(areas)
|
||||
areas -= area
|
||||
|
||||
for(var/obj/machinery/door/airlock/door in area.contents)
|
||||
if(can_break_door(door))
|
||||
addtimer(CALLBACK(src, PROC_REF(break_door), door), 1) // Emagging proc is actually a blocking proc and that's bad for the ticker.
|
||||
door.visible_message(span("danger", "\The [door]'s panel sparks!"))
|
||||
playsound(door, "sparks", 50, 1)
|
||||
log_debug("Airlock Failure event has broken \the [door] airlock in [area].")
|
||||
affected_areas |= area
|
||||
doors_to_break--
|
||||
|
||||
if(doors_to_break <= 0)
|
||||
return
|
||||
|
||||
/datum/event2/event/airlock_failure/announce()
|
||||
if(prob(announce_odds))
|
||||
command_announcement.Announce("An electrical issue has been detected in the airlock grid at [english_list(affected_areas)]. \
|
||||
Some airlocks may require servicing by a qualified technician.", "Electrical Alert")
|
||||
|
||||
|
||||
/datum/event2/event/airlock_failure/proc/can_break_door(obj/machinery/door/airlock/door)
|
||||
if(istype(door, /obj/machinery/door/airlock/lift))
|
||||
return FALSE
|
||||
return door.arePowerSystemsOn()
|
||||
|
||||
// Override this for door busting.
|
||||
/datum/event2/event/airlock_failure/proc/break_door(obj/machinery/door/airlock/door)
|
||||
|
||||
/datum/event2/event/airlock_failure/emag/break_door(obj/machinery/door/airlock/door)
|
||||
door.emag_act(1)
|
||||
|
||||
/datum/event2/event/airlock_failure/door_crush/break_door(obj/machinery/door/airlock/door)
|
||||
door.normalspeed = FALSE
|
||||
door.safe = FALSE
|
||||
|
||||
/datum/event2/event/airlock_failure/shock/break_door(obj/machinery/door/airlock/door)
|
||||
door.electrify(-1)
|
||||
|
||||
@@ -1,160 +1,160 @@
|
||||
/datum/event2/meta/blob
|
||||
name = "blob"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL)
|
||||
chaos = 30
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT
|
||||
event_class = "blob" // This makes it so there is no potential for multiple blob events of different types happening in the same round.
|
||||
event_type = /datum/event2/event/blob
|
||||
// In the distant future, if a mechanical skill system were to come into being, these vars could be replaced with skill checks so off duty people could count.
|
||||
var/required_fighters = 2 // Fighters refers to engineering OR security.
|
||||
var/required_support = 1 // Support refers to doctors AND roboticists, depending on fighter composition.
|
||||
|
||||
/datum/event2/meta/blob/hard
|
||||
name = "harder blob"
|
||||
chaos = 40
|
||||
event_type = /datum/event2/event/blob/hard_blob
|
||||
required_fighters = 3
|
||||
|
||||
/datum/event2/meta/blob/multi_blob
|
||||
name = "multi blob"
|
||||
chaos = 60
|
||||
event_type = /datum/event2/event/blob/multi_blob
|
||||
required_fighters = 4
|
||||
required_support = 2
|
||||
|
||||
// For bussing only.
|
||||
/datum/event2/meta/blob/omni_blob
|
||||
name = "omni blob"
|
||||
chaos = 200
|
||||
event_type = /datum/event2/event/blob/omni_blob
|
||||
enabled = FALSE
|
||||
|
||||
/datum/event2/meta/blob/get_weight()
|
||||
// Count the 'fighters'.
|
||||
var/list/engineers = metric.get_people_in_department(DEPARTMENT_ENGINEERING)
|
||||
var/list/security = metric.get_people_in_department(DEPARTMENT_SECURITY)
|
||||
|
||||
if(engineers.len + security.len < required_fighters)
|
||||
return 0
|
||||
|
||||
// Now count the 'support'.
|
||||
var/list/medical = metric.get_people_in_department(DEPARTMENT_MEDICAL)
|
||||
var/need_medical = FALSE
|
||||
|
||||
var/list/robotics = metric.get_people_with_job(/datum/job/roboticist)
|
||||
var/need_robotics = FALSE
|
||||
|
||||
// Determine what kind of support might be needed.
|
||||
for(var/mob/living/L in engineers|security)
|
||||
if(L.isSynthetic())
|
||||
need_robotics = TRUE
|
||||
else
|
||||
need_medical = TRUE
|
||||
|
||||
// Medical is more important than robotics, since robits tend to not suffer slow deaths if there isn't a roboticist.
|
||||
if(medical.len < required_support && need_medical)
|
||||
return 0
|
||||
|
||||
// Engineers can sometimes fill in as robotics. This is done in the interest of the event having a chance of not being super rare.
|
||||
// In the uncertain future, a mechanical skill system check could replace this check here.
|
||||
if(robotics.len + engineers.len < required_support && need_robotics)
|
||||
return 0
|
||||
|
||||
var/fighter_weight = (engineers.len + security.len) * 20
|
||||
var/support_weight = (medical.len + robotics.len) * 10 // Not counting engineers as support so they don't cause 30 weight each.
|
||||
var/chaos_weight = chaos / 2 // Chaos is added as a weight in order to make more chaotic variants be preferred if they are allowed to be picked.
|
||||
|
||||
return fighter_weight + support_weight + chaos_weight
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/blob
|
||||
announce_delay_lower_bound = 1 MINUTE
|
||||
announce_delay_upper_bound = 5 MINUTES
|
||||
// This could be made into a GLOB accessible list for reuse if needed.
|
||||
var/list/area/excluded = list(
|
||||
/area/submap,
|
||||
/area/shuttle,
|
||||
/area/crew_quarters,
|
||||
/area/holodeck,
|
||||
/area/engineering/engine_room
|
||||
)
|
||||
var/list/open_turfs = list()
|
||||
var/spawn_blob_type = /obj/structure/blob/core/random_medium
|
||||
var/number_of_blobs = 1
|
||||
var/list/blobs = list() // A list containing weakrefs to blob cores created. Weakrefs mean this event won't interfere with qdel.
|
||||
|
||||
/datum/event2/event/blob/hard_blob
|
||||
spawn_blob_type = /obj/structure/blob/core/random_hard
|
||||
|
||||
/datum/event2/event/blob/multi_blob
|
||||
spawn_blob_type = /obj/structure/blob/core/random_hard // Lethargic blobs are boring.
|
||||
number_of_blobs = 2
|
||||
|
||||
// For adminbus only.
|
||||
/datum/event2/event/blob/omni_blob
|
||||
number_of_blobs = 16 // Someday maybe we can get this to specifically spawn every blob.
|
||||
|
||||
/datum/event2/event/blob/set_up()
|
||||
open_turfs = find_random_turfs(5 + number_of_blobs)
|
||||
|
||||
if(!open_turfs.len)
|
||||
log_debug("Blob infestation event: Giving up after failure to find blob spots.")
|
||||
abort()
|
||||
|
||||
/datum/event2/event/blob/start()
|
||||
for(var/i = 1 to number_of_blobs)
|
||||
var/turf/T = pick(open_turfs)
|
||||
var/obj/structure/blob/core/new_blob = new spawn_blob_type(T)
|
||||
blobs += WEAKREF(new_blob)
|
||||
open_turfs -= T // So we can't put two cores on the same tile if doing multiblob.
|
||||
log_debug("Spawned [new_blob.overmind.blob_type.name] blob at [get_area(new_blob)].")
|
||||
|
||||
/datum/event2/event/blob/should_end()
|
||||
for(var/datum/weakref/weakref as anything in blobs)
|
||||
if(weakref.resolve()) // If the weakref is resolvable, that means the blob hasn't been deleted yet.
|
||||
return FALSE
|
||||
return TRUE // Only end if all blobs die.
|
||||
|
||||
// Normally this does nothing, but is useful if aborted by an admin.
|
||||
/datum/event2/event/blob/end()
|
||||
for(var/datum/weakref/weakref as anything in blobs)
|
||||
var/obj/structure/blob/core/B = weakref.resolve()
|
||||
if(istype(B))
|
||||
qdel(B)
|
||||
|
||||
/datum/event2/event/blob/announce()
|
||||
if(!ended) // Don't announce if the blobs die early.
|
||||
var/danger_level = 0
|
||||
var/list/blob_type_names = list()
|
||||
var/multiblob = FALSE
|
||||
for(var/datum/weakref/weakref as anything in blobs)
|
||||
var/obj/structure/blob/core/B = weakref.resolve()
|
||||
if(!istype(B))
|
||||
continue
|
||||
var/datum/blob_type/blob_type = B.overmind.blob_type
|
||||
|
||||
blob_type_names += blob_type.name
|
||||
if(danger_level > blob_type.difficulty) // The highest difficulty is used, if multiple blobs are present.
|
||||
danger_level = blob_type.difficulty
|
||||
|
||||
if(blob_type_names.len > 1) // More than one blob is harder.
|
||||
danger_level += blob_type_names.len
|
||||
multiblob = TRUE
|
||||
|
||||
var/list/lines = list()
|
||||
lines += "Confirmed outbreak of level [7 + danger_level] biohazard[multiblob ? "s": ""] \
|
||||
aboard [location_name()]. All personnel must contain the outbreak."
|
||||
|
||||
if(danger_level >= BLOB_DIFFICULTY_MEDIUM) // Tell them what kind of blob it is if it's tough.
|
||||
lines += "The biohazard[multiblob ? "s have": " has"] been identified as [english_list(blob_type_names)]."
|
||||
|
||||
if(danger_level >= BLOB_DIFFICULTY_HARD) // If it's really hard then tell them where it is so the response occurs faster.
|
||||
var/turf/T = open_turfs[1]
|
||||
var/area/A = T.loc
|
||||
lines += "[multiblob ? "It is": "They are"] suspected to have originated from \the [A]."
|
||||
|
||||
if(danger_level >= BLOB_DIFFICULTY_SUPERHARD)
|
||||
lines += "Extreme caution is advised."
|
||||
|
||||
command_announcement.Announce(lines.Join("\n"), "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg')
|
||||
/datum/event2/meta/blob
|
||||
name = "blob"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL)
|
||||
chaos = 30
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT
|
||||
event_class = "blob" // This makes it so there is no potential for multiple blob events of different types happening in the same round.
|
||||
event_type = /datum/event2/event/blob
|
||||
// In the distant future, if a mechanical skill system were to come into being, these vars could be replaced with skill checks so off duty people could count.
|
||||
var/required_fighters = 2 // Fighters refers to engineering OR security.
|
||||
var/required_support = 1 // Support refers to doctors AND roboticists, depending on fighter composition.
|
||||
|
||||
/datum/event2/meta/blob/hard
|
||||
name = "harder blob"
|
||||
chaos = 40
|
||||
event_type = /datum/event2/event/blob/hard_blob
|
||||
required_fighters = 3
|
||||
|
||||
/datum/event2/meta/blob/multi_blob
|
||||
name = "multi blob"
|
||||
chaos = 60
|
||||
event_type = /datum/event2/event/blob/multi_blob
|
||||
required_fighters = 4
|
||||
required_support = 2
|
||||
|
||||
// For bussing only.
|
||||
/datum/event2/meta/blob/omni_blob
|
||||
name = "omni blob"
|
||||
chaos = 200
|
||||
event_type = /datum/event2/event/blob/omni_blob
|
||||
enabled = FALSE
|
||||
|
||||
/datum/event2/meta/blob/get_weight()
|
||||
// Count the 'fighters'.
|
||||
var/list/engineers = metric.get_people_in_department(DEPARTMENT_ENGINEERING)
|
||||
var/list/security = metric.get_people_in_department(DEPARTMENT_SECURITY)
|
||||
|
||||
if(engineers.len + security.len < required_fighters)
|
||||
return 0
|
||||
|
||||
// Now count the 'support'.
|
||||
var/list/medical = metric.get_people_in_department(DEPARTMENT_MEDICAL)
|
||||
var/need_medical = FALSE
|
||||
|
||||
var/list/robotics = metric.get_people_with_job(/datum/job/roboticist)
|
||||
var/need_robotics = FALSE
|
||||
|
||||
// Determine what kind of support might be needed.
|
||||
for(var/mob/living/L in engineers|security)
|
||||
if(L.isSynthetic())
|
||||
need_robotics = TRUE
|
||||
else
|
||||
need_medical = TRUE
|
||||
|
||||
// Medical is more important than robotics, since robits tend to not suffer slow deaths if there isn't a roboticist.
|
||||
if(medical.len < required_support && need_medical)
|
||||
return 0
|
||||
|
||||
// Engineers can sometimes fill in as robotics. This is done in the interest of the event having a chance of not being super rare.
|
||||
// In the uncertain future, a mechanical skill system check could replace this check here.
|
||||
if(robotics.len + engineers.len < required_support && need_robotics)
|
||||
return 0
|
||||
|
||||
var/fighter_weight = (engineers.len + security.len) * 20
|
||||
var/support_weight = (medical.len + robotics.len) * 10 // Not counting engineers as support so they don't cause 30 weight each.
|
||||
var/chaos_weight = chaos / 2 // Chaos is added as a weight in order to make more chaotic variants be preferred if they are allowed to be picked.
|
||||
|
||||
return fighter_weight + support_weight + chaos_weight
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/blob
|
||||
announce_delay_lower_bound = 1 MINUTE
|
||||
announce_delay_upper_bound = 5 MINUTES
|
||||
// This could be made into a GLOB accessible list for reuse if needed.
|
||||
var/list/area/excluded = list(
|
||||
/area/submap,
|
||||
/area/shuttle,
|
||||
/area/crew_quarters,
|
||||
/area/holodeck,
|
||||
/area/engineering/engine_room
|
||||
)
|
||||
var/list/open_turfs = list()
|
||||
var/spawn_blob_type = /obj/structure/blob/core/random_medium
|
||||
var/number_of_blobs = 1
|
||||
var/list/blobs = list() // A list containing weakrefs to blob cores created. Weakrefs mean this event won't interfere with qdel.
|
||||
|
||||
/datum/event2/event/blob/hard_blob
|
||||
spawn_blob_type = /obj/structure/blob/core/random_hard
|
||||
|
||||
/datum/event2/event/blob/multi_blob
|
||||
spawn_blob_type = /obj/structure/blob/core/random_hard // Lethargic blobs are boring.
|
||||
number_of_blobs = 2
|
||||
|
||||
// For adminbus only.
|
||||
/datum/event2/event/blob/omni_blob
|
||||
number_of_blobs = 16 // Someday maybe we can get this to specifically spawn every blob.
|
||||
|
||||
/datum/event2/event/blob/set_up()
|
||||
open_turfs = find_random_turfs(5 + number_of_blobs)
|
||||
|
||||
if(!open_turfs.len)
|
||||
log_debug("Blob infestation event: Giving up after failure to find blob spots.")
|
||||
abort()
|
||||
|
||||
/datum/event2/event/blob/start()
|
||||
for(var/i = 1 to number_of_blobs)
|
||||
var/turf/T = pick(open_turfs)
|
||||
var/obj/structure/blob/core/new_blob = new spawn_blob_type(T)
|
||||
blobs += WEAKREF(new_blob)
|
||||
open_turfs -= T // So we can't put two cores on the same tile if doing multiblob.
|
||||
log_debug("Spawned [new_blob.overmind.blob_type.name] blob at [get_area(new_blob)].")
|
||||
|
||||
/datum/event2/event/blob/should_end()
|
||||
for(var/datum/weakref/weakref as anything in blobs)
|
||||
if(weakref.resolve()) // If the weakref is resolvable, that means the blob hasn't been deleted yet.
|
||||
return FALSE
|
||||
return TRUE // Only end if all blobs die.
|
||||
|
||||
// Normally this does nothing, but is useful if aborted by an admin.
|
||||
/datum/event2/event/blob/end()
|
||||
for(var/datum/weakref/weakref as anything in blobs)
|
||||
var/obj/structure/blob/core/B = weakref.resolve()
|
||||
if(istype(B))
|
||||
qdel(B)
|
||||
|
||||
/datum/event2/event/blob/announce()
|
||||
if(!ended) // Don't announce if the blobs die early.
|
||||
var/danger_level = 0
|
||||
var/list/blob_type_names = list()
|
||||
var/multiblob = FALSE
|
||||
for(var/datum/weakref/weakref as anything in blobs)
|
||||
var/obj/structure/blob/core/B = weakref.resolve()
|
||||
if(!istype(B))
|
||||
continue
|
||||
var/datum/blob_type/blob_type = B.overmind.blob_type
|
||||
|
||||
blob_type_names += blob_type.name
|
||||
if(danger_level > blob_type.difficulty) // The highest difficulty is used, if multiple blobs are present.
|
||||
danger_level = blob_type.difficulty
|
||||
|
||||
if(blob_type_names.len > 1) // More than one blob is harder.
|
||||
danger_level += blob_type_names.len
|
||||
multiblob = TRUE
|
||||
|
||||
var/list/lines = list()
|
||||
lines += "Confirmed outbreak of level [7 + danger_level] biohazard[multiblob ? "s": ""] \
|
||||
aboard [location_name()]. All personnel must contain the outbreak."
|
||||
|
||||
if(danger_level >= BLOB_DIFFICULTY_MEDIUM) // Tell them what kind of blob it is if it's tough.
|
||||
lines += "The biohazard[multiblob ? "s have": " has"] been identified as [english_list(blob_type_names)]."
|
||||
|
||||
if(danger_level >= BLOB_DIFFICULTY_HARD) // If it's really hard then tell them where it is so the response occurs faster.
|
||||
var/turf/T = open_turfs[1]
|
||||
var/area/A = T.loc
|
||||
lines += "[multiblob ? "It is": "They are"] suspected to have originated from \the [A]."
|
||||
|
||||
if(danger_level >= BLOB_DIFFICULTY_SUPERHARD)
|
||||
lines += "Extreme caution is advised."
|
||||
|
||||
command_announcement.Announce(lines.Join("\n"), "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg')
|
||||
|
||||
@@ -1,90 +1,90 @@
|
||||
/datum/event2/meta/brand_intelligence
|
||||
name = "vending machine malware"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/brand_intelligence
|
||||
|
||||
/datum/event2/meta/brand_intelligence/get_weight()
|
||||
return 10 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/brand_intelligence
|
||||
var/malware_spread_cooldown = 30 SECONDS
|
||||
|
||||
var/list/vending_machines = list() // List of venders that can potentially be infected.
|
||||
var/list/infected_vending_machines = list() // List of venders that have been infected.
|
||||
var/obj/machinery/vending/vender_zero = null // The first vending machine infected. If that one gets fixed, all other infected machines will be cured.
|
||||
var/last_malware_spread_time = null
|
||||
|
||||
/datum/event2/event/brand_intelligence/set_up()
|
||||
for(var/obj/machinery/vending/V in machines)
|
||||
if(!(V.z in using_map.station_levels))
|
||||
continue
|
||||
vending_machines += V
|
||||
|
||||
if(!vending_machines.len)
|
||||
log_debug("Could not find any vending machines on station Z levels. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
vender_zero = pick(vending_machines)
|
||||
|
||||
/datum/event2/event/brand_intelligence/announce()
|
||||
if(prob(90))
|
||||
command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard \the [location_name()], \
|
||||
which appears to transmit to nearby vendors. The original infected machine is believed to be \a [vender_zero].", "Vendor Service Alert")
|
||||
|
||||
/datum/event2/event/brand_intelligence/start()
|
||||
infect_vender(vender_zero)
|
||||
|
||||
/datum/event2/event/brand_intelligence/event_tick()
|
||||
if(last_malware_spread_time + malware_spread_cooldown > world.time)
|
||||
return // Still on cooldown.
|
||||
last_malware_spread_time = world.time
|
||||
|
||||
if(vending_machines.len)
|
||||
var/next_victim = pick(vending_machines)
|
||||
infect_vender(next_victim)
|
||||
|
||||
// Every time Vender Zero infects, it says something.
|
||||
vender_zero.speak(pick("Try our aggressive new marketing strategies!", \
|
||||
"You should buy products to feed your lifestyle obsession!", \
|
||||
"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."))
|
||||
|
||||
|
||||
/datum/event2/event/brand_intelligence/should_end()
|
||||
if(!vending_machines.len)
|
||||
return TRUE
|
||||
if(!can_propagate(vender_zero))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/event2/event/brand_intelligence/end()
|
||||
if(can_propagate(vender_zero)) // The crew failed and all the machines are infected!
|
||||
return
|
||||
// Otherwise Vender Zero was taken out in some form.
|
||||
if(vender_zero)
|
||||
vender_zero.visible_message(span("notice", "\The [vender_zero]'s network activity light flickers wildly \
|
||||
for a few seconds as a small screen reads: 'Rolling out firmware reset to networked machines'."))
|
||||
for(var/obj/machinery/vending/vender in infected_vending_machines)
|
||||
cure_vender(vender)
|
||||
|
||||
/datum/event2/event/brand_intelligence/proc/infect_vender(obj/machinery/vending/V)
|
||||
vending_machines -= V
|
||||
infected_vending_machines += V
|
||||
V.shut_up = FALSE
|
||||
V.shoot_inventory = TRUE
|
||||
|
||||
/datum/event2/event/brand_intelligence/proc/cure_vender(obj/machinery/vending/V)
|
||||
infected_vending_machines -= V
|
||||
V.shut_up = TRUE
|
||||
V.shoot_inventory = FALSE
|
||||
|
||||
/datum/event2/event/brand_intelligence/proc/can_propagate(obj/machinery/vending/V)
|
||||
return V && V.shut_up == FALSE
|
||||
/datum/event2/meta/brand_intelligence
|
||||
name = "vending machine malware"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/brand_intelligence
|
||||
|
||||
/datum/event2/meta/brand_intelligence/get_weight()
|
||||
return 10 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/brand_intelligence
|
||||
var/malware_spread_cooldown = 30 SECONDS
|
||||
|
||||
var/list/vending_machines = list() // List of venders that can potentially be infected.
|
||||
var/list/infected_vending_machines = list() // List of venders that have been infected.
|
||||
var/obj/machinery/vending/vender_zero = null // The first vending machine infected. If that one gets fixed, all other infected machines will be cured.
|
||||
var/last_malware_spread_time = null
|
||||
|
||||
/datum/event2/event/brand_intelligence/set_up()
|
||||
for(var/obj/machinery/vending/V in machines)
|
||||
if(!(V.z in using_map.station_levels))
|
||||
continue
|
||||
vending_machines += V
|
||||
|
||||
if(!vending_machines.len)
|
||||
log_debug("Could not find any vending machines on station Z levels. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
vender_zero = pick(vending_machines)
|
||||
|
||||
/datum/event2/event/brand_intelligence/announce()
|
||||
if(prob(90))
|
||||
command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard \the [location_name()], \
|
||||
which appears to transmit to nearby vendors. The original infected machine is believed to be \a [vender_zero].", "Vendor Service Alert")
|
||||
|
||||
/datum/event2/event/brand_intelligence/start()
|
||||
infect_vender(vender_zero)
|
||||
|
||||
/datum/event2/event/brand_intelligence/event_tick()
|
||||
if(last_malware_spread_time + malware_spread_cooldown > world.time)
|
||||
return // Still on cooldown.
|
||||
last_malware_spread_time = world.time
|
||||
|
||||
if(vending_machines.len)
|
||||
var/next_victim = pick(vending_machines)
|
||||
infect_vender(next_victim)
|
||||
|
||||
// Every time Vender Zero infects, it says something.
|
||||
vender_zero.speak(pick("Try our aggressive new marketing strategies!", \
|
||||
"You should buy products to feed your lifestyle obsession!", \
|
||||
"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."))
|
||||
|
||||
|
||||
/datum/event2/event/brand_intelligence/should_end()
|
||||
if(!vending_machines.len)
|
||||
return TRUE
|
||||
if(!can_propagate(vender_zero))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/event2/event/brand_intelligence/end()
|
||||
if(can_propagate(vender_zero)) // The crew failed and all the machines are infected!
|
||||
return
|
||||
// Otherwise Vender Zero was taken out in some form.
|
||||
if(vender_zero)
|
||||
vender_zero.visible_message(span("notice", "\The [vender_zero]'s network activity light flickers wildly \
|
||||
for a few seconds as a small screen reads: 'Rolling out firmware reset to networked machines'."))
|
||||
for(var/obj/machinery/vending/vender in infected_vending_machines)
|
||||
cure_vender(vender)
|
||||
|
||||
/datum/event2/event/brand_intelligence/proc/infect_vender(obj/machinery/vending/V)
|
||||
vending_machines -= V
|
||||
infected_vending_machines += V
|
||||
V.shut_up = FALSE
|
||||
V.shoot_inventory = TRUE
|
||||
|
||||
/datum/event2/event/brand_intelligence/proc/cure_vender(obj/machinery/vending/V)
|
||||
infected_vending_machines -= V
|
||||
V.shut_up = TRUE
|
||||
V.shoot_inventory = FALSE
|
||||
|
||||
/datum/event2/event/brand_intelligence/proc/can_propagate(obj/machinery/vending/V)
|
||||
return V && V.shut_up == FALSE
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
/datum/event2/meta/camera_damage
|
||||
name = "random camera damage"
|
||||
departments = list(DEPARTMENT_SYNTHETIC, DEPARTMENT_ENGINEERING)
|
||||
chaos = 5
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/camera_damage
|
||||
|
||||
/datum/event2/meta/camera_damage/get_weight()
|
||||
return 30 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) + (metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 40)
|
||||
|
||||
/datum/event2/event/camera_damage
|
||||
var/camera_range = 7
|
||||
|
||||
/datum/event2/event/camera_damage/start()
|
||||
var/obj/machinery/camera/C = acquire_random_camera()
|
||||
if(!C)
|
||||
return
|
||||
|
||||
for(var/obj/machinery/camera/cam in range(camera_range, C))
|
||||
if(is_valid_camera(cam))
|
||||
cam.wires.cut(WIRE_MAIN_POWER1)
|
||||
if(prob(25))
|
||||
cam.wires.cut(WIRE_CAM_ALARM)
|
||||
|
||||
/datum/event2/event/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5)
|
||||
if(!cameranet.cameras.len)
|
||||
return
|
||||
if(!remaining_attempts)
|
||||
return
|
||||
|
||||
var/obj/machinery/camera/C = pick(cameranet.cameras)
|
||||
if(is_valid_camera(C))
|
||||
return C
|
||||
// It is very important to use --var and not var-- for recursive calls, as var-- will cause an infinite loop.
|
||||
return acquire_random_camera(--remaining_attempts)
|
||||
|
||||
/datum/event2/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C)
|
||||
// Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access
|
||||
var/turf/T = get_turf(C)
|
||||
/datum/event2/meta/camera_damage
|
||||
name = "random camera damage"
|
||||
departments = list(DEPARTMENT_SYNTHETIC, DEPARTMENT_ENGINEERING)
|
||||
chaos = 5
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/camera_damage
|
||||
|
||||
/datum/event2/meta/camera_damage/get_weight()
|
||||
return 30 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) + (metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 40)
|
||||
|
||||
/datum/event2/event/camera_damage
|
||||
var/camera_range = 7
|
||||
|
||||
/datum/event2/event/camera_damage/start()
|
||||
var/obj/machinery/camera/C = acquire_random_camera()
|
||||
if(!C)
|
||||
return
|
||||
|
||||
for(var/obj/machinery/camera/cam in range(camera_range, C))
|
||||
if(is_valid_camera(cam))
|
||||
cam.wires.cut(WIRE_MAIN_POWER1)
|
||||
if(prob(25))
|
||||
cam.wires.cut(WIRE_CAM_ALARM)
|
||||
|
||||
/datum/event2/event/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5)
|
||||
if(!cameranet.cameras.len)
|
||||
return
|
||||
if(!remaining_attempts)
|
||||
return
|
||||
|
||||
var/obj/machinery/camera/C = pick(cameranet.cameras)
|
||||
if(is_valid_camera(C))
|
||||
return C
|
||||
// It is very important to use --var and not var-- for recursive calls, as var-- will cause an infinite loop.
|
||||
return acquire_random_camera(--remaining_attempts)
|
||||
|
||||
/datum/event2/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C)
|
||||
// Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access
|
||||
var/turf/T = get_turf(C)
|
||||
return T && C?.can_use() && istype(C.loc, /turf) && (T.z in using_map.player_levels)
|
||||
@@ -1,26 +1,26 @@
|
||||
//
|
||||
// This event chooses a random canister on player levels and breaks it, releasing its contents!
|
||||
//
|
||||
|
||||
/datum/event2/meta/canister_leak
|
||||
name = "canister leak"
|
||||
departments = list(DEPARTMENT_ENGINEERING)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/canister_leak
|
||||
|
||||
/datum/event2/meta/canister_leak/get_weight()
|
||||
return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 30
|
||||
|
||||
/datum/event2/event/canister_leak/start()
|
||||
// List of all non-destroyed canisters on station levels
|
||||
var/list/all_canisters = list()
|
||||
for(var/obj/machinery/portable_atmospherics/canister/C in machines)
|
||||
if(!C.destroyed && (C.z in using_map.station_levels) && C.air_contents.total_moles >= MOLES_CELLSTANDARD)
|
||||
all_canisters += C
|
||||
var/obj/machinery/portable_atmospherics/canister/C = pick(all_canisters)
|
||||
log_debug("canister_leak event: Canister [C] ([C.x],[C.y],[C.z]) destroyed.")
|
||||
C.health = 0
|
||||
C.healthcheck()
|
||||
|
||||
//
|
||||
// This event chooses a random canister on player levels and breaks it, releasing its contents!
|
||||
//
|
||||
|
||||
/datum/event2/meta/canister_leak
|
||||
name = "canister leak"
|
||||
departments = list(DEPARTMENT_ENGINEERING)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/canister_leak
|
||||
|
||||
/datum/event2/meta/canister_leak/get_weight()
|
||||
return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 30
|
||||
|
||||
/datum/event2/event/canister_leak/start()
|
||||
// List of all non-destroyed canisters on station levels
|
||||
var/list/all_canisters = list()
|
||||
for(var/obj/machinery/portable_atmospherics/canister/C in machines)
|
||||
if(!C.destroyed && (C.z in using_map.station_levels) && C.air_contents.total_moles >= MOLES_CELLSTANDARD)
|
||||
all_canisters += C
|
||||
var/obj/machinery/portable_atmospherics/canister/C = pick(all_canisters)
|
||||
log_debug("canister_leak event: Canister [C] ([C.x],[C.y],[C.z]) destroyed.")
|
||||
C.health = 0
|
||||
C.healthcheck()
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/datum/event2/meta/dust
|
||||
name = "dust"
|
||||
departments = list(DEPARTMENT_ENGINEERING)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/dust
|
||||
|
||||
/datum/event2/meta/dust/get_weight()
|
||||
return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/dust/announce()
|
||||
if(prob(33))
|
||||
command_announcement.Announce("Dust has been detected on a collision course with \the [location_name()].")
|
||||
|
||||
/datum/event2/event/dust/start()
|
||||
dust_swarm("norm")
|
||||
/datum/event2/meta/dust
|
||||
name = "dust"
|
||||
departments = list(DEPARTMENT_ENGINEERING)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/dust
|
||||
|
||||
/datum/event2/meta/dust/get_weight()
|
||||
return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/dust/announce()
|
||||
if(prob(33))
|
||||
command_announcement.Announce("Dust has been detected on a collision course with \the [location_name()].")
|
||||
|
||||
/datum/event2/event/dust/start()
|
||||
dust_swarm("norm")
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
/datum/event2/meta/gas_leak
|
||||
name = "gas leak"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SYNTHETIC)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/gas_leak
|
||||
|
||||
/datum/event2/meta/gas_leak/get_weight()
|
||||
// Synthetics are counted in higher value because they can wirelessly connect to alarms.
|
||||
var/engineering_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10
|
||||
var/synthetic_factor = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 30
|
||||
return (15 + engineering_factor + synthetic_factor) / (times_ran + 1)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/gas_leak
|
||||
var/potential_gas_choices = list("carbon_dioxide", "nitrous_oxide", "phoron", "volatile_fuel")
|
||||
var/chosen_gas = null
|
||||
var/turf/chosen_turf = null
|
||||
|
||||
/datum/event2/event/gas_leak/set_up()
|
||||
chosen_gas = pick(potential_gas_choices)
|
||||
|
||||
var/list/turfs = find_random_turfs()
|
||||
if(!turfs.len)
|
||||
log_debug("Gas Leak event failed to find any available turfs to leak into. Aborting.")
|
||||
abort()
|
||||
return
|
||||
chosen_turf = pick(turfs)
|
||||
|
||||
/datum/event2/event/gas_leak/announce()
|
||||
if(chosen_turf)
|
||||
command_announcement.Announce("Warning, hazardous [lowertext(gas_data.name[chosen_gas])] gas leak detected in \the [chosen_turf.loc], evacuate the area.", "Hazard Alert")
|
||||
|
||||
/datum/event2/event/gas_leak/start()
|
||||
// Okay, time to actually put the gas in the room!
|
||||
// TODO - Would be nice to break a waste pipe perhaps?
|
||||
// TODO - Maybe having it released from a single point and thus causing airflow to blow stuff around
|
||||
|
||||
// Fow now just add a bunch of it to the air
|
||||
|
||||
var/datum/gas_mixture/air_contents = new
|
||||
air_contents.temperature = T20C + rand(-50, 50)
|
||||
air_contents.gas[chosen_gas] = 10 * MOLES_CELLSTANDARD
|
||||
chosen_turf.assume_air(air_contents)
|
||||
/datum/event2/meta/gas_leak
|
||||
name = "gas leak"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SYNTHETIC)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/gas_leak
|
||||
|
||||
/datum/event2/meta/gas_leak/get_weight()
|
||||
// Synthetics are counted in higher value because they can wirelessly connect to alarms.
|
||||
var/engineering_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10
|
||||
var/synthetic_factor = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 30
|
||||
return (15 + engineering_factor + synthetic_factor) / (times_ran + 1)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/gas_leak
|
||||
var/potential_gas_choices = list("carbon_dioxide", "nitrous_oxide", "phoron", "volatile_fuel")
|
||||
var/chosen_gas = null
|
||||
var/turf/chosen_turf = null
|
||||
|
||||
/datum/event2/event/gas_leak/set_up()
|
||||
chosen_gas = pick(potential_gas_choices)
|
||||
|
||||
var/list/turfs = find_random_turfs()
|
||||
if(!turfs.len)
|
||||
log_debug("Gas Leak event failed to find any available turfs to leak into. Aborting.")
|
||||
abort()
|
||||
return
|
||||
chosen_turf = pick(turfs)
|
||||
|
||||
/datum/event2/event/gas_leak/announce()
|
||||
if(chosen_turf)
|
||||
command_announcement.Announce("Warning, hazardous [lowertext(gas_data.name[chosen_gas])] gas leak detected in \the [chosen_turf.loc], evacuate the area.", "Hazard Alert")
|
||||
|
||||
/datum/event2/event/gas_leak/start()
|
||||
// Okay, time to actually put the gas in the room!
|
||||
// TODO - Would be nice to break a waste pipe perhaps?
|
||||
// TODO - Maybe having it released from a single point and thus causing airflow to blow stuff around
|
||||
|
||||
// Fow now just add a bunch of it to the air
|
||||
|
||||
var/datum/gas_mixture/air_contents = new
|
||||
air_contents.temperature = T20C + rand(-50, 50)
|
||||
air_contents.gas[chosen_gas] = 10 * MOLES_CELLSTANDARD
|
||||
chosen_turf.assume_air(air_contents)
|
||||
playsound(chosen_turf, 'sound/effects/smoke.ogg', 75, 1)
|
||||
@@ -1,50 +1,50 @@
|
||||
// New grid check event:
|
||||
// Very similar to the old one, power goes out in most of the station, however the new feature is the ability for engineering to
|
||||
// get power back on sooner, if they are able to reach a special machine and initiate a manual reboot. If no one is able to do so,
|
||||
// it will reboot itself after a few minutes, just like the old one. Bad things happen if there is no grid checker machine protecting
|
||||
// the powernet when this event fires.
|
||||
|
||||
/datum/event2/meta/grid_check
|
||||
name = "grid check"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/grid_check
|
||||
|
||||
// Having the turbines be way over their rated limit makes grid checks more likely.
|
||||
/datum/event2/meta/grid_check/proc/get_overpower()
|
||||
var/highest_overpower = 0
|
||||
for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines)
|
||||
var/overpower = max((turbine.effective_gen / turbine.max_power) - 1, 0)
|
||||
if(overpower > highest_overpower)
|
||||
highest_overpower = overpower
|
||||
return highest_overpower
|
||||
|
||||
/datum/event2/meta/grid_check/get_weight()
|
||||
var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10
|
||||
var/overpower_factor = 50 * get_overpower() // Will be 0 if not overloaded at all, and 50 if turbines are outputting twice as much as rated.
|
||||
return (20 + population_factor + overpower_factor) / (times_ran + 1)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/grid_check
|
||||
var/obj/machinery/power/generator/engine // The turbine that will send a power spike.
|
||||
|
||||
/datum/event2/event/grid_check/set_up()
|
||||
// Find the turbine being pushed the most.
|
||||
var/obj/machinery/power/generator/most_stressed_turbine = null
|
||||
for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines)
|
||||
if(!most_stressed_turbine)
|
||||
most_stressed_turbine = turbine
|
||||
else if(turbine.effective_gen > most_stressed_turbine.effective_gen)
|
||||
most_stressed_turbine = turbine
|
||||
engine = most_stressed_turbine
|
||||
|
||||
/datum/event2/event/grid_check/start()
|
||||
// This sets off a chain of events that lead to the actual grid check (or perhaps worse).
|
||||
// First, the Supermatter engine makes a power spike.
|
||||
if(engine)
|
||||
engine.power_spike()
|
||||
// After that, the engine checks if a grid checker exists on the same powernet, and if so, it triggers a blackout.
|
||||
// If not, lots of stuff breaks. See code/modules/power/generator.dm for that piece of code.
|
||||
// New grid check event:
|
||||
// Very similar to the old one, power goes out in most of the station, however the new feature is the ability for engineering to
|
||||
// get power back on sooner, if they are able to reach a special machine and initiate a manual reboot. If no one is able to do so,
|
||||
// it will reboot itself after a few minutes, just like the old one. Bad things happen if there is no grid checker machine protecting
|
||||
// the powernet when this event fires.
|
||||
|
||||
/datum/event2/meta/grid_check
|
||||
name = "grid check"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/grid_check
|
||||
|
||||
// Having the turbines be way over their rated limit makes grid checks more likely.
|
||||
/datum/event2/meta/grid_check/proc/get_overpower()
|
||||
var/highest_overpower = 0
|
||||
for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines)
|
||||
var/overpower = max((turbine.effective_gen / turbine.max_power) - 1, 0)
|
||||
if(overpower > highest_overpower)
|
||||
highest_overpower = overpower
|
||||
return highest_overpower
|
||||
|
||||
/datum/event2/meta/grid_check/get_weight()
|
||||
var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10
|
||||
var/overpower_factor = 50 * get_overpower() // Will be 0 if not overloaded at all, and 50 if turbines are outputting twice as much as rated.
|
||||
return (20 + population_factor + overpower_factor) / (times_ran + 1)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/grid_check
|
||||
var/obj/machinery/power/generator/engine // The turbine that will send a power spike.
|
||||
|
||||
/datum/event2/event/grid_check/set_up()
|
||||
// Find the turbine being pushed the most.
|
||||
var/obj/machinery/power/generator/most_stressed_turbine = null
|
||||
for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines)
|
||||
if(!most_stressed_turbine)
|
||||
most_stressed_turbine = turbine
|
||||
else if(turbine.effective_gen > most_stressed_turbine.effective_gen)
|
||||
most_stressed_turbine = turbine
|
||||
engine = most_stressed_turbine
|
||||
|
||||
/datum/event2/event/grid_check/start()
|
||||
// This sets off a chain of events that lead to the actual grid check (or perhaps worse).
|
||||
// First, the Supermatter engine makes a power spike.
|
||||
if(engine)
|
||||
engine.power_spike()
|
||||
// After that, the engine checks if a grid checker exists on the same powernet, and if so, it triggers a blackout.
|
||||
// If not, lots of stuff breaks. See code/modules/power/generator.dm for that piece of code.
|
||||
|
||||
@@ -1,83 +1,83 @@
|
||||
// This event gives the station an advance warning about meteors, so that they can prepare in various ways.
|
||||
|
||||
/datum/event2/meta/meteor_defense
|
||||
name = "meteor defense"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_CARGO)
|
||||
chaos = 50
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT
|
||||
event_class = "meteor defense"
|
||||
event_type = /datum/event2/event/meteor_defense
|
||||
|
||||
/datum/event2/meta/meteor_defense/get_weight()
|
||||
// Engineers count as 20.
|
||||
var/engineers = metric.count_people_in_department(DEPARTMENT_ENGINEERING)
|
||||
if(engineers < 3) // There -must- be at least three engineers for this to be possible.
|
||||
return 0
|
||||
|
||||
. = engineers * 20
|
||||
|
||||
// Cargo and AI/borgs count as 10.
|
||||
var/cargo = metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm)
|
||||
var/bots = metric.count_people_in_department(DEPARTMENT_SYNTHETIC)
|
||||
|
||||
. += (cargo + bots) * 10
|
||||
|
||||
|
||||
/datum/event2/event/meteor_defense
|
||||
start_delay_lower_bound = 10 MINUTES
|
||||
start_delay_upper_bound = 15 MINUTES
|
||||
var/soon_announced = FALSE
|
||||
var/direction = null // Actual dir used for which side the meteors come from.
|
||||
var/dir_text = null // Direction shown in the announcement.
|
||||
var/list/meteor_types = null
|
||||
var/waves = null // How many times to send meteors.
|
||||
var/last_wave_time = null // world.time of latest wave.
|
||||
var/wave_delay = 10 SECONDS
|
||||
var/wave_upper_bound = 8 // Max amount of meteors per wave.
|
||||
var/wave_lower_bound = 4 // Min amount.
|
||||
|
||||
/datum/event2/event/meteor_defense/proc/set_meteor_types()
|
||||
meteor_types = meteors_threatening.Copy()
|
||||
|
||||
/datum/event2/event/meteor_defense/set_up()
|
||||
direction = pick(cardinal) // alldirs doesn't work with current meteor code unfortunately.
|
||||
waves = rand(3, 6)
|
||||
switch(direction)
|
||||
if(NORTH)
|
||||
dir_text = "aft" // For some reason this is needed.
|
||||
if(SOUTH)
|
||||
dir_text = "fore"
|
||||
if(EAST)
|
||||
dir_text = "port"
|
||||
if(WEST)
|
||||
dir_text = "starboard"
|
||||
set_meteor_types()
|
||||
|
||||
/datum/event2/event/meteor_defense/announce()
|
||||
var/announcement = "Meteors are expected to approach from the [dir_text] side, in approximately [DisplayTimeText(time_to_start - world.time, 60)]."
|
||||
command_announcement.Announce(announcement, "Meteor Alert", new_sound = 'sound/AI/meteors.ogg')
|
||||
|
||||
/datum/event2/event/meteor_defense/wait_tick()
|
||||
if(!soon_announced)
|
||||
if((time_to_start - world.time) <= 5 MINUTES)
|
||||
soon_announced = TRUE
|
||||
var/announcement = "The incoming meteors are expected to approach from the [dir_text] side. \
|
||||
ETA to arrival is approximately [DisplayTimeText(time_to_start - world.time, 60)]."
|
||||
command_announcement.Announce(announcement, "Meteor Alert - Update")
|
||||
|
||||
/datum/event2/event/meteor_defense/start()
|
||||
command_announcement.Announce("Incoming meteors approach from \the [dir_text] side!", "Meteor Alert - Update")
|
||||
|
||||
/datum/event2/event/meteor_defense/event_tick()
|
||||
if(world.time > last_wave_time + wave_delay)
|
||||
last_wave_time = world.time
|
||||
waves--
|
||||
message_admins("[waves] more wave\s of meteors remain.")
|
||||
// Dir is reversed because the direction describes where meteors are going, not what side it's gonna hit.
|
||||
spawn_meteors(rand(wave_upper_bound, wave_lower_bound), meteor_types, reverse_dir[direction])
|
||||
|
||||
/datum/event2/event/meteor_defense/should_end()
|
||||
return waves <= 0
|
||||
|
||||
/datum/event2/event/meteor_defense/end()
|
||||
command_announcement.Announce("\The [location_name()] will clear the incoming meteors in a moment.", "Meteor Alert - Update")
|
||||
// This event gives the station an advance warning about meteors, so that they can prepare in various ways.
|
||||
|
||||
/datum/event2/meta/meteor_defense
|
||||
name = "meteor defense"
|
||||
departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_CARGO)
|
||||
chaos = 50
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT
|
||||
event_class = "meteor defense"
|
||||
event_type = /datum/event2/event/meteor_defense
|
||||
|
||||
/datum/event2/meta/meteor_defense/get_weight()
|
||||
// Engineers count as 20.
|
||||
var/engineers = metric.count_people_in_department(DEPARTMENT_ENGINEERING)
|
||||
if(engineers < 3) // There -must- be at least three engineers for this to be possible.
|
||||
return 0
|
||||
|
||||
. = engineers * 20
|
||||
|
||||
// Cargo and AI/borgs count as 10.
|
||||
var/cargo = metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm)
|
||||
var/bots = metric.count_people_in_department(DEPARTMENT_SYNTHETIC)
|
||||
|
||||
. += (cargo + bots) * 10
|
||||
|
||||
|
||||
/datum/event2/event/meteor_defense
|
||||
start_delay_lower_bound = 10 MINUTES
|
||||
start_delay_upper_bound = 15 MINUTES
|
||||
var/soon_announced = FALSE
|
||||
var/direction = null // Actual dir used for which side the meteors come from.
|
||||
var/dir_text = null // Direction shown in the announcement.
|
||||
var/list/meteor_types = null
|
||||
var/waves = null // How many times to send meteors.
|
||||
var/last_wave_time = null // world.time of latest wave.
|
||||
var/wave_delay = 10 SECONDS
|
||||
var/wave_upper_bound = 8 // Max amount of meteors per wave.
|
||||
var/wave_lower_bound = 4 // Min amount.
|
||||
|
||||
/datum/event2/event/meteor_defense/proc/set_meteor_types()
|
||||
meteor_types = meteors_threatening.Copy()
|
||||
|
||||
/datum/event2/event/meteor_defense/set_up()
|
||||
direction = pick(cardinal) // alldirs doesn't work with current meteor code unfortunately.
|
||||
waves = rand(3, 6)
|
||||
switch(direction)
|
||||
if(NORTH)
|
||||
dir_text = "aft" // For some reason this is needed.
|
||||
if(SOUTH)
|
||||
dir_text = "fore"
|
||||
if(EAST)
|
||||
dir_text = "port"
|
||||
if(WEST)
|
||||
dir_text = "starboard"
|
||||
set_meteor_types()
|
||||
|
||||
/datum/event2/event/meteor_defense/announce()
|
||||
var/announcement = "Meteors are expected to approach from the [dir_text] side, in approximately [DisplayTimeText(time_to_start - world.time, 60)]."
|
||||
command_announcement.Announce(announcement, "Meteor Alert", new_sound = 'sound/AI/meteors.ogg')
|
||||
|
||||
/datum/event2/event/meteor_defense/wait_tick()
|
||||
if(!soon_announced)
|
||||
if((time_to_start - world.time) <= 5 MINUTES)
|
||||
soon_announced = TRUE
|
||||
var/announcement = "The incoming meteors are expected to approach from the [dir_text] side. \
|
||||
ETA to arrival is approximately [DisplayTimeText(time_to_start - world.time, 60)]."
|
||||
command_announcement.Announce(announcement, "Meteor Alert - Update")
|
||||
|
||||
/datum/event2/event/meteor_defense/start()
|
||||
command_announcement.Announce("Incoming meteors approach from \the [dir_text] side!", "Meteor Alert - Update")
|
||||
|
||||
/datum/event2/event/meteor_defense/event_tick()
|
||||
if(world.time > last_wave_time + wave_delay)
|
||||
last_wave_time = world.time
|
||||
waves--
|
||||
message_admins("[waves] more wave\s of meteors remain.")
|
||||
// Dir is reversed because the direction describes where meteors are going, not what side it's gonna hit.
|
||||
spawn_meteors(rand(wave_upper_bound, wave_lower_bound), meteor_types, reverse_dir[direction])
|
||||
|
||||
/datum/event2/event/meteor_defense/should_end()
|
||||
return waves <= 0
|
||||
|
||||
/datum/event2/event/meteor_defense/end()
|
||||
command_announcement.Announce("\The [location_name()] will clear the incoming meteors in a moment.", "Meteor Alert - Update")
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/datum/event2/meta/spacevine
|
||||
name = "space-vine infestation"
|
||||
departments = list(DEPARTMENT_ENGINEERING)
|
||||
chaos = 10 // There's a really rare chance of vines getting something awful like phoron atmosphere but thats not really controllable.
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/spacevine
|
||||
|
||||
/datum/event2/meta/spacevine/get_weight()
|
||||
return 20 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/spacevine/announce()
|
||||
level_seven_announcement()
|
||||
|
||||
/datum/event2/event/spacevine/start()
|
||||
spacevine_infestation()
|
||||
/datum/event2/meta/spacevine
|
||||
name = "space-vine infestation"
|
||||
departments = list(DEPARTMENT_ENGINEERING)
|
||||
chaos = 10 // There's a really rare chance of vines getting something awful like phoron atmosphere but thats not really controllable.
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/spacevine
|
||||
|
||||
/datum/event2/meta/spacevine/get_weight()
|
||||
return 20 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/spacevine/announce()
|
||||
level_seven_announcement()
|
||||
|
||||
/datum/event2/event/spacevine/start()
|
||||
spacevine_infestation()
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
/datum/event2/meta/wallrot
|
||||
name = "wall-rot"
|
||||
departments = list(DEPARTMENT_ENGINEERING)
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/wallrot
|
||||
|
||||
/datum/event2/meta/wallrot/get_weight()
|
||||
return (10 + metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) / (times_ran + 1)
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/wallrot
|
||||
var/turf/simulated/wall/origin = null
|
||||
|
||||
/datum/event2/event/wallrot/set_up()
|
||||
for(var/i = 1 to 100)
|
||||
var/turf/candidate = locate(rand(1, world.maxx), rand(1, world.maxy), pick(get_location_z_levels()) )
|
||||
if(istype(candidate, /turf/simulated/wall))
|
||||
origin = candidate
|
||||
log_debug("Wall-rot event has chosen \the [origin] ([origin.loc]) as the origin for the wallrot infestation.")
|
||||
return
|
||||
|
||||
log_debug("Wall-rot event failed to find a valid wall after one hundred tries. Aborting.")
|
||||
abort()
|
||||
|
||||
/datum/event2/event/wallrot/announce()
|
||||
if(origin && prob(80))
|
||||
command_announcement.Announce("Harmful fungi detected on \the [location_name()], near \the [origin.loc]. \
|
||||
Station structural integrity may be compromised.", "Biohazard Alert")
|
||||
|
||||
/datum/event2/event/wallrot/start()
|
||||
if(origin)
|
||||
origin.rot()
|
||||
|
||||
var/rot_count = 0
|
||||
var/target_rot = rand(5, 20)
|
||||
for(var/turf/simulated/wall/W in range(7, origin))
|
||||
if(prob(50))
|
||||
if(W.rot())
|
||||
rot_count++
|
||||
if(rot_count >= target_rot)
|
||||
break
|
||||
|
||||
|
||||
|
||||
/datum/event2/meta/wallrot
|
||||
name = "wall-rot"
|
||||
departments = list(DEPARTMENT_ENGINEERING)
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/wallrot
|
||||
|
||||
/datum/event2/meta/wallrot/get_weight()
|
||||
return (10 + metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) / (times_ran + 1)
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/wallrot
|
||||
var/turf/simulated/wall/origin = null
|
||||
|
||||
/datum/event2/event/wallrot/set_up()
|
||||
for(var/i = 1 to 100)
|
||||
var/turf/candidate = locate(rand(1, world.maxx), rand(1, world.maxy), pick(get_location_z_levels()) )
|
||||
if(istype(candidate, /turf/simulated/wall))
|
||||
origin = candidate
|
||||
log_debug("Wall-rot event has chosen \the [origin] ([origin.loc]) as the origin for the wallrot infestation.")
|
||||
return
|
||||
|
||||
log_debug("Wall-rot event failed to find a valid wall after one hundred tries. Aborting.")
|
||||
abort()
|
||||
|
||||
/datum/event2/event/wallrot/announce()
|
||||
if(origin && prob(80))
|
||||
command_announcement.Announce("Harmful fungi detected on \the [location_name()], near \the [origin.loc]. \
|
||||
Station structural integrity may be compromised.", "Biohazard Alert")
|
||||
|
||||
/datum/event2/event/wallrot/start()
|
||||
if(origin)
|
||||
origin.rot()
|
||||
|
||||
var/rot_count = 0
|
||||
var/target_rot = rand(5, 20)
|
||||
for(var/turf/simulated/wall/W in range(7, origin))
|
||||
if(prob(50))
|
||||
if(W.rot())
|
||||
rot_count++
|
||||
if(rot_count >= target_rot)
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,148 +1,148 @@
|
||||
// This event causes a random window near space to become damaged.
|
||||
// If that window is not fixed in a certain amount of time,
|
||||
// that window and nearby windows will shatter, causing a breach.
|
||||
|
||||
/datum/event2/meta/window_break
|
||||
name = "window break"
|
||||
departments = list(DEPARTMENT_ENGINEERING)
|
||||
chaos = 10
|
||||
reusable = TRUE
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/window_break
|
||||
|
||||
/datum/event2/meta/window_break/get_weight()
|
||||
return (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) / (times_ran + 1)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/window_break
|
||||
announce_delay_lower_bound = 10 SECONDS
|
||||
announce_delay_upper_bound = 20 SECONDS
|
||||
length_lower_bound = 8 MINUTES
|
||||
length_upper_bound = 12 MINUTES
|
||||
var/turf/chosen_turf_with_windows = null
|
||||
var/obj/structure/window/chosen_window = null
|
||||
var/list/collateral_windows = list()
|
||||
|
||||
/datum/event2/event/window_break/set_up()
|
||||
var/list/areas = find_random_areas()
|
||||
if(!LAZYLEN(areas))
|
||||
log_debug("Window Break event could not find any areas. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
while(areas.len)
|
||||
var/area/area = pick(areas)
|
||||
areas -= area
|
||||
|
||||
for(var/obj/structure/window/W in area.contents)
|
||||
if(!is_window_to_space(W))
|
||||
continue
|
||||
chosen_turf_with_windows = get_turf(W)
|
||||
collateral_windows = gather_collateral_windows(W)
|
||||
break // Break out of the inner loop.
|
||||
|
||||
if(chosen_turf_with_windows)
|
||||
log_debug("Window Break event has chosen turf '[chosen_turf_with_windows.name]' in [chosen_turf_with_windows.loc].")
|
||||
break // Then the outer loop.
|
||||
|
||||
if(!chosen_turf_with_windows)
|
||||
log_debug("Window Break event could not find a turf with valid windows to break. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/window_break/announce()
|
||||
if(chosen_window)
|
||||
command_announcement.Announce("Structural integrity of space-facing windows at \the [get_area(chosen_turf_with_windows)] are failing. \
|
||||
Repair of the damaged window is advised. Personnel without EVA suits in the area should leave until repairs are complete.", "Structural Alert")
|
||||
|
||||
/datum/event2/event/window_break/start()
|
||||
if(!chosen_turf_with_windows)
|
||||
return
|
||||
|
||||
for(var/obj/structure/window/W in chosen_turf_with_windows.contents)
|
||||
if(W.is_fulltile()) // Full tile windows are simple and can always be used.
|
||||
chosen_window = W
|
||||
break
|
||||
else // Otherwise we only want the window that is on the inside side of the station.
|
||||
var/turf/T = get_step(W, W.dir)
|
||||
if(T.is_space())
|
||||
continue
|
||||
if(T.check_density())
|
||||
continue
|
||||
chosen_window = W
|
||||
break
|
||||
|
||||
if(!chosen_window)
|
||||
return
|
||||
|
||||
chosen_window.take_damage(chosen_window.maxhealth * 0.8)
|
||||
playsound(chosen_window, 'sound/effects/Glasshit.ogg', 100, 1)
|
||||
chosen_window.visible_message(span("danger", "\The [chosen_window] suddenly begins to crack!"))
|
||||
|
||||
/datum/event2/event/window_break/should_end()
|
||||
. = ..()
|
||||
if(!.) // If the timer didn't expire, we can still end it early if someone messes up.
|
||||
if(!chosen_window || !chosen_window.anchored || chosen_window.health == chosen_window.maxhealth)
|
||||
// If the window got deconstructed/moved/etc, immediately end and make the breach happen.
|
||||
// Also end early if it was repaired.
|
||||
return TRUE
|
||||
|
||||
/datum/event2/event/window_break/end()
|
||||
// If someone fixed the window, then everything is fine.
|
||||
if(chosen_window && chosen_window.anchored && chosen_window.health == chosen_window.maxhealth)
|
||||
log_debug("Window Break event ended with window repaired.")
|
||||
return
|
||||
|
||||
// Otherwise a bunch of windows shatter.
|
||||
chosen_window?.shatter()
|
||||
|
||||
var/windows_to_shatter = min(rand(4, 10), collateral_windows.len)
|
||||
for(var/i = 1 to windows_to_shatter)
|
||||
var/obj/structure/window/W = collateral_windows[i]
|
||||
W?.shatter()
|
||||
|
||||
log_debug("Window Break event ended with [windows_to_shatter] shattered windows and a breach.")
|
||||
|
||||
// Checks if a window is adjacent to a space tile, and also that the opposite direction is open.
|
||||
// This is done to avoid getting caught in corner parts of windows.
|
||||
/datum/event2/event/window_break/proc/is_window_to_space(obj/structure/window/W)
|
||||
for(var/direction in GLOB.cardinal)
|
||||
var/turf/T = get_step(W, direction)
|
||||
if(T.is_space())
|
||||
var/turf/opposite_T = get_step(W, GLOB.reverse_dir[direction])
|
||||
if(!opposite_T.check_density())
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//TL;DR: breadth first search for all connected turfs with windows
|
||||
/datum/event2/event/window_break/proc/gather_collateral_windows(var/obj/structure/window/target_window)
|
||||
var/list/turf/frontier_set = list(target_window.loc)
|
||||
var/list/obj/structure/window/result_set = list()
|
||||
var/list/turf/explored_set = list()
|
||||
|
||||
while(frontier_set.len > 0)
|
||||
var/turf/current = frontier_set[1]
|
||||
frontier_set -= current
|
||||
explored_set += current
|
||||
|
||||
var/contains_windows = 0
|
||||
for(var/obj/structure/window/to_add in current.contents)
|
||||
contains_windows = 1
|
||||
result_set += to_add
|
||||
|
||||
if(contains_windows)
|
||||
//add adjacent turfs to be checked for windows as well
|
||||
var/turf/neighbor = locate(current.x + 1, current.y, current.z)
|
||||
if(!(neighbor in frontier_set) && !(neighbor in explored_set))
|
||||
frontier_set += neighbor
|
||||
neighbor = locate(current.x - 1, current.y, current.z)
|
||||
if(!(neighbor in frontier_set) && !(neighbor in explored_set))
|
||||
frontier_set += neighbor
|
||||
neighbor = locate(current.x, current.y + 1, current.z)
|
||||
if(!(neighbor in frontier_set) && !(neighbor in explored_set))
|
||||
frontier_set += neighbor
|
||||
neighbor = locate(current.x, current.y - 1, current.z)
|
||||
if(!(neighbor in frontier_set) && !(neighbor in explored_set))
|
||||
frontier_set += neighbor
|
||||
return result_set
|
||||
// This event causes a random window near space to become damaged.
|
||||
// If that window is not fixed in a certain amount of time,
|
||||
// that window and nearby windows will shatter, causing a breach.
|
||||
|
||||
/datum/event2/meta/window_break
|
||||
name = "window break"
|
||||
departments = list(DEPARTMENT_ENGINEERING)
|
||||
chaos = 10
|
||||
reusable = TRUE
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/window_break
|
||||
|
||||
/datum/event2/meta/window_break/get_weight()
|
||||
return (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) / (times_ran + 1)
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/window_break
|
||||
announce_delay_lower_bound = 10 SECONDS
|
||||
announce_delay_upper_bound = 20 SECONDS
|
||||
length_lower_bound = 8 MINUTES
|
||||
length_upper_bound = 12 MINUTES
|
||||
var/turf/chosen_turf_with_windows = null
|
||||
var/obj/structure/window/chosen_window = null
|
||||
var/list/collateral_windows = list()
|
||||
|
||||
/datum/event2/event/window_break/set_up()
|
||||
var/list/areas = find_random_areas()
|
||||
if(!LAZYLEN(areas))
|
||||
log_debug("Window Break event could not find any areas. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
while(areas.len)
|
||||
var/area/area = pick(areas)
|
||||
areas -= area
|
||||
|
||||
for(var/obj/structure/window/W in area.contents)
|
||||
if(!is_window_to_space(W))
|
||||
continue
|
||||
chosen_turf_with_windows = get_turf(W)
|
||||
collateral_windows = gather_collateral_windows(W)
|
||||
break // Break out of the inner loop.
|
||||
|
||||
if(chosen_turf_with_windows)
|
||||
log_debug("Window Break event has chosen turf '[chosen_turf_with_windows.name]' in [chosen_turf_with_windows.loc].")
|
||||
break // Then the outer loop.
|
||||
|
||||
if(!chosen_turf_with_windows)
|
||||
log_debug("Window Break event could not find a turf with valid windows to break. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/window_break/announce()
|
||||
if(chosen_window)
|
||||
command_announcement.Announce("Structural integrity of space-facing windows at \the [get_area(chosen_turf_with_windows)] are failing. \
|
||||
Repair of the damaged window is advised. Personnel without EVA suits in the area should leave until repairs are complete.", "Structural Alert")
|
||||
|
||||
/datum/event2/event/window_break/start()
|
||||
if(!chosen_turf_with_windows)
|
||||
return
|
||||
|
||||
for(var/obj/structure/window/W in chosen_turf_with_windows.contents)
|
||||
if(W.is_fulltile()) // Full tile windows are simple and can always be used.
|
||||
chosen_window = W
|
||||
break
|
||||
else // Otherwise we only want the window that is on the inside side of the station.
|
||||
var/turf/T = get_step(W, W.dir)
|
||||
if(T.is_space())
|
||||
continue
|
||||
if(T.check_density())
|
||||
continue
|
||||
chosen_window = W
|
||||
break
|
||||
|
||||
if(!chosen_window)
|
||||
return
|
||||
|
||||
chosen_window.take_damage(chosen_window.maxhealth * 0.8)
|
||||
playsound(chosen_window, 'sound/effects/Glasshit.ogg', 100, 1)
|
||||
chosen_window.visible_message(span("danger", "\The [chosen_window] suddenly begins to crack!"))
|
||||
|
||||
/datum/event2/event/window_break/should_end()
|
||||
. = ..()
|
||||
if(!.) // If the timer didn't expire, we can still end it early if someone messes up.
|
||||
if(!chosen_window || !chosen_window.anchored || chosen_window.health == chosen_window.maxhealth)
|
||||
// If the window got deconstructed/moved/etc, immediately end and make the breach happen.
|
||||
// Also end early if it was repaired.
|
||||
return TRUE
|
||||
|
||||
/datum/event2/event/window_break/end()
|
||||
// If someone fixed the window, then everything is fine.
|
||||
if(chosen_window && chosen_window.anchored && chosen_window.health == chosen_window.maxhealth)
|
||||
log_debug("Window Break event ended with window repaired.")
|
||||
return
|
||||
|
||||
// Otherwise a bunch of windows shatter.
|
||||
chosen_window?.shatter()
|
||||
|
||||
var/windows_to_shatter = min(rand(4, 10), collateral_windows.len)
|
||||
for(var/i = 1 to windows_to_shatter)
|
||||
var/obj/structure/window/W = collateral_windows[i]
|
||||
W?.shatter()
|
||||
|
||||
log_debug("Window Break event ended with [windows_to_shatter] shattered windows and a breach.")
|
||||
|
||||
// Checks if a window is adjacent to a space tile, and also that the opposite direction is open.
|
||||
// This is done to avoid getting caught in corner parts of windows.
|
||||
/datum/event2/event/window_break/proc/is_window_to_space(obj/structure/window/W)
|
||||
for(var/direction in GLOB.cardinal)
|
||||
var/turf/T = get_step(W, direction)
|
||||
if(T.is_space())
|
||||
var/turf/opposite_T = get_step(W, GLOB.reverse_dir[direction])
|
||||
if(!opposite_T.check_density())
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//TL;DR: breadth first search for all connected turfs with windows
|
||||
/datum/event2/event/window_break/proc/gather_collateral_windows(var/obj/structure/window/target_window)
|
||||
var/list/turf/frontier_set = list(target_window.loc)
|
||||
var/list/obj/structure/window/result_set = list()
|
||||
var/list/turf/explored_set = list()
|
||||
|
||||
while(frontier_set.len > 0)
|
||||
var/turf/current = frontier_set[1]
|
||||
frontier_set -= current
|
||||
explored_set += current
|
||||
|
||||
var/contains_windows = 0
|
||||
for(var/obj/structure/window/to_add in current.contents)
|
||||
contains_windows = 1
|
||||
result_set += to_add
|
||||
|
||||
if(contains_windows)
|
||||
//add adjacent turfs to be checked for windows as well
|
||||
var/turf/neighbor = locate(current.x + 1, current.y, current.z)
|
||||
if(!(neighbor in frontier_set) && !(neighbor in explored_set))
|
||||
frontier_set += neighbor
|
||||
neighbor = locate(current.x - 1, current.y, current.z)
|
||||
if(!(neighbor in frontier_set) && !(neighbor in explored_set))
|
||||
frontier_set += neighbor
|
||||
neighbor = locate(current.x, current.y + 1, current.z)
|
||||
if(!(neighbor in frontier_set) && !(neighbor in explored_set))
|
||||
frontier_set += neighbor
|
||||
neighbor = locate(current.x, current.y - 1, current.z)
|
||||
if(!(neighbor in frontier_set) && !(neighbor in explored_set))
|
||||
frontier_set += neighbor
|
||||
return result_set
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
/datum/event2/meta/comms_blackout
|
||||
name = "communications blackout"
|
||||
departments = list(DEPARTMENT_EVERYONE) // It's not an engineering event because engineering can't do anything to help . . . for now.
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/comms_blackout
|
||||
|
||||
/datum/event2/meta/comms_blackout/get_weight()
|
||||
return 50 + metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/comms_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")
|
||||
if(prob(33))
|
||||
command_announcement.Announce(alert, new_sound = 'sound/misc/interference.ogg')
|
||||
// AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms
|
||||
for(var/mob/living/silicon/ai/A in player_list)
|
||||
to_chat(A, "<br>")
|
||||
to_chat(A, "<span class='warning'><b>[alert]</b></span>")
|
||||
to_chat(A, "<br>")
|
||||
|
||||
/datum/event2/event/comms_blackout/start()
|
||||
if(prob(50))
|
||||
// One in two chance for the radios to turn i%t# t&_)#%, which can be more alarming than radio silence.
|
||||
log_debug("Doing partial outage of telecomms.")
|
||||
for(var/obj/machinery/telecomms/processor/P in telecomms_list)
|
||||
P.emp_act(1)
|
||||
else
|
||||
// Otherwise just shut everything down, madagascar style.
|
||||
log_debug("Doing complete outage of telecomms.")
|
||||
for(var/obj/machinery/telecomms/T in telecomms_list)
|
||||
T.emp_act(1)
|
||||
|
||||
// Communicators go down no matter what.
|
||||
for(var/obj/machinery/exonet_node/N in machines)
|
||||
N.emp_act(1)
|
||||
/datum/event2/meta/comms_blackout
|
||||
name = "communications blackout"
|
||||
departments = list(DEPARTMENT_EVERYONE) // It's not an engineering event because engineering can't do anything to help . . . for now.
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/comms_blackout
|
||||
|
||||
/datum/event2/meta/comms_blackout/get_weight()
|
||||
return 50 + metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/comms_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")
|
||||
if(prob(33))
|
||||
command_announcement.Announce(alert, new_sound = 'sound/misc/interference.ogg')
|
||||
// AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms
|
||||
for(var/mob/living/silicon/ai/A in player_list)
|
||||
to_chat(A, "<br>")
|
||||
to_chat(A, "<span class='warning'><b>[alert]</b></span>")
|
||||
to_chat(A, "<br>")
|
||||
|
||||
/datum/event2/event/comms_blackout/start()
|
||||
if(prob(50))
|
||||
// One in two chance for the radios to turn i%t# t&_)#%, which can be more alarming than radio silence.
|
||||
log_debug("Doing partial outage of telecomms.")
|
||||
for(var/obj/machinery/telecomms/processor/P in telecomms_list)
|
||||
P.emp_act(1)
|
||||
else
|
||||
// Otherwise just shut everything down, madagascar style.
|
||||
log_debug("Doing complete outage of telecomms.")
|
||||
for(var/obj/machinery/telecomms/T in telecomms_list)
|
||||
T.emp_act(1)
|
||||
|
||||
// Communicators go down no matter what.
|
||||
for(var/obj/machinery/exonet_node/N in machines)
|
||||
N.emp_act(1)
|
||||
|
||||
@@ -1,99 +1,99 @@
|
||||
// Makes a spooky electrical thing happen, that can blow the lights or make the APCs turn off for a short period of time.
|
||||
// Doesn't do any permanent damage beyond the small chance to emag an APC, which just unlocks it forever. As such, this is free to occur even with no engineers.
|
||||
// Since this is an 'external' thing, the Grid Checker can't stop it.
|
||||
|
||||
/datum/event2/meta/electrical_fault
|
||||
name = "electrical fault"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/electrical_fault
|
||||
|
||||
/datum/event2/meta/electrical_fault/get_weight()
|
||||
return 10 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5)
|
||||
|
||||
|
||||
/datum/event2/event/electrical_fault
|
||||
start_delay_lower_bound = 30 SECONDS
|
||||
start_delay_upper_bound = 1 MINUTE
|
||||
length_lower_bound = 20 SECONDS
|
||||
length_upper_bound = 40 SECONDS
|
||||
var/max_apcs_per_tick = 6
|
||||
|
||||
var/list/valid_apcs = null
|
||||
var/list/valid_z_levels = null
|
||||
|
||||
var/apcs_disabled = 0
|
||||
var/apcs_overloaded = 0
|
||||
var/apcs_emagged = 0
|
||||
|
||||
/datum/event2/event/electrical_fault/announce()
|
||||
// Trying to be vague to avoid 'space lightning storms'.
|
||||
// This could be re-flavored to be a solar flare or something and have robots outside be sad.
|
||||
command_announcement.Announce("External conditions near \the [location_name()] are likely \
|
||||
to cause voltage spikes and other electrical issues very soon. Please secure sensitive electrical equipment until the situation passes.", "[location_name()] Sensor Array")
|
||||
|
||||
/datum/event2/event/electrical_fault/set_up()
|
||||
valid_z_levels = get_location_z_levels()
|
||||
valid_z_levels -= using_map.sealed_levels // Space levels only please!
|
||||
|
||||
valid_apcs = list()
|
||||
for(var/obj/machinery/power/apc/A in GLOB.apcs)
|
||||
if(A.z in valid_z_levels)
|
||||
valid_apcs += A
|
||||
|
||||
/datum/event2/event/electrical_fault/start()
|
||||
command_announcement.Announce("Irregularities detected in \the [location_name()] power grid.", "[location_name()] Power Grid Monitoring")
|
||||
|
||||
/datum/event2/event/electrical_fault/event_tick()
|
||||
if(!valid_apcs.len)
|
||||
log_debug("ELECTRICAL EVENT: No valid APCs found for electrical fault event. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
var/list/picked_apcs = list()
|
||||
for(var/i = 1 to max_apcs_per_tick)
|
||||
picked_apcs |= pick(valid_apcs)
|
||||
|
||||
for(var/A in picked_apcs)
|
||||
affect_apc(A)
|
||||
|
||||
/datum/event2/event/electrical_fault/end()
|
||||
command_announcement.Announce("The irregular electrical conditions inside \the [location_name()] power grid has ceased.", "[location_name()] Power Grid Monitoring")
|
||||
log_debug("Electrical Fault event caused [apcs_disabled] APC\s to shut off, \
|
||||
[apcs_overloaded] APC\s to overload lighting, and [apcs_emagged] APC\s to be emagged.")
|
||||
|
||||
/datum/event2/event/electrical_fault/proc/affect_apc(obj/machinery/power/apc/A)
|
||||
// Main breaker is turned off or is Special(tm). Consider it protected.
|
||||
// Important APCs like the AI or the engine core shouldn't get shut off by this event.
|
||||
if((!A.operating || A.failure_timer > 0) || A.is_critical)
|
||||
return
|
||||
|
||||
// In reality this would probably make the lights get brighter but oh well.
|
||||
for(var/obj/machinery/light/L in get_area(A))
|
||||
L.flicker(rand(10, 20))
|
||||
|
||||
// Chance to make the APC turn off for awhile.
|
||||
// This will actually protect it from further damage.
|
||||
if(prob(25))
|
||||
A.energy_fail(rand(60, 120))
|
||||
// log_debug("ELECTRICAL EVENT: Disabled \the [A]'s power for a temporary amount of time.")
|
||||
playsound(A, 'sound/machines/defib_success.ogg', 50, 1)
|
||||
apcs_disabled++
|
||||
return
|
||||
|
||||
// Decent chance to overload lighting circuit.
|
||||
if(prob(30))
|
||||
A.overload_lighting()
|
||||
// log_debug("ELECTRICAL EVENT: Overloaded \the [A]'s lighting.")
|
||||
playsound(A, 'sound/effects/lightningshock.ogg', 50, 1)
|
||||
apcs_overloaded++
|
||||
|
||||
// Relatively small chance to emag the apc as apc_damage event does.
|
||||
if(prob(5))
|
||||
A.emagged = TRUE
|
||||
A.update_icon()
|
||||
// log_debug("ELECTRICAL EVENT: Emagged \the [A].")
|
||||
playsound(A, 'sound/machines/chime.ogg', 50, 1)
|
||||
apcs_emagged++
|
||||
|
||||
// Makes a spooky electrical thing happen, that can blow the lights or make the APCs turn off for a short period of time.
|
||||
// Doesn't do any permanent damage beyond the small chance to emag an APC, which just unlocks it forever. As such, this is free to occur even with no engineers.
|
||||
// Since this is an 'external' thing, the Grid Checker can't stop it.
|
||||
|
||||
/datum/event2/meta/electrical_fault
|
||||
name = "electrical fault"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/electrical_fault
|
||||
|
||||
/datum/event2/meta/electrical_fault/get_weight()
|
||||
return 10 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5)
|
||||
|
||||
|
||||
/datum/event2/event/electrical_fault
|
||||
start_delay_lower_bound = 30 SECONDS
|
||||
start_delay_upper_bound = 1 MINUTE
|
||||
length_lower_bound = 20 SECONDS
|
||||
length_upper_bound = 40 SECONDS
|
||||
var/max_apcs_per_tick = 6
|
||||
|
||||
var/list/valid_apcs = null
|
||||
var/list/valid_z_levels = null
|
||||
|
||||
var/apcs_disabled = 0
|
||||
var/apcs_overloaded = 0
|
||||
var/apcs_emagged = 0
|
||||
|
||||
/datum/event2/event/electrical_fault/announce()
|
||||
// Trying to be vague to avoid 'space lightning storms'.
|
||||
// This could be re-flavored to be a solar flare or something and have robots outside be sad.
|
||||
command_announcement.Announce("External conditions near \the [location_name()] are likely \
|
||||
to cause voltage spikes and other electrical issues very soon. Please secure sensitive electrical equipment until the situation passes.", "[location_name()] Sensor Array")
|
||||
|
||||
/datum/event2/event/electrical_fault/set_up()
|
||||
valid_z_levels = get_location_z_levels()
|
||||
valid_z_levels -= using_map.sealed_levels // Space levels only please!
|
||||
|
||||
valid_apcs = list()
|
||||
for(var/obj/machinery/power/apc/A in GLOB.apcs)
|
||||
if(A.z in valid_z_levels)
|
||||
valid_apcs += A
|
||||
|
||||
/datum/event2/event/electrical_fault/start()
|
||||
command_announcement.Announce("Irregularities detected in \the [location_name()] power grid.", "[location_name()] Power Grid Monitoring")
|
||||
|
||||
/datum/event2/event/electrical_fault/event_tick()
|
||||
if(!valid_apcs.len)
|
||||
log_debug("ELECTRICAL EVENT: No valid APCs found for electrical fault event. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
var/list/picked_apcs = list()
|
||||
for(var/i = 1 to max_apcs_per_tick)
|
||||
picked_apcs |= pick(valid_apcs)
|
||||
|
||||
for(var/A in picked_apcs)
|
||||
affect_apc(A)
|
||||
|
||||
/datum/event2/event/electrical_fault/end()
|
||||
command_announcement.Announce("The irregular electrical conditions inside \the [location_name()] power grid has ceased.", "[location_name()] Power Grid Monitoring")
|
||||
log_debug("Electrical Fault event caused [apcs_disabled] APC\s to shut off, \
|
||||
[apcs_overloaded] APC\s to overload lighting, and [apcs_emagged] APC\s to be emagged.")
|
||||
|
||||
/datum/event2/event/electrical_fault/proc/affect_apc(obj/machinery/power/apc/A)
|
||||
// Main breaker is turned off or is Special(tm). Consider it protected.
|
||||
// Important APCs like the AI or the engine core shouldn't get shut off by this event.
|
||||
if((!A.operating || A.failure_timer > 0) || A.is_critical)
|
||||
return
|
||||
|
||||
// In reality this would probably make the lights get brighter but oh well.
|
||||
for(var/obj/machinery/light/L in get_area(A))
|
||||
L.flicker(rand(10, 20))
|
||||
|
||||
// Chance to make the APC turn off for awhile.
|
||||
// This will actually protect it from further damage.
|
||||
if(prob(25))
|
||||
A.energy_fail(rand(60, 120))
|
||||
// log_debug("ELECTRICAL EVENT: Disabled \the [A]'s power for a temporary amount of time.")
|
||||
playsound(A, 'sound/machines/defib_success.ogg', 50, 1)
|
||||
apcs_disabled++
|
||||
return
|
||||
|
||||
// Decent chance to overload lighting circuit.
|
||||
if(prob(30))
|
||||
A.overload_lighting()
|
||||
// log_debug("ELECTRICAL EVENT: Overloaded \the [A]'s lighting.")
|
||||
playsound(A, 'sound/effects/lightningshock.ogg', 50, 1)
|
||||
apcs_overloaded++
|
||||
|
||||
// Relatively small chance to emag the apc as apc_damage event does.
|
||||
if(prob(5))
|
||||
A.emagged = TRUE
|
||||
A.update_icon()
|
||||
// log_debug("ELECTRICAL EVENT: Emagged \the [A].")
|
||||
playsound(A, 'sound/machines/chime.ogg', 50, 1)
|
||||
apcs_emagged++
|
||||
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
/datum/event2/meta/gravity
|
||||
name = "gravity failure"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
chaos = 20
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/gravity
|
||||
|
||||
/datum/event2/meta/gravity/get_weight()
|
||||
return (20 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5)) / (times_ran + 1)
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/gravity
|
||||
length_lower_bound = 4 MINUTES
|
||||
length_upper_bound = 8 MINUTES
|
||||
|
||||
/datum/event2/event/gravity/announce()
|
||||
command_announcement.Announce("Feedback surge detected in mass-distributions systems. \
|
||||
Artificial gravity has been disabled whilst the system reinitializes. \
|
||||
Please stand by while the gravity system reinitializes.", "Gravity Failure")
|
||||
|
||||
/datum/event2/event/gravity/start()
|
||||
for(var/area/A in world)
|
||||
if(A.z in get_location_z_levels(space_only = TRUE))
|
||||
A.gravitychange(FALSE)
|
||||
|
||||
/datum/event2/event/gravity/end()
|
||||
for(var/area/A in world)
|
||||
if(A.z in get_location_z_levels(space_only = TRUE))
|
||||
A.gravitychange(TRUE)
|
||||
|
||||
/datum/event2/meta/gravity
|
||||
name = "gravity failure"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
chaos = 20
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/gravity
|
||||
|
||||
/datum/event2/meta/gravity/get_weight()
|
||||
return (20 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5)) / (times_ran + 1)
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/gravity
|
||||
length_lower_bound = 4 MINUTES
|
||||
length_upper_bound = 8 MINUTES
|
||||
|
||||
/datum/event2/event/gravity/announce()
|
||||
command_announcement.Announce("Feedback surge detected in mass-distributions systems. \
|
||||
Artificial gravity has been disabled whilst the system reinitializes. \
|
||||
Please stand by while the gravity system reinitializes.", "Gravity Failure")
|
||||
|
||||
/datum/event2/event/gravity/start()
|
||||
for(var/area/A in world)
|
||||
if(A.z in get_location_z_levels(space_only = TRUE))
|
||||
A.gravitychange(FALSE)
|
||||
|
||||
/datum/event2/event/gravity/end()
|
||||
for(var/area/A in world)
|
||||
if(A.z in get_location_z_levels(space_only = TRUE))
|
||||
A.gravitychange(TRUE)
|
||||
|
||||
command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored")
|
||||
@@ -1,77 +1,77 @@
|
||||
/datum/event2/meta/infestation
|
||||
event_class = "infestation"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
|
||||
/datum/event2/meta/infestation/get_weight()
|
||||
return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 10
|
||||
|
||||
/datum/event2/meta/infestation/rodents
|
||||
name = "infestation - rodents"
|
||||
event_type = /datum/event2/event/infestation/rodents
|
||||
|
||||
/datum/event2/meta/infestation/lizards
|
||||
name = "infestation - lizards"
|
||||
event_type = /datum/event2/event/infestation/lizards
|
||||
|
||||
/datum/event2/meta/infestation/spiderlings
|
||||
name = "infestation - spiders"
|
||||
event_type = /datum/event2/event/infestation/spiderlings
|
||||
|
||||
/datum/event2/event/infestation/cockroaches
|
||||
vermin_string = "cockroaches"
|
||||
max_vermin = 6
|
||||
things_to_spawn = list(/mob/living/simple_mob/animal/passive/cockroach)
|
||||
|
||||
/datum/event2/event/infestation
|
||||
var/vermin_string = null
|
||||
var/max_vermin = 0
|
||||
var/list/things_to_spawn = list()
|
||||
|
||||
var/list/turfs = list()
|
||||
|
||||
/datum/event2/event/infestation/rodents
|
||||
vermin_string = "rodents"
|
||||
max_vermin = 12
|
||||
things_to_spawn = list(
|
||||
/mob/living/simple_mob/animal/passive/mouse/gray,
|
||||
/mob/living/simple_mob/animal/passive/mouse/brown,
|
||||
/mob/living/simple_mob/animal/passive/mouse/black,
|
||||
/mob/living/simple_mob/animal/passive/mouse/white,
|
||||
/mob/living/simple_mob/animal/passive/mouse/rat
|
||||
)
|
||||
|
||||
/datum/event2/event/infestation/lizards
|
||||
vermin_string = "lizards"
|
||||
max_vermin = 6
|
||||
things_to_spawn = list(
|
||||
/mob/living/simple_mob/animal/passive/lizard,
|
||||
/mob/living/simple_mob/animal/passive/lizard/large,
|
||||
/mob/living/simple_mob/animal/passive/lizard/large/defensive
|
||||
)
|
||||
|
||||
/datum/event2/event/infestation/spiderlings
|
||||
vermin_string = "spiders"
|
||||
max_vermin = 3
|
||||
things_to_spawn = list(/obj/effect/spider/spiderling/non_growing)
|
||||
|
||||
|
||||
/datum/event2/event/infestation/set_up()
|
||||
turfs = find_random_turfs(max_vermin)
|
||||
if(!turfs.len)
|
||||
log_debug("Infestation event failed to find any valid turfs. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/infestation/announce()
|
||||
var/turf/T = turfs[1]
|
||||
command_announcement.Announce("Bioscans indicate that [vermin_string] have been breeding \
|
||||
in \the [T.loc]. Clear them out, before this starts to affect productivity.", "Vermin infestation")
|
||||
|
||||
|
||||
/datum/event2/event/infestation/start()
|
||||
var/vermin_to_spawn = rand(2, max_vermin)
|
||||
for(var/i = 1 to vermin_to_spawn)
|
||||
var/turf/T = pick(turfs)
|
||||
turfs -= T
|
||||
var/spawn_type = pick(things_to_spawn)
|
||||
new spawn_type(T)
|
||||
/datum/event2/meta/infestation
|
||||
event_class = "infestation"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
|
||||
/datum/event2/meta/infestation/get_weight()
|
||||
return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 10
|
||||
|
||||
/datum/event2/meta/infestation/rodents
|
||||
name = "infestation - rodents"
|
||||
event_type = /datum/event2/event/infestation/rodents
|
||||
|
||||
/datum/event2/meta/infestation/lizards
|
||||
name = "infestation - lizards"
|
||||
event_type = /datum/event2/event/infestation/lizards
|
||||
|
||||
/datum/event2/meta/infestation/spiderlings
|
||||
name = "infestation - spiders"
|
||||
event_type = /datum/event2/event/infestation/spiderlings
|
||||
|
||||
/datum/event2/event/infestation/cockroaches
|
||||
vermin_string = "cockroaches"
|
||||
max_vermin = 6
|
||||
things_to_spawn = list(/mob/living/simple_mob/animal/passive/cockroach)
|
||||
|
||||
/datum/event2/event/infestation
|
||||
var/vermin_string = null
|
||||
var/max_vermin = 0
|
||||
var/list/things_to_spawn = list()
|
||||
|
||||
var/list/turfs = list()
|
||||
|
||||
/datum/event2/event/infestation/rodents
|
||||
vermin_string = "rodents"
|
||||
max_vermin = 12
|
||||
things_to_spawn = list(
|
||||
/mob/living/simple_mob/animal/passive/mouse/gray,
|
||||
/mob/living/simple_mob/animal/passive/mouse/brown,
|
||||
/mob/living/simple_mob/animal/passive/mouse/black,
|
||||
/mob/living/simple_mob/animal/passive/mouse/white,
|
||||
/mob/living/simple_mob/animal/passive/mouse/rat
|
||||
)
|
||||
|
||||
/datum/event2/event/infestation/lizards
|
||||
vermin_string = "lizards"
|
||||
max_vermin = 6
|
||||
things_to_spawn = list(
|
||||
/mob/living/simple_mob/animal/passive/lizard,
|
||||
/mob/living/simple_mob/animal/passive/lizard/large,
|
||||
/mob/living/simple_mob/animal/passive/lizard/large/defensive
|
||||
)
|
||||
|
||||
/datum/event2/event/infestation/spiderlings
|
||||
vermin_string = "spiders"
|
||||
max_vermin = 3
|
||||
things_to_spawn = list(/obj/effect/spider/spiderling/non_growing)
|
||||
|
||||
|
||||
/datum/event2/event/infestation/set_up()
|
||||
turfs = find_random_turfs(max_vermin)
|
||||
if(!turfs.len)
|
||||
log_debug("Infestation event failed to find any valid turfs. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/infestation/announce()
|
||||
var/turf/T = turfs[1]
|
||||
command_announcement.Announce("Bioscans indicate that [vermin_string] have been breeding \
|
||||
in \the [T.loc]. Clear them out, before this starts to affect productivity.", "Vermin infestation")
|
||||
|
||||
|
||||
/datum/event2/event/infestation/start()
|
||||
var/vermin_to_spawn = rand(2, max_vermin)
|
||||
for(var/i = 1 to vermin_to_spawn)
|
||||
var/turf/T = pick(turfs)
|
||||
turfs -= T
|
||||
var/spawn_type = pick(things_to_spawn)
|
||||
new spawn_type(T)
|
||||
|
||||
@@ -1,142 +1,142 @@
|
||||
/datum/event2/meta/pda_spam
|
||||
name = "pda spam"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
event_type = /datum/event2/event/pda_spam
|
||||
|
||||
/datum/event2/meta/pda_spam/get_weight()
|
||||
return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2
|
||||
|
||||
|
||||
/datum/event2/event/pda_spam
|
||||
length_lower_bound = 30 MINUTES
|
||||
length_upper_bound = 1 HOUR
|
||||
var/spam_debug = FALSE // If true, notices of the event sending spam go to `log_debug()`.
|
||||
var/last_spam_time = null // world.time of most recent spam.
|
||||
var/next_spam_attempt_time = 0 // world.time of next attempt to try to spam.
|
||||
var/give_up_after = 5 MINUTES
|
||||
var/obj/machinery/message_server/MS = null
|
||||
var/obj/machinery/exonet_node/node = null
|
||||
|
||||
/datum/event2/event/pda_spam/set_up()
|
||||
last_spam_time = world.time // So it won't immediately give up.
|
||||
MS = pick_message_server()
|
||||
node = get_exonet_node()
|
||||
|
||||
/datum/event2/event/pda_spam/event_tick()
|
||||
if(!can_spam())
|
||||
return
|
||||
|
||||
if(world.time < next_spam_attempt_time)
|
||||
return
|
||||
|
||||
next_spam_attempt_time = world.time + rand(30 SECONDS, 2 MINUTES)
|
||||
|
||||
var/obj/item/device/pda/P = null
|
||||
var/list/viables = list()
|
||||
|
||||
for(var/obj/item/device/pda/check_pda in sortAtom(PDAs))
|
||||
if (!check_pda.owner || check_pda == src || check_pda.hidden)
|
||||
continue
|
||||
|
||||
var/datum/data/pda/app/messenger/M = check_pda.find_program(/datum/data/pda/app/messenger)
|
||||
if(!M || M.toff)
|
||||
continue
|
||||
viables += check_pda
|
||||
|
||||
if(!viables.len)
|
||||
return
|
||||
|
||||
P = pick(viables)
|
||||
var/list/spam = generate_spam()
|
||||
|
||||
if(MS.send_pda_message("[P.owner]", spam[1], spam[2])) // Message been filtered by spam filter.
|
||||
return
|
||||
|
||||
send_spam(P, spam[1], spam[2])
|
||||
|
||||
|
||||
/datum/event2/event/pda_spam/should_end()
|
||||
. = ..()
|
||||
if(!.)
|
||||
// Give up if nobody was reachable for five minutes.
|
||||
if(last_spam_time + give_up_after < world.time)
|
||||
log_debug("PDA Spam event giving up after not being able to spam for awhile.")
|
||||
return TRUE
|
||||
|
||||
/datum/event2/event/pda_spam/proc/can_spam()
|
||||
if(!node || !node.on || !node.allow_external_PDAs)
|
||||
node = get_exonet_node()
|
||||
return FALSE
|
||||
|
||||
if(!MS || !MS.active)
|
||||
MS = pick_message_server()
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
// Returns a list containing two items, the sender and message.
|
||||
/datum/event2/event/pda_spam/proc/generate_spam()
|
||||
var/sender = null
|
||||
var/message = null
|
||||
switch(rand(1, 7))
|
||||
if(1)
|
||||
sender = pick("MaxBet","MaxBet Online Casino","There is no better time to register","I'm excited for you to join us")
|
||||
message = pick("Triple deposits are waiting for you at MaxBet Online when you register to play with us.",\
|
||||
"You can qualify for a 200% Welcome Bonus at MaxBet Online when you sign up today.",\
|
||||
"Once you are a player with MaxBet, you will also receive lucrative weekly and monthly promotions.",\
|
||||
"You will be able to enjoy over 450 top-flight casino games at MaxBet.")
|
||||
if(2)
|
||||
sender = pick(300;"QuickDatingSystem",200;"Find your russian bride",50;"Tajaran beauties are waiting",50;"Find your secret skrell crush",50;"Beautiful unathi brides")
|
||||
message = pick("Your profile caught my attention and I wanted to write and say hello (QuickDating).",\
|
||||
"If you will write to me on my email [pick(first_names_female)]@[pick(last_names)].[pick("ru","ck","tj","ur","nt")] I shall necessarily send you a photo (QuickDating).",\
|
||||
"I want that we write each other and I hope, that you will like my profile and you will answer me (QuickDating).",\
|
||||
"You have (1) new message!",\
|
||||
"You have (2) new profile views!")
|
||||
if(3)
|
||||
sender = pick("Galactic Payments Association","Better Business Bureau","[using_map.starsys_name] E-Payments","NAnoTransen Finance Deparmtent","Luxury Replicas")
|
||||
message = pick("Luxury watches for Blowout sale prices!",\
|
||||
"Watches, Jewelry & Accessories, Bags & Wallets !",\
|
||||
"Deposit 100$ and get 300$ totally free!",\
|
||||
" 100K NT.|WOWGOLD �nly $89 <HOT>",\
|
||||
"We have been filed with a complaint from one of your customers in respect of their business relations with you.",\
|
||||
"We kindly ask you to open the COMPLAINT REPORT (attached) to reply on this complaint..")
|
||||
if(4)
|
||||
sender = pick("Buy Dr. Maxman","Having dysfuctional troubles?")
|
||||
message = pick("DR MAXMAN: REAL Doctors, REAL Science, REAL Results!",\
|
||||
"Dr. Maxman was created by George Acuilar, M.D, a [using_map.boss_short] Certified Urologist who has treated over 70,000 patients sector wide with 'male problems'.",\
|
||||
"After seven years of research, Dr Acuilar and his team came up with this simple breakthrough male enhancement formula.",\
|
||||
"Men of all species report AMAZING increases in length, width and stamina.")
|
||||
if(5)
|
||||
sender = pick("Dr","Crown prince","King Regent","Professor","Captain")
|
||||
sender += " " + pick("Robert","Alfred","Josephat","Kingsley","Sehi","Zbahi")
|
||||
sender += " " + pick("Mugawe","Nkem","Gbatokwia","Nchekwube","Ndim","Ndubisi")
|
||||
message = pick("YOUR FUND HAS BEEN MOVED TO [uppertext(pick("Salusa","Segunda","Cepheus","Andromeda","Gruis","Corona","Aquila","ARES","Asellus"))] DEVELOPMENTARY BANK FOR ONWARD REMITTANCE.",\
|
||||
"We are happy to inform you that due to the delay, we have been instructed to IMMEDIATELY deposit all funds into your account",\
|
||||
"Dear fund beneficiary, We have please to inform you that overdue funds payment has finally been approved and released for payment",\
|
||||
"Due to my lack of agents I require an off-world financial account to immediately deposit the sum of 1 POINT FIVE MILLION credits.",\
|
||||
"Greetings sir, I regretfully to inform you that as I lay dying here due to my lack ofheirs I have chosen you to recieve the full sum of my lifetime savings of 1.5 billion credits")
|
||||
if(6)
|
||||
sender = pick("[using_map.company_name] Morale Divison","Feeling Lonely?","Bored?","www.wetskrell.nt")
|
||||
message = pick("The [using_map.company_name] Morale Division wishes to provide you with quality entertainment sites.",\
|
||||
"WetSkrell.nt is a xenophillic website endorsed by NT for the use of male crewmembers among it's many stations and outposts.",\
|
||||
"Wetskrell.nt only provides the higest quality of male entertaiment to [using_map.company_name] Employees.",\
|
||||
"Simply enter your [using_map.company_name] Bank account system number and pin. With three easy steps this service could be yours!")
|
||||
if(7)
|
||||
sender = pick("You have won free tickets!","Click here to claim your prize!","You are the 1000th vistor!","You are our lucky grand prize winner!")
|
||||
message = pick("You have won tickets to the newest ACTION JAXSON MOVIE!",\
|
||||
"You have won tickets to the newest crime drama DETECTIVE MYSTERY IN THE CLAMITY CAPER!",\
|
||||
"You have won tickets to the newest romantic comedy 16 RULES OF LOVE!",\
|
||||
"You have won tickets to the newest thriller THE CULT OF THE SLEEPING ONE!")
|
||||
return list(sender, message)
|
||||
|
||||
/datum/event2/event/pda_spam/proc/send_spam(obj/item/device/pda/P, sender, message)
|
||||
last_spam_time = world.time
|
||||
var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger)
|
||||
PM.notify("<b>Message from [sender] (Unknown / spam?), </b>\"[message]\" (Unable to Reply)", 0)
|
||||
if(spam_debug)
|
||||
log_debug("PDA Spam event sent spam to \the [P].")
|
||||
|
||||
|
||||
/datum/event2/event/pda_spam/proc/pick_message_server()
|
||||
if(LAZYLEN(message_servers))
|
||||
return pick(message_servers)
|
||||
/datum/event2/meta/pda_spam
|
||||
name = "pda spam"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
event_type = /datum/event2/event/pda_spam
|
||||
|
||||
/datum/event2/meta/pda_spam/get_weight()
|
||||
return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2
|
||||
|
||||
|
||||
/datum/event2/event/pda_spam
|
||||
length_lower_bound = 30 MINUTES
|
||||
length_upper_bound = 1 HOUR
|
||||
var/spam_debug = FALSE // If true, notices of the event sending spam go to `log_debug()`.
|
||||
var/last_spam_time = null // world.time of most recent spam.
|
||||
var/next_spam_attempt_time = 0 // world.time of next attempt to try to spam.
|
||||
var/give_up_after = 5 MINUTES
|
||||
var/obj/machinery/message_server/MS = null
|
||||
var/obj/machinery/exonet_node/node = null
|
||||
|
||||
/datum/event2/event/pda_spam/set_up()
|
||||
last_spam_time = world.time // So it won't immediately give up.
|
||||
MS = pick_message_server()
|
||||
node = get_exonet_node()
|
||||
|
||||
/datum/event2/event/pda_spam/event_tick()
|
||||
if(!can_spam())
|
||||
return
|
||||
|
||||
if(world.time < next_spam_attempt_time)
|
||||
return
|
||||
|
||||
next_spam_attempt_time = world.time + rand(30 SECONDS, 2 MINUTES)
|
||||
|
||||
var/obj/item/device/pda/P = null
|
||||
var/list/viables = list()
|
||||
|
||||
for(var/obj/item/device/pda/check_pda in sortAtom(PDAs))
|
||||
if (!check_pda.owner || check_pda == src || check_pda.hidden)
|
||||
continue
|
||||
|
||||
var/datum/data/pda/app/messenger/M = check_pda.find_program(/datum/data/pda/app/messenger)
|
||||
if(!M || M.toff)
|
||||
continue
|
||||
viables += check_pda
|
||||
|
||||
if(!viables.len)
|
||||
return
|
||||
|
||||
P = pick(viables)
|
||||
var/list/spam = generate_spam()
|
||||
|
||||
if(MS.send_pda_message("[P.owner]", spam[1], spam[2])) // Message been filtered by spam filter.
|
||||
return
|
||||
|
||||
send_spam(P, spam[1], spam[2])
|
||||
|
||||
|
||||
/datum/event2/event/pda_spam/should_end()
|
||||
. = ..()
|
||||
if(!.)
|
||||
// Give up if nobody was reachable for five minutes.
|
||||
if(last_spam_time + give_up_after < world.time)
|
||||
log_debug("PDA Spam event giving up after not being able to spam for awhile.")
|
||||
return TRUE
|
||||
|
||||
/datum/event2/event/pda_spam/proc/can_spam()
|
||||
if(!node || !node.on || !node.allow_external_PDAs)
|
||||
node = get_exonet_node()
|
||||
return FALSE
|
||||
|
||||
if(!MS || !MS.active)
|
||||
MS = pick_message_server()
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
// Returns a list containing two items, the sender and message.
|
||||
/datum/event2/event/pda_spam/proc/generate_spam()
|
||||
var/sender = null
|
||||
var/message = null
|
||||
switch(rand(1, 7))
|
||||
if(1)
|
||||
sender = pick("MaxBet","MaxBet Online Casino","There is no better time to register","I'm excited for you to join us")
|
||||
message = pick("Triple deposits are waiting for you at MaxBet Online when you register to play with us.",\
|
||||
"You can qualify for a 200% Welcome Bonus at MaxBet Online when you sign up today.",\
|
||||
"Once you are a player with MaxBet, you will also receive lucrative weekly and monthly promotions.",\
|
||||
"You will be able to enjoy over 450 top-flight casino games at MaxBet.")
|
||||
if(2)
|
||||
sender = pick(300;"QuickDatingSystem",200;"Find your russian bride",50;"Tajaran beauties are waiting",50;"Find your secret skrell crush",50;"Beautiful unathi brides")
|
||||
message = pick("Your profile caught my attention and I wanted to write and say hello (QuickDating).",\
|
||||
"If you will write to me on my email [pick(first_names_female)]@[pick(last_names)].[pick("ru","ck","tj","ur","nt")] I shall necessarily send you a photo (QuickDating).",\
|
||||
"I want that we write each other and I hope, that you will like my profile and you will answer me (QuickDating).",\
|
||||
"You have (1) new message!",\
|
||||
"You have (2) new profile views!")
|
||||
if(3)
|
||||
sender = pick("Galactic Payments Association","Better Business Bureau","[using_map.starsys_name] E-Payments","NAnoTransen Finance Deparmtent","Luxury Replicas")
|
||||
message = pick("Luxury watches for Blowout sale prices!",\
|
||||
"Watches, Jewelry & Accessories, Bags & Wallets !",\
|
||||
"Deposit 100$ and get 300$ totally free!",\
|
||||
" 100K NT.|WOWGOLD �nly $89 <HOT>",\
|
||||
"We have been filed with a complaint from one of your customers in respect of their business relations with you.",\
|
||||
"We kindly ask you to open the COMPLAINT REPORT (attached) to reply on this complaint..")
|
||||
if(4)
|
||||
sender = pick("Buy Dr. Maxman","Having dysfuctional troubles?")
|
||||
message = pick("DR MAXMAN: REAL Doctors, REAL Science, REAL Results!",\
|
||||
"Dr. Maxman was created by George Acuilar, M.D, a [using_map.boss_short] Certified Urologist who has treated over 70,000 patients sector wide with 'male problems'.",\
|
||||
"After seven years of research, Dr Acuilar and his team came up with this simple breakthrough male enhancement formula.",\
|
||||
"Men of all species report AMAZING increases in length, width and stamina.")
|
||||
if(5)
|
||||
sender = pick("Dr","Crown prince","King Regent","Professor","Captain")
|
||||
sender += " " + pick("Robert","Alfred","Josephat","Kingsley","Sehi","Zbahi")
|
||||
sender += " " + pick("Mugawe","Nkem","Gbatokwia","Nchekwube","Ndim","Ndubisi")
|
||||
message = pick("YOUR FUND HAS BEEN MOVED TO [uppertext(pick("Salusa","Segunda","Cepheus","Andromeda","Gruis","Corona","Aquila","ARES","Asellus"))] DEVELOPMENTARY BANK FOR ONWARD REMITTANCE.",\
|
||||
"We are happy to inform you that due to the delay, we have been instructed to IMMEDIATELY deposit all funds into your account",\
|
||||
"Dear fund beneficiary, We have please to inform you that overdue funds payment has finally been approved and released for payment",\
|
||||
"Due to my lack of agents I require an off-world financial account to immediately deposit the sum of 1 POINT FIVE MILLION credits.",\
|
||||
"Greetings sir, I regretfully to inform you that as I lay dying here due to my lack ofheirs I have chosen you to recieve the full sum of my lifetime savings of 1.5 billion credits")
|
||||
if(6)
|
||||
sender = pick("[using_map.company_name] Morale Divison","Feeling Lonely?","Bored?","www.wetskrell.nt")
|
||||
message = pick("The [using_map.company_name] Morale Division wishes to provide you with quality entertainment sites.",\
|
||||
"WetSkrell.nt is a xenophillic website endorsed by NT for the use of male crewmembers among it's many stations and outposts.",\
|
||||
"Wetskrell.nt only provides the higest quality of male entertaiment to [using_map.company_name] Employees.",\
|
||||
"Simply enter your [using_map.company_name] Bank account system number and pin. With three easy steps this service could be yours!")
|
||||
if(7)
|
||||
sender = pick("You have won free tickets!","Click here to claim your prize!","You are the 1000th vistor!","You are our lucky grand prize winner!")
|
||||
message = pick("You have won tickets to the newest ACTION JAXSON MOVIE!",\
|
||||
"You have won tickets to the newest crime drama DETECTIVE MYSTERY IN THE CLAMITY CAPER!",\
|
||||
"You have won tickets to the newest romantic comedy 16 RULES OF LOVE!",\
|
||||
"You have won tickets to the newest thriller THE CULT OF THE SLEEPING ONE!")
|
||||
return list(sender, message)
|
||||
|
||||
/datum/event2/event/pda_spam/proc/send_spam(obj/item/device/pda/P, sender, message)
|
||||
last_spam_time = world.time
|
||||
var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger)
|
||||
PM.notify("<b>Message from [sender] (Unknown / spam?), </b>\"[message]\" (Unable to Reply)", 0)
|
||||
if(spam_debug)
|
||||
log_debug("PDA Spam event sent spam to \the [P].")
|
||||
|
||||
|
||||
/datum/event2/event/pda_spam/proc/pick_message_server()
|
||||
if(LAZYLEN(message_servers))
|
||||
return pick(message_servers)
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
/datum/event2/meta/radiation_storm
|
||||
name = "radiation storm"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
chaos = 20
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/radiation_storm
|
||||
|
||||
/datum/event2/meta/radiation_storm/get_weight()
|
||||
var/medical_factor = metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10
|
||||
var/population_factor = metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 // Note medical people will get counted twice at 25 weight.
|
||||
return 20 + medical_factor + population_factor
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/radiation_storm
|
||||
start_delay_lower_bound = 1 MINUTE
|
||||
length_lower_bound = 1 MINUTE
|
||||
|
||||
/datum/event2/event/radiation_storm/announce()
|
||||
command_announcement.Announce("High levels of radiation detected near \the [location_name()]. \
|
||||
Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg')
|
||||
make_maint_all_access()
|
||||
|
||||
/datum/event2/event/radiation_storm/start()
|
||||
command_announcement.Announce("The station has entered the radiation belt. \
|
||||
Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert")
|
||||
|
||||
/datum/event2/event/radiation_storm/event_tick()
|
||||
radiate()
|
||||
|
||||
/datum/event2/event/radiation_storm/proc/radiate()
|
||||
var/radiation_level = rand(15, 35)
|
||||
for(var/z in using_map.station_levels)
|
||||
SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1)
|
||||
|
||||
/datum/event2/event/radiation_storm/end()
|
||||
command_announcement.Announce("The station has passed the radiation belt. \
|
||||
Please allow for up to one minute while radiation levels dissipate, and report to \
|
||||
medbay if you experience any unusual symptoms. Maintenance will lose all \
|
||||
access again shortly.", "Anomaly Alert")
|
||||
addtimer(CALLBACK(src, PROC_REF(maint_callback)), 2 MINUTES)
|
||||
|
||||
/datum/event2/event/radiation_storm/proc/maint_callback()
|
||||
revoke_maint_all_access()
|
||||
|
||||
|
||||
// There is no actual radiation during a fake storm.
|
||||
/datum/event2/event/radiation_storm/fake/radiate()
|
||||
return
|
||||
/datum/event2/meta/radiation_storm
|
||||
name = "radiation storm"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
chaos = 20
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/radiation_storm
|
||||
|
||||
/datum/event2/meta/radiation_storm/get_weight()
|
||||
var/medical_factor = metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10
|
||||
var/population_factor = metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 // Note medical people will get counted twice at 25 weight.
|
||||
return 20 + medical_factor + population_factor
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/radiation_storm
|
||||
start_delay_lower_bound = 1 MINUTE
|
||||
length_lower_bound = 1 MINUTE
|
||||
|
||||
/datum/event2/event/radiation_storm/announce()
|
||||
command_announcement.Announce("High levels of radiation detected near \the [location_name()]. \
|
||||
Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg')
|
||||
make_maint_all_access()
|
||||
|
||||
/datum/event2/event/radiation_storm/start()
|
||||
command_announcement.Announce("The station has entered the radiation belt. \
|
||||
Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert")
|
||||
|
||||
/datum/event2/event/radiation_storm/event_tick()
|
||||
radiate()
|
||||
|
||||
/datum/event2/event/radiation_storm/proc/radiate()
|
||||
var/radiation_level = rand(15, 35)
|
||||
for(var/z in using_map.station_levels)
|
||||
SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1)
|
||||
|
||||
/datum/event2/event/radiation_storm/end()
|
||||
command_announcement.Announce("The station has passed the radiation belt. \
|
||||
Please allow for up to one minute while radiation levels dissipate, and report to \
|
||||
medbay if you experience any unusual symptoms. Maintenance will lose all \
|
||||
access again shortly.", "Anomaly Alert")
|
||||
addtimer(CALLBACK(src, PROC_REF(maint_callback)), 2 MINUTES)
|
||||
|
||||
/datum/event2/event/radiation_storm/proc/maint_callback()
|
||||
revoke_maint_all_access()
|
||||
|
||||
|
||||
// There is no actual radiation during a fake storm.
|
||||
/datum/event2/event/radiation_storm/fake/radiate()
|
||||
return
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
// No idea if this is needed for autotraitor or not.
|
||||
// If it is, it shouldn't depend on the event system, but fixing that would be it's own project.
|
||||
// If not, it can stay off until an admin wants to play with it.
|
||||
|
||||
/datum/event2/meta/random_antagonist
|
||||
name = "random antagonist"
|
||||
enabled = FALSE
|
||||
reusable = TRUE
|
||||
chaos = 0 // This is zero due to the event system not being able to know if an antag actually got spawned or not.
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/random_antagonist
|
||||
|
||||
// This has an abnormally high weight due to antags being very important for the round,
|
||||
// however the weight will decay with more antags, and more attempts to add antags.
|
||||
/datum/event2/meta/random_antagonist/get_weight()
|
||||
var/antags = metric.count_all_antags()
|
||||
return 200 / (antags + times_ran + 1)
|
||||
|
||||
|
||||
|
||||
// The random spawn proc on the antag datum will handle announcing the spawn and whatnot, in theory.
|
||||
/datum/event2/event/random_antagonist/start()
|
||||
var/list/valid_types = list()
|
||||
for(var/antag_type in all_antag_types)
|
||||
var/datum/antagonist/antag = all_antag_types[antag_type]
|
||||
if(antag.flags & ANTAG_RANDSPAWN)
|
||||
valid_types |= antag
|
||||
if(valid_types.len)
|
||||
var/datum/antagonist/antag = pick(valid_types)
|
||||
antag.attempt_random_spawn()
|
||||
// No idea if this is needed for autotraitor or not.
|
||||
// If it is, it shouldn't depend on the event system, but fixing that would be it's own project.
|
||||
// If not, it can stay off until an admin wants to play with it.
|
||||
|
||||
/datum/event2/meta/random_antagonist
|
||||
name = "random antagonist"
|
||||
enabled = FALSE
|
||||
reusable = TRUE
|
||||
chaos = 0 // This is zero due to the event system not being able to know if an antag actually got spawned or not.
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/random_antagonist
|
||||
|
||||
// This has an abnormally high weight due to antags being very important for the round,
|
||||
// however the weight will decay with more antags, and more attempts to add antags.
|
||||
/datum/event2/meta/random_antagonist/get_weight()
|
||||
var/antags = metric.count_all_antags()
|
||||
return 200 / (antags + times_ran + 1)
|
||||
|
||||
|
||||
|
||||
// The random spawn proc on the antag datum will handle announcing the spawn and whatnot, in theory.
|
||||
/datum/event2/event/random_antagonist/start()
|
||||
var/list/valid_types = list()
|
||||
for(var/antag_type in all_antag_types)
|
||||
var/datum/antagonist/antag = all_antag_types[antag_type]
|
||||
if(antag.flags & ANTAG_RANDSPAWN)
|
||||
valid_types |= antag
|
||||
if(valid_types.len)
|
||||
var/datum/antagonist/antag = pick(valid_types)
|
||||
antag.attempt_random_spawn()
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
/datum/event2/meta/solar_storm
|
||||
name = "solar storm"
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/solar_storm
|
||||
|
||||
/datum/event2/meta/solar_storm/get_weight()
|
||||
var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10
|
||||
var/space_factor = metric.count_all_space_mobs() * 50
|
||||
return (20 + population_factor + space_factor) / (times_ran + 1)
|
||||
|
||||
|
||||
/datum/event2/event/solar_storm
|
||||
start_delay_lower_bound = 1 MINUTE
|
||||
start_delay_upper_bound = 1 MINUTE
|
||||
length_lower_bound = 2 MINUTES
|
||||
length_upper_bound = 4 MINUTES
|
||||
var/base_solar_gen_rate = null
|
||||
|
||||
/datum/event2/event/solar_storm/announce()
|
||||
command_announcement.Announce("A solar storm has been detected approaching \the [station_name()]. \
|
||||
Please halt all EVA activites immediately and return to the interior of the station.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg')
|
||||
adjust_solar_output(1.5)
|
||||
|
||||
/datum/event2/event/solar_storm/start()
|
||||
command_announcement.Announce("The solar storm has reached the station. Please refrain from EVA and remain inside the station until it has passed.", "Anomaly Alert")
|
||||
adjust_solar_output(5)
|
||||
|
||||
/datum/event2/event/solar_storm/event_tick()
|
||||
radiate()
|
||||
|
||||
/datum/event2/event/solar_storm/end()
|
||||
command_announcement.Announce("The solar storm has passed the station. It is now safe to resume EVA activities. \
|
||||
Please report to medbay if you experience any unusual symptoms.", "Anomaly Alert")
|
||||
adjust_solar_output(1)
|
||||
|
||||
/datum/event2/event/solar_storm/proc/adjust_solar_output(var/mult = 1)
|
||||
if(isnull(base_solar_gen_rate))
|
||||
base_solar_gen_rate = GLOB.solar_gen_rate
|
||||
GLOB.solar_gen_rate = mult * base_solar_gen_rate
|
||||
|
||||
/datum/event2/event/solar_storm/proc/radiate()
|
||||
// Note: Too complicated to be worth trying to use the radiation system for this. Its only in space anyway, so we make an exception in this case.
|
||||
for(var/mob/living/L in player_list)
|
||||
var/turf/T = get_turf(L)
|
||||
if(!T)
|
||||
continue
|
||||
|
||||
if(!istype(T.loc,/area/space) && !istype(T,/turf/space)) //Make sure you're in a space area or on a space turf
|
||||
continue
|
||||
|
||||
//Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation.
|
||||
L.rad_act(rand(15, 30))
|
||||
/datum/event2/meta/solar_storm
|
||||
name = "solar storm"
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/solar_storm
|
||||
|
||||
/datum/event2/meta/solar_storm/get_weight()
|
||||
var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10
|
||||
var/space_factor = metric.count_all_space_mobs() * 50
|
||||
return (20 + population_factor + space_factor) / (times_ran + 1)
|
||||
|
||||
|
||||
/datum/event2/event/solar_storm
|
||||
start_delay_lower_bound = 1 MINUTE
|
||||
start_delay_upper_bound = 1 MINUTE
|
||||
length_lower_bound = 2 MINUTES
|
||||
length_upper_bound = 4 MINUTES
|
||||
var/base_solar_gen_rate = null
|
||||
|
||||
/datum/event2/event/solar_storm/announce()
|
||||
command_announcement.Announce("A solar storm has been detected approaching \the [station_name()]. \
|
||||
Please halt all EVA activites immediately and return to the interior of the station.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg')
|
||||
adjust_solar_output(1.5)
|
||||
|
||||
/datum/event2/event/solar_storm/start()
|
||||
command_announcement.Announce("The solar storm has reached the station. Please refrain from EVA and remain inside the station until it has passed.", "Anomaly Alert")
|
||||
adjust_solar_output(5)
|
||||
|
||||
/datum/event2/event/solar_storm/event_tick()
|
||||
radiate()
|
||||
|
||||
/datum/event2/event/solar_storm/end()
|
||||
command_announcement.Announce("The solar storm has passed the station. It is now safe to resume EVA activities. \
|
||||
Please report to medbay if you experience any unusual symptoms.", "Anomaly Alert")
|
||||
adjust_solar_output(1)
|
||||
|
||||
/datum/event2/event/solar_storm/proc/adjust_solar_output(var/mult = 1)
|
||||
if(isnull(base_solar_gen_rate))
|
||||
base_solar_gen_rate = GLOB.solar_gen_rate
|
||||
GLOB.solar_gen_rate = mult * base_solar_gen_rate
|
||||
|
||||
/datum/event2/event/solar_storm/proc/radiate()
|
||||
// Note: Too complicated to be worth trying to use the radiation system for this. Its only in space anyway, so we make an exception in this case.
|
||||
for(var/mob/living/L in player_list)
|
||||
var/turf/T = get_turf(L)
|
||||
if(!T)
|
||||
continue
|
||||
|
||||
if(!istype(T.loc,/area/space) && !istype(T,/turf/space)) //Make sure you're in a space area or on a space turf
|
||||
continue
|
||||
|
||||
//Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation.
|
||||
L.rad_act(rand(15, 30))
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
/datum/event2/meta/sudden_weather_shift
|
||||
name = "sudden weather shift"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/sudden_weather_shift
|
||||
|
||||
/datum/event2/meta/sudden_weather_shift/get_weight()
|
||||
// The proc name is a bit misleading, it only counts players outside, not all mobs.
|
||||
return (metric.count_all_outdoor_mobs() * 20) / (times_ran + 1)
|
||||
|
||||
/datum/event2/event/sudden_weather_shift
|
||||
start_delay_lower_bound = 30 SECONDS
|
||||
start_delay_upper_bound = 1 MINUTE
|
||||
var/datum/planet/chosen_planet = null
|
||||
|
||||
/datum/event2/event/sudden_weather_shift/set_up()
|
||||
if(!LAZYLEN(SSplanets.planets))
|
||||
log_debug("Weather shift event was ran when no planets exist. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
chosen_planet = pick(SSplanets.planets)
|
||||
|
||||
/datum/event2/event/sudden_weather_shift/announce()
|
||||
if(!chosen_planet)
|
||||
return
|
||||
command_announcement.Announce("Local weather patterns on [chosen_planet.name] suggest that a \
|
||||
sudden atmospheric fluctuation has occurred. All groundside personnel should be wary of \
|
||||
rapidly deteriorating conditions.", "Weather Alert")
|
||||
|
||||
/datum/event2/event/sudden_weather_shift/start()
|
||||
// Using the roundstart weather list is handy, because it avoids the chance of choosing a bus-only weather.
|
||||
// It also makes this event generic and suitable for other planets besides the main one, with no additional code needed.
|
||||
// Only flaw is that roundstart weathers are -usually- safe ones, but we can fix that by tweaking a copy of it.
|
||||
var/list/weather_choices = chosen_planet.weather_holder.roundstart_weather_chances.Copy()
|
||||
var/list/new_weather_weights = list()
|
||||
|
||||
// A lazy way of inverting the odds is to use some division.
|
||||
for(var/weather in weather_choices)
|
||||
new_weather_weights[weather] = 100 / weather_choices[weather]
|
||||
|
||||
// Now choose a new weather.
|
||||
var/new_weather = pickweight(new_weather_weights)
|
||||
log_debug("Sudden weather shift event is now changing [chosen_planet.name]'s weather to [new_weather].")
|
||||
chosen_planet.weather_holder.change_weather(new_weather)
|
||||
/datum/event2/meta/sudden_weather_shift
|
||||
name = "sudden weather shift"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/sudden_weather_shift
|
||||
|
||||
/datum/event2/meta/sudden_weather_shift/get_weight()
|
||||
// The proc name is a bit misleading, it only counts players outside, not all mobs.
|
||||
return (metric.count_all_outdoor_mobs() * 20) / (times_ran + 1)
|
||||
|
||||
/datum/event2/event/sudden_weather_shift
|
||||
start_delay_lower_bound = 30 SECONDS
|
||||
start_delay_upper_bound = 1 MINUTE
|
||||
var/datum/planet/chosen_planet = null
|
||||
|
||||
/datum/event2/event/sudden_weather_shift/set_up()
|
||||
if(!LAZYLEN(SSplanets.planets))
|
||||
log_debug("Weather shift event was ran when no planets exist. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
chosen_planet = pick(SSplanets.planets)
|
||||
|
||||
/datum/event2/event/sudden_weather_shift/announce()
|
||||
if(!chosen_planet)
|
||||
return
|
||||
command_announcement.Announce("Local weather patterns on [chosen_planet.name] suggest that a \
|
||||
sudden atmospheric fluctuation has occurred. All groundside personnel should be wary of \
|
||||
rapidly deteriorating conditions.", "Weather Alert")
|
||||
|
||||
/datum/event2/event/sudden_weather_shift/start()
|
||||
// Using the roundstart weather list is handy, because it avoids the chance of choosing a bus-only weather.
|
||||
// It also makes this event generic and suitable for other planets besides the main one, with no additional code needed.
|
||||
// Only flaw is that roundstart weathers are -usually- safe ones, but we can fix that by tweaking a copy of it.
|
||||
var/list/weather_choices = chosen_planet.weather_holder.roundstart_weather_chances.Copy()
|
||||
var/list/new_weather_weights = list()
|
||||
|
||||
// A lazy way of inverting the odds is to use some division.
|
||||
for(var/weather in weather_choices)
|
||||
new_weather_weights[weather] = 100 / weather_choices[weather]
|
||||
|
||||
// Now choose a new weather.
|
||||
var/new_weather = pickweight(new_weather_weights)
|
||||
log_debug("Sudden weather shift event is now changing [chosen_planet.name]'s weather to [new_weather].")
|
||||
chosen_planet.weather_holder.change_weather(new_weather)
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
// Generic subtype for events that make ghost pods.
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner
|
||||
var/pod_type = null
|
||||
var/list/desired_turf_areas = list() // If this is left empty, it will default to a global list of 'station' turfs.
|
||||
var/list/free_turfs = list()
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/set_up()
|
||||
free_turfs = find_random_turfs(5, desired_turf_areas)
|
||||
|
||||
if(!free_turfs.len)
|
||||
log_debug("Ghost Pod Spawning event failed to find a place to spawn. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/start()
|
||||
var/obj/structure/ghost_pod/pod = new pod_type(pick(free_turfs))
|
||||
post_pod_creation(pod)
|
||||
|
||||
// Override to do things to the pod after it's spawned.
|
||||
// Generic subtype for events that make ghost pods.
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner
|
||||
var/pod_type = null
|
||||
var/list/desired_turf_areas = list() // If this is left empty, it will default to a global list of 'station' turfs.
|
||||
var/list/free_turfs = list()
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/set_up()
|
||||
free_turfs = find_random_turfs(5, desired_turf_areas)
|
||||
|
||||
if(!free_turfs.len)
|
||||
log_debug("Ghost Pod Spawning event failed to find a place to spawn. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/start()
|
||||
var/obj/structure/ghost_pod/pod = new pod_type(pick(free_turfs))
|
||||
post_pod_creation(pod)
|
||||
|
||||
// Override to do things to the pod after it's spawned.
|
||||
/datum/event2/event/ghost_pod_spawner/proc/post_pod_creation(obj/structure/ghost_pod/pod)
|
||||
@@ -1,63 +1,63 @@
|
||||
// This is a somewhat special type of event, that bridges to the old event datum and makes it work with the new system.
|
||||
// It acts as a compatability layer between the old event, and the new GM system.
|
||||
// This is possible because the new datum is mostly a superset of the old one.
|
||||
/datum/event2/event/legacy
|
||||
var/datum/event/legacy_event = null
|
||||
|
||||
// Used to emulate legacy's `activeFor` tick counter.
|
||||
var/tick_count = 0
|
||||
|
||||
// How 'severe' the legacy event should be. This should only be used for legacy events, as severity is an outdated concept for the GM system.
|
||||
var/severity = EVENT_LEVEL_MODERATE
|
||||
|
||||
/datum/event2/meta/legacy/get_weight()
|
||||
return 50
|
||||
|
||||
/datum/event2/event/legacy/process()
|
||||
..()
|
||||
tick_count++
|
||||
|
||||
/datum/event2/event/legacy/set_up()
|
||||
legacy_event = new legacy_event(null, external_use = TRUE)
|
||||
legacy_event.severity = severity
|
||||
legacy_event.setup()
|
||||
|
||||
/datum/event2/event/legacy/should_announce()
|
||||
return tick_count >= legacy_event.announceWhen
|
||||
|
||||
/datum/event2/event/legacy/announce()
|
||||
legacy_event.announce()
|
||||
|
||||
|
||||
// Legacy events don't tick before they start, so we don't need to do `wait_tick()`.
|
||||
|
||||
/datum/event2/event/legacy/should_start()
|
||||
return tick_count >= legacy_event.startWhen
|
||||
|
||||
/datum/event2/event/legacy/start()
|
||||
legacy_event.start()
|
||||
|
||||
/datum/event2/event/legacy/event_tick()
|
||||
legacy_event.tick()
|
||||
|
||||
|
||||
/datum/event2/event/legacy/should_end()
|
||||
return tick_count >= legacy_event.endWhen
|
||||
|
||||
/datum/event2/event/legacy/end()
|
||||
legacy_event.end()
|
||||
|
||||
/datum/event2/event/legacy/finish()
|
||||
legacy_event.kill(external_use = TRUE)
|
||||
..()
|
||||
|
||||
// Proof of concept.
|
||||
/*
|
||||
/datum/event2/meta/legacy_gravity
|
||||
name = "gravity (legacy)"
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/legacy/gravity
|
||||
|
||||
/datum/event2/event/legacy/gravity
|
||||
legacy_event = /datum/event/gravity
|
||||
// This is a somewhat special type of event, that bridges to the old event datum and makes it work with the new system.
|
||||
// It acts as a compatability layer between the old event, and the new GM system.
|
||||
// This is possible because the new datum is mostly a superset of the old one.
|
||||
/datum/event2/event/legacy
|
||||
var/datum/event/legacy_event = null
|
||||
|
||||
// Used to emulate legacy's `activeFor` tick counter.
|
||||
var/tick_count = 0
|
||||
|
||||
// How 'severe' the legacy event should be. This should only be used for legacy events, as severity is an outdated concept for the GM system.
|
||||
var/severity = EVENT_LEVEL_MODERATE
|
||||
|
||||
/datum/event2/meta/legacy/get_weight()
|
||||
return 50
|
||||
|
||||
/datum/event2/event/legacy/process()
|
||||
..()
|
||||
tick_count++
|
||||
|
||||
/datum/event2/event/legacy/set_up()
|
||||
legacy_event = new legacy_event(null, external_use = TRUE)
|
||||
legacy_event.severity = severity
|
||||
legacy_event.setup()
|
||||
|
||||
/datum/event2/event/legacy/should_announce()
|
||||
return tick_count >= legacy_event.announceWhen
|
||||
|
||||
/datum/event2/event/legacy/announce()
|
||||
legacy_event.announce()
|
||||
|
||||
|
||||
// Legacy events don't tick before they start, so we don't need to do `wait_tick()`.
|
||||
|
||||
/datum/event2/event/legacy/should_start()
|
||||
return tick_count >= legacy_event.startWhen
|
||||
|
||||
/datum/event2/event/legacy/start()
|
||||
legacy_event.start()
|
||||
|
||||
/datum/event2/event/legacy/event_tick()
|
||||
legacy_event.tick()
|
||||
|
||||
|
||||
/datum/event2/event/legacy/should_end()
|
||||
return tick_count >= legacy_event.endWhen
|
||||
|
||||
/datum/event2/event/legacy/end()
|
||||
legacy_event.end()
|
||||
|
||||
/datum/event2/event/legacy/finish()
|
||||
legacy_event.kill(external_use = TRUE)
|
||||
..()
|
||||
|
||||
// Proof of concept.
|
||||
/*
|
||||
/datum/event2/meta/legacy_gravity
|
||||
name = "gravity (legacy)"
|
||||
reusable = TRUE
|
||||
event_type = /datum/event2/event/legacy/gravity
|
||||
|
||||
/datum/event2/event/legacy/gravity
|
||||
legacy_event = /datum/event/gravity
|
||||
*/
|
||||
@@ -1,36 +1,36 @@
|
||||
/datum/event2/meta/appendicitis
|
||||
name = "appendicitis"
|
||||
departments = list(DEPARTMENT_MEDICAL)
|
||||
chaos = 40
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/appendicitis
|
||||
|
||||
/datum/event2/meta/appendicitis/get_weight()
|
||||
var/list/doctors = metric.get_people_with_job(/datum/job/doctor)
|
||||
|
||||
doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/nurse)
|
||||
doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist)
|
||||
doctors += metric.get_people_with_job(/datum/job/cmo)
|
||||
|
||||
return doctors.len * 10
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/appendicitis/start()
|
||||
for(var/mob/living/carbon/human/H in shuffle(living_mob_list))
|
||||
// Don't do it to SSD people.
|
||||
if(!H.client)
|
||||
continue
|
||||
|
||||
// Or antags / bellied.
|
||||
if(player_is_antag(H.mind) || isbelly(H.loc))
|
||||
continue
|
||||
|
||||
// Or doctors (otherwise it could be possible for the only surgeon to need surgery).
|
||||
if(H in metric.get_people_with_job(/datum/job/doctor) )
|
||||
continue
|
||||
|
||||
if(H.appendicitis())
|
||||
log_debug("Appendicitis event gave appendicitis to \the [H].")
|
||||
return
|
||||
log_debug("Appendicitis event could not find a valid victim.")
|
||||
/datum/event2/meta/appendicitis
|
||||
name = "appendicitis"
|
||||
departments = list(DEPARTMENT_MEDICAL)
|
||||
chaos = 40
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/appendicitis
|
||||
|
||||
/datum/event2/meta/appendicitis/get_weight()
|
||||
var/list/doctors = metric.get_people_with_job(/datum/job/doctor)
|
||||
|
||||
doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/nurse)
|
||||
doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist)
|
||||
doctors += metric.get_people_with_job(/datum/job/cmo)
|
||||
|
||||
return doctors.len * 10
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/appendicitis/start()
|
||||
for(var/mob/living/carbon/human/H in shuffle(living_mob_list))
|
||||
// Don't do it to SSD people.
|
||||
if(!H.client)
|
||||
continue
|
||||
|
||||
// Or antags / bellied.
|
||||
if(player_is_antag(H.mind) || isbelly(H.loc))
|
||||
continue
|
||||
|
||||
// Or doctors (otherwise it could be possible for the only surgeon to need surgery).
|
||||
if(H in metric.get_people_with_job(/datum/job/doctor) )
|
||||
continue
|
||||
|
||||
if(H.appendicitis())
|
||||
log_debug("Appendicitis event gave appendicitis to \the [H].")
|
||||
return
|
||||
log_debug("Appendicitis event could not find a valid victim.")
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
/datum/event2/meta/virus
|
||||
name = "viral infection"
|
||||
event_class = "virus"
|
||||
departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE)
|
||||
chaos = 40
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT
|
||||
event_type = /datum/event2/event/virus
|
||||
|
||||
/datum/event2/meta/virus/superbug
|
||||
name = "viral superbug"
|
||||
chaos = 60
|
||||
event_type = /datum/event2/event/virus/superbug
|
||||
|
||||
/datum/event2/meta/virus/outbreak
|
||||
name = "viral outbreak"
|
||||
chaos = 60
|
||||
event_type = /datum/event2/event/virus/outbreak
|
||||
|
||||
/datum/event2/meta/virus/get_weight()
|
||||
var/list/virologists = metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist)
|
||||
virologists += metric.get_people_with_job(/datum/job/cmo)
|
||||
|
||||
return virologists.len * 25
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/virus
|
||||
announce_delay_lower_bound = 1 MINUTE
|
||||
announce_delay_upper_bound = 3 MINUTES
|
||||
var/number_of_viruses = 1
|
||||
var/virus_power = 2 // Ranges from 1 to 3, with 1 being the weakest.
|
||||
var/list/candidates = list()
|
||||
|
||||
// A single powerful virus.
|
||||
/datum/event2/event/virus/superbug
|
||||
virus_power = 3
|
||||
|
||||
// A lot of weaker viruses.
|
||||
/datum/event2/event/virus/outbreak
|
||||
virus_power = 1
|
||||
number_of_viruses = 3
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/virus/set_up()
|
||||
for(var/mob/living/carbon/human/H in player_list)
|
||||
if(H.client && !H.isSynthetic() && H.stat != DEAD && !player_is_antag(H.mind) && !isbelly(H.loc))
|
||||
candidates += H
|
||||
candidates = shuffle(candidates)
|
||||
|
||||
/datum/event2/event/virus/announce()
|
||||
command_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard \the [location_name()]. \
|
||||
All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg')
|
||||
|
||||
/datum/event2/event/virus/start()
|
||||
if(!candidates.len)
|
||||
log_debug("Virus event could not find any valid targets to infect. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
for(var/i = 1 to number_of_viruses)
|
||||
var/mob/living/carbon/human/H = LAZYACCESS(candidates, 1)
|
||||
if(!H)
|
||||
return
|
||||
var/datum/disease2/disease/D = new()
|
||||
D.makerandom(virus_power)
|
||||
log_debug("Virus event is now infecting \the [H] with a new random virus.")
|
||||
infect_mob(H, D)
|
||||
candidates -= H
|
||||
/datum/event2/meta/virus
|
||||
name = "viral infection"
|
||||
event_class = "virus"
|
||||
departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE)
|
||||
chaos = 40
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT
|
||||
event_type = /datum/event2/event/virus
|
||||
|
||||
/datum/event2/meta/virus/superbug
|
||||
name = "viral superbug"
|
||||
chaos = 60
|
||||
event_type = /datum/event2/event/virus/superbug
|
||||
|
||||
/datum/event2/meta/virus/outbreak
|
||||
name = "viral outbreak"
|
||||
chaos = 60
|
||||
event_type = /datum/event2/event/virus/outbreak
|
||||
|
||||
/datum/event2/meta/virus/get_weight()
|
||||
var/list/virologists = metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist)
|
||||
virologists += metric.get_people_with_job(/datum/job/cmo)
|
||||
|
||||
return virologists.len * 25
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/virus
|
||||
announce_delay_lower_bound = 1 MINUTE
|
||||
announce_delay_upper_bound = 3 MINUTES
|
||||
var/number_of_viruses = 1
|
||||
var/virus_power = 2 // Ranges from 1 to 3, with 1 being the weakest.
|
||||
var/list/candidates = list()
|
||||
|
||||
// A single powerful virus.
|
||||
/datum/event2/event/virus/superbug
|
||||
virus_power = 3
|
||||
|
||||
// A lot of weaker viruses.
|
||||
/datum/event2/event/virus/outbreak
|
||||
virus_power = 1
|
||||
number_of_viruses = 3
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/virus/set_up()
|
||||
for(var/mob/living/carbon/human/H in player_list)
|
||||
if(H.client && !H.isSynthetic() && H.stat != DEAD && !player_is_antag(H.mind) && !isbelly(H.loc))
|
||||
candidates += H
|
||||
candidates = shuffle(candidates)
|
||||
|
||||
/datum/event2/event/virus/announce()
|
||||
command_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard \the [location_name()]. \
|
||||
All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg')
|
||||
|
||||
/datum/event2/event/virus/start()
|
||||
if(!candidates.len)
|
||||
log_debug("Virus event could not find any valid targets to infect. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
for(var/i = 1 to number_of_viruses)
|
||||
var/mob/living/carbon/human/H = LAZYACCESS(candidates, 1)
|
||||
if(!H)
|
||||
return
|
||||
var/datum/disease2/disease/D = new()
|
||||
D.makerandom(virus_power)
|
||||
log_debug("Virus event is now infecting \the [H] with a new random virus.")
|
||||
infect_mob(H, D)
|
||||
candidates -= H
|
||||
|
||||
@@ -1,97 +1,97 @@
|
||||
// A subtype that involves spawning mobs like carp, rogue drones, spiders, etc.
|
||||
|
||||
/datum/event2/event/mob_spawning
|
||||
var/list/spawned_mobs = list()
|
||||
var/use_map_edge_with_landmarks = TRUE // Use both landmarks and spawning from the "edge" of the map. Otherise uses landmarks over map edge.
|
||||
var/landmark_name = "carpspawn" // Which landmark to use for spawning.
|
||||
|
||||
// Spawns a specific mob from the "edge" of the map, and makes them go towards the station.
|
||||
// Can also use landmarks, if desired.
|
||||
/datum/event2/event/mob_spawning/proc/spawn_mobs_in_space(mob_type, number_of_groups, min_size_of_group, max_size_of_group, dir)
|
||||
if(isnull(dir))
|
||||
dir = pick(GLOB.cardinal)
|
||||
|
||||
var/list/valid_z_levels = get_location_z_levels()
|
||||
valid_z_levels -= using_map.sealed_levels // Space levels only please!
|
||||
|
||||
// Check if any landmarks exist!
|
||||
var/list/spawn_locations = list()
|
||||
for(var/obj/effect/landmark/C in landmarks_list)
|
||||
if(C.name == landmark_name && (C.z in valid_z_levels))
|
||||
spawn_locations.Add(C.loc)
|
||||
|
||||
var/prioritize_landmarks = TRUE
|
||||
if(use_map_edge_with_landmarks && prob(50))
|
||||
prioritize_landmarks = FALSE // One in two chance to come from the edge instead.
|
||||
|
||||
if(spawn_locations.len && prioritize_landmarks) // Okay we've got landmarks, lets use those!
|
||||
shuffle_inplace(spawn_locations)
|
||||
number_of_groups = min(number_of_groups, spawn_locations.len)
|
||||
var/i = 1
|
||||
while (i <= number_of_groups)
|
||||
var/group_size = rand(min_size_of_group, max_size_of_group)
|
||||
for (var/j = 0, j < group_size, j++)
|
||||
spawn_one_mob(spawn_locations[i], mob_type)
|
||||
i++
|
||||
return
|
||||
|
||||
// Okay we did *not* have any landmarks, or we're being told to do both, so lets do our best!
|
||||
var/i = 1
|
||||
while(i <= number_of_groups)
|
||||
var/z_level = pick(valid_z_levels)
|
||||
var/group_size = rand(min_size_of_group, max_size_of_group)
|
||||
var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), z_level)
|
||||
var/turf/group_center = pick_random_edge_turf(dir, z_level, TRANSITIONEDGE + 2)
|
||||
var/list/turfs = getcircle(group_center, 2)
|
||||
for(var/j = 0, j < group_size, j++)
|
||||
// On larger maps, BYOND gets in the way of letting simple_mobs path to the closest edge of the station.
|
||||
// So instead we need to simulate the mob's travel, then spawn them somewhere still hopefully off screen.
|
||||
|
||||
// Find a turf to be the edge of the map.
|
||||
var/turf/edge_of_map = turfs[(i % turfs.len) + 1]
|
||||
|
||||
// Now walk a straight line towards the center of the map, until we find a non-space tile.
|
||||
var/turf/edge_of_station = null
|
||||
|
||||
var/list/space_line = list() // This holds all space tiles on the line. Will be used a bit later.
|
||||
for(var/turf/T in getline(edge_of_map, map_center))
|
||||
if(!T.is_space())
|
||||
break // We found the station!
|
||||
space_line += T
|
||||
edge_of_station = T
|
||||
|
||||
// Now put the mob somewhere on the line, hopefully off screen.
|
||||
// I wish this was higher than 8 but the BYOND internal A* algorithm gives up sometimes when using
|
||||
// 16 or more.
|
||||
// In the future, a new AI stance that handles long distance travel using getline() could work.
|
||||
var/max_distance = 8
|
||||
var/turf/spawn_turf = null
|
||||
for(var/turf/point as anything in space_line)
|
||||
if(get_dist(point, edge_of_station) <= max_distance)
|
||||
spawn_turf = point
|
||||
break
|
||||
|
||||
if(spawn_turf)
|
||||
// Finally, make the simple_mob go towards the edge of the station.
|
||||
var/mob/living/simple_mob/M = spawn_one_mob(spawn_turf, mob_type)
|
||||
if(edge_of_station)
|
||||
M.ai_holder?.give_destination(edge_of_station) // Ask simple_mobs to fly towards the edge of the station.
|
||||
i++
|
||||
|
||||
/datum/event2/event/mob_spawning/proc/spawn_one_mob(new_loc, mob_type)
|
||||
var/mob/living/simple_mob/M = new mob_type(new_loc)
|
||||
GLOB.destroyed_event.register(M, src, PROC_REF(on_mob_destruction))
|
||||
spawned_mobs += M
|
||||
return M
|
||||
|
||||
// Counts living simple_mobs spawned by this event.
|
||||
/datum/event2/event/mob_spawning/proc/count_spawned_mobs()
|
||||
. = 0
|
||||
for(var/mob/living/simple_mob/M as anything in spawned_mobs)
|
||||
if(!QDELETED(M) && M.stat != DEAD)
|
||||
. += 1
|
||||
|
||||
// If simple_mob is bomphed, remove it from the list.
|
||||
/datum/event2/event/mob_spawning/proc/on_mob_destruction(mob/M)
|
||||
spawned_mobs -= M
|
||||
GLOB.destroyed_event.unregister(M, src, PROC_REF(on_mob_destruction))
|
||||
// A subtype that involves spawning mobs like carp, rogue drones, spiders, etc.
|
||||
|
||||
/datum/event2/event/mob_spawning
|
||||
var/list/spawned_mobs = list()
|
||||
var/use_map_edge_with_landmarks = TRUE // Use both landmarks and spawning from the "edge" of the map. Otherise uses landmarks over map edge.
|
||||
var/landmark_name = "carpspawn" // Which landmark to use for spawning.
|
||||
|
||||
// Spawns a specific mob from the "edge" of the map, and makes them go towards the station.
|
||||
// Can also use landmarks, if desired.
|
||||
/datum/event2/event/mob_spawning/proc/spawn_mobs_in_space(mob_type, number_of_groups, min_size_of_group, max_size_of_group, dir)
|
||||
if(isnull(dir))
|
||||
dir = pick(GLOB.cardinal)
|
||||
|
||||
var/list/valid_z_levels = get_location_z_levels()
|
||||
valid_z_levels -= using_map.sealed_levels // Space levels only please!
|
||||
|
||||
// Check if any landmarks exist!
|
||||
var/list/spawn_locations = list()
|
||||
for(var/obj/effect/landmark/C in landmarks_list)
|
||||
if(C.name == landmark_name && (C.z in valid_z_levels))
|
||||
spawn_locations.Add(C.loc)
|
||||
|
||||
var/prioritize_landmarks = TRUE
|
||||
if(use_map_edge_with_landmarks && prob(50))
|
||||
prioritize_landmarks = FALSE // One in two chance to come from the edge instead.
|
||||
|
||||
if(spawn_locations.len && prioritize_landmarks) // Okay we've got landmarks, lets use those!
|
||||
shuffle_inplace(spawn_locations)
|
||||
number_of_groups = min(number_of_groups, spawn_locations.len)
|
||||
var/i = 1
|
||||
while (i <= number_of_groups)
|
||||
var/group_size = rand(min_size_of_group, max_size_of_group)
|
||||
for (var/j = 0, j < group_size, j++)
|
||||
spawn_one_mob(spawn_locations[i], mob_type)
|
||||
i++
|
||||
return
|
||||
|
||||
// Okay we did *not* have any landmarks, or we're being told to do both, so lets do our best!
|
||||
var/i = 1
|
||||
while(i <= number_of_groups)
|
||||
var/z_level = pick(valid_z_levels)
|
||||
var/group_size = rand(min_size_of_group, max_size_of_group)
|
||||
var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), z_level)
|
||||
var/turf/group_center = pick_random_edge_turf(dir, z_level, TRANSITIONEDGE + 2)
|
||||
var/list/turfs = getcircle(group_center, 2)
|
||||
for(var/j = 0, j < group_size, j++)
|
||||
// On larger maps, BYOND gets in the way of letting simple_mobs path to the closest edge of the station.
|
||||
// So instead we need to simulate the mob's travel, then spawn them somewhere still hopefully off screen.
|
||||
|
||||
// Find a turf to be the edge of the map.
|
||||
var/turf/edge_of_map = turfs[(i % turfs.len) + 1]
|
||||
|
||||
// Now walk a straight line towards the center of the map, until we find a non-space tile.
|
||||
var/turf/edge_of_station = null
|
||||
|
||||
var/list/space_line = list() // This holds all space tiles on the line. Will be used a bit later.
|
||||
for(var/turf/T in getline(edge_of_map, map_center))
|
||||
if(!T.is_space())
|
||||
break // We found the station!
|
||||
space_line += T
|
||||
edge_of_station = T
|
||||
|
||||
// Now put the mob somewhere on the line, hopefully off screen.
|
||||
// I wish this was higher than 8 but the BYOND internal A* algorithm gives up sometimes when using
|
||||
// 16 or more.
|
||||
// In the future, a new AI stance that handles long distance travel using getline() could work.
|
||||
var/max_distance = 8
|
||||
var/turf/spawn_turf = null
|
||||
for(var/turf/point as anything in space_line)
|
||||
if(get_dist(point, edge_of_station) <= max_distance)
|
||||
spawn_turf = point
|
||||
break
|
||||
|
||||
if(spawn_turf)
|
||||
// Finally, make the simple_mob go towards the edge of the station.
|
||||
var/mob/living/simple_mob/M = spawn_one_mob(spawn_turf, mob_type)
|
||||
if(edge_of_station)
|
||||
M.ai_holder?.give_destination(edge_of_station) // Ask simple_mobs to fly towards the edge of the station.
|
||||
i++
|
||||
|
||||
/datum/event2/event/mob_spawning/proc/spawn_one_mob(new_loc, mob_type)
|
||||
var/mob/living/simple_mob/M = new mob_type(new_loc)
|
||||
GLOB.destroyed_event.register(M, src, PROC_REF(on_mob_destruction))
|
||||
spawned_mobs += M
|
||||
return M
|
||||
|
||||
// Counts living simple_mobs spawned by this event.
|
||||
/datum/event2/event/mob_spawning/proc/count_spawned_mobs()
|
||||
. = 0
|
||||
for(var/mob/living/simple_mob/M as anything in spawned_mobs)
|
||||
if(!QDELETED(M) && M.stat != DEAD)
|
||||
. += 1
|
||||
|
||||
// If simple_mob is bomphed, remove it from the list.
|
||||
/datum/event2/event/mob_spawning/proc/on_mob_destruction(mob/M)
|
||||
spawned_mobs -= M
|
||||
GLOB.destroyed_event.unregister(M, src, PROC_REF(on_mob_destruction))
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
/datum/event2/meta/carp_migration
|
||||
name = "carp migration"
|
||||
event_class = "carp"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaos = 30
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/mob_spawning/carp_migration
|
||||
|
||||
/datum/event2/meta/carp_migration/get_weight()
|
||||
return 10 + (metric.count_people_in_department(DEPARTMENT_SECURITY) * 20) + (metric.count_all_space_mobs() * 40)
|
||||
|
||||
|
||||
/datum/event2/event/mob_spawning/carp_migration
|
||||
announce_delay_lower_bound = 1 MINUTE
|
||||
announce_delay_upper_bound = 2 MINUTES
|
||||
length_lower_bound = 30 SECONDS
|
||||
length_upper_bound = 1 MINUTE
|
||||
var/carp_cap = 30 // No more than this many (living) carp can exist from this event.
|
||||
var/carp_smallest_group = 3
|
||||
var/carp_largest_group = 5
|
||||
var/carp_wave_cooldown = 10 SECONDS
|
||||
|
||||
var/last_carp_wave_time = null // Last world.time we spawned a carp wave.
|
||||
|
||||
/datum/event2/event/mob_spawning/carp_migration/announce()
|
||||
var/announcement = "Unknown biological entities been detected near \the [location_name()], please stand-by."
|
||||
command_announcement.Announce(announcement, "Lifesign Alert")
|
||||
|
||||
/datum/event2/event/mob_spawning/carp_migration/event_tick()
|
||||
if(last_carp_wave_time + carp_wave_cooldown > world.time)
|
||||
return
|
||||
last_carp_wave_time = world.time
|
||||
|
||||
if(count_spawned_mobs() < carp_cap)
|
||||
spawn_mobs_in_space(
|
||||
mob_type = /mob/living/simple_mob/animal/space/carp/event,
|
||||
number_of_groups = rand(1, 4),
|
||||
min_size_of_group = carp_smallest_group,
|
||||
max_size_of_group = carp_largest_group
|
||||
)
|
||||
|
||||
/datum/event2/event/mob_spawning/carp_migration/end()
|
||||
// Clean up carp that died in space for some reason.
|
||||
for(var/mob/living/simple_mob/SM in spawned_mobs)
|
||||
if(SM.stat == DEAD)
|
||||
var/turf/T = get_turf(SM)
|
||||
if(istype(T, /turf/space))
|
||||
if(prob(75))
|
||||
qdel(SM)
|
||||
/datum/event2/meta/carp_migration
|
||||
name = "carp migration"
|
||||
event_class = "carp"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaos = 30
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/mob_spawning/carp_migration
|
||||
|
||||
/datum/event2/meta/carp_migration/get_weight()
|
||||
return 10 + (metric.count_people_in_department(DEPARTMENT_SECURITY) * 20) + (metric.count_all_space_mobs() * 40)
|
||||
|
||||
|
||||
/datum/event2/event/mob_spawning/carp_migration
|
||||
announce_delay_lower_bound = 1 MINUTE
|
||||
announce_delay_upper_bound = 2 MINUTES
|
||||
length_lower_bound = 30 SECONDS
|
||||
length_upper_bound = 1 MINUTE
|
||||
var/carp_cap = 30 // No more than this many (living) carp can exist from this event.
|
||||
var/carp_smallest_group = 3
|
||||
var/carp_largest_group = 5
|
||||
var/carp_wave_cooldown = 10 SECONDS
|
||||
|
||||
var/last_carp_wave_time = null // Last world.time we spawned a carp wave.
|
||||
|
||||
/datum/event2/event/mob_spawning/carp_migration/announce()
|
||||
var/announcement = "Unknown biological entities been detected near \the [location_name()], please stand-by."
|
||||
command_announcement.Announce(announcement, "Lifesign Alert")
|
||||
|
||||
/datum/event2/event/mob_spawning/carp_migration/event_tick()
|
||||
if(last_carp_wave_time + carp_wave_cooldown > world.time)
|
||||
return
|
||||
last_carp_wave_time = world.time
|
||||
|
||||
if(count_spawned_mobs() < carp_cap)
|
||||
spawn_mobs_in_space(
|
||||
mob_type = /mob/living/simple_mob/animal/space/carp/event,
|
||||
number_of_groups = rand(1, 4),
|
||||
min_size_of_group = carp_smallest_group,
|
||||
max_size_of_group = carp_largest_group
|
||||
)
|
||||
|
||||
/datum/event2/event/mob_spawning/carp_migration/end()
|
||||
// Clean up carp that died in space for some reason.
|
||||
for(var/mob/living/simple_mob/SM in spawned_mobs)
|
||||
if(SM.stat == DEAD)
|
||||
var/turf/T = get_turf(SM)
|
||||
if(istype(T, /turf/space))
|
||||
if(prob(75))
|
||||
qdel(SM)
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/datum/event2/meta/security_drill
|
||||
name = "security drill"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // Don't run if we just got hit by meteors.
|
||||
event_type = /datum/event2/event/security_drill
|
||||
|
||||
/datum/event2/meta/security_drill/get_weight()
|
||||
var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY)
|
||||
var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE)
|
||||
|
||||
if(!sec) // If there's no security, then there is no drill.
|
||||
return 0
|
||||
if(everyone - sec < 0) // If there's no non-sec, then there is no drill.
|
||||
return 0
|
||||
|
||||
// Each security player adds +5 weight, while non-security adds +1.5.
|
||||
return (sec * 5) + ((everyone - sec) * 1.5)
|
||||
|
||||
/datum/event2/event/security_drill/announce()
|
||||
command_announcement.Announce("[pick("A NanoTrasen security director", "A Vir-Gov correspondant", "Local Sif authoritiy")] \
|
||||
has advised the enactment of [pick("a rampant wildlife", "a fire", "a hostile boarding", \
|
||||
"a bomb", "an emergent intelligence")] drill with the personnel onboard \the [location_name()].", "Security Advisement")
|
||||
/datum/event2/meta/security_drill
|
||||
name = "security drill"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // Don't run if we just got hit by meteors.
|
||||
event_type = /datum/event2/event/security_drill
|
||||
|
||||
/datum/event2/meta/security_drill/get_weight()
|
||||
var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY)
|
||||
var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE)
|
||||
|
||||
if(!sec) // If there's no security, then there is no drill.
|
||||
return 0
|
||||
if(everyone - sec < 0) // If there's no non-sec, then there is no drill.
|
||||
return 0
|
||||
|
||||
// Each security player adds +5 weight, while non-security adds +1.5.
|
||||
return (sec * 5) + ((everyone - sec) * 1.5)
|
||||
|
||||
/datum/event2/event/security_drill/announce()
|
||||
command_announcement.Announce("[pick("A NanoTrasen security director", "A Vir-Gov correspondant", "Local Sif authoritiy")] \
|
||||
has advised the enactment of [pick("a rampant wildlife", "a fire", "a hostile boarding", \
|
||||
"a bomb", "an emergent intelligence")] drill with the personnel onboard \the [location_name()].", "Security Advisement")
|
||||
|
||||
@@ -1,234 +1,234 @@
|
||||
|
||||
// Type for inheritence.
|
||||
// It has a null name, so it won't be ran.
|
||||
/datum/event2/meta/prison_break
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
// The weight system can check if people are in these areas.
|
||||
// This isn't the same list as what the event itself will break, as the event will also
|
||||
// break open areas inbetween the holding area and the public hallway, like the brig area verses
|
||||
// the prison area.
|
||||
var/list/relevant_areas = list()
|
||||
var/list/irrelevant_areas = list()
|
||||
|
||||
/datum/event2/meta/prison_break/get_weight()
|
||||
// First, don't do this if nobody can fix the doors.
|
||||
var/door_fixers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + metric.count_people_in_department(DEPARTMENT_SYNTHETIC)
|
||||
if(!door_fixers)
|
||||
return 0
|
||||
var/list/afflicted_departments = departments.Copy()
|
||||
var/afflicted_crew = 0
|
||||
|
||||
afflicted_departments -= DEPARTMENT_SYNTHETIC
|
||||
for(var/D in afflicted_departments)
|
||||
afflicted_crew += metric.count_people_in_department(D)
|
||||
|
||||
// Don't do it if nobody is around to ""appreciate"" it.
|
||||
if(!afflicted_crew)
|
||||
return 0
|
||||
|
||||
var/trapped = get_odds_from_trapped_mobs()
|
||||
|
||||
return 10 + (door_fixers * 20) + (afflicted_crew * 10) + trapped
|
||||
|
||||
// This is overriden to have specific events trigger more often based on who is trapped in where, if applicable.
|
||||
/datum/event2/meta/prison_break/proc/get_odds_from_trapped_mobs()
|
||||
return 0
|
||||
|
||||
/datum/event2/meta/prison_break/proc/is_mob_in_relevant_area(mob/living/L)
|
||||
var/area/A = get_area(L)
|
||||
if(!A)
|
||||
return FALSE
|
||||
if(is_type_in_list(A, relevant_areas) && !is_type_in_list(A, irrelevant_areas))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/event2/meta/prison_break/brig
|
||||
name = "prison break - brig"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC)
|
||||
event_type = /datum/event2/event/prison_break/brig
|
||||
relevant_areas = list(
|
||||
/area/security/prison,
|
||||
/area/security/security_cell_hallway,
|
||||
/area/security/security_processing,
|
||||
/area/security/interrogation
|
||||
)
|
||||
|
||||
/datum/event2/meta/prison_break/brig/get_odds_from_trapped_mobs()
|
||||
. = 0
|
||||
for(var/mob/living/L in player_list)
|
||||
if(is_mob_in_relevant_area(L))
|
||||
// Don't count them if they're in security.
|
||||
if(!(L in metric.count_people_in_department(DEPARTMENT_SECURITY)))
|
||||
. += 40
|
||||
|
||||
|
||||
/datum/event2/meta/prison_break/armory
|
||||
name = "prison break - armory"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC)
|
||||
chaos = 40 // Potentially free guns.
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/prison_break/armory
|
||||
|
||||
/datum/event2/meta/prison_break/bridge
|
||||
name = "prison break - bridge"
|
||||
departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SYNTHETIC)
|
||||
chaos = 40 // Potentially free spare ID.
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/prison_break/bridge
|
||||
|
||||
/datum/event2/meta/prison_break/xenobio
|
||||
name = "prison break - xenobio"
|
||||
departments = list(DEPARTMENT_RESEARCH, DEPARTMENT_SYNTHETIC)
|
||||
chaos = 20 // This one is more likely to actually kill someone.
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/prison_break/xenobio
|
||||
relevant_areas = list(/area/rnd/xenobiology)
|
||||
irrelevant_areas = list(
|
||||
/area/rnd/xenobiology/xenoflora,
|
||||
/area/rnd/xenobiology/xenoflora_storage
|
||||
)
|
||||
|
||||
/datum/event2/meta/prison_break/xenobio/get_odds_from_trapped_mobs()
|
||||
. = 0
|
||||
for(var/mob/living/simple_mob/slime/xenobio/X in living_mob_list)
|
||||
if(is_mob_in_relevant_area(X))
|
||||
. += 5
|
||||
|
||||
|
||||
/datum/event2/meta/prison_break/virology
|
||||
name = "prison break - virology"
|
||||
departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_SYNTHETIC)
|
||||
event_type = /datum/event2/event/prison_break/virology
|
||||
relevant_areas = list(
|
||||
/area/medical/virology,
|
||||
/area/medical/virologyaccess
|
||||
)
|
||||
|
||||
/datum/event2/meta/prison_break/virology/get_odds_from_trapped_mobs()
|
||||
. = 0
|
||||
for(var/mob/living/L in player_list)
|
||||
if(is_mob_in_relevant_area(L))
|
||||
// Don't count them if they're in medical.
|
||||
if(!(L in metric.count_people_in_department(DEPARTMENT_MEDICAL)))
|
||||
. += 40
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/prison_break
|
||||
start_delay_lower_bound = 3 MINUTES
|
||||
start_delay_upper_bound = 4 MINUTES
|
||||
length_lower_bound = 40 SECONDS
|
||||
length_upper_bound = 1 MINUTE
|
||||
var/area_display_name = null // A string used to describe the area being messed with.
|
||||
var/containment_display_desc = null
|
||||
var/list/areas_to_break = list()
|
||||
var/list/area_types_to_break = null // Area types to include.
|
||||
var/list/area_types_to_ignore = null // Area types to exclude, usually due to undesired inclusion from inheritence.
|
||||
var/ignore_blast_doors = FALSE
|
||||
|
||||
/datum/event2/event/prison_break/brig
|
||||
area_display_name = "Brig"
|
||||
containment_display_desc = "imprisonment"
|
||||
area_types_to_break = list(
|
||||
/area/security/prison,
|
||||
/area/security/brig,
|
||||
/area/security/security_cell_hallway,
|
||||
/area/security/security_processing,
|
||||
/area/security/interrogation
|
||||
)
|
||||
|
||||
/datum/event2/event/prison_break/armory
|
||||
area_display_name = "Armory"
|
||||
containment_display_desc = "protection"
|
||||
area_types_to_break = list(
|
||||
/area/security/brig,
|
||||
/area/security/warden,
|
||||
/area/security/evidence_storage,
|
||||
/area/security/security_equiptment_storage,
|
||||
/area/security/armoury,
|
||||
/area/security/tactical
|
||||
)
|
||||
|
||||
/datum/event2/event/prison_break/bridge
|
||||
area_display_name = "Bridge"
|
||||
containment_display_desc = "isolation"
|
||||
area_types_to_break = list(
|
||||
/area/bridge,
|
||||
/area/bridge_hallway
|
||||
)
|
||||
|
||||
/datum/event2/event/prison_break/xenobio
|
||||
area_display_name = "Xenobiology"
|
||||
containment_display_desc = "containment"
|
||||
area_types_to_break = list(/area/rnd/xenobiology)
|
||||
area_types_to_ignore = list(
|
||||
/area/rnd/xenobiology/xenoflora,
|
||||
/area/rnd/xenobiology/xenoflora_storage
|
||||
)
|
||||
|
||||
/datum/event2/event/prison_break/virology
|
||||
area_display_name = "Virology"
|
||||
containment_display_desc = "quarantine"
|
||||
area_types_to_break = list(
|
||||
/area/medical/virology,
|
||||
/area/medical/virologyaccess
|
||||
)
|
||||
|
||||
|
||||
/datum/event2/event/prison_break/set_up()
|
||||
for(var/area/A in world)
|
||||
if(is_type_in_list(A, area_types_to_break) && !is_type_in_list(A, area_types_to_ignore))
|
||||
areas_to_break += A
|
||||
|
||||
if(!areas_to_break.len)
|
||||
log_debug("Prison Break event failed to find any areas to break. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/prison_break/announce()
|
||||
var/my_department = "[location_name()] Firewall Subroutines"
|
||||
var/message = "An unknown malicious program has been detected in the [area_display_name] \
|
||||
lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised \
|
||||
within approximately three minutes. Direct intervention is required immediately. Disabling the \
|
||||
main breaker in the APCs will protect the APC's room from being compromised."
|
||||
|
||||
for(var/obj/machinery/message_server/MS in machines)
|
||||
MS.send_rc_message(DEPARTMENT_ENGINEERING, my_department, "[message]<br>", "", "", 2)
|
||||
|
||||
// Nobody reads the requests consoles so lets use the radio as well.
|
||||
global_announcer.autosay(message, my_department, DEPARTMENT_ENGINEERING)
|
||||
|
||||
for(var/mob/living/silicon/ai/A in player_list)
|
||||
to_chat(A, span("danger", "Malicious program detected in the [area_display_name] lighting and airlock control systems by [my_department]. \
|
||||
Disabling the main breaker in the APCs will protect the APC's room from being compromised."))
|
||||
|
||||
var/time_to_flicker = start_delay - 10 SECONDS
|
||||
addtimer(CALLBACK(src, PROC_REF(flicker_area)), time_to_flicker)
|
||||
|
||||
|
||||
/datum/event2/event/prison_break/proc/flicker_area()
|
||||
for(var/area/A in areas_to_break)
|
||||
var/obj/machinery/power/apc/apc = A.get_apc()
|
||||
if(istype(apc) && apc.operating) //If the apc's off, it's a little hard to overload the lights.
|
||||
for(var/obj/machinery/light/L in A)
|
||||
L.flicker(10)
|
||||
|
||||
/datum/event2/event/prison_break/start()
|
||||
for(var/area/A in areas_to_break)
|
||||
spawn(0) // So we don't block the ticker.
|
||||
A.prison_break(TRUE, TRUE, !ignore_blast_doors) // Naming `open_blast_doors` causes mysterious runtimes.
|
||||
|
||||
// There's between 40 seconds and one minute before the whole station knows.
|
||||
// If there's a baddie engineer, they can choose to keep their early announcement to themselves and get a minute to exploit it.
|
||||
/datum/event2/event/prison_break/end()
|
||||
command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] was detected \
|
||||
in \the [location_name()] [area_display_name] [containment_display_desc] subroutines. Secure any compromised \
|
||||
areas immediately. AI involvement is recommended.", "[capitalize(containment_display_desc)] Alert")
|
||||
|
||||
global_announcer.autosay(
|
||||
"It is now safe to reactivate the APCs' main breakers inside [area_display_name].",
|
||||
"[location_name()] Firewall Subroutines",
|
||||
DEPARTMENT_ENGINEERING
|
||||
)
|
||||
|
||||
// Type for inheritence.
|
||||
// It has a null name, so it won't be ran.
|
||||
/datum/event2/meta/prison_break
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
// The weight system can check if people are in these areas.
|
||||
// This isn't the same list as what the event itself will break, as the event will also
|
||||
// break open areas inbetween the holding area and the public hallway, like the brig area verses
|
||||
// the prison area.
|
||||
var/list/relevant_areas = list()
|
||||
var/list/irrelevant_areas = list()
|
||||
|
||||
/datum/event2/meta/prison_break/get_weight()
|
||||
// First, don't do this if nobody can fix the doors.
|
||||
var/door_fixers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + metric.count_people_in_department(DEPARTMENT_SYNTHETIC)
|
||||
if(!door_fixers)
|
||||
return 0
|
||||
var/list/afflicted_departments = departments.Copy()
|
||||
var/afflicted_crew = 0
|
||||
|
||||
afflicted_departments -= DEPARTMENT_SYNTHETIC
|
||||
for(var/D in afflicted_departments)
|
||||
afflicted_crew += metric.count_people_in_department(D)
|
||||
|
||||
// Don't do it if nobody is around to ""appreciate"" it.
|
||||
if(!afflicted_crew)
|
||||
return 0
|
||||
|
||||
var/trapped = get_odds_from_trapped_mobs()
|
||||
|
||||
return 10 + (door_fixers * 20) + (afflicted_crew * 10) + trapped
|
||||
|
||||
// This is overriden to have specific events trigger more often based on who is trapped in where, if applicable.
|
||||
/datum/event2/meta/prison_break/proc/get_odds_from_trapped_mobs()
|
||||
return 0
|
||||
|
||||
/datum/event2/meta/prison_break/proc/is_mob_in_relevant_area(mob/living/L)
|
||||
var/area/A = get_area(L)
|
||||
if(!A)
|
||||
return FALSE
|
||||
if(is_type_in_list(A, relevant_areas) && !is_type_in_list(A, irrelevant_areas))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/event2/meta/prison_break/brig
|
||||
name = "prison break - brig"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC)
|
||||
event_type = /datum/event2/event/prison_break/brig
|
||||
relevant_areas = list(
|
||||
/area/security/prison,
|
||||
/area/security/security_cell_hallway,
|
||||
/area/security/security_processing,
|
||||
/area/security/interrogation
|
||||
)
|
||||
|
||||
/datum/event2/meta/prison_break/brig/get_odds_from_trapped_mobs()
|
||||
. = 0
|
||||
for(var/mob/living/L in player_list)
|
||||
if(is_mob_in_relevant_area(L))
|
||||
// Don't count them if they're in security.
|
||||
if(!(L in metric.count_people_in_department(DEPARTMENT_SECURITY)))
|
||||
. += 40
|
||||
|
||||
|
||||
/datum/event2/meta/prison_break/armory
|
||||
name = "prison break - armory"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC)
|
||||
chaos = 40 // Potentially free guns.
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/prison_break/armory
|
||||
|
||||
/datum/event2/meta/prison_break/bridge
|
||||
name = "prison break - bridge"
|
||||
departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SYNTHETIC)
|
||||
chaos = 40 // Potentially free spare ID.
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/prison_break/bridge
|
||||
|
||||
/datum/event2/meta/prison_break/xenobio
|
||||
name = "prison break - xenobio"
|
||||
departments = list(DEPARTMENT_RESEARCH, DEPARTMENT_SYNTHETIC)
|
||||
chaos = 20 // This one is more likely to actually kill someone.
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/prison_break/xenobio
|
||||
relevant_areas = list(/area/rnd/xenobiology)
|
||||
irrelevant_areas = list(
|
||||
/area/rnd/xenobiology/xenoflora,
|
||||
/area/rnd/xenobiology/xenoflora_storage
|
||||
)
|
||||
|
||||
/datum/event2/meta/prison_break/xenobio/get_odds_from_trapped_mobs()
|
||||
. = 0
|
||||
for(var/mob/living/simple_mob/slime/xenobio/X in living_mob_list)
|
||||
if(is_mob_in_relevant_area(X))
|
||||
. += 5
|
||||
|
||||
|
||||
/datum/event2/meta/prison_break/virology
|
||||
name = "prison break - virology"
|
||||
departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_SYNTHETIC)
|
||||
event_type = /datum/event2/event/prison_break/virology
|
||||
relevant_areas = list(
|
||||
/area/medical/virology,
|
||||
/area/medical/virologyaccess
|
||||
)
|
||||
|
||||
/datum/event2/meta/prison_break/virology/get_odds_from_trapped_mobs()
|
||||
. = 0
|
||||
for(var/mob/living/L in player_list)
|
||||
if(is_mob_in_relevant_area(L))
|
||||
// Don't count them if they're in medical.
|
||||
if(!(L in metric.count_people_in_department(DEPARTMENT_MEDICAL)))
|
||||
. += 40
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/prison_break
|
||||
start_delay_lower_bound = 3 MINUTES
|
||||
start_delay_upper_bound = 4 MINUTES
|
||||
length_lower_bound = 40 SECONDS
|
||||
length_upper_bound = 1 MINUTE
|
||||
var/area_display_name = null // A string used to describe the area being messed with.
|
||||
var/containment_display_desc = null
|
||||
var/list/areas_to_break = list()
|
||||
var/list/area_types_to_break = null // Area types to include.
|
||||
var/list/area_types_to_ignore = null // Area types to exclude, usually due to undesired inclusion from inheritence.
|
||||
var/ignore_blast_doors = FALSE
|
||||
|
||||
/datum/event2/event/prison_break/brig
|
||||
area_display_name = "Brig"
|
||||
containment_display_desc = "imprisonment"
|
||||
area_types_to_break = list(
|
||||
/area/security/prison,
|
||||
/area/security/brig,
|
||||
/area/security/security_cell_hallway,
|
||||
/area/security/security_processing,
|
||||
/area/security/interrogation
|
||||
)
|
||||
|
||||
/datum/event2/event/prison_break/armory
|
||||
area_display_name = "Armory"
|
||||
containment_display_desc = "protection"
|
||||
area_types_to_break = list(
|
||||
/area/security/brig,
|
||||
/area/security/warden,
|
||||
/area/security/evidence_storage,
|
||||
/area/security/security_equiptment_storage,
|
||||
/area/security/armoury,
|
||||
/area/security/tactical
|
||||
)
|
||||
|
||||
/datum/event2/event/prison_break/bridge
|
||||
area_display_name = "Bridge"
|
||||
containment_display_desc = "isolation"
|
||||
area_types_to_break = list(
|
||||
/area/bridge,
|
||||
/area/bridge_hallway
|
||||
)
|
||||
|
||||
/datum/event2/event/prison_break/xenobio
|
||||
area_display_name = "Xenobiology"
|
||||
containment_display_desc = "containment"
|
||||
area_types_to_break = list(/area/rnd/xenobiology)
|
||||
area_types_to_ignore = list(
|
||||
/area/rnd/xenobiology/xenoflora,
|
||||
/area/rnd/xenobiology/xenoflora_storage
|
||||
)
|
||||
|
||||
/datum/event2/event/prison_break/virology
|
||||
area_display_name = "Virology"
|
||||
containment_display_desc = "quarantine"
|
||||
area_types_to_break = list(
|
||||
/area/medical/virology,
|
||||
/area/medical/virologyaccess
|
||||
)
|
||||
|
||||
|
||||
/datum/event2/event/prison_break/set_up()
|
||||
for(var/area/A in world)
|
||||
if(is_type_in_list(A, area_types_to_break) && !is_type_in_list(A, area_types_to_ignore))
|
||||
areas_to_break += A
|
||||
|
||||
if(!areas_to_break.len)
|
||||
log_debug("Prison Break event failed to find any areas to break. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/prison_break/announce()
|
||||
var/my_department = "[location_name()] Firewall Subroutines"
|
||||
var/message = "An unknown malicious program has been detected in the [area_display_name] \
|
||||
lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised \
|
||||
within approximately three minutes. Direct intervention is required immediately. Disabling the \
|
||||
main breaker in the APCs will protect the APC's room from being compromised."
|
||||
|
||||
for(var/obj/machinery/message_server/MS in machines)
|
||||
MS.send_rc_message(DEPARTMENT_ENGINEERING, my_department, "[message]<br>", "", "", 2)
|
||||
|
||||
// Nobody reads the requests consoles so lets use the radio as well.
|
||||
global_announcer.autosay(message, my_department, DEPARTMENT_ENGINEERING)
|
||||
|
||||
for(var/mob/living/silicon/ai/A in player_list)
|
||||
to_chat(A, span("danger", "Malicious program detected in the [area_display_name] lighting and airlock control systems by [my_department]. \
|
||||
Disabling the main breaker in the APCs will protect the APC's room from being compromised."))
|
||||
|
||||
var/time_to_flicker = start_delay - 10 SECONDS
|
||||
addtimer(CALLBACK(src, PROC_REF(flicker_area)), time_to_flicker)
|
||||
|
||||
|
||||
/datum/event2/event/prison_break/proc/flicker_area()
|
||||
for(var/area/A in areas_to_break)
|
||||
var/obj/machinery/power/apc/apc = A.get_apc()
|
||||
if(istype(apc) && apc.operating) //If the apc's off, it's a little hard to overload the lights.
|
||||
for(var/obj/machinery/light/L in A)
|
||||
L.flicker(10)
|
||||
|
||||
/datum/event2/event/prison_break/start()
|
||||
for(var/area/A in areas_to_break)
|
||||
spawn(0) // So we don't block the ticker.
|
||||
A.prison_break(TRUE, TRUE, !ignore_blast_doors) // Naming `open_blast_doors` causes mysterious runtimes.
|
||||
|
||||
// There's between 40 seconds and one minute before the whole station knows.
|
||||
// If there's a baddie engineer, they can choose to keep their early announcement to themselves and get a minute to exploit it.
|
||||
/datum/event2/event/prison_break/end()
|
||||
command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] was detected \
|
||||
in \the [location_name()] [area_display_name] [containment_display_desc] subroutines. Secure any compromised \
|
||||
areas immediately. AI involvement is recommended.", "[capitalize(containment_display_desc)] Alert")
|
||||
|
||||
global_announcer.autosay(
|
||||
"It is now safe to reactivate the APCs' main breakers inside [area_display_name].",
|
||||
"[location_name()] Firewall Subroutines",
|
||||
DEPARTMENT_ENGINEERING
|
||||
)
|
||||
|
||||
@@ -1,70 +1,70 @@
|
||||
/datum/event2/meta/rogue_drones
|
||||
name = "rogue drones"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaos = 40
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/mob_spawning/rogue_drones
|
||||
|
||||
/datum/event2/meta/rogue_drones/get_weight()
|
||||
. = 10 // Start with a base weight, since this event does provide some value even if no sec is around.
|
||||
. += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20
|
||||
. += metric.count_all_space_mobs() * 40
|
||||
|
||||
|
||||
/datum/event2/event/mob_spawning/rogue_drones
|
||||
length_lower_bound = 15 MINUTES
|
||||
length_upper_bound = 20 MINUTES
|
||||
var/drones_to_spawn = 6
|
||||
|
||||
/datum/event2/event/mob_spawning/rogue_drones/set_up()
|
||||
if(prob(10)) // Small chance for a false alarm.
|
||||
drones_to_spawn = 0
|
||||
|
||||
/datum/event2/event/mob_spawning/rogue_drones/announce()
|
||||
var/msg = null
|
||||
var/rng = rand(1,5)
|
||||
switch(rng)
|
||||
if(1)
|
||||
msg = "A combat drone wing operating in close orbit above Sif has failed to return from a anti-piracy sweep. \
|
||||
If any are sighted, approach with caution."
|
||||
if(2)
|
||||
msg = "Contact has been lost with a combat drone wing in Sif orbit. \
|
||||
If any are sighted in the area, approach with caution."
|
||||
if(3)
|
||||
msg = "Unidentified hackers have targeted a combat drone wing deployed around Sif. \
|
||||
If any are sighted in the area, approach with caution."
|
||||
if(4)
|
||||
msg = "A passing derelict ship's drone defense systems have just activated. \
|
||||
If any are sighted in the area, use caution."
|
||||
if(5)
|
||||
msg = "We're detecting a swarm of small objects approaching your station. \
|
||||
Most likely a bunch of drones. Please exercise caution if you see any."
|
||||
|
||||
command_announcement.Announce(msg, "Rogue drone alert")
|
||||
|
||||
/datum/event2/event/mob_spawning/rogue_drones/start()
|
||||
for(var/i = 1 to drones_to_spawn)
|
||||
spawn_mobs_in_space(
|
||||
mob_type = /mob/living/simple_mob/mechanical/combat_drone/event,
|
||||
number_of_groups = 1,
|
||||
min_size_of_group = 1,
|
||||
max_size_of_group = 1
|
||||
)
|
||||
|
||||
/datum/event2/event/mob_spawning/rogue_drones/end()
|
||||
if(drones_to_spawn)
|
||||
var/number_recovered = 0
|
||||
for(var/mob/living/simple_mob/mechanical/combat_drone/D in spawned_mobs)
|
||||
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread()
|
||||
sparks.set_up(3, 0, D.loc)
|
||||
sparks.start()
|
||||
D.z = using_map.admin_levels[1]
|
||||
D.loot_list = list()
|
||||
|
||||
qdel(D)
|
||||
number_recovered++
|
||||
|
||||
if(number_recovered > spawned_mobs.len * 0.75)
|
||||
command_announcement.Announce("The drones that were malfunctioning have been recovered safely.", "Rogue drone alert")
|
||||
else
|
||||
command_announcement.Announce("We're disappointed at the loss of the drones, but the survivors have been recovered.", "Rogue drone alert")
|
||||
/datum/event2/meta/rogue_drones
|
||||
name = "rogue drones"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaos = 40
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/mob_spawning/rogue_drones
|
||||
|
||||
/datum/event2/meta/rogue_drones/get_weight()
|
||||
. = 10 // Start with a base weight, since this event does provide some value even if no sec is around.
|
||||
. += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20
|
||||
. += metric.count_all_space_mobs() * 40
|
||||
|
||||
|
||||
/datum/event2/event/mob_spawning/rogue_drones
|
||||
length_lower_bound = 15 MINUTES
|
||||
length_upper_bound = 20 MINUTES
|
||||
var/drones_to_spawn = 6
|
||||
|
||||
/datum/event2/event/mob_spawning/rogue_drones/set_up()
|
||||
if(prob(10)) // Small chance for a false alarm.
|
||||
drones_to_spawn = 0
|
||||
|
||||
/datum/event2/event/mob_spawning/rogue_drones/announce()
|
||||
var/msg = null
|
||||
var/rng = rand(1,5)
|
||||
switch(rng)
|
||||
if(1)
|
||||
msg = "A combat drone wing operating in close orbit above Sif has failed to return from a anti-piracy sweep. \
|
||||
If any are sighted, approach with caution."
|
||||
if(2)
|
||||
msg = "Contact has been lost with a combat drone wing in Sif orbit. \
|
||||
If any are sighted in the area, approach with caution."
|
||||
if(3)
|
||||
msg = "Unidentified hackers have targeted a combat drone wing deployed around Sif. \
|
||||
If any are sighted in the area, approach with caution."
|
||||
if(4)
|
||||
msg = "A passing derelict ship's drone defense systems have just activated. \
|
||||
If any are sighted in the area, use caution."
|
||||
if(5)
|
||||
msg = "We're detecting a swarm of small objects approaching your station. \
|
||||
Most likely a bunch of drones. Please exercise caution if you see any."
|
||||
|
||||
command_announcement.Announce(msg, "Rogue drone alert")
|
||||
|
||||
/datum/event2/event/mob_spawning/rogue_drones/start()
|
||||
for(var/i = 1 to drones_to_spawn)
|
||||
spawn_mobs_in_space(
|
||||
mob_type = /mob/living/simple_mob/mechanical/combat_drone/event,
|
||||
number_of_groups = 1,
|
||||
min_size_of_group = 1,
|
||||
max_size_of_group = 1
|
||||
)
|
||||
|
||||
/datum/event2/event/mob_spawning/rogue_drones/end()
|
||||
if(drones_to_spawn)
|
||||
var/number_recovered = 0
|
||||
for(var/mob/living/simple_mob/mechanical/combat_drone/D in spawned_mobs)
|
||||
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread()
|
||||
sparks.set_up(3, 0, D.loc)
|
||||
sparks.start()
|
||||
D.z = using_map.admin_levels[1]
|
||||
D.loot_list = list()
|
||||
|
||||
qdel(D)
|
||||
number_recovered++
|
||||
|
||||
if(number_recovered > spawned_mobs.len * 0.75)
|
||||
command_announcement.Announce("The drones that were malfunctioning have been recovered safely.", "Rogue drone alert")
|
||||
else
|
||||
command_announcement.Announce("We're disappointed at the loss of the drones, but the survivors have been recovered.", "Rogue drone alert")
|
||||
|
||||
@@ -1,93 +1,93 @@
|
||||
/datum/event2/meta/security_screening
|
||||
name = "security screening"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // So this won't get called in the middle of a crisis.
|
||||
event_type = /datum/event2/event/security_screening
|
||||
|
||||
/datum/event2/meta/security_screening/get_weight()
|
||||
. = 0
|
||||
var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY)
|
||||
if(!sec < 2)
|
||||
return 0 // Can't screen with no security.
|
||||
. += sec * 10
|
||||
. += metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2
|
||||
|
||||
// Having ""suspecious"" people present makes this more likely to be picked.
|
||||
var/suspicious_people = 0
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_PROMETHEAN) * 20
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_UNATHI) * 10
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_ZADDAT) * 10
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_SKRELL) * 5 // Not sure why skrell are so high.
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_TAJ) * 5
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_TESHARI) * 5
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_HUMAN_VATBORN) * 5
|
||||
suspicious_people += metric.count_all_FBPs_of_kind(FBP_DRONE) * 20
|
||||
suspicious_people += metric.count_all_FBPs_of_kind(FBP_POSI) * 10
|
||||
if(!suspicious_people)
|
||||
return 0
|
||||
. += suspicious_people
|
||||
|
||||
/datum/event2/event/security_screening
|
||||
var/victim = null
|
||||
var/list/species_weights = list(
|
||||
SPECIES_SKRELL = 9,
|
||||
SPECIES_UNATHI = 15,
|
||||
SPECIES_HUMAN_VATBORN = 6,
|
||||
SPECIES_TESHARI = 2,
|
||||
SPECIES_TAJ = 3,
|
||||
SPECIES_DIONA = 1,
|
||||
SPECIES_ZADDAT = 25,
|
||||
SPECIES_PROMETHEAN = 30
|
||||
)
|
||||
|
||||
var/list/synth_weights = list(
|
||||
FBP_CYBORG = 15,
|
||||
FBP_DRONE = 30,
|
||||
FBP_POSI = 25
|
||||
)
|
||||
|
||||
/datum/event2/event/security_screening/set_up()
|
||||
var/list/end_weights = list()
|
||||
|
||||
// First pass makes popular things more likely to get picked, e.g. 5 prommies vs 1 drone.
|
||||
for(var/species_name in species_weights)
|
||||
var/give_weight = 0
|
||||
for(var/datum/data/record/R in data_core.general)
|
||||
if(R.fields["species"] == species_name)
|
||||
give_weight += species_weights[species_name]
|
||||
|
||||
end_weights[species_name] = give_weight
|
||||
|
||||
for(var/bot_type in synth_weights)
|
||||
var/give_weight = 0
|
||||
for(var/datum/data/record/R in data_core.general)
|
||||
if(R.fields["brain_type"] == bot_type)
|
||||
give_weight += synth_weights[bot_type]
|
||||
|
||||
end_weights[bot_type] = give_weight
|
||||
|
||||
// Second pass eliminates things that don't exist on the station.
|
||||
// It's possible to choose something like drones when all the drones are AFK. This prevents that from happening.
|
||||
while(end_weights.len) // Keep at it until we find someone or run out of possibilities.
|
||||
var/victim_chosen = pickweight(end_weights)
|
||||
|
||||
if(victim_chosen in synth_weights)
|
||||
if(metric.count_all_FBPs_of_kind(victim_chosen) > 0)
|
||||
victim = victim_chosen
|
||||
break
|
||||
else
|
||||
if(metric.count_all_of_specific_species(victim_chosen) > 0)
|
||||
victim = victim_chosen
|
||||
break
|
||||
if(!victim)
|
||||
end_weights -= victim_chosen
|
||||
|
||||
if(!victim)
|
||||
log_debug("Security Screening event failed to find anyone to screen. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/security_screening/announce()
|
||||
command_announcement.Announce("[pick("A nearby Navy vessel", "A Solar official", "A Vir-Gov official", "A NanoTrasen board director")] has \
|
||||
requested the screening of [pick("every other", "every", "suspicious", "willing")] [victim] \
|
||||
personnel onboard \the [location_name()].", "Security Advisement")
|
||||
/datum/event2/meta/security_screening
|
||||
name = "security screening"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // So this won't get called in the middle of a crisis.
|
||||
event_type = /datum/event2/event/security_screening
|
||||
|
||||
/datum/event2/meta/security_screening/get_weight()
|
||||
. = 0
|
||||
var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY)
|
||||
if(!sec < 2)
|
||||
return 0 // Can't screen with no security.
|
||||
. += sec * 10
|
||||
. += metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2
|
||||
|
||||
// Having ""suspecious"" people present makes this more likely to be picked.
|
||||
var/suspicious_people = 0
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_PROMETHEAN) * 20
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_UNATHI) * 10
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_ZADDAT) * 10
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_SKRELL) * 5 // Not sure why skrell are so high.
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_TAJ) * 5
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_TESHARI) * 5
|
||||
suspicious_people += metric.count_all_of_specific_species(SPECIES_HUMAN_VATBORN) * 5
|
||||
suspicious_people += metric.count_all_FBPs_of_kind(FBP_DRONE) * 20
|
||||
suspicious_people += metric.count_all_FBPs_of_kind(FBP_POSI) * 10
|
||||
if(!suspicious_people)
|
||||
return 0
|
||||
. += suspicious_people
|
||||
|
||||
/datum/event2/event/security_screening
|
||||
var/victim = null
|
||||
var/list/species_weights = list(
|
||||
SPECIES_SKRELL = 9,
|
||||
SPECIES_UNATHI = 15,
|
||||
SPECIES_HUMAN_VATBORN = 6,
|
||||
SPECIES_TESHARI = 2,
|
||||
SPECIES_TAJ = 3,
|
||||
SPECIES_DIONA = 1,
|
||||
SPECIES_ZADDAT = 25,
|
||||
SPECIES_PROMETHEAN = 30
|
||||
)
|
||||
|
||||
var/list/synth_weights = list(
|
||||
FBP_CYBORG = 15,
|
||||
FBP_DRONE = 30,
|
||||
FBP_POSI = 25
|
||||
)
|
||||
|
||||
/datum/event2/event/security_screening/set_up()
|
||||
var/list/end_weights = list()
|
||||
|
||||
// First pass makes popular things more likely to get picked, e.g. 5 prommies vs 1 drone.
|
||||
for(var/species_name in species_weights)
|
||||
var/give_weight = 0
|
||||
for(var/datum/data/record/R in data_core.general)
|
||||
if(R.fields["species"] == species_name)
|
||||
give_weight += species_weights[species_name]
|
||||
|
||||
end_weights[species_name] = give_weight
|
||||
|
||||
for(var/bot_type in synth_weights)
|
||||
var/give_weight = 0
|
||||
for(var/datum/data/record/R in data_core.general)
|
||||
if(R.fields["brain_type"] == bot_type)
|
||||
give_weight += synth_weights[bot_type]
|
||||
|
||||
end_weights[bot_type] = give_weight
|
||||
|
||||
// Second pass eliminates things that don't exist on the station.
|
||||
// It's possible to choose something like drones when all the drones are AFK. This prevents that from happening.
|
||||
while(end_weights.len) // Keep at it until we find someone or run out of possibilities.
|
||||
var/victim_chosen = pickweight(end_weights)
|
||||
|
||||
if(victim_chosen in synth_weights)
|
||||
if(metric.count_all_FBPs_of_kind(victim_chosen) > 0)
|
||||
victim = victim_chosen
|
||||
break
|
||||
else
|
||||
if(metric.count_all_of_specific_species(victim_chosen) > 0)
|
||||
victim = victim_chosen
|
||||
break
|
||||
if(!victim)
|
||||
end_weights -= victim_chosen
|
||||
|
||||
if(!victim)
|
||||
log_debug("Security Screening event failed to find anyone to screen. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
/datum/event2/event/security_screening/announce()
|
||||
command_announcement.Announce("[pick("A nearby Navy vessel", "A Solar official", "A Vir-Gov official", "A NanoTrasen board director")] has \
|
||||
requested the screening of [pick("every other", "every", "suspicious", "willing")] [victim] \
|
||||
personnel onboard \the [location_name()].", "Security Advisement")
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
/datum/event2/meta/spider_infestation
|
||||
name = "spider infestation"
|
||||
event_class = "spiders"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE)
|
||||
chaos = 30
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/spider_infestation
|
||||
|
||||
/datum/event2/meta/spider_infestation/weak
|
||||
name = "weak spider infestation"
|
||||
chaos = 20
|
||||
event_type = /datum/event2/event/spider_infestation/weak
|
||||
|
||||
|
||||
/datum/event2/meta/spider_infestation/get_weight()
|
||||
. = 10
|
||||
. += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20
|
||||
. += metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10
|
||||
|
||||
|
||||
// This isn't a /mob_spawning subtype since spiderlings aren't actually mobs.
|
||||
|
||||
/datum/event2/event/spider_infestation
|
||||
var/spiders_to_spawn = 8
|
||||
var/spiderling_to_spawn = /obj/effect/spider/spiderling
|
||||
|
||||
/datum/event2/event/spider_infestation/weak
|
||||
spiders_to_spawn = 5
|
||||
spiderling_to_spawn = /obj/effect/spider/spiderling/stunted
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/spider_infestation/announce()
|
||||
command_announcement.Announce("Unidentified lifesigns detected coming aboard \the [location_name()]. \
|
||||
Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg')
|
||||
|
||||
/datum/event2/event/spider_infestation/start()
|
||||
var/list/vents = list()
|
||||
for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines)
|
||||
if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in get_location_z_levels()))
|
||||
if(temp_vent.network.normal_members.len > 50)
|
||||
vents += temp_vent
|
||||
|
||||
while((spiders_to_spawn >= 1) && vents.len)
|
||||
var/obj/vent = pick(vents)
|
||||
new spiderling_to_spawn(vent.loc)
|
||||
log_debug("Spider infestation event spawned a spiderling at [get_area(vent)].")
|
||||
vents -= vent
|
||||
spiders_to_spawn--
|
||||
/datum/event2/meta/spider_infestation
|
||||
name = "spider infestation"
|
||||
event_class = "spiders"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE)
|
||||
chaos = 30
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/spider_infestation
|
||||
|
||||
/datum/event2/meta/spider_infestation/weak
|
||||
name = "weak spider infestation"
|
||||
chaos = 20
|
||||
event_type = /datum/event2/event/spider_infestation/weak
|
||||
|
||||
|
||||
/datum/event2/meta/spider_infestation/get_weight()
|
||||
. = 10
|
||||
. += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20
|
||||
. += metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10
|
||||
|
||||
|
||||
// This isn't a /mob_spawning subtype since spiderlings aren't actually mobs.
|
||||
|
||||
/datum/event2/event/spider_infestation
|
||||
var/spiders_to_spawn = 8
|
||||
var/spiderling_to_spawn = /obj/effect/spider/spiderling
|
||||
|
||||
/datum/event2/event/spider_infestation/weak
|
||||
spiders_to_spawn = 5
|
||||
spiderling_to_spawn = /obj/effect/spider/spiderling/stunted
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/spider_infestation/announce()
|
||||
command_announcement.Announce("Unidentified lifesigns detected coming aboard \the [location_name()]. \
|
||||
Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg')
|
||||
|
||||
/datum/event2/event/spider_infestation/start()
|
||||
var/list/vents = list()
|
||||
for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines)
|
||||
if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in get_location_z_levels()))
|
||||
if(temp_vent.network.normal_members.len > 50)
|
||||
vents += temp_vent
|
||||
|
||||
while((spiders_to_spawn >= 1) && vents.len)
|
||||
var/obj/vent = pick(vents)
|
||||
new spiderling_to_spawn(vent.loc)
|
||||
log_debug("Spider infestation event spawned a spiderling at [get_area(vent)].")
|
||||
vents -= vent
|
||||
spiders_to_spawn--
|
||||
|
||||
@@ -1,64 +1,64 @@
|
||||
// Base type used for inheritence.
|
||||
/datum/event2/meta/stowaway
|
||||
event_class = "stowaway"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/stowaway
|
||||
var/safe_for_extended = FALSE
|
||||
|
||||
/datum/event2/meta/stowaway/normal
|
||||
name = "stowaway - normal"
|
||||
safe_for_extended = TRUE
|
||||
|
||||
/datum/event2/meta/stowaway/renegade
|
||||
name = "stowaway - renegade"
|
||||
chaos = 30
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/stowaway/renegade
|
||||
|
||||
/datum/event2/meta/stowaway/infiltrator
|
||||
name = "stowaway - infiltrator"
|
||||
chaos = 60
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/stowaway/infiltrator
|
||||
|
||||
/datum/event2/meta/stowaway/get_weight()
|
||||
if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended)
|
||||
return 0
|
||||
|
||||
var/security = metric.count_people_in_department(DEPARTMENT_SECURITY)
|
||||
var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - security
|
||||
var/ghost_activity = metric.assess_all_dead_mobs() / 100
|
||||
|
||||
return ( (security * 20) + (everyone * 2) ) * ghost_activity
|
||||
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/stowaway
|
||||
pod_type = /obj/structure/ghost_pod/ghost_activated/human
|
||||
desired_turf_areas = list(/area/maintenance)
|
||||
announce_delay_lower_bound = 15 MINUTES
|
||||
announce_delay_upper_bound = 30 MINUTES
|
||||
var/antag_type = MODE_STOWAWAY
|
||||
var/announce_odds = 20
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/stowaway/renegade
|
||||
antag_type = MODE_RENEGADE
|
||||
announce_odds = 33
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/stowaway/infiltrator
|
||||
antag_type = MODE_INFILTRATOR
|
||||
announce_odds = 50
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/stowaway/post_pod_creation(obj/structure/ghost_pod/ghost_activated/human/pod)
|
||||
pod.make_antag = antag_type
|
||||
pod.occupant_type = "[pod.make_antag] [pod.occupant_type]"
|
||||
|
||||
say_dead_object("[span("notice", pod.occupant_type)] pod is now available in \the [get_area(pod)].", pod)
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/stowaway/announce()
|
||||
if(prob(announce_odds))
|
||||
if(atc?.squelched)
|
||||
return
|
||||
atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution is advised as \
|
||||
[pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \
|
||||
has been detected passing multiple local stations.")
|
||||
// Base type used for inheritence.
|
||||
/datum/event2/meta/stowaway
|
||||
event_class = "stowaway"
|
||||
departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE)
|
||||
chaos = 10
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/stowaway
|
||||
var/safe_for_extended = FALSE
|
||||
|
||||
/datum/event2/meta/stowaway/normal
|
||||
name = "stowaway - normal"
|
||||
safe_for_extended = TRUE
|
||||
|
||||
/datum/event2/meta/stowaway/renegade
|
||||
name = "stowaway - renegade"
|
||||
chaos = 30
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/stowaway/renegade
|
||||
|
||||
/datum/event2/meta/stowaway/infiltrator
|
||||
name = "stowaway - infiltrator"
|
||||
chaos = 60
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/stowaway/infiltrator
|
||||
|
||||
/datum/event2/meta/stowaway/get_weight()
|
||||
if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended)
|
||||
return 0
|
||||
|
||||
var/security = metric.count_people_in_department(DEPARTMENT_SECURITY)
|
||||
var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - security
|
||||
var/ghost_activity = metric.assess_all_dead_mobs() / 100
|
||||
|
||||
return ( (security * 20) + (everyone * 2) ) * ghost_activity
|
||||
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/stowaway
|
||||
pod_type = /obj/structure/ghost_pod/ghost_activated/human
|
||||
desired_turf_areas = list(/area/maintenance)
|
||||
announce_delay_lower_bound = 15 MINUTES
|
||||
announce_delay_upper_bound = 30 MINUTES
|
||||
var/antag_type = MODE_STOWAWAY
|
||||
var/announce_odds = 20
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/stowaway/renegade
|
||||
antag_type = MODE_RENEGADE
|
||||
announce_odds = 33
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/stowaway/infiltrator
|
||||
antag_type = MODE_INFILTRATOR
|
||||
announce_odds = 50
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/stowaway/post_pod_creation(obj/structure/ghost_pod/ghost_activated/human/pod)
|
||||
pod.make_antag = antag_type
|
||||
pod.occupant_type = "[pod.make_antag] [pod.occupant_type]"
|
||||
|
||||
say_dead_object("[span("notice", pod.occupant_type)] pod is now available in \the [get_area(pod)].", pod)
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/stowaway/announce()
|
||||
if(prob(announce_odds))
|
||||
if(atc?.squelched)
|
||||
return
|
||||
atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution is advised as \
|
||||
[pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \
|
||||
has been detected passing multiple local stations.")
|
||||
|
||||
@@ -1,71 +1,71 @@
|
||||
// This event sends a few carp after someone hanging around in space, unannounced.
|
||||
|
||||
/datum/event2/meta/surprise_carp
|
||||
name = "surprise carp"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
chaos = 20
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/surprise_carp
|
||||
|
||||
/datum/event2/meta/surprise_carp/get_weight()
|
||||
return metric.count_all_space_mobs() * 50
|
||||
|
||||
|
||||
/datum/event2/event/surprise_carp
|
||||
var/mob/living/victim = null
|
||||
|
||||
/datum/event2/event/surprise_carp/set_up()
|
||||
var/list/potential_victims = list()
|
||||
for(var/mob/living/L in player_list)
|
||||
if(!(L.z in get_location_z_levels()))
|
||||
continue // Not on the right z-level.
|
||||
if(L.stat)
|
||||
continue // Don't want dead people.
|
||||
if(istype(get_turf(L), /turf/space) && istype(get_area(L),/area/space))
|
||||
potential_victims += L
|
||||
|
||||
if(potential_victims.len)
|
||||
victim = pick(potential_victims)
|
||||
|
||||
/datum/event2/event/surprise_carp/start()
|
||||
if(!victim)
|
||||
log_debug("Failed to find a target for surprise carp attack. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
var/number_of_carp = rand(1, 2)
|
||||
log_debug("Sending [number_of_carp] carp\s after \the [victim].")
|
||||
// Getting off screen tiles is kind of tricky due to potential edge cases that could arise.
|
||||
// The method we're gonna do is make a big square around the victim, then
|
||||
// subtract a smaller square in the middle for the default vision range.
|
||||
var/list/outer_square = get_safe_square(victim, world.view + 3)
|
||||
var/list/inner_square = get_safe_square(victim, world.view)
|
||||
|
||||
var/list/donut = outer_square - inner_square
|
||||
for(var/T in donut)
|
||||
if(!istype(T, /turf/space))
|
||||
donut -= T
|
||||
|
||||
for(var/i = 1 to number_of_carp)
|
||||
var/turf/spawning_turf = pick(donut)
|
||||
|
||||
if(spawning_turf)
|
||||
var/mob/living/simple_mob/animal/space/carp/C = new(spawning_turf)
|
||||
// Ask carp to swim onto the victim's screen. The AI will then switch to hostile and try to eat them.
|
||||
C.ai_holder?.give_destination(get_turf(victim))
|
||||
else
|
||||
log_debug("Surprise carp attack failed to find any space turfs offscreen to the victim.")
|
||||
|
||||
// Gets suitable spots for carp to spawn, without risk of going off the edge of the map.
|
||||
// If there is demand for this proc, then it can easily be made independant and moved into one of the helper files.
|
||||
/datum/event2/event/surprise_carp/proc/get_safe_square(atom/center, radius)
|
||||
var/lower_left_x = max(center.x - radius, 1 + TRANSITIONEDGE)
|
||||
var/lower_left_y = max(center.y - radius, 1 + TRANSITIONEDGE)
|
||||
|
||||
var/upper_right_x = min(center.x + radius, world.maxx - TRANSITIONEDGE)
|
||||
var/upper_right_y = min(center.y + radius, world.maxy - TRANSITIONEDGE)
|
||||
|
||||
var/turf/lower_left = locate(lower_left_x, lower_left_y, victim.z)
|
||||
var/turf/upper_right = locate(upper_right_x, upper_right_y, victim.z)
|
||||
|
||||
return block(lower_left, upper_right)
|
||||
// This event sends a few carp after someone hanging around in space, unannounced.
|
||||
|
||||
/datum/event2/meta/surprise_carp
|
||||
name = "surprise carp"
|
||||
departments = list(DEPARTMENT_EVERYONE)
|
||||
chaos = 20
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/surprise_carp
|
||||
|
||||
/datum/event2/meta/surprise_carp/get_weight()
|
||||
return metric.count_all_space_mobs() * 50
|
||||
|
||||
|
||||
/datum/event2/event/surprise_carp
|
||||
var/mob/living/victim = null
|
||||
|
||||
/datum/event2/event/surprise_carp/set_up()
|
||||
var/list/potential_victims = list()
|
||||
for(var/mob/living/L in player_list)
|
||||
if(!(L.z in get_location_z_levels()))
|
||||
continue // Not on the right z-level.
|
||||
if(L.stat)
|
||||
continue // Don't want dead people.
|
||||
if(istype(get_turf(L), /turf/space) && istype(get_area(L),/area/space))
|
||||
potential_victims += L
|
||||
|
||||
if(potential_victims.len)
|
||||
victim = pick(potential_victims)
|
||||
|
||||
/datum/event2/event/surprise_carp/start()
|
||||
if(!victim)
|
||||
log_debug("Failed to find a target for surprise carp attack. Aborting.")
|
||||
abort()
|
||||
return
|
||||
|
||||
var/number_of_carp = rand(1, 2)
|
||||
log_debug("Sending [number_of_carp] carp\s after \the [victim].")
|
||||
// Getting off screen tiles is kind of tricky due to potential edge cases that could arise.
|
||||
// The method we're gonna do is make a big square around the victim, then
|
||||
// subtract a smaller square in the middle for the default vision range.
|
||||
var/list/outer_square = get_safe_square(victim, world.view + 3)
|
||||
var/list/inner_square = get_safe_square(victim, world.view)
|
||||
|
||||
var/list/donut = outer_square - inner_square
|
||||
for(var/T in donut)
|
||||
if(!istype(T, /turf/space))
|
||||
donut -= T
|
||||
|
||||
for(var/i = 1 to number_of_carp)
|
||||
var/turf/spawning_turf = pick(donut)
|
||||
|
||||
if(spawning_turf)
|
||||
var/mob/living/simple_mob/animal/space/carp/C = new(spawning_turf)
|
||||
// Ask carp to swim onto the victim's screen. The AI will then switch to hostile and try to eat them.
|
||||
C.ai_holder?.give_destination(get_turf(victim))
|
||||
else
|
||||
log_debug("Surprise carp attack failed to find any space turfs offscreen to the victim.")
|
||||
|
||||
// Gets suitable spots for carp to spawn, without risk of going off the edge of the map.
|
||||
// If there is demand for this proc, then it can easily be made independant and moved into one of the helper files.
|
||||
/datum/event2/event/surprise_carp/proc/get_safe_square(atom/center, radius)
|
||||
var/lower_left_x = max(center.x - radius, 1 + TRANSITIONEDGE)
|
||||
var/lower_left_y = max(center.y - radius, 1 + TRANSITIONEDGE)
|
||||
|
||||
var/upper_right_x = min(center.x + radius, world.maxx - TRANSITIONEDGE)
|
||||
var/upper_right_y = min(center.y + radius, world.maxy - TRANSITIONEDGE)
|
||||
|
||||
var/turf/lower_left = locate(lower_left_x, lower_left_y, victim.z)
|
||||
var/turf/upper_right = locate(upper_right_x, upper_right_y, victim.z)
|
||||
|
||||
return block(lower_left, upper_right)
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
// This is just porting the event to the new new event system, it's not been balanced in any way
|
||||
// so don't @ me if these things are grossly OP.
|
||||
/datum/event2/meta/swarm_boarder
|
||||
event_class = "swarm boarder"
|
||||
departments = list(DEPARTMENT_EVERYONE, DEPARTMENT_SECURITY, DEPARTMENT_ENGINEERING)
|
||||
chaos = 60
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT
|
||||
enabled = FALSE // Turns out they are in fact grossly OP.
|
||||
var/safe_for_extended = FALSE
|
||||
|
||||
/datum/event2/meta/swarm_boarder/get_weight()
|
||||
if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended)
|
||||
return 0
|
||||
|
||||
var/security = metric.count_people_in_department(DEPARTMENT_SECURITY)
|
||||
var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING)
|
||||
var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (engineering + security)
|
||||
|
||||
var/ghost_activity = metric.assess_all_dead_mobs() / 100
|
||||
|
||||
return ( (security * 20) + (engineering * 10) + (everyone * 2) ) * ghost_activity
|
||||
|
||||
/datum/event2/meta/swarm_boarder/normal
|
||||
name = "swarmer shell - normal"
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder
|
||||
|
||||
/datum/event2/meta/swarm_boarder/melee
|
||||
name = "swarmer shell - melee"
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/melee
|
||||
|
||||
/datum/event2/meta/swarm_boarder/gunner
|
||||
name = "swarmer shell - gunner"
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/swarm_boarder
|
||||
announce_delay_lower_bound = 5 MINUTES
|
||||
announce_delay_upper_bound = 15 MINUTES
|
||||
pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event
|
||||
desired_turf_areas = list(/area/maintenance)
|
||||
var/announce_odds = 80
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/swarm_boarder/melee
|
||||
pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner
|
||||
pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/swarm_boarder/announce()
|
||||
if(prob(announce_odds))
|
||||
if(atc?.squelched)
|
||||
atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution \
|
||||
is advised as [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \
|
||||
has been detected passing multiple local stations.")
|
||||
// This is just porting the event to the new new event system, it's not been balanced in any way
|
||||
// so don't @ me if these things are grossly OP.
|
||||
/datum/event2/meta/swarm_boarder
|
||||
event_class = "swarm boarder"
|
||||
departments = list(DEPARTMENT_EVERYONE, DEPARTMENT_SECURITY, DEPARTMENT_ENGINEERING)
|
||||
chaos = 60
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT
|
||||
enabled = FALSE // Turns out they are in fact grossly OP.
|
||||
var/safe_for_extended = FALSE
|
||||
|
||||
/datum/event2/meta/swarm_boarder/get_weight()
|
||||
if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended)
|
||||
return 0
|
||||
|
||||
var/security = metric.count_people_in_department(DEPARTMENT_SECURITY)
|
||||
var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING)
|
||||
var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (engineering + security)
|
||||
|
||||
var/ghost_activity = metric.assess_all_dead_mobs() / 100
|
||||
|
||||
return ( (security * 20) + (engineering * 10) + (everyone * 2) ) * ghost_activity
|
||||
|
||||
/datum/event2/meta/swarm_boarder/normal
|
||||
name = "swarmer shell - normal"
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder
|
||||
|
||||
/datum/event2/meta/swarm_boarder/melee
|
||||
name = "swarmer shell - melee"
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/melee
|
||||
|
||||
/datum/event2/meta/swarm_boarder/gunner
|
||||
name = "swarmer shell - gunner"
|
||||
event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/swarm_boarder
|
||||
announce_delay_lower_bound = 5 MINUTES
|
||||
announce_delay_upper_bound = 15 MINUTES
|
||||
pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event
|
||||
desired_turf_areas = list(/area/maintenance)
|
||||
var/announce_odds = 80
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/swarm_boarder/melee
|
||||
pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner
|
||||
pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner
|
||||
|
||||
/datum/event2/event/ghost_pod_spawner/swarm_boarder/announce()
|
||||
if(prob(announce_odds))
|
||||
if(atc?.squelched)
|
||||
atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution \
|
||||
is advised as [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \
|
||||
has been detected passing multiple local stations.")
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
/datum/event2/meta/ion_storm
|
||||
name = "ion storm"
|
||||
departments = list(DEPARTMENT_SYNTHETIC)
|
||||
chaos = 40
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/ion_storm
|
||||
|
||||
/datum/event2/meta/ion_storm/get_weight()
|
||||
var/list/bots = metric.get_people_in_department(DEPARTMENT_SYNTHETIC)
|
||||
. = 5 // A small chance even if no synths are on, since it can still emag beepsky.
|
||||
for(var/mob/living/silicon/S in bots)
|
||||
if(istype(S, /mob/living/silicon/robot/drone)) // Drones don't get their laws screwed with, so don't count them.
|
||||
continue
|
||||
. += 40
|
||||
|
||||
|
||||
/datum/event2/event/ion_storm
|
||||
announce_delay_lower_bound = 7 MINUTES
|
||||
announce_delay_upper_bound = 15 MINUTES
|
||||
var/bot_emag_chance = 30 // This is rolled once, instead of once a second for a minute like the old version.
|
||||
var/announce_odds = 50 // Probability of an announcement actually happening after the delay.
|
||||
|
||||
/datum/event2/event/ion_storm/start()
|
||||
// Ion laws.
|
||||
for(var/mob/living/silicon/target in silicon_mob_list)
|
||||
if(target.z in get_location_z_levels())
|
||||
// Don't ion law drons.
|
||||
if(istype(target, /mob/living/silicon/robot/drone))
|
||||
continue
|
||||
|
||||
// Or borgs with an AI (they'll get their AI's ion law anyways).
|
||||
if(istype(target, /mob/living/silicon/robot))
|
||||
var/mob/living/silicon/robot/R = target
|
||||
if(R.connected_ai)
|
||||
continue
|
||||
if(R.shell)
|
||||
continue
|
||||
|
||||
// Crew member names, and excluding off station antags, are handled by `generate_ion_law()` automatically.
|
||||
var/law = target.generate_ion_law()
|
||||
target.add_ion_law(law)
|
||||
target.show_laws()
|
||||
|
||||
// Emag bots.
|
||||
for(var/mob/living/bot/B in mob_list)
|
||||
if(B.z in get_location_z_levels())
|
||||
if(prob(bot_emag_chance))
|
||||
B.emag_act(1)
|
||||
|
||||
// Messaging server spam filters.
|
||||
// This might be better served as a seperate event since it seems more like a hacker attack than a natural occurance.
|
||||
if(message_servers)
|
||||
for(var/obj/machinery/message_server/MS in message_servers)
|
||||
if(MS.z in get_location_z_levels())
|
||||
MS.spamfilter.Cut()
|
||||
for (var/i = 1, i <= MS.spamfilter_limit, i++)
|
||||
MS.spamfilter += pick("warble","help","almach","ai","liberty","freedom","drugs", "[using_map.station_short]", \
|
||||
"admin","sol","security","meow","_","monkey","-","moron","pizza","message","spam",\
|
||||
"director", "Hello", "Hi!"," ","nuke","crate","taj","xeno")
|
||||
|
||||
/datum/event2/event/ion_storm/announce()
|
||||
if(prob(announce_odds))
|
||||
command_announcement.Announce("An ion storm was detected within proximity to \the [location_name()] recently. \
|
||||
Check all AI controlled equipment for corruption.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg')
|
||||
|
||||
// Fake variant used by traitors.
|
||||
/datum/event2/event/ion_storm/fake
|
||||
// Fake ion storms announce instantly, so the traitor can time it to make the AI look suspicious.
|
||||
announce_delay_lower_bound = 0
|
||||
announce_delay_upper_bound = 0
|
||||
announce_odds = 100
|
||||
|
||||
/datum/event2/event/ion_storm/fake/start()
|
||||
/datum/event2/meta/ion_storm
|
||||
name = "ion storm"
|
||||
departments = list(DEPARTMENT_SYNTHETIC)
|
||||
chaos = 40
|
||||
chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT
|
||||
event_type = /datum/event2/event/ion_storm
|
||||
|
||||
/datum/event2/meta/ion_storm/get_weight()
|
||||
var/list/bots = metric.get_people_in_department(DEPARTMENT_SYNTHETIC)
|
||||
. = 5 // A small chance even if no synths are on, since it can still emag beepsky.
|
||||
for(var/mob/living/silicon/S in bots)
|
||||
if(istype(S, /mob/living/silicon/robot/drone)) // Drones don't get their laws screwed with, so don't count them.
|
||||
continue
|
||||
. += 40
|
||||
|
||||
|
||||
/datum/event2/event/ion_storm
|
||||
announce_delay_lower_bound = 7 MINUTES
|
||||
announce_delay_upper_bound = 15 MINUTES
|
||||
var/bot_emag_chance = 30 // This is rolled once, instead of once a second for a minute like the old version.
|
||||
var/announce_odds = 50 // Probability of an announcement actually happening after the delay.
|
||||
|
||||
/datum/event2/event/ion_storm/start()
|
||||
// Ion laws.
|
||||
for(var/mob/living/silicon/target in silicon_mob_list)
|
||||
if(target.z in get_location_z_levels())
|
||||
// Don't ion law drons.
|
||||
if(istype(target, /mob/living/silicon/robot/drone))
|
||||
continue
|
||||
|
||||
// Or borgs with an AI (they'll get their AI's ion law anyways).
|
||||
if(istype(target, /mob/living/silicon/robot))
|
||||
var/mob/living/silicon/robot/R = target
|
||||
if(R.connected_ai)
|
||||
continue
|
||||
if(R.shell)
|
||||
continue
|
||||
|
||||
// Crew member names, and excluding off station antags, are handled by `generate_ion_law()` automatically.
|
||||
var/law = target.generate_ion_law()
|
||||
target.add_ion_law(law)
|
||||
target.show_laws()
|
||||
|
||||
// Emag bots.
|
||||
for(var/mob/living/bot/B in mob_list)
|
||||
if(B.z in get_location_z_levels())
|
||||
if(prob(bot_emag_chance))
|
||||
B.emag_act(1)
|
||||
|
||||
// Messaging server spam filters.
|
||||
// This might be better served as a seperate event since it seems more like a hacker attack than a natural occurance.
|
||||
if(message_servers)
|
||||
for(var/obj/machinery/message_server/MS in message_servers)
|
||||
if(MS.z in get_location_z_levels())
|
||||
MS.spamfilter.Cut()
|
||||
for (var/i = 1, i <= MS.spamfilter_limit, i++)
|
||||
MS.spamfilter += pick("warble","help","almach","ai","liberty","freedom","drugs", "[using_map.station_short]", \
|
||||
"admin","sol","security","meow","_","monkey","-","moron","pizza","message","spam",\
|
||||
"director", "Hello", "Hi!"," ","nuke","crate","taj","xeno")
|
||||
|
||||
/datum/event2/event/ion_storm/announce()
|
||||
if(prob(announce_odds))
|
||||
command_announcement.Announce("An ion storm was detected within proximity to \the [location_name()] recently. \
|
||||
Check all AI controlled equipment for corruption.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg')
|
||||
|
||||
// Fake variant used by traitors.
|
||||
/datum/event2/event/ion_storm/fake
|
||||
// Fake ion storms announce instantly, so the traitor can time it to make the AI look suspicious.
|
||||
announce_delay_lower_bound = 0
|
||||
announce_delay_upper_bound = 0
|
||||
announce_odds = 100
|
||||
|
||||
/datum/event2/event/ion_storm/fake/start()
|
||||
return
|
||||
@@ -1,81 +1,81 @@
|
||||
// The 'meta' object contains information about its assigned 'action' object, like what departments it will affect.
|
||||
// It is directly held inside the Game Master Event System.
|
||||
|
||||
// The code for actually executing an event should go inside the event object instead.
|
||||
/datum/event2/meta
|
||||
// Name used for organization, shown in the debug verb for the GM system.
|
||||
// If null, the meta event will be discarded when the GM system initializes, so it is safe to use nameless subtypes for inheritence.
|
||||
var/name = null
|
||||
|
||||
// If FALSE, the GM system won't pick this.
|
||||
// Some events set this to FALSE after running, to avoid running twice.
|
||||
var/enabled = TRUE
|
||||
|
||||
// What departments the event attached might affect.
|
||||
var/list/departments = list(DEPARTMENT_EVERYONE)
|
||||
|
||||
// A guess on how disruptive to a round the event might be. If the action is chosen, the GM's
|
||||
// 'danger' score is increased by this number.
|
||||
// Negative numbers could be used to signify helpful events.
|
||||
var/chaos = 0
|
||||
|
||||
// A threshold the GM will use alongside its 'danger' score, to determine if it should pass
|
||||
// over the event associated with this object. The decision is based on
|
||||
var/chaotic_threshold = null
|
||||
|
||||
// If true, the event won't have it's `enabled` var set to FALSE when ran by the GM system.
|
||||
var/reusable = FALSE
|
||||
|
||||
// A string used to identify a 'class' of similar events.
|
||||
// If the event is not reusable, than all events sharing the same class are disabled.
|
||||
// Useful if you only ever want one event per round while having a lot of different subtypes of the event.
|
||||
var/event_class = null
|
||||
|
||||
// Counter for how many times this event has been picked by the GM.
|
||||
// Can be used to make event repeats discouraged but not forbidden by adjusting the weight based on it.
|
||||
var/times_ran = 0
|
||||
|
||||
// The type path to the event associated with this meta object.
|
||||
// When the GM chooses this event, a new instance is made.
|
||||
// Seperate instances allow for multiple concurrent events without sharing state, e.g. two blobs.
|
||||
var/event_type = null
|
||||
|
||||
|
||||
// Called by the GM system to actually start an event.
|
||||
/datum/event2/meta/proc/make_event()
|
||||
var/datum/event2/event/E = new event_type()
|
||||
E.execute()
|
||||
return E
|
||||
|
||||
// Returns a TRUE or FALSE for if the GM system should be able to pick this event.
|
||||
// Can be extended to check for more than just `enabled` later.
|
||||
/datum/event2/meta/proc/can_pick()
|
||||
return enabled
|
||||
|
||||
/*
|
||||
* Procs to Override
|
||||
*/
|
||||
|
||||
// Returns a number that determines how likely it is for the event to be picked over others.
|
||||
// Individual events should override this for their own weights.
|
||||
/datum/event2/meta/proc/get_weight()
|
||||
return 0
|
||||
|
||||
|
||||
/datum/event2/meta/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG))
|
||||
message_admins("[usr] has attempted to manipulate an event without sufficent privilages.")
|
||||
return
|
||||
|
||||
if(href_list["force"])
|
||||
// SSevent_ticker.start_event(event_type) // VOREStation Edit - We don't use SSgame_master yet.
|
||||
message_admins("Event '[name]' was forced by [usr.key].")
|
||||
|
||||
if(href_list["toggle"])
|
||||
enabled = !enabled
|
||||
message_admins("Event '[name]' was toggled [enabled ? "on" : "off"] by [usr.key].")
|
||||
|
||||
// The 'meta' object contains information about its assigned 'action' object, like what departments it will affect.
|
||||
// It is directly held inside the Game Master Event System.
|
||||
|
||||
// The code for actually executing an event should go inside the event object instead.
|
||||
/datum/event2/meta
|
||||
// Name used for organization, shown in the debug verb for the GM system.
|
||||
// If null, the meta event will be discarded when the GM system initializes, so it is safe to use nameless subtypes for inheritence.
|
||||
var/name = null
|
||||
|
||||
// If FALSE, the GM system won't pick this.
|
||||
// Some events set this to FALSE after running, to avoid running twice.
|
||||
var/enabled = TRUE
|
||||
|
||||
// What departments the event attached might affect.
|
||||
var/list/departments = list(DEPARTMENT_EVERYONE)
|
||||
|
||||
// A guess on how disruptive to a round the event might be. If the action is chosen, the GM's
|
||||
// 'danger' score is increased by this number.
|
||||
// Negative numbers could be used to signify helpful events.
|
||||
var/chaos = 0
|
||||
|
||||
// A threshold the GM will use alongside its 'danger' score, to determine if it should pass
|
||||
// over the event associated with this object. The decision is based on
|
||||
var/chaotic_threshold = null
|
||||
|
||||
// If true, the event won't have it's `enabled` var set to FALSE when ran by the GM system.
|
||||
var/reusable = FALSE
|
||||
|
||||
// A string used to identify a 'class' of similar events.
|
||||
// If the event is not reusable, than all events sharing the same class are disabled.
|
||||
// Useful if you only ever want one event per round while having a lot of different subtypes of the event.
|
||||
var/event_class = null
|
||||
|
||||
// Counter for how many times this event has been picked by the GM.
|
||||
// Can be used to make event repeats discouraged but not forbidden by adjusting the weight based on it.
|
||||
var/times_ran = 0
|
||||
|
||||
// The type path to the event associated with this meta object.
|
||||
// When the GM chooses this event, a new instance is made.
|
||||
// Seperate instances allow for multiple concurrent events without sharing state, e.g. two blobs.
|
||||
var/event_type = null
|
||||
|
||||
|
||||
// Called by the GM system to actually start an event.
|
||||
/datum/event2/meta/proc/make_event()
|
||||
var/datum/event2/event/E = new event_type()
|
||||
E.execute()
|
||||
return E
|
||||
|
||||
// Returns a TRUE or FALSE for if the GM system should be able to pick this event.
|
||||
// Can be extended to check for more than just `enabled` later.
|
||||
/datum/event2/meta/proc/can_pick()
|
||||
return enabled
|
||||
|
||||
/*
|
||||
* Procs to Override
|
||||
*/
|
||||
|
||||
// Returns a number that determines how likely it is for the event to be picked over others.
|
||||
// Individual events should override this for their own weights.
|
||||
/datum/event2/meta/proc/get_weight()
|
||||
return 0
|
||||
|
||||
|
||||
/datum/event2/meta/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG))
|
||||
message_admins("[usr] has attempted to manipulate an event without sufficent privilages.")
|
||||
return
|
||||
|
||||
if(href_list["force"])
|
||||
// SSevent_ticker.start_event(event_type) // VOREStation Edit - We don't use SSgame_master yet.
|
||||
message_admins("Event '[name]' was forced by [usr.key].")
|
||||
|
||||
if(href_list["toggle"])
|
||||
enabled = !enabled
|
||||
message_admins("Event '[name]' was toggled [enabled ? "on" : "off"] by [usr.key].")
|
||||
|
||||
// SSgame_master.interact(usr) // To refresh the UI. // VOREStation Edit - We don't use SSgame_master yet.
|
||||
Reference in New Issue
Block a user