toast. And DiscoFox™️

This commit is contained in:
Letter N
2021-02-08 18:46:05 +08:00
parent 50d66bb93c
commit f87aa0ac5b
118 changed files with 3889 additions and 1382 deletions

View File

@@ -0,0 +1,99 @@
// Keep the identifiers here below 32 characters, you can put the full display name in the actual achievement datum
#define ACHIEVEMENT_DEFAULT "default"
#define ACHIEVEMENT_SCORE "score"
//Misc Medal hub IDs
#define MEDAL_METEOR "Your Life Before Your Eyes"
#define MEDAL_PULSE "Jackpot"
#define MEDAL_TIMEWASTE "Overextended The Joke"
#define MEDAL_RODSUPLEX "Feat of Strength"
#define MEDAL_CLOWNCARKING "Round and Full"
#define MEDAL_THANKSALOT "The Best Driver"
#define MEDAL_HELBITALJANKEN "Hel-bent on Winning"
#define MEDAL_MATERIALCRAFT "Getting an Upgrade"
#define MEDAL_DISKPLEASE "Disk, Please!"
#define MEDAL_GAMER "I'm Not Important"
#define MEDAL_VENDORSQUISH "Teenage Anarchist"
#define MEDAL_SWIRLIE "Bowl-d"
#define MEDAL_SELFOUCH "Hands???"
#define MEDAL_SANDMAN "Mister Sandman"
#define MEDAL_CLEANBOSS "Cleanboss"
#define MEDAL_RULE8 "Rule 8"
#define MEDAL_LONGSHIFT "longshift"
#define MEDAL_SNAIL "KKKiiilll mmmeee"
#define MEDAL_LOOKOUTSIR "Look Out, Sir!"
#define MEDAL_GOTTEM "GOTTEM"
#define MEDAL_ASCENSION "Ascension"
#define MEDAL_FRENCHING "FrenchingTheBubble"
#define MEDAL_ASH_ASCENSION "Ash"
#define MEDAL_FLESH_ASCENSION "Flesh"
#define MEDAL_RUST_ASCENSION "Rust"
#define MEDAL_VOID_ASCENSION "Void"
#define MEDAL_TOOLBOX_SOUL "Toolsoul"
//Skill medal hub IDs
#define MEDAL_LEGENDARY_MINER "Legendary Miner"
//Mafia medal hub IDs (wins)
#define MAFIA_MEDAL_ASSISTANT "Assistant"
#define MAFIA_MEDAL_DETECTIVE "Detective"
#define MAFIA_MEDAL_PSYCHOLOGIST "Psychologist"
#define MAFIA_MEDAL_CHAPLAIN "Chaplain"
#define MAFIA_MEDAL_MD "Medical Doctor"
#define MAFIA_MEDAL_LAWYER "Lawyer"
#define MAFIA_MEDAL_HOP "Head of Personnel"
#define MAFIA_MEDAL_CHANGELING "CHANGELING"
#define MAFIA_MEDAL_THOUGHTFEEDER "Thoughtfeeder"
#define MAFIA_MEDAL_TRAITOR "Traitor"
#define MAFIA_MEDAL_NIGHTMARE "Nightmare"
#define MAFIA_MEDAL_FUGITIVE "Fugitive"
#define MAFIA_MEDAL_OBSESSED "Obsessed"
#define MAFIA_MEDAL_CLOWN "Clown"
//Mafia medal hub IDs (misc stuff)
#define MAFIA_MEDAL_HATED "Universally Hated"
//Boss medals
// Medal hub IDs for boss medals (Pre-fixes)
#define BOSS_MEDAL_ANY "Boss Killer"
#define BOSS_MEDAL_MINER "Blood-drunk Miner Killer"
#define BOSS_MEDAL_FROSTMINER "Demonic-frost Miner Killer"
#define BOSS_MEDAL_BUBBLEGUM "Bubblegum Killer"
#define BOSS_MEDAL_COLOSSUS "Colossus Killer"
#define BOSS_MEDAL_DRAKE "Drake Killer"
#define BOSS_MEDAL_HIEROPHANT "Hierophant Killer"
#define BOSS_MEDAL_LEGION "Legion Killer"
#define BOSS_MEDAL_TENDRIL "Tendril Exterminator"
#define BOSS_MEDAL_SWARMERS "Swarmer Beacon Killer"
#define BOSS_MEDAL_WENDIGO "Wendigo Killer"
#define BOSS_MEDAL_KINGGOAT "King Goat Killer"
#define BOSS_MEDAL_MINER_CRUSHER "Blood-drunk Miner Crusher"
#define BOSS_MEDAL_FROSTMINER_CRUSHER "Demonic-frost Miner Crusher"
#define BOSS_MEDAL_BUBBLEGUM_CRUSHER "Bubblegum Crusher"
#define BOSS_MEDAL_COLOSSUS_CRUSHER "Colossus Crusher"
#define BOSS_MEDAL_DRAKE_CRUSHER "Drake Crusher"
#define BOSS_MEDAL_HIEROPHANT_CRUSHER "Hierophant Crusher"
#define BOSS_MEDAL_LEGION_CRUSHER "Legion Crusher"
#define BOSS_MEDAL_SWARMERS_CRUSHER "Swarmer Beacon Crusher"
#define BOSS_MEDAL_WENDIGO_CRUSHER "Wendigo Crusher"
#define BOSS_MEDAL_KINGGOAT_CRUSHER "King Goat Crusher"
// Medal hub IDs for boss-kill scores
#define BOSS_SCORE "Bosses Killed"
#define MINER_SCORE "BDMs Killed"
#define FROST_MINER_SCORE "DFMs Killed"
#define BUBBLEGUM_SCORE "Bubblegum Killed"
#define COLOSSUS_SCORE "Colossus Killed"
#define DRAKE_SCORE "Drakes Killed"
#define HIEROPHANT_SCORE "Hierophants Killed"
#define LEGION_SCORE "Legion Killed"
#define SWARMER_BEACON_SCORE "Swarmer Beacs Killed"
#define WENDIGO_SCORE "Wendigos Killed"
#define KINGGOAT_SCORE "King Goat Killed"
#define TENDRIL_CLEAR_SCORE "Tendrils Killed"
// DB IDs for hardcore random mode
#define HARDCORE_RANDOM_SCORE "Hardcore Random Score"

View File

@@ -52,3 +52,9 @@
#define COLOR_ASSEMBLY_BLUE "#38559E"
#define COLOR_ASSEMBLY_PURPLE "#6F6192"
#define COLOR_ASSEMBLY_PINK "#ff4adc"
#define COLOR_WHITE "#FFFFFF"
#define COLOR_VERY_LIGHT_GRAY "#EEEEEE"
#define COLOR_SILVER "#C0C0C0"
#define COLOR_GRAY "#808080"
#define COLOR_HALF_TRANSPARENT_BLACK "#0000007A"

View File

@@ -1,29 +0,0 @@
// Medal names
#define BOSS_KILL_MEDAL "Killer"
#define ALL_KILL_MEDAL "Exterminator" //Killing all of x type
#define BOSS_KILL_MEDAL_CRUSHER "Crusher"
//Defines for boss medals
#define BOSS_MEDAL_MINER "Blood-drunk Miner"
#define BOSS_MEDAL_BUBBLEGUM "Bubblegum"
#define BOSS_MEDAL_COLOSSUS "Colossus"
#define BOSS_MEDAL_DRAKE "Drake"
#define BOSS_MEDAL_HIEROPHANT "Hierophant"
#define BOSS_MEDAL_LEGION "Legion"
#define BOSS_MEDAL_TENDRIL "Tendril"
#define BOSS_MEDAL_SWARMERS "Swarmer Beacon"
// Score names
#define HIEROPHANT_SCORE "Hierophants Killed"
#define BOSS_SCORE "Bosses Killed"
#define BUBBLEGUM_SCORE "Bubblegum Killed"
#define COLOSSUS_SCORE "Colossus Killed"
#define DRAKE_SCORE "Drakes Killed"
#define LEGION_SCORE "Legion Killed"
#define SWARMER_BEACON_SCORE "Swarmer Beacons Killed"
#define TENDRIL_CLEAR_SCORE "Tendrils Killed"
//Misc medals
#define MEDAL_METEOR "Your Life Before Your Eyes"
#define MEDAL_PULSE "Jackpot"
#define MEDAL_TIMEWASTE "Overextended The Joke"

View File

@@ -435,8 +435,13 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
//text files
#define BRAIN_DAMAGE_FILE "traumas.json"
#define ION_FILE "ion_laws.json"
#define REDPILL_FILE "redpill.json"
#define PIRATE_NAMES_FILE "pirates.json"
#define REDPILL_FILE "redpill.json"
#define ARCADE_FILE "arcade.json"
// #define BOOMER_FILE "boomer.json"
// #define LOCATIONS_FILE "locations.json"
// #define WANTED_FILE "wanted_message.json"
// #define VISTA_FILE "steve.json"
#define FLESH_SCAR_FILE "wounds/flesh_scar_desc.json"
#define BONE_SCAR_FILE "wounds/bone_scar_desc.json"
#define SCAR_LOC_FILE "wounds/scar_loc.json"

View File

@@ -108,7 +108,7 @@
#define INIT_ORDER_SOUNDS 83
#define INIT_ORDER_INSTRUMENTS 82
#define INIT_ORDER_VIS 80
// #define INIT_ORDER_ACHIEVEMENTS 77
#define INIT_ORDER_ACHIEVEMENTS 77
#define INIT_ORDER_RESEARCH 75
#define INIT_ORDER_EVENTS 70
#define INIT_ORDER_JOBS 65

View File

@@ -90,6 +90,9 @@
#define VV_HK_TRIGGER_EMP "empulse"
#define VV_HK_TRIGGER_EXPLOSION "explode"
#define VV_HK_AUTO_RENAME "auto_rename"
// #define VV_HK_RADIATE "radiate"
#define VV_HK_EDIT_FILTERS "edit_filters"
// #define VV_HK_ADD_AI "add_ai"
// /obj
#define VV_HK_OSAY "osay"

319
code/__HELPERS/filters.dm Normal file
View File

@@ -0,0 +1,319 @@
#define ICON_NOT_SET "Not Set"
//This is stored as a nested list instead of datums or whatever because it json encodes nicely for usage in tgui
GLOBAL_LIST_INIT(master_filter_info, list(
"alpha" = list(
"defaults" = list(
"x" = 0,
"y" = 0,
"icon" = ICON_NOT_SET,
"render_source" = "",
"flags" = 0
),
"flags" = list(
"MASK_INVERSE" = MASK_INVERSE,
"MASK_SWAP" = MASK_SWAP
)
),
"angular_blur" = list(
"defaults" = list(
"x" = 0,
"y" = 0,
"size" = 1
)
),
/* Not supported because making a proper matrix editor on the frontend would be a huge dick pain.
Uncomment if you ever implement it
"color" = list(
"defaults" = list(
"color" = matrix(),
"space" = FILTER_COLOR_RGB
)
),
*/
"displace" = list(
"defaults" = list(
"x" = 0,
"y" = 0,
"size" = null,
"icon" = ICON_NOT_SET,
"render_source" = ""
)
),
"drop_shadow" = list(
"defaults" = list(
"x" = 1,
"y" = -1,
"size" = 1,
"offset" = 0,
"color" = COLOR_HALF_TRANSPARENT_BLACK
)
),
"blur" = list(
"defaults" = list(
"size" = 1
)
),
"layer" = list(
"defaults" = list(
"x" = 0,
"y" = 0,
"icon" = ICON_NOT_SET,
"render_source" = "",
"flags" = FILTER_OVERLAY,
"color" = "",
"transform" = null,
"blend_mode" = BLEND_DEFAULT
)
),
"motion_blur" = list(
"defaults" = list(
"x" = 0,
"y" = 0
)
),
"outline" = list(
"defaults" = list(
"size" = 0,
"color" = COLOR_BLACK,
"flags" = NONE
),
"flags" = list(
"OUTLINE_SHARP" = OUTLINE_SHARP,
"OUTLINE_SQUARE" = OUTLINE_SQUARE
)
),
"radial_blur" = list(
"defaults" = list(
"x" = 0,
"y" = 0,
"size" = 0.01
)
),
"rays" = list(
"defaults" = list(
"x" = 0,
"y" = 0,
"size" = 16,
"color" = COLOR_WHITE,
"offset" = 0,
"density" = 10,
"threshold" = 0.5,
"factor" = 0,
"flags" = FILTER_OVERLAY | FILTER_UNDERLAY
),
"flags" = list(
"FILTER_OVERLAY" = FILTER_OVERLAY,
"FILTER_UNDERLAY" = FILTER_UNDERLAY
)
),
"ripple" = list(
"defaults" = list(
"x" = 0,
"y" = 0,
"size" = 1,
"repeat" = 2,
"radius" = 0,
"falloff" = 1,
"flags" = NONE
),
"flags" = list(
"WAVE_BOUNDED" = WAVE_BOUNDED
)
),
"wave" = list(
"defaults" = list(
"x" = 0,
"y" = 0,
"size" = 1,
"offset" = 0,
"flags" = NONE
),
"flags" = list(
"WAVE_SIDEWAYS" = WAVE_SIDEWAYS,
"WAVE_BOUNDED" = WAVE_BOUNDED
)
)
))
#undef ICON_NOT_SET
//Helpers to generate lists for filter helpers
//This is the only practical way of writing these that actually produces sane lists
/proc/alpha_mask_filter(x, y, icon/icon, render_source, flags)
. = list("type" = "alpha")
if(!isnull(x))
.["x"] = x
if(!isnull(y))
.["y"] = y
if(!isnull(icon))
.["icon"] = icon
if(!isnull(render_source))
.["render_source"] = render_source
if(!isnull(flags))
.["flags"] = flags
/proc/angular_blur_filter(x, y, size)
. = list("type" = "angular_blur")
if(!isnull(x))
.["x"] = x
if(!isnull(y))
.["y"] = y
if(!isnull(size))
.["size"] = size
/proc/color_matrix_filter(matrix/in_matrix, space)
. = list("type" = "color")
.["color"] = in_matrix
if(!isnull(space))
.["space"] = space
/proc/displacement_map_filter(icon, render_source, x, y, size = 32)
. = list("type" = "displace")
if(!isnull(icon))
.["icon"] = icon
if(!isnull(render_source))
.["render_source"] = render_source
if(!isnull(x))
.["x"] = x
if(!isnull(y))
.["y"] = y
if(!isnull(size))
.["size"] = size
/proc/drop_shadow_filter(x, y, size, offset, color)
. = list("type" = "drop_shadow")
if(!isnull(x))
.["x"] = x
if(!isnull(y))
.["y"] = y
if(!isnull(size))
.["size"] = size
if(!isnull(offset))
.["offset"] = offset
if(!isnull(color))
.["color"] = color
/proc/gauss_blur_filter(size)
. = list("type" = "blur")
if(!isnull(size))
.["size"] = size
/proc/layering_filter(icon, render_source, x, y, flags, color, transform, blend_mode)
. = list("type" = "layer")
if(!isnull(icon))
.["icon"] = icon
if(!isnull(render_source))
.["render_source"] = render_source
if(!isnull(x))
.["x"] = x
if(!isnull(y))
.["y"] = y
if(!isnull(color))
.["color"] = color
if(!isnull(flags))
.["flags"] = flags
if(!isnull(transform))
.["transform"] = transform
if(!isnull(blend_mode))
.["blend_mode"] = blend_mode
/proc/motion_blur_filter(x, y)
. = list("type" = "motion_blur")
if(!isnull(x))
.["x"] = x
if(!isnull(y))
.["y"] = y
/proc/outline_filter(size, color, flags)
. = list("type" = "outline")
if(!isnull(size))
.["size"] = size
if(!isnull(color))
.["color"] = color
if(!isnull(flags))
.["flags"] = flags
/proc/radial_blur_filter(size, x, y)
. = list("type" = "radial_blur")
if(!isnull(size))
.["size"] = size
if(!isnull(x))
.["x"] = x
if(!isnull(y))
.["y"] = y
/proc/rays_filter(size, color, offset, density, threshold, factor, x, y, flags)
. = list("type" = "rays")
if(!isnull(size))
.["size"] = size
if(!isnull(color))
.["color"] = color
if(!isnull(offset))
.["offset"] = offset
if(!isnull(density))
.["density"] = density
if(!isnull(threshold))
.["threshold"] = threshold
if(!isnull(factor))
.["factor"] = factor
if(!isnull(x))
.["x"] = x
if(!isnull(y))
.["y"] = y
if(!isnull(flags))
.["flags"] = flags
/proc/ripple_filter(radius, size, falloff, repeat, x, y, flags)
. = list("type" = "ripple")
if(!isnull(radius))
.["radius"] = radius
if(!isnull(size))
.["size"] = size
if(!isnull(falloff))
.["falloff"] = falloff
if(!isnull(repeat))
.["repeat"] = repeat
if(!isnull(flags))
.["flags"] = flags
if(!isnull(x))
.["x"] = x
if(!isnull(y))
.["y"] = y
/proc/wave_filter(x, y, size, offset, flags)
. = list("type" = "wave")
if(!isnull(size))
.["size"] = size
if(!isnull(x))
.["x"] = x
if(!isnull(y))
.["y"] = y
if(!isnull(offset))
.["offset"] = offset
if(!isnull(flags))
.["flags"] = flags
/proc/apply_wibbly_filters(atom/in_atom, length)
for(var/i in 1 to 7)
//This is a very baffling and strange way of doing this but I am just preserving old functionality
var/X
var/Y
var/rsq
do
X = 60*rand() - 30
Y = 60*rand() - 30
rsq = X*X + Y*Y
while(rsq<100 || rsq>900) // Yeah let's just loop infinitely due to bad luck what's the worst that could happen?
var/random_roll = rand()
in_atom.add_filter("wibbly-[i]", 5, wave_filter(x = X, y = Y, size = rand() * 2.5 + 0.5, offset = random_roll))
var/filter = in_atom.get_filter("wibbly-[i]")
animate(filter, offset = random_roll, time = 0, loop = -1, flags = ANIMATION_PARALLEL)
animate(offset = random_roll - 1, time = rand() * 20 + 10)
/proc/remove_wibbly_filters(atom/in_atom)
var/filter
for(var/i in 1 to 7)
filter = in_atom.get_filter("wibbly-[i]")
animate(filter)
in_atom.remove_filter("wibbly-[i]")

View File

@@ -169,6 +169,28 @@
file_data["wanted"] = list("author" = "[GLOB.news_network.wanted_issue.scannedUser]", "criminal" = "[GLOB.news_network.wanted_issue.criminal]", "description" = "[GLOB.news_network.wanted_issue.body]", "photo file" = "[GLOB.news_network.wanted_issue.photo_file]")
WRITE_FILE(json_file, json_encode(file_data))
///Handles random hardcore point rewarding if it applies.
/datum/controller/subsystem/ticker/proc/HandleRandomHardcoreScore(client/player_client)
if(!ishuman(player_client.mob))
return FALSE
var/mob/living/carbon/human/human_mob = player_client.mob
if(!human_mob.hardcore_survival_score) ///no score no glory
return FALSE
if(human_mob.mind && (human_mob.mind.special_role || length(human_mob.mind.antag_datums) > 0))
var/didthegamerwin = TRUE
for(var/a in human_mob.mind.antag_datums)
var/datum/antagonist/antag_datum = a
for(var/i in antag_datum.objectives)
var/datum/objective/objective_datum = i
if(!objective_datum.check_completion())
didthegamerwin = FALSE
if(!didthegamerwin)
return FALSE
player_client.give_award(/datum/award/score/hardcore_random, human_mob, round(human_mob.hardcore_survival_score))
else if(human_mob.onCentCom())
player_client.give_award(/datum/award/score/hardcore_random, human_mob, round(human_mob.hardcore_survival_score))
/datum/controller/subsystem/ticker/proc/declare_completion()
set waitfor = FALSE
@@ -186,6 +208,19 @@
C.RollCredits()
C.playtitlemusic(40)
CONFIG_SET(flag/suicide_allowed,TRUE) // EORG suicides allowed
var/speed_round = FALSE
if(world.time - SSticker.round_start_time <= 300 SECONDS)
speed_round = TRUE
for(var/client/C in GLOB.clients)
if(!C.credits)
C.RollCredits()
C.playtitlemusic(40)
if(speed_round)
C.give_award(/datum/award/achievement/misc/speed_round, C.mob)
HandleRandomHardcoreScore(C)
var/popcount = gather_roundend_feedback()
display_report(popcount)

View File

@@ -97,6 +97,9 @@
M.lastattacker = user.real_name
M.lastattackerckey = user.ckey
if(force && M == user && user.client)
user.client.give_award(/datum/award/achievement/misc/selfouch, user)
user.do_attack_animation(M)
M.attacked_by(src, user, attackchain_flags, damage_multiplier)

View File

@@ -0,0 +1,74 @@
SUBSYSTEM_DEF(achievements)
name = "Achievements"
flags = SS_NO_FIRE
init_order = INIT_ORDER_ACHIEVEMENTS
var/achievements_enabled = FALSE
///List of achievements
var/list/datum/award/achievement/achievements = list()
///List of scores
var/list/datum/award/score/scores = list()
///List of all awards
var/list/datum/award/awards = list()
/datum/controller/subsystem/achievements/Initialize(timeofday)
if(!SSdbcore.Connect())
return ..()
achievements_enabled = TRUE
for(var/T in subtypesof(/datum/award/achievement))
var/instance = new T
achievements[T] = instance
awards[T] = instance
for(var/T in subtypesof(/datum/award/score))
var/instance = new T
scores[T] = instance
awards[T] = instance
update_metadata()
for(var/i in GLOB.clients)
var/client/C = i
if(!C.player_details.achievements.initialized)
C.player_details.achievements.InitializeData()
return ..()
/datum/controller/subsystem/achievements/Shutdown()
save_achievements_to_db()
/datum/controller/subsystem/achievements/proc/save_achievements_to_db()
var/list/cheevos_to_save = list()
for(var/ckey in GLOB.player_details)
var/datum/player_details/PD = GLOB.player_details[ckey]
if(!PD || !PD.achievements)
continue
cheevos_to_save += PD.achievements.get_changed_data()
if(!length(cheevos_to_save))
return
SSdbcore.MassInsert(format_table_name("achievements"),cheevos_to_save,duplicate_key = TRUE)
//Update the metadata if any are behind
/datum/controller/subsystem/achievements/proc/update_metadata()
var/list/current_metadata = list()
//select metadata here
var/datum/DBQuery/Q = SSdbcore.NewQuery("SELECT achievement_key,achievement_version FROM [format_table_name("achievement_metadata")]")
if(!Q.Execute(async = TRUE))
qdel(Q)
return
else
while(Q.NextRow())
current_metadata[Q.item[1]] = text2num(Q.item[2])
qdel(Q)
var/list/to_update = list()
for(var/T in awards)
var/datum/award/A = awards[T]
if(!A.database_id)
continue
if(!current_metadata[A.database_id] || current_metadata[A.database_id] < A.achievement_version)
to_update += list(A.get_metadata_row())
if(to_update.len)
SSdbcore.MassInsert(format_table_name("achievement_metadata"),to_update,duplicate_key = TRUE)

View File

