mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-01-01 04:52:16 +00:00
Adds 3 Crescent Expanse regions due to how large a region it is: - Crescent Expanse (West): Nralakk and Solarian heavy region. - Crescent Expanse (East): Coalition and Solarian heavy region. - Crescent Expanse (Uncharted): Deep Crescent expanse, independents and certain surveyors only. Also adds a Lemurian Sea (Uncharted) sector if we ever go there. Adds a `ccia_link` variable for sectors. Only usage is to block outbound faxes to Central Command. For uncharted sectors. EBS remains an option due to necessary CCIA functions (eg. scuttling), per Bear's wishes. Adds `low_supply` variants of most vending machines and dispensers. For later use, none mapped in. Removes the `TEMPLATE_FLAG_SPAWN_GUARANTEED` flag from the sensors array, because it meant it wasn't affected by `sectors_blacklist`. The sensors array isn't so important it needs to be in every sector anyway. Removes Burzsia from the `ALL_DANGEROUS_SECTORS` list, as the crisis has passed and humanitarian relief efforts completed. Adds the `ALL_EVENT_ONLY_SECTORS` for limited usage during event arcs/similar where canon odysseys or certain thirdparties may infringe upon an arc's narrative. Allows selective enabling/disabling of canon odysseys or third parties for said arcs. Not intended for liberal application. ### Asset Licenses The following assets that **have not** been created by myself are included in this PR: | Path | Original Author | License | | --- | --- | --- | sound/music/lobby/crescent_expanse/crescent_expanse_1.ogg | "Lüüü - Weedance" by Lüüü, Obtained from https://myprivateunderground.bandcamp.com/track/lu-u-u-weedance | CC BY-NC-SA 3.0. sound/music/lobby/crescent_expanse/crescent_expanse_2.ogg | "Little Bradley - Sunset Drive" by Little Bradley, Obtained from https://myprivateunderground.bandcamp.com/track/little-bradley-sunset-drive | CC BY-NC 3.0 sound/music/lobby/dangerous_space/dangerous_space_1.ogg | "You Can't Kill The Boogeyman" by Karl Casey. Obtained from https://karlcasey.bandcamp.com/track/you-can-t-kill-the-boogeyman | CC BY 3.0 sound/music/lobby/dangerous_space_2.ogg | "Prison Planet" by Karl Casey, Obtained from https://karlcasey.bandcamp.com/track/prison-planet | CC BY 3.0 sound/music/lobby/lights_edge/lights_edge_1.ogg | "Is Anyone Left?" by Karl Casey. Obtained from https://karlcasey.bandcamp.com/track/is-anyone-left | CC BY 3.0 sound/music/lobby/lights_edge/lights_edge_2.ogg | "Running From The Wendigo" by Karl Casey. Obtained from https://karlcasey.bandcamp.com/track/running-from-the-wendigo | CC BY 3.0
264 lines
10 KiB
Plaintext
264 lines
10 KiB
Plaintext
SUBSYSTEM_DEF(odyssey)
|
|
name = "Odyssey"
|
|
init_order = INIT_ORDER_ODYSSEY
|
|
runlevels = RUNLEVELS_PLAYING
|
|
can_fire = FALSE //We process only if we are running an odyssey scenario, this is set to TRUE by `pick_odyssey()`
|
|
|
|
/// The selected scenario singleton.
|
|
var/singleton/scenario/scenario
|
|
/// The z-levels that the odyssey takes place in.
|
|
var/list/scenario_zlevels = list()
|
|
/// This is the site the odyssey takes place on. If null, then the mission takes place on a non-site zlevel.
|
|
/// Should only be changed through set_scenario_site().
|
|
var/datum/map_template/ruin/away_site/scenario_site
|
|
/// A list of currently spawned actors for easy access.
|
|
var/list/mob/living/carbon/human/actors
|
|
/// A list of currently spawned storytellers for easy access.
|
|
var/list/mob/abstract/ghost/storyteller/storytellers
|
|
/// Whether or not we've sent the odyssey's roundstart report yet.
|
|
var/has_sent_roundstart_announcement = FALSE
|
|
/// The current station map's overmap object. We keep it here for convenience.
|
|
var/obj/effect/overmap/visitable/ship/main_map
|
|
/// If ships can dock on the Odyssey site. True by default, edited by the scenario.
|
|
var/site_landing_restricted = TRUE
|
|
|
|
/datum/controller/subsystem/odyssey/Initialize()
|
|
return SS_INIT_SUCCESS
|
|
|
|
/datum/controller/subsystem/odyssey/Recover()
|
|
scenario = SSodyssey.scenario
|
|
scenario_site = SSodyssey.scenario_site
|
|
scenario_zlevels = SSodyssey.scenario_zlevels
|
|
actors = SSodyssey.actors
|
|
storytellers = SSodyssey.storytellers
|
|
|
|
/datum/controller/subsystem/odyssey/fire()
|
|
if(!has_sent_roundstart_announcement)
|
|
// First of all, notify the Horizon.
|
|
addtimer(CALLBACK(scenario, TYPE_PROC_REF(/singleton/scenario, send_main_map_message), main_map), rand(4 MINUTES, 6 MINUTES))
|
|
addtimer(CALLBACK(scenario, TYPE_PROC_REF(/singleton/scenario, unrestrict_landing_and_message_horizon), main_map), 40 MINUTES)
|
|
|
|
var/obj/effect/overmap/odyssey_site = get_odyssey_overmap_effect()
|
|
|
|
if(odyssey_site)
|
|
// Next, notify the offships - these announcements can happen earlier to potentially give them a bit of an edge in reaching the objective area.
|
|
addtimer(CALLBACK(scenario, TYPE_PROC_REF(/singleton/scenario, notify_offships), odyssey_site), rand(20 MINUTES, 50 MINUTES))
|
|
|
|
has_sent_roundstart_announcement = TRUE
|
|
|
|
/**
|
|
* Returns the current scenario's overmap effect. Returns null if there isn't any.
|
|
*/
|
|
/datum/controller/subsystem/odyssey/proc/get_odyssey_overmap_effect()
|
|
var/obj/effect/overmap/odyssey_site
|
|
for(var/z in scenario_zlevels)
|
|
odyssey_site = GLOB.map_sectors["[z]"]
|
|
if(!istype(odyssey_site))
|
|
continue
|
|
return odyssey_site
|
|
|
|
/**
|
|
* Picks a random odyssey while keeping in mind sector requirements.
|
|
* If successful, makes the SS start firing.
|
|
*/
|
|
/datum/controller/subsystem/odyssey/proc/pick_odyssey()
|
|
var/list/singleton/scenario/all_scenarios = GET_SINGLETON_SUBTYPE_LIST(/singleton/scenario)
|
|
var/list/possible_scenarios = list()
|
|
for(var/singleton/scenario/S as anything in all_scenarios)
|
|
if((SSatlas.current_sector.name in S.sector_whitelist) || !length(S.sector_whitelist))
|
|
possible_scenarios[S] = S.weight
|
|
|
|
if(!length(possible_scenarios))
|
|
log_subsystem_odyssey("CRITICAL ERROR: No available odyssey for sector [SSatlas.current_sector.name]!")
|
|
log_and_message_admins(SPAN_DANGER(FONT_HUGE("CRITICAL ERROR: NO SITUATIONS ARE AVAILABLE FOR THIS SECTOR!")))
|
|
return FALSE
|
|
|
|
scenario = pickweight(possible_scenarios)
|
|
setup_scenario_variables()
|
|
var/list/possible_station_levels = SSmapping.levels_by_all_traits(list(ZTRAIT_STATION))
|
|
main_map = GLOB.map_sectors["[pick(possible_station_levels)]"]
|
|
|
|
// Now that we actually have an odyssey, the subsystem can fire!
|
|
can_fire = TRUE
|
|
|
|
return TRUE
|
|
|
|
/**
|
|
* This proc overrides the gamemode variables for the amount of actors and takes care of overriding any other variables the scenario might set on external things.
|
|
* Note that Storytellers spawn through a ghost role.
|
|
*/
|
|
/datum/controller/subsystem/odyssey/proc/setup_scenario_variables()
|
|
var/datum/game_mode/odyssey/ody_gamemode = GLOB.gamemode_cache["odyssey"]
|
|
if(scenario)
|
|
ody_gamemode.required_players = scenario.min_player_amount
|
|
ody_gamemode.required_enemies = scenario.min_actor_amount
|
|
|
|
//Setting the scenario_type variable for use here in UI info and chat notices.
|
|
if(!length(scenario.possible_scenario_types))
|
|
scenario.scenario_type = SCENARIO_TYPE_NONCANON
|
|
else if(SSatlas.current_sector in ALL_EVENT_ONLY_SECTORS) // If we are in an exclusive event area for an arc (EG. The Horizon finds itself isolated and alone), we may not want canon odysseys spawning.
|
|
scenario.scenario_type = SCENARIO_TYPE_NONCANON // Noncanon odysseys are fine though!
|
|
else
|
|
scenario.scenario_type = pick(scenario.possible_scenario_types)
|
|
|
|
site_landing_restricted = scenario.site_landing_restricted
|
|
|
|
/**
|
|
* This is the proc that should be used to set the odyssey away site.
|
|
*/
|
|
/datum/controller/subsystem/odyssey/proc/set_scenario_site(datum/map_template/ruin/away_site/site)
|
|
if(!istype(site))
|
|
return
|
|
|
|
scenario_site = site
|
|
|
|
/**
|
|
* Adds an actor to the subsystem actor list.
|
|
*/
|
|
/datum/controller/subsystem/odyssey/proc/add_actor(mob/living/carbon/human/H)
|
|
LAZYDISTINCTADD(actors, H)
|
|
|
|
/**
|
|
* Adds a storyteller to the subsystem storyteller list.
|
|
*/
|
|
/datum/controller/subsystem/odyssey/proc/add_storyteller(mob/abstract/ghost/storyteller/S)
|
|
LAZYDISTINCTADD(storytellers, S)
|
|
|
|
/**
|
|
* Removes an actor from the subsystem actor list.
|
|
*/
|
|
/datum/controller/subsystem/odyssey/proc/remove_actor(mob/living/carbon/human/H)
|
|
LAZYREMOVE(actors, H)
|
|
|
|
/**
|
|
* Removes a storyteller from the subsystem storyteller list.
|
|
*/
|
|
/datum/controller/subsystem/odyssey/proc/remove_storyteller(mob/abstract/ghost/storyteller/S)
|
|
LAZYREMOVE(storytellers, S)
|
|
|
|
/datum/controller/subsystem/odyssey/ui_state()
|
|
return GLOB.always_state
|
|
|
|
/datum/controller/subsystem/odyssey/ui_interact(mob/user, datum/tgui/ui)
|
|
. = ..()
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "OdysseyPanel", "Odyssey Panel")
|
|
ui.open()
|
|
|
|
/datum/controller/subsystem/odyssey/ui_data(mob/user)
|
|
var/list/data = list()
|
|
if(scenario)
|
|
data["scenario_name"] = SSodyssey.scenario.name
|
|
data["scenario_desc"] = SSodyssey.scenario.desc
|
|
data["scenario_canonicity"] = SSodyssey.scenario.scenario_type == SCENARIO_TYPE_CANON ? "Canon" : "Non-Canon"
|
|
data["is_storyteller"] = isstoryteller(user) || check_rights(R_ADMIN, FALSE, user)
|
|
|
|
if(length(scenario.roles))
|
|
data["scenario_roles"] = list()
|
|
for(var/role_singleton in scenario.roles)
|
|
var/singleton/role/scenario_role = GET_SINGLETON(role_singleton)
|
|
data["scenario_roles"] += list(
|
|
list(
|
|
"name" = scenario_role.name,
|
|
"desc" = scenario_role.desc,
|
|
"outfit" = "[scenario_role.outfit]",
|
|
"type" = scenario_role.type
|
|
)
|
|
)
|
|
return data
|
|
|
|
/datum/controller/subsystem/odyssey/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
|
|
var/mob/odyssey_user = ui.user
|
|
if(!ismob(odyssey_user))
|
|
return
|
|
|
|
var/is_admin = check_rights(R_ADMIN, FALSE, odyssey_user)
|
|
|
|
switch(action)
|
|
if("equip_outfit")
|
|
if(!ishuman(odyssey_user))
|
|
return
|
|
|
|
var/mob/living/carbon/human/player = odyssey_user
|
|
if(player.incapacitated())
|
|
return
|
|
|
|
if(!((player.z in SSodyssey.scenario_zlevels) || (isAdminLevel(player.z))))
|
|
to_chat(player, SPAN_WARNING("You can only equip outfits on the odyssey scenario z-level, or the actor prep area!"))
|
|
return
|
|
|
|
var/outfit_type = text2path(params["outfit_type"])
|
|
if(ispath(outfit_type, /obj/outfit))
|
|
player.delete_inventory(TRUE)
|
|
player.preEquipOutfit(outfit_type, FALSE)
|
|
player.equipOutfit(outfit_type, FALSE)
|
|
return TRUE
|
|
|
|
if("edit_scenario_name")
|
|
if(!isstoryteller(odyssey_user) && !is_admin)
|
|
return
|
|
|
|
var/new_scenario_name = tgui_input_text(usr, "Insert the new name for your scenario. Remember that this will be visible for anyone in the Stat Panel.", "Odyssey Panel", max_length = MAX_NAME_LEN)
|
|
if(!new_scenario_name)
|
|
return
|
|
|
|
log_and_message_admins("has changed the scenario name from [SSodyssey.scenario.name] to [new_scenario_name]", odyssey_user)
|
|
SSodyssey.scenario.name = new_scenario_name
|
|
return TRUE
|
|
|
|
if("edit_scenario_desc")
|
|
if(!isstoryteller(odyssey_user) && !is_admin)
|
|
return
|
|
|
|
var/new_scenario_desc = tgui_input_text(odyssey_user, "Insert the new description for your scenario. This is visible only in the Odyssey Panel.", "Odyssey Panel", max_length = MAX_MESSAGE_LEN)
|
|
if(!new_scenario_desc)
|
|
return
|
|
|
|
log_and_message_admins("has changed the scenario description", odyssey_user)
|
|
SSodyssey.scenario.desc = new_scenario_desc
|
|
return TRUE
|
|
|
|
if("edit_role")
|
|
if(!isstoryteller(odyssey_user) && !is_admin)
|
|
return
|
|
|
|
var/role_path = text2path(params["role_type"])
|
|
if(!role_path || !ispath(role_path, /singleton/role))
|
|
to_chat(odyssey_user, SPAN_WARNING("Invalid or inexisting role!"))
|
|
return
|
|
|
|
var/singleton/role/role_to_edit = GET_SINGLETON(role_path)
|
|
var/editing_name = params["new_name"]
|
|
var/editing_desc = params["new_desc"]
|
|
var/editing_outfit = params["edit_outfit"]
|
|
|
|
if(editing_name)
|
|
var/new_name = tgui_input_text(odyssey_user, "Insert the new name for this role.", "Odyssey Panel", max_length = MAX_NAME_LEN)
|
|
if(new_name)
|
|
log_and_message_admins("has changed the [role_to_edit.name] role's name to [new_name]", odyssey_user)
|
|
role_to_edit.name = new_name
|
|
return TRUE
|
|
|
|
if(editing_desc)
|
|
var/new_desc = tgui_input_text(odyssey_user, "Insert the new description for this role.", "Odyssey Panel", max_length = MAX_MESSAGE_LEN)
|
|
if(new_desc)
|
|
log_and_message_admins("has changed the [role_to_edit.name] role's description", odyssey_user)
|
|
role_to_edit.desc = new_desc
|
|
return TRUE
|
|
|
|
if(editing_outfit)
|
|
var/chosen_outfit = tgui_input_list(odyssey_user, "Select the new outfit for this role.", "Odyssey Panel", GLOB.outfit_cache)
|
|
if(chosen_outfit)
|
|
var/obj/outfit/new_outfit = GLOB.outfit_cache[chosen_outfit]
|
|
if(new_outfit)
|
|
log_and_message_admins("has changed the [role_to_edit.name] role's outfit from [role_to_edit.outfit] to [new_outfit]", odyssey_user)
|
|
role_to_edit.outfit = new_outfit.type
|
|
return TRUE
|
|
|
|
|