mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-02-07 06:48:44 +00:00
## About The Pull Request This is a remake of https://github.com/tgstation/tgstation/pull/84501 with a few small changes to avoid mapping issues. This time it's restricted to a once-in-a-year event, mainly because neither Jacq and Lemon were ok with the possibility of Pun Pun being posibly playable all year, however Jacq said he's fine with it being available on the holiday, while Lemon isn't around on Discord atm so I don't know what he's to say. The trait makes pun pun playable and gives them the job of being a busser (waiter) in the cafeteria. They're clever and can use tools most other monkeys can't, but cannot be humanized, while also being unable to speak Galactic Common. Should they roll traitor, they'll be able to buy syndicate monkey reinforcements and equipment, which it can also use, being a monkey itself. ## Why It's Good For The Game This is a fairly unique job, like the Cargo Gorilla, due to the fact it's not a conventional humanoid crew member nor the AI or a cyborg. I thought this was a fun idea, though I met some obstacles and the original PR was DNM'ed and then closed. However, the trait perfectly fits the theme of the holiday, making a good compromise since Pun Pun will stay AI-controlled the rest of the year, as Jacquarel and Lemon want it to be. It can also be added by an admin through VV, whether they're planning some shenanigean or just want to add something more to the round. ## Changelog 🆑 add: Pun Pun is a playable crewmember during Monkey Day (14 December). /🆑
186 lines
7.8 KiB
Plaintext
186 lines
7.8 KiB
Plaintext
PROCESSING_SUBSYSTEM_DEF(station)
|
|
name = "Station"
|
|
init_order = INIT_ORDER_STATION
|
|
flags = SS_BACKGROUND
|
|
runlevels = RUNLEVEL_GAME
|
|
wait = 5 SECONDS
|
|
|
|
///A list of currently active station traits
|
|
var/list/station_traits = list()
|
|
///Assoc list of trait type || assoc list of traits with weighted value. Used for picking traits from a specific category.
|
|
var/list/selectable_traits_by_types = list(STATION_TRAIT_POSITIVE = list(), STATION_TRAIT_NEUTRAL = list(), STATION_TRAIT_NEGATIVE = list())
|
|
///Currently active announcer. Starts as a type but gets initialized after traits are selected
|
|
var/datum/centcom_announcer/announcer = /datum/centcom_announcer/default
|
|
///A list of trait roles that should be protected from antag
|
|
var/list/antag_protected_roles = list()
|
|
///A list of trait roles that should never be able to roll antag
|
|
var/list/antag_restricted_roles = list()
|
|
|
|
/// Assosciative list of station goal type -> goal instance
|
|
var/list/datum/station_goal/goals_by_type = list()
|
|
|
|
/datum/controller/subsystem/processing/station/Initialize()
|
|
//If doing unit tests we don't do none of that trait shit ya know?
|
|
// Autowiki also wants consistent outputs, for example making sure the vending machine page always reports the normal products
|
|
#if !defined(UNIT_TESTS) && !defined(AUTOWIKI)
|
|
SetupTraits()
|
|
display_lobby_traits()
|
|
#endif
|
|
|
|
announcer = new announcer() //Initialize the station's announcer datum
|
|
SSparallax.post_station_setup() //Apply station effects that parallax might have
|
|
|
|
return SS_INIT_SUCCESS
|
|
|
|
/datum/controller/subsystem/processing/station/Recover()
|
|
station_traits = SSstation.station_traits
|
|
selectable_traits_by_types = SSstation.selectable_traits_by_types
|
|
announcer = SSstation.announcer
|
|
antag_protected_roles = SSstation.antag_protected_roles
|
|
antag_restricted_roles = SSstation.antag_restricted_roles
|
|
goals_by_type = SSstation.goals_by_type
|
|
..()
|
|
|
|
/// This gets called by SSdynamic during initial gamemode setup.
|
|
/// This is done because for a greenshift we want all goals to be generated
|
|
/datum/controller/subsystem/processing/station/proc/generate_station_goals(goal_budget)
|
|
var/list/possible = subtypesof(/datum/station_goal)
|
|
|
|
var/goal_weights = 0
|
|
var/chosen_goals = list()
|
|
var/is_planetary = SSmapping.is_planetary()
|
|
while(possible.len && goal_weights < goal_budget)
|
|
var/datum/station_goal/picked = pick_n_take(possible)
|
|
if(picked::requires_space && is_planetary)
|
|
continue
|
|
|
|
goal_weights += initial(picked.weight)
|
|
chosen_goals += picked
|
|
|
|
for(var/chosen in chosen_goals)
|
|
new chosen()
|
|
|
|
/// Returns all station goals that are currently active
|
|
/datum/controller/subsystem/processing/station/proc/get_station_goals()
|
|
var/list/goals = list()
|
|
for(var/goal_type in goals_by_type)
|
|
goals += goals_by_type[goal_type]
|
|
return goals
|
|
|
|
/// Returns a specific station goal by type
|
|
/datum/controller/subsystem/processing/station/proc/get_station_goal(goal_type)
|
|
return goals_by_type[goal_type]
|
|
|
|
///Rolls for the amount of traits and adds them to the traits list
|
|
/datum/controller/subsystem/processing/station/proc/SetupTraits()
|
|
if (CONFIG_GET(flag/forbid_station_traits))
|
|
return
|
|
|
|
if (fexists(FUTURE_STATION_TRAITS_FILE))
|
|
var/forced_traits_contents = file2text(FUTURE_STATION_TRAITS_FILE)
|
|
fdel(FUTURE_STATION_TRAITS_FILE)
|
|
|
|
var/list/forced_traits_text_paths = json_decode(forced_traits_contents)
|
|
forced_traits_text_paths = SANITIZE_LIST(forced_traits_text_paths)
|
|
|
|
for (var/trait_text_path in forced_traits_text_paths)
|
|
var/station_trait_path = text2path(trait_text_path)
|
|
if (!ispath(station_trait_path, /datum/station_trait) || station_trait_path == /datum/station_trait)
|
|
var/message = "Invalid station trait path [station_trait_path] was requested in the future station traits!"
|
|
log_game(message)
|
|
message_admins(message)
|
|
continue
|
|
|
|
setup_trait(station_trait_path)
|
|
|
|
return
|
|
|
|
for(var/datum/station_trait/trait_typepath as anything in subtypesof(/datum/station_trait))
|
|
|
|
// If forced, (probably debugging), just set it up now, keep it out of the pool.
|
|
if(initial(trait_typepath.force))
|
|
setup_trait(trait_typepath)
|
|
continue
|
|
|
|
if(initial(trait_typepath.abstract_type) == trait_typepath)
|
|
continue //Dont add abstract ones to it
|
|
|
|
if(!(initial(trait_typepath.trait_flags) & STATION_TRAIT_PLANETARY) && SSmapping.is_planetary()) // we're on a planet but we can't do planet ;_;
|
|
continue
|
|
|
|
if(!(initial(trait_typepath.trait_flags) & STATION_TRAIT_SPACE_BOUND) && !SSmapping.is_planetary()) //we're in space but we can't do space ;_;
|
|
continue
|
|
|
|
if(!(initial(trait_typepath.trait_flags) & STATION_TRAIT_REQUIRES_AI) && !CONFIG_GET(flag/allow_ai)) //can't have AI traits without AI
|
|
continue
|
|
|
|
if(ispath(trait_typepath, /datum/station_trait/random_event_weight_modifier)) //Don't add event modifiers for events that can't occur on our map.
|
|
var/datum/station_trait/random_event_weight_modifier/random_trait_typepath = trait_typepath
|
|
var/datum/round_event_control/event_to_check = initial(random_trait_typepath.event_control_path)
|
|
if(event_to_check)
|
|
event_to_check = new event_to_check()
|
|
if(!event_to_check.valid_for_map())
|
|
continue
|
|
|
|
selectable_traits_by_types[initial(trait_typepath.trait_type)][trait_typepath] = initial(trait_typepath.weight)
|
|
|
|
var/positive_trait_budget = text2num(pick_weight(CONFIG_GET(keyed_list/positive_station_traits)))
|
|
var/neutral_trait_budget = text2num(pick_weight(CONFIG_GET(keyed_list/neutral_station_traits)))
|
|
var/negative_trait_budget = text2num(pick_weight(CONFIG_GET(keyed_list/negative_station_traits)))
|
|
|
|
#ifdef MAP_TEST
|
|
positive_trait_budget = 0
|
|
neutral_trait_budget = 0
|
|
negative_trait_budget = 0
|
|
#endif
|
|
|
|
pick_traits(STATION_TRAIT_POSITIVE, positive_trait_budget)
|
|
pick_traits(STATION_TRAIT_NEUTRAL, neutral_trait_budget)
|
|
pick_traits(STATION_TRAIT_NEGATIVE, negative_trait_budget)
|
|
|
|
/**
|
|
* Picks traits of a specific category (e.g. bad or good), initializes them, adds them to the list of traits,
|
|
* then removes them from possible traits as to not roll twice and subtracts their cost from the budget.
|
|
* All until the whole budget is spent or no more traits can be picked with it.
|
|
*/
|
|
/datum/controller/subsystem/processing/station/proc/pick_traits(trait_sign, budget)
|
|
if(!budget)
|
|
return
|
|
///A list of traits of the same trait sign
|
|
var/list/selectable_traits = selectable_traits_by_types[trait_sign]
|
|
while(budget)
|
|
///Remove any station trait with a cost bigger than the budget
|
|
for(var/datum/station_trait/proto_trait as anything in selectable_traits)
|
|
if(initial(proto_trait.cost) > budget)
|
|
selectable_traits -= proto_trait
|
|
///We have spare budget but no trait that can be bought with what's left of it
|
|
if(!length(selectable_traits))
|
|
return
|
|
//Rolls from the table for the specific trait type
|
|
var/datum/station_trait/trait_type = pick_weight(selectable_traits)
|
|
selectable_traits -= trait_type
|
|
budget -= initial(trait_type.cost)
|
|
setup_trait(trait_type)
|
|
|
|
///Creates a given trait of a specific type, while also removing any blacklisted ones from the future pool.
|
|
/datum/controller/subsystem/processing/station/proc/setup_trait(datum/station_trait/trait_type)
|
|
if(locate(trait_type) in station_traits)
|
|
return
|
|
var/datum/station_trait/trait_instance = new trait_type()
|
|
station_traits += trait_instance
|
|
log_game("Station Trait: [trait_instance.name] chosen for this round.")
|
|
if(!trait_instance.blacklist)
|
|
return
|
|
for(var/i in trait_instance.blacklist)
|
|
var/datum/station_trait/trait_to_remove = i
|
|
selectable_traits_by_types[initial(trait_to_remove.trait_type)] -= trait_to_remove
|
|
|
|
/// Update station trait lobby buttons for clients who joined before we initialised this subsystem
|
|
/datum/controller/subsystem/processing/station/proc/display_lobby_traits()
|
|
for (var/mob/dead/new_player/player as anything in GLOB.new_player_list)
|
|
var/datum/hud/new_player/observer_hud = player.hud_used
|
|
if (!istype(observer_hud))
|
|
continue
|
|
observer_hud.add_station_trait_buttons()
|
|
observer_hud.show_hud(observer_hud.hud_version)
|