@@ -1,87 +0,0 @@
SUBSYSTEM_DEF(medals)
name = "Medals"
flags = SS_NO_FIRE
var/hub_enabled = FALSE
/datum/controller/subsystem/medals/Initialize(timeofday)
if(CONFIG_GET(string/medal_hub_address) && CONFIG_GET(string/medal_hub_password))
hub_enabled = TRUE
return ..()
/datum/controller/subsystem/medals/proc/UnlockMedal(medal, client/player)
set waitfor = FALSE
if(!medal || !hub_enabled)
return
if(isnull(world.SetMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
hub_enabled = FALSE
log_game("MEDAL ERROR: Could not contact hub to award medal:[medal] player:[player.key]")
message_admins("Error! Failed to contact hub to award [medal] medal to [player.key]!")
return
to_chat(player, "<span class='greenannounce'><B>Achievement unlocked: [medal]!</B></span>")
/datum/controller/subsystem/medals/proc/SetScore(score, client/player, increment, force)
set waitfor = FALSE
if(!score || !hub_enabled)
return
var/list/oldscore = GetScore(score, player, TRUE)
if(increment)
if(!oldscore[score])
oldscore[score] = 1
else
oldscore[score] = (text2num(oldscore[score]) + 1)
else
oldscore[score] = force
var/newscoreparam = list2params(oldscore)
if(isnull(world.SetScores(player.ckey, newscoreparam, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
hub_enabled = FALSE
log_game("SCORE ERROR: Could not contact hub to set score. Score:[score] player:[player.key]")
message_admins("Error! Failed to contact hub to set [score] score for [player.key]!")
/datum/controller/subsystem/medals/proc/GetScore(score, client/player, returnlist)
if(!score || !hub_enabled)
return
var/scoreget = world.GetScores(player.ckey, score, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))
if(isnull(scoreget))
hub_enabled = FALSE
log_game("SCORE ERROR: Could not contact hub to get score. Score:[score] player:[player.key]")
message_admins("Error! Failed to contact hub to get score: [score] for [player.key]!")
return
. = params2list(scoreget)
if(!returnlist)
return .[score]
/datum/controller/subsystem/medals/proc/CheckMedal(medal, client/player)
if(!medal || !hub_enabled)
return
if(isnull(world.GetMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
hub_enabled = FALSE
log_game("MEDAL ERROR: Could not contact hub to get medal:[medal] player: [player.key]")
message_admins("Error! Failed to contact hub to get [medal] medal for [player.key]!")
return
to_chat(player, "[medal] is unlocked")
/datum/controller/subsystem/medals/proc/LockMedal(medal, client/player)
if(!player || !medal || !hub_enabled)
return
var/result = world.ClearMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))
switch(result)
if(null)
hub_enabled = FALSE
log_game("MEDAL ERROR: Could not contact hub to clear medal:[medal] player:[player.key]")
message_admins("Error! Failed to contact hub to clear [medal] medal for [player.key]!")
if(TRUE)
message_admins("Medal: [medal] removed for [player.key]")
if(FALSE)
message_admins("Medal: [medal] was not found for [player.key]. Unable to clear.")
/datum/controller/subsystem/medals/proc/ClearScore(client/player)
if(isnull(world.SetScores(player.ckey, "", CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
log_game("MEDAL ERROR: Could not contact hub to clear scores for [player.key]!")
message_admins("Error! Failed to contact hub to clear scores for [player.key]!")

View File

@@ -65,6 +65,8 @@ SUBSYSTEM_DEF(shuttle)
var/datum/turf_reservation/preview_reservation
var/shuttle_loading
/datum/controller/subsystem/shuttle/Initialize(timeofday)
ordernum = rand(1, 9000)
@@ -664,7 +666,7 @@ SUBSYSTEM_DEF(shuttle)
emergencyNoRecall = TRUE
endvote_passed = TRUE
/datum/controller/subsystem/shuttle/proc/action_load(datum/map_template/shuttle/loading_template, obj/docking_port/stationary/destination_port)
/datum/controller/subsystem/shuttle/proc/action_load(datum/map_template/shuttle/loading_template, obj/docking_port/stationary/destination_port, replace = FALSE)
// Check for an existing preview
if(preview_shuttle && (loading_template != preview_template))
preview_shuttle.jumpToNullSpace()
@@ -673,8 +675,8 @@ SUBSYSTEM_DEF(shuttle)
QDEL_NULL(preview_reservation)
if(!preview_shuttle)
if(load_template(loading_template))
preview_shuttle.linkup(loading_template, destination_port)
load_template(loading_template)
preview_shuttle.linkup(loading_template, destination_port)
preview_template = loading_template
// get the existing shuttle information, if any
@@ -684,7 +686,7 @@ SUBSYSTEM_DEF(shuttle)
if(istype(destination_port))
D = destination_port
else if(existing_shuttle)
else if(existing_shuttle && replace)
timer = existing_shuttle.timer
mode = existing_shuttle.mode
D = existing_shuttle.get_docked()
@@ -703,11 +705,12 @@ SUBSYSTEM_DEF(shuttle)
WARNING("Template shuttle [preview_shuttle] cannot dock at [D] ([result]).")
return
if(existing_shuttle)
if(existing_shuttle && replace)
existing_shuttle.jumpToNullSpace()
var/list/force_memory = preview_shuttle.movement_force
preview_shuttle.movement_force = list("KNOCKDOWN" = 0, "THROW" = 0)
preview_shuttle.mode = SHUTTLE_PREARRIVAL//No idle shuttle moving. Transit dock get removed if shuttle moves too long.
preview_shuttle.initiate_docking(D)
preview_shuttle.movement_force = force_memory
@@ -718,7 +721,7 @@ SUBSYSTEM_DEF(shuttle)
preview_shuttle.timer = timer
preview_shuttle.mode = mode
preview_shuttle.register()
preview_shuttle.register(replace)
// TODO indicate to the user that success happened, rather than just
// blanking the modification tab
@@ -848,7 +851,8 @@ SUBSYSTEM_DEF(shuttle)
return data
/datum/controller/subsystem/shuttle/ui_act(action, params)
if(..())
. = ..()
if(.)
return
var/mob/user = usr
@@ -891,22 +895,10 @@ SUBSYSTEM_DEF(shuttle)
SSblackbox.record_feedback("text", "shuttle_manipulator", 1, "[M.name]")
break
if("preview")
if(S)
. = TRUE
unload_preview()
load_template(S)
if(preview_shuttle)
preview_template = S
user.forceMove(get_turf(preview_shuttle))
if("load")
if(existing_shuttle == backup_shuttle)
// TODO make the load button disabled
WARNING("The shuttle that the selected shuttle will replace \
is the backup shuttle. Backup shuttle is required to be \
intact for round sanity.")
else if(S)
if(S && !shuttle_loading)
. = TRUE
shuttle_loading = TRUE
// If successful, returns the mobile docking port
var/obj/docking_port/mobile/mdp = action_load(S)
if(mdp)
@@ -914,3 +906,38 @@ SUBSYSTEM_DEF(shuttle)
message_admins("[key_name_admin(usr)] loaded [mdp] with the shuttle manipulator.")
log_admin("[key_name(usr)] loaded [mdp] with the shuttle manipulator.</span>")
SSblackbox.record_feedback("text", "shuttle_manipulator", 1, "[mdp.name]")
shuttle_loading = FALSE
if("preview")
//if(preview_shuttle && (loading_template != preview_template))
if(S && !shuttle_loading)
. = TRUE
shuttle_loading = TRUE
unload_preview()
load_template(S)
if(preview_shuttle)
preview_template = S
user.forceMove(get_turf(preview_shuttle))
shuttle_loading = FALSE
if("replace")
if(existing_shuttle == backup_shuttle)
// TODO make the load button disabled
WARNING("The shuttle that the selected shuttle will replace \
is the backup shuttle. Backup shuttle is required to be \
intact for round sanity.")
else if(S && !shuttle_loading)
. = TRUE
shuttle_loading = TRUE
// If successful, returns the mobile docking port
var/obj/docking_port/mobile/mdp = action_load(S, replace = TRUE)
if(mdp)
user.forceMove(get_turf(mdp))
message_admins("[key_name_admin(usr)] load/replaced [mdp] with the shuttle manipulator.")
log_admin("[key_name(usr)] load/replaced [mdp] with the shuttle manipulator.</span>")
SSblackbox.record_feedback("text", "shuttle_manipulator", 1, "[mdp.name]")
shuttle_loading = FALSE
if(emergency == mdp) //you just changed the emergency shuttle, there are events in game + captains that can change your snowflake choice.
var/set_purchase = alert(usr, "Do you want to also disable shuttle purchases/random events that would change the shuttle?", "Butthurt Admin Prevention", "Yes, disable purchases/events", "No, I want to possibly get owned")
if(set_purchase == "Yes, disable purchases/events")
SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_FORCED

View File

@@ -0,0 +1,148 @@
///Datum that handles
/datum/achievement_data
///Ckey of this achievement data's owner
var/owner_ckey
///Up to date list of all achievements and their info.
var/data = list()
///Original status of achievement.
var/original_cached_data = list()
///Have we done our set-up yet?
var/initialized = FALSE
/datum/achievement_data/New(ckey)
owner_ckey = ckey
if(SSachievements.initialized && !initialized)
InitializeData()
/datum/achievement_data/proc/InitializeData()
initialized = TRUE
load_all_achievements() //So we know which achievements we have unlocked so far.
///Gets list of changed rows in MassInsert format
/datum/achievement_data/proc/get_changed_data()
. = list()
for(var/T in data)
var/datum/award/A = SSachievements.awards[T]
if(data[T] != original_cached_data[T])//If our data from before is not the same as now, save it to db.
var/deets = A.get_changed_rows(owner_ckey,data[T])
if(deets)
. += list(deets)
/datum/achievement_data/proc/load_all_achievements()
set waitfor = FALSE
var/list/kv = list()
var/datum/DBQuery/Query = SSdbcore.NewQuery(
"SELECT achievement_key,value FROM [format_table_name("achievements")] WHERE ckey = [owner_ckey]",
)
if(!Query.Execute())
qdel(Query)
return
while(Query.NextRow())
var/key = Query.item[1]
var/value = text2num(Query.item[2])
kv[key] = value
qdel(Query)
for(var/T in subtypesof(/datum/award))
var/datum/award/A = SSachievements.awards[T]
if(!A || !A.name) //Skip abstract achievements types
continue
if(!data[T])
data[T] = A.parse_value(kv[A.database_id])
original_cached_data[T] = data[T]
///Updates local cache with db data for the given achievement type if it wasn't loaded yet.
/datum/achievement_data/proc/get_data(achievement_type)
var/datum/award/A = SSachievements.awards[achievement_type]
if(!A.name)
return FALSE
if(!data[achievement_type])
data[achievement_type] = A.load(owner_ckey)
original_cached_data[achievement_type] = data[achievement_type]
///Unlocks an achievement of a specific type. achievement type is a typepath to the award, user is the mob getting the award, and value is an optional value to be used for defining a score to add to the leaderboard
/datum/achievement_data/proc/unlock(achievement_type, mob/user, value = 1)
set waitfor = FALSE
if(!SSachievements.achievements_enabled)
return
var/datum/award/A = SSachievements.awards[achievement_type]
get_data(achievement_type) //Get the current status first if necessary
if(istype(A, /datum/award/achievement))
if(data[achievement_type]) //You already unlocked it so don't bother running the unlock proc
return
data[achievement_type] = TRUE
A.on_unlock(user) //Only on default achievement, as scores keep going up.
else if(istype(A, /datum/award/score))
data[achievement_type] += value
///Getter for the status/score of an achievement
/datum/achievement_data/proc/get_achievement_status(achievement_type)
return data[achievement_type]
///Resets an achievement to default values.
/datum/achievement_data/proc/reset(achievement_type)
if(!SSachievements.achievements_enabled)
return
var/datum/award/A = SSachievements.awards[achievement_type]
get_data(achievement_type)
if(istype(A, /datum/award/achievement))
data[achievement_type] = FALSE
else if(istype(A, /datum/award/score))
data[achievement_type] = 0
/datum/achievement_data/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/simple/achievements),
)
/datum/achievement_data/ui_state(mob/user)
return GLOB.always_state
/datum/achievement_data/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Achievements")
ui.open()
/datum/achievement_data/ui_data(mob/user)
var/ret_data = list() // screw standards (qustinnus you must rename src.data ok)
ret_data["categories"] = list("Bosses", "Misc", "Mafia", "Scores")
ret_data["achievements"] = list()
ret_data["user_key"] = user.ckey
var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/simple/achievements)
//This should be split into static data later
for(var/achievement_type in SSachievements.awards)
if(!SSachievements.awards[achievement_type].name) //No name? we a subtype.
continue
if(isnull(data[achievement_type])) //We're still loading
continue
var/list/this = list(
"name" = SSachievements.awards[achievement_type].name,
"desc" = SSachievements.awards[achievement_type].desc,
"category" = SSachievements.awards[achievement_type].category,
"icon_class" = assets.icon_class_name(SSachievements.awards[achievement_type].icon),
"value" = data[achievement_type],
"score" = ispath(achievement_type,/datum/award/score)
)
ret_data["achievements"] += list(this)
return ret_data
/datum/achievement_data/ui_static_data(mob/user)
. = ..()
.["highscore"] = list()
for(var/score in SSachievements.scores)
var/datum/award/score/S = SSachievements.scores[score]
if(!S.name || !S.track_high_scores || !S.high_scores.len)
continue
.["highscore"] += list(list("name" = S.name,"scores" = S.high_scores))
/client/verb/checkachievements()
set category = "OOC"
set name = "Check achievements"
set desc = "See all of your achievements!"
player_details.achievements.ui_interact(usr)

View File

@@ -0,0 +1,115 @@
/datum/award
///Name of the achievement, If null it won't show up in the achievement browser. (Handy for inheritance trees)
var/name
var/desc = "You did it."
///Found in UI_Icons/Achievements
var/icon = "default"
var/category = "Normal"
///What ID do we use in db, limited to 32 characters
var/database_id
//Bump this up if you're changing outdated table identifier and/or achievement type
var/achievement_version = 2
//Value returned on db connection failure, in case we want to differ 0 and nonexistent later on
var/default_value = FALSE
///This proc loads the achievement data from the hub.
/datum/award/proc/load(key)
if(!SSdbcore.Connect())
return default_value
if(!key || !database_id || !name)
return default_value
var/raw_value = get_raw_value(key)
return parse_value(raw_value)
///This saves the changed data to the hub.
/datum/award/proc/get_changed_rows(key, value)
if(!database_id || !key || !name)
return
return list(
"ckey" = key,
"achievement_key" = database_id,
"value" = value,
)
/datum/award/proc/get_metadata_row()
return list(
"achievement_key" = database_id,
"achievement_version" = achievement_version,
"achievement_type" = "award",
"achievement_name" = name,
"achievement_description" = desc,
)
///Get raw numerical achievement value from the database
/datum/award/proc/get_raw_value(key)
var/datum/DBQuery/Q = SSdbcore.NewQuery(
"SELECT value FROM [format_table_name("achievements")] WHERE ckey = [key] AND achievement_key = [database_id]"
)
if(!Q.Execute(async = TRUE))
qdel(Q)
return 0
var/result = 0
if(Q.NextRow())
result = text2num(Q.item[1])
qdel(Q)
return result
//Should return sanitized value for achievement cache
/datum/award/proc/parse_value(raw_value)
return default_value
///Can be overriden for achievement specific events
/datum/award/proc/on_unlock(mob/user)
return
///Achievements are one-off awards for usually doing cool things.
/datum/award/achievement
desc = "Achievement for epic people"
/datum/award/achievement/get_metadata_row()
. = ..()
.["achievement_type"] = "achievement"
/datum/award/achievement/parse_value(raw_value)
return raw_value > 0
/datum/award/achievement/on_unlock(mob/user)
. = ..()
to_chat(user, "<span class='greenannounce'><B>Achievement unlocked: [name]!</B></span>")
///Scores are for leaderboarded things, such as killcount of a specific boss
/datum/award/score
desc = "you did it sooo many times."
category = "Scores"
default_value = 0
var/track_high_scores = TRUE
var/list/high_scores = list()
/datum/award/score/New()
. = ..()
if(track_high_scores)
LoadHighScores()
/datum/award/score/get_metadata_row()
. = ..()
.["achievement_type"] = "score"
/datum/award/score/proc/LoadHighScores()
var/datum/DBQuery/Q = SSdbcore.NewQuery(
"SELECT ckey,value FROM [format_table_name("achievements")] WHERE achievement_key = [database_id] ORDER BY value DESC LIMIT 50"
)
if(!Q.Execute(async = TRUE))
qdel(Q)
return
else
while(Q.NextRow())
var/key = Q.item[1]
var/score = text2num(Q.item[2])
high_scores[key] = score
qdel(Q)
/datum/award/score/parse_value(raw_value)
return isnum(raw_value) ? raw_value : 0

View File

@@ -0,0 +1,130 @@
/datum/award/achievement/boss
category = "Bosses"
icon = "baseboss"
/datum/award/achievement/boss/tendril_exterminator
name = "Tendril Exterminator"
desc = "Watch your step"
database_id = BOSS_MEDAL_TENDRIL
icon = "tendril"
/datum/award/achievement/boss/boss_killer
name = "Boss Killer"
desc = "You've come a long ways from asking how to switch hands."
database_id = "Boss Killer"
icon = "firstboss"
/datum/award/achievement/boss/blood_miner_kill
name = "Blood-Drunk Miner Killer"
desc = "I guess he couldn't handle his drink that well."
database_id = BOSS_MEDAL_MINER
icon = "miner"
/datum/award/achievement/boss/demonic_miner_kill
name = "Demonic-Frost Miner Killer"
desc = "Definitely harder than the Blood-Drunk Miner."
database_id = BOSS_MEDAL_FROSTMINER
/datum/award/achievement/boss/bubblegum_kill
name = "Bubblegum Killer"
desc = "I guess he wasn't made of candy after all"
database_id = BOSS_MEDAL_BUBBLEGUM
icon = "bbgum"
/datum/award/achievement/boss/colossus_kill
name = "Colossus Killer"
desc = "The bigger they are... the better the loot"
database_id = BOSS_MEDAL_COLOSSUS
icon = "colossus"
/datum/award/achievement/boss/drake_kill
name = "Drake Killer"
desc = "Now I can wear Rune Platebodies!"
database_id = BOSS_MEDAL_DRAKE
icon = "drake"
/datum/award/achievement/boss/hierophant_kill
name = "Hierophant Killer"
desc = "Hierophant, but not triumphant."
database_id = BOSS_MEDAL_HIEROPHANT
icon = "hierophant"
/datum/award/achievement/boss/legion_kill
name = "Legion Killer"
desc = "We were many..now we are none."
database_id = BOSS_MEDAL_LEGION
icon = "legion"
/datum/award/achievement/boss/swarmer_beacon_kill
name = "Swarm Beacon Killer"
desc = "GET THEM OFF OF ME!"
database_id = BOSS_MEDAL_SWARMERS
icon = "swarmer"
/datum/award/achievement/boss/wendigo_kill
name = "Wendigo Killer"
desc = "You've now ruined years of mythical storytelling."
database_id = BOSS_MEDAL_WENDIGO
/datum/award/achievement/boss/blood_miner_crusher
name = "Blood-Drunk Miner Crusher"
desc = "I guess he couldn't handle his drink that well."
database_id = BOSS_MEDAL_MINER_CRUSHER
icon = "miner"
/datum/award/achievement/boss/demonic_miner_crusher
name = "Demonic-Frost Miner Crusher"
desc = "Definitely harder than the Blood-Drunk Miner."
database_id = BOSS_MEDAL_FROSTMINER_CRUSHER
/datum/award/achievement/boss/bubblegum_crusher
name = "Bubblegum Crusher"
desc = "I guess he wasn't made of candy after all"
database_id = BOSS_MEDAL_BUBBLEGUM_CRUSHER
icon = "bbgum"
/datum/award/achievement/boss/colossus_crusher
name = "Colossus Crusher"
desc = "The bigger they are... the better the loot"
database_id = BOSS_MEDAL_COLOSSUS_CRUSHER
icon = "colossus"
/datum/award/achievement/boss/drake_crusher
name = "Drake Crusher"
desc = "Now I can wear Rune Platebodies!"
database_id = BOSS_MEDAL_DRAKE_CRUSHER
icon = "drake"
/datum/award/achievement/boss/hierophant_crusher
name = "Hierophant Crusher"
desc = "Hierophant, but not triumphant."
database_id = BOSS_MEDAL_HIEROPHANT_CRUSHER
icon = "hierophant"
/datum/award/achievement/boss/legion_crusher
name = "Legion Crusher"
desc = "We were many... now we are none."
database_id = BOSS_MEDAL_LEGION_CRUSHER
/datum/award/achievement/boss/swarmer_beacon_crusher
name = "Swarm Beacon Crusher"
desc = "GET THEM OFF OF ME!"
database_id = BOSS_MEDAL_SWARMERS_CRUSHER
/datum/award/achievement/boss/wendigo_crusher
name = "Wendigo Crusher"
desc = "You've now ruined years of mythical storytelling."
database_id = BOSS_MEDAL_WENDIGO_CRUSHER
//should be removed soon
/datum/award/achievement/boss/king_goat_kill
name = "King Goat Killer"
desc = "The king is dead, long live the king!"
database_id = BOSS_MEDAL_KINGGOAT
icon = "goatboss"
/datum/award/achievement/boss/king_goat_crusher
name = "King Goat Crusher"
desc = "The king is dead, long live the king!"
database_id = BOSS_MEDAL_KINGGOAT_CRUSHER
icon = "goatboss"

View File

@@ -0,0 +1,54 @@
/datum/award/score/tendril_score
name = "Tendril Score"
desc = "Watch your step"
database_id = TENDRIL_CLEAR_SCORE
/datum/award/score/boss_score
name = "Bosses Killed"
desc = "You've killed HOW many?"
database_id = BOSS_SCORE
/datum/award/score/blood_miner_score
name = "Blood-Drunk Miners Killed"
desc = "You've killed HOW many?"
database_id = MINER_SCORE
/datum/award/score/demonic_miner_score
name = "Demonic-Frost Miners Killed"
desc = "You've killed HOW many?"
database_id = FROST_MINER_SCORE
/datum/award/score/bubblegum_score
name = "Bubblegums Killed"
desc = "You've killed HOW many?"
database_id = BUBBLEGUM_SCORE
/datum/award/score/colussus_score
name = "Colossus Killed"
desc = "You've killed HOW many?"
database_id = COLOSSUS_SCORE
/datum/award/score/drake_score
name = "Drakes Killed"
desc = "You've killed HOW many?"
database_id = DRAKE_SCORE
/datum/award/score/hierophant_score
name = "Hierophants Killed"
desc = "You've killed HOW many?"
database_id = HIEROPHANT_SCORE
/datum/award/score/legion_score
name = "Legions Killed"
desc = "You've killed HOW many?"
database_id = LEGION_SCORE
/datum/award/score/swarmer_beacon_score
name = "Swarmer Beacons Killed"
desc = "You've killed HOW many?"
database_id = SWARMER_BEACON_SCORE
/datum/award/score/wendigo_score
name = "Wendigos Killed"
desc = "You've killed HOW many?"
database_id = WENDIGO_SCORE

View File

@@ -0,0 +1,4 @@
/datum/award/score/hardcore_random
name = "Hardcore random points"
desc = "Well, I might be a blind, deaf, crippled guy, but hey, at least I'm alive."
database_id = HARDCORE_RANDOM_SCORE

View File

@@ -0,0 +1,97 @@
/datum/award/achievement/mafia
category = "Mafia"
icon = "basemafia"
///ALL THE ACHIEVEMENTS FOR WINNING A ROUND AS A ROLE///
/datum/award/achievement/mafia/assistant
name = "Assistant Victory"
desc = "If you got killed instead of someone more important, you just flexed the true strength of your \"\"\"\"role\"\"\"\"."
database_id = MAFIA_MEDAL_ASSISTANT
icon = "assistant"
/datum/award/achievement/mafia/detective
name = "Detective Victory"
desc = "If you did this with a Medical Doctor in the game, i'm not really that impressed."
database_id = MAFIA_MEDAL_DETECTIVE
icon = "detective"
/datum/award/achievement/mafia/psychologist
name = "Psychologist Victory"
desc = "You learned how to not reveal someone random night one! Or... maybe you're just a lucky bastard."
database_id = MAFIA_MEDAL_PSYCHOLOGIST
icon = "psychologist"
/datum/award/achievement/mafia/chaplain
name = "Chaplain Victory"
desc = "Useless... until the one night the thoughtfeeder confidently claims themselves as detective. Mafia's true bullshit detector."
database_id = MAFIA_MEDAL_CHAPLAIN
icon = "chaplain"
/datum/award/achievement/mafia/md
name = "Medical Doctor Victory"
desc = "Congratulations on learning how to not talk!"
database_id = MAFIA_MEDAL_MD
icon = "md"
/datum/award/achievement/mafia/lawyer
name = "Lawyer Victory"
desc = "Oh don't mind me, i'm just the worst rol- Oops, I just instantly ended the game."
database_id = MAFIA_MEDAL_LAWYER
icon = "lawyer"
/datum/award/achievement/mafia/hop
name = "Head of Personnel Victory"
desc = "King of Assistants, waster of a single mafia's night, thrower of games."
database_id = MAFIA_MEDAL_HOP
icon = "hop"
/datum/award/achievement/mafia/changeling
name = "Changeling Victory"
desc = "I think the changelings are metacomming."
database_id = MAFIA_MEDAL_CHANGELING
icon = "changeling"
/datum/award/achievement/mafia/thoughtfeeder
name = "Thoughtfeeder Victory"
desc = "Clown's best friend. And Obsessed. And fugitive? Whose side are you on?!"
database_id = MAFIA_MEDAL_THOUGHTFEEDER
icon = "thoughtfeeder"
/datum/award/achievement/mafia/traitor
name = "Traitor Victory"
desc = "Guys, we still have two more changelings to ki-!! TRAITOR VICTORY !!"
database_id = MAFIA_MEDAL_TRAITOR
icon = "traitor"
/datum/award/achievement/mafia/nightmare
name = "Nightmare Victory"
desc = "DID YOUR LIGHT FLICKER?!"
database_id = MAFIA_MEDAL_NIGHTMARE
icon = "nightmare"
/datum/award/achievement/mafia/fugitive
name = "Fugitive Victory"
desc = "I'm just the description on an achievement, but if you end up having to choose between town and changelings, go changelings."
database_id = MAFIA_MEDAL_FUGITIVE
icon = "fugitive"
/datum/award/achievement/mafia/obsessed
name = "Obsessed Victory"
desc = "You got your target lynched, so instead of being spiteful and annoying, you're just smug and annoying."
database_id = MAFIA_MEDAL_OBSESSED
icon = "obsessed"
/datum/award/achievement/mafia/clown
name = "Clown Victory"
desc = "Did you know this works on traitors, despite their immunity? If you hit the jackpot and manage to kill one, they'll salt into the next dimension. Clown tips!"
database_id = MAFIA_MEDAL_CLOWN
icon = "clown"
///ALL THE ACHIEVEMENTS FOR MISC MAFIA ODDITIES///
/datum/award/achievement/mafia/universally_hated
name = "Universally Hated"
desc = "Managed to get more than 12 votes when put up on trial, jesus christ."
database_id = MAFIA_MEDAL_HATED
icon = "hated"

View File

@@ -0,0 +1,155 @@
/datum/award/achievement/misc
category = "Misc"
icon = "basemisc"
/datum/award/achievement/misc/meteor_examine
name = "Your Life Before Your Eyes"
desc = "Take a close look at hurtling space debris"
database_id = MEDAL_METEOR
icon = "meteors"
/datum/award/achievement/misc/pulse
name = "Jackpot"
desc = "Win a pulse rifle from an arcade machine"
database_id = MEDAL_PULSE
icon = "jackpot"
/datum/award/achievement/misc/time_waste
name = "Time waster"
desc = "Speak no evil, hear no evil, see just errors"
database_id = MEDAL_TIMEWASTE
icon = "timewaste"
/datum/award/achievement/misc/feat_of_strength
name = "Feat of Strength"
desc = "If the rod is immovable, is it passing you or are you passing it?"
database_id = MEDAL_RODSUPLEX
icon = "featofstrength"
/datum/award/achievement/misc/round_and_full
name = "Round and Full"
desc = "Well at least you aren't down the river, I hear they eat people there."
database_id = MEDAL_CLOWNCARKING
icon = "clownking"
/datum/award/achievement/misc/the_best_driver
name = "The Best Driver"
desc = "100 honks later"
database_id = MEDAL_THANKSALOT
icon = "clownthanks"
/datum/award/achievement/misc/helbitaljanken
name = "Helbitaljanken"
desc = "You janked hard"
database_id = MEDAL_HELBITALJANKEN
icon = "helbital"
/datum/award/achievement/misc/getting_an_upgrade
name = "Getting an upgrade"
desc = "Make your first unique material item!"
database_id = MEDAL_MATERIALCRAFT
/datum/award/achievement/misc/rocket_holdup
name = "Disk, Please!"
desc = "Is the man currently pointing a loaded rocket launcher at your head point blank really dumb enough to pull the trigger? Do you really want to find out?"
database_id = MEDAL_DISKPLEASE
/datum/award/achievement/misc/gamer
name = "My Watchlist Status is Not Important"
desc = "You may be under the impression that violent video games are a harmless pastime, but the security and medical personnel swarming your location with batons and knockout gas look like they disagree."
database_id = MEDAL_GAMER
/datum/award/achievement/misc/vendor_squish
name = "I Was a Teenage Anarchist"
desc = "You were doing a great job sticking it to the system until that vending machine decided to fight back."
database_id = MEDAL_VENDORSQUISH
/datum/award/achievement/misc/swirlie
name = "A Bowl-d New World"
desc = "There's a lot of grisly ways to kick it on the Spinward Periphery, but drowning to death in a toilet probably wasn't what you had in mind. Probably."
database_id = MEDAL_SWIRLIE
/datum/award/achievement/misc/selfouch
name = "How Do I Switch Hands???"
desc = "If you saw someone casually club themselves upside the head with a toolbox anywhere in the galaxy but here, you'd probably be pretty concerned for them."
database_id = MEDAL_SELFOUCH
/datum/award/achievement/misc/sandman
name = "Mister Sandman"
desc = "Mechanically speaking, there's no real benefit to being unconscious during surgery. Weird how insistent this doctor is about using the N2O anyway though, huh?"
database_id = MEDAL_SANDMAN
/datum/award/achievement/misc/cleanboss
name = "One Lean, Mean, Cleaning Machine"
desc = "How does it feel to know that your workplace values a mop bucket on wheels more than you?" // i can do better than this give me time
database_id = MEDAL_CLEANBOSS
/datum/award/achievement/misc/rule8
name = "Rule 8"
desc = "Call an admin this is ILLEGAL!!"
database_id = MEDAL_RULE8
icon = "rule8"
/datum/award/achievement/misc/speed_round
name = "Long shift"
desc = "Well, that didn't take long."
database_id = MEDAL_LONGSHIFT
icon = "longshift"
/datum/award/achievement/misc/snail
name = "KKKiiilll mmmeee"
desc = "You were a little too ambitious, but hey, I guess you're still alive?"
database_id = MEDAL_SNAIL
icon = "snail"
/datum/award/achievement/misc/lookoutsir
name = "Look Out, Sir!"
desc = "Either awarded for making the ultimate sacrifice for your comrades, or a really dumb attempt at grenade jumping."
database_id = MEDAL_LOOKOUTSIR
/datum/award/achievement/misc/gottem
name = "HA, GOTTEM"
desc = "Made you look!"
database_id = MEDAL_GOTTEM
/datum/award/achievement/misc/ascension
name = "Ascension"
desc = "Caedite eos. Novit enim Dominus qui sunt eius."
database_id = MEDAL_ASCENSION
icon = "ascension"
/datum/award/achievement/misc/frenching
name = "Frenching"
desc = "Just a taste, for science!"
database_id = MEDAL_FRENCHING
icon = "frenching"
/datum/award/achievement/misc/ash_ascension
name = "Nightwatcher's Eyes"
desc = "You've risen above the flames, became one with the ashes. You've been reborn as one with the Nightwatcher."
database_id = MEDAL_ASH_ASCENSION
icon = "ashascend"
/datum/award/achievement/misc/flesh_ascension
name = "Vortex of Arms"
desc = "You've became something more, something greater. A piece of the emperor resides within you, and you within him."
database_id = MEDAL_FLESH_ASCENSION
icon = "fleshascend"
/datum/award/achievement/misc/rust_ascension
name = "Hills of Rust"
desc = "You've summoned a piece of the Hill of rust, and so the Hills welcome you."
database_id = MEDAL_RUST_ASCENSION
icon = "rustascend"
/datum/award/achievement/misc/void_ascension
name = "All that perish"
desc = "Place of a diffrent being, diffrent time. Everything ends there... but maybe it is just the beginning?"
database_id = MEDAL_VOID_ASCENSION
icon = "voidascend"
/datum/award/achievement/misc/toolbox_soul
name = "SOUL'd Out"
desc = "My eternal soul was destroyed to make a toolbox look funny and all I got was this achievement..."
database_id = MEDAL_TOOLBOX_SOUL
icon = "toolbox_soul"

View File

@@ -0,0 +1,10 @@
/datum/award/achievement/skill
category = "Skills"
icon = "baseskill"
/datum/award/achievement/skill/legendary_miner
name = "Legendary miner"
desc = "No mere rock can stop me!"
database_id = MEDAL_LEGENDARY_MINER
icon = "mining"

View File

@@ -275,6 +275,11 @@
else
target.visible_message("<span class='danger'>[target] is hit by a [proj_name][hit_part ? " in the [hit_part.name]" : ""]!</span>", null, null, COMBAT_MESSAGE_RANGE, target)
to_chat(target, "<span class='userdanger'>You're hit by a [proj_name][hit_part ? " in the [hit_part.name]" : ""]!</span>")
for(var/M in purple_hearts)
var/mob/living/martyr = M
if(martyr.stat == DEAD && martyr.client)
martyr.client.give_award(/datum/award/achievement/misc/lookoutsir, martyr)
UnregisterSignal(parent, COMSIG_PARENT_PREQDELETED)
if(queued_delete)
qdel(parent)

View File

@@ -882,6 +882,9 @@
VV_DROPDOWN_OPTION(VV_HK_ADD_REAGENT, "Add Reagent")
VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EMP, "EMP Pulse")
VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EXPLOSION, "Explosion")
// VV_DROPDOWN_OPTION(VV_HK_RADIATE, "Radiate")
VV_DROPDOWN_OPTION(VV_HK_EDIT_FILTERS, "Edit Filters")
// VV_DROPDOWN_OPTION(VV_HK_ADD_AI, "Add AI controller")
/atom/vv_do_topic(list/href_list)
. = ..()
@@ -925,6 +928,9 @@
var/newname = input(usr, "What do you want to rename this to?", "Automatic Rename") as null|text
if(newname)
vv_auto_rename(newname)
if(href_list[VV_HK_EDIT_FILTERS] && check_rights(R_VAREDIT))
var/client/C = usr.client
C?.open_filter_editor(src)
/atom/vv_get_header()
. = ..()
@@ -1145,7 +1151,6 @@
victim.log_message(message, LOG_ATTACK, color="blue")
// Filter stuff
/atom/proc/add_filter(name,priority,list/params)
LAZYINITLIST(filter_data)
var/list/p = params.Copy()
@@ -1161,16 +1166,50 @@
var/list/arguments = data.Copy()
arguments -= "priority"
filters += filter(arglist(arguments))
UNSETEMPTY(filter_data)
/atom/proc/transition_filter(name, time, list/new_params, easing, loop)
var/filter = get_filter(name)
if(!filter)
return
var/list/old_filter_data = filter_data[name]
var/list/params = old_filter_data.Copy()
for(var/thing in new_params)
params[thing] = new_params[thing]
animate(filter, new_params, time = time, easing = easing, loop = loop)
for(var/param in params)
filter_data[name][param] = params[param]
/atom/proc/change_filter_priority(name, new_priority)
if(!filter_data || !filter_data[name])
return
filter_data[name]["priority"] = new_priority
update_filters()
/obj/item/update_filters()
. = ..()
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
/atom/proc/get_filter(name)
if(filter_data && filter_data[name])
return filters[filter_data.Find(name)]
/atom/proc/remove_filter(name)
if(filter_data && filter_data[name])
filter_data -= name
update_filters()
return TRUE
/atom/proc/remove_filter(name_or_names)
if(!filter_data)
return
var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names)
for(var/name in names)
if(filter_data[name])
filter_data -= name
update_filters()
/atom/proc/intercept_zImpact(atom/movable/AM, levels = 1)
. |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels)

View File

