From dec0629faef89df1b59d6a15931233ffce833e26 Mon Sep 17 00:00:00 2001 From: HarpyEagle Date: Sun, 11 Oct 2015 11:26:01 -0400 Subject: [PATCH 01/15] Renames latespawn() to be less confusing --- code/game/gamemodes/game_mode_latespawn.dm | 3 ++- code/modules/mob/new_player/new_player.dm | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/code/game/gamemodes/game_mode_latespawn.dm b/code/game/gamemodes/game_mode_latespawn.dm index 038cf351ab..f2bce2d871 100644 --- a/code/game/gamemodes/game_mode_latespawn.dm +++ b/code/game/gamemodes/game_mode_latespawn.dm @@ -20,7 +20,8 @@ process_count = 0 try_latespawn() -/datum/game_mode/proc/latespawn(var/mob/living/carbon/human/character) +//This can be overriden in case a game mode needs to do stuff when a player latejoins +/datum/game_mode/proc/handle_latejoin(var/mob/living/carbon/human/character) if(!character.mind) return try_latespawn(character.mind) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index fb3b6f4fca..6afacc4af9 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -323,7 +323,7 @@ character.loc = C.loc AnnounceCyborg(character, rank, "has been downloaded to the empty core in \the [character.loc.loc]") - ticker.mode.latespawn(character) + ticker.mode.handle_latejoin(character) qdel(C) qdel(src) @@ -354,7 +354,7 @@ character.buckled.loc = character.loc character.buckled.set_dir(character.dir) - ticker.mode.latespawn(character) + ticker.mode.handle_latejoin(character) if(character.mind.assigned_role != "Cyborg") data_core.manifest_inject(character) From 43d5f2e3bd493ff6fb6b9673be190af953684659 Mon Sep 17 00:00:00 2001 From: HarpyEagle Date: Sun, 11 Oct 2015 11:39:26 -0400 Subject: [PATCH 02/15] Ensures leftover candidates and pending antags are cleared after finalizing --- code/game/antagonist/antagonist.dm | 9 +++++++-- code/game/gamemodes/game_mode.dm | 2 +- code/modules/mob/new_player/new_player.dm | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/code/game/antagonist/antagonist.dm b/code/game/antagonist/antagonist.dm index faf7526a9c..d9bc5c5690 100644 --- a/code/game/antagonist/antagonist.dm +++ b/code/game/antagonist/antagonist.dm @@ -186,11 +186,16 @@ for(var/datum/mind/player in pending_antagonists) pending_antagonists -= player add_antagonist(player,0,0,1) + + reset_antag_selection() -//Resets all pending_antagonists, clearing their special_role (and assigned_role if ANTAG_OVERRIDE_JOB is set) -/datum/antagonist/proc/reset() +//Resets the antag selection, clearing all pending_antagonists and their special_role +//(and assigned_role if ANTAG_OVERRIDE_JOB is set) as well as clearing the candidate list. +//Existing antagonists are left untouched. +/datum/antagonist/proc/reset_antag_selection() for(var/datum/mind/player in pending_antagonists) if(flags & ANTAG_OVERRIDE_JOB) player.assigned_role = null player.special_role = null pending_antagonists.Cut() + candidates.Cut() diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 705c044e01..57a936f581 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -305,7 +305,7 @@ var/global/list/additional_antag_types = list() /datum/game_mode/proc/fail_setup() for(var/datum/antagonist/antag in antag_templates) - antag.reset() + antag.reset_antag_selection() /datum/game_mode/proc/announce_ert_disabled() if(!ert_disabled) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 6afacc4af9..9eb43e48d9 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -289,7 +289,7 @@ proc/AttemptLateSpawn(rank,var/spawning_at) - if (src != usr) + if(src != usr) return 0 if(!ticker || ticker.current_state != GAME_STATE_PLAYING) usr << "\red The round is either not ready, or has already finished..." From d715e0645fada6bfe067fc93b2d4cb863053b00b Mon Sep 17 00:00:00 2001 From: HarpyEagle Date: Sun, 11 Oct 2015 11:48:04 -0400 Subject: [PATCH 03/15] Removes unused latejoin template list --- code/game/gamemodes/game_mode.dm | 3 --- code/game/gamemodes/game_mode_latespawn.dm | 6 ++---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 57a936f581..b8a99badf1 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -22,7 +22,6 @@ var/global/list/additional_antag_types = list() var/list/antag_tags = list() // Core antag templates to spawn. var/list/antag_templates // Extra antagonist types to include. - var/list/latejoin_templates = list() var/round_autoantag = 0 // Will this round attempt to periodically spawn more antagonists? var/antag_scaling_coeff = 5 // Coefficient for scaling max antagonists to player count. var/require_all_templates = 0 // Will only start if all templates are checked and can spawn. @@ -291,8 +290,6 @@ var/global/list/additional_antag_types = list() if(!(antag.flags & ANTAG_OVERRIDE_JOB)) antag.attempt_spawn() //select antags to be spawned antag.finalize_spawn() //actually spawn antags - if(antag.is_latejoin_template()) - latejoin_templates |= antag if(emergency_shuttle && auto_recall_shuttle) emergency_shuttle.auto_recall = 1 diff --git a/code/game/gamemodes/game_mode_latespawn.dm b/code/game/gamemodes/game_mode_latespawn.dm index f2bce2d871..28db276b49 100644 --- a/code/game/gamemodes/game_mode_latespawn.dm +++ b/code/game/gamemodes/game_mode_latespawn.dm @@ -27,7 +27,7 @@ try_latespawn(character.mind) return 0 -/datum/game_mode/proc/try_latespawn(var/datum/mind/player, var/latejoin_only) +/datum/game_mode/proc/try_latespawn(var/datum/mind/player) if(emergency_shuttle.departed || !round_autoantag) return @@ -38,9 +38,7 @@ message_admins("AUTO[uppertext(name)]: Attempting spawn.") var/list/usable_templates - if(latejoin_only && latejoin_templates.len) - usable_templates = get_usable_templates(latejoin_templates) - else if (antag_templates && antag_templates.len) + if (antag_templates && antag_templates.len) usable_templates = get_usable_templates(antag_templates) else message_admins("AUTO[uppertext(name)]: Failed to find configured mode spawn templates, please disable auto-antagonists until one is added.") From c7afded12d7aeac970d46f863021ebae08516d4a Mon Sep 17 00:00:00 2001 From: HarpyEagle Date: Sun, 11 Oct 2015 12:00:21 -0400 Subject: [PATCH 04/15] Cleans up calamity shuffle, makes sure antag types are selected in a random order for other game modes. --- code/game/gamemodes/calamity/calamity.dm | 13 +++++++------ code/game/gamemodes/game_mode.dm | 1 + 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/code/game/gamemodes/calamity/calamity.dm b/code/game/gamemodes/calamity/calamity.dm index 4f7b9ab49d..3104e624e3 100644 --- a/code/game/gamemodes/calamity/calamity.dm +++ b/code/game/gamemodes/calamity/calamity.dm @@ -12,14 +12,15 @@ /datum/game_mode/calamity/create_antagonists() - shuffle(all_antag_types) // This is probably the only instance in the game where the order will be important. - var/i = 1 + //Let's not modify global lists for trivial reasons, even if it seems harmless right now. + var/list/antag_candidates = all_antag_types.Copy() + var/grab_antags = round(num_players()/ANTAG_TYPE_RATIO)+1 - for(var/antag_id in all_antag_types) - if(i > grab_antags) - break + while(antag_candidates.len && antag_tags.len < grab_antags) + var/antag_id = pick(antag_candidates) + antag_candidates -= antag_id antag_tags |= antag_id - i++ + ..() /datum/game_mode/calamity/check_victory() diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index b8a99badf1..08a8289060 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -572,6 +572,7 @@ var/global/list/additional_antag_types = list() if(antag) antag_templates |= antag + shuffle(antag_templates) //In the case of multiple antag types newscaster_announcements = pick(newscaster_standard_feeds) /datum/game_mode/proc/check_victory() From e381e2faf2afa6b89ef7bbecf5e1854f0effd776 Mon Sep 17 00:00:00 2001 From: HarpyEagle Date: Sun, 11 Oct 2015 12:16:08 -0400 Subject: [PATCH 05/15] Fixes do_not_announce parameters not actually doing anything --- code/game/antagonist/antagonist_create.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/game/antagonist/antagonist_create.dm b/code/game/antagonist/antagonist_create.dm index 416a774541..1b8b82210a 100644 --- a/code/game/antagonist/antagonist_create.dm +++ b/code/game/antagonist/antagonist_create.dm @@ -16,7 +16,8 @@ create_objectives(target) update_icons_added(target) greet(target) - announce_antagonist_spawn() + if(!gag_announcement) + announce_antagonist_spawn() /datum/antagonist/proc/create_default(var/mob/source) var/mob/living/M From 241520f0bb4cc5e0d639df8942b86e7ec8c5f4d6 Mon Sep 17 00:00:00 2001 From: HarpyEagle Date: Mon, 12 Oct 2015 12:47:41 -0400 Subject: [PATCH 06/15] Cleans up autospawn, now uses existing code for selecting and choosing candidates. Adds more debug messages, and removes unused code. --- code/game/antagonist/antagonist.dm | 54 +++++++++++++--------- code/game/antagonist/antagonist_helpers.dm | 6 +-- code/game/antagonist/station/rogue_ai.dm | 4 +- code/game/gamemodes/game_mode.dm | 1 + code/game/gamemodes/game_mode_latespawn.dm | 53 ++++++++------------- code/modules/admin/admin.dm | 7 ++- 6 files changed, 60 insertions(+), 65 deletions(-) diff --git a/code/game/antagonist/antagonist.dm b/code/game/antagonist/antagonist.dm index d9bc5c5690..011ae5ebe6 100644 --- a/code/game/antagonist/antagonist.dm +++ b/code/game/antagonist/antagonist.dm @@ -86,7 +86,8 @@ /datum/antagonist/proc/tick() return 1 -/datum/antagonist/proc/get_candidates(var/ghosts_only) +// Get the raw list of potential players. +/datum/antagonist/proc/build_candidate_list(var/ghosts_only) candidates = list() // Clear. // Prune restricted status. Broke it up for readability. @@ -108,46 +109,57 @@ return candidates /datum/antagonist/proc/attempt_random_spawn() + update_current_antag_max() build_candidate_list(flags & (ANTAG_OVERRIDE_MOB|ANTAG_OVERRIDE_JOB)) attempt_spawn() finalize_spawn() -/datum/antagonist/proc/attempt_late_spawn(var/datum/mind/player) +/datum/antagonist/proc/attempt_auto_spawn() if(!can_late_spawn()) return 0 - if(!istype(player)) - var/list/players = get_candidates(is_latejoin_template()) - if(players && players.len) - player = pick(players) - if(!istype(player)) - message_admins("AUTO[uppertext(ticker.mode.name)]: Failed to find a candidate for [role_text].") - return 0 - player.current << "You have been selected this round as an antagonist!" - message_admins("AUTO[uppertext(ticker.mode.name)]: Selected [player] as a [role_text].") - if(istype(player.current, /mob/dead)) - create_default(player.current) - else - add_antagonist(player,0,0,0,1,1) - return 1 -/datum/antagonist/proc/build_candidate_list(var/ghosts_only) - // Get the raw list of potential players. update_current_antag_max() - candidates = get_candidates(ghosts_only) + var/active_antags = get_active_antag_count() + log_debug("[uppertext(id)]: Found [active_antags]/[cur_max] active [role_text_plural].") + + if(active_antags >= cur_max) + log_debug("Could not auto-spawn a [role_text], active antag limit reached.") + return 0 + + build_candidate_list(flags & (ANTAG_OVERRIDE_MOB|ANTAG_OVERRIDE_JOB)) + if(!candidates.len) + log_debug("Could not auto-spawn a [role_text], no candidates found.") + return 0 + + attempt_spawn(1) //auto-spawn antags one at a time + if(!pending_antagonists.len) + log_debug("Could not auto-spawn a [role_text], none of the available candidates could be selected.") + return 0 + + var/datum/mind/player = pending_antagonists[1] + if(!add_antagonist(player,0,0,0,1,1)) + log_debug("Could not auto-spawn a [role_text], failed to add antagonist.") + return 0 + + reset_antag_selection() + + return 1 //Selects players that will be spawned in the antagonist role from the potential candidates //Selected players are added to the pending_antagonists lists. //Attempting to spawn an antag role with ANTAG_OVERRIDE_JOB should be done before jobs are assigned, //so that they do not occupy regular job slots. All other antag roles should be spawned after jobs are //assigned, so that job restrictions can be respected. -/datum/antagonist/proc/attempt_spawn(var/rebuild_candidates = 1) +/datum/antagonist/proc/attempt_spawn(var/spawn_target = null) + if(spawn_target == null) + spawn_target = initial_spawn_target // Update our boundaries. if(!candidates.len) return 0 //Grab candidates randomly until we have enough. - while(candidates.len && pending_antagonists.len < initial_spawn_target) + while(candidates.len && pending_antagonists.len < spawn_target) var/datum/mind/player = pick(candidates) candidates -= player draft_antagonist(player) diff --git a/code/game/antagonist/antagonist_helpers.dm b/code/game/antagonist/antagonist_helpers.dm index 056fbd1865..37e332d8ef 100644 --- a/code/game/antagonist/antagonist_helpers.dm +++ b/code/game/antagonist/antagonist_helpers.dm @@ -20,6 +20,9 @@ /datum/antagonist/proc/get_antag_count() return current_antagonists ? current_antagonists.len : 0 +/datum/antagonist/proc/get_active_antag_count() + return get_antag_count() //TODO + /datum/antagonist/proc/is_antagonist(var/datum/mind/player) if(player in current_antagonists) return 1 @@ -33,9 +36,6 @@ return (flags & ANTAG_VOTABLE) /datum/antagonist/proc/can_late_spawn() - update_current_antag_max() - if(get_antag_count() >= cur_max) - return 0 return 1 /datum/antagonist/proc/is_latejoin_template() diff --git a/code/game/antagonist/station/rogue_ai.dm b/code/game/antagonist/station/rogue_ai.dm index 52fa7e3f38..a277df922b 100644 --- a/code/game/antagonist/station/rogue_ai.dm +++ b/code/game/antagonist/station/rogue_ai.dm @@ -22,13 +22,11 @@ var/datum/antagonist/rogue_ai/malf malf = src -/datum/antagonist/rogue_ai/get_candidates() +/datum/antagonist/rogue_ai/build_candidate_list() ..() for(var/datum/mind/player in candidates) if(player.assigned_role && player.assigned_role != "AI") candidates -= player - if(!candidates.len) - return list() return candidates diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 08a8289060..28f420547f 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -266,6 +266,7 @@ var/global/list/additional_antag_types = list() /datum/game_mode/proc/pre_setup() for(var/datum/antagonist/antag in antag_templates) + antag.update_current_antag_max() antag.build_candidate_list() //compile a list of all eligible candidates //antag roles that replace jobs need to be assigned before the job controller hands out jobs. diff --git a/code/game/gamemodes/game_mode_latespawn.dm b/code/game/gamemodes/game_mode_latespawn.dm index 28db276b49..9a10f0ce35 100644 --- a/code/game/gamemodes/game_mode_latespawn.dm +++ b/code/game/gamemodes/game_mode_latespawn.dm @@ -3,54 +3,39 @@ /datum/game_mode/var/max_autotraitor_delay = 12000 // Approx 20 minutes. /datum/game_mode/var/process_count = 0 -/datum/game_mode/proc/get_usable_templates(var/list/supplied_templates) - var/list/usable_templates = list() - for(var/datum/antagonist/A in supplied_templates) - if(A.can_late_spawn()) - message_admins("AUTO[uppertext(name)]: [A.id] selected for spawn attempt.") - usable_templates |= A - return usable_templates - ///process() ///Called by the gameticker /datum/game_mode/proc/process() - // Slow this down a bit so latejoiners have a chance of being antags. - process_count++ - if(process_count >= 10) - process_count = 0 - try_latespawn() + if(round_autoantag && world.time < next_spawn && !emergency_shuttle.departed) + process_autoantag() //This can be overriden in case a game mode needs to do stuff when a player latejoins /datum/game_mode/proc/handle_latejoin(var/mob/living/carbon/human/character) - if(!character.mind) - return - try_latespawn(character.mind) return 0 -/datum/game_mode/proc/try_latespawn(var/datum/mind/player) +/datum/game_mode/proc/process_autoantag() + message_admins("[uppertext(name)]: Attempting autospawn.") - if(emergency_shuttle.departed || !round_autoantag) - return - - if(world.time < next_spawn) - return - - message_admins("AUTO[uppertext(name)]: Attempting spawn.") - - var/list/usable_templates - if (antag_templates && antag_templates.len) - usable_templates = get_usable_templates(antag_templates) - else - message_admins("AUTO[uppertext(name)]: Failed to find configured mode spawn templates, please disable auto-antagonists until one is added.") + var/list/usable_templates = list() + for(var/datum/antagonist/A in antag_templates) + if(A.can_late_spawn()) + message_admins("[uppertext(name)]: [A.id] selected for spawn attempt.") + usable_templates |= A + + if(!usable_templates.len) + message_admins("[uppertext(name)]: Failed to find configured mode spawn templates, please re-enable auto-antagonists after one is added.") round_autoantag = 0 return - + while(usable_templates.len) var/datum/antagonist/spawn_antag = pick(usable_templates) usable_templates -= spawn_antag - if(spawn_antag.attempt_late_spawn(player)) - message_admins("AUTO[uppertext(name)]: Attempting to latespawn [spawn_antag.id]. ([spawn_antag.get_antag_count()]/[spawn_antag.cur_max])") + + if(spawn_antag.attempt_auto_spawn()) + message_admins("[uppertext(name)]: Auto-added a new [spawn_antag.role_text].") + message_admins("There are now [spawn_antag.get_active_antag_count()]/[spawn_antag.cur_max] active [spawn_antag.role_text_plural].") next_spawn = world.time + rand(min_autotraitor_delay, max_autotraitor_delay) return - message_admins("AUTO[uppertext(name)]: Failed to proc a viable spawn template.") + + message_admins("[uppertext(name)]: Failed to proc a viable spawn template.") next_spawn = world.time + rand(min_autotraitor_delay, max_autotraitor_delay) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index f02292d81f..241f394cca 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -1386,7 +1386,7 @@ proc/admin_notice(var/message, var/rights) var/datum/antagonist/antag = all_antag_types[antag_type] message_admins("[key_name(usr)] attempting to force latespawn with template [antag.id].") - antag.attempt_late_spawn() + antag.attempt_auto_spawn() /datum/admins/proc/force_mode_latespawn() set category = "Admin" @@ -1403,6 +1403,5 @@ proc/admin_notice(var/message, var/rights) usr << "Mode has not started." return - message_admins("[key_name(usr)] attempting to force mode latespawn.") - ticker.mode.next_spawn = 0 - ticker.mode.try_latespawn() + message_admins("[key_name(usr)] attempting to force mode autospawn.") + ticker.mode.process_autoantag() From c0dc32d60cbe5a2adde646fc84de4072ed471317 Mon Sep 17 00:00:00 2001 From: HarpyEagle Date: Mon, 12 Oct 2015 12:50:48 -0400 Subject: [PATCH 07/15] If an antag could not be auto-spawned, the game rechecks in the minimum time. --- code/game/gamemodes/game_mode_latespawn.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/game_mode_latespawn.dm b/code/game/gamemodes/game_mode_latespawn.dm index 9a10f0ce35..9c79a68925 100644 --- a/code/game/gamemodes/game_mode_latespawn.dm +++ b/code/game/gamemodes/game_mode_latespawn.dm @@ -38,4 +38,4 @@ return message_admins("[uppertext(name)]: Failed to proc a viable spawn template.") - next_spawn = world.time + rand(min_autotraitor_delay, max_autotraitor_delay) + next_spawn = world.time + min_autotraitor_delay //recheck again in the miniumum time From d7b6784e2834d18ea01353db2b14732be7687e1c Mon Sep 17 00:00:00 2001 From: PsiOmegaDelta Date: Tue, 13 Oct 2015 19:44:49 +0200 Subject: [PATCH 08/15] Fixes law change logging. The "Show last X law changes" review window should now actually be useful, displaying AI law updates with time, user, and law change. --- code/modules/admin/admin.dm | 2 +- code/modules/admin/topic.dm | 2 +- code/modules/mob/living/silicon/laws.dm | 20 ++++++++++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index f02292d81f..9331819654 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -612,7 +612,7 @@ proc/admin_notice(var/message, var/rights) Bombing List
Show current traitors and objectives
Show last [length(lastsignalers)] signalers
- Show last [length(lawchanges)] law changes
+ Show last [lawchanges.len] law change\s
Show AI Laws
Show Game Mode
Show Crew Manifest
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 983292ebc3..45397fa097 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -2300,7 +2300,7 @@ dat += "[sig]
" usr << browse(dat, "window=lastsignalers;size=800x500") if("list_lawchanges") - var/dat = "Showing last [length(lawchanges)] law changes.
" + var/dat = "Showing last [lawchanges.len] law change\s.
" for(var/sig in lawchanges) dat += "[sig]
" usr << browse(dat, "window=lawchanges;size=800x500") diff --git a/code/modules/mob/living/silicon/laws.dm b/code/modules/mob/living/silicon/laws.dm index 5c2068abdf..90236d2e52 100644 --- a/code/modules/mob/living/silicon/laws.dm +++ b/code/modules/mob/living/silicon/laws.dm @@ -12,7 +12,7 @@ /mob/living/silicon/proc/set_zeroth_law(var/law, var/law_borg) laws_sanity_check() laws.set_zeroth_law(law, law_borg) - log_and_message_admins("has given [src] the zeroth laws: [law]/[law_borg ? law_borg : "N/A"]") + log_law("has given [src] the zeroth law: '[law]'[law_borg ? " / '[law_borg]'" : ""]") /mob/living/silicon/robot/set_zeroth_law(var/law, var/law_borg) ..() @@ -22,40 +22,40 @@ /mob/living/silicon/proc/add_ion_law(var/law) laws_sanity_check() laws.add_ion_law(law) - log_and_message_admins("has given [src] the ion law: [law]") + log_law("has given [src] the ion law: [law]") /mob/living/silicon/proc/add_inherent_law(var/law) laws_sanity_check() laws.add_inherent_law(law) - log_and_message_admins("has given [src] the inherent law: [law]") + log_law("has given [src] the inherent law: [law]") /mob/living/silicon/proc/add_supplied_law(var/number, var/law) laws_sanity_check() laws.add_supplied_law(number, law) - log_and_message_admins("has given [src] the supplied law: [law]") + log_law("has given [src] the supplied law: [law]") /mob/living/silicon/proc/delete_law(var/datum/ai_law/law) laws_sanity_check() laws.delete_law(law) - log_and_message_admins("has deleted a law belonging to [src]: [law.law]") + log_law("has deleted a law belonging to [src]: [law.law]") /mob/living/silicon/proc/clear_inherent_laws(var/silent = 0) laws_sanity_check() laws.clear_inherent_laws() if(!silent) - log_and_message_admins("cleared the inherent laws of [src]") + log_law("cleared the inherent laws of [src]") /mob/living/silicon/proc/clear_ion_laws(var/silent = 0) laws_sanity_check() laws.clear_ion_laws() if(!silent) - log_and_message_admins("cleared the ion laws of [src]") + log_law("cleared the ion laws of [src]") /mob/living/silicon/proc/clear_supplied_laws(var/silent = 0) laws_sanity_check() laws.clear_supplied_laws() if(!silent) - log_and_message_admins("cleared the supplied laws of [src]") + log_law("cleared the supplied laws of [src]") /mob/living/silicon/proc/statelaws(var/datum/ai_laws/laws) var/prefix = "" @@ -102,3 +102,7 @@ /mob/living/silicon/proc/lawsync() laws_sanity_check() laws.sort_laws() + +/mob/living/silicon/proc/log_law(var/law_message) + log_and_message_admins(law_message) + lawchanges += "[worldtime2text()] - [usr ? "[key_name(usr)]" : "EVENT"] [law_message]" From ffb26a6f87f01d3418523fa7a965614b49eb28bb Mon Sep 17 00:00:00 2001 From: HarpyEagle Date: Mon, 12 Oct 2015 13:25:42 -0400 Subject: [PATCH 09/15] Adds checks for antags being dead or SSD --- code/game/antagonist/antagonist_helpers.dm | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/code/game/antagonist/antagonist_helpers.dm b/code/game/antagonist/antagonist_helpers.dm index 37e332d8ef..adc706734f 100644 --- a/code/game/antagonist/antagonist_helpers.dm +++ b/code/game/antagonist/antagonist_helpers.dm @@ -21,7 +21,15 @@ return current_antagonists ? current_antagonists.len : 0 /datum/antagonist/proc/get_active_antag_count() - return get_antag_count() //TODO + var/active_antags = 0 + for(var/datum/mind/player in current_antagonists) + var/mob/living/L = player.current + if(!L || L.stat == DEAD) + continue //no mob or dead + if(!L.client && !L.teleop) + continue //SSD + active_antags++ + return active_antags /datum/antagonist/proc/is_antagonist(var/datum/mind/player) if(player in current_antagonists) From fd03e4d59b63d90c11ca586f89b34461fece75fa Mon Sep 17 00:00:00 2001 From: PsiOmegaDelta Date: Thu, 15 Oct 2015 10:18:55 +0200 Subject: [PATCH 10/15] Surgical kits can now contain their equipment. The surgical kit can now contain the equipment it came with. It can also not contain more items of a given type than it was spawned with. This means that even if the storage container otherwise has sufficient space it can, for example, still only ever contain 1 surgical saw. Fixes #11298. Fixes #11297. --- code/__HELPERS/lists.dm | 16 +++++++--- .../objects/items/weapons/storage/firstaid.dm | 6 ++-- .../objects/items/weapons/storage/storage.dm | 31 ++++++++++++++----- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index ed0f0c9780..a6b010dbc9 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -71,11 +71,17 @@ proc/isemptylist(list/list) return 1 return 0 -//Empties the list by setting the length to 0. Hopefully the elements get garbage collected -proc/clearlist(list/list) - if(istype(list)) - list.len = 0 - return +/proc/instances_of_type_in_list(var/atom/A, var/list/L) + var/instances = 0 + for(var/type in L) + if(istype(A, type)) + instances++ + return instances + +//Empties the list by .Cut(). Setting lenght = 0 has been confirmed to leak references. +proc/clearlist(var/list/L) + if(islist(L)) + L.Cut() //Removes any null entries from the list proc/listclearnulls(list/list) diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm index c23dfcdb51..a76fb4f1a1 100644 --- a/code/game/objects/items/weapons/storage/firstaid.dm +++ b/code/game/objects/items/weapons/storage/firstaid.dm @@ -130,8 +130,7 @@ /obj/item/weapon/storage/firstaid/surgery name = "surgery kit" - desc = "Contains tools for surgery." - storage_slots = 10 + desc = "Contains tools for surgery. Has precise foam fitting for safe transport." /obj/item/weapon/storage/firstaid/surgery/New() ..() @@ -146,7 +145,8 @@ new /obj/item/weapon/bonegel(src) new /obj/item/weapon/FixOVein(src) new /obj/item/stack/medical/advanced/bruise_pack(src) - return + + make_exact_fit() /* * Pill Bottles diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index 22277c33e2..6f2ba544ab 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -51,10 +51,10 @@ //there's got to be a better way of doing this. if (!(src.loc == usr) || (src.loc && src.loc.loc == usr)) return - + if (( usr.restrained() ) || ( usr.stat )) return - + if ((src.loc == usr) && !usr.unEquip(src)) return @@ -232,12 +232,16 @@ usr << "[src] is full, make some space." return 0 //Storage item is full - if(can_hold.len && !is_type_in_list(W, can_hold)) - if(!stop_messages) - if (istype(W, /obj/item/weapon/hand_labeler)) - return 0 - usr << "[src] cannot hold [W]." - return 0 + if(can_hold.len) + if(!is_type_in_list(W, can_hold)) + if(!stop_messages && ! istype(W, /obj/item/weapon/hand_labeler)) + usr << "[src] cannot hold [W]." + return 0 + var/max_instances = can_hold[W.type] + if(max_instances && instances_of_type_in_list(W, contents) >= max_instances) + if(!stop_messages && !istype(W, /obj/item/weapon/hand_labeler)) + usr << "[src] has no more space for an additional [W]." + return 0 if(cant_hold.len && is_type_in_list(W, cant_hold)) if(!stop_messages) @@ -449,6 +453,17 @@ var/obj/O = A O.hear_talk(M, text, verb, speaking) +/obj/item/weapon/storage/proc/make_exact_fit() + storage_slots = contents.len + + can_hold.Cut() + max_w_class = 0 + max_storage_space = 0 + for(var/obj/item/I in src) + max_w_class = max(I.w_class, max_w_class) + max_storage_space += I.get_storage_cost() + can_hold[I.type] = ++can_hold[I.type] + //Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area). //Returns -1 if the atom was not found on container. /atom/proc/storage_depth(atom/container) From e79e82f55d54e4b5a41272718ecf27b43275af67 Mon Sep 17 00:00:00 2001 From: PsiOmegaDelta Date: Thu, 15 Oct 2015 10:32:56 +0200 Subject: [PATCH 11/15] Gives replacement light boxes the same fitness treatment. --- code/game/objects/items/weapons/storage/boxes.dm | 13 +++++++------ code/game/objects/items/weapons/storage/storage.dm | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm index d28107dd8f..aa3fe35f9b 100644 --- a/code/game/objects/items/weapons/storage/boxes.dm +++ b/code/game/objects/items/weapons/storage/boxes.dm @@ -598,32 +598,33 @@ icon_state = "light" desc = "This box is shaped on the inside so that only light tubes and bulbs fit." item_state = "syringe_kit" - storage_slots=21 - can_hold = list(/obj/item/weapon/light/tube, /obj/item/weapon/light/bulb) - max_storage_space = 42 //holds 21 items of w_class 2 use_to_pickup = 1 // for picking up broken bulbs, not that most people will try + +/obj/item/weapon/storage/box/lights/New() + ..() + make_exact_fit() /obj/item/weapon/storage/box/lights/bulbs/New() - ..() for(var/i = 0; i < 21; i++) new /obj/item/weapon/light/bulb(src) + ..() /obj/item/weapon/storage/box/lights/tubes name = "box of replacement tubes" icon_state = "lighttube" /obj/item/weapon/storage/box/lights/tubes/New() - ..() for(var/i = 0; i < 21; i++) new /obj/item/weapon/light/tube(src) + ..() /obj/item/weapon/storage/box/lights/mixed name = "box of replacement lights" icon_state = "lightmixed" /obj/item/weapon/storage/box/lights/mixed/New() - ..() for(var/i = 0; i < 14; i++) new /obj/item/weapon/light/tube(src) for(var/i = 0; i < 7; i++) new /obj/item/weapon/light/bulb(src) + ..() diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index 6f2ba544ab..9f5fe9ea7c 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -235,12 +235,12 @@ if(can_hold.len) if(!is_type_in_list(W, can_hold)) if(!stop_messages && ! istype(W, /obj/item/weapon/hand_labeler)) - usr << "[src] cannot hold [W]." + usr << "[src] cannot hold \the [W]." return 0 var/max_instances = can_hold[W.type] if(max_instances && instances_of_type_in_list(W, contents) >= max_instances) if(!stop_messages && !istype(W, /obj/item/weapon/hand_labeler)) - usr << "[src] has no more space for an additional [W]." + usr << "[src] has no more space specifically for \the [W]." return 0 if(cant_hold.len && is_type_in_list(W, cant_hold)) @@ -460,9 +460,9 @@ max_w_class = 0 max_storage_space = 0 for(var/obj/item/I in src) + can_hold[I.type]++ max_w_class = max(I.w_class, max_w_class) max_storage_space += I.get_storage_cost() - can_hold[I.type] = ++can_hold[I.type] //Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area). //Returns -1 if the atom was not found on container. From 2fcaaf80c7155f0dc0ca8b0716a98f0861dfbe33 Mon Sep 17 00:00:00 2001 From: PsiOmegaDelta Date: Thu, 15 Oct 2015 11:31:57 +0200 Subject: [PATCH 12/15] Chemistry machinery fixes. Makes Topic() calls check parent. Adds proper operable() checks where applicable. Makes the interact_offline only affect the NOPOWER flag. Fixes #11116. Fixes #11302. --- code/game/machinery/machinery.dm | 5 +++- code/modules/reagents/Chemistry-Machinery.dm | 28 ++++++++----------- code/modules/reagents/dispenser/dispenser2.dm | 11 ++------ 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 62909fc3da..97d92903c0 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -195,7 +195,10 @@ Class Procs: return (stat & (NOPOWER|BROKEN|additional_flags)) /obj/machinery/CanUseTopic(var/mob/user) - if(!interact_offline && (stat & (NOPOWER|BROKEN))) + if(stat & BROKEN) + return STATUS_CLOSE + + if(!interact_offline && (stat & NOPOWER)) return STATUS_CLOSE return ..() diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index c010acd4bb..726303273a 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -80,13 +80,8 @@ return /obj/machinery/chem_master/Topic(href, href_list) - if(stat & (BROKEN|NOPOWER)) return - if(usr.stat || usr.restrained()) return - if(!in_range(src, usr)) return - - src.add_fingerprint(usr) - usr.set_machine(src) - + if(..()) + return 1 if (href_list["ejectp"]) if(loaded_pill_bottle) @@ -236,7 +231,7 @@ return src.attack_hand(user) /obj/machinery/chem_master/attack_hand(mob/user as mob) - if(stat & BROKEN) + if(inoperable()) return user.set_machine(src) if(!(user.client in has_sprites)) @@ -345,11 +340,8 @@ /obj/machinery/computer/pandemic/Topic(href, href_list) - if(stat & (NOPOWER|BROKEN)) return - if(usr.stat || usr.restrained()) return - if(!in_range(src, usr)) return - - usr.set_machine(src) + if(..()) + return 1 if(!beaker) return if (href_list["create_vaccine"]) @@ -657,10 +649,12 @@ return 0 /obj/machinery/reagentgrinder/attack_hand(mob/user as mob) - user.set_machine(src) interact(user) /obj/machinery/reagentgrinder/interact(mob/user as mob) // The microwave Menu + if(inoperable()) + return + user.set_machine(src) var/is_chamber_empty = 0 var/is_beaker_ready = 0 var/processing_chamber = "" @@ -707,8 +701,8 @@ /obj/machinery/reagentgrinder/Topic(href, href_list) if(..()) - return - usr.set_machine(src) + return 1 + switch(href_list["action"]) if ("grind") grind() @@ -717,7 +711,7 @@ if ("detach") detach() src.updateUsrDialog() - return + return 1 /obj/machinery/reagentgrinder/proc/detach() diff --git a/code/modules/reagents/dispenser/dispenser2.dm b/code/modules/reagents/dispenser/dispenser2.dm index 8decba10d4..df63f5c8a7 100644 --- a/code/modules/reagents/dispenser/dispenser2.dm +++ b/code/modules/reagents/dispenser/dispenser2.dm @@ -113,9 +113,6 @@ return ..() /obj/machinery/chemical_dispenser/ui_interact(mob/user, ui_key = "main",var/datum/nanoui/ui = null, var/force_open = 1) - if(stat & (BROKEN|NOPOWER)) return - if(user.stat || user.restrained()) return - // this is the data which will be sent to the ui var/data[0] data["amount"] = amount @@ -148,8 +145,8 @@ ui.open() /obj/machinery/chemical_dispenser/Topic(href, href_list) - if(stat & (NOPOWER|BROKEN)) - return 0 // don't update UIs attached to this object + if(..()) + return 1 if(href_list["amount"]) amount = round(text2num(href_list["amount"]), 1) // round to nearest 1 @@ -171,9 +168,7 @@ return 1 // update UIs attached to this object /obj/machinery/chemical_dispenser/attack_ai(mob/user as mob) - src.attack_hand(user) + ui_interact(user) /obj/machinery/chemical_dispenser/attack_hand(mob/user as mob) - if(stat & BROKEN) - return ui_interact(user) From 7272deb5ae3c43b1eb3fcb1f05340985727df7d0 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Thu, 15 Oct 2015 06:57:23 -0400 Subject: [PATCH 13/15] Fixes #11216 Sunglass-derived custom gear is now only available to roles that normally have access to them. --- code/modules/client/preferences_gear.dm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/modules/client/preferences_gear.dm b/code/modules/client/preferences_gear.dm index 5be7e2dcde..9cc57be536 100644 --- a/code/modules/client/preferences_gear.dm +++ b/code/modules/client/preferences_gear.dm @@ -297,17 +297,18 @@ var/global/list/gear_datums = list() allowed_roles = list("Security Officer","Head of Security","Warden") /datum/gear/thugshades - display_name = "Sunglasses, Fat (Security)" + display_name = "Sunglasses, Fat" path = /obj/item/clothing/glasses/sunglasses/big cost = 1 slot = slot_glasses - allowed_roles = list("Security Officer","Head of Security","Warden") + allowed_roles = list("Security Officer","Head of Security","Warden","Detective","Internal Affairs Agent","Quartermaster","Head of Personnel","Captain") /datum/gear/prescriptionsun display_name = "sunglasses, presciption" path = /obj/item/clothing/glasses/sunglasses/prescription cost = 2 slot = slot_glasses + allowed_roles = list("Security Officer","Head of Security","Warden","Detective","Internal Affairs Agent","Quartermaster","Head of Personnel","Captain") // Mask From 111973ed5c622c4c1c7e24c9cf97bf43f60caa3f Mon Sep 17 00:00:00 2001 From: ccomp5950 Date: Thu, 15 Oct 2015 19:05:31 -0400 Subject: [PATCH 14/15] AI's can now use languages through holopads... ...and have it respect if the listener understands. Resolves #11113 (AI's speech not timestamped through holopad) Resolves #11183 (All languages can be understood when AI speaks them through a holopad.) Resolves #6217 (pAIs cannot hear AI hologram speech) --- code/modules/mob/living/silicon/say.dm | 57 ++++++++++++++++---------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index ece00da5ee..16a00368a8 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -68,31 +68,46 @@ if (!message) return - var/obj/machinery/hologram/holopad/T = src.holo - if(T && T.masters[src])//If there is a hologram and its master is the user. - - //Human-like, sorta, heard by those who understand humans. - var/rendered_a - //Speach distorted, heard by those who do not understand AIs. - var/message_stars = stars(message) - var/rendered_b + var/obj/machinery/hologram/holopad/H = src.holo + if(H && H.masters[src])//If there is a hologram and its master is the user. + // AI can hear their own message, this formats it for them. if(speaking) - rendered_a = "[name] [speaking.format_message(message, verb)]" - rendered_b = "[voice_name] [speaking.format_message(message_stars, verb)]" - src << "Holopad transmitted, [real_name] [speaking.format_message(message, verb)]"//The AI can "hear" its own message. + src << "Holopad transmitted, [real_name] [speaking.format_message(message, verb)]" else - rendered_a = "[name] [verb], \"[message]\"" - rendered_b = "[voice_name] [verb], \"[message_stars]\"" - src << "Holopad transmitted, [real_name] [verb], \"[message]\""//The AI can "hear" its own message. + src << "Holopad transmitted, [real_name] [verb], \"[message]\"" + + //This is so pAI's and people inside lockers/boxes,etc can hear the AI Holopad, the alternative being recursion through contents. + //This is much faster. + var/list/listening = list() + var/list/listening_obj = list() + var/turf/T = get_turf(H) + + if(T) + var/list/hear = hear(7, T) + var/list/hearturfs = list() + + for(var/I in hear) + if(istype(I, /mob/)) + var/mob/M = I + listening += M + hearturfs += M.locs[1] + for(var/obj/O in M.contents) + listening_obj |= O + else if(istype(I, /obj/)) + var/obj/O = I + hearturfs += O.locs[1] + listening_obj |= O + + + for(var/mob/M in player_list) + if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS)) + M.hear_say(message,verb,speaking,null,null, src) + continue + if(M.loc && M.locs[1] in hearturfs) + M.hear_say(message,verb,speaking,null,null, src) + - for(var/mob/M in hearers(T.loc))//The location is the object, default distance. - if(M.say_understands(src))//If they understand AI speak. Humans and the like will be able to. - M.show_message(rendered_a, 2) - else//If they do not. - M.show_message(rendered_b, 2) - /*Radios "filter out" this conversation channel so we don't need to account for them. - This is another way of saying that we won't bother dealing with them.*/ else src << "No holopad connected." return 0 From bb93245ccda71ef77001f7b5ca9a7530b9c7213d Mon Sep 17 00:00:00 2001 From: ccomp5950 Date: Thu, 15 Oct 2015 21:08:14 -0400 Subject: [PATCH 15/15] Fixes the tapping messages check. The check is now in Topic() instead of being based on the href link sent. Resolves #11039 --- code/game/objects/items/devices/PDA/PDA.dm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 25f2b407ce..35ab855f05 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -721,7 +721,8 @@ var/global/list/obj/item/device/pda/PDAs = list() if("Message") var/obj/item/device/pda/P = locate(href_list["target"]) - src.create_message(U, P, !href_list["notap"]) + var/tap = istype(U, /mob/living/carbon) + src.create_message(U, P, tap) if(mode == 2) if(href_list["target"] in conversations) // Need to make sure the message went through, if not welp. active_conversation = href_list["target"] @@ -1040,7 +1041,7 @@ var/global/list/obj/item/device/pda/PDAs = list() new_message(sending_device, sending_device.owner, sending_device.ownjob, message) /obj/item/device/pda/proc/new_message(var/sending_unit, var/sender, var/sender_job, var/message) - var/reception_message = "\icon[src] Message from [sender] ([sender_job]), \"[message]\" (Reply)" + var/reception_message = "\icon[src] Message from [sender] ([sender_job]), \"[message]\" (Reply)" new_info(message_silent, ttone, reception_message) log_pda("[usr] (PDA: [sending_unit]) sent \"[message]\" to [name]") @@ -1052,7 +1053,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if(ismob(sending_unit.loc) && isAI(loc)) track = "(Follow)" - var/reception_message = "\icon[src] Message from [sender] ([sender_job]), \"[message]\" (Reply) [track]" + var/reception_message = "\icon[src] Message from [sender] ([sender_job]), \"[message]\" (Reply) [track]" new_info(message_silent, newstone, reception_message) log_pda("[usr] (PDA: [sending_unit]) sent \"[message]\" to [name]")