Merge conflict fix
This commit is contained in:
@@ -429,6 +429,10 @@
|
||||
for(var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset)
|
||||
dat += {"<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_remove=\ref[rule]'>-> [rule.name] <-</A><br>"}
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_clear=1'>(Clear Rulesets)</A><br>"
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_storyteller=1'>(Force Storyteller)</A><br>"
|
||||
if (GLOB.dynamic_forced_storyteller)
|
||||
var/datum/dynamic_storyteller/S = GLOB.dynamic_forced_storyteller
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_storyteller_clear=1'>-> [initial(S.name)] <-</A><br>"
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_options=1'>(Dynamic mode options)</A><br>"
|
||||
else if (SSticker.IsRoundInProgress())
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_latejoin=1'>(Force Next Latejoin Ruleset)</A><br>"
|
||||
@@ -690,7 +694,7 @@
|
||||
var/prev_dynamic_voting = CONFIG_GET(flag/dynamic_voting)
|
||||
CONFIG_SET(flag/dynamic_voting,!prev_dynamic_voting)
|
||||
if (!prev_dynamic_voting)
|
||||
to_chat(world, "<B>Vote is now a ranked choice of dynamic storytellers.</B>")
|
||||
to_chat(world, "<B>Vote is now between dynamic storytellers.</B>")
|
||||
else
|
||||
to_chat(world, "<B>Vote is now between extended and secret.</B>")
|
||||
log_admin("[key_name(usr)] [prev_dynamic_voting ? "disabled" : "enabled"] dynamic voting.")
|
||||
|
||||
@@ -81,7 +81,7 @@ GLOBAL_PROTECT(admin_verbs_admin)
|
||||
)
|
||||
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /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/set_round_end_sound))
|
||||
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)
|
||||
GLOBAL_LIST_INIT(admin_verbs_fun, list(
|
||||
/client/proc/cmd_admin_dress,
|
||||
@@ -364,7 +364,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
log_admin("[key_name(usr)] admin ghosted.")
|
||||
message_admins("[key_name_admin(usr)] admin ghosted.")
|
||||
var/mob/body = mob
|
||||
body.ghostize(1)
|
||||
body.ghostize(1, voluntary = TRUE)
|
||||
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!
|
||||
|
||||
@@ -120,7 +120,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
|
||||
//adv proc call this, ya nerds
|
||||
/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
|
||||
if(target == GLOBAL_PROC)
|
||||
return call(procname)(arglist(arguments))
|
||||
return call(text2path("/proc/[procname]"))(arglist(arguments))
|
||||
else if(target != world)
|
||||
return call(target, procname)(arglist(arguments))
|
||||
else
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
var/new_volume = input(user, "Choose a volume.", "Sound Emitter", sound_volume) as null|num
|
||||
if(isnull(new_volume))
|
||||
return
|
||||
new_volume = CLAMP(new_volume, 0, 100)
|
||||
new_volume = clamp(new_volume, 0, 100)
|
||||
sound_volume = new_volume
|
||||
to_chat(user, "<span class='notice'>Volume set to [sound_volume]%.</span>")
|
||||
if(href_list["edit_mode"])
|
||||
@@ -118,7 +118,7 @@
|
||||
var/new_radius = input(user, "Choose a radius.", "Sound Emitter", sound_volume) as null|num
|
||||
if(isnull(new_radius))
|
||||
return
|
||||
new_radius = CLAMP(new_radius, 0, 127)
|
||||
new_radius = clamp(new_radius, 0, 127)
|
||||
play_radius = new_radius
|
||||
to_chat(user, "<span class='notice'>Audible radius set to [play_radius].</span>")
|
||||
if(href_list["play"])
|
||||
|
||||
@@ -393,7 +393,7 @@
|
||||
var/nsd = CONFIG_GET(number/note_stale_days)
|
||||
var/nfd = CONFIG_GET(number/note_fresh_days)
|
||||
if (agegate && type == "note" && isnum(nsd) && isnum(nfd) && nsd > nfd)
|
||||
var/alpha = CLAMP(100 - (age - nfd) * (85 / (nsd - nfd)), 15, 100)
|
||||
var/alpha = clamp(100 - (age - nfd) * (85 / (nsd - nfd)), 15, 100)
|
||||
if (alpha < 100)
|
||||
if (alpha <= 15)
|
||||
if (skipped)
|
||||
|
||||
@@ -1394,6 +1394,32 @@
|
||||
log_admin("[key_name(usr)] removed [rule] from the forced roundstart rulesets.")
|
||||
message_admins("[key_name(usr)] removed [rule] from the forced roundstart rulesets.", 1)
|
||||
|
||||
else if(href_list["f_dynamic_storyteller"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
if(SSticker && SSticker.mode)
|
||||
return alert(usr, "The game has already started.", null, null, null, null)
|
||||
if(GLOB.master_mode != "dynamic")
|
||||
return alert(usr, "The game mode has to be dynamic mode.", null, null, null, null)
|
||||
var/list/choices = list()
|
||||
for(var/T in config.storyteller_cache)
|
||||
var/datum/dynamic_storyteller/S = T
|
||||
choices[initial(S.name)] = T
|
||||
var/choice = choices[input("Select storyteller:", "Storyteller", "Classic") as null|anything in choices]
|
||||
if(choice)
|
||||
GLOB.dynamic_forced_storyteller = choice
|
||||
log_admin("[key_name(usr)] forced the storyteller to [GLOB.dynamic_forced_storyteller].")
|
||||
message_admins("[key_name(usr)] forced the storyteller to [GLOB.dynamic_forced_storyteller].")
|
||||
Game()
|
||||
|
||||
else if(href_list["f_dynamic_storyteller_clear"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
GLOB.dynamic_forced_storyteller = null
|
||||
Game()
|
||||
log_admin("[key_name(usr)] cleared the forced storyteller. The mode will pick one as normal.")
|
||||
message_admins("[key_name(usr)] cleared the forced storyteller. The mode will pick one as normal.", 1)
|
||||
|
||||
else if(href_list["f_dynamic_latejoin"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
@@ -2363,7 +2389,7 @@
|
||||
return
|
||||
|
||||
var/list/offset = splittext(href_list["offset"],",")
|
||||
var/number = CLAMP(text2num(href_list["object_count"]), 1, 100)
|
||||
var/number = clamp(text2num(href_list["object_count"]), 1, 100)
|
||||
var/X = offset.len > 0 ? text2num(offset[1]) : 0
|
||||
var/Y = offset.len > 1 ? text2num(offset[2]) : 0
|
||||
var/Z = offset.len > 2 ? text2num(offset[3]) : 0
|
||||
|
||||
@@ -764,7 +764,6 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
|
||||
for(var/arg in arguments)
|
||||
new_args[++new_args.len] = SDQL_expression(source, arg)
|
||||
if(object == GLOB) // Global proc.
|
||||
procname = "/proc/[procname]"
|
||||
return superuser? (call(procname)(new_args)) : (WrapAdminProcCall(GLOBAL_PROC, procname, new_args))
|
||||
return superuser? (call(object, procname)(new_args)) : (WrapAdminProcCall(object, procname, new_args))
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
if ("set_charge")
|
||||
var/newcharge = input("New charge (0-[borg.cell.maxcharge]):", borg.name, borg.cell.charge) as num|null
|
||||
if (newcharge)
|
||||
borg.cell.charge = CLAMP(newcharge, 0, borg.cell.maxcharge)
|
||||
borg.cell.charge = clamp(newcharge, 0, borg.cell.maxcharge)
|
||||
message_admins("[key_name_admin(user)] set the charge of [ADMIN_LOOKUPFLW(borg)] to [borg.cell.charge].")
|
||||
log_admin("[key_name(user)] set the charge of [key_name(borg)] to [borg.cell.charge].")
|
||||
if ("remove_cell")
|
||||
|
||||
@@ -705,10 +705,17 @@
|
||||
var/list/names = list()
|
||||
names += "---- Space Ruins ----"
|
||||
for(var/name in SSmapping.space_ruins_templates)
|
||||
names[name] = list(SSmapping.space_ruins_templates[name], ZTRAIT_SPACE_RUINS, /area/space)
|
||||
names[name] = list(SSmapping.space_ruins_templates[name], ZTRAIT_SPACE_RUINS, list(/area/space))
|
||||
names += "---- Lava Ruins ----"
|
||||
for(var/name in SSmapping.lava_ruins_templates)
|
||||
names[name] = list(SSmapping.lava_ruins_templates[name], ZTRAIT_LAVA_RUINS, /area/lavaland/surface/outdoors/unexplored)
|
||||
names[name] = list(SSmapping.lava_ruins_templates[name], ZTRAIT_LAVA_RUINS, list(/area/lavaland/surface/outdoors/unexplored))
|
||||
names += "---- Ice Ruins ----"
|
||||
for(var/name in SSmapping.ice_ruins_templates)
|
||||
names[name] = list(SSmapping.ice_ruins_templates[name], ZTRAIT_ICE_RUINS, list(/area/icemoon/surface/outdoors/unexplored, /area/icemoon/underground/unexplored))
|
||||
names += "---- Ice Underground Ruins ----"
|
||||
for(var/name in SSmapping.ice_ruins_underground_templates)
|
||||
names[name] = list(SSmapping.ice_ruins_underground_templates[name], ZTRAIT_ICE_RUINS_UNDERGROUND, list(/area/icemoon/underground/unexplored))
|
||||
|
||||
var/ruinname = input("Select ruin", "Spawn Ruin") as null|anything in names
|
||||
var/data = names[ruinname]
|
||||
|
||||
@@ -18,9 +18,12 @@
|
||||
var/image/item = image('icons/turf/overlays.dmi',S,"greenOverlay")
|
||||
item.plane = ABOVE_LIGHTING_PLANE
|
||||
preview += item
|
||||
var/list/orientations = list("South" = SOUTH, "North" = NORTH, "East" = EAST, "West" = WEST)
|
||||
var/choice = input(src, "Which orientation? Maps are normally facing SOUTH.", "Template Orientation", "South") as null|anything in orientations
|
||||
var/orientation = orientations[choice]
|
||||
images += preview
|
||||
if(alert(src,"Confirm location.","Template Confirm","Yes","No") == "Yes")
|
||||
if(template.load(T, centered = TRUE))
|
||||
if(template.load(T, centered = TRUE, orientation = orientation))
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(src)] has placed a map template ([template.name]) at [ADMIN_COORDJMP(T)]</span>")
|
||||
else
|
||||
to_chat(src, "Failed to place map")
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
|
||||
if(!freq)
|
||||
freq = 1
|
||||
vol = CLAMP(vol, 1, 100)
|
||||
vol = clamp(vol, 1, 100)
|
||||
|
||||
var/sound/admin_sound = new()
|
||||
admin_sound.file = S
|
||||
@@ -138,6 +138,49 @@
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Internet Sound")
|
||||
|
||||
/client/proc/manual_play_web_sound()
|
||||
set category = "Fun"
|
||||
set name = "Manual Play Internet Sound"
|
||||
if(!check_rights(R_SOUNDS))
|
||||
return
|
||||
|
||||
var/web_sound_input = input("Enter content stream URL (fetch this from local youtube-dl!)", "Play Internet Sound via direct URL") as text|null
|
||||
if(istext(web_sound_input))
|
||||
if(!length(web_sound_input))
|
||||
log_admin("[key_name(src)] stopped web sound")
|
||||
message_admins("[key_name(src)] stopped web sound")
|
||||
var/mob/M
|
||||
for(var/i in GLOB.player_list)
|
||||
M = i
|
||||
M?.client?.chatOutput?.stopMusic()
|
||||
return
|
||||
else
|
||||
if(web_sound_input && !findtext(web_sound_input, GLOB.is_http_protocol))
|
||||
to_chat(src, "<span class='boldwarning'>BLOCKED: Content URL not using http(s) protocol</span>")
|
||||
return
|
||||
var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
|
||||
if(isnull(freq))
|
||||
return
|
||||
if(!freq)
|
||||
freq = 1
|
||||
SSblackbox.record_feedback("nested tally", "played_url", 1, list("[ckey]", "[web_sound_input]"))
|
||||
var/logstr = "[key_name(src)] played web sound at freq [freq]: [web_sound_input]"
|
||||
log_admin(logstr)
|
||||
message_admins(logstr)
|
||||
var/mob/M
|
||||
var/client/C
|
||||
var/datum/chatOutput/O
|
||||
for(var/i in GLOB.player_list)
|
||||
M = i
|
||||
C = M.client
|
||||
if(!(C?.prefs?.toggles & SOUND_MIDI))
|
||||
continue
|
||||
O = C.chatOutput
|
||||
if(!O || O.broken || !O.loaded)
|
||||
continue
|
||||
O.sendMusic(web_sound_input, freq)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Manual Play Internet Sound")
|
||||
|
||||
/client/proc/set_round_end_sound(S as sound)
|
||||
set category = "Fun"
|
||||
set name = "Set Round End Sound"
|
||||
|
||||
@@ -1372,3 +1372,47 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
|
||||
else
|
||||
message_admins("[key_name_admin(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name_admin(C)]")
|
||||
log_admin("[key_name(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name(C)]")
|
||||
|
||||
/// Allow admin to add or remove traits of datum
|
||||
/datum/admins/proc/modify_traits(datum/D)
|
||||
if(!D)
|
||||
return
|
||||
|
||||
var/add_or_remove = input("Remove/Add?", "Trait Remove/Add") as null|anything in list("Add","Remove")
|
||||
if(!add_or_remove)
|
||||
return
|
||||
var/list/availible_traits = list()
|
||||
|
||||
switch(add_or_remove)
|
||||
if("Add")
|
||||
for(var/key in GLOB.traits_by_type)
|
||||
if(istype(D,key))
|
||||
availible_traits += GLOB.traits_by_type[key]
|
||||
if("Remove")
|
||||
if(!GLOB.trait_name_map)
|
||||
GLOB.trait_name_map = generate_trait_name_map()
|
||||
for(var/trait in D.status_traits)
|
||||
var/name = GLOB.trait_name_map[trait] || trait
|
||||
availible_traits[name] = trait
|
||||
|
||||
var/chosen_trait = input("Select trait to modify", "Trait") as null|anything in sortList(availible_traits)
|
||||
if(!chosen_trait)
|
||||
return
|
||||
chosen_trait = availible_traits[chosen_trait]
|
||||
|
||||
var/source = "adminabuse"
|
||||
switch(add_or_remove)
|
||||
if("Add") //Not doing source choosing here intentionally to make this bit faster to use, you can always vv it.
|
||||
ADD_TRAIT(D,chosen_trait,source)
|
||||
if("Remove")
|
||||
var/specific = input("All or specific source ?", "Trait Remove/Add") as null|anything in list("All","Specific")
|
||||
if(!specific)
|
||||
return
|
||||
switch(specific)
|
||||
if("All")
|
||||
source = null
|
||||
if("Specific")
|
||||
source = input("Source to be removed","Trait Remove/Add") as null|anything in sortList(D.status_traits[chosen_trait])
|
||||
if(!source)
|
||||
return
|
||||
REMOVE_TRAIT(D,chosen_trait,source)
|
||||
|
||||
@@ -194,9 +194,10 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
else
|
||||
variable = L[index]
|
||||
//EXPERIMENTAL - Keep old associated value while modifying key, if any
|
||||
var/found = L[variable]
|
||||
if(!isnull(found))
|
||||
old_assoc_value = found
|
||||
if(IS_VALID_ASSOC_KEY(variable))
|
||||
var/found = L[variable]
|
||||
if(!isnull(found))
|
||||
old_assoc_value = found
|
||||
//
|
||||
|
||||
default = vv_get_class(objectvar, variable)
|
||||
|
||||
@@ -47,5 +47,33 @@
|
||||
usr.client.debug_variables(src)
|
||||
if(href_list[VV_HK_MARK])
|
||||
usr.client.mark_datum(target)
|
||||
if(href_list[VV_HK_ADDCOMPONENT])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
var/list/names = list()
|
||||
var/list/componentsubtypes = subtypesof(/datum/component)
|
||||
names += "---Components---"
|
||||
names += componentsubtypes
|
||||
names += "---Elements---"
|
||||
names += subtypesof(/datum/element)
|
||||
var/result = input(usr, "Choose a component/element to add","better know what ur fuckin doin pal") as null|anything in names
|
||||
if(!usr || !result || result == "---Components---" || result == "---Elements---")
|
||||
return
|
||||
if(QDELETED(src))
|
||||
to_chat(usr, "That thing doesn't exist anymore!")
|
||||
return
|
||||
var/list/lst = get_callproc_args()
|
||||
if(!lst)
|
||||
return
|
||||
var/datumname = "error"
|
||||
lst.Insert(1, result)
|
||||
if(result in componentsubtypes)
|
||||
datumname = "component"
|
||||
target._AddComponent(lst)
|
||||
else
|
||||
datumname = "element"
|
||||
target._AddElement(lst)
|
||||
log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(src)].")
|
||||
message_admins("<span class='notice'>[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(src)].</span>")
|
||||
if(href_list[VV_HK_CALLPROC])
|
||||
usr.client.callproc_datum(target)
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
if(objective.completable)
|
||||
var/completion = objective.check_completion()
|
||||
if(completion >= 1)
|
||||
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
|
||||
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
|
||||
else if(completion <= 0)
|
||||
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
win = FALSE
|
||||
|
||||
@@ -492,7 +492,7 @@
|
||||
|
||||
user.do_attack_animation(L)
|
||||
|
||||
if(L.run_block(src, 0, "[user]'s [src]", ATTACK_TYPE_MELEE, 0, user, check_zone(user.zone_selected)) & BLOCK_SUCCESS)
|
||||
if(L.mob_run_block(src, 0, "[user]'s [src]", ATTACK_TYPE_MELEE, 0, user, check_zone(user.zone_selected), null) & BLOCK_SUCCESS)
|
||||
playsound(L, 'sound/weapons/genhit.ogg', 50, TRUE)
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
status = ORGAN_ROBOTIC
|
||||
beating = TRUE
|
||||
organ_flags = ORGAN_NO_SPOIL
|
||||
no_pump = TRUE
|
||||
var/true_name = "baseline placebo referencer"
|
||||
var/cooldown_low = 300
|
||||
var/cooldown_high = 300
|
||||
@@ -92,6 +93,7 @@
|
||||
update_gland_hud()
|
||||
|
||||
/obj/item/organ/heart/gland/on_life()
|
||||
. = ..()
|
||||
if(!beating)
|
||||
// alien glands are immune to stopping.
|
||||
beating = TRUE
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
/obj/item/organ/heart/gland/slime/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
|
||||
..()
|
||||
owner.faction |= "slime"
|
||||
owner.grant_language(/datum/language/slime)
|
||||
owner.grant_language(/datum/language/slime, TRUE, TRUE, LANGUAGE_GLAND)
|
||||
|
||||
/obj/item/organ/heart/gland/slime/Remove(mob/living/carbon/M, special = 0)
|
||||
..()
|
||||
owner.faction -= "slime"
|
||||
owner.remove_language(/datum/language/slime, TRUE, TRUE, LANGUAGE_GLAND)
|
||||
|
||||
/obj/item/organ/heart/gland/slime/activate()
|
||||
to_chat(owner, "<span class='warning'>You feel nauseated!</span>")
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
/obj/item/organ/heart/gland/trauma/activate()
|
||||
to_chat(owner, "<span class='warning'>You feel a spike of pain in your head.</span>")
|
||||
if(prob(33))
|
||||
owner.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_LOBOTOMY))
|
||||
owner.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_SURGERY))
|
||||
else
|
||||
if(prob(20))
|
||||
owner.gain_trauma_type(BRAIN_TRAUMA_SEVERE, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_LOBOTOMY))
|
||||
owner.gain_trauma_type(BRAIN_TRAUMA_SEVERE, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_SURGERY))
|
||||
else
|
||||
owner.gain_trauma_type(BRAIN_TRAUMA_MILD, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_LOBOTOMY))
|
||||
owner.gain_trauma_type(BRAIN_TRAUMA_MILD, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_SURGERY))
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
/mob/living/simple_animal/hostile/blob/fire_act(exposed_temperature, exposed_volume)
|
||||
..()
|
||||
if(exposed_temperature)
|
||||
adjustFireLoss(CLAMP(0.01 * exposed_temperature, 1, 5))
|
||||
adjustFireLoss(clamp(0.01 * exposed_temperature, 1, 5))
|
||||
else
|
||||
adjustFireLoss(5)
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
B.hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(blob_core.obj_integrity)]</font></div>"
|
||||
|
||||
/mob/camera/blob/proc/add_points(points)
|
||||
blob_points = CLAMP(blob_points + points, 0, max_blob_points)
|
||||
blob_points = clamp(blob_points + points, 0, max_blob_points)
|
||||
hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(blob_points)]</font></div>"
|
||||
|
||||
/mob/camera/blob/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
|
||||
/obj/structure/blob/CanAStarPass(ID, dir, caller)
|
||||
. = 0
|
||||
if(ismovableatom(caller))
|
||||
if(ismovable(caller))
|
||||
var/atom/movable/mover = caller
|
||||
. = . || (mover.pass_flags & PASSBLOB)
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
set waitfor = FALSE // Don't make on_gain() wait for this function to finish. This lets this code run on the side.
|
||||
var/notice_healing
|
||||
while(owner && !AmFinalDeath()) // owner.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) == src
|
||||
if(owner.current.stat == CONSCIOUS && !poweron_feed && !HAS_TRAIT(owner.current, TRAIT_DEATHCOMA)) // Deduct Blood
|
||||
if(owner.current.stat == CONSCIOUS && !poweron_feed && !HAS_TRAIT(owner.current, TRAIT_FAKEDEATH)) // Deduct Blood
|
||||
AddBloodVolume(passive_blood_drain) // -.1 currently
|
||||
if(HandleHealing(1)) // Heal
|
||||
if(notice_healing == FALSE && owner.current.blood_volume > 0)
|
||||
if(!notice_healing && owner.current.blood_volume > 0)
|
||||
to_chat(owner, "<span class='notice'>The power of your blood begins knitting your wounds...</span>")
|
||||
notice_healing = TRUE
|
||||
else if(notice_healing == TRUE)
|
||||
@@ -25,7 +25,7 @@
|
||||
HandleStarving() // Death
|
||||
HandleDeath() // Standard Update
|
||||
update_hud()// Daytime Sleep in Coffin
|
||||
if(SSticker.mode.is_daylight() && !HAS_TRAIT_FROM(owner.current, TRAIT_DEATHCOMA, "bloodsucker"))
|
||||
if(SSticker.mode.is_daylight() && !HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker"))
|
||||
if(istype(owner.current.loc, /obj/structure/closet/crate/coffin))
|
||||
Torpor_Begin()
|
||||
// Wait before next pass
|
||||
@@ -39,7 +39,7 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/datum/antagonist/bloodsucker/proc/AddBloodVolume(value)
|
||||
owner.current.blood_volume = CLAMP(owner.current.blood_volume + value, 0, max_blood_volume)
|
||||
owner.current.blood_volume = clamp(owner.current.blood_volume + value, 0, max_blood_volume)
|
||||
update_hud()
|
||||
|
||||
/datum/antagonist/bloodsucker/proc/HandleFeeding(mob/living/carbon/target, mult=1)
|
||||
@@ -83,7 +83,7 @@
|
||||
// NOTE: Mult of 0 is just a TEST to see if we are injured and need to go into Torpor!
|
||||
//It is called from your coffin on close (by you only)
|
||||
var/actual_regen = regen_rate + additional_regen
|
||||
if(poweron_masquerade == TRUE || owner.current.AmStaked())
|
||||
if(poweron_masquerade|| owner.current.AmStaked())
|
||||
return FALSE
|
||||
if(owner.current.reagents.has_reagent(/datum/reagent/consumable/garlic))
|
||||
return FALSE
|
||||
@@ -101,8 +101,8 @@
|
||||
var/mob/living/carbon/C = owner.current
|
||||
var/costMult = 1 // Coffin makes it cheaper
|
||||
var/fireheal = 0 // BURN: Heal in Coffin while Fakedeath, or when damage above maxhealth (you can never fully heal fire)
|
||||
var/amInCoffinWhileTorpor = istype(C.loc, /obj/structure/closet/crate/coffin) && (mult == 0 || HAS_TRAIT(C, TRAIT_FAKEDEATH)) // Check for mult 0 OR death coma. (mult 0 means we're testing from coffin)
|
||||
if(amInCoffinWhileTorpor)
|
||||
// Check for mult 0 OR death coma. (mult 0 means we're testing from coffin)
|
||||
if(istype(C.loc, /obj/structure/closet/crate/coffin) && (mult == 0 || HAS_TRAIT(C, TRAIT_FAKEDEATH)))
|
||||
mult *= 4 // Increase multiplier if we're sleeping in a coffin.
|
||||
fireheal = min(C.getFireLoss(), regen_rate) // NOTE: Burn damage ONLY heals in torpor.
|
||||
C.ExtinguishMob()
|
||||
@@ -112,7 +112,10 @@
|
||||
CheckVampOrgans() // Heart, Eyes
|
||||
if(check_limbs(costMult))
|
||||
return TRUE
|
||||
|
||||
else if(owner.current.stat >= UNCONSCIOUS) //Faster regeneration and slight burn healing while unconcious
|
||||
mult *= 2
|
||||
fireheal = min(C.getFireLoss(), regen_rate * 0.2)
|
||||
|
||||
// BRUTE: Always Heal
|
||||
var/bruteheal = min(C.getBruteLoss(), actual_regen)
|
||||
var/toxinheal = min(C.getToxLoss(), actual_regen)
|
||||
@@ -120,8 +123,6 @@
|
||||
if(bruteheal + fireheal + toxinheal > 0) // Just a check? Don't heal/spend, and return.
|
||||
if(mult == 0)
|
||||
return TRUE
|
||||
if(owner.current.stat >= UNCONSCIOUS) //Faster regeneration while unconcious, so you dont have to wait all day
|
||||
mult *= 2
|
||||
// We have damage. Let's heal (one time)
|
||||
C.adjustBruteLoss(-bruteheal * mult, forced = TRUE)// Heal BRUTE / BURN in random portions throughout the body.
|
||||
C.adjustFireLoss(-fireheal * mult, forced = TRUE)
|
||||
@@ -129,7 +130,7 @@
|
||||
//C.heal_overall_damage(bruteheal * mult, fireheal * mult) // REMOVED: We need to FORCE this, because otherwise, vamps won't heal EVER. Swapped to above.
|
||||
AddBloodVolume((bruteheal * -0.5 + fireheal * -1 + toxinheal * -0.2) / mult * costMult) // Costs blood to heal
|
||||
return TRUE // Healed! Done for this tick.
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/antagonist/bloodsucker/proc/check_limbs(costMult)
|
||||
@@ -137,7 +138,7 @@
|
||||
var/mob/living/carbon/C = owner.current
|
||||
var/list/missing = C.get_missing_limbs()
|
||||
if(missing.len && C.blood_volume < limb_regen_cost + 5)
|
||||
return FALSE
|
||||
return FALSE
|
||||
for(var/targetLimbZone in missing) // 1) Find ONE Limb and regenerate it.
|
||||
C.regenerate_limb(targetLimbZone, FALSE) // regenerate_limbs() <--- If you want to EXCLUDE certain parts, do it like this ----> regenerate_limbs(0, list("head"))
|
||||
AddBloodVolume(50)
|
||||
@@ -146,12 +147,6 @@
|
||||
to_chat(C, "<span class='notice'>Your flesh knits as it regrows your [L]!</span>")
|
||||
playsound(C, 'sound/magic/demon_consume.ogg', 50, TRUE)
|
||||
return TRUE
|
||||
/*for(var/obj/item/bodypart/BP in C.bodyparts)
|
||||
if(!istype(BP) && !BP.status == 2)
|
||||
return FALSE
|
||||
to_chat(C, "<span class='notice'>Your body expels the [BP]!</span>")
|
||||
BP.drop_limb()
|
||||
return TRUE */
|
||||
|
||||
/datum/antagonist/bloodsucker/proc/CureDisabilities()
|
||||
var/mob/living/carbon/C = owner.current
|
||||
@@ -176,14 +171,14 @@
|
||||
// EMPTY: Frenzy!
|
||||
// BLOOD_VOLUME_GOOD: [336] Pale (handled in bloodsucker_integration.dm
|
||||
// BLOOD_VOLUME_BAD: [224] Jitter
|
||||
if(owner.current.blood_volume < BLOOD_VOLUME_BAD && !prob(0.5))
|
||||
if(owner.current.blood_volume < BLOOD_VOLUME_BAD && !prob(0.5 && HAS_TRAIT(owner, TRAIT_FAKEDEATH)) && !poweron_masquerade)
|
||||
owner.current.Jitter(3)
|
||||
// BLOOD_VOLUME_SURVIVE: [122] Blur Vision
|
||||
if(owner.current.blood_volume < BLOOD_VOLUME_BAD / 2)
|
||||
owner.current.blur_eyes(8 - 8 * (owner.current.blood_volume / BLOOD_VOLUME_BAD))
|
||||
// Nutrition
|
||||
owner.current.nutrition = clamp(owner.current.blood_volume, 545, 0) //The amount of blood is how full we are.
|
||||
//A bit higher regeneration based on blood volume
|
||||
//A bit higher regeneration based on blood volume
|
||||
if(owner.current.blood_volume < 700)
|
||||
additional_regen = 0.4
|
||||
else if(owner.current.blood_volume < BLOOD_VOLUME_NORMAL)
|
||||
@@ -230,16 +225,16 @@
|
||||
Torpor_Begin()
|
||||
to_chat(owner, "<span class='danger'>Your immortal body will not yet relinquish your soul to the abyss. You enter Torpor.</span>")
|
||||
sleep(30) //To avoid spam
|
||||
if(poweron_masquerade == TRUE)
|
||||
if(poweron_masquerade)
|
||||
to_chat(owner, "<span class='warning'>Your wounds will not heal until you disable the <span class='boldnotice'>Masquerade</span> power.</span>")
|
||||
// End Torpor:
|
||||
else // No damage, OR toxin healed AND brute healed and NOT in coffin (since you cannot heal burn)
|
||||
if(total_damage <= 0 || total_toxloss <= 0 && total_brute <= 0 && !istype(owner.current.loc, /obj/structure/closet/crate/coffin))
|
||||
// Not Daytime, Not in Torpor
|
||||
if(!SSticker.mode.is_daylight() && HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker"))
|
||||
// Not Daytime, Not in Torpor, enough health to not die the moment you end torpor
|
||||
if(!SSticker.mode.is_daylight() && HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker") && total_damage < owner.current.getMaxHealth())
|
||||
Torpor_End()
|
||||
// Fake Unconscious
|
||||
if(poweron_masquerade == TRUE && total_damage >= owner.current.getMaxHealth() - HEALTH_THRESHOLD_FULLCRIT)
|
||||
if(poweron_masquerade && total_damage >= owner.current.getMaxHealth() - HEALTH_THRESHOLD_FULLCRIT)
|
||||
owner.current.Unconscious(20, 1)
|
||||
|
||||
/datum/antagonist/bloodsucker/proc/Torpor_Begin(amInCoffin = FALSE)
|
||||
@@ -249,6 +244,7 @@
|
||||
ADD_TRAIT(owner.current, TRAIT_NODEATH, "bloodsucker") // Without this, you'll just keep dying while you recover.
|
||||
ADD_TRAIT(owner.current, TRAIT_RESISTHIGHPRESSURE, "bloodsucker") // So you can heal in space. Otherwise you just...heal forever.
|
||||
ADD_TRAIT(owner.current, TRAIT_RESISTLOWPRESSURE, "bloodsucker")
|
||||
owner.current.Jitter(0)
|
||||
// Visuals
|
||||
owner.current.update_sight()
|
||||
owner.current.reload_fullscreen()
|
||||
@@ -256,6 +252,9 @@
|
||||
for(var/datum/action/bloodsucker/power in powers)
|
||||
if(power.active && !power.can_use_in_torpor)
|
||||
power.DeactivatePower()
|
||||
if(owner.current.suiciding)
|
||||
owner.current.suiciding = FALSE //Youll die but not for long.
|
||||
to_chat(owner.current, "<span class='warning'>Your body keeps you going, even as you try to end yourself.</span>")
|
||||
|
||||
/datum/antagonist/bloodsucker/proc/Torpor_End()
|
||||
owner.current.stat = SOFT_CRIT
|
||||
@@ -369,8 +368,8 @@
|
||||
//Puke blood only if puke_blood is true, and loose some blood, else just puke normally.
|
||||
if(puke_blood)
|
||||
C.blood_volume = max(0, C.blood_volume - foodInGut * 2)
|
||||
C.vomit(foodInGut * 4, foodInGut * 2, 0)
|
||||
else
|
||||
C.vomit(foodInGut * 4, foodInGut * 2, 0)
|
||||
else
|
||||
C.vomit(foodInGut * 4, FALSE, 0)
|
||||
C.Stun(30)
|
||||
//C.Dizzy(50)
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
// Heads?
|
||||
if (target_role == "HEAD")
|
||||
target_amount = rand(1, round(SSticker.mode.num_players() / 20))
|
||||
target_amount = CLAMP(target_amount,1,3)
|
||||
target_amount = clamp(target_amount,1,3)
|
||||
// Department?
|
||||
else
|
||||
switch(target_role)
|
||||
@@ -100,7 +100,7 @@
|
||||
if("Quartermaster")
|
||||
department_string = "Cargo"
|
||||
target_amount = rand(round(SSticker.mode.num_players() / 20), round(SSticker.mode.num_players() / 10))
|
||||
target_amount = CLAMP(target_amount, 2, 4)
|
||||
target_amount = clamp(target_amount, 2, 4)
|
||||
..()
|
||||
|
||||
// EXPLANATION
|
||||
|
||||
@@ -45,15 +45,15 @@
|
||||
// (FINAL LIL WARNING)
|
||||
while(time_til_cycle > 5)
|
||||
sleep(10)
|
||||
if (cancel_me)
|
||||
if(cancel_me)
|
||||
return
|
||||
//sleep(TIME_BLOODSUCKER_DAY_FINAL_WARN - 50)
|
||||
warn_daylight(3,"<span class = 'userdanger'>Seek cover, for Sol rises!</span>")
|
||||
|
||||
// Part 3: Night Ending
|
||||
while (time_til_cycle > 0)
|
||||
while(time_til_cycle > 0)
|
||||
sleep(10)
|
||||
if (cancel_me)
|
||||
if(cancel_me)
|
||||
return
|
||||
//sleep(50)
|
||||
warn_daylight(4,"<span class = 'userdanger'>Solar flares bombard the station with deadly UV light!</span><br><span class = ''>Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!</span>",\
|
||||
@@ -69,11 +69,11 @@
|
||||
while(time_til_cycle > 0)
|
||||
punish_vamps()
|
||||
sleep(TIME_BLOODSUCKER_BURN_INTERVAL)
|
||||
if (cancel_me)
|
||||
if(cancel_me)
|
||||
return
|
||||
//daylight_time -= TIME_BLOODSUCKER_BURN_INTERVAL
|
||||
// Issue Level Up!
|
||||
if(!issued_XP && time_til_cycle <= 15)
|
||||
if(!issued_XP && time_til_cycle <= 5)
|
||||
issued_XP = TRUE
|
||||
vamps_rank_up()
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
var/poweron_feed = FALSE // Am I feeding?
|
||||
var/poweron_masquerade = FALSE
|
||||
// STATS
|
||||
var/bloodsucker_level
|
||||
var/bloodsucker_level
|
||||
var/bloodsucker_level_unspent = 1
|
||||
var/regen_rate = 0.3 // How fast do I regenerate?
|
||||
var/additional_regen // How much additional blood regen we gain from bonuses such as high blood.
|
||||
@@ -209,7 +209,7 @@
|
||||
// Physiology
|
||||
CheckVampOrgans() // Heart, Eyes
|
||||
// Language
|
||||
owner.current.grant_language(/datum/language/vampiric)
|
||||
owner.current.grant_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
|
||||
owner.hasSoul = FALSE // If false, renders the character unable to sell their soul.
|
||||
owner.isholy = FALSE // is this person a chaplain or admin role allowed to use bibles
|
||||
// Disabilities
|
||||
@@ -227,7 +227,7 @@
|
||||
// Traits
|
||||
for(var/T in defaultTraits)
|
||||
REMOVE_TRAIT(owner.current, T, BLOODSUCKER_TRAIT)
|
||||
if(had_toxlover == TRUE)
|
||||
if(had_toxlover)
|
||||
ADD_TRAIT(owner.current, TRAIT_TOXINLOVER, SPECIES_TRAIT)
|
||||
|
||||
// Traits: Species
|
||||
@@ -246,7 +246,7 @@
|
||||
// Update Health
|
||||
owner.current.setMaxHealth(100)
|
||||
// Language
|
||||
owner.current.remove_language(/datum/language/vampiric)
|
||||
owner.current.remove_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
|
||||
// Soul
|
||||
if (owner.soulOwner == owner) // Return soul, if *I* own it.
|
||||
owner.hasSoul = TRUE
|
||||
@@ -649,10 +649,10 @@
|
||||
return TRUE
|
||||
// Check 3) If I am a BLOODSUCKER, then are they my Vassal?
|
||||
if (mob_B && atom_V && (atom_V in mob_B.vassals))
|
||||
return TRUE
|
||||
return TRUE
|
||||
// Check 4) If we are both VASSAL, then do we have the same master?
|
||||
if (atom_V && mob_V && atom_V.master == mob_V.master)
|
||||
return TRUE
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -710,7 +710,7 @@
|
||||
invisibility = INVISIBILITY_ABSTRACT
|
||||
|
||||
/obj/screen/bloodsucker/proc/update_counter(value, valuecolor)
|
||||
invisibility = 0
|
||||
invisibility = 0
|
||||
|
||||
/obj/screen/bloodsucker/blood_counter
|
||||
icon = 'icons/mob/actions/bloodsucker.dmi'
|
||||
@@ -758,7 +758,7 @@
|
||||
|
||||
/obj/screen/bloodsucker/sunlight_counter/update_counter(value, valuecolor)
|
||||
..()
|
||||
maptext = "<div align='center' valign='bottom' style='position:relative; top:0px; left:6px'><font color='[valuecolor]'>[value]</font></div>"
|
||||
maptext = "<div align='center' valign='bottom' style='position:relative; top:0px; left:6px'><font color='[valuecolor]'>[value]</font></div>"
|
||||
|
||||
/datum/antagonist/bloodsucker/proc/count_vassals(datum/mind/master)
|
||||
var/datum/antagonist/bloodsucker/B = master.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
objectives += vassal_objective
|
||||
objectives_given += vassal_objective
|
||||
give_thrall_eyes()
|
||||
owner.current.grant_language(/datum/language/vampiric)
|
||||
owner.current.grant_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_VASSAL)
|
||||
// Add Antag HUD
|
||||
update_vassal_icons_added(owner.current, "vassal")
|
||||
. = ..()
|
||||
@@ -81,7 +81,7 @@
|
||||
qdel(O)
|
||||
objectives_given = list()
|
||||
remove_thrall_eyes()
|
||||
owner.current.remove_language(/datum/language/vampiric)
|
||||
owner.current.remove_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_VASSAL)
|
||||
// Clear Antag HUD
|
||||
update_vassal_icons_removed(owner.current)
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
// Find Animals in Area
|
||||
/* if(rand(0,2) == 0)
|
||||
var/mobCount = 0
|
||||
var/mobMax = CLAMP(area_turfs.len / 25, 1, 4)
|
||||
var/mobMax = clamp(area_turfs.len / 25, 1, 4)
|
||||
for (var/turf/T in area_turfs)
|
||||
if(!T) continue
|
||||
var/mob/living/simple_animal/SA = locate() in T
|
||||
|
||||
@@ -18,14 +18,11 @@
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
|
||||
// must have nobody around to see the cloak
|
||||
var/watchers = viewers(9,get_turf(owner))
|
||||
for(var/mob/living/M in watchers)
|
||||
for(var/mob/living/M in viewers(9, owner))
|
||||
if(M != owner)
|
||||
to_chat(owner, "<span class='warning'>You may only vanish into the shadows unseen.</span>")
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/action/bloodsucker/cloak/ActivatePower()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
/datum/action/bloodsucker/fortitude
|
||||
name = "Fortitude"//"Cellular Emporium"
|
||||
name = "Fortitude"
|
||||
desc = "Withstand egregious physical wounds and walk away from attacks that would stun, pierce, and dismember lesser beings. You cannot run while active."
|
||||
button_icon_state = "power_fortitude"
|
||||
bloodcost = 30
|
||||
@@ -11,28 +11,27 @@
|
||||
bloodsucker_can_buy = TRUE
|
||||
amToggle = TRUE
|
||||
warn_constant_cost = TRUE
|
||||
var/was_running
|
||||
|
||||
var/this_resist // So we can raise and lower your brute resist based on what your level_current WAS.
|
||||
var/fortitude_resist // So we can raise and lower your brute resist based on what your level_current WAS.
|
||||
|
||||
/datum/action/bloodsucker/fortitude/ActivatePower()
|
||||
var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
var/datum/antagonist/bloodsucker/B = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
var/mob/living/user = owner
|
||||
to_chat(user, "<span class='notice'>Your flesh, skin, and muscles become as steel.</span>")
|
||||
// Traits & Effects
|
||||
ADD_TRAIT(user, TRAIT_PIERCEIMMUNE, "fortitude")
|
||||
ADD_TRAIT(user, TRAIT_NODISMEMBER, "fortitude")
|
||||
ADD_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude")
|
||||
ADD_TRAIT(user, TRAIT_NORUNNING, "fortitude")
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
this_resist = max(0.3, 0.7 - level_current * 0.1)
|
||||
H.physiology.brute_mod *= this_resist//0.5
|
||||
H.physiology.burn_mod *= this_resist//0.5
|
||||
// Stop Running (Taken from /datum/quirk/nyctophobia in negative.dm)
|
||||
var/was_running = (user.m_intent == MOVE_INTENT_RUN)
|
||||
fortitude_resist = max(0.3, 0.7 - level_current * 0.1)
|
||||
H.physiology.brute_mod *= fortitude_resist
|
||||
H.physiology.burn_mod *= fortitude_resist
|
||||
was_running = (user.m_intent == MOVE_INTENT_RUN)
|
||||
if(was_running)
|
||||
user.toggle_move_intent()
|
||||
while(bloodsuckerdatum && ContinueActive(user) || user.m_intent == MOVE_INTENT_RUN)
|
||||
while(B && ContinueActive(user) || user.m_intent == MOVE_INTENT_RUN)
|
||||
if(istype(user.buckled, /obj/vehicle)) //We dont want people using fortitude being able to use vehicles
|
||||
var/obj/vehicle/V = user.buckled
|
||||
var/datum/component/riding/VRD = V.GetComponent(/datum/component/riding)
|
||||
@@ -44,20 +43,21 @@
|
||||
to_chat(user, "<span class='notice'>You fall off the [V], your weight making you too heavy to be supported by it.</span>")
|
||||
// Pay Blood Toll (if awake)
|
||||
if(user.stat == CONSCIOUS)
|
||||
bloodsuckerdatum.AddBloodVolume(-0.5) // Used to be 0.3 blood per 2 seconds, but we're making it more expensive to keep on.
|
||||
B.AddBloodVolume(-0.5)
|
||||
sleep(20) // Check every few ticks that we haven't disabled this power
|
||||
// Return to Running (if you were before)
|
||||
if(was_running && user.m_intent != MOVE_INTENT_RUN)
|
||||
user.toggle_move_intent()
|
||||
|
||||
|
||||
/datum/action/bloodsucker/fortitude/DeactivatePower(mob/living/user = owner, mob/living/target)
|
||||
..()
|
||||
// Restore Traits & Effects
|
||||
REMOVE_TRAIT(user, TRAIT_PIERCEIMMUNE, "fortitude")
|
||||
REMOVE_TRAIT(user, TRAIT_NODISMEMBER, "fortitude")
|
||||
REMOVE_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude")
|
||||
REMOVE_TRAIT(user, TRAIT_NORUNNING, "fortitude")
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.physiology.brute_mod /= this_resist//0.5
|
||||
H.physiology.burn_mod /= this_resist//0.5
|
||||
if(!ishuman(owner))
|
||||
return
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.physiology.brute_mod /= fortitude_resist
|
||||
H.physiology.burn_mod /= fortitude_resist
|
||||
if(was_running && user.m_intent == MOVE_INTENT_WALK)
|
||||
user.toggle_move_intent()
|
||||
|
||||
|
||||
@@ -1,88 +1,46 @@
|
||||
// Level 1: Grapple level 2
|
||||
// Level 2: Grapple 3 from Behind
|
||||
// Level 3: Grapple 3 from Shadows
|
||||
/datum/action/bloodsucker/targeted/lunge
|
||||
|
||||
|
||||
/datum/action/bloodsucker/lunge
|
||||
name = "Predatory Lunge"
|
||||
desc = "Spring at your target and aggressively grapple them without warning. Attacks from concealment or the rear may even knock them down."
|
||||
desc = "Prepare the strenght to grapple your prey."
|
||||
button_icon_state = "power_lunge"
|
||||
bloodcost = 10
|
||||
cooldown = 120
|
||||
target_range = 3
|
||||
power_activates_immediately = TRUE
|
||||
message_Trigger = "Whom will you ensnare within your grasp?"
|
||||
must_be_capacitated = TRUE
|
||||
cooldown = 30
|
||||
bloodsucker_can_buy = TRUE
|
||||
warn_constant_cost = TRUE
|
||||
amToggle = TRUE
|
||||
var/leap_skill_mod = 5
|
||||
|
||||
/datum/action/bloodsucker/targeted/lunge/CheckCanUse(display_error)
|
||||
if(!..(display_error))// DEFAULT CHECKS
|
||||
return FALSE
|
||||
// Being Grabbed
|
||||
if(owner.pulledby && owner.pulledby.grab_state >= GRAB_AGGRESSIVE)
|
||||
if(display_error)
|
||||
to_chat(owner, "<span class='warning'>You're being grabbed!</span>")
|
||||
return FALSE
|
||||
if(!owner.has_gravity(owner.loc))//TODO figure out how to check if theyre able to move while in nograv
|
||||
if(display_error)
|
||||
to_chat(owner, "<span class='warning'>You cant lunge while floating!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
/datum/action/bloodsucker/lunge/New()
|
||||
. = ..()
|
||||
|
||||
|
||||
/datum/action/bloodsucker/targeted/lunge/CheckValidTarget(atom/A)
|
||||
return iscarbon(A)
|
||||
/datum/action/bloodsucker/lunge/Destroy()
|
||||
. = ..()
|
||||
UnregisterSignal(owner, COMSIG_CARBON_TACKLED)
|
||||
|
||||
/datum/action/bloodsucker/targeted/lunge/CheckCanTarget(atom/A, display_error)
|
||||
// Check: Self
|
||||
if(target == owner)
|
||||
return FALSE
|
||||
// Check: Range
|
||||
//if (!(target in view(target_range, get_turf(owner))))
|
||||
// if (display_error)
|
||||
// to_chat(owner, "<span class='warning'>Your victim is too far away.</span>")
|
||||
// return FALSE
|
||||
// DEFAULT CHECKS (Distance)
|
||||
if(!..())
|
||||
return FALSE
|
||||
// Check: Turf
|
||||
var/mob/living/L = A
|
||||
if(!isturf(L.loc))
|
||||
return FALSE
|
||||
return TRUE
|
||||
/datum/action/bloodsucker/lunge/ActivatePower()
|
||||
var/mob/living/carbon/user = owner
|
||||
var/datum/antagonist/bloodsucker/B = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
var/datum/component/tackler/T = user.LoadComponent(/datum/component/tackler)
|
||||
T.stamina_cost = 50
|
||||
T.base_knockdown = 3 SECONDS
|
||||
T.range = 4
|
||||
T.speed = 0.8
|
||||
T.skill_mod = 5 //Monstrous tackling
|
||||
T.min_distance = 2
|
||||
active = TRUE
|
||||
user.toggle_throw_mode()
|
||||
RegisterSignal(user, COMSIG_CARBON_TACKLED, .proc/DelayedDeactivatePower)
|
||||
while(B && ContinueActive(user))
|
||||
B.AddBloodVolume(-0.1)
|
||||
sleep(5)
|
||||
|
||||
/datum/action/bloodsucker/targeted/lunge/FireTargetedPower(atom/A)
|
||||
// set waitfor = FALSE <---- DONT DO THIS!We WANT this power to hold up ClickWithPower(), so that we can unlock the power when it's done.
|
||||
var/mob/living/carbon/target = A
|
||||
var/turf/T = get_turf(target)
|
||||
var/mob/living/L = owner
|
||||
// Clear Vars
|
||||
owner.pulling = null
|
||||
// Will we Knock them Down?
|
||||
var/do_knockdown = !is_A_facing_B(target,owner) || owner.alpha <= 0 || istype(owner.loc, /obj/structure/closet)
|
||||
// CAUSES: Target has their back to me, I'm invisible, or I'm in a Closet
|
||||
// Step One: Heatseek toward Target's Turf
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/_walk, owner, 0), 2 SECONDS)
|
||||
target.playsound_local(get_turf(owner), 'sound/bloodsucker/lunge_warn.ogg', 60, FALSE, pressure_affected = FALSE) // target-only telegraphing
|
||||
owner.playsound_local(owner, 'sound/bloodsucker/lunge_warn.ogg', 60, FALSE, pressure_affected = FALSE) // audio feedback to the user
|
||||
if(do_mob(owner, owner, 7, TRUE, TRUE))
|
||||
walk_towards(owner, T, 0.1, 10) // yes i know i shouldn't use this but i don't know how to work in anything better
|
||||
if(get_turf(owner) != T && !(isliving(target) && target.Adjacent(owner)) && owner.incapacitated() && !CHECK_MOBILITY(L, MOBILITY_STAND))
|
||||
var/send_dir = get_dir(owner, T)
|
||||
new /datum/forced_movement(owner, get_ranged_target_turf(owner, send_dir, 1), 1, FALSE)
|
||||
owner.spin(10)
|
||||
// Step Two: Check if I'm at/adjectent to Target's CURRENT turf (not original...that was just a destination)
|
||||
for(var/i in 1 to 6)
|
||||
if (target.Adjacent(owner))
|
||||
// LEVEL 2: If behind target, mute or unconscious!
|
||||
if(do_knockdown) // && level_current >= 1)
|
||||
target.Knockdown(15 + 10 * level_current,1)
|
||||
target.adjustStaminaLoss(40 + 10 * level_current)
|
||||
// Cancel Walk (we were close enough to contact them)
|
||||
walk(owner, 0)
|
||||
target.Stun(10,1) //Without this the victim can just walk away
|
||||
target.grabbedby(owner) // Taken from mutations.dm under changelings
|
||||
target.grippedby(owner, instant = TRUE) //instant aggro grab
|
||||
break
|
||||
sleep(3)
|
||||
//Without this, the leap component would get removed too early, causing the normal crash into effects.
|
||||
/datum/action/bloodsucker/lunge/proc/DelayedDeactivatePower()
|
||||
addtimer(CALLBACK(src, .proc/DeactivatePower), 1 SECONDS, TIMER_UNIQUE)
|
||||
|
||||
/datum/action/bloodsucker/targeted/lunge/DeactivatePower(mob/living/user = owner, mob/living/target)
|
||||
..() // activate = FALSE
|
||||
user.update_mobility()
|
||||
/datum/action/bloodsucker/lunge/DeactivatePower(mob/living/user = owner)
|
||||
. = ..()
|
||||
qdel(user.GetComponent(/datum/component/tackler))
|
||||
UnregisterSignal(user, COMSIG_CARBON_TACKLED)
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
REMOVE_TRAIT(user, TRAIT_NOHARDCRIT, "bloodsucker")
|
||||
REMOVE_TRAIT(user, TRAIT_NOSOFTCRIT, "bloodsucker")
|
||||
REMOVE_TRAIT(user, TRAIT_VIRUSIMMUNE, "bloodsucker")
|
||||
REMOVE_TRAIT(user, TRAIT_NOBREATH, "bloodsucker")
|
||||
var/obj/item/organ/heart/vampheart/H = user.getorganslot(ORGAN_SLOT_HEART)
|
||||
var/obj/item/organ/eyes/vassal/bloodsucker/E = user.getorganslot(ORGAN_SLOT_EYES)
|
||||
E.flash_protect = 0
|
||||
@@ -93,6 +94,7 @@
|
||||
ADD_TRAIT(user, TRAIT_NOHARDCRIT, "bloodsucker")
|
||||
ADD_TRAIT(user, TRAIT_NOSOFTCRIT, "bloodsucker")
|
||||
ADD_TRAIT(user, TRAIT_VIRUSIMMUNE, "bloodsucker")
|
||||
ADD_TRAIT(user, TRAIT_NOBREATH, "bloodsucker")
|
||||
|
||||
// HEART
|
||||
var/obj/item/organ/heart/H = user.getorganslot(ORGAN_SLOT_HEART)
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
/datum/action/bloodsucker/targeted/trespass/CheckValidTarget(atom/A)
|
||||
// Can't target my tile
|
||||
if (A == get_turf(owner) || get_turf(A) == get_turf(owner))
|
||||
if(A == get_turf(owner) || get_turf(A) == get_turf(owner))
|
||||
return FALSE
|
||||
|
||||
return TRUE // All we care about is destination. Anything you click is fine.
|
||||
@@ -43,13 +43,13 @@
|
||||
// Are either tiles WALLS?
|
||||
var/turf/from_turf = get_turf(owner)
|
||||
var/this_dir // = get_dir(from_turf, target_turf)
|
||||
for (var/i=1 to 2)
|
||||
for(var/i=1 to 2)
|
||||
// Keep Prev Direction if we've reached final turf
|
||||
if (from_turf != final_turf)
|
||||
if(from_turf != final_turf)
|
||||
this_dir = get_dir(from_turf, final_turf) // Recalculate dir so we don't overshoot on a diagonal.
|
||||
from_turf = get_step(from_turf, this_dir)
|
||||
// ERROR! Wall!
|
||||
if (iswallturf(from_turf))
|
||||
if(iswallturf(from_turf))
|
||||
if (display_error)
|
||||
var/wallwarning = (i == 1) ? "in the way" : "at your destination"
|
||||
to_chat(owner, "<span class='warning'>There is a solid wall [wallwarning].</span>")
|
||||
@@ -84,7 +84,7 @@
|
||||
user.next_move = world.time + mist_delay
|
||||
user.Stun(mist_delay, ignore_canstun = TRUE)
|
||||
user.notransform = TRUE
|
||||
user.density = 0
|
||||
user.density = FALSE
|
||||
var/invis_was = user.invisibility
|
||||
user.invisibility = INVISIBILITY_MAXIMUM
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
sleep(mist_delay / 2)
|
||||
|
||||
// Move & Freeze
|
||||
if (isturf(target_turf))
|
||||
if(isturf(target_turf))
|
||||
do_teleport(owner, target_turf, no_effects=TRUE, channel = TELEPORT_CHANNEL_QUANTUM) // in teleport.dm?
|
||||
user.next_move = world.time + mist_delay / 2
|
||||
user.Stun(mist_delay / 2, ignore_canstun = TRUE)
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
H.socks = random_socks(H.gender)
|
||||
//H.eye_color = random_eye_color()
|
||||
REMOVE_TRAIT(H, TRAIT_DISFIGURED, null) //
|
||||
H.dna.features = random_features(H.dna.species?.id)
|
||||
H.dna.features = random_features(H.dna.species?.id, H.gender)
|
||||
|
||||
// Apply Appearance
|
||||
H.update_body(TRUE) // Outfit and underwear, also body and privates.
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
if(objective.completable)
|
||||
var/completion = objective.check_completion()
|
||||
if(completion >= 1)
|
||||
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
|
||||
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
|
||||
else if(completion <= 0)
|
||||
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
win = FALSE
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
create_initial_profile()
|
||||
if(give_objectives)
|
||||
forge_objectives()
|
||||
owner.current.grant_all_languages(FALSE, FALSE, TRUE) //Grants omnitongue. We are able to transform our body after all.
|
||||
remove_clownmut()
|
||||
. = ..()
|
||||
|
||||
@@ -430,7 +431,7 @@
|
||||
objectives += destroy_objective
|
||||
else
|
||||
if(prob(70))
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
var/datum/objective/assassinate/once/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
if(team_mode) //No backstabbing while in a team
|
||||
kill_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
|
||||
@@ -563,7 +564,7 @@
|
||||
if(objective.completable)
|
||||
var/completion = objective.check_completion()
|
||||
if(completion >= 1)
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
|
||||
else if(completion <= 0)
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
changelingwin = FALSE
|
||||
|
||||
@@ -61,10 +61,10 @@
|
||||
if(user.nutrition < NUTRITION_LEVEL_WELL_FED)
|
||||
user.nutrition = min((user.nutrition + target.nutrition), NUTRITION_LEVEL_WELL_FED)
|
||||
|
||||
if(target.mind)//if the victim has got a mind
|
||||
// Absorb a lizard, speak Draconic.
|
||||
user.copy_known_languages_from(target)
|
||||
// Absorb a lizard, speak Draconic.
|
||||
user.copy_languages(target, LANGUAGE_ABSORB)
|
||||
|
||||
if(target.mind && user.mind)//if the victim and user have minds
|
||||
target.mind.show_memory(user, 0) //I can read your mind, kekeke. Output all their notes.
|
||||
|
||||
//Some of target's recent speech, so the changeling can attempt to imitate them better.
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
|
||||
//Recover from stuns.
|
||||
/obj/effect/proc_holder/changeling/adrenaline/sting_action(mob/living/user)
|
||||
user.do_adrenaline(0, FALSE, 70, 0, TRUE, list(/datum/reagent/medicine/epinephrine = 3, /datum/reagent/drug/methamphetamine/changeling = 10, /datum/reagent/medicine/mannitol = 10, /datum/reagent/medicine/regen_jelly = 10, /datum/reagent/medicine/changelingadrenaline = 5), "<span class='notice'>Energy rushes through us.</span>", 0, 0.75, 0)
|
||||
return TRUE
|
||||
user.do_adrenaline(0, FALSE, 70, 0, TRUE, list(/datum/reagent/medicine/epinephrine = 3, /datum/reagent/drug/methamphetamine/changeling = 10, /datum/reagent/medicine/changelingadrenaline = 5), "<span class='notice'>Energy rushes through us.</span>", 0, 0.75, 0)
|
||||
return TRUE
|
||||
|
||||
@@ -27,12 +27,12 @@
|
||||
var/obj/item/organ/eyes/E = user.getorganslot(ORGAN_SLOT_EYES)
|
||||
if(E)
|
||||
if(!active)
|
||||
E.sight_flags |= SEE_MOBS | SEE_OBJS | SEE_TURFS //Add sight flags to the user's eyes
|
||||
ADD_TRAIT(user, TRAIT_THERMAL_VISION, CHANGELING_TRAIT)
|
||||
E.flash_protect = -1 //Adjust the user's eyes' flash protection
|
||||
to_chat(user, "We adjust our eyes to sense prey through walls.")
|
||||
active = TRUE //Defined in code/modules/spells/spell.dm
|
||||
else
|
||||
E.sight_flags ^= SEE_MOBS | SEE_OBJS | SEE_TURFS //Remove sight flags from the user's eyes
|
||||
REMOVE_TRAIT(user, TRAIT_THERMAL_VISION, CHANGELING_TRAIT)
|
||||
E.flash_protect = 2 //Adjust the user's eyes' flash protection
|
||||
to_chat(user, "We adjust our eyes to protect them from bright lights.")
|
||||
active = FALSE
|
||||
@@ -47,10 +47,8 @@
|
||||
|
||||
/obj/effect/proc_holder/changeling/augmented_eyesight/on_refund(mob/user) //Get rid of X-ray vision and flash protection when the user refunds this ability
|
||||
action.Remove(user)
|
||||
REMOVE_TRAIT(user, TRAIT_THERMAL_VISION, CHANGELING_TRAIT)
|
||||
var/obj/item/organ/eyes/E = user.getorganslot(ORGAN_SLOT_EYES)
|
||||
if(E)
|
||||
if (active)
|
||||
E.sight_flags ^= SEE_MOBS | SEE_OBJS | SEE_TURFS
|
||||
else
|
||||
E.flash_protect = 0
|
||||
user.update_sight()
|
||||
E.flash_protect = initial(E.flash_protect)
|
||||
user.update_sight()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "Biodegrade"
|
||||
desc = "Dissolves restraints or other objects preventing free movement."
|
||||
helptext = "This is obvious to nearby people, and can destroy standard restraints and closets. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
|
||||
chemical_cost = 30 //High cost to prevent spam
|
||||
chemical_cost = 15 //High cost to prevent spam
|
||||
loudness = 1
|
||||
dna_cost = 2
|
||||
req_human = 1
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
desc = "Our skin pigmentation rapidly changes to suit our current environment."
|
||||
helptext = "Allows us to become invisible after a few seconds of standing still. Can be toggled on and off."
|
||||
dna_cost = 2
|
||||
chemical_cost = 25
|
||||
req_human = 1
|
||||
action_icon = 'icons/mob/actions/actions_changeling.dmi'
|
||||
action_icon_state = "ling_camouflage"
|
||||
@@ -23,4 +22,4 @@
|
||||
action.Remove(user)
|
||||
if(user.has_dna())
|
||||
var/mob/living/carbon/C = user
|
||||
C.dna.remove_mutation(CHAMELEON)
|
||||
C.dna.remove_mutation(CHAMELEON)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/obj/effect/proc_holder/changeling/fleshmend
|
||||
name = "Fleshmend"
|
||||
desc = "Our flesh rapidly regenerates, healing our burns, bruises, and shortness of breath. Functions while unconscious."
|
||||
desc = "Our flesh rapidly regenerates, healing our burns, bruises, and shortness of breath. Functions while unconscious. This ability is loud, and might cause our blood to react violently to heat."
|
||||
helptext = "If we are on fire, the healing effect will not function. Does not regrow limbs or restore lost blood."
|
||||
chemical_cost = 20
|
||||
loudness = 2
|
||||
dna_cost = 2
|
||||
req_stat = UNCONSCIOUS
|
||||
action_icon = 'icons/mob/actions/actions_changeling.dmi'
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
changeling.purchasedpowers -= src
|
||||
|
||||
var/newmob = user.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS)
|
||||
var/newmob = user.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
|
||||
|
||||
changeling_transform(newmob, chosen_prof)
|
||||
return TRUE
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
name = "Arm Blade"
|
||||
desc = "We reform one of our arms into a deadly blade."
|
||||
helptext = "We may retract our armblade in the same manner as we form it. Cannot be used while in lesser form. This ability is loud, and might cause our blood to react violently to heat."
|
||||
chemical_cost = 20
|
||||
chemical_cost = 10
|
||||
dna_cost = 2
|
||||
loudness = 2
|
||||
req_human = 1
|
||||
@@ -410,7 +410,7 @@
|
||||
desc = "We reform one of our arms into a hard shield."
|
||||
helptext = "Organic tissue cannot resist damage forever; the shield will break after it is hit too much. The more genomes we absorb, the stronger it is. Cannot be used while in lesser form. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
|
||||
chemical_cost = 20
|
||||
dna_cost = 1
|
||||
dna_cost = 2
|
||||
loudness = 1
|
||||
req_human = 1
|
||||
action_icon = 'icons/mob/actions/actions_changeling.dmi'
|
||||
@@ -522,12 +522,12 @@
|
||||
/obj/effect/proc_holder/changeling/suit/armor
|
||||
name = "Chitinous Armor"
|
||||
desc = "We turn our skin into tough chitin to protect us from damage."
|
||||
helptext = "Upkeep of the armor requires a low expenditure of chemicals. The armor is strong against brute force, but does not provide much protection from lasers. Cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat."
|
||||
helptext = "Upkeep of the armor requires a constant expenditure of chemicals, resulting in a reduced chemical generation. The armor is strong against brute force, but does not provide much protection from lasers. Cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat."
|
||||
chemical_cost = 20
|
||||
dna_cost = 1
|
||||
loudness = 2
|
||||
req_human = 1
|
||||
recharge_slowdown = 0.25
|
||||
recharge_slowdown = 0.5
|
||||
action_icon = 'icons/mob/actions/actions_changeling.dmi'
|
||||
action_icon_state = "ling_armor"
|
||||
action_background_icon_state = "bg_ling"
|
||||
@@ -543,7 +543,7 @@
|
||||
icon_state = "lingarmor"
|
||||
item_flags = DROPDEL
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 20, "bomb" = 10, "bio" = 4, "rad" = 0, "fire" = 90, "acid" = 90)
|
||||
armor = list("melee" = 70, "bullet" = 60, "laser" = 30, "energy" = 40, "bomb" = 10, "bio" = 4, "rad" = 0, "fire" = 50, "acid" = 90)
|
||||
flags_inv = HIDEJUMPSUIT
|
||||
cold_protection = 0
|
||||
heat_protection = 0
|
||||
@@ -559,7 +559,7 @@
|
||||
desc = "A tough, hard covering of black chitin with transparent chitin in front."
|
||||
icon_state = "lingarmorhelmet"
|
||||
item_flags = DROPDEL
|
||||
armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 20, "bomb" = 10, "bio" = 4, "rad" = 0, "fire" = 90, "acid" = 90)
|
||||
armor = list("melee" = 70, "bullet" = 60, "laser" = 30, "energy" = 40, "bomb" = 10, "bio" = 4, "rad" = 0, "fire" = 50, "acid" = 90)
|
||||
flags_inv = HIDEEARS|HIDEHAIR|HIDEEYES|HIDEFACIALHAIR|HIDEFACE
|
||||
|
||||
/obj/item/clothing/head/helmet/changeling/Initialize()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
desc = "Our lungs and vocal cords shift, allowing us to briefly emit a noise that deafens and confuses the weak-minded."
|
||||
helptext = "Emits a high-frequency sound that confuses and deafens humans, blows out nearby lights and overloads cyborg sensors. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
|
||||
chemical_cost = 20
|
||||
dna_cost = 1
|
||||
dna_cost = 2
|
||||
loudness = 1
|
||||
req_human = 1
|
||||
action_icon = 'icons/mob/actions/actions_changeling.dmi'
|
||||
@@ -37,7 +37,7 @@
|
||||
desc = "We shift our vocal cords to release a high-frequency sound that overloads nearby electronics."
|
||||
helptext = "Emits a high-frequency sound that overloads nearby electronics. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
|
||||
chemical_cost = 20
|
||||
dna_cost = 1
|
||||
dna_cost = 2
|
||||
loudness = 1
|
||||
action_icon = 'icons/mob/actions/actions_changeling.dmi'
|
||||
action_icon_state = "ling_dissonant"
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
name = "Strained Muscles"
|
||||
desc = "We evolve the ability to reduce the acid buildup in our muscles, allowing us to move much faster."
|
||||
helptext = "The strain will make us tired, and we will rapidly become fatigued. Standard weight restrictions, like hardsuits, still apply. Cannot be used in lesser form."
|
||||
chemical_cost = 15
|
||||
dna_cost = 1
|
||||
req_human = 1
|
||||
var/stacks = 0 //Increments every 5 seconds; damage increases over time
|
||||
@@ -15,16 +14,13 @@
|
||||
action_background_icon_state = "bg_ling"
|
||||
|
||||
/obj/effect/proc_holder/changeling/strained_muscles/sting_action(mob/living/carbon/user)
|
||||
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
|
||||
active = !active
|
||||
if(active)
|
||||
to_chat(user, "<span class='notice'>Our muscles tense and strengthen.</span>")
|
||||
changeling.chem_recharge_slowdown += 0.5
|
||||
else
|
||||
user.remove_movespeed_modifier(MOVESPEED_ID_CHANGELING_MUSCLES)
|
||||
user.remove_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
|
||||
to_chat(user, "<span class='notice'>Our muscles relax.</span>")
|
||||
changeling.chem_recharge_slowdown -= 0.5
|
||||
if(stacks >= 20)
|
||||
if(stacks >= 10)
|
||||
to_chat(user, "<span class='danger'>We collapse in exhaustion.</span>")
|
||||
user.DefaultCombatKnockdown(60)
|
||||
user.emote("gasp")
|
||||
@@ -34,26 +30,24 @@
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/changeling/strained_muscles/proc/muscle_loop(mob/living/carbon/user)
|
||||
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
|
||||
while(active)
|
||||
user.add_movespeed_modifier(MOVESPEED_ID_CHANGELING_MUSCLES, update=TRUE, priority=100, multiplicative_slowdown=-1, blacklisted_movetypes=(FLYING|FLOATING))
|
||||
user.add_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
|
||||
if(user.stat != CONSCIOUS || user.staminaloss >= 90)
|
||||
active = !active
|
||||
to_chat(user, "<span class='notice'>Our muscles relax without the energy to strengthen them.</span>")
|
||||
user.DefaultCombatKnockdown(40)
|
||||
user.remove_movespeed_modifier(MOVESPEED_ID_CHANGELING_MUSCLES)
|
||||
changeling.chem_recharge_slowdown -= 0.5
|
||||
user.remove_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
|
||||
break
|
||||
|
||||
stacks++
|
||||
//user.take_bodypart_damage(stacks * 0.03, 0)
|
||||
user.adjustStaminaLoss(stacks*1.3) //At first the changeling may regenerate stamina fast enough to nullify fatigue, but it will stack
|
||||
user.adjustStaminaLoss(stacks*1.5) //At first the changeling may regenerate stamina fast enough to nullify fatigue, but it will stack
|
||||
|
||||
if(stacks == 10) //Warning message that the stacks are getting too high
|
||||
if(stacks == 5) //Warning message that the stacks are getting too high
|
||||
to_chat(user, "<span class='warning'>Our legs are really starting to hurt...</span>")
|
||||
|
||||
sleep(40)
|
||||
|
||||
while(!active && stacks) //Damage stacks decrease fairly rapidly while not in sanic mode
|
||||
while(!active && stacks) //Damage stacks decrease slowly while not in sanic mode
|
||||
stacks--
|
||||
sleep(20)
|
||||
sleep(100)
|
||||
|
||||
@@ -239,7 +239,7 @@
|
||||
if(!do_after(user, repair_values["healing_for_cycle"] * fabricator.speed_multiplier, target = src, \
|
||||
extra_checks = CALLBACK(fabricator, /obj/item/clockwork/replica_fabricator.proc/fabricator_repair_checks, repair_values, src, user, TRUE)))
|
||||
break
|
||||
obj_integrity = CLAMP(obj_integrity + repair_values["healing_for_cycle"], 0, max_integrity)
|
||||
obj_integrity = clamp(obj_integrity + repair_values["healing_for_cycle"], 0, max_integrity)
|
||||
adjust_clockwork_power(-repair_values["power_required"])
|
||||
playsound(src, 'sound/machines/click.ogg', 50, 1)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
if(GLOB.ratvar_awakens)
|
||||
current_power = GLOB.clockwork_power = INFINITY
|
||||
else
|
||||
current_power = GLOB.clockwork_power = CLAMP(GLOB.clockwork_power + amount, 0, MAX_CLOCKWORK_POWER)
|
||||
current_power = GLOB.clockwork_power = clamp(GLOB.clockwork_power + amount, 0, MAX_CLOCKWORK_POWER)
|
||||
for(var/obj/effect/clockwork/sigil/transmission/T in GLOB.all_clockwork_objects)
|
||||
T.update_icon()
|
||||
var/unlock_message
|
||||
|
||||
@@ -211,7 +211,7 @@
|
||||
var/mob/living/carbon/C = L
|
||||
C.stuttering = max(8, C.stuttering)
|
||||
C.drowsyness = max(8, C.drowsyness)
|
||||
C.confused += CLAMP(16 - C.confused, 0, 8)
|
||||
C.confused += clamp(16 - C.confused, 0, 8)
|
||||
C.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
|
||||
L.adjustFireLoss(15)
|
||||
..()
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
if(issilicon(L))
|
||||
L.DefaultCombatKnockdown(100)
|
||||
else if(iscultist(L))
|
||||
L.confused += CLAMP(10 - L.confused, 0, 5) // Spearthrow now confuses enemy cultists + just deals extra damage / sets on fire instead of hardstunning + damage
|
||||
L.confused += clamp(10 - L.confused, 0, 5) // Spearthrow now confuses enemy cultists + just deals extra damage / sets on fire instead of hardstunning + damage
|
||||
to_chat(L, "<span class ='userdanger'>[src] crashes into you with burning force, sending you reeling!</span>")
|
||||
L.adjust_fire_stacks(2)
|
||||
L.DefaultCombatKnockdown(1)
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
laws = new /datum/ai_laws/ratvar()
|
||||
braintype = picked_name
|
||||
GLOB.all_clockwork_objects += src
|
||||
brainmob.add_blocked_language(subtypesof(/datum/language) - /datum/language/ratvar, LANGUAGE_CLOCKIE)
|
||||
|
||||
/obj/item/mmi/posibrain/soul_vessel/Destroy()
|
||||
GLOB.all_clockwork_objects -= src
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
var/mob/living/L = M.current
|
||||
if(ishuman(L) && L.stat != DEAD)
|
||||
human_servants++
|
||||
construct_limit = round(CLAMP((human_servants / 4), 1, 3)) //1 per 4 human servants, maximum of 3
|
||||
construct_limit = round(clamp((human_servants / 4), 1, 3)) //1 per 4 human servants, maximum of 3
|
||||
|
||||
//Summon Neovgre: Summon a very powerful combat mech that explodes when destroyed for massive damage.
|
||||
/datum/clockwork_scripture/create_object/summon_arbiter
|
||||
|
||||
@@ -63,4 +63,4 @@
|
||||
break
|
||||
if(!M)
|
||||
M = H.apply_status_effect(STATUS_EFFECT_MANIAMOTOR, src)
|
||||
M.severity = CLAMP(M.severity + ((11 - get_dist(src, H)) * efficiency * efficiency), 0, MAX_MANIA_SEVERITY)
|
||||
M.severity = clamp(M.severity + ((11 - get_dist(src, H)) * efficiency * efficiency), 0, MAX_MANIA_SEVERITY)
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
current = mob_override
|
||||
GLOB.all_clockwork_mobs += current
|
||||
current.faction |= "ratvar"
|
||||
current.grant_language(/datum/language/ratvar)
|
||||
current.grant_language(/datum/language/ratvar, TRUE, TRUE, LANGUAGE_CLOCKIE)
|
||||
current.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them for whatever reason, we need to update buttons
|
||||
if(issilicon(current))
|
||||
var/mob/living/silicon/S = current
|
||||
@@ -102,6 +102,7 @@
|
||||
R.module.rebuild_modules()
|
||||
else if(isAI(S))
|
||||
var/mob/living/silicon/ai/A = S
|
||||
A.add_blocked_language(subtypesof(/datum/language) - /datum/language/ratvar, LANGUAGE_CLOCKIE)
|
||||
A.can_be_carded = FALSE
|
||||
A.requires_power = POWER_REQ_CLOCKCULT
|
||||
var/list/AI_frame = list(mutable_appearance('icons/mob/clockwork_mobs.dmi', "aiframe")) //make the AI's cool frame
|
||||
@@ -142,7 +143,7 @@
|
||||
current = mob_override
|
||||
GLOB.all_clockwork_mobs -= current
|
||||
current.faction -= "ratvar"
|
||||
current.remove_language(/datum/language/ratvar)
|
||||
current.remove_language(/datum/language/ratvar, TRUE, TRUE, LANGUAGE_CLOCKIE)
|
||||
current.clear_alert("clockinfo")
|
||||
for(var/datum/action/innate/clockwork_armaments/C in owner.current.actions) //Removes any bound clockwork armor
|
||||
qdel(C)
|
||||
@@ -152,6 +153,7 @@
|
||||
var/mob/living/silicon/S = current
|
||||
if(isAI(S))
|
||||
var/mob/living/silicon/ai/A = S
|
||||
A.remove_blocked_language(subtypesof(/datum/language) - /datum/language/ratvar, LANGUAGE_CLOCKIE)
|
||||
A.can_be_carded = initial(A.can_be_carded)
|
||||
A.requires_power = initial(A.requires_power)
|
||||
A.cut_overlays()
|
||||
|
||||
@@ -454,16 +454,16 @@
|
||||
S.emp_act(EMP_HEAVY)
|
||||
else if(iscarbon(target))
|
||||
var/mob/living/carbon/C = L
|
||||
C.silent += CLAMP(12 - C.silent, 0, 6)
|
||||
C.stuttering += CLAMP(30 - C.stuttering, 0, 15)
|
||||
C.cultslurring += CLAMP(30 - C.cultslurring, 0, 15)
|
||||
C.silent += clamp(12 - C.silent, 0, 6)
|
||||
C.stuttering += clamp(30 - C.stuttering, 0, 15)
|
||||
C.cultslurring += clamp(30 - C.cultslurring, 0, 15)
|
||||
C.Jitter(15)
|
||||
else // cultstun no longer hardstuns + damages hostile cultists, instead debuffs them hard + deals some damage; debuffs for a bit longer since they don't add the clockie belligerent debuff
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/C = L
|
||||
C.stuttering = max(10, C.stuttering)
|
||||
C.drowsyness = max(10, C.drowsyness)
|
||||
C.confused += CLAMP(20 - C.confused, 0, 10)
|
||||
C.confused += clamp(20 - C.confused, 0, 10)
|
||||
L.adjustBruteLoss(15)
|
||||
to_chat(user, "<span class='cultitalic'>In an brilliant flash of red, [L] [iscultist(L) ? "writhes in pain" : "falls to the ground!"]</span>")
|
||||
uses--
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
current.faction |= "cult"
|
||||
current.grant_language(/datum/language/narsie)
|
||||
current.grant_language(/datum/language/narsie, TRUE, TRUE, LANGUAGE_CULTIST)
|
||||
if(!cult_team?.cult_master)
|
||||
vote.Grant(current)
|
||||
communion.Grant(current)
|
||||
@@ -134,7 +134,7 @@
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
current.faction -= "cult"
|
||||
current.remove_language(/datum/language/narsie)
|
||||
current.remove_language(/datum/language/narsie, TRUE, TRUE, LANGUAGE_CULTIST)
|
||||
vote.Remove(current)
|
||||
communion.Remove(current)
|
||||
magic.Remove(current)
|
||||
@@ -441,7 +441,7 @@
|
||||
if(objective.completable)
|
||||
var/completion = objective.check_completion()
|
||||
if(completion >= 1)
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
|
||||
else if(completion <= 0)
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
else
|
||||
|
||||
@@ -539,7 +539,7 @@
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
|
||||
var/cursetime = 1800
|
||||
var/timer = SSshuttle.emergency.timeLeft(1) + cursetime
|
||||
var/security_num = seclevel2num(get_security_level())
|
||||
var/security_num = SECLEVEL2NUM(NUM2SECLEVEL(GLOB.security_level))
|
||||
var/set_coefficient = 1
|
||||
switch(security_num)
|
||||
if(SEC_LEVEL_GREEN)
|
||||
@@ -731,7 +731,7 @@
|
||||
if(!L.anti_magic_check())
|
||||
if(is_servant_of_ratvar(L))
|
||||
to_chat(L, "<span class='cultlarge'>\"Kneel for me, scum\"</span>")
|
||||
L.confused += CLAMP(10 - L.confused, 0, 5) //confuses and lightly knockdowns + damages hostile cultists instead of hardstunning like before
|
||||
L.confused += clamp(10 - L.confused, 0, 5) //confuses and lightly knockdowns + damages hostile cultists instead of hardstunning like before
|
||||
L.DefaultCombatKnockdown(15)
|
||||
L.adjustBruteLoss(10)
|
||||
else
|
||||
|
||||
@@ -527,7 +527,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
|
||||
|
||||
/datum/antagonist/devil/apply_innate_effects(mob/living/mob_override)
|
||||
give_appropriate_spells()
|
||||
owner.current.grant_all_languages(TRUE)
|
||||
owner.current.grant_all_languages(TRUE, TRUE, TRUE, LANGUAGE_DEVIL)
|
||||
update_hud()
|
||||
.=..()
|
||||
|
||||
@@ -536,6 +536,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
|
||||
var/obj/effect/proc_holder/spell/S = X
|
||||
if(is_type_in_typecache(S, devil_spells))
|
||||
owner.RemoveSpell(S)
|
||||
owner.current.remove_all_languages(LANGUAGE_DEVIL)
|
||||
.=..()
|
||||
|
||||
/datum/antagonist/devil/proc/printdevilinfo()
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
/mob/living/carbon/true_devil/Initialize()
|
||||
create_bodyparts() //initialize bodyparts
|
||||
create_internal_organs()
|
||||
grant_all_languages(omnitongue=TRUE)
|
||||
grant_all_languages()
|
||||
..()
|
||||
|
||||
/mob/living/carbon/true_devil/create_internal_organs()
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
if(objective.completable)
|
||||
var/completion = objective.check_completion()
|
||||
if(completion >= 1)
|
||||
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
|
||||
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
|
||||
else if(completion <= 0)
|
||||
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
win = FALSE
|
||||
|
||||
@@ -128,6 +128,9 @@ the new instance inside the host to be updated to the template's stats.
|
||||
link = FOLLOW_LINK(src, to_follow)
|
||||
else
|
||||
link = ""
|
||||
// Create map text prior to modifying message for goonchat
|
||||
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
|
||||
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||
// Recompose the message, because it's scrambled by default
|
||||
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
|
||||
to_chat(src, "[link] [message]")
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
STOP_PROCESSING(SSobj, core)
|
||||
update_icon()
|
||||
GLOB.poi_list |= src
|
||||
previous_level = get_security_level()
|
||||
previous_level = NUM2SECLEVEL(GLOB.security_level)
|
||||
|
||||
/obj/machinery/nuclearbomb/Destroy()
|
||||
safety = FALSE
|
||||
@@ -360,7 +360,7 @@
|
||||
if(NUKEUI_AWAIT_TIMER)
|
||||
var/number_value = text2num(numeric_input)
|
||||
if(number_value)
|
||||
timer_set = CLAMP(number_value, minimum_timer_set, maximum_timer_set)
|
||||
timer_set = clamp(number_value, minimum_timer_set, maximum_timer_set)
|
||||
playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
|
||||
set_safety()
|
||||
. = TRUE
|
||||
@@ -421,7 +421,7 @@
|
||||
return
|
||||
timing = !timing
|
||||
if(timing)
|
||||
previous_level = get_security_level()
|
||||
previous_level = NUM2SECLEVEL(GLOB.security_level)
|
||||
detonation_timer = world.time + (timer_set * 10)
|
||||
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
|
||||
S.switch_mode_to(TRACK_INFILTRATOR)
|
||||
|
||||
@@ -64,8 +64,8 @@
|
||||
|
||||
/mob/living/simple_animal/slaughter/phasein()
|
||||
. = ..()
|
||||
add_movespeed_modifier(MOVESPEED_ID_SLAUGHTER, update=TRUE, priority=100, multiplicative_slowdown=-1)
|
||||
addtimer(CALLBACK(src, .proc/remove_movespeed_modifier, MOVESPEED_ID_SLAUGHTER, TRUE), 6 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
|
||||
add_movespeed_modifier(/datum/movespeed_modifier/slaughter)
|
||||
addtimer(CALLBACK(src, .proc/remove_movespeed_modifier, /datum/movespeed_modifier/slaughter), 6 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
|
||||
|
||||
|
||||
//The loot from killing a slaughter demon - can be consumed to allow the user to blood crawl
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
|
||||
if(isturf(L.loc))
|
||||
spawn_locs += L.loc
|
||||
for(var/obj/effect/landmark/loneopspawn/L in GLOB.landmarks_list)
|
||||
if(isturf(L.loc))
|
||||
spawn_locs += L.loc
|
||||
|
||||
if(!spawn_locs)
|
||||
message_admins("No valid spawn locations found, aborting...")
|
||||
|
||||
@@ -65,5 +65,5 @@
|
||||
/datum/traitor_class/ai/finalize_traitor(datum/antagonist/traitor/T)
|
||||
T.add_law_zero()
|
||||
T.owner.current.playsound_local(get_turf(T.owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE)
|
||||
T.owner.current.grant_language(/datum/language/codespeak)
|
||||
T.owner.current.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MALF)
|
||||
return FALSE
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
var/datum/traitor_class/class = GLOB.traitor_classes[C]
|
||||
var/weight = LOGISTIC_FUNCTION(1.5*class.weight,chaos_weight,class.chaos,0)
|
||||
weights[C] = weight * 1000
|
||||
var/choice = pickweightAllowZero(weights)
|
||||
var/choice = pickweight(weights, 0)
|
||||
if(!choice)
|
||||
choice = TRAITOR_HUMAN // it's an "easter egg"
|
||||
var/datum/traitor_class/actual_class = GLOB.traitor_classes[choice]
|
||||
@@ -239,7 +239,7 @@
|
||||
if(objective.completable)
|
||||
var/completion = objective.check_completion()
|
||||
if(completion >= 1)
|
||||
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
|
||||
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
|
||||
else if(completion <= 0)
|
||||
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
traitorwin = FALSE
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
/obj/item/scrying/attack_self(mob/user)
|
||||
to_chat(user, "<span class='notice'>You can see...everything!</span>")
|
||||
visible_message("<span class='danger'>[user] stares into [src], their eyes glazing over.</span>")
|
||||
user.ghostize(1)
|
||||
user.ghostize(1, voluntary = TRUE)
|
||||
|
||||
/////////////////////////////////////////Necromantic Stone///////////////////
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@
|
||||
newstruct.cancel_camera()
|
||||
|
||||
|
||||
/obj/item/soulstone/proc/init_shade(mob/living/carbon/human/T, mob/U, vic = 0)
|
||||
/obj/item/soulstone/proc/init_shade(mob/living/carbon/human/T, mob/user, vic = 0)
|
||||
new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton
|
||||
T.stop_sound_channel(CHANNEL_HEARTBEAT)
|
||||
T.invisibility = INVISIBILITY_ABSTRACT
|
||||
@@ -245,20 +245,23 @@
|
||||
S.name = "Shade of [T.real_name]"
|
||||
S.real_name = "Shade of [T.real_name]"
|
||||
T.transfer_ckey(S)
|
||||
S.language_holder = U.language_holder.copy(S)
|
||||
if(U)
|
||||
S.faction |= "[REF(U)]" //Add the master as a faction, allowing inter-mob cooperation
|
||||
if(U && iscultist(U))
|
||||
S.copy_languages(T, LANGUAGE_MIND)//Copies the old mobs languages into the new mob holder.
|
||||
S.copy_languages(user, LANGUAGE_MASTER)
|
||||
S.update_atom_languages()
|
||||
grant_all_languages(FALSE, FALSE, TRUE) //Grants omnitongue
|
||||
if(user)
|
||||
S.faction |= "[REF(user)]" //Add the master as a faction, allowing inter-mob cooperation
|
||||
if(user && iscultist(user))
|
||||
SSticker.mode.add_cultist(S.mind, 0)
|
||||
S.cancel_camera()
|
||||
name = "soulstone: Shade of [T.real_name]"
|
||||
icon_state = "soulstone2"
|
||||
if(U && (iswizard(U) || usability))
|
||||
to_chat(S, "Your soul has been captured! You are now bound to [U.real_name]'s will. Help [U.p_them()] succeed in [U.p_their()] goals at all costs.")
|
||||
else if(U && iscultist(U))
|
||||
if(user && (iswizard(user) || usability))
|
||||
to_chat(S, "Your soul has been captured! You are now bound to [user.real_name]'s will. Help [user.p_them()] succeed in [user.p_their()] goals at all costs.")
|
||||
else if(user && iscultist(user))
|
||||
to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.")
|
||||
if(vic && U)
|
||||
to_chat(U, "<span class='info'><b>Capture successful!</b>:</span> [T.real_name]'s soul has been ripped from [T.p_their()] body and stored within the soul stone.")
|
||||
if(vic && user)
|
||||
to_chat(user, "<span class='info'><b>Capture successful!</b>:</span> [T.real_name]'s soul has been ripped from [T.p_their()] body and stored within the soul stone.")
|
||||
|
||||
|
||||
/obj/item/soulstone/proc/getCultGhost(mob/living/carbon/human/T, mob/U)
|
||||
|
||||
@@ -294,7 +294,7 @@
|
||||
name = "Staff of Change"
|
||||
desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself."
|
||||
item_path = /obj/item/gun/magic/staff/change
|
||||
dynamic_requirement = 60
|
||||
dynamic_requirement = 200
|
||||
|
||||
/datum/spellbook_entry/item/staffanimation
|
||||
name = "Staff of Animation"
|
||||
@@ -361,7 +361,7 @@
|
||||
desc = "A collection of wands that allow for a wide variety of utility. Wands have a limited number of charges, so be conservative in use. Comes in a handy belt."
|
||||
item_path = /obj/item/storage/belt/wands/full
|
||||
category = "Defensive"
|
||||
dynamic_requirement = 60
|
||||
dynamic_requirement = 200
|
||||
|
||||
/datum/spellbook_entry/item/armor
|
||||
name = "Mastercrafted Armor Set"
|
||||
@@ -386,7 +386,7 @@
|
||||
name = "Plasma Fist"
|
||||
desc = "A forbidden martial art designed on the surging power of plasma. Use it to harness the ancient power."
|
||||
item_path = /obj/item/book/granter/martial/plasma_fist
|
||||
cost = 3
|
||||
cost = 2
|
||||
|
||||
/datum/spellbook_entry/item/guardian
|
||||
name = "Guardian Deck"
|
||||
|
||||
@@ -269,7 +269,7 @@
|
||||
if(objective.completable)
|
||||
var/completion = objective.check_completion()
|
||||
if(completion >= 1)
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
|
||||
else if(completion <= 0)
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
wizardwin = FALSE
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
/mob/living
|
||||
var/mb_cd_length = 5 SECONDS //5 second cooldown for masturbating because fuck spam.
|
||||
var/mb_cd_timer = 0 //The timer itself
|
||||
|
||||
/mob/living/carbon/human
|
||||
var/saved_underwear = ""//saves their underwear so it can be toggled later
|
||||
var/saved_undershirt = ""
|
||||
var/saved_socks = ""
|
||||
var/hidden_underwear = FALSE
|
||||
var/hidden_undershirt = FALSE
|
||||
var/hidden_socks = FALSE
|
||||
|
||||
//Mob procs
|
||||
/mob/living/carbon/human/proc/underwear_toggle()
|
||||
set name = "Toggle undergarments"
|
||||
set category = "IC"
|
||||
|
||||
var/confirm = input(src, "Select what part of your form to alter", "Undergarment Toggling") as null|anything in list("Top", "Bottom", "Socks", "All")
|
||||
if(!confirm)
|
||||
return
|
||||
if(confirm == "Top")
|
||||
hidden_undershirt = !hidden_undershirt
|
||||
|
||||
if(confirm == "Bottom")
|
||||
hidden_underwear = !hidden_underwear
|
||||
|
||||
if(confirm == "Socks")
|
||||
hidden_socks = !hidden_socks
|
||||
|
||||
if(confirm == "All")
|
||||
var/on_off = (hidden_undershirt || hidden_underwear || hidden_socks) ? FALSE : TRUE
|
||||
hidden_undershirt = on_off
|
||||
hidden_underwear = on_off
|
||||
hidden_socks = on_off
|
||||
|
||||
update_body(TRUE)
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/adjust_arousal(strength,aphro = FALSE,maso = FALSE) // returns all genitals that were adjust
|
||||
var/list/obj/item/organ/genital/genit_list = list()
|
||||
if(!client?.prefs.arousable || (aphro && (client?.prefs.cit_toggles & NO_APHRO)) || (maso && !HAS_TRAIT(src, TRAIT_MASO)))
|
||||
return // no adjusting made here
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(G.genital_flags & GENITAL_CAN_AROUSE && !G.aroused_state && prob(strength*G.sensitivity))
|
||||
G.set_aroused_state(strength > 0)
|
||||
G.update_appearance()
|
||||
if(G.aroused_state)
|
||||
genit_list += G
|
||||
return genit_list
|
||||
|
||||
/obj/item/organ/genital/proc/climaxable(mob/living/carbon/human/H, silent = FALSE) //returns the fluid source (ergo reagents holder) if found.
|
||||
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
|
||||
. = reagents
|
||||
else
|
||||
if(linked_organ)
|
||||
. = linked_organ.reagents
|
||||
if(!. && !silent)
|
||||
to_chat(H, "<span class='warning'>Your [name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
|
||||
|
||||
/mob/living/carbon/human/proc/do_climax(datum/reagents/R, atom/target, obj/item/organ/genital/G, spill = TRUE)
|
||||
if(!G)
|
||||
return
|
||||
if(!target || !R)
|
||||
return
|
||||
var/turfing = isturf(target)
|
||||
G.generate_fluid(R)
|
||||
if(spill && R.total_volume >= 5)
|
||||
R.reaction(turfing ? target : target.loc, TOUCH, 1, 0)
|
||||
if(!turfing)
|
||||
R.trans_to(target, R.total_volume * (spill ? G.fluid_transfer_factor : 1))
|
||||
G.time_since_last_orgasm = 0
|
||||
R.clear_reagents()
|
||||
|
||||
/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30) //This is used for forced orgasms and other hands-free climaxes
|
||||
var/datum/reagents/fluid_source = G.climaxable(src, TRUE)
|
||||
if(!fluid_source)
|
||||
to_chat(src,"<span class='userdanger'>Your [G.name] cannot cum.</span>")
|
||||
return
|
||||
if(mb_time) //as long as it's not instant, give a warning
|
||||
to_chat(src,"<span class='userlove'>You feel yourself about to orgasm.</span>")
|
||||
if(!do_after(src, mb_time, target = src) || !G.climaxable(src, TRUE))
|
||||
return
|
||||
to_chat(src,"<span class='userlove'>You climax[isturf(loc) ? " onto [loc]" : ""] with your [G.name].</span>")
|
||||
do_climax(fluid_source, loc, G)
|
||||
|
||||
/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, mob/living/L, spillage = TRUE, mb_time = 30) //Used for climaxing with any living thing
|
||||
var/datum/reagents/fluid_source = G.climaxable(src)
|
||||
if(!fluid_source)
|
||||
return
|
||||
if(mb_time) //Skip warning if this is an instant climax.
|
||||
to_chat(src,"<span class='userlove'>You're about to climax with [L]!</span>")
|
||||
to_chat(L,"<span class='userlove'>[src] is about to climax with you!</span>")
|
||||
if(!do_after(src, mb_time, target = src) || !in_range(src, L) || !G.climaxable(src, TRUE))
|
||||
return
|
||||
if(spillage)
|
||||
to_chat(src,"<span class='userlove'>You orgasm with [L], spilling out of them, using your [G.name].</span>")
|
||||
to_chat(L,"<span class='userlove'>[src] climaxes with you, overflowing and spilling, using [p_their()] [G.name]!</span>")
|
||||
else //knots and other non-spilling orgasms
|
||||
to_chat(src,"<span class='userlove'>You climax with [L], your [G.name] spilling nothing.</span>")
|
||||
to_chat(L,"<span class='userlove'>[src] climaxes with you, [p_their()] [G.name] spilling nothing!</span>")
|
||||
SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
|
||||
do_climax(fluid_source, spillage ? loc : L, G, spillage)
|
||||
|
||||
/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/G, obj/item/reagent_containers/container, mb_time = 30) //For beaker-filling, beware the bartender
|
||||
var/datum/reagents/fluid_source = G.climaxable(src)
|
||||
if(!fluid_source)
|
||||
return
|
||||
if(mb_time)
|
||||
to_chat(src,"<span class='userlove'>You start to [G.masturbation_verb] your [G.name] over [container].</span>")
|
||||
if(!do_after(src, mb_time, target = src) || !in_range(src, container) || !G.climaxable(src, TRUE))
|
||||
return
|
||||
to_chat(src,"<span class='userlove'>You used your [G.name] to fill [container].</span>")
|
||||
do_climax(fluid_source, container, G, FALSE)
|
||||
|
||||
/mob/living/carbon/human/proc/pick_climax_genitals(silent = FALSE)
|
||||
var/list/genitals_list
|
||||
var/list/worn_stuff = get_equipped_items()
|
||||
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(CHECK_BITFIELD(G.genital_flags, CAN_CLIMAX_WITH) && G.is_exposed(worn_stuff)) //filter out what you can't masturbate with
|
||||
LAZYADD(genitals_list, G)
|
||||
if(LAZYLEN(genitals_list))
|
||||
var/obj/item/organ/genital/ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
|
||||
return ret_organ
|
||||
else if(!silent)
|
||||
to_chat(src, "<span class='warning'>You cannot climax without available genitals.</span>")
|
||||
|
||||
/mob/living/carbon/human/proc/pick_partner(silent = FALSE)
|
||||
var/list/partners = list()
|
||||
if(pulling)
|
||||
partners += pulling
|
||||
if(pulledby)
|
||||
partners += pulledby
|
||||
//Now we got both of them, let's check if they're proper
|
||||
for(var/mob/living/L in partners)
|
||||
if(!L.client || !L.mind) // can't consent, not a partner
|
||||
partners -= L
|
||||
if(iscarbon(L))
|
||||
var/mob/living/carbon/C = L
|
||||
if(!C.exposed_genitals.len && !C.is_groin_exposed() && !C.is_chest_exposed()) //Nothing through_clothing, no proper partner.
|
||||
partners -= C
|
||||
//NOW the list should only contain correct partners
|
||||
if(!partners.len)
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>You cannot do this alone.</span>")
|
||||
return //No one left.
|
||||
var/mob/living/target = input(src, "With whom?", "Sexual partner", null) as null|anything in partners //pick one, default to null
|
||||
if(target && in_range(src, target))
|
||||
to_chat(src,"<span class='notice'>Waiting for consent...</span>")
|
||||
var/consenting = input(target, "Do you want [src] to climax with you?","Climax mechanics","No") in list("Yes","No")
|
||||
if(consenting == "Yes")
|
||||
return target
|
||||
else
|
||||
message_admins("[src] tried to climax with [target], but [target] did not consent.")
|
||||
log_consent("[src] tried to climax with [target], but [target] did not consent.")
|
||||
|
||||
/mob/living/carbon/human/proc/pick_climax_container(silent = FALSE)
|
||||
var/list/containers_list = list()
|
||||
|
||||
for(var/obj/item/reagent_containers/C in held_items)
|
||||
if(C.is_open_container() || istype(C, /obj/item/reagent_containers/food/snacks))
|
||||
containers_list += C
|
||||
for(var/obj/item/reagent_containers/C in range(1, src))
|
||||
if((C.is_open_container() || istype(C, /obj/item/reagent_containers/food/snacks)) && CanReach(C))
|
||||
containers_list += C
|
||||
|
||||
if(containers_list.len)
|
||||
var/obj/item/reagent_containers/SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list
|
||||
if(SC && CanReach(SC))
|
||||
return SC
|
||||
else if(!silent)
|
||||
to_chat(src, "<span class='warning'>You cannot do this without an appropriate container.</span>")
|
||||
|
||||
/mob/living/carbon/human/proc/available_rosie_palms(silent = FALSE, list/whitelist_typepaths = list(/obj/item/dildo))
|
||||
if(restrained(TRUE)) //TRUE ignores grabs
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>You can't do that while restrained!</span>")
|
||||
return FALSE
|
||||
if(!get_num_arms() || !get_empty_held_indexes())
|
||||
if(whitelist_typepaths)
|
||||
if(!islist(whitelist_typepaths))
|
||||
whitelist_typepaths = list(whitelist_typepaths)
|
||||
for(var/path in whitelist_typepaths)
|
||||
if(is_holding_item_of_type(path))
|
||||
return TRUE
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
//Here's the main proc itself
|
||||
/mob/living/carbon/human/proc/mob_climax(forced_climax=FALSE) //Forced is instead of the other proc, makes you cum if you have the tools for it, ignoring restraints
|
||||
if(mb_cd_timer > world.time)
|
||||
if(!forced_climax) //Don't spam the message to the victim if forced to come too fast
|
||||
to_chat(src, "<span class='warning'>You need to wait [DisplayTimeText((mb_cd_timer - world.time), TRUE)] before you can do that again!</span>")
|
||||
return
|
||||
|
||||
if(!client?.prefs.arousable || !has_dna())
|
||||
return
|
||||
if(stat == DEAD)
|
||||
if(!forced_climax)
|
||||
to_chat(src, "<span class='warning'>You can't do that while dead!</span>")
|
||||
return
|
||||
if(forced_climax) //Something forced us to cum, this is not a masturbation thing and does not progress to the other checks
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(!CHECK_BITFIELD(G.genital_flags, CAN_CLIMAX_WITH)) //Skip things like wombs and testicles
|
||||
continue
|
||||
var/mob/living/partner
|
||||
var/check_target
|
||||
var/list/worn_stuff = get_equipped_items()
|
||||
|
||||
if(G.is_exposed(worn_stuff))
|
||||
if(pulling) //Are we pulling someone? Priority target, we can't be making option menus for this, has to be quick
|
||||
if(isliving(pulling)) //Don't fuck objects
|
||||
check_target = pulling
|
||||
if(pulledby && !check_target) //prioritise pulled over pulledby
|
||||
if(isliving(pulledby))
|
||||
check_target = pulledby
|
||||
//Now we should have a partner, or else we have to come alone
|
||||
if(check_target)
|
||||
if(iscarbon(check_target)) //carbons can have clothes
|
||||
var/mob/living/carbon/C = check_target
|
||||
if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) //Are they naked enough?
|
||||
partner = C
|
||||
else //A cat is fine too
|
||||
partner = check_target
|
||||
if(partner) //Did they pass the clothing checks?
|
||||
mob_climax_partner(G, partner, mb_time = 0) //Instant climax due to forced
|
||||
continue //You've climaxed once with this organ, continue on
|
||||
//not exposed OR if no partner was found while exposed, climax alone
|
||||
mob_climax_outside(G, mb_time = 0) //removed climax timer for sudden, forced orgasms
|
||||
//Now all genitals that could climax, have.
|
||||
//Since this was a forced climax, we do not need to continue with the other stuff
|
||||
mb_cd_timer = world.time + mb_cd_length
|
||||
return
|
||||
//If we get here, then this is not a forced climax and we gotta check a few things.
|
||||
|
||||
if(stat == UNCONSCIOUS) //No sleep-masturbation, you're unconscious.
|
||||
to_chat(src, "<span class='warning'>You must be conscious to do that!</span>")
|
||||
return
|
||||
|
||||
//Ok, now we check what they want to do.
|
||||
var/choice = input(src, "Select sexual activity", "Sexual activity:") as null|anything in list("Climax alone","Climax with partner", "Fill container")
|
||||
if(!choice)
|
||||
return
|
||||
|
||||
switch(choice)
|
||||
if("Climax alone")
|
||||
if(!available_rosie_palms())
|
||||
return
|
||||
var/obj/item/organ/genital/picked_organ = pick_climax_genitals()
|
||||
if(picked_organ && available_rosie_palms(TRUE))
|
||||
mob_climax_outside(picked_organ)
|
||||
if("Climax with partner")
|
||||
//We need no hands, we can be restrained and so on, so let's pick an organ
|
||||
var/obj/item/organ/genital/picked_organ = pick_climax_genitals()
|
||||
if(picked_organ)
|
||||
var/mob/living/partner = pick_partner() //Get someone
|
||||
if(partner)
|
||||
var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as null|anything in list("Yes", "No")
|
||||
if(spillage && in_range(src, partner))
|
||||
mob_climax_partner(picked_organ, partner, spillage == "Yes" ? TRUE : FALSE)
|
||||
if("Fill container")
|
||||
//We'll need hands and no restraints.
|
||||
if(!available_rosie_palms(FALSE, /obj/item/reagent_containers))
|
||||
return
|
||||
//We got hands, let's pick an organ
|
||||
var/obj/item/organ/genital/picked_organ
|
||||
picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
|
||||
if(picked_organ)
|
||||
//Good, got an organ, time to pick a container
|
||||
var/obj/item/reagent_containers/fluid_container = pick_climax_container()
|
||||
if(fluid_container && available_rosie_palms(TRUE, /obj/item/reagent_containers))
|
||||
mob_fill_container(picked_organ, fluid_container)
|
||||
|
||||
mb_cd_timer = world.time + mb_cd_length
|
||||
|
||||
/mob/living/carbon/human/verb/climax_verb()
|
||||
set category = "IC"
|
||||
set name = "Climax"
|
||||
set desc = "Lets you choose a couple ways to ejaculate."
|
||||
mob_climax()
|
||||
@@ -0,0 +1,374 @@
|
||||
/obj/item/organ/genital
|
||||
color = "#fcccb3"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
organ_flags = ORGAN_NO_DISMEMBERMENT
|
||||
var/shape
|
||||
var/sensitivity = 1 // wow if this were ever used that'd be cool but it's not but i'm keeping it for my unshit code
|
||||
var/genital_flags //see citadel_defines.dm
|
||||
var/masturbation_verb = "masturbate"
|
||||
var/orgasm_verb = "cumming" //present continous
|
||||
var/arousal_verb = "You feel aroused"
|
||||
var/unarousal_verb = "You no longer feel aroused"
|
||||
var/fluid_transfer_factor = 0 //How much would a partner get in them if they climax using this?
|
||||
var/size = 2 //can vary between num or text, just used in icon_state strings
|
||||
var/datum/reagent/fluid_id = null
|
||||
var/fluid_max_volume = 50
|
||||
var/fluid_efficiency = 1
|
||||
var/fluid_rate = CUM_RATE
|
||||
var/fluid_mult = 1
|
||||
var/time_since_last_orgasm = 500
|
||||
var/aroused_state = FALSE //Boolean used in icon_state strings
|
||||
var/obj/item/organ/genital/linked_organ
|
||||
var/linked_organ_slot //used for linking an apparatus' organ to its other half on update_link().
|
||||
var/layer_index = GENITAL_LAYER_INDEX //Order should be very important. FIRST vagina, THEN testicles, THEN penis, as this affects the order they are rendered in.
|
||||
|
||||
/obj/item/organ/genital/Initialize(mapload, do_update = TRUE)
|
||||
. = ..()
|
||||
if(fluid_id)
|
||||
create_reagents(fluid_max_volume, NONE, NO_REAGENTS_VALUE)
|
||||
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
|
||||
reagents.add_reagent(fluid_id, fluid_max_volume)
|
||||
if(do_update)
|
||||
update()
|
||||
|
||||
/obj/item/organ/genital/proc/set_aroused_state(new_state)
|
||||
if(!(genital_flags & GENITAL_CAN_AROUSE))
|
||||
return FALSE
|
||||
if(!((HAS_TRAIT(owner,TRAIT_PERMABONER) && !new_state) || HAS_TRAIT(owner,TRAIT_NEVERBONER) && new_state))
|
||||
aroused_state = new_state
|
||||
return aroused_state
|
||||
|
||||
/obj/item/organ/genital/proc/update()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
update_size()
|
||||
update_appearance()
|
||||
if(genital_flags & UPDATE_OWNER_APPEARANCE && owner && ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.update_genitals()
|
||||
if(linked_organ_slot || (linked_organ && !owner))
|
||||
update_link()
|
||||
|
||||
//exposure and through-clothing code
|
||||
/mob/living/carbon
|
||||
var/list/exposed_genitals = list() //Keeping track of them so we don't have to iterate through every genitalia and see if exposed
|
||||
|
||||
/obj/item/organ/genital/proc/is_exposed()
|
||||
if(!owner || genital_flags & (GENITAL_INTERNAL|GENITAL_HIDDEN))
|
||||
return FALSE
|
||||
if(genital_flags & GENITAL_UNDIES_HIDDEN && ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
if(!(NO_UNDERWEAR in H.dna.species.species_traits))
|
||||
var/datum/sprite_accessory/underwear/top/T = H.hidden_undershirt ? null : GLOB.undershirt_list[H.undershirt]
|
||||
var/datum/sprite_accessory/underwear/bottom/B = H.hidden_underwear ? null : GLOB.underwear_list[H.underwear]
|
||||
if(zone == BODY_ZONE_CHEST ? (T?.covers_chest || B?.covers_chest) : (T?.covers_groin || B?.covers_groin))
|
||||
return FALSE
|
||||
if(genital_flags & GENITAL_THROUGH_CLOTHES)
|
||||
return TRUE
|
||||
|
||||
switch(zone) //update as more genitals are added
|
||||
if(BODY_ZONE_CHEST)
|
||||
return owner.is_chest_exposed()
|
||||
if(BODY_ZONE_PRECISE_GROIN)
|
||||
return owner.is_groin_exposed()
|
||||
|
||||
/obj/item/organ/genital/proc/toggle_visibility(visibility, update = TRUE)
|
||||
genital_flags &= ~(GENITAL_THROUGH_CLOTHES|GENITAL_HIDDEN|GENITAL_UNDIES_HIDDEN)
|
||||
if(owner)
|
||||
owner.exposed_genitals -= src
|
||||
switch(visibility)
|
||||
if(GEN_VISIBLE_ALWAYS)
|
||||
genital_flags |= GENITAL_THROUGH_CLOTHES
|
||||
if(owner)
|
||||
owner.exposed_genitals += src
|
||||
if(GEN_VISIBLE_NO_UNDIES)
|
||||
genital_flags |= GENITAL_UNDIES_HIDDEN
|
||||
if(GEN_VISIBLE_NEVER)
|
||||
genital_flags |= GENITAL_HIDDEN
|
||||
|
||||
if(update && owner && ishuman(owner)) //recast to use update genitals proc
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.update_genitals()
|
||||
|
||||
/mob/living/carbon/verb/toggle_genitals()
|
||||
set category = "IC"
|
||||
set name = "Expose/Hide genitals"
|
||||
set desc = "Allows you to toggle which genitals should show through clothes or not."
|
||||
|
||||
if(stat != CONSCIOUS)
|
||||
to_chat(usr, "<span class='warning'>You can toggle genitals visibility right now...</span>")
|
||||
return
|
||||
|
||||
var/list/genital_list = list()
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(!CHECK_BITFIELD(G.genital_flags, GENITAL_INTERNAL))
|
||||
genital_list += G
|
||||
if(!genital_list.len) //There is nothing to expose
|
||||
return
|
||||
//Full list of exposable genitals created
|
||||
var/obj/item/organ/genital/picked_organ
|
||||
picked_organ = input(src, "Choose which genitalia to expose/hide", "Expose/Hide genitals") as null|anything in genital_list
|
||||
if(picked_organ && (picked_organ in internal_organs))
|
||||
var/picked_visibility = input(src, "Choose visibility setting", "Expose/Hide genitals") as null|anything in GLOB.genitals_visibility_toggles
|
||||
if(picked_visibility && picked_organ && (picked_organ in internal_organs))
|
||||
picked_organ.toggle_visibility(picked_visibility)
|
||||
return
|
||||
|
||||
/mob/living/carbon/verb/toggle_arousal_state()
|
||||
set category = "IC"
|
||||
set name = "Toggle genital arousal"
|
||||
set desc = "Allows you to toggle which genitals are showing signs of arousal."
|
||||
var/list/genital_list = list()
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(G.genital_flags & GENITAL_CAN_AROUSE)
|
||||
genital_list += G
|
||||
if(!genital_list.len) //There's nothing that can show arousal
|
||||
return
|
||||
var/obj/item/organ/genital/picked_organ
|
||||
picked_organ = input(src, "Choose which genitalia to toggle arousal on", "Set genital arousal", null) in genital_list
|
||||
if(picked_organ)
|
||||
var/original_state = picked_organ.aroused_state
|
||||
picked_organ.set_aroused_state(!picked_organ.aroused_state)
|
||||
if(original_state != picked_organ.aroused_state)
|
||||
to_chat(src,"<span class='userlove'>[picked_organ.aroused_state ? picked_organ.arousal_verb : picked_organ.unarousal_verb].</span>")
|
||||
else
|
||||
to_chat(src,"<span class='userlove'>You can't make that genital [picked_organ.aroused_state ? "unaroused" : "aroused"]!</span>")
|
||||
picked_organ.update_appearance()
|
||||
return
|
||||
|
||||
|
||||
/obj/item/organ/genital/proc/modify_size(modifier, min = -INFINITY, max = INFINITY)
|
||||
fluid_max_volume += modifier*2.5
|
||||
fluid_rate += modifier/10
|
||||
if(reagents)
|
||||
reagents.maximum_volume = fluid_max_volume
|
||||
return
|
||||
|
||||
/obj/item/organ/genital/proc/update_size()
|
||||
return
|
||||
|
||||
/obj/item/organ/genital/proc/update_appearance()
|
||||
if(!owner || owner.stat == DEAD)
|
||||
aroused_state = FALSE
|
||||
|
||||
/obj/item/organ/genital/on_life()
|
||||
. = ..()
|
||||
if(!reagents || !.)
|
||||
return
|
||||
reagents.maximum_volume = fluid_max_volume
|
||||
if(fluid_id && CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
|
||||
time_since_last_orgasm++
|
||||
|
||||
/obj/item/organ/genital/proc/generate_fluid(datum/reagents/R)
|
||||
var/amount = clamp(fluid_rate * time_since_last_orgasm * fluid_mult,0,fluid_max_volume)
|
||||
R.clear_reagents()
|
||||
R.add_reagent(fluid_id,amount)
|
||||
return TRUE
|
||||
|
||||
/obj/item/organ/genital/proc/update_link()
|
||||
if(owner)
|
||||
if(linked_organ)
|
||||
return FALSE
|
||||
linked_organ = owner.getorganslot(linked_organ_slot)
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = src
|
||||
linked_organ.upon_link()
|
||||
upon_link()
|
||||
return TRUE
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = null
|
||||
linked_organ = null
|
||||
return FALSE
|
||||
|
||||
//post organ duo making arrangements.
|
||||
/obj/item/organ/genital/proc/upon_link()
|
||||
return
|
||||
|
||||
/obj/item/organ/genital/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
|
||||
. = ..()
|
||||
if(.)
|
||||
update()
|
||||
RegisterSignal(owner, COMSIG_MOB_DEATH, .proc/update_appearance)
|
||||
if(genital_flags & GENITAL_THROUGH_CLOTHES)
|
||||
owner.exposed_genitals += src
|
||||
|
||||
/obj/item/organ/genital/Remove(special = FALSE)
|
||||
. = ..()
|
||||
var/mob/living/carbon/C = .
|
||||
update()
|
||||
if(!QDELETED(C))
|
||||
if(genital_flags & UPDATE_OWNER_APPEARANCE && ishuman(C))
|
||||
var/mob/living/carbon/human/H = .
|
||||
H.update_genitals()
|
||||
C.exposed_genitals -= src
|
||||
UnregisterSignal(C, COMSIG_MOB_DEATH)
|
||||
|
||||
//proc to give a player their genitals and stuff when they log in
|
||||
/mob/living/carbon/human/proc/give_genitals(clean = FALSE)//clean will remove all pre-existing genitals. proc will then give them any genitals that are enabled in their DNA
|
||||
if(clean)
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
qdel(G)
|
||||
if (NOGENITALS in dna.species.species_traits)
|
||||
return
|
||||
if(dna.features["has_vag"])
|
||||
give_genital(/obj/item/organ/genital/vagina)
|
||||
if(dna.features["has_womb"])
|
||||
give_genital(/obj/item/organ/genital/womb)
|
||||
if(dna.features["has_balls"])
|
||||
give_genital(/obj/item/organ/genital/testicles)
|
||||
if(dna.features["has_breasts"])
|
||||
give_genital(/obj/item/organ/genital/breasts)
|
||||
if(dna.features["has_cock"])
|
||||
give_genital(/obj/item/organ/genital/penis)
|
||||
|
||||
/mob/living/carbon/human/proc/give_genital(obj/item/organ/genital/G)
|
||||
if(!dna || (NOGENITALS in dna.species.species_traits) || getorganslot(initial(G.slot)))
|
||||
return FALSE
|
||||
G = new G(null, FALSE)
|
||||
G.get_features(src)
|
||||
G.Insert(src)
|
||||
return G
|
||||
|
||||
/obj/item/organ/genital/proc/get_features(mob/living/carbon/human/H)
|
||||
return
|
||||
|
||||
|
||||
//procs to handle sprite overlays being applied to humans
|
||||
|
||||
/mob/living/carbon/human/equip_to_slot(obj/item/I, slot)
|
||||
. = ..()
|
||||
if(!. && I && slot && !(slot in GLOB.no_genitals_update_slots)) //the item was successfully equipped, and the chosen slot wasn't merely storage, hands or cuffs.
|
||||
update_genitals()
|
||||
|
||||
/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE)
|
||||
var/no_update = FALSE
|
||||
if(!I || I == l_store || I == r_store || I == s_store || I == handcuffed || I == legcuffed || get_held_index_of_item(I)) //stops storages, cuffs and held items from triggering it.
|
||||
no_update = TRUE
|
||||
. = ..()
|
||||
if(!. || no_update)
|
||||
return
|
||||
update_genitals()
|
||||
|
||||
/mob/living/carbon/human/proc/update_genitals()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
var/static/list/relevant_layers = list("[GENITALS_BEHIND_LAYER]" = "BEHIND", "[GENITALS_FRONT_LAYER]" = "FRONT")
|
||||
var/static/list/layers_num
|
||||
if(!layers_num)
|
||||
for(var/L in relevant_layers)
|
||||
LAZYSET(layers_num, L, text2num(L))
|
||||
for(var/L in relevant_layers) //Less hardcode
|
||||
remove_overlay(layers_num[L])
|
||||
remove_overlay(GENITALS_EXPOSED_LAYER)
|
||||
if(!LAZYLEN(internal_organs) || ((NOGENITALS in dna.species.species_traits) && !genital_override) || HAS_TRAIT(src, TRAIT_HUSK))
|
||||
return
|
||||
|
||||
//start scanning for genitals
|
||||
|
||||
var/list/gen_index[GENITAL_LAYER_INDEX_LENGTH]
|
||||
var/list/genitals_to_add
|
||||
var/list/fully_exposed
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes
|
||||
LAZYADD(gen_index[G.layer_index], G)
|
||||
for(var/L in gen_index)
|
||||
if(L) //skip nulls
|
||||
LAZYADD(genitals_to_add, L)
|
||||
if(!genitals_to_add)
|
||||
return
|
||||
//Now we added all genitals that aren't internal and should be rendered
|
||||
//start applying overlays
|
||||
for(var/layer in relevant_layers)
|
||||
var/list/standing = list()
|
||||
var/layertext = relevant_layers[layer]
|
||||
for(var/A in genitals_to_add)
|
||||
var/obj/item/organ/genital/G = A
|
||||
var/datum/sprite_accessory/S
|
||||
var/size = G.size
|
||||
switch(G.type)
|
||||
if(/obj/item/organ/genital/penis)
|
||||
S = GLOB.cock_shapes_list[G.shape]
|
||||
if(/obj/item/organ/genital/testicles)
|
||||
S = GLOB.balls_shapes_list[G.shape]
|
||||
if(/obj/item/organ/genital/vagina)
|
||||
S = GLOB.vagina_shapes_list[G.shape]
|
||||
if(/obj/item/organ/genital/breasts)
|
||||
S = GLOB.breasts_shapes_list[G.shape]
|
||||
|
||||
if(!S || S.icon_state == "none")
|
||||
continue
|
||||
var/aroused_state = G.aroused_state && S.alt_aroused
|
||||
var/accessory_icon = S.icon
|
||||
var/do_center = S.center
|
||||
var/dim_x = S.dimension_x
|
||||
var/dim_y = S.dimension_y
|
||||
if(G.genital_flags & GENITAL_CAN_TAUR && S.taur_icon && (!S.feat_taur || dna.features[S.feat_taur]) && dna.species.mutant_bodyparts["taur"])
|
||||
var/datum/sprite_accessory/taur/T = GLOB.taur_list[dna.features["taur"]]
|
||||
if(T?.taur_mode & S.accepted_taurs)
|
||||
accessory_icon = S.taur_icon
|
||||
do_center = TRUE
|
||||
dim_x = S.taur_dimension_x
|
||||
dim_y = S.taur_dimension_y
|
||||
|
||||
var/mutable_appearance/genital_overlay = mutable_appearance(accessory_icon, layer = -layer)
|
||||
if(do_center)
|
||||
genital_overlay = center_image(genital_overlay, dim_x, dim_y)
|
||||
|
||||
if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
|
||||
genital_overlay.color = SKINTONE2HEX(skin_tone)
|
||||
else
|
||||
switch(S.color_src)
|
||||
if("cock_color")
|
||||
genital_overlay.color = "#[dna.features["cock_color"]]"
|
||||
if("balls_color")
|
||||
genital_overlay.color = "#[dna.features["balls_color"]]"
|
||||
if("breasts_color")
|
||||
genital_overlay.color = "#[dna.features["breasts_color"]]"
|
||||
if("vag_color")
|
||||
genital_overlay.color = "#[dna.features["vag_color"]]"
|
||||
|
||||
genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size][(dna.species.use_skintones && !dna.skin_tone_override) ? "_s" : ""]_[aroused_state]_[layertext]"
|
||||
|
||||
if(layers_num[layer] == GENITALS_FRONT_LAYER && G.genital_flags & GENITAL_THROUGH_CLOTHES)
|
||||
genital_overlay.layer = -GENITALS_EXPOSED_LAYER
|
||||
LAZYADD(fully_exposed, genital_overlay)
|
||||
else
|
||||
genital_overlay.layer = -layers_num[layer]
|
||||
standing += genital_overlay
|
||||
|
||||
if(LAZYLEN(standing))
|
||||
overlays_standing[layers_num[layer]] = standing
|
||||
|
||||
if(LAZYLEN(fully_exposed))
|
||||
overlays_standing[GENITALS_EXPOSED_LAYER] = fully_exposed
|
||||
apply_overlay(GENITALS_EXPOSED_LAYER)
|
||||
|
||||
for(var/L in relevant_layers)
|
||||
apply_overlay(layers_num[L])
|
||||
|
||||
|
||||
//Checks to see if organs are new on the mob, and changes their colours so that they don't get crazy colours.
|
||||
/mob/living/carbon/human/proc/emergent_genital_call()
|
||||
if(!client.prefs.arousable)
|
||||
return FALSE
|
||||
|
||||
var/organCheck = locate(/obj/item/organ/genital) in internal_organs
|
||||
var/breastCheck = getorganslot(ORGAN_SLOT_BREASTS)
|
||||
var/willyCheck = getorganslot(ORGAN_SLOT_PENIS)
|
||||
|
||||
if(organCheck == FALSE)
|
||||
if(ishuman(src) && dna.species.use_skintones)
|
||||
dna.features["genitals_use_skintone"] = TRUE
|
||||
if(src.dna.species.fixed_mut_color)
|
||||
dna.features["cock_color"] = "[dna.species.fixed_mut_color]"
|
||||
dna.features["breasts_color"] = "[dna.species.fixed_mut_color]"
|
||||
return
|
||||
//So people who haven't set stuff up don't get rainbow surprises.
|
||||
dna.features["cock_color"] = "[dna.features["mcolor"]]"
|
||||
dna.features["breasts_color"] = "[dna.features["mcolor"]]"
|
||||
else //If there's a new organ, make it the same colour.
|
||||
if(breastCheck == FALSE)
|
||||
dna.features["breasts_color"] = dna.features["cock_color"]
|
||||
else if (willyCheck == FALSE)
|
||||
dna.features["cock_color"] = dna.features["breasts_color"]
|
||||
return TRUE
|
||||
@@ -0,0 +1,121 @@
|
||||
/datum/sprite_accessory
|
||||
var/alt_aroused = FALSE //CIT CODE if this is TRUE, then the genitals will use an alternate icon_state when aroused.
|
||||
var/taur_icon //leave null if the genital doesn't have a taur counterpart.
|
||||
var/accepted_taurs = STYLE_HOOF_TAURIC|STYLE_PAW_TAURIC //Types that match with the accessory.
|
||||
var/feat_taur //the text string of the dna feature to check for those who want to opt out.
|
||||
var/taur_dimension_y = 32
|
||||
var/taur_dimension_x = 32
|
||||
|
||||
|
||||
//DICKS,COCKS,PENISES,WHATEVER YOU WANT TO CALL THEM
|
||||
/datum/sprite_accessory/penis
|
||||
icon = 'icons/obj/genitals/penis_onmob.dmi'
|
||||
name = "penis" //the preview name of the accessory
|
||||
color_src = "cock_color"
|
||||
alt_aroused = TRUE
|
||||
feat_taur = "cock_taur"
|
||||
|
||||
/datum/sprite_accessory/penis/human
|
||||
icon_state = "human"
|
||||
name = "Human"
|
||||
|
||||
/datum/sprite_accessory/penis/knotted
|
||||
icon_state = "knotted"
|
||||
name = "Knotted"
|
||||
taur_icon = 'icons/obj/genitals/taur_penis_onmob.dmi'
|
||||
taur_dimension_x = 64
|
||||
|
||||
/datum/sprite_accessory/penis/flared
|
||||
icon_state = "flared"
|
||||
name = "Flared"
|
||||
taur_icon = 'icons/obj/genitals/taur_penis_onmob.dmi'
|
||||
taur_dimension_x = 64
|
||||
|
||||
/datum/sprite_accessory/penis/barbknot
|
||||
icon_state = "barbknot"
|
||||
name = "Barbed, Knotted"
|
||||
|
||||
/datum/sprite_accessory/penis/tapered
|
||||
icon_state = "tapered"
|
||||
name = "Tapered"
|
||||
taur_icon = 'icons/obj/genitals/taur_penis_onmob.dmi'
|
||||
taur_dimension_x = 64
|
||||
|
||||
/datum/sprite_accessory/penis/tentacle
|
||||
icon_state = "tentacle"
|
||||
name = "Tentacled"
|
||||
|
||||
/datum/sprite_accessory/penis/hemi
|
||||
icon_state = "hemi"
|
||||
name = "Hemi"
|
||||
|
||||
/datum/sprite_accessory/penis/hemiknot
|
||||
icon_state = "hemiknot"
|
||||
name = "Knotted Hemi"
|
||||
|
||||
//Testicles
|
||||
/datum/sprite_accessory/testicles
|
||||
icon = 'icons/obj/genitals/testicles_onmob.dmi'
|
||||
icon_state = "testicle"
|
||||
name = "testicle" //the preview name of the accessory
|
||||
color_src = "balls_color"
|
||||
|
||||
/datum/sprite_accessory/testicles/single
|
||||
icon_state = "single"
|
||||
name = "Single" //Single as "single pair", for clarity.
|
||||
|
||||
//Vaginas
|
||||
/datum/sprite_accessory/vagina
|
||||
icon = 'icons/obj/genitals/vagina_onmob.dmi'
|
||||
name = "vagina"
|
||||
color_src = "vag_color"
|
||||
alt_aroused = TRUE
|
||||
|
||||
/datum/sprite_accessory/vagina/human
|
||||
icon_state = "human"
|
||||
name = "Human"
|
||||
|
||||
/datum/sprite_accessory/vagina/tentacles
|
||||
icon_state = "tentacle"
|
||||
name = "Tentacle"
|
||||
|
||||
/datum/sprite_accessory/vagina/dentata
|
||||
icon_state = "dentata"
|
||||
name = "Dentata"
|
||||
|
||||
/datum/sprite_accessory/vagina/hairy
|
||||
icon_state = "hairy"
|
||||
name = "Hairy"
|
||||
alt_aroused = FALSE
|
||||
|
||||
/datum/sprite_accessory/vagina/spade
|
||||
icon_state = "spade"
|
||||
name = "Spade"
|
||||
alt_aroused = FALSE
|
||||
|
||||
/datum/sprite_accessory/vagina/furred
|
||||
icon_state = "furred"
|
||||
name = "Furred"
|
||||
alt_aroused = FALSE
|
||||
|
||||
/datum/sprite_accessory/vagina/gaping
|
||||
icon_state = "gaping"
|
||||
name = "Gaping"
|
||||
|
||||
//BREASTS BE HERE
|
||||
/datum/sprite_accessory/breasts
|
||||
icon = 'icons/obj/genitals/breasts_onmob.dmi'
|
||||
name = "breasts"
|
||||
color_src = "breasts_color"
|
||||
|
||||
/datum/sprite_accessory/breasts/pair
|
||||
icon_state = "pair"
|
||||
name = "Pair"
|
||||
|
||||
/datum/sprite_accessory/breasts/quad
|
||||
icon_state = "quad"
|
||||
name = "Quad"
|
||||
|
||||
/datum/sprite_accessory/breasts/sextuple
|
||||
icon_state = "sextuple"
|
||||
name = "Sextuple"
|
||||
@@ -0,0 +1,137 @@
|
||||
#define BREASTS_ICON_MIN_SIZE 1
|
||||
#define BREASTS_ICON_MAX_SIZE 6
|
||||
|
||||
/obj/item/organ/genital/breasts
|
||||
name = "breasts"
|
||||
desc = "Female milk producing organs."
|
||||
icon_state = "breasts"
|
||||
icon = 'icons/obj/genitals/breasts.dmi'
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_BREASTS
|
||||
size = BREASTS_SIZE_DEF // "c". Refer to the breast_values static list below for the cups associated number values
|
||||
fluid_id = /datum/reagent/consumable/milk
|
||||
fluid_rate = MILK_RATE
|
||||
shape = DEF_BREASTS_SHAPE
|
||||
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_FUID_PRODUCTION|GENITAL_CAN_AROUSE|UPDATE_OWNER_APPEARANCE|GENITAL_UNDIES_HIDDEN
|
||||
masturbation_verb = "massage"
|
||||
arousal_verb = "Your breasts start feeling sensitive"
|
||||
unarousal_verb = "Your breasts no longer feel sensitive"
|
||||
orgasm_verb = "leaking"
|
||||
fluid_transfer_factor = 0.5
|
||||
var/static/list/breast_values = list("a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5, "f" = 6, "g" = 7, "h" = 8, "i" = 9, "j" = 10, "k" = 11, "l" = 12, "m" = 13, "n" = 14, "o" = 15, "huge" = 16, "flat" = 0)
|
||||
var/cached_size //these two vars pertain size modifications and so should be expressed in NUMBERS.
|
||||
var/prev_size //former cached_size value, to allow update_size() to early return should be there no significant changes.
|
||||
|
||||
/obj/item/organ/genital/breasts/Initialize(mapload, do_update = TRUE)
|
||||
if(do_update)
|
||||
cached_size = breast_values[size]
|
||||
prev_size = cached_size
|
||||
return ..()
|
||||
|
||||
/obj/item/organ/genital/breasts/update_appearance()
|
||||
. = ..()
|
||||
var/lowershape = lowertext(shape)
|
||||
switch(lowershape)
|
||||
if("pair")
|
||||
desc = "You see a pair of breasts."
|
||||
if("quad")
|
||||
desc = "You see two pairs of breast, one just under the other."
|
||||
if("sextuple")
|
||||
desc = "You see three sets of breasts, running from their chest to their belly."
|
||||
else
|
||||
desc = "You see some breasts, they seem to be quite exotic."
|
||||
if(size == "huge")
|
||||
desc = "You see [pick("some serious honkers", "a real set of badonkers", "some dobonhonkeros", "massive dohoonkabhankoloos", "two big old tonhongerekoogers", "a couple of giant bonkhonagahoogs", "a pair of humongous hungolomghnonoloughongous")]. Their volume is way beyond cupsize now, measuring in about [round(cached_size)]cm in diameter."
|
||||
else
|
||||
if (size == "flat")
|
||||
desc += " They're very small and flatchested, however."
|
||||
else
|
||||
desc += " You estimate that they're [uppertext(size)]-cups."
|
||||
|
||||
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION) && aroused_state)
|
||||
var/datum/reagent/R = GLOB.chemical_reagents_list[fluid_id]
|
||||
if(R)
|
||||
desc += " They're leaking [lowertext(R.name)]."
|
||||
var/datum/sprite_accessory/S = GLOB.breasts_shapes_list[shape]
|
||||
var/icon_shape = S ? S.icon_state : "pair"
|
||||
var/icon_size = clamp(breast_values[size], BREASTS_ICON_MIN_SIZE, BREASTS_ICON_MAX_SIZE)
|
||||
icon_state = "breasts_[icon_shape]_[icon_size]"
|
||||
if(owner)
|
||||
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
|
||||
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
|
||||
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
if(!H.dna.skin_tone_override)
|
||||
icon_state += "_s"
|
||||
else
|
||||
color = "#[owner.dna.features["breasts_color"]]"
|
||||
|
||||
//Allows breasts to grow and change size, with sprite changes too.
|
||||
//maximum wah
|
||||
//Comical sizes slow you down in movement and actions.
|
||||
//Ridiculous sizes makes you more cumbersome.
|
||||
//this is far too lewd wah
|
||||
|
||||
/obj/item/organ/genital/breasts/modify_size(modifier, min = -INFINITY, max = INFINITY)
|
||||
var/new_value = clamp(cached_size + modifier, min, max)
|
||||
if(new_value == cached_size)
|
||||
return
|
||||
prev_size = cached_size
|
||||
cached_size = new_value
|
||||
update()
|
||||
..()
|
||||
|
||||
/obj/item/organ/genital/breasts/update_size()//wah
|
||||
var/rounded_cached = round(cached_size)
|
||||
if(cached_size < 0)//I don't actually know what round() does to negative numbers, so to be safe!!fixed
|
||||
if(owner)
|
||||
to_chat(owner, "<span class='warning'>You feel your breasts shrinking away from your body as your chest flattens out.</span>")
|
||||
QDEL_IN(src, 1)
|
||||
return
|
||||
var/enlargement = FALSE
|
||||
switch(rounded_cached)
|
||||
if(0) //flatchested
|
||||
size = "flat"
|
||||
if(1 to 8) //modest
|
||||
size = breast_values[rounded_cached]
|
||||
if(9 to 15) //massive
|
||||
size = breast_values[rounded_cached]
|
||||
enlargement = TRUE
|
||||
if(16 to INFINITY) //rediculous
|
||||
size = "huge"
|
||||
enlargement = TRUE
|
||||
if(owner)
|
||||
var/status_effect = owner.has_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
|
||||
if(enlargement && !status_effect)
|
||||
owner.apply_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
|
||||
else if(!enlargement && status_effect)
|
||||
owner.remove_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
|
||||
|
||||
if(rounded_cached < 16 && owner)//Because byond doesn't count from 0, I have to do this.
|
||||
var/mob/living/carbon/human/H = owner
|
||||
var/r_prev_size = round(prev_size)
|
||||
if (rounded_cached > r_prev_size)
|
||||
to_chat(H, "<span class='warning'>Your breasts [pick("swell up to", "flourish into", "expand into", "burst forth into", "grow eagerly into", "amplify into")] a [uppertext(size)]-cup.</span>")
|
||||
else if (rounded_cached < r_prev_size)
|
||||
to_chat(H, "<span class='warning'>Your breasts [pick("shrink down to", "decrease into", "diminish into", "deflate into", "shrivel regretfully into", "contracts into")] a [uppertext(size)]-cup.</span>")
|
||||
|
||||
/obj/item/organ/genital/breasts/get_features(mob/living/carbon/human/H)
|
||||
var/datum/dna/D = H.dna
|
||||
if(D.species.use_skintones && D.features["genitals_use_skintone"])
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
else
|
||||
color = "#[D.features["breasts_color"]]"
|
||||
size = D.features["breasts_size"]
|
||||
shape = D.features["breasts_shape"]
|
||||
if(!D.features["breasts_producing"])
|
||||
DISABLE_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION|CAN_CLIMAX_WITH|CAN_MASTURBATE_WITH)
|
||||
if(!isnum(size))
|
||||
cached_size = breast_values[size]
|
||||
else
|
||||
cached_size = size
|
||||
size = breast_values[size]
|
||||
prev_size = cached_size
|
||||
toggle_visibility(D.features["breasts_visibility"], FALSE)
|
||||
|
||||
#undef BREASTS_ICON_MIN_SIZE
|
||||
#undef BREASTS_ICON_MAX_SIZE
|
||||
@@ -0,0 +1,109 @@
|
||||
/obj/item/organ/genital/penis
|
||||
name = "penis"
|
||||
desc = "A male reproductive organ."
|
||||
icon_state = "penis"
|
||||
icon = 'icons/obj/genitals/penis.dmi'
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_PENIS
|
||||
masturbation_verb = "stroke"
|
||||
arousal_verb = "You pop a boner"
|
||||
unarousal_verb = "Your boner goes down"
|
||||
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_CAN_AROUSE|UPDATE_OWNER_APPEARANCE|GENITAL_UNDIES_HIDDEN|GENITAL_CAN_TAUR
|
||||
linked_organ_slot = ORGAN_SLOT_TESTICLES
|
||||
fluid_transfer_factor = 0.5
|
||||
shape = DEF_COCK_SHAPE
|
||||
size = 2 //arbitrary value derived from length and diameter for sprites.
|
||||
layer_index = PENIS_LAYER_INDEX
|
||||
var/length = 6 //inches
|
||||
|
||||
var/prev_length = 6 //really should be renamed to prev_length
|
||||
var/diameter = 4.38
|
||||
var/diameter_ratio = COCK_DIAMETER_RATIO_DEF //0.25; check citadel_defines.dm
|
||||
|
||||
/obj/item/organ/genital/penis/modify_size(modifier, min = -INFINITY, max = INFINITY)
|
||||
var/new_value = clamp(length + modifier, min, max)
|
||||
if(new_value == length)
|
||||
return
|
||||
prev_length = length
|
||||
length = clamp(length + modifier, min, max)
|
||||
update()
|
||||
..()
|
||||
|
||||
/obj/item/organ/genital/penis/update_size(modified = FALSE)
|
||||
if(length <= 0)//I don't actually know what round() does to negative numbers, so to be safe!!
|
||||
if(owner)
|
||||
to_chat(owner, "<span class='warning'>You feel your tallywacker shrinking away from your body as your groin flattens out!</b></span>")
|
||||
QDEL_IN(src, 1)
|
||||
if(linked_organ)
|
||||
QDEL_IN(linked_organ, 1)
|
||||
return
|
||||
var/rounded_length = round(length)
|
||||
var/new_size
|
||||
var/enlargement = FALSE
|
||||
switch(rounded_length)
|
||||
if(0 to 6) //If modest size
|
||||
new_size = 1
|
||||
if(7 to 11) //If large
|
||||
new_size = 2
|
||||
if(12 to 20) //If massive
|
||||
new_size = 3
|
||||
if(21 to 34) //If massive and due for large effects
|
||||
new_size = 3
|
||||
enlargement = TRUE
|
||||
if(35 to INFINITY) //If comical
|
||||
new_size = 4 //no new sprites for anything larger yet
|
||||
enlargement = TRUE
|
||||
if(owner)
|
||||
var/status_effect = owner.has_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
|
||||
if(enlargement && !status_effect)
|
||||
owner.apply_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
|
||||
else if(!enlargement && status_effect)
|
||||
owner.remove_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
|
||||
if(linked_organ)
|
||||
linked_organ.size = clamp(size + new_size, BALLS_SIZE_MIN, BALLS_SIZE_MAX)
|
||||
linked_organ.update()
|
||||
size = new_size
|
||||
|
||||
if(owner)
|
||||
if (round(length) > round(prev_length))
|
||||
to_chat(owner, "<span class='warning'>Your [pick(GLOB.dick_nouns)] [pick("swells up to", "flourishes into", "expands into", "bursts forth into", "grows eagerly into", "amplifys into")] a [uppertext(round(length))] inch penis.</b></span>")
|
||||
else if ((round(length) < round(prev_length)) && (length > 0.5))
|
||||
to_chat(owner, "<span class='warning'>Your [pick(GLOB.dick_nouns)] [pick("shrinks down to", "decreases into", "diminishes into", "deflates into", "shrivels regretfully into", "contracts into")] a [uppertext(round(length))] inch penis.</b></span>")
|
||||
icon_state = sanitize_text("penis_[shape]_[size]")
|
||||
diameter = (length * diameter_ratio)//Is it just me or is this ludicous, why not make it exponentially decay?
|
||||
|
||||
|
||||
/obj/item/organ/genital/penis/update_appearance()
|
||||
. = ..()
|
||||
var/datum/sprite_accessory/S = GLOB.cock_shapes_list[shape]
|
||||
var/icon_shape = S ? S.icon_state : "human"
|
||||
icon_state = "penis_[icon_shape]_[size]"
|
||||
var/lowershape = lowertext(shape)
|
||||
|
||||
if(owner)
|
||||
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
|
||||
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
|
||||
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
if(!H.dna.skin_tone_override)
|
||||
icon_state += "_s"
|
||||
else
|
||||
color = "#[owner.dna.features["cock_color"]]"
|
||||
if(genital_flags & GENITAL_CAN_TAUR && S?.taur_icon && (!S.feat_taur || owner.dna.features[S.feat_taur]) && owner.dna.species.mutant_bodyparts["taur"])
|
||||
var/datum/sprite_accessory/taur/T = GLOB.taur_list[owner.dna.features["taur"]]
|
||||
if(T.taur_mode & S.accepted_taurs) //looks out of place on those.
|
||||
lowershape = "taur, [lowershape]"
|
||||
|
||||
desc = "You see [aroused_state ? "an erect" : "a flaccid"] [lowershape] [name]. You estimate it's about [round(length, 0.25)] inch[round(length, 0.25) != 1 ? "es" : ""] long and [round(diameter, 0.25)] inch[round(diameter, 0.25) != 1 ? "es" : ""] in diameter."
|
||||
|
||||
/obj/item/organ/genital/penis/get_features(mob/living/carbon/human/H)
|
||||
var/datum/dna/D = H.dna
|
||||
if(D.species.use_skintones && D.features["genitals_use_skintone"])
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
else
|
||||
color = "#[D.features["cock_color"]]"
|
||||
length = D.features["cock_length"]
|
||||
diameter_ratio = D.features["cock_diameter_ratio"]
|
||||
shape = D.features["cock_shape"]
|
||||
prev_length = length
|
||||
toggle_visibility(D.features["cock_visibility"], FALSE)
|
||||
@@ -0,0 +1,67 @@
|
||||
/obj/item/organ/genital/testicles
|
||||
name = "testicles"
|
||||
desc = "A male reproductive organ."
|
||||
icon_state = "testicles"
|
||||
icon = 'icons/obj/genitals/testicles.dmi'
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_TESTICLES
|
||||
size = BALLS_SIZE_MIN
|
||||
arousal_verb = "Your balls ache a little"
|
||||
unarousal_verb = "Your balls finally stop aching, again"
|
||||
linked_organ_slot = ORGAN_SLOT_PENIS
|
||||
genital_flags = CAN_MASTURBATE_WITH|MASTURBATE_LINKED_ORGAN|GENITAL_FUID_PRODUCTION|UPDATE_OWNER_APPEARANCE|GENITAL_UNDIES_HIDDEN
|
||||
var/size_name = "average"
|
||||
shape = DEF_BALLS_SHAPE
|
||||
fluid_id = /datum/reagent/consumable/semen
|
||||
masturbation_verb = "massage"
|
||||
layer_index = TESTICLES_LAYER_INDEX
|
||||
|
||||
/obj/item/organ/genital/testicles/generate_fluid()
|
||||
if(!linked_organ && !update_link())
|
||||
return FALSE
|
||||
return ..()
|
||||
// in memoriam "Your balls finally feel full, again." ??-2020
|
||||
|
||||
/obj/item/organ/genital/testicles/upon_link()
|
||||
size = linked_organ.size
|
||||
update_size()
|
||||
update_appearance()
|
||||
|
||||
/obj/item/organ/genital/testicles/update_size(modified = FALSE)
|
||||
switch(size)
|
||||
if(BALLS_SIZE_MIN)
|
||||
size_name = "average"
|
||||
if(BALLS_SIZE_DEF)
|
||||
size_name = "enlarged"
|
||||
if(BALLS_SIZE_MAX)
|
||||
size_name = "engorged"
|
||||
else
|
||||
size_name = "nonexistant"
|
||||
|
||||
/obj/item/organ/genital/testicles/update_appearance()
|
||||
. = ..()
|
||||
desc = "You see an [size_name] pair of testicles."
|
||||
var/datum/sprite_accessory/S = GLOB.balls_shapes_list[shape]
|
||||
var/icon_shape = S ? S.icon_state : "single"
|
||||
icon_state = "testicles_[icon_shape]_[size]"
|
||||
if(owner)
|
||||
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
|
||||
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
|
||||
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
if(!H.dna.skin_tone_override)
|
||||
icon_state += "_s"
|
||||
else
|
||||
color = "#[owner.dna.features["balls_color"]]"
|
||||
|
||||
/obj/item/organ/genital/testicles/get_features(mob/living/carbon/human/H)
|
||||
var/datum/dna/D = H.dna
|
||||
if(D.species.use_skintones && D.features["genitals_use_skintone"])
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
else
|
||||
color = "#[D.features["balls_color"]]"
|
||||
shape = D.features["balls_shape"]
|
||||
fluid_rate = D.features["balls_cum_rate"]
|
||||
fluid_mult = D.features["balls_cum_mult"]
|
||||
fluid_efficiency = D.features["balls_efficiency"]
|
||||
toggle_visibility(D.features["balls_visibility"], FALSE)
|
||||
@@ -0,0 +1,74 @@
|
||||
/obj/item/organ/genital/vagina
|
||||
name = "vagina"
|
||||
desc = "A female reproductive organ."
|
||||
icon = 'icons/obj/genitals/vagina.dmi'
|
||||
icon_state = ORGAN_SLOT_VAGINA
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = "vagina"
|
||||
size = 1 //There is only 1 size right now
|
||||
shape = DEF_VAGINA_SHAPE
|
||||
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_CAN_AROUSE|GENITAL_UNDIES_HIDDEN
|
||||
masturbation_verb = "finger"
|
||||
arousal_verb = "You feel wetness on your crotch"
|
||||
unarousal_verb = "You no longer feel wet"
|
||||
fluid_transfer_factor = 0.1 //Yes, some amount is exposed to you, go get your AIDS
|
||||
layer_index = VAGINA_LAYER_INDEX
|
||||
var/cap_length = 8//D E P T H (cap = capacity)
|
||||
var/cap_girth = 12
|
||||
var/cap_girth_ratio = 1.5
|
||||
var/clits = 1
|
||||
var/clit_diam = 0.25
|
||||
var/clit_len = 0.25
|
||||
var/list/vag_types = list("tentacle", "dentata", "hairy", "spade", "furred")
|
||||
|
||||
/obj/item/organ/genital/vagina/update_appearance()
|
||||
. = ..()
|
||||
icon_state = "vagina"
|
||||
var/lowershape = lowertext(shape)
|
||||
var/details
|
||||
|
||||
switch(lowershape)
|
||||
if("tentacle")
|
||||
details = "Its opening is lined with several tentacles and "
|
||||
if("dentata")
|
||||
details = "There's teeth inside it and it "
|
||||
if("hairy")
|
||||
details = "It has quite a bit of hair growing on it and "
|
||||
if("human")
|
||||
details = "It is taut with smooth skin, though without much hair and "
|
||||
if("gaping")
|
||||
details = "It is gaping slightly open, though without much hair and "
|
||||
if("spade")
|
||||
details = "It is a plush canine spade, it "
|
||||
if("furred")
|
||||
details = "It has neatly groomed fur around the outer folds, it "
|
||||
else
|
||||
details = "It has an exotic shape and "
|
||||
if(aroused_state)
|
||||
details += "is slick with female arousal."
|
||||
else
|
||||
details += "seems to be dry."
|
||||
|
||||
desc = "You see a vagina. [details]"
|
||||
|
||||
if(owner)
|
||||
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
|
||||
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
|
||||
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
if(!H.dna.skin_tone_override)
|
||||
icon_state += "_s"
|
||||
else
|
||||
color = "#[owner.dna.features["vag_color"]]"
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.update_genitals()
|
||||
|
||||
/obj/item/organ/genital/vagina/get_features(mob/living/carbon/human/H)
|
||||
var/datum/dna/D = H.dna
|
||||
if(D.species.use_skintones && D.features["genitals_use_skintone"])
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
else
|
||||
color = "[D.features["vag_color"]]"
|
||||
shape = "[D.features["vag_shape"]]"
|
||||
toggle_visibility(D.features["vag_visibility"], FALSE)
|
||||
@@ -0,0 +1,10 @@
|
||||
/obj/item/organ/genital/womb
|
||||
name = "womb"
|
||||
desc = "A female reproductive organ."
|
||||
icon = 'icons/obj/genitals/vagina.dmi'
|
||||
icon_state = "womb"
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_WOMB
|
||||
genital_flags = GENITAL_INTERNAL|GENITAL_FUID_PRODUCTION
|
||||
fluid_id = /datum/reagent/consumable/femcum
|
||||
linked_organ_slot = ORGAN_SLOT_VAGINA
|
||||
@@ -0,0 +1,159 @@
|
||||
//////////
|
||||
//DILDOS//
|
||||
//////////
|
||||
/obj/item/dildo
|
||||
name = "dildo"
|
||||
desc = "Floppy!"
|
||||
icon = 'icons/obj/genitals/dildo.dmi'
|
||||
force = 0
|
||||
hitsound = 'sound/weapons/tap.ogg'
|
||||
throwforce = 0
|
||||
icon_state = "dildo_knotted_2"
|
||||
alpha = 192//transparent
|
||||
var/can_customize = FALSE
|
||||
var/dildo_shape = "human"
|
||||
var/dildo_size = 2
|
||||
var/dildo_type = "dildo"//pretty much just used for the icon state
|
||||
var/random_color = TRUE
|
||||
var/random_size = FALSE
|
||||
var/random_shape = FALSE
|
||||
var/is_knotted = FALSE
|
||||
//Lists moved to _cit_helpers.dm as globals so they're not instanced individually
|
||||
|
||||
/obj/item/dildo/proc/update_appearance()
|
||||
icon_state = "[dildo_type]_[dildo_shape]_[dildo_size]"
|
||||
var/sizeword = ""
|
||||
switch(dildo_size)
|
||||
if(1)
|
||||
sizeword = "small "
|
||||
if(3)
|
||||
sizeword = "big "
|
||||
if(4)
|
||||
sizeword = "huge "
|
||||
if(5)
|
||||
sizeword = "gigantic "
|
||||
|
||||
name = "[sizeword][dildo_shape] [can_customize ? "custom " : ""][dildo_type]"
|
||||
|
||||
/obj/item/dildo/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
|
||||
return
|
||||
customize(user)
|
||||
return TRUE
|
||||
|
||||
/obj/item/dildo/proc/customize(mob/living/user)
|
||||
if(!can_customize)
|
||||
return FALSE
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/color_choice = input(user,"Choose a color for your dildo.","Dildo Color") as null|anything in GLOB.dildo_colors
|
||||
if(src && color_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_inlist(color_choice, GLOB.dildo_colors, "Red")
|
||||
color = GLOB.dildo_colors[color_choice]
|
||||
update_appearance()
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/shape_choice = input(user,"Choose a shape for your dildo.","Dildo Shape") as null|anything in GLOB.dildo_shapes
|
||||
if(src && shape_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_inlist(shape_choice, GLOB.dildo_colors, "Knotted")
|
||||
dildo_shape = GLOB.dildo_shapes[shape_choice]
|
||||
update_appearance()
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/size_choice = input(user,"Choose the size for your dildo.","Dildo Size") as null|anything in GLOB.dildo_sizes
|
||||
if(src && size_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_inlist(size_choice, GLOB.dildo_colors, "Medium")
|
||||
dildo_size = GLOB.dildo_sizes[size_choice]
|
||||
update_appearance()
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/transparency_choice = input(user,"Choose the transparency of your dildo. Lower is more transparent!(192-255)","Dildo Transparency") as null|num
|
||||
if(src && transparency_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_integer(transparency_choice, 192, 255, 192)
|
||||
alpha = transparency_choice
|
||||
update_appearance()
|
||||
return TRUE
|
||||
|
||||
/obj/item/dildo/Initialize()
|
||||
. = ..()
|
||||
if(random_color == TRUE)
|
||||
var/randcolor = pick(GLOB.dildo_colors)
|
||||
color = GLOB.dildo_colors[randcolor]
|
||||
if(random_shape == TRUE)
|
||||
var/randshape = pick(GLOB.dildo_shapes)
|
||||
dildo_shape = GLOB.dildo_shapes[randshape]
|
||||
if(random_size == TRUE)
|
||||
var/randsize = pick(GLOB.dildo_sizes)
|
||||
dildo_size = GLOB.dildo_sizes[randsize]
|
||||
update_appearance()
|
||||
alpha = rand(192, 255)
|
||||
pixel_y = rand(-7,7)
|
||||
pixel_x = rand(-7,7)
|
||||
|
||||
/obj/item/dildo/examine(mob/user)
|
||||
. = ..()
|
||||
if(can_customize)
|
||||
. += "<span class='notice'>Alt-Click \the [src.name] to customize it.</span>"
|
||||
|
||||
/obj/item/dildo/random//totally random
|
||||
name = "random dildo"//this name will show up in vendors and shit so you know what you're vending(or don't, i guess :^))
|
||||
random_color = TRUE
|
||||
random_shape = TRUE
|
||||
random_size = TRUE
|
||||
|
||||
/obj/item/dildo/knotted
|
||||
dildo_shape = "knotted"
|
||||
name = "knotted dildo"
|
||||
attack_verb = list("penetrated", "knotted", "slapped", "inseminated")
|
||||
|
||||
obj/item/dildo/human
|
||||
dildo_shape = "human"
|
||||
name = "human dildo"
|
||||
attack_verb = list("penetrated", "slapped", "inseminated")
|
||||
|
||||
obj/item/dildo/plain
|
||||
dildo_shape = "plain"
|
||||
name = "plain dildo"
|
||||
attack_verb = list("penetrated", "slapped", "inseminated")
|
||||
|
||||
obj/item/dildo/flared
|
||||
dildo_shape = "flared"
|
||||
name = "flared dildo"
|
||||
attack_verb = list("penetrated", "slapped", "neighed", "gaped", "prolapsed", "inseminated")
|
||||
|
||||
obj/item/dildo/flared/huge
|
||||
name = "literal horse cock"
|
||||
desc = "THIS THING IS HUGE!"
|
||||
dildo_size = 4
|
||||
|
||||
obj/item/dildo/custom
|
||||
name = "customizable dildo"
|
||||
desc = "Thanks to significant advances in synthetic nanomaterials, this dildo is capable of taking on many different forms to fit the user's preferences! Pricy!"
|
||||
can_customize = TRUE
|
||||
random_color = TRUE
|
||||
random_shape = TRUE
|
||||
random_size = TRUE
|
||||
|
||||
// Suicide acts, by request
|
||||
|
||||
/obj/item/dildo/proc/manual_suicide(mob/living/user)
|
||||
user.visible_message("<span class='suicide'>[user] finally finishes deepthroating the [src], and their life.</span>")
|
||||
user.adjustOxyLoss(200)
|
||||
user.death(0)
|
||||
|
||||
/obj/item/dildo/suicide_act(mob/living/user)
|
||||
// is_knotted = ((src.dildo_shape == "knotted")?"They swallowed the knot":"Their face is turning blue")
|
||||
if(do_after(user,17,target=src))
|
||||
user.visible_message("<span class='suicide'>[user] tears-up and gags as they shove [src] down their throat! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||
playsound(loc, 'sound/weapons/gagging.ogg', 50, 1, -1)
|
||||
user.Stun(150)
|
||||
user.adjust_blurriness(8)
|
||||
var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES)
|
||||
eyes?.applyOrganDamage(10)
|
||||
return MANUAL_SUICIDE
|
||||
|
||||
/obj/item/dildo/flared/huge/suicide_act(mob/living/user)
|
||||
if(do_after(user,35,target=src))
|
||||
user.visible_message("<span class='suicide'>[user] tears-up and gags as they try to deepthroat the [src]! WHY WOULD THEY DO THAT? It looks like [user.p_theyre()] trying to commit suicide!!</span>")
|
||||
playsound(loc, 'sound/weapons/gagging.ogg', 50, 2, -1)
|
||||
user.Stun(300)
|
||||
user.adjust_blurriness(8)
|
||||
return MANUAL_SUICIDE
|
||||
|
||||
@@ -3,14 +3,35 @@
|
||||
desc = "A small electronic device able to control a blast door remotely."
|
||||
icon_state = "control"
|
||||
attachable = TRUE
|
||||
var/id = null
|
||||
var/can_change_id = 0
|
||||
/// Our ID. Make the first character ! if you want to obfuscate it as a mapper via randomization.
|
||||
var/id
|
||||
/// Can the ID be changed if used in hand?
|
||||
var/can_change_id = FALSE
|
||||
/// Show ID?
|
||||
var/show_id = TRUE
|
||||
var/cooldown = FALSE //Door cooldowns
|
||||
|
||||
/obj/item/assembly/control/Initialize(mapload)
|
||||
if(mapload && id)
|
||||
if(copytext(id, 1, 2) == "!")
|
||||
id = SSmapping.get_obfuscated_id(id)
|
||||
return ..()
|
||||
|
||||
/obj/item/assembly/control/examine(mob/user)
|
||||
. = ..()
|
||||
if(id)
|
||||
if(id && show_id)
|
||||
. += "<span class='notice'>Its channel ID is '[id]'.</span>"
|
||||
if(can_change_id)
|
||||
. += "<span class='notice'>Use in hand to change ID.</span>"
|
||||
|
||||
/obj/item/assembly/control/attack_self(mob/living/user)
|
||||
. = ..()
|
||||
if(!can_change_id)
|
||||
return
|
||||
var/new_id
|
||||
new_id = input(user, "Set ID", "Set ID", show_id? id : null) as text|null
|
||||
if(!isnull(new_id)) //0/"" is considered !, so check null instead of just !.
|
||||
id = new_id
|
||||
|
||||
/obj/item/assembly/control/activate()
|
||||
cooldown = TRUE
|
||||
@@ -22,7 +43,6 @@
|
||||
INVOKE_ASYNC(M, openclose ? /obj/machinery/door/poddoor.proc/open : /obj/machinery/door/poddoor.proc/close)
|
||||
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 10)
|
||||
|
||||
|
||||
/obj/item/assembly/control/airlock
|
||||
name = "airlock controller"
|
||||
desc = "A small electronic device able to control an airlock remotely."
|
||||
@@ -123,7 +143,6 @@
|
||||
|
||||
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 50)
|
||||
|
||||
|
||||
/obj/item/assembly/control/crematorium
|
||||
name = "crematorium controller"
|
||||
desc = "An evil-looking remote controller for a crematorium."
|
||||
@@ -135,3 +154,14 @@
|
||||
C.cremate(usr)
|
||||
|
||||
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 50)
|
||||
|
||||
/obj/item/assembly/control/electrochromatic
|
||||
name = "electrochromatic window controller"
|
||||
desc = "Toggles linked electrochromatic windows."
|
||||
can_change_id = TRUE
|
||||
/// Stores our status to prevent windows from desyncing.
|
||||
var/on = FALSE
|
||||
|
||||
/obj/item/assembly/control/electrochromatic/activate()
|
||||
on = !on
|
||||
do_electrochromatic_toggle(on, id)
|
||||
|
||||
@@ -229,6 +229,7 @@
|
||||
var/flashcd = 20
|
||||
var/overheat = 0
|
||||
var/obj/item/organ/cyberimp/arm/flash/I = null
|
||||
var/active_light_strength = 7
|
||||
|
||||
/obj/item/assembly/flash/armimplant/burn_out()
|
||||
if(I && I.owner)
|
||||
@@ -248,6 +249,12 @@
|
||||
update_icon(1)
|
||||
return TRUE
|
||||
|
||||
/obj/item/assembly/flash/armimplant/Moved(oldLoc, dir)
|
||||
. = ..()
|
||||
if(!ismob(loc))
|
||||
set_light(0)
|
||||
else
|
||||
set_light(7)
|
||||
|
||||
/obj/item/assembly/flash/armimplant/proc/cooldown()
|
||||
overheat = FALSE
|
||||
|
||||
@@ -161,12 +161,53 @@
|
||||
return FALSE
|
||||
if(signal.data["code"] != code)
|
||||
return FALSE
|
||||
if(suicider)
|
||||
manual_suicide(suicider)
|
||||
for(var/obj/effect/anomaly/A in get_turf(src))
|
||||
A.anomalyNeutralize()
|
||||
return TRUE
|
||||
|
||||
/obj/item/assembly/signaler/anomaly/attack_self()
|
||||
return
|
||||
/obj/item/assembly/signaler/anomaly/manual_suicide(mob/living/carbon/user)
|
||||
user.visible_message("<span class='suicide'>[user]'s [src] is reacting to the radio signal, warping [user.p_their()] body!</span>")
|
||||
user.suiciding = TRUE
|
||||
user.suicide_log()
|
||||
user.gib()
|
||||
|
||||
/obj/item/assembly/signaler/anomaly/attackby(obj/item/I, mob/user, params)
|
||||
if(I.tool_behaviour == TOOL_ANALYZER)
|
||||
to_chat(user, "<span class='notice'>Analyzing... [src]'s stabilized field is fluctuating along frequency [format_frequency(frequency)], code [code].</span>")
|
||||
..()
|
||||
|
||||
//Anomaly cores
|
||||
/obj/item/assembly/signaler/anomaly/pyro
|
||||
name = "\improper pyroclastic anomaly core"
|
||||
desc = "The neutralized core of a pyroclastic anomaly. It feels warm to the touch. It'd probably be valuable for research."
|
||||
icon_state = "pyro core"
|
||||
anomaly_type = /obj/effect/anomaly/pyro
|
||||
|
||||
/obj/item/assembly/signaler/anomaly/grav
|
||||
name = "\improper gravitational anomaly core"
|
||||
desc = "The neutralized core of a gravitational anomaly. It feels much heavier than it looks. It'd probably be valuable for research."
|
||||
icon_state = "grav core"
|
||||
anomaly_type = /obj/effect/anomaly/grav
|
||||
|
||||
/obj/item/assembly/signaler/anomaly/flux
|
||||
name = "\improper flux anomaly core"
|
||||
desc = "The neutralized core of a flux anomaly. Touching it makes your skin tingle. It'd probably be valuable for research."
|
||||
icon_state = "flux core"
|
||||
anomaly_type = /obj/effect/anomaly/flux
|
||||
|
||||
/obj/item/assembly/signaler/anomaly/bluespace
|
||||
name = "\improper bluespace anomaly core"
|
||||
desc = "The neutralized core of a bluespace anomaly. It keeps phasing in and out of view. It'd probably be valuable for research."
|
||||
icon_state = "anomaly core"
|
||||
anomaly_type = /obj/effect/anomaly/bluespace
|
||||
|
||||
/obj/item/assembly/signaler/anomaly/vortex
|
||||
name = "\improper vortex anomaly core"
|
||||
desc = "The neutralized core of a vortex anomaly. It won't sit still, as if some invisible force is acting on it. It'd probably be valuable for research."
|
||||
icon_state = "vortex core"
|
||||
anomaly_type = /obj/effect/anomaly/bhole
|
||||
|
||||
/obj/item/assembly/signaler/cyborg
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
return
|
||||
|
||||
|
||||
/turf/open/hotspot_expose(exposed_temperature, exposed_volume, soh)
|
||||
/turf/open/hotspot_expose(exposed_temperature, exposed_volume, soh = FALSE, holo = FALSE)
|
||||
var/datum/gas_mixture/air_contents = return_air()
|
||||
if(!air_contents)
|
||||
return 0
|
||||
@@ -35,7 +35,7 @@
|
||||
if(oxy < 0.5)
|
||||
return 0
|
||||
|
||||
active_hotspot = new /obj/effect/hotspot(src)
|
||||
active_hotspot = new /obj/effect/hotspot(src, holo)
|
||||
active_hotspot.temperature = exposed_temperature*50
|
||||
active_hotspot.volume = exposed_volume*25
|
||||
|
||||
@@ -67,8 +67,10 @@
|
||||
var/bypassing = FALSE
|
||||
var/visual_update_tick = 0
|
||||
|
||||
/obj/effect/hotspot/Initialize()
|
||||
/obj/effect/hotspot/Initialize(mapload, holo = FALSE)
|
||||
. = ..()
|
||||
if(holo)
|
||||
flags_1 |= HOLOGRAM_1
|
||||
SSair.hotspots += src
|
||||
perform_exposure()
|
||||
setDir(pick(GLOB.cardinals))
|
||||
@@ -192,7 +194,8 @@
|
||||
|
||||
if(bypassing)
|
||||
icon_state = "3"
|
||||
location.burn_tile()
|
||||
if(!(flags_1 & HOLOGRAM_1))
|
||||
location.burn_tile()
|
||||
|
||||
//Possible spread due to radiated heat
|
||||
if(location.air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_SPREAD)
|
||||
@@ -200,7 +203,7 @@
|
||||
for(var/t in location.atmos_adjacent_turfs)
|
||||
var/turf/open/T = t
|
||||
if(!T.active_hotspot)
|
||||
T.hotspot_expose(radiated_temperature, CELL_VOLUME/4)
|
||||
T.hotspot_expose(radiated_temperature, CELL_VOLUME/4, flags_1 & HOLOGRAM_1)
|
||||
|
||||
else
|
||||
if(volume > CELL_VOLUME*0.4)
|
||||
@@ -224,7 +227,8 @@
|
||||
var/turf/open/T = loc
|
||||
if(istype(T) && T.active_hotspot == src)
|
||||
T.active_hotspot = null
|
||||
DestroyTurf()
|
||||
if(!(flags_1 & HOLOGRAM_1))
|
||||
DestroyTurf()
|
||||
return ..()
|
||||
|
||||
/obj/effect/hotspot/proc/DestroyTurf()
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
temperature_archived = temperature
|
||||
|
||||
/turf/open/archive()
|
||||
ARCHIVE_TEMPERATURE(air)
|
||||
ARCHIVE(air)
|
||||
archived_cycle = SSair.times_fired
|
||||
temperature_archived = temperature
|
||||
|
||||
@@ -215,7 +215,7 @@
|
||||
if (planet_atmos) //share our air with the "atmosphere" "above" the turf
|
||||
var/datum/gas_mixture/G = new
|
||||
G.copy_from_turf(src)
|
||||
ARCHIVE_TEMPERATURE(G)
|
||||
ARCHIVE(G)
|
||||
if(our_air.compare(G))
|
||||
if(!our_excited_group)
|
||||
var/datum/excited_group/EG = new
|
||||
|
||||
@@ -17,6 +17,7 @@ GLOBAL_LIST_INIT(meta_gas_ids, meta_gas_id_list())
|
||||
GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
/datum/gas_mixture
|
||||
var/list/gases = list()
|
||||
var/list/gas_archive = list()
|
||||
var/temperature = 0 //kelvins
|
||||
var/tmp/temperature_archived = 0
|
||||
var/volume = CELL_VOLUME //liters
|
||||
@@ -31,13 +32,25 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
|
||||
//PV = nRT
|
||||
|
||||
/datum/gas_mixture/proc/heat_capacity() //joules per kelvin
|
||||
/datum/gas_mixture/proc/heat_capacity()
|
||||
|
||||
/datum/gas_mixture/proc/archived_heat_capacity()
|
||||
|
||||
/datum/gas_mixture/heat_capacity() //joules per kelvin
|
||||
var/list/cached_gases = gases
|
||||
var/list/cached_gasheats = GLOB.meta_gas_specific_heats
|
||||
. = 0
|
||||
for(var/id in cached_gases)
|
||||
. += cached_gases[id] * cached_gasheats[id]
|
||||
|
||||
/datum/gas_mixture/archived_heat_capacity()
|
||||
// lots of copypasta but heat_capacity is the single proc called the most in a regular round, bar none, so performance loss adds up
|
||||
var/list/cached_gases = gas_archive
|
||||
var/list/cached_gasheats = GLOB.meta_gas_specific_heats
|
||||
. = 0
|
||||
for(var/id in cached_gases)
|
||||
. += cached_gases[id] * cached_gasheats[id]
|
||||
|
||||
/datum/gas_mixture/turf/heat_capacity() // Same as above except vacuums return HEAT_CAPACITY_VACUUM
|
||||
var/list/cached_gases = gases
|
||||
var/list/cached_gasheats = GLOB.meta_gas_specific_heats
|
||||
@@ -46,6 +59,14 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
if(!.)
|
||||
. += HEAT_CAPACITY_VACUUM //we want vacuums in turfs to have the same heat capacity as space
|
||||
|
||||
/datum/gas_mixture/turf/archived_heat_capacity() // Same as above except vacuums return HEAT_CAPACITY_VACUUM
|
||||
var/list/cached_gases = gas_archive
|
||||
var/list/cached_gasheats = GLOB.meta_gas_specific_heats
|
||||
for(var/id in cached_gases)
|
||||
. += cached_gases[id] * cached_gasheats[id]
|
||||
if(!.)
|
||||
. += HEAT_CAPACITY_VACUUM //we want vacuums in turfs to have the same heat capacity as space
|
||||
|
||||
/datum/gas_mixture/proc/total_moles()
|
||||
var/cached_gases = gases
|
||||
TOTAL_MOLES(cached_gases, .)
|
||||
@@ -67,6 +88,10 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
/datum/gas_mixture/proc/thermal_energy() //joules
|
||||
return THERMAL_ENERGY(src) //see code/__DEFINES/atmospherics.dm; use the define in performance critical areas
|
||||
|
||||
/datum/gas_mixture/proc/archive()
|
||||
//Update archived versions of variables
|
||||
//Returns: 1 in all cases
|
||||
|
||||
/datum/gas_mixture/proc/merge(datum/gas_mixture/giver)
|
||||
//Merges all air from giver into self. Deletes giver.
|
||||
//Returns: 1 if we are mutable, 0 otherwise
|
||||
@@ -111,6 +136,10 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
//Performs various reactions such as combustion or fusion (LOL)
|
||||
//Returns: 1 if any reaction took place; 0 otherwise
|
||||
|
||||
/datum/gas_mixture/archive()
|
||||
temperature_archived = temperature
|
||||
gas_archive = gases.Copy()
|
||||
return 1
|
||||
|
||||
/datum/gas_mixture/merge(datum/gas_mixture/giver)
|
||||
if(!giver)
|
||||
@@ -245,7 +274,7 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
//GAS TRANSFER
|
||||
for(var/id in cached_gases | sharer_gases) // transfer gases
|
||||
|
||||
delta = QUANTIZE(cached_gases[id] - sharer_gases[id])/(atmos_adjacent_turfs+1) //the amount of gas that gets moved between the mixtures
|
||||
delta = QUANTIZE(gas_archive[id] - sharer.gas_archive[id])/(atmos_adjacent_turfs+1) //the amount of gas that gets moved between the mixtures
|
||||
|
||||
if(delta && abs_temperature_delta > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
|
||||
gas_heat_capacity = delta * cached_gasheats[id]
|
||||
@@ -293,8 +322,8 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
sharer_temperature = sharer.temperature_archived
|
||||
var/temperature_delta = temperature_archived - sharer_temperature
|
||||
if(abs(temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
|
||||
var/self_heat_capacity = heat_capacity()
|
||||
sharer_heat_capacity = sharer_heat_capacity || sharer.heat_capacity()
|
||||
var/self_heat_capacity = archived_heat_capacity()
|
||||
sharer_heat_capacity = sharer_heat_capacity || sharer.archived_heat_capacity()
|
||||
|
||||
if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
|
||||
var/heat = conduction_coefficient*temperature_delta* \
|
||||
|
||||
@@ -192,6 +192,7 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(/datum/gas/oxygen, /datum/g
|
||||
anchored = TRUE // should only appear in vis_contents, but to be safe
|
||||
layer = FLY_LAYER
|
||||
appearance_flags = TILE_BOUND
|
||||
vis_flags = NONE
|
||||
|
||||
/obj/effect/overlay/gas/New(state, alph)
|
||||
. = ..()
|
||||
|
||||
@@ -282,7 +282,7 @@
|
||||
|
||||
var/new_heat_capacity = air.heat_capacity()
|
||||
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
|
||||
air.temperature = CLAMP(((air.temperature*old_heat_capacity + reaction_energy)/new_heat_capacity),TCMB,INFINITY)
|
||||
air.temperature = clamp(((air.temperature*old_heat_capacity + reaction_energy)/new_heat_capacity),TCMB,INFINITY)
|
||||
return REACTING
|
||||
|
||||
/datum/gas_reaction/nitrylformation //The formation of nitryl. Endothermic. Requires N2O as a catalyst.
|
||||
|
||||
@@ -786,9 +786,8 @@
|
||||
return
|
||||
user.visible_message("[user.name] wires the air alarm.", \
|
||||
"<span class='notice'>You start wiring the air alarm...</span>")
|
||||
if (do_after(user, 20, target = src))
|
||||
if (cable.get_amount() >= 5 && buildstage == 1)
|
||||
cable.use(5)
|
||||
if (W.use_tool(src, user, 20, 5))
|
||||
if (buildstage == 1)
|
||||
to_chat(user, "<span class='notice'>You wire the air alarm.</span>")
|
||||
wires.repair()
|
||||
aidisabled = 0
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
nullifyNode(i)
|
||||
|
||||
SSair.atmos_machinery -= src
|
||||
SSair.pipenets_needing_rebuilt -= src
|
||||
|
||||
dropContents()
|
||||
if(pipe_vision_img)
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
if(node2)
|
||||
node2.atmosinit()
|
||||
node2.addMember(src)
|
||||
build_network()
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -159,13 +159,13 @@
|
||||
pump_direction = 1
|
||||
|
||||
if("set_input_pressure" in signal.data)
|
||||
input_pressure_min = CLAMP(text2num(signal.data["set_input_pressure"]),0,ONE_ATMOSPHERE*50)
|
||||
input_pressure_min = clamp(text2num(signal.data["set_input_pressure"]),0,ONE_ATMOSPHERE*50)
|
||||
|
||||
if("set_output_pressure" in signal.data)
|
||||
output_pressure_max = CLAMP(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50)
|
||||
output_pressure_max = clamp(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50)
|
||||
|
||||
if("set_external_pressure" in signal.data)
|
||||
external_pressure_bound = CLAMP(text2num(signal.data["set_external_pressure"]),0,ONE_ATMOSPHERE*50)
|
||||
external_pressure_bound = clamp(text2num(signal.data["set_external_pressure"]),0,ONE_ATMOSPHERE*50)
|
||||
|
||||
if("status" in signal.data)
|
||||
spawn(2)
|
||||
|
||||
@@ -137,7 +137,7 @@ Passive gate is similar to the regular pump except:
|
||||
pressure = text2num(pressure)
|
||||
. = TRUE
|
||||
if(.)
|
||||
target_pressure = CLAMP(pressure, 0, MAX_OUTPUT_PRESSURE)
|
||||
target_pressure = clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
|
||||
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS)
|
||||
update_icon()
|
||||
|
||||
@@ -159,7 +159,7 @@ Passive gate is similar to the regular pump except:
|
||||
on = !on
|
||||
|
||||
if("set_output_pressure" in signal.data)
|
||||
target_pressure = CLAMP(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50)
|
||||
target_pressure = clamp(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50)
|
||||
|
||||
if(on != old_on)
|
||||
investigate_log("was turned [on ? "on" : "off"] by a remote signal", INVESTIGATE_ATMOS)
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
pressure = text2num(pressure)
|
||||
. = TRUE
|
||||
if(.)
|
||||
target_pressure = CLAMP(pressure, 0, MAX_OUTPUT_PRESSURE)
|
||||
target_pressure = clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
|
||||
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS)
|
||||
update_icon()
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
on = !on
|
||||
|
||||
if("set_output_pressure" in signal.data)
|
||||
target_pressure = CLAMP(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50)
|
||||
target_pressure = clamp(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50)
|
||||
|
||||
if(on != old_on)
|
||||
investigate_log("was turned [on ? "on" : "off"] by a remote signal", INVESTIGATE_ATMOS)
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
pressure = text2num(pressure)
|
||||
. = TRUE
|
||||
if(.)
|
||||
open_pressure = CLAMP(pressure, close_pressure, 50*ONE_ATMOSPHERE)
|
||||
open_pressure = clamp(pressure, close_pressure, 50*ONE_ATMOSPHERE)
|
||||
investigate_log("open pressure was set to [open_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS)
|
||||
if("close_pressure")
|
||||
var/pressure = params["close_pressure"]
|
||||
@@ -106,6 +106,6 @@
|
||||
pressure = text2num(pressure)
|
||||
. = TRUE
|
||||
if(.)
|
||||
close_pressure = CLAMP(pressure, 0, open_pressure)
|
||||
close_pressure = clamp(pressure, 0, open_pressure)
|
||||
investigate_log("close pressure was set to [close_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS)
|
||||
update_icon()
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
rate = text2num(rate)
|
||||
. = TRUE
|
||||
if(.)
|
||||
transfer_rate = CLAMP(rate, 0, MAX_TRANSFER_RATE)
|
||||
transfer_rate = clamp(rate, 0, MAX_TRANSFER_RATE)
|
||||
investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", INVESTIGATE_ATMOS)
|
||||
update_icon()
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
|
||||
if("set_transfer_rate" in signal.data)
|
||||
var/datum/gas_mixture/air1 = airs[1]
|
||||
transfer_rate = CLAMP(text2num(signal.data["set_transfer_rate"]),0,air1.volume)
|
||||
transfer_rate = clamp(text2num(signal.data["set_transfer_rate"]),0,air1.volume)
|
||||
|
||||
if(on != old_on)
|
||||
investigate_log("was turned [on ? "on" : "off"] by a remote signal", INVESTIGATE_ATMOS)
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
var/datum/pipeline/parent = parents[i]
|
||||
if(!parent)
|
||||
stack_trace("Component is missing a pipenet! Rebuilding...")
|
||||
build_network()
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
parent.update = 1
|
||||
|
||||
/obj/machinery/atmospherics/components/returnPipenets()
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
rate = text2num(rate)
|
||||
. = TRUE
|
||||
if(.)
|
||||
transfer_rate = CLAMP(rate, 0, MAX_TRANSFER_RATE)
|
||||
transfer_rate = clamp(rate, 0, MAX_TRANSFER_RATE)
|
||||
investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", INVESTIGATE_ATMOS)
|
||||
if("filter")
|
||||
filter_type = null
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
pressure = text2num(pressure)
|
||||
. = TRUE
|
||||
if(.)
|
||||
target_pressure = CLAMP(pressure, 0, MAX_OUTPUT_PRESSURE)
|
||||
target_pressure = clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
|
||||
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS)
|
||||
if("node1")
|
||||
var/value = text2num(params["concentration"])
|
||||
|
||||
@@ -447,6 +447,6 @@
|
||||
if(node)
|
||||
node.atmosinit()
|
||||
node.addMember(src)
|
||||
build_network()
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
|
||||
#undef CRYOMOBS
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
if("set_volume_rate" in signal.data)
|
||||
var/number = text2num(signal.data["set_volume_rate"])
|
||||
var/datum/gas_mixture/air_contents = airs[1]
|
||||
volume_rate = CLAMP(number, 0, air_contents.volume)
|
||||
volume_rate = clamp(number, 0, air_contents.volume)
|
||||
|
||||
if("status" in signal.data)
|
||||
spawn(2)
|
||||
@@ -187,7 +187,7 @@
|
||||
rate = text2num(rate)
|
||||
. = TRUE
|
||||
if(.)
|
||||
volume_rate = CLAMP(rate, 0, MAX_TRANSFER_RATE)
|
||||
volume_rate = clamp(rate, 0, MAX_TRANSFER_RATE)
|
||||
investigate_log("was set to [volume_rate] L/s by [key_name(usr)]", INVESTIGATE_ATMOS)
|
||||
update_icon()
|
||||
broadcast_status()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user