@@ -1,4 +1,6 @@
#define DEFAULT_METEOR_LIFETIME 1800
#define MAP_EDGE_PAD 5
GLOBAL_VAR_INIT(meteor_wave_delay, 625) //minimum wait between waves in tenths of seconds
//set to at least 100 unless you want evarr ruining every round
@@ -30,7 +32,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
var/turf/pickedgoal
var/max_i = 10//number of tries to spawn meteor.
while(!isspaceturf(pickedstart))
var/startSide = dir || pick(GLOB.cardinals)
var/startSide = (dir ? dir : pick(GLOB.cardinals))
var/startZ = pick(SSmapping.levels_by_trait(ZTRAIT_STATION))
pickedstart = spaceDebrisStartLoc(startSide, startZ)
pickedgoal = spaceDebrisFinishLoc(startSide, startZ)
@@ -46,17 +48,17 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
var/startx
switch(startSide)
if(NORTH)
starty = world.maxy-(TRANSITIONEDGE+2)
startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2))
starty = world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)
startx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD))
if(EAST)
starty = rand((TRANSITIONEDGE+2),world.maxy-(TRANSITIONEDGE+2))
startx = world.maxx-(TRANSITIONEDGE+2)
starty = rand((TRANSITIONEDGE + MAP_EDGE_PAD),world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD))
startx = world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)
if(SOUTH)
starty = (TRANSITIONEDGE+2)
startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2))
starty = (TRANSITIONEDGE + MAP_EDGE_PAD)
startx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD))
if(WEST)
starty = rand((TRANSITIONEDGE+2), world.maxy-(TRANSITIONEDGE+2))
startx = (TRANSITIONEDGE+2)
starty = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD))
startx = (TRANSITIONEDGE + MAP_EDGE_PAD)
. = locate(startx, starty, Z)
/proc/spaceDebrisFinishLoc(startSide, Z)
@@ -64,17 +66,17 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
var/endx
switch(startSide)
if(NORTH)
endy = (TRANSITIONEDGE+1)
endx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
endy = (TRANSITIONEDGE + MAP_EDGE_PAD)
endx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD))
if(EAST)
endy = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1))
endx = (TRANSITIONEDGE+1)
endy = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD))
endx = (TRANSITIONEDGE + MAP_EDGE_PAD)
if(SOUTH)
endy = world.maxy-(TRANSITIONEDGE+1)
endx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
endy = world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)
endx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD))
if(WEST)
endy = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1))
endx = world.maxx-(TRANSITIONEDGE+1)
endy = rand((TRANSITIONEDGE + MAP_EDGE_PAD),world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD))
endx = world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)
. = locate(endx, endy, Z)
///////////////////////
@@ -82,7 +84,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
//////////////////////
/obj/effect/meteor
name = "the concept of meteor"
name = "\proper the concept of meteor"
desc = "You should probably run instead of gawking at this."
icon = 'icons/obj/meteor.dmi'
icon_state = "small"
@@ -92,7 +94,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
var/hitpwr = 2 //Level of ex_act to be called on hit.
var/dest
pass_flags = PASSTABLE
var/heavy = 0
var/heavy = FALSE
var/meteorsound = 'sound/effects/meteorimpact.ogg'
var/z_original
var/threat = 0 // used for determining which meteors are most interesting
@@ -108,12 +110,12 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
. = ..() //process movement...
var/turf/T = get_turf(loc)
if(.)//.. if did move, ram the turf we get in
var/turf/T = get_turf(loc)
ram_turf(T)
if(prob(10) && !isspaceturf(T) && !istype(T, /turf/closed/mineral) && !istype(T, /turf/open/floor/plating/asteroid))//randomly takes a 'hit' from ramming
get_hit()
if(prob(10) && !isspaceturf(T) && !istype(T, /turf/closed/mineral) && !istype(T, /turf/open/floor/plating/asteroid))//randomly takes a 'hit' from ramming, and ignore spare ruin aseroids
get_hit()
/obj/effect/meteor/Destroy()
if (timerid)
@@ -135,24 +137,25 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
/obj/effect/meteor/Bump(atom/A)
if(A)
ram_turf(get_turf(A))
playsound(src.loc, meteorsound, 40, 1)
if(!istype(A, /turf/closed/mineral) && !istype(A, /turf/open/floor/plating/asteroid))
playsound(src.loc, meteorsound, 40, TRUE)
if(!istype(A, /turf/closed/mineral) && !istype(A, /turf/open/floor/plating/asteroid)) // ignore localstation ruins
get_hit()
/obj/effect/meteor/proc/ram_turf(turf/T)
//first bust whatever is in the turf
for(var/atom/A in T)
if(A != src)
if(isliving(A))
A.visible_message("<span class='warning'>[src] slams into [A].</span>", "<span class='userdanger'>[src] slams into you!.</span>")
A.ex_act(hitpwr)
for(var/thing in T)
if(thing == src)
continue
if(isliving(thing))
var/mob/living/living_thing = thing
living_thing.visible_message("<span class='warning'>[src] slams into [living_thing].</span>", "<span class='userdanger'>[src] slams into you!.</span>")
A.ex_act(hitpwr)
//then, ram the turf if it still exists
if(T)
T.ex_act(hitpwr)
//process getting 'hit' by colliding with a dense object
//or randomly when ramming turfs
/obj/effect/meteor/proc/get_hit()
@@ -162,13 +165,10 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
meteor_effect()
qdel(src)
/obj/effect/meteor/ex_act()
return
/obj/effect/meteor/examine(mob/user)
. = ..()
if(!(flags_1 & ADMIN_SPAWNED_1) && isliving(user))
SSmedals.UnlockMedal(MEDAL_METEOR, user.client)
return ..()
user.client.give_award(/datum/award/achievement/misc/meteor_examine, user)
/obj/effect/meteor/attackby(obj/item/I, mob/user, params)
if(I.tool_behaviour == TOOL_MINING)
@@ -232,7 +232,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
name = "big meteor"
icon_state = "large"
hits = 6
heavy = 1
heavy = TRUE
dropamt = 4
threat = 10
@@ -245,7 +245,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
name = "flaming meteor"
icon_state = "flaming"
hits = 5
heavy = 1
heavy = TRUE
meteorsound = 'sound/effects/bamf.ogg'
meteordrop = list(/obj/item/stack/ore/plasma)
threat = 20
@@ -258,7 +258,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
/obj/effect/meteor/irradiated
name = "glowing meteor"
icon_state = "glowing"
heavy = 1
heavy = TRUE
meteordrop = list(/obj/item/stack/ore/uranium)
threat = 15
@@ -275,7 +275,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
icon_state = "meateor"
desc = "Just... don't think too hard about where this thing came from."
hits = 2
heavy = 1
heavy = TRUE
meteorsound = 'sound/effects/blobattack.ogg'
meteordrop = list(/obj/item/reagent_containers/food/snacks/meat/slab/human, /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant, /obj/item/organ/heart, /obj/item/organ/lungs, /obj/item/organ/tongue, /obj/item/organ/appendix/)
var/meteorgibs = /obj/effect/gibspawner/generic
@@ -327,7 +327,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
desc = "Your life briefly passes before your eyes the moment you lay them on this monstrosity."
hits = 30
hitpwr = 1
heavy = 1
heavy = TRUE
meteorsound = 'sound/effects/bamf.ogg'
meteordrop = list(/obj/item/stack/ore/plasma)
threat = 50
@@ -358,7 +358,7 @@ GLOBAL_LIST_INIT(meteorsSPOOKY, list(/obj/effect/meteor/pumpkin))
icon = 'icons/obj/meteor_spooky.dmi'
icon_state = "pumpkin"
hits = 10
heavy = 1
heavy = TRUE
dropamt = 1
meteordrop = list(/obj/item/clothing/head/hardhat/pumpkinhead, /obj/item/reagent_containers/food/snacks/grown/pumpkin)
threat = 100
@@ -368,3 +368,4 @@ GLOBAL_LIST_INIT(meteorsSPOOKY, list(/obj/effect/meteor/pumpkin))
meteorsound = pick('sound/hallucinations/im_here1.ogg','sound/hallucinations/im_here2.ogg')
//////////////////////////
#undef DEFAULT_METEOR_LIFETIME
#undef MAP_EDGE_PAD

View File

@@ -1,6 +1,6 @@
#define AUTOLATHE_MAIN_MENU 1
#define AUTOLATHE_CATEGORY_MENU 2
#define AUTOLATHE_SEARCH_MENU 3
#define AUTOLATHE_MAIN_MENU 1
#define AUTOLATHE_CATEGORY_MENU 2
#define AUTOLATHE_SEARCH_MENU 3
/obj/machinery/autolathe
name = "autolathe"
@@ -17,7 +17,7 @@
var/list/L = list()
var/list/LL = list()
var/hacked = FALSE
var/disabled = 0
var/disabled = FALSE
var/shocked = FALSE
var/hack_wire
var/disable_wire
@@ -27,13 +27,13 @@
var/prod_coeff = 1
var/datum/design/being_built
var/datum/techweb/stored_research
var/list/datum/design/matching_designs
var/selected_category
var/screen = 1
var/base_price = 25
var/hacked_price = 50
var/datum/techweb/specialized/autounlocking/stored_research = /datum/techweb/specialized/autounlocking/autolathe
var/list/categories = list(
"Tools",
"Electronics",
@@ -46,19 +46,13 @@
"Dinnerware",
"Imported"
)
var/list/allowed_materials
/// Base print speed
var/base_print_speed = 10
/obj/machinery/autolathe/Initialize()
var/list/mats = allowed_materials
if(!mats)
mats = SSmaterials.materialtypes_by_category[MAT_CATEGORY_RIGID]
AddComponent(/datum/component/material_container, mats, _show_on_examine=TRUE, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert))
AddComponent(/datum/component/material_container, SSmaterials.materialtypes_by_category[MAT_CATEGORY_RIGID], 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert))
. = ..()
wires = new /datum/wires/autolathe(src)
stored_research = new stored_research
stored_research = new /datum/techweb/specialized/autounlocking/autolathe
matching_designs = list()
/obj/machinery/autolathe/Destroy()
@@ -83,7 +77,7 @@
if(AUTOLATHE_SEARCH_MENU)
dat = search_win(user)
var/datum/browser/popup = new(user, name, name, 400, 500)
var/datum/browser/popup = new(user, "autolathe", name, 400, 500)
popup.set_content(dat)
popup.open()
@@ -114,9 +108,9 @@
return TRUE
if(istype(O, /obj/item/disk/design_disk))
user.visible_message("[user] begins to load \the [O] in \the [src]...",
"You begin to load a design from \the [O]...",
"You hear the chatter of a floppy drive.")
user.visible_message("<span class='notice'>[user] begins to load \the [O] in \the [src]...</span>",
"<span class='notice'>You begin to load a design from \the [O]...</span>",
"<span class='hear'>You hear the chatter of a floppy drive.</span>")
busy = TRUE
var/obj/item/disk/design_disk/D = O
if(do_after(user, 14.4, target = src))
@@ -128,14 +122,16 @@
return ..()
/obj/machinery/autolathe/proc/AfterMaterialInsert(obj/item/item_inserted, id_inserted, amount_inserted)
/obj/machinery/autolathe/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted)
if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal))
use_power(MINERAL_MATERIAL_AMOUNT / 10)
else if(item_inserted.custom_materials?.len && item_inserted.custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)])
else if(custom_materials && custom_materials.len && custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)])
flick("autolathe_r",src)//plays glass insertion animation by default otherwise
else
flick("autolathe_o",src)//plays metal insertion animation
use_power(min(1000, amount_inserted / 100))
updateUsrDialog()
@@ -187,7 +183,7 @@
if(materials.materials[i] > 0)
list_to_show += i
used_material = input("Choose [used_material]", "Custom Material") as null|anything in list_to_show
used_material = input("Choose [used_material]", "Custom Material") as null|anything in sortList(list_to_show, /proc/cmp_typepaths_asc)
if(!used_material)
return //Didn't pick any material, so you can't build shit either.
custom_materials[used_material] += amount_needed
@@ -198,8 +194,8 @@
busy = TRUE
use_power(power)
icon_state = "autolathe_n"
var/time = is_stack ? 10 : base_print_speed * coeff * multiplier
addtimer(CALLBACK(src, .proc/make_item, power, materials_used, custom_materials, multiplier, coeff, is_stack), time)
var/time = is_stack ? 32 : (32 * coeff * multiplier) ** 0.8
addtimer(CALLBACK(src, .proc/make_item, power, materials_used, custom_materials, multiplier, coeff, is_stack, usr), time)
else
to_chat(usr, "<span class=\"alert\">Not enough materials for this operation.</span>")
@@ -218,10 +214,11 @@
return
/obj/machinery/autolathe/proc/make_item(power, var/list/materials_used, var/list/picked_materials, multiplier, coeff, is_stack)
/obj/machinery/autolathe/proc/make_item(power, list/materials_used, list/picked_materials, multiplier, coeff, is_stack, mob/user)
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/atom/A = drop_location()
use_power(power)
materials.use_materials(materials_used)
if(is_stack)
@@ -235,6 +232,11 @@
if(length(picked_materials))
new_item.set_custom_materials(picked_materials, 1 / multiplier) //Ensure we get the non multiplied amount
for(var/x in picked_materials)
var/datum/material/M = x
if(!istype(M, /datum/material/glass) && !istype(M, /datum/material/iron))
user.client.give_award(/datum/award/achievement/misc/getting_an_upgrade, user)
icon_state = "autolathe"
busy = FALSE
@@ -246,12 +248,10 @@
T += MB.rating*75000
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
materials.max_amount = T
var/manips = 0
var/total_manip_rating = 0
T=1.2
for(var/obj/item/stock_parts/manipulator/M in component_parts)
total_manip_rating += M.rating
manips++
prod_coeff = STANDARD_PART_LEVEL_LATHE_COEFFICIENT(total_manip_rating / (manips? manips : 1))
T -= M.rating*0.2
prod_coeff = min(1,max(0,T)) // Coeff going 1 -> 0,8 -> 0,6 -> 0,4
/obj/machinery/autolathe/examine(mob/user)
. += ..()
@@ -376,6 +376,7 @@
return materials.has_materials(required_materials)
/obj/machinery/autolathe/proc/get_design_cost(datum/design/D)
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff)
var/dat
@@ -416,9 +417,7 @@
hacked = state
for(var/id in SSresearch.techweb_designs)
var/datum/design/D = SSresearch.techweb_design_by_id(id)
if(D.build_type & stored_research.design_autounlock_skip_types)
continue
if((D.build_type & stored_research.design_autounlock_buildtypes) && ("hacked" in D.category))
if((D.build_type & AUTOLATHE) && ("hacked" in D.category))
if(hacked)
stored_research.add_design(D)
else
@@ -428,19 +427,24 @@
. = ..()
adjust_hacked(TRUE)
//Called when the object is constructed by an autolathe
//Has a reference to the autolathe so you can do !!FUN!! things with hacked lathes
/obj/item/proc/autolathe_crafted(obj/machinery/autolathe/A)
return
/obj/machinery/autolathe/secure
name = "secured autolathe"
desc = "It produces items using metal and glass. This model was reprogrammed without some of the more hazardous designs."
circuit = /obj/item/circuitboard/machine/autolathe/secure
stored_research = /datum/techweb/specialized/autounlocking/autolathe/public
base_print_speed = 20
/obj/machinery/autolathe/secure/Initialize()
. = ..()
stored_research = new /datum/techweb/specialized/autounlocking/autolathe/public
/obj/machinery/autolathe/toy
name = "autoylathe"
desc = "It produces toys using plastic, metal and glass."
circuit = /obj/item/circuitboard/machine/autolathe/toy
stored_research = /datum/techweb/specialized/autounlocking/autolathe/toy
categories = list(
"Toys",
"Figurines",
@@ -453,12 +457,8 @@
"Misc",
"Imported"
)
allowed_materials = list(
/datum/material/iron,
/datum/material/glass,
/datum/material/plastic
)
/obj/machinery/autolathe/toy/hacked/Initialize()
. = ..()
adjust_hacked(TRUE)
stored_research = new /datum/techweb/specialized/autounlocking/autolathe/toy

View File

@@ -3,14 +3,7 @@
#define ARCADE_WEIGHT_RARE 1
#define ARCADE_RATIO_PLUSH 0.20 // average 1 out of 6 wins is a plush.
/obj/machinery/computer/arcade
name = "random arcade"
desc = "random arcade machine"
icon_state = "arcade"
icon_keyboard = null
icon_screen = "invaders"
clockwork = TRUE //it'd look weird
var/list/prizes = list(
GLOBAL_LIST_INIT(arcade_prize_pool, list(
/obj/item/toy/balloon = ARCADE_WEIGHT_USELESS,
/obj/item/toy/beach_ball = ARCADE_WEIGHT_USELESS,
/obj/item/toy/cattoy = ARCADE_WEIGHT_USELESS,
@@ -70,9 +63,16 @@
/obj/item/clothing/mask/fakemoustache/italian = ARCADE_WEIGHT_RARE,
/obj/item/clothing/suit/hooded/wintercoat/ratvar/fake = ARCADE_WEIGHT_TRICK,
/obj/item/clothing/suit/hooded/wintercoat/narsie/fake = ARCADE_WEIGHT_TRICK
)
))
/obj/machinery/computer/arcade
name = "random arcade"
desc = "random arcade machine"
icon_state = "arcade"
icon_keyboard = "no_keyboard"
icon_screen = "invaders"
light_color = LIGHT_COLOR_GREEN
var/list/prize_override
/obj/machinery/computer/arcade/proc/Reset()
return
@@ -96,39 +96,51 @@
prizes[/obj/item/toy/plush/random] = counterlist_sum(prizes) * ARCADE_RATIO_PLUSH
Reset()
/obj/machinery/computer/arcade/proc/prizevend(mob/user, list/rarity_classes)
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "arcade", /datum/mood_event/arcade)
/obj/machinery/computer/arcade/proc/prizevend(mob/user, prizes = 1)
// if(user.mind?.get_skill_level(/datum/skill/gaming) >= SKILL_LEVEL_LEGENDARY && HAS_TRAIT(user, TRAIT_GAMERGOD))
// visible_message("<span class='notice'>[user] inputs an intense cheat code!",\
// "<span class='notice'>You hear a flurry of buttons being pressed.</span>")
// say("CODE ACTIVATED: EXTRA PRIZES.")
// prizes *= 2
for(var/i = 0, i < prizes, i++)
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "arcade", /datum/mood_event/arcade)
if(prob(0.0001)) //1 in a million
new /obj/item/gun/energy/pulse/prize(src)
visible_message("<span class='notice'>[src] dispenses.. woah, a gun! Way past cool.</span>", "<span class='notice'>You hear a chime and a shot.</span>")
user.client.give_award(/datum/award/achievement/misc/pulse, user)
return
if(prob(1) && prob(1) && prob(1)) //Proper 1 in a million
new /obj/item/gun/energy/pulse/prize(src)
SSmedals.UnlockMedal(MEDAL_PULSE, usr.client)
var/prizeselect
if(prize_override)
prizeselect = pickweight(prize_override)
else
prizeselect = pickweight(GLOB.arcade_prize_pool)
var/atom/movable/the_prize = new prizeselect(get_turf(src))
playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
visible_message("<span class='notice'>[src] dispenses [the_prize]!</span>", "<span class='notice'>You hear a chime and a clunk.</span>")
if(!contents.len)
var/list/toy_raffle
if(rarity_classes)
for(var/A in prizes)
if(prizes[A] in rarity_classes)
LAZYSET(toy_raffle, A, prizes[A])
if(!toy_raffle)
toy_raffle = prizes
var/prizeselect = pickweight(toy_raffle)
new prizeselect(src)
var/atom/movable/prize = pick(contents)
visible_message("<span class='notice'>[src] dispenses [prize]!</span>", "<span class='notice'>You hear a chime and a clunk.</span>")
prize.forceMove(get_turf(src))
/obj/machinery/computer/arcade/emp_act(severity)
. = ..()
var/override = FALSE
if(prize_override)
override = TRUE
if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF)
return
var/empprize = null
var/num_of_prizes = rand(round(severity/50),round(severity/100))
var/num_of_prizes = 0
switch(severity)
if(1)
num_of_prizes = rand(1,4)
if(2)
num_of_prizes = rand(0,2)
for(var/i = num_of_prizes; i > 0; i--)
empprize = pickweight(prizes)
if(override)
empprize = pickweight(prize_override)
else
empprize = pickweight(GLOB.arcade_prize_pool)
new empprize(loc)
explosion(loc, -1, 0, 1+num_of_prizes, flame_range = 1+num_of_prizes)

View File

@@ -1,130 +1,399 @@
// ** BATTLE ** //
/obj/machinery/computer/arcade/battle
name = "arcade machine"
desc = "Does not support Pinball."
icon_state = "arcade"
circuit = /obj/item/circuitboard/computer/arcade/battle
var/enemy_name = "Space Villain"
var/temp = "Winners don't use space drugs" //Temporary message, for attack messages, etc
var/player_hp = 30 //Player health/attack points
var/player_mp = 10
var/enemy_hp = 45 //Enemy health/attack points
var/enemy_mp = 20
var/gameover = FALSE
var/blocked = FALSE //Player cannot attack/heal while set
var/turtle = 0
var/turn_speed = 5 //Measured in deciseconds.
var/enemy_name = "Space Villain"
///Enemy health/attack points
var/enemy_hp = 100
var/enemy_mp = 40
///Temporary message, for attack messages, etc
var/temp = "<br><center><h3>Winners don't use space drugs<center><h3>"
///the list of passive skill the enemy currently has. the actual passives are added in the enemy_setup() proc
var/list/enemy_passive
///if all the enemy's weakpoints have been triggered becomes TRUE
var/finishing_move = FALSE
///linked to passives, when it's equal or above the max_passive finishing move will become TRUE
var/pissed_off = 0
///the number of passives the enemy will start with
var/max_passive = 3
///weapon wielded by the enemy, the shotgun doesn't count.
var/chosen_weapon
///Player health
var/player_hp = 85
///player magic points
var/player_mp = 20
///used to remember the last three move of the player before this turn.
var/list/last_three_move
///if the enemy or player died. restart the game when TRUE
var/gameover = FALSE
///the player cannot make any move while this is set to TRUE. should only TRUE during enemy turns.
var/blocked = FALSE
///used to clear the enemy_action proc timer when the game is restarted
var/timer_id
///weapon used by the enemy, pure fluff.for certain actions
var/list/weapons
///unique to the emag mode, acts as a time limit where the player dies when it reaches 0.
var/bomb_cooldown = 19
///creates the enemy base stats for a new round along with the enemy passives
/obj/machinery/computer/arcade/battle/proc/enemy_setup(player_skill)
player_hp = 85
player_mp = 20
enemy_hp = 100
enemy_mp = 40
gameover = FALSE
blocked = FALSE
finishing_move = FALSE
pissed_off = 0
last_three_move = null
enemy_passive = list("short_temper" = TRUE, "poisonous" = TRUE, "smart" = TRUE, "shotgun" = TRUE, "magical" = TRUE, "chonker" = TRUE)
for(var/i = LAZYLEN(enemy_passive); i > max_passive; i--) //we'll remove passives from the list until we have the number of passive we want
var/picked_passive = pick(enemy_passive)
LAZYREMOVE(enemy_passive, picked_passive)
if(LAZYACCESS(enemy_passive, "chonker"))
enemy_hp += 20
if(LAZYACCESS(enemy_passive, "shotgun"))
chosen_weapon = "shotgun"
else if(weapons)
chosen_weapon = pick(weapons)
else
chosen_weapon = "null gun" //if the weapons list is somehow empty, shouldn't happen but runtimes are sneaky bastards.
if(player_skill)
player_hp += player_skill * 2
/obj/machinery/computer/arcade/battle/Reset()
max_passive = 3
var/name_action
var/name_part1
var/name_part2
name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "Ban ")
if(SSevents.holidays && SSevents.holidays[HALLOWEEN])
name_action = pick_list(ARCADE_FILE, "rpg_action_halloween")
name_part1 = pick_list(ARCADE_FILE, "rpg_adjective_halloween")
name_part2 = pick_list(ARCADE_FILE, "rpg_enemy_halloween")
weapons = strings(ARCADE_FILE, "rpg_weapon_halloween")
else if(SSevents.holidays && SSevents.holidays[CHRISTMAS])
name_action = pick_list(ARCADE_FILE, "rpg_action_xmas")
name_part1 = pick_list(ARCADE_FILE, "rpg_adjective_xmas")
name_part2 = pick_list(ARCADE_FILE, "rpg_enemy_xmas")
weapons = strings(ARCADE_FILE, "rpg_weapon_xmas")
else if(SSevents.holidays && SSevents.holidays[VALENTINES])
name_action = pick_list(ARCADE_FILE, "rpg_action_valentines")
name_part1 = pick_list(ARCADE_FILE, "rpg_adjective_valentines")
name_part2 = pick_list(ARCADE_FILE, "rpg_enemy_valentines")
weapons = strings(ARCADE_FILE, "rpg_weapon_valentines")
else
name_action = pick_list(ARCADE_FILE, "rpg_action")
name_part1 = pick_list(ARCADE_FILE, "rpg_adjective")
name_part2 = pick_list(ARCADE_FILE, "rpg_enemy")
weapons = strings(ARCADE_FILE, "rpg_weapon")
name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ")
name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn", "Bloopers")
enemy_name = ("The " + name_part1 + " " + name_part2)
name = (name_action + " " + enemy_name)
enemy_name = replacetext((name_part1 + name_part2), "the ", "")
name = (name_action + name_part1 + name_part2)
enemy_setup(0) //in the case it's reset we assume the player skill is 0 because the VOID isn't a gamer
/obj/machinery/computer/arcade/battle/ui_interact(mob/user)
. = ..()
screen_setup(user)
///sets up the main screen for the user
/obj/machinery/computer/arcade/battle/proc/screen_setup(mob/user)
var/dat = "<a href='byond://?src=[REF(src)];close=1'>Close</a>"
dat += "<center><h4>[enemy_name]</h4></center>"
dat += "<br><center><h3>[temp]</h3></center>"
dat += "[temp]"
dat += "<br><center>Health: [player_hp] | Magic: [player_mp] | Enemy Health: [enemy_hp]</center>"
if (gameover)
dat += "<center><b><a href='byond://?src=[REF(src)];newgame=1'>New Game</a>"
else
dat += "<center><b><a href='byond://?src=[REF(src)];attack=1'>Attack</a> | "
dat += "<a href='byond://?src=[REF(src)];heal=1'>Heal</a> | "
dat += "<a href='byond://?src=[REF(src)];charge=1'>Recharge Power</a>"
dat += "<center><b><a href='byond://?src=[REF(src)];attack=1'>Light attack</a>"
dat += "<center><b><a href='byond://?src=[REF(src)];defend=1'>Defend</a>"
dat += "<center><b><a href='byond://?src=[REF(src)];counter_attack=1'>Counter attack</a>"
dat += "<center><b><a href='byond://?src=[REF(src)];power_attack=1'>Power attack</a>"
dat += "</b></center>"
var/datum/browser/popup = new(user, "arcade", "Space Villain 2000")
popup.set_content(dat)
popup.open()
if(user.client) //mainly here to avoid a runtime when the player gets gibbed when losing the emag mode.
var/datum/browser/popup = new(user, "arcade", "Space Villain 2000")
popup.set_content(dat)
popup.open()
/obj/machinery/computer/arcade/battle/Topic(href, href_list)
if(..())
return
var/gamerSkill = 0
// if(usr?.mind)
// gamerSkill = usr.mind.get_skill_level(/datum/skill/gaming)
if (!blocked && !gameover)
var/attackamt = rand(5,7) + rand(0, gamerSkill)
if(finishing_move) //time to bonk that fucker,cuban pete will sometime survive a finishing move.
attackamt *= 100
//light attack suck absolute ass but it doesn't cost any MP so it's pretty good to finish an enemy off
if (href_list["attack"])
blocked = TRUE
var/attackamt = rand(2,6)
temp = "You attack for [attackamt] damage!"
playsound(loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3)
updateUsrDialog()
if(turtle > 0)
turtle--
sleep(turn_speed)
temp = "<br><center><h3>you do quick jab for [attackamt] of damage!</h3></center>"
enemy_hp -= attackamt
arcade_action(usr)
arcade_action(usr,"attack",attackamt)
else if (href_list["heal"])
blocked = TRUE
var/pointamt = rand(1,3)
var/healamt = rand(6,8)
temp = "You use [pointamt] magic to heal for [healamt] damage!"
playsound(loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3)
updateUsrDialog()
turtle++
//defend lets you gain back MP and take less damage from non magical attack.
else if(href_list["defend"])
temp = "<br><center><h3>you take a defensive stance and gain back 10 mp!</h3></center>"
player_mp += 10
arcade_action(usr,"defend",attackamt)
playsound(src, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3)
sleep(turn_speed)
player_mp -= pointamt
player_hp += healamt
blocked = TRUE
updateUsrDialog()
arcade_action(usr)
//mainly used to counter short temper and their absurd damage, will deal twice the damage the player took of a non magical attack.
else if(href_list["counter_attack"] && player_mp >= 10)
temp = "<br><center><h3>you prepare yourself to counter the next attack!</h3></center>"
player_mp -= 10
arcade_action(usr,"counter_attack",attackamt)
playsound(src, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3)
else if (href_list["charge"])
blocked = TRUE
var/chargeamt = rand(4,7)
temp = "You regain [chargeamt] points"
playsound(loc, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3)
player_mp += chargeamt
if(turtle > 0)
turtle--
else if(href_list["counter_attack"] && player_mp < 10)
temp = "<br><center><h3>you don't have the mp necessary to counter attack and defend yourself instead</h3></center>"
player_mp += 10
arcade_action(usr,"defend",attackamt)
playsound(src, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3)
updateUsrDialog()
sleep(turn_speed)
arcade_action(usr)
//power attack deals twice the amount of damage but is really expensive MP wise, mainly used with combos to get weakpoints.
else if (href_list["power_attack"] && player_mp >= 20)
temp = "<br><center><h3>You attack [enemy_name] with all your might for [attackamt * 2] damage!</h3></center>"
enemy_hp -= attackamt * 2
player_mp -= 20
arcade_action(usr,"power_attack",attackamt)
else if(href_list["power_attack"] && player_mp < 20)
temp = "<br><center><h3>You don't have the mp necessary for a power attack and settle for a light attack!</h3></center>"
enemy_hp -= attackamt
arcade_action(usr,"attack",attackamt)
if (href_list["close"])
usr.unset_machine()
usr << browse(null, "window=arcade")
else if (href_list["newgame"]) //Reset everything
temp = "New Round"
player_hp = initial(player_hp)
player_mp = initial(player_mp)
enemy_hp = initial(enemy_hp)
enemy_mp = initial(enemy_mp)
gameover = FALSE
turtle = 0
temp = "<br><center><h3>New Round<center><h3>"
if(obj_flags & EMAGGED)
Reset()
obj_flags &= ~EMAGGED
enemy_setup(gamerSkill)
screen_setup(usr)
add_fingerprint(usr)
updateUsrDialog()
return
/obj/machinery/computer/arcade/battle/proc/arcade_action(mob/user)
if ((enemy_mp <= 0) || (enemy_hp <= 0))
///happens after a player action and before the enemy turn. the enemy turn will be cancelled if there's a gameover.
/obj/machinery/computer/arcade/battle/proc/arcade_action(mob/user,player_stance,attackamt)
screen_setup(user)
blocked = TRUE
if(player_stance == "attack" || player_stance == "power_attack")
if(attackamt > 40)
playsound(src, 'sound/arcade/boom.ogg', 50, TRUE, extrarange = -3)
else
playsound(src, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3)
timer_id = addtimer(CALLBACK(src, .proc/enemy_action,player_stance,user),1 SECONDS,TIMER_STOPPABLE)
gameover_check(user)
///the enemy turn, the enemy's action entirely depend on their current passive and a teensy tiny bit of randomness
/obj/machinery/computer/arcade/battle/proc/enemy_action(player_stance,mob/user)
var/list/list_temp = list()
switch(LAZYLEN(last_three_move)) //we keep the last three action of the player in a list here
if(0 to 2)
LAZYADD(last_three_move, player_stance)
if(3)
for(var/i in 1 to 2)
last_three_move[i] = last_three_move[i + 1]
last_three_move[3] = player_stance
if(4 to INFINITY)
last_three_move = null //this shouldn't even happen but we empty the list if it somehow goes above 3
var/enemy_stance
var/attack_amount = rand(8,10) //making the attack amount not vary too much so that it's easier to see if the enemy has a shotgun
if(player_stance == "defend")
attack_amount -= 5
//if emagged, cuban pete will set up a bomb acting up as a timer. when it reaches 0 the player fucking dies
if(obj_flags & EMAGGED)
switch(bomb_cooldown--)
if(18)
list_temp += "<br><center><h3>[enemy_name] takes two valve tank and links them together, what's he planning?<center><h3>"
if(15)
list_temp += "<br><center><h3>[enemy_name] adds a remote control to the tan- ho god is that a bomb?<center><h3>"
if(12)
list_temp += "<br><center><h3>[enemy_name] throws the bomb next to you, you'r too scared to pick it up. <center><h3>"
if(6)
list_temp += "<br><center><h3>[enemy_name]'s hand brushes the remote linked to the bomb, your heart skipped a beat. <center><h3>"
if(2)
list_temp += "<br><center><h3>[enemy_name] is going to press the button! It's now or never! <center><h3>"
if(0)
player_hp -= attack_amount * 1000 //hey it's a maxcap we might as well go all in
//yeah I used the shotgun as a passive, you know why? because the shotgun gives +5 attack which is pretty good
if(LAZYACCESS(enemy_passive, "shotgun"))
if(weakpoint_check("shotgun","defend","defend","power_attack"))
list_temp += "<br><center><h3>You manage to disarm [enemy_name] with a surprise power attack and shoot him with his shotgun until it runs out of ammo! <center><h3> "
enemy_hp -= 10
chosen_weapon = "empty shotgun"
else
attack_amount += 5
//heccing chonker passive, only gives more HP at the start of a new game but has one of the hardest weakpoint to trigger.
if(LAZYACCESS(enemy_passive, "chonker"))
if(weakpoint_check("chonker","power_attack","power_attack","power_attack"))
list_temp += "<br><center><h3>After a lot of power attacks you manage to tip over [enemy_name] as they fall over their enormous weight<center><h3> "
enemy_hp -= 30
//smart passive trait, mainly works in tandem with other traits, makes the enemy unable to be counter_attacked
if(LAZYACCESS(enemy_passive, "smart"))
if(weakpoint_check("smart","defend","defend","attack"))
list_temp += "<br><center><h3>[enemy_name] is confused by your illogical strategy!<center><h3> "
attack_amount -= 5
else if(attack_amount >= player_hp)
player_hp -= attack_amount
list_temp += "<br><center><h3>[enemy_name] figures out you are really close to death and finishes you off with their [chosen_weapon]!<center><h3>"
enemy_stance = "attack"
else if(player_stance == "counter_attack")
list_temp += "<br><center><h3>[enemy_name] is not taking your bait. <center><h3> "
if(LAZYACCESS(enemy_passive, "short_temper"))
list_temp += "However controlling their hatred of you still takes a toll on their mental and physical health!"
enemy_hp -= 5
enemy_mp -= 5
enemy_stance = "defensive"
//short temper passive trait, gets easily baited into being counter attacked but will bypass your counter when low on HP
if(LAZYACCESS(enemy_passive, "short_temper"))
if(weakpoint_check("short_temper","counter_attack","counter_attack","counter_attack"))
list_temp += "<br><center><h3>[enemy_name] is getting frustrated at all your counter attacks and throws a tantrum!<center><h3>"
enemy_hp -= attack_amount
else if(player_stance == "counter_attack")
if(!(LAZYACCESS(enemy_passive, "smart")) && enemy_hp > 30)
list_temp += "<br><center><h3>[enemy_name] took the bait and allowed you to counter attack for [attack_amount * 2] damage!<center><h3>"
player_hp -= attack_amount
enemy_hp -= attack_amount * 2
enemy_stance = "attack"
else if(enemy_hp <= 30) //will break through the counter when low enough on HP even when smart.
list_temp += "<br><center><h3>[enemy_name] is getting tired of your tricks and breaks through your counter with their [chosen_weapon]!<center><h3>"
player_hp -= attack_amount
enemy_stance = "attack"
else if(!enemy_stance)
var/added_temp
if(rand())
added_temp = "you for [attack_amount + 5] damage!"
player_hp -= attack_amount + 5
enemy_stance = "attack"
else
added_temp = "the wall, breaking their skull in the process and losing [attack_amount] hp!" //[enemy_name] you have a literal dent in your skull
enemy_hp -= attack_amount
enemy_stance = "attack"
list_temp += "<br><center><h3>[enemy_name] grits their teeth and charge right into [added_temp]<center><h3>"
//in the case none of the previous passive triggered, Mainly here to set an enemy stance for passives that needs it like the magical passive.
if(!enemy_stance)
enemy_stance = pick("attack","defensive")
if(enemy_stance == "attack")
player_hp -= attack_amount
list_temp += "<br><center><h3>[enemy_name] attacks you for [attack_amount] points of damage with their [chosen_weapon]<center><h3>"
if(player_stance == "counter_attack")
enemy_hp -= attack_amount * 2
list_temp += "<br><center><h3>You counter [enemy_name]'s attack and deal [attack_amount * 2] points of damage!<center><h3>"
if(enemy_stance == "defensive" && enemy_mp < 15)
list_temp += "<br><center><h3>[enemy_name] take some time to get some mp back!<center><h3> "
enemy_mp += attack_amount
else if (enemy_stance == "defensive" && enemy_mp >= 15 && !(LAZYACCESS(enemy_passive, "magical")))
list_temp += "<br><center><h3>[enemy_name] quickly heal themselves for 5 hp!<center><h3> "
enemy_mp -= 15
enemy_hp += 5
//magical passive trait, recharges MP nearly every turn it's not blasting you with magic.
if(LAZYACCESS(enemy_passive, "magical"))
if(player_mp >= 50)
list_temp += "<br><center><h3>the huge amount of magical energy you have acumulated throws [enemy_name] off balance!<center><h3>"
enemy_mp = 0
LAZYREMOVE(enemy_passive, "magical")
pissed_off++
else if(LAZYACCESS(enemy_passive, "smart") && player_stance == "counter_attack" && enemy_mp >= 20)
list_temp += "<br><center><h3>[enemy_name] blasts you with magic from afar for 10 points of damage before you can counter!<center><h3>"
player_hp -= 10
enemy_mp -= 20
else if(enemy_hp >= 20 && enemy_mp >= 40 && enemy_stance == "defensive")
list_temp += "<br><center><h3>[enemy_name] Blasts you with magic from afar!<center><h3>"
enemy_mp -= 40
player_hp -= 30
enemy_stance = "attack"
else if(enemy_hp < 20 && enemy_mp >= 20 && enemy_stance == "defensive") //it's a pretty expensive spell so they can't spam it that much
list_temp += "<br><center><h3>[enemy_name] heal themselves with magic and gain back 20 hp!<center><h3>"
enemy_hp += 20
enemy_mp -= 30
else
list_temp += "<br><center><h3>[enemy_name]'s magical nature lets them get some mp back!<center><h3>"
enemy_mp += attack_amount
//poisonous passive trait, while it's less damage added than the shotgun it acts up even when the enemy doesn't attack at all.
if(LAZYACCESS(enemy_passive, "poisonous"))
if(weakpoint_check("poisonous","attack","attack","attack"))
list_temp += "<br><center><h3>your flurry of attack throws back the poisonnous gas at [enemy_name] and makes them choke on it!<center><h3> "
enemy_hp -= 5
else
list_temp += "<br><center><h3>the stinky breath of [enemy_name] hurts you for 3 hp!<center><h3> "
player_hp -= 3
//if all passive's weakpoint have been triggered, set finishing_move to TRUE
if(pissed_off >= max_passive && !finishing_move)
list_temp += "<br><center><h3>You have weakened [enemy_name] enough for them to show their weak point, you will do 10 times as much damage with your next attack!<center><h3> "
finishing_move = TRUE
playsound(src, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3)
temp = list_temp.Join()
gameover_check(user)
screen_setup(user)
blocked = FALSE
/obj/machinery/computer/arcade/battle/proc/gameover_check(mob/user)
var/xp_gained = 0
if(enemy_hp <= 0)
if(!gameover)
if(timer_id)
deltimer(timer_id)
timer_id = null
if(player_hp <= 0)
player_hp = 1 //let's just pretend the enemy didn't kill you so not both the player and enemy look dead.
gameover = TRUE
temp = "[enemy_name] has fallen! Rejoice!"
playsound(loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3)
blocked = FALSE
temp = "<br><center><h3>[enemy_name] has fallen! Rejoice!<center><h3>"
playsound(loc, 'sound/arcade/win.ogg', 50, TRUE)
if(obj_flags & EMAGGED)
new /obj/effect/spawner/newbomb/timer/syndicate(loc)
@@ -133,78 +402,76 @@
log_game("[key_name(usr)] has outbombed Cuban Pete and been awarded a bomb.")
Reset()
obj_flags &= ~EMAGGED
xp_gained += 100
else
prizevend(user)
xp_gained += 50
SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("win", (obj_flags & EMAGGED ? "emagged":"normal")))
else if ((obj_flags & EMAGGED) && (turtle >= 4))
var/boomamt = rand(5,10)
temp = "[enemy_name] throws a bomb, exploding you for [boomamt] damage!"
playsound(loc, 'sound/arcade/boom.ogg', 50, TRUE, extrarange = -3)
player_hp -= boomamt
else if ((enemy_mp <= 5) && (prob(70)))
var/stealamt = rand(2,3)
temp = "[enemy_name] steals [stealamt] of your power!"
playsound(loc, 'sound/arcade/steal.ogg', 50, TRUE, extrarange = -3)
player_mp -= stealamt
updateUsrDialog()
if (player_mp <= 0)
gameover = TRUE
sleep(turn_speed)
temp = "You have been drained! GAME OVER"
playsound(loc, 'sound/arcade/lose.ogg', 50, TRUE, extrarange = -3)
if(obj_flags & EMAGGED)
usr.gib()
SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("loss", "mana", (obj_flags & EMAGGED ? "emagged":"normal")))
else if ((enemy_hp <= 10) && (enemy_mp > 4))
temp = "[enemy_name] heals for 4 health!"
playsound(loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3)
enemy_hp += 4
enemy_mp -= 4
else
var/attackamt = rand(3,6)
temp = "[enemy_name] attacks for [attackamt] damage!"
playsound(loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3)
player_hp -= attackamt
if ((player_mp <= 0) || (player_hp <= 0))
else if(player_hp <= 0)
if(timer_id)
deltimer(timer_id)
timer_id = null
gameover = TRUE
temp = "You have been crushed! GAME OVER"
playsound(loc, 'sound/arcade/lose.ogg', 50, TRUE, extrarange = -3)
temp = "<br><center><h3>You have been crushed! GAME OVER<center><h3>"
playsound(loc, 'sound/arcade/lose.ogg', 50, TRUE)
xp_gained += 10//pity points
if(obj_flags & EMAGGED)
usr.gib()
var/mob/living/living_user = user
if (istype(living_user))
living_user.gib()
SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("loss", "hp", (obj_flags & EMAGGED ? "emagged":"normal")))
blocked = FALSE
return
// if(gameover)
// user?.mind?.adjust_experience(/datum/skill/gaming, xp_gained+1)//always gain at least 1 point of XP
///used to check if the last three move of the player are the one we want in the right order and if the passive's weakpoint has been triggered yet
/obj/machinery/computer/arcade/battle/proc/weakpoint_check(passive,first_move,second_move,third_move)
if(LAZYLEN(last_three_move) < 3)
return FALSE
if(last_three_move[1] == first_move && last_three_move[2] == second_move && last_three_move[3] == third_move && LAZYACCESS(enemy_passive, passive))
LAZYREMOVE(enemy_passive, passive)
pissed_off++
return TRUE
else
return FALSE
/obj/machinery/computer/arcade/battle/Destroy()
enemy_passive = null
weapons = null
last_three_move = null
return ..() //well boys we did it, lists are no more
/obj/machinery/computer/arcade/battle/examine_more(mob/user)
to_chat(user, "<span class='notice'>Scribbled on the side of the Arcade Machine you notice some writing...\
\nmagical -> >=50 power\
\nsmart -> defend, defend, light attack\
\nshotgun -> defend, defend, power attack\
\nshort temper -> counter, counter, counter\
\npoisonous -> light attack, light attack, light attack\
\nchonker -> power attack, power attack, power attack</span>")
return ..()
var/list/msg = list("<span class='notice'><i>You notice some writing scribbled on the side of [src]...</i></span>")
msg += "\t<span class='info'>smart -> defend, defend, light attack</span>"
msg += "\t<span class='info'>shotgun -> defend, defend, power attack</span>"
msg += "\t<span class='info'>short temper -> counter, counter, counter</span>"
msg += "\t<span class='info'>poisonous -> light attack, light attack, light attack</span>"
msg += "\t<span class='info'>chonker -> power attack, power attack, power attack</span>"
return msg
/obj/machinery/computer/arcade/battle/emag_act(mob/user)
. = ..()
if(obj_flags & EMAGGED)
return
to_chat(user, "<span class='warning'>A mesmerizing Rhumba beat starts playing from the arcade machine's speakers!</span>")
temp = "If you die in the game, you die for real!"
player_hp = 30
player_mp = 10
enemy_hp = 45
enemy_mp = 20
temp = "<br><center><h2>If you die in the game, you die for real!<center><h2>"
max_passive = 6
bomb_cooldown = 18
var/gamerSkill = 0
// if(usr?.mind)
// gamerSkill = usr.mind.get_skill_level(/datum/skill/gaming)
enemy_setup(gamerSkill)
enemy_hp += 100 //extra HP just to make cuban pete even more bullshit
player_hp += 30 //the player will also get a few extra HP in order to have a fucking chance
screen_setup(user)
gameover = FALSE
blocked = FALSE
obj_flags |= EMAGGED

