diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 64bfe5106794..73cd2093ba1b 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -367,6 +367,9 @@ GLOBAL_LIST_INIT(round_end_images, world.file2list("data/image_urls.txt")) // MO /datum/controller/subsystem/ticker/proc/build_roundend_report() var/list/parts = list() + //might want to make this a full section + parts += "
[("Storyteller: [SSgamemode.storyteller ? SSgamemode.storyteller.name : "N/A"]")]
" //monkestation edit + //AI laws parts += law_report() diff --git a/code/controllers/master.dm b/code/controllers/master.dm index f44a5f5161e9..d30a344432d8 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -282,8 +282,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(sleep_offline_after_initializations && CONFIG_GET(flag/resume_after_initializations)) world.sleep_offline = FALSE initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10 - /// run votes - SSvote.initiate_vote(/datum/vote/storyteller, "pick round storyteller", forced = TRUE) // idk where else to run this lol + SSgamemode.handle_picking_stroyteller() //monkestation edit /** * Initialize a given subsystem and handle the results. diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm index 56ea80b3c98c..ee0e2c10422f 100644 --- a/code/controllers/subsystem/statpanel.dm +++ b/code/controllers/subsystem/statpanel.dm @@ -26,7 +26,7 @@ SUBSYSTEM_DEF(statpanels) global_data = list( "Map: [SSmapping.config?.map_name || "Loading..."]", cached ? "Next Map: [cached.map_name]" : null, - "Storyteller: [SSgamemode.storyteller ? SSgamemode.storyteller.name : "N/A"]", //monkestation addition + "Storyteller: [!SSgamemode.secret_storyteller && SSgamemode.storyteller ? SSgamemode.storyteller.name : "Secret"]", //monkestation addition "Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]", "Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss")]", "Round Time: [ROUND_TIME()]", diff --git a/code/game/objects/items/melee/baton.dm b/code/game/objects/items/melee/baton.dm index 1c7ea1d47a44..b7ed92f84449 100644 --- a/code/game/objects/items/melee/baton.dm +++ b/code/game/objects/items/melee/baton.dm @@ -18,7 +18,7 @@ /// Used interally, you don't want to modify var/cooldown_check = 0 /// Default wait time until can stun again. - var/cooldown = (1 SECONDS) + var/cooldown = (1.5 SECONDS) /// The length of the knockdown applied to a struck living, non-cyborg mob. var/knockdown_time = (1.5 SECONDS) /// If affect_cyborg is TRUE, this is how long we stun cyborgs for on a hit. @@ -26,7 +26,7 @@ /// The length of the knockdown applied to the user on clumsy_check() var/clumsy_knockdown_time = 18 SECONDS /// How much stamina damage we deal on a successful hit against a living, non-cyborg mob. - var/stamina_damage = 100 + var/stamina_damage = 95 //3 hit stam crit from full, but they most likely wont be due to running a bit /// Chance of causing force_say() when stunning a human mob var/force_say_chance = 33 /// Can we stun cyborgs? @@ -310,6 +310,7 @@ bare_wound_bonus = 5 clumsy_knockdown_time = 15 SECONDS active = FALSE + cooldown = 1.5 SECONDS //monkestation edit /// The sound effecte played when our baton is extended. var/on_sound = 'sound/weapons/batonextend.ogg' diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index e564e8aa7e13..aface7208736 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -76,7 +76,8 @@ . = ..() var/mob/M = usr - if(istype(over_object, /atom/movable/screen/inventory)) +//monkestation edit start, this is currently removed as it breaks things +/* if(istype(over_object, /atom/movable/screen/inventory)) var/atom/movable/screen/inventory/slot = over_object if(M.get_item_by_slot(slot.slot_id)) var/obj/item/clothing/item = M.get_item_by_slot(slot.slot_id) @@ -86,7 +87,8 @@ if(!M.put_in_inactive_hand(item)) if(!M.active_storage?.attempt_insert(item, M)) item.forceMove(get_turf(M)) - item.equip_to_best_slot() + item.equip_to_best_slot()*/ +//monkestation edit end if(ismecha(M.loc)) // stops inventory actions in a mech return diff --git a/code/modules/events/ghost_role/revenant_event.dm b/code/modules/events/ghost_role/revenant_event.dm index 04d3ce7309ec..079454cf11bb 100644 --- a/code/modules/events/ghost_role/revenant_event.dm +++ b/code/modules/events/ghost_role/revenant_event.dm @@ -54,7 +54,7 @@ return MAP_ERROR var/mob/living/basic/revenant/revvie = new(pick(spawn_locs)) - selected.mind.transfer_to(revvie) + revvie.key = selected.key message_admins("[ADMIN_LOOKUPFLW(revvie)] has been made into a revenant by an event.") revvie.log_message("was spawned as a revenant by an event.", LOG_GAME) spawned_mobs += revvie diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 479d2b8ad271..dcc19b859344 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -448,7 +448,7 @@ if(!I) to_chat(src, span_warning("You are not holding anything to equip!")) return - if (temporarilyRemoveItemFromInventory(, idrop = FALSE) && !QDELETED(I)) + if (temporarilyRemoveItemFromInventory(I) && !QDELETED(I)) if(I.equip_to_best_slot(src)) return if(put_in_active_hand(I)) diff --git a/monkestation/code/modules/storytellers/converted_events/solo/monsterhunter.dm b/monkestation/code/modules/storytellers/converted_events/solo/monsterhunter.dm new file mode 100644 index 000000000000..e096121d29e8 --- /dev/null +++ b/monkestation/code/modules/storytellers/converted_events/solo/monsterhunter.dm @@ -0,0 +1,47 @@ +#define MINIMUM_MONSTERS_REQUIRED 2 + +/datum/round_event_control/antagonist/solo/monsterhunter + track = EVENT_TRACK_MAJOR //being an anrtag event is for backend reasons, the event itself is major + antag_flag = ROLE_MONSTERHUNTER + tags = list(TAG_MAGICAL, TAG_TARGETED, TAG_COMBAT) + antag_datum = /datum/antagonist/monsterhunter + protected_roles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_PERSONNEL, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_RESEARCH_DIRECTOR, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_PRISONER, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + ) + restricted_roles = list( + JOB_AI, + JOB_CYBORG, + ) + min_players = 30 //no required enemies deu to instead needing enemy antags + weight = 2 + maximum_antags = 1 + prompted_picking = TRUE + +/datum/round_event_control/antagonist/solo/monsterhunter/can_spawn_event(players_amt, allow_magic = FALSE, fake_check = FALSE) + . = ..() + if(!.) + return + + var/count = 0 + for(var/datum/antagonist/monster as anything in GLOB.antagonists) + if(!monster.owner || !monster.owner.current || monster.owner.current.stat == DEAD) + continue + + if(GLOB.monster_antagonist_types.Find(monster.type)) + count++ + + if(MINIMUM_MONSTERS_REQUIRED > count) + return FALSE + + return ..() + +#undef MINIMUM_MONSTERS_REQUIRED diff --git a/monkestation/code/modules/storytellers/gamemode_subsystem.dm b/monkestation/code/modules/storytellers/gamemode_subsystem.dm index 6e72052eec7c..f8c993c4981d 100644 --- a/monkestation/code/modules/storytellers/gamemode_subsystem.dm +++ b/monkestation/code/modules/storytellers/gamemode_subsystem.dm @@ -1,6 +1,8 @@ #define INIT_ORDER_GAMEMODE 70 ///how many storytellers can be voted for along with always_votable ones #define DEFAULT_STORYTELLER_VOTE_OPTIONS 4 +///amount of players we can have before no longer running votes for storyteller +#define MAX_POP_FOR_STORYTELLER_VOTE 25 SUBSYSTEM_DEF(gamemode) name = "Gamemode" @@ -13,8 +15,8 @@ SUBSYSTEM_DEF(gamemode) var/list/event_tracks = EVENT_TRACKS /// Our storyteller. They progresses our trackboards and picks out events var/datum/storyteller/storyteller - /// Result of the storyteller vote. Defaults to the guide. - var/voted_storyteller = /datum/storyteller/guide + /// Result of the storyteller vote/pick. Defaults to the guide. + var/selected_storyteller = /datum/storyteller/guide /// List of all the storytellers. Populated at init. Associative from type var/list/storytellers = list() /// Next process for our storyteller. The wait time is STORYTELLER_WAIT_TIME @@ -141,7 +143,10 @@ SUBSYSTEM_DEF(gamemode) var/sec_crew = 0 var/med_crew = 0 - var/wizardmode = FALSE + /// Is storyteller secret or not + var/secret_storyteller = FALSE + + var/wizardmode = FALSE //refactor this into just being a unique storyteller var/datum/round_event_control/current_roundstart_event var/list/last_round_events = list() @@ -777,21 +782,21 @@ SUBSYSTEM_DEF(gamemode) point_thresholds[EVENT_TRACK_ROLESET] = CONFIG_GET(number/roleset_point_threshold) point_thresholds[EVENT_TRACK_OBJECTIVES] = CONFIG_GET(number/objectives_point_threshold) +/datum/controller/subsystem/gamemode/proc/handle_picking_stroyteller() + if(length(GLOB.clients) > MAX_POP_FOR_STORYTELLER_VOTE) + secret_storyteller = TRUE + selected_storyteller = pick_weight(get_valid_storytellers(TRUE)) + return + SSvote.initiate_vote(/datum/vote/storyteller, "pick round storyteller", forced = TRUE) + /datum/controller/subsystem/gamemode/proc/storyteller_vote_choices() - var/client_amount = length(GLOB.clients) var/list/final_choices = list() var/list/pick_from = list() - for(var/storyteller_type in storytellers) - var/datum/storyteller/storyboy = storytellers[storyteller_type] - if(!storyboy.votable) - continue - if((storyboy.population_min && storyboy.population_min > client_amount) || (storyboy.population_max && storyboy.population_max < client_amount)) - continue - + for(var/datum/storyteller/storyboy in get_valid_storytellers()) if(storyboy.always_votable) final_choices[storyboy.name] = 0 else - pick_from[storyboy.name] = storyboy.weight + pick_from[storyboy.name] = storyboy.weight //might be able to refactor this to be slightly better due to get_valid_storytellers returning a weighted list var/added_storytellers = 0 while(added_storytellers < DEFAULT_STORYTELLER_VOTE_OPTIONS && length(pick_from)) @@ -813,19 +818,35 @@ SUBSYSTEM_DEF(gamemode) for(var/storyteller_type in storytellers) var/datum/storyteller/storyboy = storytellers[storyteller_type] if(storyboy.name == winner_name) - voted_storyteller = storyteller_type + selected_storyteller = storyteller_type break +///return a weighted list of all storytellers that are currently valid to roll, if return_types is set then we will return types instead of instances +/datum/controller/subsystem/gamemode/proc/get_valid_storytellers(return_types = FALSE) + var/client_amount = length(GLOB.clients) + var/list/valid_storytellers = list() + for(var/storyteller_type in storytellers) + var/datum/storyteller/storyboy = storytellers[storyteller_type] + if(storyboy.restricted || (storyboy.population_min && storyboy.population_min > client_amount) || (storyboy.population_max && storyboy.population_max < client_amount)) + continue + + valid_storytellers[return_types ? storyboy.type : storyboy] = storyboy.weight + return valid_storytellers + /datum/controller/subsystem/gamemode/proc/init_storyteller() - set_storyteller(voted_storyteller) + set_storyteller(selected_storyteller) /datum/controller/subsystem/gamemode/proc/set_storyteller(passed_type) if(!storytellers[passed_type]) - message_admins("Attempted to set an invalid storyteller type: [passed_type].") + message_admins("Attempted to set an invalid storyteller type: [passed_type], force setting to guide instead.") + storyteller = storytellers[/datum/storyteller/guide] //if we dont have any then we brick, lets not do that CRASH("Attempted to set an invalid storyteller type: [passed_type].") storyteller = storytellers[passed_type] - to_chat(world, span_notice("Storyteller is [storyteller.name]!")) - to_chat(world, span_notice("[storyteller.welcome_text]")) + if(!secret_storyteller) + send_to_playing_players(span_notice("Storyteller is [storyteller.name]!")) + send_to_playing_players(span_notice("[storyteller.welcome_text]")) + else + send_to_observers(span_boldbig("Storyteller is [storyteller.name]!")) //observers still get to know /// Panel containing information, variables and controls about the gamemode and scheduled event /datum/controller/subsystem/gamemode/proc/admin_panel(mob/user) @@ -1173,3 +1194,4 @@ SUBSYSTEM_DEF(gamemode) listed.occurrences++ #undef DEFAULT_STORYTELLER_VOTE_OPTIONS +#undef MAX_POP_FOR_STORYTELLER_VOTE diff --git a/monkestation/code/modules/storytellers/storytellers/_storyteller.dm b/monkestation/code/modules/storytellers/storytellers/_storyteller.dm index f245ac730a49..f52e32de3c8d 100644 --- a/monkestation/code/modules/storytellers/storytellers/_storyteller.dm +++ b/monkestation/code/modules/storytellers/storytellers/_storyteller.dm @@ -40,8 +40,8 @@ /// Whether the storyteller has the distributions disabled. Important for ghost storytellers var/disable_distribution = FALSE - /// Whether people can vote for the storyteller - var/votable = TRUE + /// Whether a storyteller is pickable/can be voted for + var/restricted = FALSE /// If defined, will need a minimum of population to be votable var/population_min /// If defined, it will not be votable if exceeding the population @@ -56,7 +56,7 @@ var/ignores_roundstart = FALSE ///is a storyteller always able to be voted for(also does not count for the amount of storytellers to pick from) var/always_votable = FALSE - ///weight this has of showing up in the vote if not always_votable + ///weight this has of being picked for random storyteller/showing up in the vote if not always_votable var/weight = 0 /datum/storyteller/process(delta_time) @@ -181,4 +181,5 @@ /datum/storyteller/guide name = "The Guide" desc = "The Guide will provide a balanced and varied experience. Consider this the default experience." + weight = 8 always_votable = TRUE diff --git a/monkestation/code/modules/storytellers/storytellers/clown.dm b/monkestation/code/modules/storytellers/storytellers/clown.dm index 5a785ca75e26..27e0f6ce4076 100644 --- a/monkestation/code/modules/storytellers/storytellers/clown.dm +++ b/monkestation/code/modules/storytellers/storytellers/clown.dm @@ -12,6 +12,6 @@ ) tag_multipliers = list(TAG_COMMUNAL = 1.1, TAG_SPOOKY = 1.2) guarantees_roundstart_roleset = FALSE - votable = FALSE //admins can still use this if they want the crew to really suffer, for that reason im going all in + restricted = FALSE //admins can still use this if they want the crew to really suffer, for that reason im going all in roundstart_prob = 75 ignores_roundstart = TRUE diff --git a/monkestation/code/modules/storytellers/storytellers/ghost.dm b/monkestation/code/modules/storytellers/storytellers/ghost.dm index e347f554e609..7659268aaa14 100644 --- a/monkestation/code/modules/storytellers/storytellers/ghost.dm +++ b/monkestation/code/modules/storytellers/storytellers/ghost.dm @@ -5,4 +5,4 @@ disable_distribution = TRUE population_max = 25 welcome_text = "The station feels invisible to outside influence." - weight = 4 //extra likely if its an option + weight = 3 diff --git a/monkestation/code/modules/storytellers/storytellers/sleeper.dm b/monkestation/code/modules/storytellers/storytellers/sleeper.dm index 45460feba3ee..6539e51ca965 100644 --- a/monkestation/code/modules/storytellers/storytellers/sleeper.dm +++ b/monkestation/code/modules/storytellers/storytellers/sleeper.dm @@ -12,3 +12,4 @@ tag_multipliers = list(TAG_COMBAT = 0.6, TAG_DESTRUCTIVE = 0.7) always_votable = TRUE //good for low pop welcome_text = "The day is going slowly." + weight = 1 //close to greenshift so its very low weight diff --git a/tgstation.dme b/tgstation.dme index 501b257d2f5c..7df2f08948c0 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -6488,6 +6488,7 @@ #include "monkestation\code\modules\storytellers\converted_events\solo\clown_operative.dm" #include "monkestation\code\modules\storytellers\converted_events\solo\heretic.dm" #include "monkestation\code\modules\storytellers\converted_events\solo\malf.dm" +#include "monkestation\code\modules\storytellers\converted_events\solo\monsterhunter.dm" #include "monkestation\code\modules\storytellers\converted_events\solo\nuclear_operative.dm" #include "monkestation\code\modules\storytellers\converted_events\solo\obsessed.dm" #include "monkestation\code\modules\storytellers\converted_events\solo\revolutionary.dm"