mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 20:48:56 +01:00
## About The Pull Request  Pre-discussed with @Watermelon914, this PR removes Secondary & Final Objectives from all Traitors, rather than just midround ones. It also removes all of the surrounding supporting code. Randomly assigned Primary Objectives still exist, I just used the ability to rewrite mine to take the screenshot. In terms of final objectives, the surrounding items that were available still exist but don't necessarily have sources. If anyone has good ideas for readding these in some other form it can be done in future PRs. It also allows all traitors to buy the Contractor kit, previously limited to midround traitors which lacked secondary objectives, because now all traitors lack secondary objectives. This essentially limits all traitors to a maximum of 20 TC (16 if they spawn with an uplink implant). Currently I don't foresee that they strictly need any additional way of gaining TC during a round as 20 is quite sufficient, but it may take some time to adjust and get used to it after such a long time of having access to more. If we need to adjust the starting value or add a slow drip of more points over time or something, that can be done in followup PRs. This also removes the ability to recreate your uplink added by my beautiful wife in #74315 This was part of the progression traitor design document, but ultimately probably a bad idea as it essentially made traitors impossible to properly disarm. You will once more just need to carefully protect your uplink. **This does not remove the threat/progression system**. Like midround traitors, all Reputation requirements on gear are now simple timelocks, most of which will have elapsed by the time 30 minutes have passed. **Finally** this PR also adds Romerol to the traitor uplink for 25 TC and 30 minutes of reputation, as a treat (and because I removed the final objective that previously granted it). ## Why It's Good For The Game We've tried this system for a long time (3 years last month!) and while I think it had a lot of promise, enabled some cool moments, and also solved several of the problems it set out to solve, overall I think some of the behaviours it has encouraged in players have been overall negative for the game. While the _game systems_ are fine, even quite fun and cool (especially final objectives) I am of the opinion that having them in the game creates a net negative purely in the way that they react with players' _brains_, creating incentives towards behaviour we don't actually want people to pursue. While it's hard-to-impossible to prove any of this with hard data, there has been a prevailing feeling for some time among many (though certainly not all) people that the simple fact of _having_ a constant drip-feed of objective available to players leads directly to less interesting antagonist play. While certainly nobody is _forced_ to do secondary objectives you are directly and quite strongly rewarded for doing so, doing so efficiently, and doing so in a way which makes sure that nobody (alive) sees you do it. This leads to a tendency to play defensively and try to maximise the number of tasks you can complete in one round, which also has a knock-on effect of generally minimising the number of people you attempt to interact with in a round (unless you are killing them). Even people who _intend_ on doing some more interesting gimmick can fall into this trap, as "having more tools" is always useful for anyone who is intending on any kind of plan at all, but then executing on the secondary objectives again incentivises you to lay low, not interact with anyone, be efficient, and then reduces the time you are spending doing the thing that's your actual plan for the round. Removing the ever-present temptation to fish for extra TC leaves "doing whatever your actual plan is" as the sole thing to optimise. Final Objectives too have created unfortunate psychological effects between crewsided players and other antagonists. Because of the _threat_ (no matter how remote, Final Objectives have always been tuned to be appropriately rare) that leaving any antagonist alone will cause them to snowball by acquiring more power, it starts to feel foolish to respond to any threat with less than the maximum possible level of force even if they seem relatively innocuous in the moment. This even has an effect on other non-progression antagonists, as traitors are the most common antagonist type and how people treat them is going to be their default level of reaction to most other station threats. While there has always been the promise of expanding the system with novel and exciting objectives that leverage appearing mid-round to do something unique, we've taken very little advantage of that over time. Most objectives we have added that didn't boil down to "kill someone, with a twist" have been somewhat unsuccessful, serving either as ways to get yourself arrested and killed for no reason or ways to get free telecrystals by doing something the crew don't really care about stopping you from doing. The option still exists to add more roundstart objectives to traitors, if someone suddenly has a great idea that would fit in this space. The ideal outcome of making this change is a slight relaxation of crew attitude towards feeling like their only option after catching an antagonist that isn't sandbagging is to permanently remove them from the round (although it's fine to do this still in many scenarios), and a broadening of traitorous activity which is not purely focused on collecting as many checkboxes as possible and might give people more time to roleplay with other players, not worrying that this time could have been more efficiently spent pursuing a different secondary goal. I don't anticipate or desire that this will prevent traitors from killing anyone (or even stop them from killing people they don't have a specific objective to kill), I just want to remove the FOMO from people's minds. Also this gives us something to talk about at the coder townhall meeting on the 22nd. ## Changelog 🆑 del: Misplaced or stolen traitor uplinks can no longer be recreated using a radio code and special device, guard yours carefully or buy a backup implant. del: Roundstart traitors can no longer take on additional objectives in order to earn additional Telecrystals and fast-forward any unlock timers on items. They also cannot earn the ability to complete a Final Objective. balance: Roundstart traitors can now buy the Contractor Kit from their traitor uplink, rather than only midround traitors. add: Traitors can buy Romerol for 25 TC, after 30 minutes of time has passed in a round. /🆑
199 lines
8.2 KiB
Plaintext
199 lines
8.2 KiB
Plaintext
/// number of emagged meteor shields to get the first warning, a simple say message
|
|
#define EMAGGED_METEOR_SHIELD_THRESHOLD_ONE 3
|
|
/// number of emagged meteor shields to get the second warning, telling the user an announcement is coming
|
|
#define EMAGGED_METEOR_SHIELD_THRESHOLD_TWO 6
|
|
/// number of emagged meteor shields to get the third warning + an announcement to the crew
|
|
#define EMAGGED_METEOR_SHIELD_THRESHOLD_THREE 7
|
|
/// number of emagged meteor shields to get the fourth... ah shit the dark matt-eor is coming.
|
|
#define EMAGGED_METEOR_SHIELD_THRESHOLD_FOUR 10
|
|
/// how long between emagging meteor shields you have to wait
|
|
#define METEOR_SHIELD_EMAG_COOLDOWN 1 MINUTES
|
|
|
|
//Station Shield
|
|
// A chain of satellites encircles the station
|
|
// Satellites be actived to generate a shield that will block unorganic matter from passing it.
|
|
/datum/station_goal/station_shield
|
|
name = "Station Shield"
|
|
requires_space = TRUE
|
|
var/coverage_goal = 500
|
|
VAR_PRIVATE/cached_coverage_length
|
|
|
|
/datum/station_goal/station_shield/get_report()
|
|
return list(
|
|
"<blockquote>The station is located in a zone full of space debris.",
|
|
"We have a prototype shielding system you must deploy to reduce collision-related accidents.",
|
|
"",
|
|
"You can order the satellites and control systems at cargo.</blockquote>",
|
|
).Join("\n")
|
|
|
|
|
|
/datum/station_goal/station_shield/on_report()
|
|
//Unlock
|
|
var/datum/supply_pack/P = SSshuttle.supply_packs[/datum/supply_pack/engineering/shield_sat]
|
|
P.special_enabled = TRUE
|
|
|
|
P = SSshuttle.supply_packs[/datum/supply_pack/engineering/shield_sat_control]
|
|
P.special_enabled = TRUE
|
|
|
|
/datum/station_goal/station_shield/check_completion()
|
|
if(..())
|
|
return TRUE
|
|
update_coverage()
|
|
if(cached_coverage_length >= coverage_goal)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/datum/station_goal/station_shield/proc/get_coverage()
|
|
return cached_coverage_length
|
|
|
|
/// Gets the coverage of all active meteor shield satellites
|
|
/// Can be expensive, ensure you need this before calling it
|
|
/datum/station_goal/station_shield/proc/update_coverage()
|
|
var/list/coverage = list()
|
|
for(var/obj/machinery/satellite/meteor_shield/shield_satt as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/satellite/meteor_shield))
|
|
if(!shield_satt.active || !is_station_level(shield_satt.z))
|
|
continue
|
|
for(var/turf/covered in view(shield_satt.kill_range, shield_satt))
|
|
coverage |= covered
|
|
cached_coverage_length = length(coverage)
|
|
|
|
/obj/machinery/satellite/meteor_shield
|
|
name = "\improper Meteor Shield Satellite"
|
|
desc = "A meteor point-defense satellite."
|
|
mode = "M-SHIELD"
|
|
/// the range a meteor shield sat can destroy meteors
|
|
var/kill_range = 14
|
|
|
|
//emag behavior dark matt-eor stuff
|
|
|
|
/// Proximity monitor associated with this atom, needed for it to work.
|
|
var/datum/proximity_monitor/proximity_monitor
|
|
|
|
/// amount of emagged active meteor shields
|
|
var/static/emagged_active_meteor_shields = 0
|
|
/// the highest amount of shields you've ever emagged
|
|
var/static/highest_emagged_threshold_reached = 0
|
|
/// cooldown on emagging meteor shields because instantly summoning a dark matt-eor is very unfun
|
|
STATIC_COOLDOWN_DECLARE(shared_emag_cooldown)
|
|
|
|
/obj/machinery/satellite/meteor_shield/examine(mob/user)
|
|
. = ..()
|
|
if(active)
|
|
. += span_notice("It is currently active. You can interact with it to shut it down.")
|
|
if(obj_flags & EMAGGED)
|
|
. += span_warning("Rather than the usual sounds of beeps and pings, it produces a weird and constant hiss of white noise…")
|
|
else
|
|
. += span_notice("It emits periodic beeps and pings as it communicates with the satellite network.")
|
|
else
|
|
. += span_notice("It is currently disabled. You can interact with it to set it up.")
|
|
if(obj_flags & EMAGGED)
|
|
. += span_warning("But something seems off about it...?")
|
|
|
|
/obj/machinery/satellite/meteor_shield/proc/space_los(meteor)
|
|
for(var/turf/T in get_line(src,meteor))
|
|
if(!isspaceturf(T))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/machinery/satellite/meteor_shield/Initialize(mapload)
|
|
. = ..()
|
|
proximity_monitor = new(src, /* range = */ 0)
|
|
|
|
/obj/machinery/satellite/meteor_shield/HasProximity(atom/movable/proximity_check_mob)
|
|
. = ..()
|
|
if(!istype(proximity_check_mob, /obj/effect/meteor))
|
|
return
|
|
var/obj/effect/meteor/meteor_to_destroy = proximity_check_mob
|
|
if(space_los(meteor_to_destroy))
|
|
var/turf/beam_from = get_turf(src)
|
|
beam_from.Beam(get_turf(meteor_to_destroy), icon_state="sat_beam", time = 5)
|
|
if(meteor_to_destroy.shield_defense(src))
|
|
qdel(meteor_to_destroy)
|
|
|
|
/obj/machinery/satellite/meteor_shield/toggle(user)
|
|
if(user)
|
|
balloon_alert(user, "looking for [active ? "off" : "on"] button")
|
|
if(user && !do_after(user, 2 SECONDS, src, IGNORE_HELD_ITEM))
|
|
return FALSE
|
|
if(!..(user))
|
|
return FALSE
|
|
if(obj_flags & EMAGGED)
|
|
update_emagged_meteor_sat(user)
|
|
|
|
if(active)
|
|
proximity_monitor.set_range(kill_range)
|
|
else
|
|
proximity_monitor.set_range(0)
|
|
|
|
|
|
var/datum/station_goal/station_shield/goal = SSstation.get_station_goal(/datum/station_goal/station_shield)
|
|
goal?.update_coverage()
|
|
|
|
/obj/machinery/satellite/meteor_shield/Destroy()
|
|
. = ..()
|
|
QDEL_NULL(proximity_monitor)
|
|
if(obj_flags & EMAGGED)
|
|
//satellites that are destroying are not active, this will count down the number of emagged sats
|
|
update_emagged_meteor_sat()
|
|
|
|
/obj/machinery/satellite/meteor_shield/emag_act(mob/user, obj/item/card/emag/emag_card)
|
|
if(obj_flags & EMAGGED)
|
|
balloon_alert(user, "already emagged!")
|
|
return FALSE
|
|
if(!COOLDOWN_FINISHED(src, shared_emag_cooldown))
|
|
balloon_alert(user, "on cooldown!")
|
|
to_chat(user, span_warning("The last satellite emagged needs [DisplayTimeText(COOLDOWN_TIMELEFT(src, shared_emag_cooldown))] to recalibrate first. Emagging another so soon could damage the satellite network."))
|
|
return FALSE
|
|
var/cooldown_applied = METEOR_SHIELD_EMAG_COOLDOWN
|
|
COOLDOWN_START(src, shared_emag_cooldown, cooldown_applied)
|
|
obj_flags |= EMAGGED
|
|
to_chat(user, span_notice("You access the satellite's debug mode and it begins emitting a strange signal, increasing the chance of meteor strikes."))
|
|
AddComponent(/datum/component/gps, "Corrupted Meteor Shield Attraction Signal")
|
|
say("Recalibrating... ETA:[DisplayTimeText(cooldown_applied)].")
|
|
if(active) //if we allowed inactive updates a sat could be worth -1 active meteor shields on first emag
|
|
update_emagged_meteor_sat(user)
|
|
return TRUE
|
|
|
|
/obj/machinery/satellite/meteor_shield/proc/update_emagged_meteor_sat(mob/user)
|
|
if(!active)
|
|
change_meteor_chance(0.5)
|
|
emagged_active_meteor_shields--
|
|
if(user)
|
|
balloon_alert(user, "meteor probability halved")
|
|
return
|
|
change_meteor_chance(2)
|
|
emagged_active_meteor_shields++
|
|
if(user)
|
|
balloon_alert(user, "meteor probability doubled")
|
|
if(emagged_active_meteor_shields > highest_emagged_threshold_reached)
|
|
highest_emagged_threshold_reached = emagged_active_meteor_shields
|
|
handle_new_emagged_shield_threshold()
|
|
|
|
/obj/machinery/satellite/meteor_shield/proc/handle_new_emagged_shield_threshold()
|
|
switch(highest_emagged_threshold_reached)
|
|
if(EMAGGED_METEOR_SHIELD_THRESHOLD_ONE)
|
|
say("Warning. Meteor strike probability entering dangerous ranges for more exotic meteors.")
|
|
if(EMAGGED_METEOR_SHIELD_THRESHOLD_TWO)
|
|
say("Warning. Risk of dark matter congealment entering existent ranges. Further tampering will be reported.")
|
|
if(EMAGGED_METEOR_SHIELD_THRESHOLD_THREE)
|
|
say("Warning. Further tampering has been reported.")
|
|
priority_announce("Warning. Tampering of meteor satellites puts the station at risk of exotic, deadly meteor collisions. Please intervene by checking your GPS devices for strange signals, and dismantling the tampered meteor shields.", "Strange Meteor Signal Warning")
|
|
if(EMAGGED_METEOR_SHIELD_THRESHOLD_FOUR)
|
|
say("Warning. Warning. Dark Matt-eor on course for station.")
|
|
force_event_async(/datum/round_event_control/dark_matteor, "an array of tampered meteor satellites")
|
|
|
|
/obj/machinery/satellite/meteor_shield/proc/change_meteor_chance(mod)
|
|
// Update the weight of all meteor events
|
|
for(var/datum/round_event_control/meteor_wave/meteors in SSevents.control)
|
|
meteors.weight *= mod
|
|
for(var/datum/round_event_control/stray_meteor/stray_meteor in SSevents.control)
|
|
stray_meteor.weight *= mod
|
|
|
|
|
|
#undef EMAGGED_METEOR_SHIELD_THRESHOLD_ONE
|
|
#undef EMAGGED_METEOR_SHIELD_THRESHOLD_TWO
|
|
#undef EMAGGED_METEOR_SHIELD_THRESHOLD_THREE
|
|
#undef EMAGGED_METEOR_SHIELD_THRESHOLD_FOUR
|
|
|
|
#undef METEOR_SHIELD_EMAG_COOLDOWN
|