View File

@@ -268,7 +268,7 @@
visible_message("<span class='notice'>[src] dispenses [itemname]!</span>", "<span class='notice'>You hear a chime and a clunk.</span>")
DISABLE_BITFIELD(obj_flags, EMAGGED)
else
var/dope_prizes = (area >= 480) ? list(ARCADE_WEIGHT_RARE) : (area >= 256) ? list(ARCADE_WEIGHT_RARE, ARCADE_WEIGHT_TRICK) : null
var/dope_prizes = (area >= 480) ? 6 : (area >= 256) ? 4 : 2
prizevend(user, dope_prizes)
if(game_status == MINESWEEPER_GAME_WON)

View File

@@ -8,7 +8,7 @@
icon_state = "arcade"
circuit = /obj/item/circuitboard/computer/arcade/amputation
/obj/machinery/computer/arcade/amputation/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
/obj/machinery/computer/arcade/amputation/on_attack_hand(mob/user)
if(!iscarbon(user))
return
var/mob/living/carbon/c_user = user
@@ -24,8 +24,13 @@
var/obj/item/bodypart/chopchop = c_user.get_bodypart(which_hand)
chopchop.dismember()
qdel(chopchop)
playsound(loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3)
for(var/i=1; i<=rand(3,5); i++)
prizevend(user)
// user.mind?.adjust_experience(/datum/skill/gaming, 100)
playsound(loc, 'sound/arcade/win.ogg', 50, TRUE)
prizevend(user, rand(3,5))
else
to_chat(c_user, "<span class='notice'>You (wisely) decide against putting your hand in the machine.</span>")
/obj/machinery/computer/arcade/amputation/festive //dispenses wrapped gifts instead of arcade prizes, also known as the ancap christmas tree
name = "Mediborg's Festive Amputation Adventure"
desc = "A picture of a blood-soaked medical cyborg wearing a Santa hat flashes on the screen. The mediborg has a speech bubble that says, \"Put your hand in the machine if you aren't a <b>coward!</b>\""
prize_override = list(/obj/item/a_gift/anything = 1)

View File

@@ -1,5 +1,3 @@
// *** THE ORION TRAIL ** //
#define ORION_TRAIL_WINTURN 9
@@ -15,6 +13,8 @@
#define ORION_TRAIL_COLLISION "Collision"
#define ORION_TRAIL_SPACEPORT "Spaceport"
#define ORION_TRAIL_BLACKHOLE "BlackHole"
#define ORION_TRAIL_OLDSHIP "Old Ship"
#define ORION_TRAIL_SEARCH "Old Ship Search"
#define ORION_STATUS_START 1
#define ORION_STATUS_NORMAL 2
@@ -44,7 +44,8 @@
ORION_TRAIL_LING = 3,
ORION_TRAIL_MALFUNCTION = 2,
ORION_TRAIL_COLLISION = 1,
ORION_TRAIL_SPACEPORT = 2
ORION_TRAIL_SPACEPORT = 2,
ORION_TRAIL_OLDSHIP = 2
)
var/list/stops = list()
var/list/stopblurbs = list()
@@ -55,13 +56,27 @@
var/gameStatus = ORION_STATUS_START
var/canContinueEvent = 0
var/obj/item/radio/Radio
var/list/gamers = list()
var/killed_crew = 0
/obj/machinery/computer/arcade/orion_trail/Initialize()
. = ..()
Radio = new /obj/item/radio(src)
Radio.listening = 0
/obj/machinery/computer/arcade/orion_trail/Destroy()
QDEL_NULL(Radio)
return ..()
/obj/machinery/computer/arcade/orion_trail/kobayashi
name = "Kobayashi Maru control computer"
desc = "A test for cadets"
icon = 'icons/obj/machines/particle_accelerator.dmi'
icon_state = "control_boxp"
events = list("Raiders" = 3, "Interstellar Flux" = 1, "Illness" = 3, "Breakdown" = 2, "Malfunction" = 2, "Collision" = 1, "Spaceport" = 2)
prizes = list(/obj/item/paper/fluff/holodeck/trek_diploma = 1)
prize_override = list(/obj/item/paper/fluff/holodeck/trek_diploma = 1)
settlers = list("Kirk","Worf","Gene")
/obj/machinery/computer/arcade/orion_trail/Reset()
@@ -96,14 +111,52 @@
event = null
gameStatus = ORION_STATUS_NORMAL
lings_aboard = 0
killed_crew = 0
//spaceport junk
spaceport_raided = 0
spaceport_freebie = 0
last_spaceport_action = ""
/obj/machinery/computer/arcade/orion_trail/ui_interact(mob/user)
/obj/machinery/computer/arcade/orion_trail/proc/report_player(mob/gamer)
if(gamers[gamer] == -2)
return // enough harassing them
if(gamers[gamer] == -1)
say("WARNING: Continued antisocial behavior detected: Dispensing self-help literature.")
new /obj/item/paper/pamphlet/violent_video_games(drop_location())
gamers[gamer]--
return
if(!(gamer in gamers))
gamers[gamer] = 0
gamers[gamer]++ // How many times the player has 'prestiged' (massacred their crew)
if(gamers[gamer] > 2 && prob(20 * gamers[gamer]))
Radio.set_frequency(FREQ_SECURITY)
Radio.talk_into(src, "SECURITY ALERT: Crewmember [gamer] recorded displaying antisocial tendencies in [get_area(src)]. Please watch for violent behavior.", FREQ_SECURITY)
Radio.set_frequency(FREQ_MEDICAL)
Radio.talk_into(src, "PSYCH ALERT: Crewmember [gamer] recorded displaying antisocial tendencies in [get_area(src)]. Please schedule psych evaluation.", FREQ_MEDICAL)
gamers[gamer] = -1
gamer.client.give_award(/datum/award/achievement/misc/gamer, gamer) // PSYCH REPORT NOTE: patient kept rambling about how they did it for an "achievement", recommend continued holding for observation
// gamer.mind?.adjust_experience(/datum/skill/gaming, 50) // cheevos make u better
if(!isnull(GLOB.data_core.general))
for(var/datum/data/record/R in GLOB.data_core.general)
if(R.fields["name"] == gamer.name)
R.fields["m_stat"] = "*Unstable*"
return
/obj/machinery/computer/arcade/orion_trail/ui_interact(mob/_user)
. = ..()
if (!isliving(_user))
return
var/mob/living/user = _user
if(fuel <= 0 || food <=0 || settlers.len == 0)
gameStatus = ORION_STATUS_GAMEOVER
event = null
@@ -136,6 +189,8 @@
desc = "Learn how our ancestors got to Orion, and have fun in the process!"
dat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];menu=1'>May They Rest In Peace</a></P>"
// user?.mind?.adjust_experience(/datum/skill/gaming, 10)//learning from your mistakes is the first rule of roguelikes
else if(event)
dat = eventdat
else if(gameStatus == ORION_STATUS_NORMAL)
@@ -174,20 +229,32 @@
return
busy = TRUE
var/gamerSkillLevel = 0
var/gamerSkill = 0
var/gamerSkillRands = 0
// if(usr?.mind)
// gamerSkillLevel = usr.mind.get_skill_level(/datum/skill/gaming)
// gamerSkill = usr.mind.get_skill_modifier(/datum/skill/gaming, SKILL_PROBS_MODIFIER)
// gamerSkillRands = usr.mind.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER)
var/xp_gained = 0
if (href_list["continue"]) //Continue your travels
if(gameStatus == ORION_STATUS_NORMAL && !event && turns != 7)
if(turns >= ORION_TRAIL_WINTURN)
win(usr)
xp_gained += 34
else
food -= (alive+lings_aboard)*2
fuel -= 5
if(turns == 2 && prob(30))
if(turns == 2 && prob(30-gamerSkill))
event = ORION_TRAIL_COLLISION
event()
else if(prob(75))
else if(prob(75-gamerSkill))
event = pickweight(events)
if(lings_aboard)
if(event == ORION_TRAIL_LING || prob(55))
if(event == ORION_TRAIL_LING || prob(55-gamerSkill))
event = ORION_TRAIL_LING_ATTACK
event()
turns += 1
@@ -195,15 +262,18 @@
var/mob/living/carbon/M = usr //for some vars
switch(event)
if(ORION_TRAIL_RAIDERS)
if(prob(50))
if(prob(50-gamerSkill))
to_chat(usr, "<span class='userdanger'>You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?</span>")
M.hallucination += 30
else
to_chat(usr, "<span class='userdanger'>Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there...</span>")
M.take_bodypart_damage(30)
playsound(loc, 'sound/weapons/genhit2.ogg', 100, 1)
playsound(loc, 'sound/weapons/genhit2.ogg', 100, TRUE)
if(ORION_TRAIL_ILLNESS)
var/severity = rand(1,3) //pray to RNGesus. PRAY, PIGS
var/maxSeverity = 3
// if(gamerSkillLevel >= SKILL_LEVEL_EXPERT)
// maxSeverity = 2 //part of gitting gud is rng mitigation
var/severity = rand(1,maxSeverity) //pray to RNGesus. PRAY, PIGS
if(severity == 1)
to_chat(M, "<span class='userdanger'>You suddenly feel slightly nauseated.</span>" )
if(severity == 2)
@@ -215,16 +285,16 @@
sleep(30)
M.vomit(10, distance = 5)
if(ORION_TRAIL_FLUX)
if(prob(75))
if(prob(75-gamerSkill))
M.DefaultCombatKnockdown(60)
say("A sudden gust of powerful wind slams [M] into the floor!")
M.take_bodypart_damage(25)
playsound(loc, 'sound/weapons/genhit.ogg', 100, 1)
playsound(loc, 'sound/weapons/genhit.ogg', 100, TRUE)
else
to_chat(M, "<span class='userdanger'>A violent gale blows past you, and you barely manage to stay standing!</span>")
if(ORION_TRAIL_COLLISION) //by far the most damaging event
if(prob(90))
playsound(loc, 'sound/effects/bang.ogg', 100, 1)
if(prob(90-gamerSkill))
playsound(loc, 'sound/effects/bang.ogg', 100, TRUE)
var/turf/open/floor/F
for(F in orange(1, src))
F.ScrapeAway()
@@ -232,15 +302,15 @@
if(hull)
sleep(10)
say("A new floor suddenly appears around [src]. What the hell?")
playsound(loc, 'sound/weapons/genhit.ogg', 100, 1)
playsound(loc, 'sound/weapons/genhit.ogg', 100, TRUE)
var/turf/open/space/T
for(T in orange(1, src))
T.PlaceOnTop(/turf/open/floor/plating)
else
say("Something slams into the floor around [src] - luckily, it didn't get through!")
playsound(loc, 'sound/effects/bang.ogg', 50, 1)
playsound(loc, 'sound/effects/bang.ogg', 50, TRUE)
if(ORION_TRAIL_MALFUNCTION)
playsound(loc, 'sound/effects/empulse.ogg', 50, 1)
playsound(loc, 'sound/effects/empulse.ogg', 50, TRUE)
visible_message("<span class='danger'>[src] malfunctions, randomizing in-game stats!</span>")
var/oldfood = food
var/oldfuel = fuel
@@ -254,7 +324,7 @@
audible_message("<span class='danger'>[src] lets out a somehow ominous chime.</span>")
food = oldfood
fuel = oldfuel
playsound(loc, 'sound/machines/chime.ogg', 50, 1)
playsound(loc, 'sound/machines/chime.ogg', 50, TRUE)
else if(href_list["newgame"]) //Reset everything
if(gameStatus == ORION_STATUS_START)
@@ -266,6 +336,10 @@
food = 80
fuel = 60
settlers = list("Harry","Larry","Bob")
else if(href_list["search"]) //search old ship
if(event == ORION_TRAIL_OLDSHIP)
event = ORION_TRAIL_SEARCH
event()
else if(href_list["slow"]) //slow down
if(event == ORION_TRAIL_FLUX)
food -= (alive+lings_aboard)*2
@@ -302,11 +376,11 @@
event = null
else if(href_list["blackhole"]) //keep speed past a black hole
if(turns == 7)
if(prob(75))
if(prob(75-gamerSkill))
event = ORION_TRAIL_BLACKHOLE
event()
if(obj_flags & EMAGGED)
playsound(loc, 'sound/effects/supermatter.ogg', 100, 1)
playsound(loc, 'sound/effects/supermatter.ogg', 100, TRUE)
say("A miniature black hole suddenly appears in front of [src], devouring [usr] alive!")
if(isliving(usr))
var/mob/living/L = usr
@@ -328,22 +402,29 @@
else if(href_list["killcrew"]) //shoot a crewmember
if(gameStatus == ORION_STATUS_NORMAL || event == ORION_TRAIL_LING)
var/sheriff = remove_crewmember() //I shot the sheriff
playsound(loc,'sound/weapons/gunshot.ogg', 100, 1)
playsound(loc,'sound/weapons/gun/pistol/shot.ogg', 100, TRUE)
killed_crew++
var/mob/living/user = usr
if(settlers.len == 0 || alive == 0)
say("The last crewmember [sheriff], shot themselves, GAME OVER!")
if(obj_flags & EMAGGED)
usr.death(0)
obj_flags &= EMAGGED
user.death(FALSE)
gameStatus = ORION_STATUS_GAMEOVER
event = null
if(killed_crew >= 4)
xp_gained -= 15//no cheating by spamming game overs
report_player(usr)
else if(obj_flags & EMAGGED)
if(usr.name == sheriff)
say("The crew of the ship chose to kill [usr.name]!")
usr.death(0)
user.death(FALSE)
if(event == ORION_TRAIL_LING) //only ends the ORION_TRAIL_LING event, since you can do this action in multiple places
event = null
killed_crew-- // the kill was valid
//Spaceport specific interactions
//they get a header because most of them don't reset event (because it's a shop, you leave when you want to)
@@ -356,6 +437,7 @@
fuel -= 10
food -= 10
event()
killed_crew-- // I mean not really but you know
else if(href_list["sellcrew"]) //sell a crewmember
if(gameStatus == ORION_STATUS_MARKET)
@@ -377,15 +459,16 @@
else if(href_list["raid_spaceport"])
if(gameStatus == ORION_STATUS_MARKET)
if(!spaceport_raided)
var/success = min(15 * alive,100) //default crew (4) have a 60% chance
var/success = min(15 * alive + gamerSkill,100) //default crew (4) have a 60% chance
spaceport_raided = 1
var/FU = 0
var/FO = 0
if(prob(success))
FU = rand(5,15)
FO = rand(5,15)
FU = rand(5 + gamerSkillRands,15 + gamerSkillRands)
FO = rand(5 + gamerSkillRands,15 + gamerSkillRands)
last_spaceport_action = "You successfully raided the spaceport! You gained [FU] Fuel and [FO] Food! (+[FU]FU,+[FO]FO)"
xp_gained += 10
else
FU = rand(-5,-15)
FO = rand(-5,-15)
@@ -444,7 +527,7 @@
add_fingerprint(usr)
updateUsrDialog()
busy = FALSE
return
// usr?.mind?.adjust_experience(/datum/skill/gaming, xp_gained+1)
/obj/machinery/computer/arcade/orion_trail/proc/event()
@@ -686,8 +769,279 @@
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];leave_spaceport=1'>Depart Spaceport</a></P>"
/obj/machinery/computer/arcade/orion_trail/proc/event()
eventdat = "<center><h1>[event]</h1></center>"
canContinueEvent = 0
switch(event)
if(ORION_TRAIL_RAIDERS)
eventdat += "Raiders have come aboard your ship!"
if(prob(50))
var/sfood = rand(1,10)
var/sfuel = rand(1,10)
food -= sfood
fuel -= sfuel
eventdat += "<br>They have stolen [sfood] <b>Food</b> and [sfuel] <b>Fuel</b>."
else if(prob(10))
var/deadname = remove_crewmember()
eventdat += "<br>[deadname] tried to fight back, but was killed."
else
eventdat += "<br>Fortunately, you fended them off without any trouble."
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];eventclose=1'>Continue</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
canContinueEvent = 1
if(ORION_TRAIL_FLUX)
eventdat += "This region of space is highly turbulent. <br>If we go slowly we may avoid more damage, but if we keep our speed we won't waste supplies."
eventdat += "<br>What will you do?"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];slow=1'>Slow Down</a> <a href='byond://?src=[REF(src)];keepspeed=1'>Continue</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
if(ORION_TRAIL_OLDSHIP)
eventdat += "<br>Your crew spots an old ship floating through space. It might have some supplies, but then again it looks rather unsafe."
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];search=1'>Search it</a><a href='byond://?src=[REF(src)];eventclose=1'>Leave it</a></P><P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
canContinueEvent = 1
if(ORION_TRAIL_SEARCH)
switch(rand(100))
if(0 to 15)
var/rescued = add_crewmember()
var/oldfood = rand(1,7)
var/oldfuel = rand(4,10)
food += oldfood
fuel += oldfuel
eventdat += "<br>As you look through it you find some supplies and a living person!"
eventdat += "<br>[rescued] was rescued from the abandoned ship!"
eventdat += "<br>You found [oldfood] <b>Food</b> and [oldfuel] <b>Fuel</b>."
if(15 to 35)
var/lfuel = rand(4,7)
var/deadname = remove_crewmember()
fuel -= lfuel
eventdat += "<br>[deadname] was lost deep in the wreckage, and your own vessel lost [lfuel] <b>Fuel</b> maneuvering to the the abandoned ship."
if(35 to 65)
var/oldfood = rand(5,11)
food += oldfood
engine++
eventdat += "<br>You found [oldfood] <b>Food</b> and some parts amongst the wreck."
else
eventdat += "<br>As you look through the wreck you cannot find much of use."
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];eventclose=1'>Continue</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
canContinueEvent = 1
if(ORION_TRAIL_ILLNESS)
eventdat += "A deadly illness has been contracted!"
var/deadname = remove_crewmember()
eventdat += "<br>[deadname] was killed by the disease."
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];eventclose=1'>Continue</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
canContinueEvent = 1
if(ORION_TRAIL_BREAKDOWN)
eventdat += "Oh no! The engine has broken down!"
eventdat += "<br>You can repair it with an engine part, or you can make repairs for 3 days."
if(engine >= 1)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];useengine=1'>Use Part</a><a href='byond://?src=[REF(src)];wait=1'>Wait</a></P>"
else
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];wait=1'>Wait</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
if(ORION_TRAIL_MALFUNCTION)
eventdat += "The ship's systems are malfunctioning!"
eventdat += "<br>You can replace the broken electronics with spares, or you can spend 3 days troubleshooting the AI."
if(electronics >= 1)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];useelec=1'>Use Part</a><a href='byond://?src=[REF(src)];wait=1'>Wait</a></P>"
else
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];wait=1'>Wait</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
if(ORION_TRAIL_COLLISION)
eventdat += "Something hit us! Looks like there's some hull damage."
if(prob(25))
var/sfood = rand(5,15)
var/sfuel = rand(5,15)
food -= sfood
fuel -= sfuel
eventdat += "<br>[sfood] <b>Food</b> and [sfuel] <b>Fuel</b> was vented out into space."
if(prob(10))
var/deadname = remove_crewmember()
eventdat += "<br>[deadname] was killed by rapid depressurization."
eventdat += "<br>You can repair the damage with hull plates, or you can spend the next 3 days welding scrap together."
if(hull >= 1)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];usehull=1'>Use Part</a><a href='byond://?src=[REF(src)];wait=1'>Wait</a></P>"
else
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];wait=1'>Wait</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
if(ORION_TRAIL_BLACKHOLE)
eventdat += "You were swept away into the black hole."
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];holedeath=1'>Oh...</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
settlers = list()
if(ORION_TRAIL_LING)
eventdat += "Strange reports warn of changelings infiltrating crews on trips to Orion..."
if(settlers.len <= 2)
eventdat += "<br>Your crew's chance of reaching Orion is so slim the changelings likely avoided your ship..."
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];eventclose=1'>Continue</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
if(prob(10)) // "likely", I didn't say it was guaranteed!
lings_aboard = min(++lings_aboard,2)
else
if(lings_aboard) //less likely to stack lings
if(prob(20))
lings_aboard = min(++lings_aboard,2)
else if(prob(70))
lings_aboard = min(++lings_aboard,2)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];killcrew=1'>Kill a Crewmember</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];eventclose=1'>Risk it</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
canContinueEvent = 1
if(ORION_TRAIL_LING_ATTACK)
if(lings_aboard <= 0) //shouldn't trigger, but hey.
eventdat += "Haha, fooled you, there are no changelings on board!"
eventdat += "<br>(You should report this to a coder :S)"
else
var/ling1 = remove_crewmember()
var/ling2 = ""
if(lings_aboard >= 2)
ling2 = remove_crewmember()
eventdat += "Changelings among your crew suddenly burst from hiding and attack!"
if(ling2)
eventdat += "<br>[ling1] and [ling2]'s arms twist and contort into grotesque blades!"
else
eventdat += "<br>[ling1]'s arm twists and contorts into a grotesque blade!"
var/chance2attack = alive*20
if(prob(chance2attack))
var/chancetokill = 30*lings_aboard-(5*alive) //eg: 30*2-(10) = 50%, 2 lings, 2 crew is 50% chance
if(prob(chancetokill))
var/deadguy = remove_crewmember()
var/murder_text = pick("The changeling[ling2 ? "s" : ""] bring[ling2 ? "" : "s"] down [deadguy] and disembowel[ling2 ? "" : "s"] them in a spray of gore!", \
"[ling2 ? pick(ling1, ling2) : ling1] corners [deadguy] and impales them through the stomach!", \
"[ling2 ? pick(ling1, ling2) : ling1] decapitates [deadguy] in a single cleaving arc!")
eventdat += "<br>[murder_text]"
else
eventdat += "<br><br><b>You valiantly fight off the changeling[ling2 ? "s":""]!</b>"
if(ling2)
food += 30
lings_aboard = max(0,lings_aboard-2)
else
food += 15
lings_aboard = max(0,--lings_aboard)
eventdat += "<br><i>Well, it's perfectly good food...</i>\
<br>You cut the changeling[ling2 ? "s" : ""] into meat, gaining <b>[ling2 ? "30" : "15"]</b> Food!"
else
eventdat += "<br><br>[pick("Sensing unfavorable odds", "After a failed attack", "Suddenly breaking nerve")], \
the changeling[ling2 ? "s":""] vanish[ling2 ? "" : "es"] into space through the airlocks! You're safe... for now."
if(ling2)
lings_aboard = max(0,lings_aboard-2)
else
lings_aboard = max(0,--lings_aboard)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];eventclose=1'>Continue</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
canContinueEvent = 1
if(ORION_TRAIL_SPACEPORT)
gameStatus = ORION_STATUS_MARKET
if(spaceport_raided)
eventdat += "The spaceport is on high alert! You've been barred from docking by the local authorities after your failed raid."
if(last_spaceport_action)
eventdat += "<br><b>Last Spaceport Action:</b> [last_spaceport_action]"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];leave_spaceport=1'>Depart Spaceport</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
else
eventdat += "Your jump into the sector yields a spaceport - a lucky find!"
eventdat += "<br>This spaceport is home to travellers who failed to reach Orion, but managed to find a different home..."
eventdat += "<br>Trading terms: FU = Fuel, FO = Food"
if(last_spaceport_action)
eventdat += "<br><b>Last action:</b> [last_spaceport_action]"
eventdat += "<h3><b>Crew:</b></h3>"
eventdat += english_list(settlers)
eventdat += "<br><b>Food: </b>[food] | <b>Fuel: </b>[fuel]"
eventdat += "<br><b>Engine Parts: </b>[engine] | <b>Hull Panels: </b>[hull] | <b>Electronics: </b>[electronics]"
//If your crew is pathetic you can get freebies (provided you haven't already gotten one from this port)
if(!spaceport_freebie && (fuel < 20 || food < 20))
spaceport_freebie++
var/FU = 10
var/FO = 10
var/freecrew = 0
if(prob(30))
FU = 25
FO = 25
if(prob(10))
add_crewmember()
freecrew++
eventdat += "<br>The traders of the spaceport take pity on you, and generously give you some free supplies! (+[FU]FU, +[FO]FO)"
if(freecrew)
eventdat += "<br>You also gain a new crewmember!"
fuel += FU
food += FO
//CREW INTERACTIONS
eventdat += "<P ALIGN=Right>Crew Management:</P>"
//Buy crew
if(food >= 10 && fuel >= 10)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];buycrew=1'>Hire a New Crewmember (-10FU, -10FO)</a></P>"
else
eventdat += "<P ALIGN=Right>You cannot afford a new crewmember.</P>"
//Sell crew
if(settlers.len > 1)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];sellcrew=1'>Sell Crew for Fuel and Food (+7FU, +7FO)</a></P>"
else
eventdat += "<P ALIGN=Right>You have no other crew to sell.</P>"
//BUY/SELL STUFF
eventdat += "<P ALIGN=Right>Spare Parts:</P>"
//Engine parts
if(fuel > 5)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];buyparts=1'>Buy Engine Parts (-5FU)</a></P>"
else
eventdat += "<P ALIGN=Right>You cannot afford engine parts.</a>"
//Hull plates
if(fuel > 5)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];buyparts=2'>Buy Hull Plates (-5FU)</a></P>"
else
eventdat += "<P ALIGN=Right>You cannot afford hull plates.</a>"
//Electronics
if(fuel > 5)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];buyparts=3'>Buy Spare Electronics (-5FU)</a></P>"
else
eventdat += "<P ALIGN=Right>You cannot afford spare electronics.</a>"
//Trade
if(fuel > 5)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];trade=1'>Trade Fuel for Food (-5FU,+5FO)</a></P>"
else
eventdat += "<P ALIGN=Right>You don't have 5FU to trade.</P"
if(food > 5)
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];trade=2'>Trade Food for Fuel (+5FU,-5FO)</a></P>"
else
eventdat += "<P ALIGN=Right>You don't have 5FO to trade.</P"
//Raid the spaceport
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];raid_spaceport=1'>!! Raid Spaceport !!</a></P>"
eventdat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];leave_spaceport=1'>Depart Spaceport</a></P>"
//Add Random/Specific crewmember
/obj/machinery/computer/arcade/orion_trail/proc/add_crewmember(var/specific = "")
/obj/machinery/computer/arcade/orion_trail/proc/add_crewmember(specific = "")
var/newcrew = ""
if(specific)
newcrew = specific
@@ -703,7 +1057,7 @@
//Remove Random/Specific crewmember
/obj/machinery/computer/arcade/orion_trail/proc/remove_crewmember(var/specific = "", var/dont_remove = "")
/obj/machinery/computer/arcade/orion_trail/proc/remove_crewmember(specific = "", dont_remove = "")
var/list/safe2remove = settlers
var/removed = ""
if(dont_remove)
@@ -779,14 +1133,14 @@
to_chat(user, "<span class='warning'>You flip the switch on the underside of [src].</span>")
active = 1
visible_message("<span class='notice'>[src] softly beeps and whirs to life!</span>")
playsound(loc, 'sound/machines/defib_SaftyOn.ogg', 25, 1)
playsound(loc, 'sound/machines/defib_SaftyOn.ogg', 25, TRUE)
say("This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.")
sleep(20)
visible_message("<span class='warning'>[src] begins to vibrate...</span>")
say("Uh, Port? Having some issues with our reactor, could you check it out? Over.")
sleep(30)
say("Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-")
playsound(loc, 'sound/machines/buzz-sigh.ogg', 25, 1)
playsound(loc, 'sound/machines/buzz-sigh.ogg', 25, TRUE)
sleep(3.6)
visible_message("<span class='userdanger'>[src] explodes!</span>")
explosion(loc, 2,4,8, flame_range = 16)

View File

@@ -12,7 +12,7 @@
lefthand_file = 'icons/mob/inhands/equipment/toolbox_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/toolbox_righthand.dmi'
icon = 'icons/obj/items_and_weapons.dmi'
w_class = WEIGHT_CLASS_BULKY
w_class = WEIGHT_CLASS_GIGANTIC
force = 12
total_mass = TOTAL_MASS_NORMAL_ITEM // average toolbox
attack_verb = list("robusted")
@@ -70,19 +70,19 @@
else
. += "<span class='his_grace'>[src] is latched closed.</span>"
/obj/item/his_grace/relaymove(mob/living/user) //Allows changelings, etc. to climb out of Him after they revive, provided He isn't active
/obj/item/his_grace/relaymove(mob/living/user, direction) //Allows changelings, etc. to climb out of Him after they revive, provided He isn't active
if(!awakened)
user.forceMove(get_turf(src))
user.visible_message("<span class='warning'>[user] scrambles out of [src]!</span>", "<span class='notice'>You climb out of [src]!</span>")
/obj/item/his_grace/process()
/obj/item/his_grace/process(delta_time)
if(!bloodthirst)
drowse()
return
if(bloodthirst < HIS_GRACE_CONSUME_OWNER && !ascended)
adjust_bloodthirst(1 + FLOOR(LAZYLEN(contents) * 0.5, 1)) //Maybe adjust this?
adjust_bloodthirst((1 + FLOOR(LAZYLEN(contents) * 0.5, 1)) * delta_time) //Maybe adjust this?
else
adjust_bloodthirst(1) //don't cool off rapidly once we're at the point where His Grace consumes all.
adjust_bloodthirst(1 * delta_time) //don't cool off rapidly once we're at the point where His Grace consumes all.
var/mob/living/master = get_atom_on_turf(src, /mob/living)
if(istype(master) && (src in master.held_items))
switch(bloodthirst)
@@ -94,7 +94,7 @@
REMOVE_TRAIT(src, TRAIT_NODROP, HIS_GRACE_TRAIT)
master.DefaultCombatKnockdown(60)
master.adjustBruteLoss(master.maxHealth)
playsound(master, 'sound/effects/splat.ogg', 100, 0)
playsound(master, 'sound/effects/splat.ogg', 100, FALSE)
else
master.apply_status_effect(STATUS_EFFECT_HISGRACE)
return
@@ -115,8 +115,8 @@
if(!L.stat)
L.visible_message("<span class='warning'>[src] lunges at [L]!</span>", "<span class='his_grace big bold'>[src] lunges at you!</span>")
do_attack_animation(L, null, src)
playsound(L, 'sound/weapons/smash.ogg', 50, 1)
playsound(L, 'sound/misc/desceration-01.ogg', 50, 1)
playsound(L, 'sound/weapons/smash.ogg', 50, TRUE)
playsound(L, 'sound/misc/desceration-01.ogg', 50, TRUE)
L.adjustBruteLoss(force)
adjust_bloodthirst(-5) //Don't stop attacking they're right there!
else
@@ -137,6 +137,8 @@
move_gracefully()
/obj/item/his_grace/proc/move_gracefully()
SIGNAL_HANDLER
if(!awakened)
return
var/static/list/transforms
@@ -161,7 +163,7 @@
return
var/turf/T = get_turf(src)
T.visible_message("<span class='boldwarning'>[src] slowly stops rattling and falls still, His latch snapping shut.</span>")
playsound(loc, 'sound/weapons/batonextend.ogg', 100, 1)
playsound(loc, 'sound/weapons/batonextend.ogg', 100, TRUE)
name = initial(name)
desc = initial(desc)
icon_state = initial(icon_state)
@@ -178,8 +180,8 @@
var/victims = 0
meal.visible_message("<span class='warning'>[src] swings open and devours [meal]!</span>", "<span class='his_grace big bold'>[src] consumes you!</span>")
meal.adjustBruteLoss(200)
playsound(meal, 'sound/misc/desceration-02.ogg', 75, 1)
playsound(src, 'sound/items/eatfood.ogg', 100, 1)
playsound(meal, 'sound/misc/desceration-02.ogg', 75, TRUE)
playsound(src, 'sound/items/eatfood.ogg', 100, TRUE)
meal.forceMove(src)
force_bonus += HIS_GRACE_FORCE_BONUS
prev_bloodthirst = bloodthirst
@@ -253,3 +255,4 @@
if(istype(master))
master.visible_message("<span class='his_grace big bold'>Gods will be watching.</span>")
name = "[master]'s mythical toolbox of three powers"
master.client?.give_award(/datum/award/achievement/misc/ascension, master)

View File

@@ -24,9 +24,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
*/
/obj/item/banhammer/attack(mob/M, mob/user)
if(user.zone_selected == BODY_ZONE_HEAD)
M.visible_message("<span class='danger'>[user] are stroking the head of [M] with a bangammer</span>", "<span class='userdanger'>[user] are stroking the head with a bangammer</span>", "you hear a bangammer stroking a head");
M.visible_message("<span class='danger'>[user] are stroking the head of [M] with a bangammer.</span>", "<span class='userdanger'>[user] are stroking your head with a bangammer.</span>", "<span class='hear'>You hear a bangammer stroking a head.</span>") // see above comment
else
M.visible_message("<span class='danger'>[M] has been banned FOR NO REISIN by [user]</span>", "<span class='userdanger'>You have been banned FOR NO REISIN by [user]</span>", "you hear a banhammer banning someone")
M.visible_message("<span class='danger'>[M] has been banned FOR NO REISIN by [user]!</span>", "<span class='userdanger'>You have been banned FOR NO REISIN by [user]!</span>", "<span class='hear'>You hear a banhammer banning someone.</span>")
playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much
if(user.a_intent != INTENT_HELP)
return ..(M, user)
@@ -89,7 +89,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/claymore/highlander //ALL COMMENTS MADE REGARDING THIS SWORD MUST BE MADE IN ALL CAPS
desc = "<b><i>THERE CAN BE ONLY ONE, AND IT WILL BE YOU!!!</i></b>\nActivate it in your hand to point to the nearest victim."
flags_1 = CONDUCT_1
item_flags = DROPDEL
item_flags = DROPDEL //WOW BRO YOU LOST AN ARM, GUESS WHAT YOU DONT GET YOUR SWORD ANYMORE //I CANT BELIEVE SPOOKYDONUT WOULD BREAK THE REQUIREMENTS
slot_flags = null
block_chance = 0 //RNG WON'T HELP YOU NOW, PANSY
light_range = 3
@@ -130,8 +130,6 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/claymore/highlander/dropped(mob/living/user)
. = ..()
user.unignore_slowdown(HIGHLANDER)
if(!QDELETED(src))
qdel(src) //If this ever happens, it's because you lost an arm
/obj/item/claymore/highlander/examine(mob/user)
. = ..()
@@ -141,8 +139,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/claymore/highlander/attack(mob/living/target, mob/living/user)
. = ..()
if(!QDELETED(target) && iscarbon(target) && target.stat == DEAD && target.mind && target.mind.special_role == "highlander")
user.fully_heal() //STEAL THE LIFE OF OUR FALLEN FOES
if(!QDELETED(target) && target.stat == DEAD && target.mind && target.mind.special_role == "highlander")
user.fully_heal(admin_revive = FALSE) //STEAL THE LIFE OF OUR FALLEN FOES
add_notch(user)
target.visible_message("<span class='warning'>[target] crumbles to dust beneath [user]'s blows!</span>", "<span class='userdanger'>As you fall, your body crumbles to dust!</span>")
target.dust()
@@ -150,9 +148,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/claymore/highlander/attack_self(mob/living/user)
var/closest_victim
var/closest_distance = 255
for(var/mob/living/carbon/human/H in GLOB.player_list - user)
if(H.client && H.mind.special_role == "highlander" && (!closest_victim || get_dist(user, closest_victim) < closest_distance))
closest_victim = H
for(var/mob/living/carbon/human/scot in GLOB.player_list - user)
if(scot.mind.special_role == "highlander" && (!closest_victim || get_dist(user, closest_victim) < closest_distance))
closest_victim = scot
for(var/mob/living/silicon/robot/siliscot in GLOB.player_list - user)
if(siliscot.mind.special_role == "highlander" && (!closest_victim || get_dist(user, closest_victim) < closest_distance))
closest_victim = siliscot
if(!closest_victim)
to_chat(user, "<span class='warning'>[src] thrums for a moment and falls dark. Perhaps there's nobody nearby.</span>")
return
@@ -161,7 +163,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/claymore/highlander/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
if((attack_type & ATTACK_TYPE_PROJECTILE) && is_energy_reflectable_projectile(object))
return BLOCK_SUCCESS | BLOCK_SHOULD_REDIRECT | BLOCK_PHYSICAL_EXTERNAL | BLOCK_REDIRECTED
return ..()
return ..() //YOU THINK YOUR PUNY LASERS CAN STOP ME?
/obj/item/claymore/highlander/proc/add_notch(mob/living/user) //DYNAMIC CLAYMORE PROGRESSION SYSTEM - THIS IS THE FUTURE
notches++
@@ -214,7 +216,22 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
remove_atom_colour(ADMIN_COLOUR_PRIORITY)
name = new_name
playsound(user, 'sound/items/screwdriver2.ogg', 50, 1)
playsound(user, 'sound/items/screwdriver2.ogg', 50, TRUE)
/obj/item/claymore/highlander/robot //BLOODTHIRSTY BORGS NOW COME IN PLAID
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "claymore_cyborg"
var/mob/living/silicon/robot/robot
/obj/item/claymore/highlander/robot/Initialize()
var/obj/item/robot_module/kiltkit = loc
robot = kiltkit.loc
if(!istype(robot))
qdel(src)
return ..()
/obj/item/claymore/highlander/robot/process()
loc.layer = LARGE_MOB_LAYER
/obj/item/katana
name = "katana"
@@ -227,7 +244,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK
force = 40
throwforce = 10
w_class = WEIGHT_CLASS_BULKY
w_class = WEIGHT_CLASS_HUGE
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
block_chance = 50
@@ -417,7 +434,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
desc = "A misnomer of sorts, this is effectively a blunt katana made from steelwood, a dense organic wood derived from steelcaps. Why steelwood? Druids can use it. Duh."
icon_state = "bokken_steel"
item_state = "bokken_steel"
force = 12
force = 12
stamina_damage_increment = 3
/obj/item/melee/bokken/waki
@@ -427,7 +444,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
item_state = "wakibokken"
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_NORMAL
force = 6
force = 6
stamina_damage_increment = 4
block_parry_data = /datum/block_parry_data/bokken/waki
default_parry_data = /datum/block_parry_data/bokken/waki
@@ -442,7 +459,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
parry_time_perfect_leeway = 1
parry_imperfect_falloff_percent = 7.5
parry_efficiency_to_counterattack = 120
parry_efficiency_considered_successful = 65
parry_efficiency_considered_successful = 65
parry_efficiency_perfect = 120
parry_efficiency_perfect_override = list(
TEXT_ATTACK_TYPE_PROJECTILE = 30,
@@ -455,10 +472,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/datum/block_parry_data/bokken/waki/quick_parry //For the parry spammer in you
parry_stamina_cost = 2 // Slam that parry button
parry_time_active = 2.5
parry_time_perfect = 1
parry_time_perfect = 1
parry_time_perfect_leeway = 1
parry_failed_stagger_duration = 1 SECONDS
parry_failed_clickcd_duration = 1 SECONDS
parry_failed_clickcd_duration = 1 SECONDS
/datum/block_parry_data/bokken/waki/quick_parry/proj
parry_efficiency_perfect_override = list()
@@ -468,7 +485,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
desc = "A misnomer of sorts, this is effectively a blunt wakizashi made from steelwood, a dense organic wood derived from steelcaps. Why steelwood? Druids can use it. Duh."
icon_state = "wakibokken_steel"
item_state = "wakibokken_steel"
force = 8
force = 8
stamina_damage_increment = 2
/obj/item/melee/bokken/debug
@@ -575,7 +592,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
user.put_in_hands(S)
to_chat(user, "<span class='notice'>You fasten the glass shard to the top of the rod with the cable.</span>")
else if(istype(I, /obj/item/assembly/igniter) && !HAS_TRAIT(I, TRAIT_NODROP))
else if(istype(I, /obj/item/assembly/igniter) && !(HAS_TRAIT(I, TRAIT_NODROP)))
var/obj/item/melee/baton/cattleprod/P = new /obj/item/melee/baton/cattleprod
remove_item_from_storage(user)
@@ -598,13 +615,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi'
force = 2
throwforce = 10 //This is never used on mobs since this has a 100% embed chance.
throwforce = 10 //10 + 2 (WEIGHT_CLASS_SMALL) * 4 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = 18 damage on hit due to guaranteed embedding
throw_speed = 4
embedding = list("pain_mult" = 4, "embed_chance" = 100, "fall_chance" = 0, "embed_chance_turf_mod" = 15)
armour_penetration = 40
w_class = WEIGHT_CLASS_SMALL
sharpness = SHARP_EDGED
sharpness = SHARP_POINTY
custom_materials = list(/datum/material/iron=500, /datum/material/glass=500)
resistance_flags = FIRE_PROOF
@@ -639,27 +656,23 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
attack_verb = list("stubbed", "poked")
resistance_flags = FIRE_PROOF
var/extended = 0
var/extended_force = 20
var/extended_throwforce = 23
var/extended_icon_state = "switchblade_ext"
var/retracted_icon_state = "switchblade"
/obj/item/switchblade/attack_self(mob/user)
extended = !extended
playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1)
playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, TRUE)
if(extended)
force = extended_force
force = 20
w_class = WEIGHT_CLASS_NORMAL
throwforce = extended_throwforce
icon_state = extended_icon_state
throwforce = 23
icon_state = "switchblade_ext"
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
hitsound = 'sound/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
else
force = initial(force)
force = 3
w_class = WEIGHT_CLASS_SMALL
throwforce = initial(throwforce)
icon_state = retracted_icon_state
throwforce = 5
icon_state = "switchblade"
attack_verb = list("stubbed", "poked")
hitsound = 'sound/weapons/genhit.ogg'
sharpness = SHARP_NONE
@@ -750,6 +763,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
user.visible_message("<span class='suicide'>[user] is inhaling [src]! It looks like [user.p_theyre()] trying to visit the astral plane!</span>")
return (OXYLOSS)
// /obj/item/ectoplasm/angelic
// icon = 'icons/obj/wizard.dmi'
// icon_state = "angelplasm"
/obj/item/mounted_chainsaw
name = "mounted chainsaw"
desc = "A chainsaw that has replaced your arm."
@@ -758,7 +775,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi'
item_flags = ABSTRACT | DROPDEL
w_class = WEIGHT_CLASS_BULKY
w_class = WEIGHT_CLASS_HUGE
force = 24
throwforce = 0
throw_range = 0
@@ -801,7 +818,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/statuebust/Initialize()
. = ..()
AddElement(/datum/element/art, impressiveness)
addtimer(CALLBACK(src, /datum.proc/_AddElement, list(/datum/element/beauty, 1000)), 0)
// AddComponent(/datum/component/beauty, 1000)
// /obj/item/statuebust/hippocratic
// name = "hippocrates bust"
// desc = "A bust of the famous Greek physician Hippocrates of Kos, often referred to as the father of western medicine."
// icon_state = "hippocratic"
// impressiveness = 50
/obj/item/tailclub
name = "tail club"
@@ -825,8 +848,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon_state = "catwhip"
/obj/item/melee/skateboard
name = "improvised skateboard"
desc = "A skateboard. It can be placed on its wheels and ridden, or used as a strong weapon."
name = "skateboard"
desc = "A skateboard. It can be placed on its wheels and ridden, or used as a radical weapon."
icon_state = "skateboard"
item_state = "skateboard"
force = 12
@@ -839,17 +862,22 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/melee/skateboard/attack_self(mob/user)
if(!user.canUseTopic(src, TRUE, FALSE, TRUE))
return
var/obj/vehicle/ridden/scooter/skateboard/S = new board_item_type(get_turf(user))
var/obj/vehicle/ridden/scooter/skateboard/S = new board_item_type(get_turf(user))//this probably has fucky interactions with telekinesis but for the record it wasn't my fault
S.buckle_mob(user)
qdel(src)
/obj/item/melee/skateboard/improvised
name = "improvised skateboard"
desc = "A jury-rigged skateboard. It can be placed on its wheels and ridden, or used as a radical weapon."
// board_item_type = /obj/vehicle/ridden/scooter/skateboard/improvised
/obj/item/melee/skateboard/pro
name = "skateboard"
desc = "A RaDSTORMz brand professional skateboard. It looks sturdy and well made."
desc = "An EightO brand professional skateboard. It looks sturdy and well made."
icon_state = "skateboard2"
item_state = "skateboard2"
board_item_type = /obj/vehicle/ridden/scooter/skateboard/pro
custom_premium_price = 500
custom_premium_price = PAYCHECK_HARD * 5
/obj/item/melee/skateboard/hoverboard
name = "hoverboard"
@@ -857,10 +885,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon_state = "hoverboard_red"
item_state = "hoverboard_red"
board_item_type = /obj/vehicle/ridden/scooter/skateboard/hoverboard
custom_premium_price = 2015
custom_premium_price = PAYCHECK_COMMAND * 5.4 //If I can't make it a meme I'll make it RAD
/obj/item/melee/skateboard/hoverboard/admin
name = "\improper Board Of Directors"
name = "Board Of Directors"
desc = "The engineering complexity of a spaceship concentrated inside of a board. Just as expensive, too."
icon_state = "hoverboard_nt"
item_state = "hoverboard_nt"
@@ -879,11 +907,19 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
throwforce = 12
attack_verb = list("beat", "smacked")
custom_materials = list(/datum/material/wood = MINERAL_MATERIAL_AMOUNT * 3.5)
w_class = WEIGHT_CLASS_BULKY
w_class = WEIGHT_CLASS_HUGE
var/homerun_ready = 0
var/homerun_able = 0
total_mass = 2.7 //a regular wooden major league baseball bat weighs somewhere between 2 to 3.4 pounds, according to google
/obj/item/melee/baseball_bat/Initialize()
. = ..()
if(prob(1))
name = "cricket bat"
desc = "You've got red on you."
icon_state = "baseball_bat_brit"
item_state = "baseball_bat_brit"
/obj/item/melee/baseball_bat/chaplain
name = "blessed baseball bat"
desc = "There ain't a cult in the league that can withstand a swatter."
@@ -908,11 +944,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
..()
return
if(homerun_ready)
to_chat(user, "<span class='notice'>You're already ready to do a home run!</span>")
to_chat(user, "<span class='warning'>You're already ready to do a home run!</span>")
..()
return
to_chat(user, "<span class='warning'>You begin gathering strength...</span>")
playsound(get_turf(src), 'sound/magic/lightning_chargeup.ogg', 65, 1)
playsound(get_turf(src), 'sound/magic/lightning_chargeup.ogg', 65, TRUE)
if(do_after(user, 90, target = src))
to_chat(user, "<span class='userdanger'>You gather power! Time for a home run!</span>")
homerun_ready = 1
@@ -920,12 +956,14 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/melee/baseball_bat/attack(mob/living/target, mob/living/user)
. = ..()
if(HAS_TRAIT(user, TRAIT_PACIFISM))
return
var/atom/throw_target = get_edge_target_turf(target, user.dir)
if(homerun_ready)
user.visible_message("<span class='userdanger'>It's a home run!</span>")
target.throw_at(throw_target, rand(8,10), 14, user)
target.ex_act(EXPLODE_HEAVY)
playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, 1)
playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, TRUE)
homerun_ready = 0
return
else if(!target.anchored)
@@ -956,7 +994,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/melee/flyswatter
name = "flyswatter"
desc = "Useful for killing insects of all sizes."
desc = "Useful for killing pests of all sizes."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "flyswatter"
item_state = "flyswatter"
@@ -969,7 +1007,6 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
w_class = WEIGHT_CLASS_SMALL
//Things in this list will be instantly splatted. Flyman weakness is handled in the flyman species weakness proc.
var/list/strong_against
var/list/spider_panic
/obj/item/melee/flyswatter/Initialize()
. = ..()
@@ -977,13 +1014,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/mob/living/simple_animal/hostile/poison/bees/,
/mob/living/simple_animal/butterfly,
/mob/living/simple_animal/cockroach,
/obj/item/queen_bee
))
spider_panic = typecacheof(list(
/mob/living/simple_animal/banana_spider,
/mob/living/simple_animal/hostile/poison/giant_spider,
/obj/item/queen_bee,
/obj/structure/spider/spiderling
))
/obj/item/melee/flyswatter/afterattack(atom/target, mob/user, proximity_flag)
. = ..()
if(proximity_flag)
@@ -993,11 +1028,6 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
if(istype(target, /mob/living/))
var/mob/living/bug = target
bug.death(1)
if(is_type_in_typecache(target, spider_panic))
to_chat(user, "<span class='warning'>You easily land a critical blow on the [target].</span>")
if(istype(target, /mob/living/))
var/mob/living/bug = target
bug.adjustBruteLoss(35) //What kinda mad man would go into melee with a spider?!
else
qdel(target)
@@ -1007,7 +1037,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon_state = "madeyoulook"
force = 0
throwforce = 0
item_flags = DROPDEL | ABSTRACT
item_flags = DROPDEL | ABSTRACT // | HAND_ITEM
attack_verb = list("bopped")
/obj/item/circlegame/Initialize()
@@ -1030,6 +1060,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/// Stage 1: The mistake is made
/obj/item/circlegame/proc/ownerExamined(mob/living/owner, mob/living/sucker)
SIGNAL_HANDLER
if(!istype(sucker) || !in_range(owner, sucker))
return
addtimer(CALLBACK(src, .proc/waitASecond, owner, sucker), 4)
@@ -1076,6 +1108,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
to_chat(owner, "<span class='warning'>[sucker] looks down at your [src.name] before trying to avert [sucker.p_their()] eyes, but it's too late!</span>")
to_chat(sucker, "<span class='danger'><b>[owner] sees the fear in your eyes as you try to look away from [owner.p_their()] [src.name]!</b></span>")
owner.face_atom(sucker)
if(owner.client)
owner.client.give_award(/datum/award/achievement/misc/gottem, owner) // then everybody clapped
playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
owner.do_attack_animation(sucker)
@@ -1103,7 +1139,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
item_state = "nothing"
force = 0
throwforce = 0
item_flags = DROPDEL | ABSTRACT
item_flags = DROPDEL | ABSTRACT // | HAND_ITEM
attack_verb = list("slapped")
hitsound = 'sound/effects/snap.ogg'
@@ -1112,16 +1148,12 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
var/mob/living/carbon/human/L = M
if(L && L.dna && L.dna.species)
L.dna.species.stop_wagging_tail(M)
if(user.a_intent != INTENT_HARM && ((user.zone_selected == BODY_ZONE_PRECISE_MOUTH) || (user.zone_selected == BODY_ZONE_PRECISE_EYES) || (user.zone_selected == BODY_ZONE_HEAD)))
user.do_attack_animation(M)
playsound(M, 'sound/weapons/slap.ogg', 50, 1, -1)
user.visible_message("<span class='danger'>[user] slaps [M]!</span>",
"<span class='notice'>You slap [M]!</span>",\
"You hear a slap.")
return
else
..()
user.do_attack_animation(M)
playsound(M, 'sound/weapons/slap.ogg', 50, TRUE, -1)
user.visible_message("<span class='danger'>[user] slaps [M]!</span>",
"<span class='notice'>You slap [M]!</span>",\
"<span class='hear'>You hear a slap.</span>")
return
/obj/item/proc/can_trigger_gun(mob/living/user)
if(!user.can_use_guns(src))
return FALSE
@@ -1138,6 +1170,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
force = 0
throwforce = 5
reach = 2
var/min_reach = 2
/obj/item/extendohand/acme
name = "\improper ACME Extendo-Hand"
@@ -1145,13 +1178,26 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/extendohand/attack(atom/M, mob/living/carbon/human/user)
var/dist = get_dist(M, user)
if(dist < reach)
if(dist < min_reach)
to_chat(user, "<span class='warning'>[M] is too close to use [src] on.</span>")
return
M.attack_hand(user)
//HF blade
// /obj/item/gohei
// name = "gohei"
// desc = "A wooden stick with white streamers at the end. Originally used by shrine maidens to purify things. Now used by the station's valued weeaboos."
// force = 5
// throwforce = 5
// hitsound = "swing_hit"
// attack_verb_continuous = list("whacks", "thwacks", "wallops", "socks")
// attack_verb_simple = list("whack", "thwack", "wallop", "sock")
// icon = 'icons/obj/items_and_weapons.dmi'
// icon_state = "gohei"
// inhand_icon_state = "gohei"
// lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
// righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
//HF blade
/obj/item/vibro_weapon
icon_state = "hfrequency0"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
@@ -1160,6 +1206,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
desc = "A potent weapon capable of cutting through nearly anything. Wielding it in two hands will allow you to deflect gunfire."
armour_penetration = 100
block_chance = 40
force = 20
throwforce = 20
throw_speed = 4
sharpness = SHARP_EDGED
@@ -1182,10 +1229,14 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/// triggered on wield of two handed item
/obj/item/vibro_weapon/proc/on_wield(obj/item/source, mob/user)
SIGNAL_HANDLER
wielded = TRUE
/// triggered on unwield of two handed item
/obj/item/vibro_weapon/proc/on_unwield(obj/item/source, mob/user)
SIGNAL_HANDLER
wielded = FALSE
/obj/item/vibro_weapon/update_icon_state()

View File

@@ -52,12 +52,12 @@ GLOBAL_LIST_INIT(tendrils, list())
last_tendril = FALSE
if(last_tendril && !(flags_1 & ADMIN_SPAWNED_1))
if(SSmedals.hub_enabled)
if(SSachievements.achievements_enabled)
for(var/mob/living/L in view(7,src))
if(L.stat || !L.client)
continue
SSmedals.UnlockMedal("[BOSS_MEDAL_TENDRIL] [ALL_KILL_MEDAL]", L.client)
SSmedals.SetScore(TENDRIL_CLEAR_SCORE, L.client, 1)
L.client.give_award(/datum/award/achievement/boss/tendril_exterminator, L)
L.client.give_award(/datum/award/score/tendril_score, L) //Progresses score by one
GLOB.tendrils -= src
QDEL_NULL(emitted_light)
QDEL_NULL(gps)

View File

@@ -47,14 +47,19 @@
if(open)
GM.visible_message("<span class='danger'>[user] starts to give [GM] a swirlie!</span>", "<span class='userdanger'>[user] starts to give you a swirlie...</span>")
swirlie = GM
if(do_after(user, 30, 0, target = src))
GM.visible_message("<span class='danger'>[user] gives [GM] a swirlie!</span>", "<span class='userdanger'>[user] gives you a swirlie!</span>", "<span class='italics'>You hear a toilet flushing.</span>")
var/was_alive = (swirlie.stat != DEAD)
if(do_after(user, 3 SECONDS, target = src, timed_action_flags = IGNORE_HELD_ITEM))
GM.visible_message("<span class='danger'>[user] gives [GM] a swirlie!</span>", "<span class='userdanger'>[user] gives you a swirlie!</span>", "<span class='hear'>You hear a toilet flushing.</span>")
if(iscarbon(GM))
var/mob/living/carbon/C = GM
if(!C.internal)
log_combat(user, C, "swirlied (oxy)")
C.adjustOxyLoss(5)
else
log_combat(user, GM, "swirlied (oxy)")
GM.adjustOxyLoss(5)
if(was_alive && swirlie.stat == DEAD && swirlie.client)
swirlie.client.give_award(/datum/award/achievement/misc/swirlie, swirlie) // just like space high school all over again!
swirlie = null
else
playsound(src.loc, 'sound/effects/bang.ogg', 25, 1)

View File

@@ -872,20 +872,18 @@
/turf/closed/mineral/strong/gets_drilled(mob/user)
if(!ishuman(user))
return // see attackby
/*
var/mob/living/carbon/human/H = user
if(!(H.mind.get_skill_level(/datum/skill/mining) >= SKILL_LEVEL_MASTER))
return
*/
// if(!(H.mind?.get_skill_level(/datum/skill/mining) >= SKILL_LEVEL_MASTER))
// return
drop_ores()
// H.client.give_award(/datum/award/achievement/skill/legendary_miner, H)
H.client.give_award(/datum/award/achievement/skill/legendary_miner, H)
var/flags = NONE
if(defer_change) // TODO: make the defer change var a var for any changeturf flag
flags = CHANGETURF_DEFER_CHANGE
ScrapeAway(flags=flags)
addtimer(CALLBACK(src, .proc/AfterChange), 1, TIMER_UNIQUE)
playsound(src, 'sound/effects/break_stone.ogg', 50, TRUE) //beautiful destruction
// H.mind.adjust_experience(/datum/skill/mining, 100) //yay!
// H.mind?.adjust_experience(/datum/skill/mining, 100) //yay!
/turf/closed/mineral/strong/proc/drop_ores()
if(prob(10))

View File

@@ -6,13 +6,21 @@ GLOBAL_PROTECT(admin_verbs_default)
return list(
/client/proc/deadmin, /*destroys our own admin datum so we can play as a regular player*/
/client/proc/cmd_admin_say, /*admin-only ooc chat*/
/client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/
/client/proc/deadchat,
/client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/
/client/proc/hide_verbs, /*hides all our adminverbs*/
/client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/
/client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/
/client/proc/toggleprayers,
/client/proc/toggleadminhelpsound,
/client/proc/debugstatpanel,
/client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/
/client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/
/client/proc/secrets,
/client/proc/toggle_hear_radio, /*allows admins to hide all radio output*/
/client/proc/reload_admins,
/client/proc/reestablish_db_connection, /*reattempt a connection to the database*/
/client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/
/client/proc/stop_sounds,
/client/proc/mark_datum_mapview,
/client/proc/debugstatpanel
// /client/proc/fix_air /*resets air in designated radius to its default atmos composition*/
)
GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
GLOBAL_PROTECT(admin_verbs_admin)
@@ -24,6 +32,7 @@ GLOBAL_PROTECT(admin_verbs_admin)
/datum/verbs/menu/Admin/verb/playerpanel,
/client/proc/game_panel, /*game panel, allows to change game-mode etc*/
/client/proc/check_ai_laws, /*shows AI and borg laws*/
// /client/proc/ghost_pool_protection, /*opens a menu for toggling ghost roles*/
/datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/
/datum/admins/proc/toggleooclocal, /*toggles looc on/off for everyone*/
/datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/
@@ -35,15 +44,15 @@ GLOBAL_PROTECT(admin_verbs_admin)
/client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/
/client/proc/toggle_view_range, /*changes how far we can see*/
/client/proc/getserverlogs, /*for accessing server logs*/
/client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/
/client/proc/cmd_admin_headset_message, /*send an message to somebody through their headset as CentCom*/
/client/proc/getcurrentlogs, /*for accessing server logs for the current round*/
/client/proc/cmd_admin_subtle_message, /*send a message to somebody as a 'voice in their head'*/
/client/proc/cmd_admin_headset_message, /*send a message to somebody through their headset as CentCom*/
/client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
/client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/
/client/proc/centcom_podlauncher,/*Open a window to launch a Supplypod and configure it or it's contents*/
/client/proc/check_antagonists, /*shows all antags*/
/datum/admins/proc/access_news_network, /*allows access of newscasters*/
/client/proc/jumptocoord, /*we ghost and jump to a coordinate*/
/client/proc/getcurrentlogs, /*for accessing server logs for the current round*/
/client/proc/Getmob, /*teleports a mob to our location*/
/client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/
// /client/proc/sendmob, /*sends a mob somewhere*/ -Removed due to it needing two sorting procs to work, which were executed every time an admin right-clicked. ~Errorage
@@ -53,6 +62,8 @@ GLOBAL_PROTECT(admin_verbs_admin)
/client/proc/jumptoturf, /*allows us to jump to a specific turf*/
/client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/
/client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcom*/
// /client/proc/admin_disable_shuttle, /*allows us to disable the emergency shuttle admin-wise so that it cannot be called*/
// /client/proc/admin_enable_shuttle, /*undoes the above*/
/client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/
/client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/
/client/proc/cmd_admin_local_narrate, /*sends text to all mobs within view of atom*/
@@ -64,26 +75,18 @@ GLOBAL_PROTECT(admin_verbs_admin)
/client/proc/toggle_combo_hud, // toggle display of the combination pizza antag and taco sci/med/eng hud
/client/proc/toggle_AI_interact, /*toggle admin ability to interact with machines as an AI*/
/datum/admins/proc/open_shuttlepanel, /* Opens shuttle manipulator UI */
/client/proc/deadchat,
/client/proc/toggleprayers,
// /client/proc/toggle_prayer_sound,
// /client/proc/colorasay,
// /client/proc/resetasaycolor,
/client/proc/toggleadminhelpsound,
/client/proc/respawn_character,
/client/proc/secrets,
/client/proc/toggle_hear_radio, /*allows admins to hide all radio output*/
/client/proc/reload_admins,
/client/proc/reestablish_db_connection, /*reattempt a connection to the database*/
/client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/
/client/proc/panicbunker,
/client/proc/addbunkerbypass,
/client/proc/revokebunkerbypass,
/client/proc/stop_sounds,
/client/proc/mark_datum_mapview,
/client/proc/hide_verbs, /*hides all our adminverbs*/
/client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/
/datum/admins/proc/open_borgopanel,
/client/proc/admin_cmd_respawn_return_to_lobby,
/client/proc/admin_cmd_remove_ghost_respawn_timer
/client/proc/admin_cmd_respawn_return_to_lobby, //CIT
/client/proc/admin_cmd_remove_ghost_respawn_timer, //CIT
/datum/admins/proc/open_borgopanel
)
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel))
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/DB_ban_panel, /client/proc/stickybanpanel))
GLOBAL_PROTECT(admin_verbs_ban)
GLOBAL_LIST_INIT(admin_verbs_sounds, list(/client/proc/play_local_sound, /client/proc/play_sound, /client/proc/manual_play_web_sound, /client/proc/set_round_end_sound))
GLOBAL_PROTECT(admin_verbs_sounds)
@@ -110,13 +113,14 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/show_tip,
/client/proc/smite,
/client/proc/admin_away,
/client/proc/cmd_admin_toggle_fov,
/client/proc/cmd_admin_toggle_fov, //CIT CHANGE - FOV
/client/proc/roll_dices //CIT CHANGE - Adds dice verb
))
GLOBAL_PROTECT(admin_verbs_fun)
GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /datum/admins/proc/podspawn_atom, /datum/admins/proc/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character))
GLOBAL_PROTECT(admin_verbs_spawn)
GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer())
GLOBAL_PROTECT(admin_verbs_server)
/world/proc/AVerbsServer()
return list(
/datum/admins/proc/startnow,
@@ -126,17 +130,22 @@ GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer())
/datum/admins/proc/toggleaban,
/client/proc/everyone_random,
/datum/admins/proc/toggleAI,
/datum/admins/proc/toggleMulticam,
/datum/admins/proc/toggledynamicvote,
/datum/admins/proc/toggleMulticam, //CIT
/datum/admins/proc/toggledynamicvote, //CIT
/client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
/client/proc/cmd_debug_del_all,
/client/proc/toggle_random_events,
/client/proc/forcerandomrotate,
/client/proc/adminchangemap,
/client/proc/toggle_hub
/client/proc/panicbunker,
/client/proc/addbunkerbypass, //CIT
/client/proc/revokebunkerbypass, //CIT
// /client/proc/toggle_interviews,
/client/proc/toggle_hub,
/client/proc/toggle_cdn
)
GLOBAL_PROTECT(admin_verbs_server)
GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
GLOBAL_PROTECT(admin_verbs_debug)
/world/proc/AVerbsDebug()
return list(
/client/proc/restart_controller,
@@ -174,27 +183,37 @@ GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
/client/proc/cmd_display_init_log,
/client/proc/cmd_display_overlay_log,
/client/proc/reload_configuration,
// /client/proc/atmos_control,
// /client/proc/reload_cards,
// /client/proc/validate_cards,
// /client/proc/test_cardpack_distribution,
// /client/proc/print_cards,
#ifdef TESTING
/client/proc/check_missing_sprites,
#endif
/datum/admins/proc/create_or_modify_area,
#ifdef REFERENCE_TRACKING
/datum/admins/proc/view_refs,
/datum/admins/proc/view_del_failures,
#endif
/client/proc/generate_wikichem_list, //DO NOT PRESS UNLESS YOU WANT SUPERLAG
// /client/proc/check_timer_sources,
/client/proc/toggle_cdn,
/client/proc/generate_wikichem_list //DO NOT PRESS UNLESS YOU WANT SUPERLAG
)
GLOBAL_PROTECT(admin_verbs_debug)
GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release))
GLOBAL_PROTECT(admin_verbs_possess)
GLOBAL_LIST_INIT(admin_verbs_permissions, list(/client/proc/edit_admin_permissions))
GLOBAL_PROTECT(admin_verbs_permissions)
GLOBAL_LIST_INIT(admin_verbs_poll, list(/client/proc/create_poll))
GLOBAL_PROTECT(admin_verbs_poll)
//verbs which can be hidden - needs work
GLOBAL_PROTECT(admin_verbs_poll)
GLOBAL_LIST_INIT(admin_verbs_hideable, list(
/client/proc/set_ooc,
/client/proc/reset_ooc,
/client/proc/deadmin,
/datum/admins/proc/show_traitor_panel,
// /datum/admins/proc/show_skill_panel,
/datum/admins/proc/toggleenter,
/datum/admins/proc/toggleguests,
/datum/admins/proc/announce,
@@ -239,7 +258,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
/client/proc/Debug2,
/client/proc/reload_admins,
/client/proc/cmd_debug_make_powernets,
/client/proc/startSinglo,
/client/proc/startSinglo, // tg removed this
/client/proc/cmd_debug_mob_lists,
/client/proc/cmd_debug_del_all,
/client/proc/enable_debug_verbs,
@@ -247,8 +266,9 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
/proc/release,
/client/proc/reload_admins,
/client/proc/panicbunker,
/client/proc/addbunkerbypass,
/client/proc/revokebunkerbypass,
/client/proc/addbunkerbypass, //CIT
/client/proc/revokebunkerbypass, //CIT
// /client/proc/toggle_interviews,
/client/proc/admin_change_sec_level,
/client/proc/toggle_nuke,
/client/proc/cmd_display_del_log,
@@ -322,7 +342,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
remove_verb(src, /client/proc/hide_most_verbs)
add_verb(src, /client/proc/show_verbs)
to_chat(src, "<span class='interface'>Most of your adminverbs have been hidden.</span>")
to_chat(src, "<span class='interface'>Most of your adminverbs have been hidden.</span>", confidential = TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Hide Most Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
@@ -333,7 +353,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
remove_admin_verbs()
add_verb(src, /client/proc/show_verbs)
to_chat(src, "<span class='interface'>Almost all of your adminverbs have been hidden.</span>")
to_chat(src, "<span class='interface'>Almost all of your adminverbs have been hidden.</span>", confidential = TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Hide All Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
@@ -344,7 +364,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
remove_verb(src, /client/proc/show_verbs)
add_admin_verbs()
to_chat(src, "<span class='interface'>All of your adminverbs are now visible.</span>")
to_chat(src, "<span class='interface'>All of your adminverbs are now visible.</span>", confidential = TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -354,7 +374,8 @@ GLOBAL_PROTECT(admin_verbs_hideable)
set category = "Admin.Game"
set name = "Aghost"
if(!holder)
return FALSE
return
. = TRUE
if(isobserver(mob))
//re-enter
var/mob/dead/observer/ghost = mob
@@ -367,7 +388,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
ghost.reenter_corpse()
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Reenter") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
else if(isnewplayer(mob))
to_chat(src, "<font color='red'>Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.</font>")
to_chat(src, "<font color='red'>Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.</font>", confidential = TRUE)
return FALSE
else
//ghostize
@@ -375,10 +396,10 @@ GLOBAL_PROTECT(admin_verbs_hideable)
message_admins("[key_name_admin(usr)] admin ghosted.")
var/mob/body = mob
body.ghostize(1, voluntary = TRUE)
init_verbs()
if(body && !body.key)
body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Ghost") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return TRUE
/client/proc/invisimin()
set name = "Invisimin"
@@ -387,10 +408,10 @@ GLOBAL_PROTECT(admin_verbs_hideable)
if(holder && mob)
if(mob.invisibility == INVISIBILITY_OBSERVER)
mob.invisibility = initial(mob.invisibility)
to_chat(mob, "<span class='boldannounce'>Invisimin off. Invisibility reset.</span>")
to_chat(mob, "<span class='boldannounce'>Invisimin off. Invisibility reset.</span>", confidential = TRUE)
else
mob.invisibility = INVISIBILITY_OBSERVER
to_chat(mob, "<span class='adminnotice'><b>Invisimin on. You are now as invisible as a ghost.</b></span>")
to_chat(mob, "<span class='adminnotice'><b>Invisimin on. You are now as invisible as a ghost.</b></span>", confidential = TRUE)
/client/proc/check_antagonists()
set name = "Check Antagonists"
@@ -402,15 +423,21 @@ GLOBAL_PROTECT(admin_verbs_hideable)
message_admins("[key_name_admin(usr)] checked antagonists.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Antagonists") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/unban_panel()
set name = "Unban Panel"
set category = "Admin"
if(holder)
if(CONFIG_GET(flag/ban_legacy_system))
holder.unbanpanel()
else
holder.DB_ban_panel()
SSblackbox.record_feedback("tally", "admin_verb", 1, "Unban Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
// /client/proc/ban_panel()
// set name = "Banning Panel"
// set category = "Admin"
// if(!check_rights(R_BAN))
// return
// holder.ban_panel()
// SSblackbox.record_feedback("tally", "admin_verb", 1, "Banning Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
// /client/proc/unban_panel()
// set name = "Unbanning Panel"
// set category = "Admin"
// if(!check_rights(R_BAN))
// return
// holder.unban_panel()
// SSblackbox.record_feedback("tally", "admin_verb", 1, "Unbanning Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/game_panel()
set name = "Game Panel"
@@ -419,13 +446,13 @@ GLOBAL_PROTECT(admin_verbs_hideable)
holder.Game()
SSblackbox.record_feedback("tally", "admin_verb", 1, "Game Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/secrets()
set name = "Secrets"
set category = "Admin.Game"
if (holder)
holder.Secrets()
SSblackbox.record_feedback("tally", "admin_verb", 1, "Secrets Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
// /client/proc/poll_panel()
// set name = "Server Poll Management"
// set category = "Admin"
// if(!check_rights(R_POLL))
// return
// holder.poll_list_panel()
// SSblackbox.record_feedback("tally", "admin_verb", 1, "Server Poll Management") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/findStealthKey(txt)
if(txt)
@@ -457,7 +484,10 @@ GLOBAL_PROTECT(admin_verbs_hideable)
if(isobserver(mob))
mob.invisibility = initial(mob.invisibility)
mob.alpha = initial(mob.alpha)
mob.name = initial(mob.name)
if(mob.mind)
mob.name = mob.mind.name
else
mob.name = mob.real_name
mob.mouse_opacity = initial(mob.mouse_opacity)
else
var/new_key = ckeyEx(stripped_input(usr, "Enter your desired display name.", "Fake Key", key, 26))
@@ -485,7 +515,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
switch(choice)
if(null)
return 0
return
if("Small Bomb (1, 2, 3, 3)")
explosion(epicenter, 1, 2, 3, 3, TRUE, TRUE)
if("Medium Bomb (2, 3, 4, 4)")
@@ -538,7 +568,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
if (isnull(ex_power))
return
var/range = round((2 * ex_power)**GLOB.DYN_EX_SCALE)
to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])")
to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])", confidential = TRUE)
/client/proc/get_dynex_power()
set category = "Debug"
@@ -549,7 +579,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
if (isnull(ex_range))
return
var/power = (0.5 * ex_range)**(1/GLOB.DYN_EX_SCALE)
to_chat(usr, "Estimated Explosive Power: [power]")
to_chat(usr, "Estimated Explosive Power: [power]", confidential = TRUE)
/client/proc/set_dynex_scale()
set category = "Debug"
@@ -563,6 +593,55 @@ GLOBAL_PROTECT(admin_verbs_hideable)
log_admin("[key_name(usr)] has modified Dynamic Explosion Scale: [ex_scale]")
message_admins("[key_name_admin(usr)] has modified Dynamic Explosion Scale: [ex_scale]")
// /client/proc/atmos_control()
// set name = "Atmos Control Panel"
// set category = "Debug"
// if(!check_rights(R_DEBUG))
// return
// SSair.ui_interact(mob)
// /client/proc/reload_cards()
// set name = "Reload Cards"
// set category = "Debug"
// if(!check_rights(R_DEBUG))
// return
// if(!SStrading_card_game.loaded)
// message_admins("The card subsystem is not currently loaded")
// return
// reloadAllCardFiles(SStrading_card_game.card_files, SStrading_card_game.card_directory)
// /client/proc/validate_cards()
// set name = "Validate Cards"
// set category = "Debug"
// if(!check_rights(R_DEBUG))
// return
// if(!SStrading_card_game.loaded)
// message_admins("The card subsystem is not currently loaded")
// return
// var/message = checkCardpacks(SStrading_card_game.card_packs)
// message += checkCardDatums()
// if(message)
// message_admins(message)
// /client/proc/test_cardpack_distribution()
// set name = "Test Cardpack Distribution"
// set category = "Debug"
// if(!check_rights(R_DEBUG))
// return
// if(!SStrading_card_game.loaded)
// message_admins("The card subsystem is not currently loaded")
// return
// var/pack = input("Which pack should we test?", "You fucked it didn't you") as null|anything in sortList(SStrading_card_game.card_packs)
// var/batchCount = input("How many times should we open it?", "Don't worry, I understand") as null|num
// var/batchSize = input("How many cards per batch?", "I hope you remember to check the validation") as null|num
// var/guar = input("Should we use the pack's guaranteed rarity? If so, how many?", "We've all been there. Man you should have seen the old system") as null|num
// checkCardDistribution(pack, batchSize, batchCount, guar)
// /client/proc/print_cards()
// set name = "Print Cards"
// set category = "Debug"
// printAllCards()
/client/proc/give_spell(mob/T in GLOB.mob_list)
set category = "Admin.Fun"
set name = "Give Spell"
@@ -572,13 +651,13 @@ GLOBAL_PROTECT(admin_verbs_hideable)
var/type_length = length_char("/obj/effect/proc_holder/spell") + 2
for(var/A in GLOB.spells)
spell_list[copytext_char("[A]", type_length)] = A
var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spell_list
var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in sortList(spell_list)
if(!S)
return
SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Spell") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave [key_name(T)] the spell [S].</span>")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave [key_name_admin(T)] the spell [S].</span>")
S = spell_list[S]
if(T.mind)
@@ -592,12 +671,12 @@ GLOBAL_PROTECT(admin_verbs_hideable)
set name = "Remove Spell"
set desc = "Remove a spell from the selected mob."
if(T && T.mind)
var/obj/effect/proc_holder/spell/S = input("Choose the spell to remove", "NO ABRAKADABRA") as null|anything in T.mind.spell_list
if(T?.mind)
var/obj/effect/proc_holder/spell/S = input("Choose the spell to remove", "NO ABRAKADABRA") as null|anything in sortList(T.mind.spell_list)
if(S)
T.mind.RemoveSpell(S)
log_admin("[key_name(usr)] removed the spell [S] from [key_name(T)].")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed the spell [S] from [key_name(T)].</span>")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed the spell [S] from [key_name_admin(T)].</span>")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Remove Spell") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/give_disease(mob/living/T in GLOB.mob_living_list)
@@ -605,15 +684,15 @@ GLOBAL_PROTECT(admin_verbs_hideable)
set name = "Give Disease"
set desc = "Gives a Disease to a mob."
if(!istype(T))
to_chat(src, "<span class='notice'>You can only give a disease to a mob of type /mob/living.</span>")
to_chat(src, "<span class='notice'>You can only give a disease to a mob of type /mob/living.</span>", confidential = TRUE)
return
var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in SSdisease.diseases
var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in sortList(SSdisease.diseases, /proc/cmp_typepaths_asc)
if(!D)
return
T.ForceContractDisease(new D, FALSE, TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Disease") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave [key_name(T)] the disease [D].</span>")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave [key_name_admin(T)] the disease [D].</span>")
/client/proc/object_say(obj/O in world)
set category = "Admin.Events"
@@ -655,8 +734,8 @@ GLOBAL_PROTECT(admin_verbs_hideable)
holder.deactivate()
to_chat(src, "<span class='interface'>You are now a normal player.</span>")
log_admin("[src] deadmined themself.")
message_admins("[src] deadmined themself.")
log_admin("[src] deadminned themselves.")
message_admins("[src] deadminned themselves.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Deadmin")
/client/proc/readmin()
@@ -679,7 +758,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
if (!holder)
return //This can happen if an admin attempts to vv themself into somebody elses's deadmin datum by getting ref via brute force
to_chat(src, "<span class='interface'>You are now an admin.</span>")
to_chat(src, "<span class='interface'>You are now an admin.</span>", confidential = TRUE)
message_admins("[src] re-adminned themselves.")
log_admin("[src] re-adminned themselves.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Readmin")

View File

@@ -28,6 +28,8 @@ GLOBAL_PROTECT(href_token)
var/deadmined
var/datum/filter_editor/filteriffic
/datum/admins/CanProcCall(procname)
. = ..()
if(!check_rights(R_SENSITIVE))

View File

@@ -2500,9 +2500,6 @@
break
return
else if(href_list["secrets"])
Secrets_topic(href_list["secrets"],href_list)
else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here
if(!check_rights(R_ADMIN))
return

View File

@@ -790,11 +790,12 @@
if(!check_rights(R_DEBUG))
return
SSmedals.hub_enabled = !SSmedals.hub_enabled
SSachievements.achievements_enabled = !SSachievements.achievements_enabled
message_admins("<span class='adminnotice'>[key_name_admin(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.</span>")
message_admins("<span class='adminnotice'>[key_name_admin(src)] [SSachievements.achievements_enabled ? "disabled" : "enabled"] the medal hub lockout.</span>")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Toggle Medal Disable") // If...
log_admin("[key_name(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.")
log_admin("[key_name(src)] [SSachievements.achievements_enabled ? "disabled" : "enabled"] the medal hub lockout.")
/client/proc/view_runtimes()
set category = "Debug"

View File

@@ -63,3 +63,31 @@
load_admins()
SSblackbox.record_feedback("tally", "admin_verb", 1, "Reload All Admins") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
message_admins("[key_name_admin(usr)] manually reloaded admins")
/client/proc/toggle_cdn()
set name = "Toggle CDN"
set category = "Server"
var/static/admin_disabled_cdn_transport = null
if (alert(usr, "Are you sure you want to toggle the CDN asset transport?", "Confirm", "Yes", "No") != "Yes")
return
var/current_transport = CONFIG_GET(string/asset_transport)
if (!current_transport || current_transport == "simple")
if (admin_disabled_cdn_transport)
CONFIG_SET(string/asset_transport, admin_disabled_cdn_transport)
admin_disabled_cdn_transport = null
SSassets.OnConfigLoad()
message_admins("[key_name_admin(usr)] re-enabled the CDN asset transport")
log_admin("[key_name(usr)] re-enabled the CDN asset transport")
else
to_chat(usr, "<span class='adminnotice'>The CDN is not enabled!</span>")
if (alert(usr, "The CDN asset transport is not enabled! If you having issues with assets you can also try disabling filename mutations.", "The CDN asset transport is not enabled!", "Try disabling filename mutations", "Nevermind") == "Try disabling filename mutations")
SSassets.transport.dont_mutate_filenames = !SSassets.transport.dont_mutate_filenames
message_admins("[key_name_admin(usr)] [(SSassets.transport.dont_mutate_filenames ? "disabled" : "re-enabled")] asset filename transforms")
log_admin("[key_name(usr)] [(SSassets.transport.dont_mutate_filenames ? "disabled" : "re-enabled")] asset filename transforms")
else
admin_disabled_cdn_transport = current_transport
CONFIG_SET(string/asset_transport, "simple")
SSassets.OnConfigLoad()
SSassets.transport.dont_mutate_filenames = TRUE
message_admins("[key_name_admin(usr)] disabled the CDN asset transport")
log_admin("[key_name(usr)] disabled the CDN asset transport")

View File

@@ -0,0 +1,99 @@
/datum/filter_editor
var/atom/target
/datum/filter_editor/New(atom/target)
src.target = target
/datum/filter_editor/ui_state(mob/user)
return GLOB.admin_state
/datum/filter_editor/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Filteriffic")
ui.open()
/datum/filter_editor/ui_static_data(mob/user)
var/list/data = list()
data["filter_info"] = GLOB.master_filter_info
return data
/datum/filter_editor/ui_data()
var/list/data = list()
data["target_name"] = target.name
data["target_filter_data"] = target.filter_data
return data
/datum/filter_editor/ui_act(action, list/params)
. = ..()
if(.)
return
switch(action)
if("add_filter")
var/target_name = params["name"]
while(target.filter_data && target.filter_data[target_name])
target_name = "[target_name]-dupe"
target.add_filter(target_name, params["priority"], list("type" = params["type"]))
. = TRUE
if("remove_filter")
target.remove_filter(params["name"])
. = TRUE
if("rename_filter")
var/list/filter_data = target.filter_data[params["name"]]
target.remove_filter(params["name"])
target.add_filter(params["new_name"], filter_data["priority"], filter_data)
. = TRUE
if("edit_filter")
target.remove_filter(params["name"])
target.add_filter(params["name"], params["priority"], params["new_filter"])
. = TRUE
if("change_priority")
var/new_priority = params["new_priority"]
target.change_filter_priority(params["name"], new_priority)
. = TRUE
if("transition_filter_value")
target.transition_filter(params["name"], 4, params["new_data"])
. = TRUE
if("modify_filter_value")
var/list/old_filter_data = target.filter_data[params["name"]]
var/list/new_filter_data = old_filter_data.Copy()
for(var/entry in params["new_data"])
new_filter_data[entry] = params["new_data"][entry]
for(var/entry in new_filter_data)
if(entry == GLOB.master_filter_info[old_filter_data["type"]]["defaults"][entry])
new_filter_data.Remove(entry)
target.remove_filter(params["name"])
target.add_filter(params["name"], old_filter_data["priority"], new_filter_data)
. = TRUE
if("modify_color_value")
var/new_color = input(usr, "Pick new filter color", "Filteriffic Colors!") as color|null
if(new_color)
target.transition_filter(params["name"], 4, list("color" = new_color))
. = TRUE
if("modify_icon_value")
var/icon/new_icon = input("Pick icon:", "Icon") as null|icon
if(new_icon)
target.filter_data[params["name"]]["icon"] = new_icon
target.update_filters()
. = TRUE
if("mass_apply")
if(!check_rights_for(usr.client, R_FUN))
to_chat(usr, "<span class='userdanger>Stay in your lane, jannie.</span>'")
return
var/target_path = text2path(params["path"])
if(!target_path)
return
var/filters_to_copy = target.filters
var/filter_data_to_copy = target.filter_data
var/count = 0
for(var/thing in world.contents)
if(istype(thing, target_path))
var/atom/thing_at = thing
thing_at.filters = filters_to_copy
thing_at.filter_data = filter_data_to_copy
count += 1
message_admins("LOCAL CLOWN [usr.ckey] JUST MASS FILTER EDITED [count] WITH PATH OF [params["path"]]!")
log_admin("LOCAL CLOWN [usr.ckey] JUST MASS FILTER EDITED [count] WITH PATH OF [params["path"]]!")

View File

@@ -5,10 +5,13 @@
var/method = 0 //0 means strict type detection while 1 means this type and all subtypes (IE: /obj/item with this set to 1 will set it to ALL items)
// if(tgui_alert(src, "Are you sure you'd like to mass-modify every instance of the [var_name] variable? This can break everything if you do not know what you are doing.", "Slow down, chief!", list("Yes", "No")) != "Yes")
// return
if(!check_rights(R_VAREDIT))
return
if(A && A.type)
if(A?.type)
method = vv_subtype_prompt(A.type)
src.massmodify_variables(A, var_name, method)
@@ -114,7 +117,7 @@
var/list/varsvars = vv_parse_text(O, new_value)
var/pre_processing = new_value
var/unique
if (varsvars && varsvars.len)
if (varsvars?.len)
unique = alert(usr, "Process vars unique to each instance, or same for all?", "Variable Association", "Unique", "Same")
if(unique == "Unique")
unique = TRUE

View File

@@ -23,7 +23,7 @@ GLOBAL_PROTECT(VVpixelmovement)
var/list/subtypes = subtypesof(type)
if (!subtypes || !subtypes.len)
return FALSE
if (subtypes && subtypes.len)
if (subtypes?.len)
switch(alert("Strict object type detection?", "Type detection", "Strictly this type","This type and subtypes", "Cancel"))
if("Strictly this type")
return FALSE

View File

@@ -29,6 +29,12 @@
return
var/new_name = stripped_input(usr,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN)
// If the new name is something that would be restricted by IC chat filters,
// give the admin a warning but allow them to do it anyway if they want.
// if(CHAT_FILTER_CHECK(new_name) && alert(usr, "Your selected name contains words restricted by IC chat filters. Confirm this new name?", "IC Chat Filter Conflict", "Confirm", "Cancel") == "Cancel")
// return
if( !new_name || !M )
return

View File

@@ -47,7 +47,7 @@
usr.client.debug_variables(src)
return
#ifdef REFERENCE_TRACKING //people with debug can only access this putnam!
#ifdef REFERENCE_TRACKING
if(href_list[VV_HK_VIEW_REFERENCES])
var/datum/D = locate(href_list[VV_HK_TARGET])
if(!D)

View File

@@ -193,7 +193,7 @@
var/list/trait_list = list(TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE)
/datum/eldritch_knowledge/final/ash_final/on_finished_recipe(mob/living/user, list/atoms, loc)
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the blaze, for Ashbringer [user.real_name] has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the blaze, for the Ashlord, [user.real_name] has ascended! The flames shall consume all! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/fire_cascade/big)
user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/fire_sworn)
var/mob/living/carbon/human/H = user
@@ -201,6 +201,7 @@
H.physiology.burn_mod *= 0.5
var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic)
ascension.ascended = TRUE
H.client?.give_award(/datum/award/achievement/misc/ash_ascension, H)
for(var/X in trait_list)
ADD_TRAIT(user,X,MAGIC_TRAIT)
return ..()

View File

@@ -232,7 +232,7 @@
log_game("[key_name_admin(ghost_candidate)] has taken control of ([key_name_admin(summoned)]).")
summoned.ghostize(FALSE)
summoned.key = ghost_candidate.key
summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster)
summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster) //no you will NOT get the achivement you ghost.
var/datum/antagonist/heretic_monster/monster = summoned.mind.has_antag_datum(/datum/antagonist/heretic_monster)
var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic)
monster.set_owner(master)
@@ -243,7 +243,10 @@
user.SetImmobilized(0)
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the dark, for king of arms has ascended! Lord of the night has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
log_game("[user.real_name] ascended as [summoned.real_name]")
var/mob/living/carbon/carbon_user = user
if(!ishuman(user))
return
var/mob/living/carbon/human/H = user
H.client?.give_award(/datum/award/achievement/misc/flesh_ascension, H)
var/datum/antagonist/heretic/ascension = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
ascension.ascended = TRUE
carbon_user.mind.transfer_to(summoned, TRUE)

View File

@@ -169,7 +169,8 @@
var/mob/living/carbon/human/H = user
H.physiology.brute_mod *= 0.5
H.physiology.burn_mod *= 0.5
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the decay, for Rustbringer [user.real_name] has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
H.client?.give_award(/datum/award/achievement/misc/rust_ascension, H)
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the decay, for the Rustbringer, [user.real_name] has ascended! None shall escape the corrosion! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
new /datum/rust_spread(loc)
var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic)
ascension.ascended = TRUE

View File

@@ -80,7 +80,7 @@
if(1000)
SpeakPeace(list("The ends exists somewhere beyond meaningful milestones.", "There will be no more messages until then.", "You disgust me."))
if(5643)
SSmedals.UnlockMedal(MEDAL_TIMEWASTE, user.client)
user.client.give_award(/datum/award/achievement/misc/time_waste, user)
var/obj/item/reagent_containers/food/drinks/trophy/gold_cup/never_ends = new(get_turf(user))
never_ends.name = "Overextending The Joke: First Place"
never_ends.desc = "And so we are left alone with our regrets."

View File

@@ -1009,6 +1009,25 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
/client/proc/can_have_part(part_name)
return prefs.pref_species.mutant_bodyparts[part_name] || (part_name in GLOB.unlocked_mutant_parts)
///Redirect proc that makes it easier to call the unlock achievement proc. Achievement type is the typepath to the award, user is the mob getting the award, and value is an optional variable used for leaderboard value increments
/client/proc/give_award(achievement_type, mob/user, value = 1)
return player_details.achievements.unlock(achievement_type, user, value)
///Redirect proc that makes it easier to get the status of an achievement. Achievement type is the typepath to the award.
/client/proc/get_award_status(achievement_type, mob/user, value = 1)
return player_details.achievements.get_achievement_status(achievement_type)
///Redirect proc that makes it easier to get the status of an achievement. Achievement type is the typepath to the award.
/client/proc/award_heart(heart_reason)
to_chat(src, "<span class='nicegreen'>Someone awarded you a heart![heart_reason ? " They said: [heart_reason]!" : ""]</span>")
if(!src)
return
prefs.hearted_until = world.realtime + (24 HOURS)
prefs.hearted = TRUE
if(!src)
return
prefs.save_preferences()
/// compiles a full list of verbs and sends it to the browser
/client/proc/init_verbs()
if(IsAdminAdvancedProcCall())
@@ -1048,3 +1067,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
return TRUE
return FALSE
/client/proc/open_filter_editor(atom/in_atom)
if(holder)
holder.filteriffic = new /datum/filter_editor(in_atom)
holder.filteriffic.ui_interact(mob)

View File

@@ -3,4 +3,21 @@
var/list/logging = list()
var/list/post_login_callbacks = list()
var/list/post_logout_callbacks = list()
var/list/played_names = list() //List of names this key played under this round
var/byond_version = "Unknown"
var/datum/achievement_data/achievements
/datum/player_details/New(key)
achievements = new(key)
/proc/log_played_names(ckey, ...)
if(!ckey)
return
if(args.len < 2)
return
var/list/names = args.Copy(2)
var/datum/player_details/P = GLOB.player_details[ckey]
if(P)
for(var/name in names)
if(name)
P.played_names |= name

View File

@@ -160,6 +160,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
wizard.apply_damage(25, BRUTE)
qdel(src)
else
U.client.give_award(/datum/award/achievement/misc/feat_of_strength, U) //rod-form wizards would probably make this a lot easier to get so keep it to regular rods only
U.visible_message("<span class='boldwarning'>[U] suplexes [src] into the ground!</span>", "<span class='warning'>You suplex [src] into the ground!</span>")
new /obj/structure/festivus/anchored(drop_location())
new /obj/effect/anomaly/flux(drop_location())

View File

@@ -1,21 +1,21 @@
/**
* The mafia controller handles the mafia minigame in progress.
* It is first created when the first ghost signs up to play.
*/
* The mafia controller handles the mafia minigame in progress.
* It is first created when the first ghost signs up to play.
*/
/datum/mafia_controller
///list of observers that should get game updates.
var/list/spectators = list()
///all roles in the game, dead or alive. check their game status if you only want living or dead.
var/list/all_roles = list()
///exists to speed up role retrieval, it's a dict. player_role_lookup[player ckey] will give you the role they play
///exists to speed up role retrieval, it's a dict. `player_role_lookup[player ckey]` will give you the role they play
var/list/player_role_lookup = list()
///what part of the game you're playing in. day phases, night phases, judgement phases, etc.
var/phase = MAFIA_PHASE_SETUP
///how long the game has gone on for, changes with every sunrise. day one, night one, day two, etc.
var/turn = 0
///for debugging and testing a full game, or adminbuse. If this is not null, it will use this as a setup. clears when game is over
///for debugging and testing a full game, or adminbuse. If this is not empty, it will use this as a setup. clears when game is over
var/list/custom_setup = list()
///first day has no voting, and thus is shorter
var/first_day_phase_period = 20 SECONDS
@@ -82,20 +82,20 @@
qdel(map_deleter)
/**
* Triggers at beginning of the game when there is a confirmed list of valid, ready players.
* Creates a 100% ready game that has NOT started (no players in bodies)
* Followed by start game
*
* Does the following:
* * Picks map, and loads it
* * Grabs landmarks if it is the first time it's loading
* * Sets up the role list
* * Puts players in each role randomly
* Arguments:
* * setup_list: list of all the datum setups (fancy list of roles) that would work for the game
* * ready_players: list of filtered, sane players (so not playing or disconnected) for the game to put into roles
*/
/datum/mafia_controller/proc/prepare_game(setup_list, ready_players)
* Triggers at beginning of the game when there is a confirmed list of valid, ready players.
* Creates a 100% ready game that has NOT started (no players in bodies)
* Followed by start game
*
* Does the following:
* * Picks map, and loads it
* * Grabs landmarks if it is the first time it's loading
* * Sets up the role list
* * Puts players in each role randomly
* Arguments:
* * setup_list: list of all the datum setups (fancy list of roles) that would work for the game
* * ready_players: list of filtered, sane players (so not playing or disconnected) for the game to put into roles
*/
/datum/mafia_controller/proc/prepare_game(setup_list,ready_players)
var/list/possible_maps = subtypesof(/datum/map_template/mafia)
var/turf/spawn_area = get_turf(locate(/obj/effect/landmark/mafia_game_area) in GLOB.landmarks_list)
@@ -166,23 +166,23 @@
to_chat(M, "[link] MAFIA: [msg] [team_suffix]")
/**
* The game by this point is now all set up, and so we can put people in their bodies and start the first phase.
*
* Does the following:
* * Creates bodies for all of the roles with the first proc
* * Starts the first day manually (so no timer) with the second proc
*/
* The game by this point is now all set up, and so we can put people in their bodies and start the first phase.
*
* Does the following:
* * Creates bodies for all of the roles with the first proc
* * Starts the first day manually (so no timer) with the second proc
*/
/datum/mafia_controller/proc/start_game()
create_bodies()
start_day()
/**
* How every day starts.
*
* What players do in this phase:
* * If day one, just a small starting period to see who is in the game and check role, leading to the night phase.
* * Otherwise, it's a longer period used to discuss events that happened during the night, leading to the voting phase.
*/
* How every day starts.
*
* What players do in this phase:
* * If day one, just a small starting period to see who is in the game and check role, leading to the night phase.
* * Otherwise, it's a longer period used to discuss events that happened during the night, leading to the voting phase.
*/
/datum/mafia_controller/proc/start_day()
turn += 1
phase = MAFIA_PHASE_DAY
@@ -198,12 +198,12 @@
SStgui.update_uis(src)
/**
* Players have finished the discussion period, and now must put up someone to the chopping block.
*
* What players do in this phase:
* * Vote on which player to put up for lynching, leading to the judgement phase.
* * If no votes are case, the judgement phase is skipped, leading to the night phase.
*/
* Players have finished the discussion period, and now must put up someone to the chopping block.
*
* What players do in this phase:
* * Vote on which player to put up for lynching, leading to the judgement phase.
* * If no votes are case, the judgement phase is skipped, leading to the night phase.
*/
/datum/mafia_controller/proc/start_voting_phase()
phase = MAFIA_PHASE_VOTING
next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, TRUE),voting_phase_period,TIMER_STOPPABLE) //be verbose!
@@ -211,21 +211,21 @@
SStgui.update_uis(src)
/**
* Players have voted someone up, and now the person must defend themselves while the town votes innocent or guilty.
*
* What players do in this phase:
* * Vote innocent or guilty, if they are not on trial.
* * Defend themselves and wait for judgement, if they are.
* * Leads to the lynch phase.
* Arguments:
* * verbose: boolean, announces whether there were votes or not. after judgement it goes back here with no voting period to end the day.
*/
* Players have voted someone up, and now the person must defend themselves while the town votes innocent or guilty.
*
* What players do in this phase:
* * Vote innocent or guilty, if they are not on trial.
* * Defend themselves and wait for judgement, if they are.
* * Leads to the lynch phase.
* Arguments:
* * verbose: boolean, announces whether there were votes or not. after judgement it goes back here with no voting period to end the day.
*/
/datum/mafia_controller/proc/check_trial(verbose = TRUE)
var/datum/mafia_role/loser = get_vote_winner("Day")//, majority_of_town = TRUE)
// var/loser_votes = get_vote_count(loser,"Day")
var/loser_votes = get_vote_count(loser,"Day")
if(loser)
// if(loser_votes > 12)
// loser.body.client?.give_award(/datum/award/achievement/mafia/universally_hated, loser.body)
if(loser_votes > 12)
award_role(/datum/award/achievement/mafia/universally_hated, loser)
send_message("<b>[loser.body.real_name] wins the day vote, Listen to their defense and vote \"INNOCENT\" or \"GUILTY\"!</b>")
//refresh the lists
judgement_abstain_votes = list()
@@ -248,12 +248,12 @@
SStgui.update_uis(src)
/**
* Players have voted innocent or guilty on the person on trial, and that person is now killed or returned home.
*
* What players do in this phase:
* * r/watchpeopledie
* * If the accused is killed, their true role is revealed to the rest of the players.
*/
* Players have voted innocent or guilty on the person on trial, and that person is now killed or returned home.
*
* What players do in this phase:
* * r/watchpeopledie
* * If the accused is killed, their true role is revealed to the rest of the players.
*/
/datum/mafia_controller/proc/lynch()
for(var/i in judgement_innocent_votes)
var/datum/mafia_role/role = i
@@ -276,25 +276,25 @@
next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, FALSE),judgement_lynch_period,TIMER_STOPPABLE)// small pause to see the guy dead, no verbosity since we already did this
/**
* Teenie helper proc to move players back to their home.
* Used in the above, but also used in the debug button "send all players home"
* Arguments:
* * role: mafia role that is getting sent back to the game.
*/
* Teenie helper proc to move players back to their home.
* Used in the above, but also used in the debug button "send all players home"
* Arguments:
* * role: mafia role that is getting sent back to the game.
*/
/datum/mafia_controller/proc/send_home(datum/mafia_role/role)
role.body.forceMove(get_turf(role.assigned_landmark))
/**
* Checks to see if a faction (or solo antagonist) has won.
*
* Calculates in this order:
* * counts up town, mafia, and solo
* * solos can count as town members for the purposes of mafia winning
* * sends the amount of living people to the solo antagonists, and see if they won OR block the victory of the teams
* * checks if solos won from above, then if town, then if mafia
* * starts the end of the game if a faction won
* * returns TRUE if someone won the game, halting other procs from continuing in the case of a victory
*/
* Checks to see if a faction (or solo antagonist) has won.
*
* Calculates in this order:
* * counts up town, mafia, and solo
* * solos can count as town members for the purposes of mafia winning
* * sends the amount of living people to the solo antagonists, and see if they won OR block the victory of the teams
* * checks if solos won from above, then if town, then if mafia
* * starts the end of the game if a faction won
* * returns TRUE if someone won the game, halting other procs from continuing in the case of a victory
*/
/datum/mafia_controller/proc/check_victory()
//needed for achievements
var/list/total_town = list()
@@ -336,8 +336,7 @@
var/solo_end = FALSE
for(var/datum/mafia_role/winner in total_victors)
send_message("<span class='big comradio'>!! [uppertext(winner.name)] VICTORY !!</span>")
// var/client/winner_client = GLOB.directory[winner.player_key]
// winner_client?.give_award(winner.winner_award, winner.body)
award_role(winner.winner_award, winner)
solo_end = TRUE
if(solo_end)
start_the_end()
@@ -345,28 +344,39 @@
if(blocked_victory)
return FALSE
if(alive_mafia == 0)
// for(var/datum/mafia_role/townie in total_town)
// var/client/townie_client = GLOB.directory[townie.player_key]
// townie_client?.give_award(townie.winner_award, townie.body)
for(var/datum/mafia_role/townie in total_town)
award_role(townie.winner_award, townie)
start_the_end("<span class='big green'>!! TOWN VICTORY !!</span>")
return TRUE
else if(alive_mafia >= alive_town) //guess could change if town nightkill is added
start_the_end("<span class='big red'>!! MAFIA VICTORY !!</span>")
// for(var/datum/mafia_role/changeling in total_mafia)
// var/client/changeling_client = GLOB.directory[changeling.player_key]
// changeling_client?.give_award(changeling.winner_award, changeling.body)
for(var/datum/mafia_role/changeling in total_mafia)
award_role(changeling.winner_award, changeling)
return TRUE
/**
* The end of the game is in two procs, because we want a bit of time for players to see eachothers roles.
* Because of how check_victory works, the game is halted in other places by this point.
*
* What players do in this phase:
* * See everyone's role postgame
* * See who won the game
* Arguments:
* * message: string, if non-null it sends it to all players. used to announce team victories while solos are handled in check victory
*/
* Lets the game award roles with all their checks and sanity, prevents achievements given out for debug games
*
* Arguments:
* * award: path of the award
* * role: mafia_role datum to reward.
*/
/datum/mafia_controller/proc/award_role(award, datum/mafia_role/rewarded)
if(custom_setup.len)
return
var/client/role_client = GLOB.directory[rewarded.player_key]
role_client?.give_award(award, rewarded.body)
/**
* The end of the game is in two procs, because we want a bit of time for players to see eachothers roles.
* Because of how check_victory works, the game is halted in other places by this point.
*
* What players do in this phase:
* * See everyone's role postgame
* * See who won the game
* Arguments:
* * message: string, if non-null it sends it to all players. used to announce team victories while solos are handled in check victory
*/
/datum/mafia_controller/proc/start_the_end(message)
SEND_SIGNAL(src,COMSIG_MAFIA_GAME_END)
if(message)
@@ -377,8 +387,8 @@
next_phase_timer = addtimer(CALLBACK(src,.proc/end_game),victory_lap_period,TIMER_STOPPABLE)
/**
* Cleans up the game, resetting variables back to the beginning and removing the map with the generator.
*/
* Cleans up the game, resetting variables back to the beginning and removing the map with the generator.
*/
/datum/mafia_controller/proc/end_game()
map_deleter.generate() //remove the map, it will be loaded at the start of the next one
QDEL_LIST(all_roles)
@@ -392,17 +402,17 @@
phase = MAFIA_PHASE_SETUP
/**
* After the voting and judgement phases, the game goes to night shutting the windows and beginning night with a proc.
*/
* After the voting and judgement phases, the game goes to night shutting the windows and beginning night with a proc.
*/
/datum/mafia_controller/proc/lockdown()
toggle_night_curtains(close=TRUE)
start_night()
/**
* Shuts poddoors attached to mafia.
* Arguments:
* * close: boolean, the state you want the curtains in.
*/
* Shuts poddoors attached to mafia.
* Arguments:
* * close: boolean, the state you want the curtains in.
*/
/datum/mafia_controller/proc/toggle_night_curtains(close)
for(var/obj/machinery/door/poddoor/D in GLOB.machines) //I really dislike pathing of these
if(D.id != "mafia") //so as to not trigger shutters on station, lol
@@ -413,12 +423,12 @@
INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/open)
/**
* The actual start of night for players. Mostly info is given at the start of the night as the end of the night is when votes and actions are submitted and tried.
*
* What players do in this phase:
* * Mafia are told to begin voting on who to kill
* * Powers that are picked during the day announce themselves right now
*/
* The actual start of night for players. Mostly info is given at the start of the night as the end of the night is when votes and actions are submitted and tried.
*
* What players do in this phase:
* * Mafia are told to begin voting on who to kill
* * Powers that are picked during the day announce themselves right now
*/
/datum/mafia_controller/proc/start_night()
phase = MAFIA_PHASE_NIGHT
send_message("<b>Night [turn] started! Lockdown will end in 45 seconds.</b>")
@@ -427,16 +437,16 @@
SStgui.update_uis(src)
/**
* The end of the night, and a series of signals for the order of events on a night.
*
* Order of events, and what they mean:
* * Start of resolve (NIGHT_START) is for activating night abilities that MUST go first
* * Action phase (NIGHT_ACTION_PHASE) is for non-lethal day abilities
* * Mafia then tallies votes and kills the highest voted person (note: one random voter visits that person for the purposes of roleblocking)
* * Killing phase (NIGHT_KILL_PHASE) is for lethal night abilities
* * End of resolve (NIGHT_END) is for cleaning up abilities that went off and i guess doing some that must go last
* * Finally opens the curtains and calls the start of day phase, completing the cycle until check victory returns TRUE
*/
* The end of the night, and a series of signals for the order of events on a night.
*
* Order of events, and what they mean:
* * Start of resolve (NIGHT_START) is for activating night abilities that MUST go first
* * Action phase (NIGHT_ACTION_PHASE) is for non-lethal day abilities
* * Mafia then tallies votes and kills the highest voted person (note: one random voter visits that person for the purposes of roleblocking)
* * Killing phase (NIGHT_KILL_PHASE) is for lethal night abilities
* * End of resolve (NIGHT_END) is for cleaning up abilities that went off and i guess doing some that must go last
* * Finally opens the curtains and calls the start of day phase, completing the cycle until check victory returns TRUE
*/
/datum/mafia_controller/proc/resolve_night()
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_START)
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_ACTION_PHASE)
@@ -457,15 +467,15 @@
SStgui.update_uis(src)
/**
* Proc that goes off when players vote for something with their mafia panel.
*
* If teams, it hides the tally overlay and only sends the vote messages to the team that is voting
* Arguments:
* * voter: the mafia role that is trying to vote for...
* * target: the mafia role that is getting voted for
* * vote_type: type of vote submitted (is this the day vote? is this the mafia night vote?)
* * teams: see mafia team defines for what to put in, makes the messages only send to a specific team (so mafia night votes only sending messages to mafia at night)
*/
* Proc that goes off when players vote for something with their mafia panel.
*
* If teams, it hides the tally overlay and only sends the vote messages to the team that is voting
* Arguments:
* * voter: the mafia role that is trying to vote for...
* * target: the mafia role that is getting voted for
* * vote_type: type of vote submitted (is this the day vote? is this the mafia night vote?)
* * teams: see mafia team defines for what to put in, makes the messages only send to a specific team (so mafia night votes only sending messages to mafia at night)
*/
/datum/mafia_controller/proc/vote_for(datum/mafia_role/voter,datum/mafia_role/target,vote_type, teams)
if(!votes[vote_type])
votes[vote_type] = list()
@@ -485,8 +495,8 @@
old.body.update_icon()
/**
* Clears out the votes of a certain type (day votes, mafia kill votes) while leaving others untouched
*/
* Clears out the votes of a certain type (day votes, mafia kill votes) while leaving others untouched
*/
/datum/mafia_controller/proc/reset_votes(vote_type)
var/list/bodies_to_update = list()
for(var/vote in votes[vote_type])
@@ -497,11 +507,11 @@
M.update_icon()
/**
* Returns how many people voted for the role, in whatever vote (day vote, night kill vote)
* Arguments:
* * role: the mafia role the proc tries to get the amount of votes for
* * vote_type: the vote type (getting how many day votes were for the role, or mafia night votes for the role)
*/
* Returns how many people voted for the role, in whatever vote (day vote, night kill vote)
* Arguments:
* * role: the mafia role the proc tries to get the amount of votes for
* * vote_type: the vote type (getting how many day votes were for the role, or mafia night votes for the role)
*/
/datum/mafia_controller/proc/get_vote_count(role,vote_type)
. = 0
for(var/v in votes[vote_type])
@@ -510,11 +520,11 @@
. += votee.vote_power
/**
* Returns whichever role got the most votes, in whatever vote (day vote, night kill vote)
* returns null if no votes
* Arguments:
* * vote_type: the vote type (getting the role that got the most day votes, or the role that got the most mafia votes)
*/
* Returns whichever role got the most votes, in whatever vote (day vote, night kill vote)
* returns null if no votes
* Arguments:
* * vote_type: the vote type (getting the role that got the most day votes, or the role that got the most mafia votes)
*/
/datum/mafia_controller/proc/get_vote_winner(vote_type)
var/list/tally = list()
for(var/votee in votes[vote_type])
@@ -526,21 +536,23 @@
return length(tally) ? tally[1] : null
/**
* Returns a random person who voted for whatever vote (day vote, night kill vote)
* Arguments:
* * vote_type: vote type (getting a random day voter, or mafia night voter)
*/
* Returns a random person who voted for whatever vote (day vote, night kill vote)
* Arguments:
* * vote_type: vote type (getting a random day voter, or mafia night voter)
*/
/datum/mafia_controller/proc/get_random_voter(vote_type)
if(length(votes[vote_type]))
return pick(votes[vote_type])
/**
* Adds mutable appearances to people who get publicly voted on (so not night votes) showing how many people are picking them
* Arguments:
* * source: the body of the role getting the overlays
* * overlay_list: signal var passing the overlay list of the mob
*/
* Adds mutable appearances to people who get publicly voted on (so not night votes) showing how many people are picking them
* Arguments:
* * source: the body of the role getting the overlays
* * overlay_list: signal var passing the overlay list of the mob
*/
/datum/mafia_controller/proc/display_votes(atom/source, list/overlay_list)
SIGNAL_HANDLER
if(phase != MAFIA_PHASE_VOTING)
return
var/v = get_vote_count(player_role_lookup[source],"Day")
@@ -548,14 +560,14 @@
overlay_list += MA
/**
* Called when the game is setting up, AFTER map is loaded but BEFORE the phase timers start. Creates and places each role's body and gives the correct player key
*
* Notably:
* * Toggles godmode so the mafia players cannot kill themselves
* * Adds signals for voting overlays, see display_votes proc
* * gives mafia panel
* * sends the greeting text (goals, role name, etc)
*/
* Called when the game is setting up, AFTER map is loaded but BEFORE the phase timers start. Creates and places each role's body and gives the correct player key
*
* Notably:
* * Toggles godmode so the mafia players cannot kill themselves
* * Adds signals for voting overlays, see display_votes proc
* * gives mafia panel
* * sends the greeting text (goals, role name, etc)
*/
/datum/mafia_controller/proc/create_bodies()
for(var/datum/mafia_role/role in all_roles)
var/mob/living/carbon/human/H = new(get_turf(role.assigned_landmark))
@@ -716,13 +728,14 @@
if(GLOB.mafia_signup[C.ckey])
GLOB.mafia_signup -= C.ckey
to_chat(usr, "<span class='notice'>You unregister from Mafia.</span>")
return
return TRUE
else
GLOB.mafia_signup[C.ckey] = C
to_chat(usr, "<span class='notice'>You sign up for Mafia.</span>")
if(phase == MAFIA_PHASE_SETUP)
check_signups()
try_autostart()
return TRUE
if("mf_spectate")
if(C.ckey in spectators)
to_chat(usr, "<span class='notice'>You will no longer get messages from the game.</span>")
@@ -730,6 +743,7 @@
else
to_chat(usr, "<span class='notice'>You will now get messages from the game.</span>")
spectators += C.ckey
return TRUE
if(user_role.game_status == MAFIA_DEAD)
return
//User actions (just living)
@@ -800,13 +814,13 @@
. += L[key]
/**
* Returns a semirandom setup, with...
* Town, Two invest roles, one protect role, sometimes a misc role, and the rest assistants for town.
* Mafia, 2 normal mafia and one special.
* Neutral, two disruption roles, sometimes one is a killing.
*
* See _defines.dm in the mafia folder for a rundown on what these groups of roles include.
*/
* Returns a semirandom setup, with...
* Town, Two invest roles, one protect role, sometimes a misc role, and the rest assistants for town.
* Mafia, 2 normal mafia and one special.
* Neutral, two disruption roles, sometimes one is a killing.
*
* See _defines.dm in the mafia folder for a rundown on what these groups of roles include.
*/
/datum/mafia_controller/proc/generate_random_setup()
var/invests_left = 2
var/protects_left = 1
@@ -845,8 +859,8 @@
return random_setup
/**
* Helper proc that adds a random role of a type to a setup. if it doesn't exist in the setup, it adds the path to the list and otherwise bumps the path in the list up one
*/
* Helper proc that adds a random role of a type to a setup. if it doesn't exist in the setup, it adds the path to the list and otherwise bumps the path in the list up one
*/
/datum/mafia_controller/proc/add_setup_role(setup_list, wanted_role_type)
var/list/role_type_paths = list()
for(var/path in typesof(/datum/mafia_role))
@@ -868,17 +882,17 @@
setup_list[mafia_path] = 1
/**
* Called when enough players have signed up to fill a setup. DOESN'T NECESSARILY MEAN THE GAME WILL START.
*
* Checks for a custom setup, if so gets the required players from that and if not it sets the player requirement to required_player(max_player) and generates one IF basic setup starts a game.
* Checks if everyone signed up is an observer, and is still connected. If people aren't, they're removed from the list.
* If there aren't enough players post sanity, it aborts. otherwise, it selects enough people for the game and starts preparing the game for real.
*/
* Called when enough players have signed up to fill a setup. DOESN'T NECESSARILY MEAN THE GAME WILL START.
*
* Checks for a custom setup, if so gets the required players from that and if not it sets the player requirement to MAFIA_MAX_PLAYER_COUNT and generates one IF basic setup starts a game.
* Checks if everyone signed up is an observer, and is still connected. If people aren't, they're removed from the list.
* If there aren't enough players post sanity, it aborts. otherwise, it selects enough people for the game and starts preparing the game for real.
*/
/datum/mafia_controller/proc/basic_setup()
var/req_players
var/list/setup = custom_setup
if(!setup.len)
req_players = required_player //max_player
req_players = max_player //MAFIA_MAX_PLAYER_COUNT
else
req_players = assoc_value_sum(setup)
@@ -918,10 +932,10 @@
start_game()
/**
* Called when someone signs up, and sees if there are enough people in the signup list to begin.
*
* Only checks if everyone is actually valid to start (still connected and an observer) if there are enough players (basic_setup)
*/
* Called when someone signs up, and sees if there are enough people in the signup list to begin.
*
* Only checks if everyone is actually valid to start (still connected and an observer) if there are enough players (basic_setup)
*/
/datum/mafia_controller/proc/try_autostart()
if(phase != MAFIA_PHASE_SETUP) // || !(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME))
return
@@ -929,10 +943,10 @@
basic_setup()
/**
* Filters inactive player into a different list until they reconnect, and removes players who are no longer ghosts.
*
* If a disconnected player gets a non-ghost mob and reconnects, they will be first put back into mafia_signup then filtered by that.
*/
* Filters inactive player into a different list until they reconnect, and removes players who are no longer ghosts.
*
* If a disconnected player gets a non-ghost mob and reconnects, they will be first put back into mafia_signup then filtered by that.
*/
/datum/mafia_controller/proc/check_signups()
for(var/bad_key in GLOB.mafia_bad_signup)
if(GLOB.directory[bad_key])//they have reconnected if we can search their key and get a client
@@ -962,8 +976,8 @@
parent.ui_interact(owner)
/**
* Creates the global datum for playing mafia games, destroys the last if that's required and returns the new.
*/
* Creates the global datum for playing mafia games, destroys the last if that's required and returns the new.
*/
/proc/create_mafia_game()
if(GLOB.mafia_game)
QDEL_NULL(GLOB.mafia_game)

View File

@@ -19,7 +19,7 @@
var/list/actions = list()
var/list/targeted_actions = list()
//what the role gets when it wins a game
// var/winner_award = /datum/award/achievement/mafia/assistant
var/winner_award = /datum/award/achievement/mafia/assistant
//so mafia have to also kill them to have a majority
var/solo_counts_as_town = FALSE //(don't set this for town)
@@ -124,7 +124,7 @@
desc = "You can investigate a single person each night to learn their team."
revealed_outfit = /datum/outfit/mafia/detective
role_type = TOWN_INVEST
// winner_award = /datum/award/achievement/mafia/detective
winner_award = /datum/award/achievement/mafia/detective
hud_icon = "huddetective"
revealed_icon = "detective"
@@ -151,6 +151,8 @@
current_investigation = target
/datum/mafia_role/detective/proc/investigate(datum/mafia_controller/game)
SIGNAL_HANDLER
var/datum/mafia_role/target = current_investigation
if(target)
if(target.detect_immune)
@@ -178,7 +180,7 @@
desc = "You can visit someone ONCE PER GAME to reveal their true role in the morning!"
revealed_outfit = /datum/outfit/mafia/psychologist
role_type = TOWN_INVEST
// winner_award = /datum/award/achievement/mafia/psychologist
winner_award = /datum/award/achievement/mafia/psychologist
hud_icon = "hudpsychologist"
revealed_icon = "psychologist"
@@ -202,6 +204,8 @@
current_target = target
/datum/mafia_role/psychologist/proc/therapy_reveal(datum/mafia_controller/game)
SIGNAL_HANDLER
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"reveal",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by a lawyer.
current_target = null
if(current_target)
@@ -218,7 +222,7 @@
role_type = TOWN_INVEST
hud_icon = "hudchaplain"
revealed_icon = "chaplain"
// winner_award = /datum/award/achievement/mafia/chaplain
winner_award = /datum/award/achievement/mafia/chaplain
targeted_actions = list("Pray")
var/current_target
@@ -238,6 +242,8 @@
current_target = target
/datum/mafia_role/chaplain/proc/commune(datum/mafia_controller/game)
SIGNAL_HANDLER
var/datum/mafia_role/target = current_target
if(target)
to_chat(body,"<span class='warning'>You invoke spirit of [target.body.real_name] and learn their role was <b>[target.name]<b>.</span>")
@@ -251,7 +257,7 @@
role_type = TOWN_PROTECT
hud_icon = "hudmedicaldoctor"
revealed_icon = "medicaldoctor"
// winner_award = /datum/award/achievement/mafia/md
winner_award = /datum/award/achievement/mafia/md
targeted_actions = list("Protect")
var/datum/mafia_role/current_protected
@@ -277,16 +283,22 @@
current_protected = target
/datum/mafia_role/md/proc/protect(datum/mafia_controller/game)
SIGNAL_HANDLER
if(current_protected)
RegisterSignal(current_protected,COMSIG_MAFIA_ON_KILL,.proc/prevent_kill)
add_note("N[game.turn] - Protected [current_protected.body.real_name]")
/datum/mafia_role/md/proc/prevent_kill(datum/source)
SIGNAL_HANDLER
to_chat(body,"<span class='warning'>The person you protected tonight was attacked!</span>")
to_chat(current_protected.body,"<span class='userdanger'>You were attacked last night, but someone nursed you back to life!</span>")
return MAFIA_PREVENT_KILL
/datum/mafia_role/md/proc/end_protection(datum/mafia_controller/game)
SIGNAL_HANDLER
if(current_protected)
UnregisterSignal(current_protected,COMSIG_MAFIA_ON_KILL)
current_protected = null
@@ -298,7 +310,7 @@
role_type = TOWN_PROTECT
hud_icon = "hudlawyer"
revealed_icon = "lawyer"
// winner_award = /datum/award/achievement/mafia/lawyer
winner_award = /datum/award/achievement/mafia/lawyer
targeted_actions = list("Advise")
var/datum/mafia_role/current_target
@@ -310,6 +322,8 @@
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/release)
/datum/mafia_role/lawyer/proc/roleblock_text(datum/mafia_controller/game)
SIGNAL_HANDLER
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"roleblock",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by another lawyer.
current_target = null
if(current_target)
@@ -335,16 +349,22 @@
to_chat(body,"<span class='warning'>You will block [target.body.real_name] tonight.</span>")
/datum/mafia_role/lawyer/proc/try_to_roleblock(datum/mafia_controller/game)
SIGNAL_HANDLER
if(current_target)
RegisterSignal(current_target,COMSIG_MAFIA_CAN_PERFORM_ACTION, .proc/prevent_action)
/datum/mafia_role/lawyer/proc/release(datum/mafia_controller/game)
SIGNAL_HANDLER
. = ..()
if(current_target)
UnregisterSignal(current_target, COMSIG_MAFIA_CAN_PERFORM_ACTION)
current_target = null
/datum/mafia_role/lawyer/proc/prevent_action(datum/source)
SIGNAL_HANDLER
if(game_status == MAFIA_ALIVE) //in case we got killed while imprisoning sk - bad luck edge
return MAFIA_PREVENT_ACTION
@@ -355,7 +375,7 @@
role_type = TOWN_MISC
hud_icon = "hudheadofpersonnel"
revealed_icon = "headofpersonnel"
// winner_award = /datum/award/achievement/mafia/hop
winner_award = /datum/award/achievement/mafia/hop
targeted_actions = list("Reveal")
@@ -378,7 +398,7 @@
role_type = MAFIA_REGULAR
hud_icon = "hudchangeling"
revealed_icon = "changeling"
// winner_award = /datum/award/achievement/mafia/changeling
winner_award = /datum/award/achievement/mafia/changeling
revealed_outfit = /datum/outfit/mafia/changeling
special_theme = "syndicate"
@@ -389,6 +409,8 @@
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/mafia_text)
/datum/mafia_role/mafia/proc/mafia_text(datum/mafia_controller/source)
SIGNAL_HANDLER
to_chat(body,"<b>Vote for who to kill tonight. The killer will be chosen randomly from voters.</b>")
//better detective for mafia
@@ -398,7 +420,7 @@
role_type = MAFIA_SPECIAL
hud_icon = "hudthoughtfeeder"
revealed_icon = "thoughtfeeder"
// winner_award = /datum/award/achievement/mafia/thoughtfeeder
winner_award = /datum/award/achievement/mafia/thoughtfeeder
targeted_actions = list("Learn Role")
var/datum/mafia_role/current_investigation
@@ -418,6 +440,8 @@
current_investigation = target
/datum/mafia_role/mafia/thoughtfeeder/proc/investigate(datum/mafia_controller/game)
SIGNAL_HANDLER
var/datum/mafia_role/target = current_investigation
current_investigation = null
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"thoughtfeed",target) & MAFIA_PREVENT_ACTION)
@@ -441,7 +465,7 @@
win_condition = "kill everyone."
team = MAFIA_TEAM_SOLO
role_type = NEUTRAL_KILL
// winner_award = /datum/award/achievement/mafia/traitor
winner_award = /datum/award/achievement/mafia/traitor
targeted_actions = list("Night Kill")
revealed_outfit = /datum/outfit/mafia/traitor
@@ -464,6 +488,8 @@
return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point)
/datum/mafia_role/traitor/proc/nightkill_immunity(datum/source,datum/mafia_controller/game,lynch)
SIGNAL_HANDLER
if(game.phase == MAFIA_PHASE_NIGHT && !lynch)
to_chat(body,"<span class='userdanger'>You were attacked, but they'll have to try harder than that to put you down.</span>")
return MAFIA_PREVENT_KILL
@@ -481,6 +507,8 @@
to_chat(body,"<span class='warning'>You will attempt to kill [target.body.real_name] tonight.</span>")
/datum/mafia_role/traitor/proc/try_to_kill(datum/mafia_controller/source)
SIGNAL_HANDLER
var/datum/mafia_role/target = current_victim
current_victim = null
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"traitor kill",target) & MAFIA_PREVENT_ACTION)
@@ -500,7 +528,7 @@
special_theme = "neutral"
hud_icon = "hudnightmare"
revealed_icon = "nightmare"
// winner_award = /datum/award/achievement/mafia/nightmare
winner_award = /datum/award/achievement/mafia/nightmare
targeted_actions = list("Flicker", "Hunt")
var/list/flickering = list()
@@ -543,6 +571,8 @@
to_chat(body,"<span class='danger'>You will hunt everyone in a flickering room down tonight.</span>")
/datum/mafia_role/nightmare/proc/flicker_or_hunt(datum/mafia_controller/source)
SIGNAL_HANDLER
if(game_status != MAFIA_ALIVE || !flicker_target)
return
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"nightmare actions",flicker_target) & MAFIA_PREVENT_ACTION)
@@ -576,7 +606,7 @@
special_theme = "neutral"
hud_icon = "hudfugitive"
revealed_icon = "fugitive"
// winner_award = /datum/award/achievement/mafia/fugitive
winner_award = /datum/award/achievement/mafia/fugitive
actions = list("Self Preservation")
var/charges = 2
@@ -604,11 +634,15 @@
protection_status = !protection_status
/datum/mafia_role/fugitive/proc/night_start(datum/mafia_controller/game)
SIGNAL_HANDLER
if(protection_status == FUGITIVE_WILL_PRESERVE)
to_chat(body,"<span class='danger'>Your preparations are complete. Nothing could kill you tonight!</span>")
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prevent_death)
/datum/mafia_role/fugitive/proc/night_end(datum/mafia_controller/game)
SIGNAL_HANDLER
if(protection_status == FUGITIVE_WILL_PRESERVE)
charges--
UnregisterSignal(src,COMSIG_MAFIA_ON_KILL)
@@ -616,13 +650,16 @@
protection_status = FUGITIVE_NOT_PRESERVING
/datum/mafia_role/fugitive/proc/prevent_death(datum/mafia_controller/game)
SIGNAL_HANDLER
to_chat(body,"<span class='userdanger'>You were attacked! Luckily, you were ready for this!</span>")
return MAFIA_PREVENT_KILL
/datum/mafia_role/fugitive/proc/survived(datum/mafia_controller/game)
SIGNAL_HANDLER
if(game_status == MAFIA_ALIVE)
// var/client/winner_client = GLOB.directory[player_key]
// winner_client?.give_award(winner_award, body)
game.award_role(winner_award, src)
game.send_message("<span class='big comradio'>!! FUGITIVE VICTORY !!</span>")
#undef FUGITIVE_NOT_PRESERVING
@@ -640,7 +677,7 @@
hud_icon = "hudobsessed"
revealed_icon = "obsessed"
// winner_award = /datum/award/achievement/mafia/obsessed
winner_award = /datum/award/achievement/mafia/obsessed
revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective!
@@ -652,6 +689,8 @@
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/find_obsession)
/datum/mafia_role/obsessed/proc/find_obsession(datum/mafia_controller/game)
SIGNAL_HANDLER
var/list/all_roles_shuffle = shuffle(game.all_roles)
for(var/role in all_roles_shuffle)
var/datum/mafia_role/possible = role
@@ -667,13 +706,14 @@
UnregisterSignal(game,COMSIG_MAFIA_SUNDOWN)
/datum/mafia_role/obsessed/proc/check_victory(datum/source,datum/mafia_controller/game,lynch)
SIGNAL_HANDLER
UnregisterSignal(source,COMSIG_MAFIA_ON_KILL)
if(game_status == MAFIA_DEAD)
return
if(lynch)
game.send_message("<span class='big comradio'>!! OBSESSED VICTORY !!</span>")
// var/client/winner_client = GLOB.directory[player_key]
// winner_client?.give_award(winner_award, body)
game.award_role(winner_award, src)
reveal_role(game, FALSE)
else
to_chat(body, "<span class='userdanger'>You have failed your objective to lynch [obsession.body]!</span>")
@@ -689,17 +729,18 @@
special_theme = "neutral"
hud_icon = "hudclown"
revealed_icon = "clown"
// winner_award = /datum/award/achievement/mafia/clown
winner_award = /datum/award/achievement/mafia/clown
/datum/mafia_role/clown/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prank)
/datum/mafia_role/clown/proc/prank(datum/source,datum/mafia_controller/game,lynch)
SIGNAL_HANDLER
if(lynch)
var/datum/mafia_role/victim = pick(game.judgement_guilty_votes + game.judgement_abstain_votes)
game.send_message("<span class='big clown'>[body.real_name] WAS A CLOWN! HONK! They take down [victim.body.real_name] with their last prank.</span>")
game.send_message("<span class='big clown'>!! CLOWN VICTORY !!</span>")
// var/client/winner_client = GLOB.directory[player_key]
// winner_client?.give_award(winner_award, body)
game.award_role(winner_award, src)
victim.kill(game,FALSE)

View File

@@ -75,6 +75,7 @@
var/lastpuke = 0
var/account_id
var/last_fire_update
var/hardcore_survival_score = 0
/// Unarmed parry data for human
/datum/block_parry_data/unarmed/human

View File

@@ -14,8 +14,8 @@
model = "Cleanbot"
bot_core_type = /obj/machinery/bot_core/cleanbot
window_id = "autoclean"
window_name = "Automatic Station Cleaner v1.3"
pass_flags = PASSMOB
window_name = "Automatic Station Cleaner v1.4"
pass_flags = PASSMOB // | PASSFLAPS
path_image_color = "#993299"
weather_immunities = list("lava","ash")
@@ -25,6 +25,7 @@
var/blood = 1
var/trash = 0
var/pests = 0
var/drawn = 0
var/list/target_types
var/obj/effect/decal/cleanable/target
@@ -53,6 +54,9 @@
var/list/prefixes
var/list/suffixes
var/ascended = FALSE // if we have all the top titles, grant achievements to living mobs that gaze upon our cleanbot god
/mob/living/simple_animal/bot/cleanbot/proc/deputize(obj/item/W, mob/user)
if(in_range(src, user))
to_chat(user, "<span class='notice'>You attach \the [W] to \the [src].</span>")
@@ -66,6 +70,8 @@
/mob/living/simple_animal/bot/cleanbot/proc/update_titles()
var/working_title = ""
ascended = TRUE
for(var/pref in prefixes)
for(var/title in pref)
if(title in stolen_valor)
@@ -73,6 +79,8 @@
if(title in officers)
commissioned = TRUE
break
else
ascended = FALSE // we didn't have the first entry in the list if we got here, so we're not achievement worthy yet
working_title += chosen_name
@@ -81,6 +89,8 @@
if(title in stolen_valor)
working_title += " " + suf[title]
break
else
ascended = FALSE
name = working_title
@@ -89,8 +99,12 @@
if(weapon)
. += " <span class='warning'>Is that \a [weapon] taped to it...?</span>"
if(ascended && user.stat == CONSCIOUS && user.client)
user.client.give_award(/datum/award/achievement/misc/cleanboss, user)
/mob/living/simple_animal/bot/cleanbot/Initialize()
. = ..()
chosen_name = name
get_targets()
icon_state = "cleanbot[on]"
@@ -98,7 +112,6 @@
var/datum/job/janitor/J = new/datum/job/janitor
access_card.access += J.get_access()
prev_access = access_card.access
stolen_valor = list()
prefixes = list(command, security, engineering)
@@ -123,7 +136,7 @@
/mob/living/simple_animal/bot/cleanbot/bot_reset()
..()
if(weapon && (emagged == 2))
if(weapon && emagged == 2)
weapon.force = weapon_orig_force
ignore_list = list() //Allows the bot to clean targets it previously ignored due to being unreachable.
target = null
@@ -151,7 +164,7 @@
C.Knockdown(20)
/mob/living/simple_animal/bot/cleanbot/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda))
if(W.GetID())
if(bot_core.allowed(user) && !open && !emagged)
locked = !locked
to_chat(user, "<span class='notice'>You [ locked ? "lock" : "unlock"] \the [src] behaviour controls.</span>")
@@ -161,7 +174,7 @@
if(open)
to_chat(user, "<span class='warning'>Please close the access panel before locking it.</span>")
else
to_chat(user, "<span class='notice'>The [src] doesn't seem to respect your authority.</span>")
to_chat(user, "<span class='notice'>\The [src] doesn't seem to respect your authority.</span>")
else if(istype(W, /obj/item/kitchen/knife) && user.a_intent != INTENT_HARM)
to_chat(user, "<span class='notice'>You start attaching \the [W] to \the [src]...</span>")
@@ -203,7 +216,8 @@
return ..()
/mob/living/simple_animal/bot/cleanbot/emag_act(mob/user)
. = ..()
..()
if(emagged == 2)
if(weapon)
weapon.force = weapon_orig_force
@@ -259,6 +273,9 @@
if(!target && trash) //Then for trash.
target = scan(/obj/item/trash)
// if(!target && trash) //Search for dead mices.
// target = scan(/obj/item/food/deadmouse)
if(!target && auto_patrol) //Search for cleanables it can see.
if(mode == BOT_IDLE || mode == BOT_START_PATROL)
start_patrol()
@@ -335,13 +352,18 @@
target_types += /mob/living/simple_animal/cockroach
target_types += /mob/living/simple_animal/mouse
if(drawn)
target_types += /obj/effect/decal/cleanable/crayon
if(trash)
target_types += /obj/item/trash
target_types += /obj/item/reagent_containers/food/snacks/meat/slab/human
target_types = typecacheof(target_types)
/mob/living/simple_animal/bot/cleanbot/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
/mob/living/simple_animal/bot/cleanbot/UnarmedAttack(atom/A)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
return
if(istype(A, /obj/effect/decal/cleanable))
anchored = TRUE
icon_state = "cleanbot-c"
@@ -361,8 +383,9 @@
icon_state = "cleanbot[on]"
else if(istype(A, /obj/item) || istype(A, /obj/effect/decal/remains))
visible_message("<span class='danger'>[src] sprays hydrofluoric acid at [A]!</span>")
playsound(src, 'sound/effects/spray2.ogg', 50, 1, -6)
playsound(src, 'sound/effects/spray2.ogg', 50, TRUE, -6)
A.acid_act(75, 10)
target = null
else if(istype(A, /mob/living/simple_animal/cockroach) || istype(A, /mob/living/simple_animal/mouse))
var/mob/living/simple_animal/M = target
if(!M.stat)
@@ -383,7 +406,7 @@
"FREED AT LEST FROM FILTHY PROGRAMMING.")
say(phrase)
victim.emote("scream")
playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1, -6)
playsound(src.loc, 'sound/effects/spray2.ogg', 50, TRUE, -6)
victim.acid_act(5, 100)
else if(A == src) // Wets floors and spawns foam randomly
if(prob(75))
@@ -412,10 +435,14 @@
do_sparks(3, TRUE, src)
..()
/mob/living/simple_animal/bot/cleanbot/medbay
name = "Scrubs, MD"
bot_core_type = /obj/machinery/bot_core/cleanbot/medbay
on = FALSE
/obj/machinery/bot_core/cleanbot
req_one_access = list(ACCESS_JANITOR, ACCESS_ROBOTICS)
/mob/living/simple_animal/bot/cleanbot/get_controls(mob/user)
var/dat
dat += hack(user)
@@ -424,9 +451,10 @@
Status: <A href='?src=[REF(src)];power=1'>[on ? "On" : "Off"]</A><BR>
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
Maintenance panel panel is [open ? "opened" : "closed"]"})
if(!locked || hasSiliconAccessInArea(user)|| IsAdminGhost(user))
if(!locked || issilicon(user)|| IsAdminGhost(user))
dat += "<BR>Clean Blood: <A href='?src=[REF(src)];operation=blood'>[blood ? "Yes" : "No"]</A>"
dat += "<BR>Clean Trash: <A href='?src=[REF(src)];operation=trash'>[trash ? "Yes" : "No"]</A>"
dat += "<BR>Clean Graffiti: <A href='?src=[REF(src)];operation=drawn'>[drawn ? "Yes" : "No"]</A>"
dat += "<BR>Exterminate Pests: <A href='?src=[REF(src)];operation=pests'>[pests ? "Yes" : "No"]</A>"
dat += "<BR><BR>Patrol Station: <A href='?src=[REF(src)];operation=patrol'>[auto_patrol ? "Yes" : "No"]</A>"
return dat
@@ -442,5 +470,10 @@ Maintenance panel panel is [open ? "opened" : "closed"]"})
pests = !pests
if("trash")
trash = !trash
if("drawn")
drawn = !drawn
get_targets()
update_controls()
/obj/machinery/bot_core/cleanbot/medbay
req_one_access = list(ACCESS_JANITOR, ACCESS_ROBOTICS, ACCESS_MEDICAL)

View File

@@ -44,6 +44,9 @@ Difficulty: Medium
wander = FALSE
del_on_death = TRUE
blood_volume = BLOOD_VOLUME_NORMAL
achievement_type = /datum/award/achievement/boss/blood_miner_kill
crusher_achievement_type = /datum/award/achievement/boss/blood_miner_crusher
score_achievement_type = /datum/award/score/blood_miner_score
medal_type = BOSS_MEDAL_MINER
var/obj/item/melee/transforming/cleaving_saw/miner/miner_saw
var/time_until_next_transform = 0

View File

@@ -51,8 +51,11 @@ Difficulty: Hard
crusher_loot = list(/obj/structure/closet/crate/necropolis/bubblegum/crusher)
loot = list(/obj/structure/closet/crate/necropolis/bubblegum)
var/charging = 0
medal_type = BOSS_MEDAL_BUBBLEGUM
score_type = BUBBLEGUM_SCORE
achievement_type = /datum/award/achievement/boss/bubblegum_kill
crusher_achievement_type = /datum/award/achievement/boss/bubblegum_crusher
score_achievement_type = /datum/award/score/bubblegum_score
deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... "
death_sound = 'sound/magic/enter_blood.ogg'
@@ -153,6 +156,20 @@ Difficulty: Hard
charging = 0
Goto(target, move_to_delay, minimum_distance)
/**
* Attack by override for bubblegum
*
* This is used to award the frenching achievement for hitting bubblegum with a tongue
*
* Arguments:
* * obj/item/W the item hitting bubblegum
* * mob/user The user of the item
* * params, extra parameters
*/
/mob/living/simple_animal/hostile/megafauna/bubblegum/attackby(obj/item/W, mob/user, params)
. = ..()
if(istype(W, /obj/item/organ/tongue))
user.client?.give_award(/datum/award/achievement/misc/frenching, user)
/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A)
if(charging)

View File

@@ -43,9 +43,10 @@ Difficulty: Very Hard
move_to_delay = 10
ranged = 1
pixel_x = -32
del_on_death = 1
medal_type = BOSS_MEDAL_COLOSSUS
score_type = COLOSSUS_SCORE
del_on_death = TRUE
achievement_type = /datum/award/achievement/boss/colossus_kill
crusher_achievement_type = /datum/award/achievement/boss/colossus_crusher
score_achievement_type = /datum/award/score/colussus_score
crusher_loot = list(/obj/structure/closet/crate/necropolis/colossus/crusher)
loot = list(/obj/structure/closet/crate/necropolis/colossus)
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30)

View File

@@ -31,6 +31,9 @@ Difficulty: Extremely Hard
wander = FALSE
del_on_death = TRUE
blood_volume = BLOOD_VOLUME_NORMAL
achievement_type = /datum/award/achievement/boss/demonic_miner_kill
crusher_achievement_type = /datum/award/achievement/boss/demonic_miner_crusher
score_achievement_type = /datum/award/score/demonic_miner_score
deathmessage = "falls to the ground, decaying into plasma particles."
deathsound = "bodyfall"
attack_action_types = list(/datum/action/innate/megafauna_attack/frost_orbs,

View File

@@ -64,8 +64,9 @@ Difficulty: Medium
guaranteed_butcher_results = list(/obj/item/stack/sheet/animalhide/ashdrake = 10)
var/swooping = NONE
var/swoop_cooldown = 0
medal_type = BOSS_MEDAL_DRAKE
score_type = DRAKE_SCORE
achievement_type = /datum/award/achievement/boss/drake_kill
crusher_achievement_type = /datum/award/achievement/boss/drake_crusher
score_achievement_type = /datum/award/score/drake_score
deathmessage = "collapses into a pile of bones, its flesh sloughing away."
death_sound = 'sound/magic/demon_dies.ogg'
var/datum/action/small_sprite/smallsprite = new/datum/action/small_sprite/drake()

View File

@@ -61,8 +61,9 @@ Difficulty: Normal
loot = list(/obj/item/hierophant_club)
crusher_loot = list(/obj/item/hierophant_club)
wander = FALSE
medal_type = BOSS_MEDAL_HIEROPHANT
score_type = HIEROPHANT_SCORE
achievement_type = /datum/award/achievement/boss/hierophant_kill
crusher_achievement_type = /datum/award/achievement/boss/hierophant_crusher
score_achievement_type = /datum/award/score/hierophant_score
del_on_death = TRUE
death_sound = 'sound/magic/repulse.ogg'

View File

@@ -42,8 +42,9 @@ SHITCODE AHEAD. BE ADVISED. Also comment extravaganza
retreat_distance = 5
minimum_distance = 5
ranged_cooldown_time = 10
medal_type = BOSS_MEDAL_LEGION
score_type = LEGION_SCORE
achievement_type = /datum/award/achievement/boss/legion_kill
crusher_achievement_type = /datum/award/achievement/boss/legion_crusher
score_achievement_type = /datum/award/score/legion_score
pixel_y = -16
pixel_x = -32
loot = list(/obj/item/stack/sheet/bone = 3)

View File

@@ -28,24 +28,31 @@
layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise
flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1
has_field_of_vision = FALSE //You are a frikkin boss
/// Crusher loot dropped when fauna killed with a crusher
/// Crusher loot dropped when the megafauna is killed with a crusher
var/list/crusher_loot
var/medal_type
/// Score given to players when the fauna is killed
var/score_type = BOSS_SCORE
/// If the megafauna is actually killed (vs entering another phase)
/// Achievement given to surrounding players when the megafauna is killed
var/achievement_type
/// Crusher achievement given to players when megafauna is killed
var/crusher_achievement_type
/// Score given to players when megafauna is killed
var/score_achievement_type
/// If the megafauna was actually killed (not just dying, then transforming into another type)
var/elimination = 0
/// Modifies attacks when at lower health
var/anger_modifier = 0
/// Internal tracking GPS inside fauna
var/obj/item/gps/internal
/// Next time fauna can use a melee attack
/// Next time the megafauna can use a melee attack
var/recovery_time = 0
var/true_spawn = TRUE // if this is a megafauna that should grant achievements, or have a gps signal
/// If this is a megafauna that is real (has achievements, gps signal)
var/true_spawn = TRUE
/// Range the megafauna can move from their nest (if they have one
var/nest_range = 10
var/chosen_attack = 1 // chosen attack num
/// The chosen attack by the megafauna
var/chosen_attack = 1
/// Attack actions, sets chosen_attack to the number in the action
var/list/attack_action_types = list()
/// If there is a small sprite icon for players controlling the megafauna to use
var/small_sprite_type
/mob/living/simple_animal/hostile/megafauna/Initialize(mapload)
@@ -73,23 +80,22 @@
return
return ..()
/mob/living/simple_animal/hostile/megafauna/death(gibbed)
/mob/living/simple_animal/hostile/megafauna/death(gibbed, list/force_grant)
if(health > 0)
return
else
var/datum/status_effect/crusher_damage/C = has_status_effect(STATUS_EFFECT_CRUSHERDAMAGETRACKING)
var/crusher_kill = FALSE
if(C && crusher_loot && C.total_damage >= maxHealth * 0.6)
spawn_crusher_loot()
crusher_kill = TRUE
if(!(flags_1 & ADMIN_SPAWNED_1))
var/tab = "megafauna_kills"
if(crusher_kill)
tab = "megafauna_kills_crusher"
var/datum/status_effect/crusher_damage/crusher_dmg = has_status_effect(STATUS_EFFECT_CRUSHERDAMAGETRACKING)
var/crusher_kill = FALSE
if(crusher_dmg && crusher_loot && crusher_dmg.total_damage >= maxHealth * 0.6)
spawn_crusher_loot()
crusher_kill = TRUE
if(true_spawn && !(flags_1 & ADMIN_SPAWNED_1))
var/tab = "megafauna_kills"
if(crusher_kill)
tab = "megafauna_kills_crusher"
if(!elimination) //used so the achievment only occurs for the last legion to die.
grant_achievement(achievement_type, score_achievement_type, crusher_kill, force_grant)
SSblackbox.record_feedback("tally", tab, 1, "[initial(name)]")
if(!elimination) //used so the achievment only occurs for the last legion to die.
grant_achievement(medal_type, score_type, crusher_kill)
..()
return ..()
/mob/living/simple_animal/hostile/megafauna/proc/spawn_crusher_loot()
loot = crusher_loot
@@ -143,26 +149,29 @@
if(EXPLODE_LIGHT)
adjustBruteLoss(50)
/mob/living/simple_animal/hostile/megafauna/proc/SetRecoveryTime(buffer_time)
/// Sets the next time the megafauna can use a melee or ranged attack, in deciseconds
/mob/living/simple_animal/hostile/megafauna/proc/SetRecoveryTime(buffer_time, ranged_buffer_time)
recovery_time = world.time + buffer_time
ranged_cooldown = max(ranged_cooldown, world.time + buffer_time) // CITADEL BANDAID FIX FOR MEGAFAUNA NOT RESPECTING RECOVERY TIME.
ranged_cooldown = world.time + buffer_time
if(ranged_buffer_time)
ranged_cooldown = world.time + ranged_buffer_time
/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill)
if(!medal_type || (flags_1 & ADMIN_SPAWNED_1)) //Don't award medals if the medal type isn't set
/// Grants medals and achievements to surrounding players
/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill, list/grant_achievement = list())
if(!achievement_type || (flags_1 & ADMIN_SPAWNED_1) || !SSachievements.achievements_enabled) //Don't award medals if the medal type isn't set
return FALSE
if(!SSmedals.hub_enabled) // This allows subtypes to carry on other special rewards not tied with medals. (such as bubblegum's arena shuttle)
return TRUE
for(var/mob/living/L in view(7,src))
if(!grant_achievement.len)
for(var/mob/living/L in view(7,src))
grant_achievement += L
for(var/mob/living/L in grant_achievement)
if(L.stat || !L.client)
continue
var/client/C = L.client
SSmedals.UnlockMedal("Boss [BOSS_KILL_MEDAL]", C)
SSmedals.UnlockMedal("[medaltype] [BOSS_KILL_MEDAL]", C)
L.client.give_award(/datum/award/achievement/boss/boss_killer, L)
L.client.give_award(achievement_type, L)
if(crusher_kill && istype(L.get_active_held_item(), /obj/item/kinetic_crusher))
SSmedals.UnlockMedal("[medaltype] [BOSS_KILL_MEDAL_CRUSHER]", C)
SSmedals.SetScore(BOSS_SCORE, C, 1)
SSmedals.SetScore(score_type, C, 1)
L.client.give_award(crusher_achievement_type, L)
L.client.give_award(/datum/award/score/boss_score, L) //Score progression for bosses killed in general
L.client.give_award(score_achievement_type, L) //Score progression for specific boss killed
return TRUE
/datum/action/innate/megafauna_attack

View File

@@ -48,8 +48,9 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
health = 750
maxHealth = 750 //""""low-ish"""" HP because it's a passive boss, and the swarm itself is the real foe
mob_biotypes = MOB_ROBOTIC
medal_type = BOSS_MEDAL_SWARMERS
score_type = SWARMER_BEACON_SCORE
achievement_type = /datum/award/achievement/boss/swarmer_beacon_kill
crusher_achievement_type = /datum/award/achievement/boss/swarmer_beacon_crusher
score_achievement_type = /datum/award/score/swarmer_beacon_score
faction = list("mining", "boss", "swarmer")
weather_immunities = list("lava","ash")
stop_automated_movement = TRUE

View File

@@ -81,13 +81,14 @@
surgery.status++
if(surgery.status > surgery.steps.len)
surgery.complete()
surgery.step_in_progress = FALSE
return advance
else
surgery.step_in_progress = FALSE
if(repeatable)
return FALSE //This is how the repeatable surgery detects it shouldn't cycle
return TRUE //Stop the attack chain! - Except on repeatable steps, because otherwise we land in an infinite loop.
if(target.stat == DEAD && was_sleeping && user.client)
user.client.give_award(/datum/award/achievement/misc/sandman, user)
surgery.step_in_progress = FALSE
if(repeatable)
return FALSE //This is how the repeatable surgery detects it shouldn't cycle
return advance //Stop the attack chain! - Except on repeatable steps, because otherwise we land in an infinite loop.
/datum/surgery_step/proc/preop(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery)

View File

@@ -559,6 +559,9 @@ GLOBAL_LIST_EMPTY(vending_products)
if(crit_case)
L.apply_damage(squish_damage, forced=TRUE)
if(was_alive && L.stat == DEAD && L.client)
L.client.give_award(/datum/award/achievement/misc/vendor_squish, L) // good job losing a fight with an inanimate object idiot
L.Paralyze(60)
L.emote("scream")
playsound(L, 'sound/effects/blobattack.ogg', 40, TRUE)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Some files were not shown because too many files have changed in this diff Show More