Merge remote-tracking branch 'origin/master' into rustsql

This commit is contained in:
Letter N
2021-02-19 12:38:32 +08:00
737 changed files with 8763 additions and 3732 deletions
+3 -3
View File
@@ -174,7 +174,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//is_bwoink is TRUE if this ticket was started by an admin PM
/datum/admin_help/New(msg, client/C, is_bwoink)
//clean the input msg
msg = sanitize(copytext_char(msg,1,MAX_MESSAGE_LEN))
msg = copytext_char(msg,1,MAX_MESSAGE_LEN)
if(!msg || !C || !C.mob)
qdel(src)
return
@@ -263,7 +263,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//message from the initiator without a target, all admins will see this
//won't bug irc
/datum/admin_help/proc/MessageNoRecipient(msg)
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
msg = copytext_char(msg, 1, MAX_MESSAGE_LEN)
var/ref_src = "[REF(src)]"
//Message to be sent to all admins
var/admin_msg = "<span class='adminnotice'><span class='adminhelp'>Ticket [TicketHref("#[id]", ref_src)]</span><b>: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]:</b> <span class='linkify'>[keywords_lookup(msg)]</span></span>"
@@ -523,7 +523,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
if(handle_spam_prevention(msg,MUTE_ADMINHELP))
return
msg = trim(msg)
msg = sanitize(trim(msg))
if(!msg)
return
+7 -3
View File
@@ -113,7 +113,6 @@
to_chat(src, "<span class='danger'>Error: Use the admin IRC/Discord channel, nerd.</span>", confidential = TRUE)
return
else
//get message text, limit it's length.and clean/escape html
if(!msg)
@@ -128,11 +127,16 @@
else
if(holder)
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
to_chat(src, "<span class='danger'><b>Message not sent:</b></span><br>[msg]", confidential = TRUE)
to_chat(src, "<span class='danger'><b>Message not sent:</b></span><br>[sanitize(msg)]", confidential = TRUE)
if(recipient_ticket)
recipient_ticket.AddInteraction("<b>No client found, message not sent:</b><br>[msg]")
return
else
//clean the message if it's not sent by a high-rank admin
if(!check_rights(R_SERVER|R_DEBUG,0)||external)//no sending html to the poor bots
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
if(!msg)
return
current_ticket.MessageNoRecipient(msg)
return
@@ -141,7 +145,7 @@
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
return
if (src.handle_spam_prevention(msg,MUTE_ADMINHELP))
if(src.handle_spam_prevention(msg,MUTE_ADMINHELP))
return
//clean the message if it's not sent by a high-rank admin
@@ -9,4 +9,4 @@
/obj/item/organ/heart/gland/ventcrawling/activate()
to_chat(owner, "<span class='notice'>You feel very stretchy.</span>")
owner.ventcrawler = VENTCRAWLER_ALWAYS
owner.AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
@@ -277,3 +277,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
var/datum/antagonist/blob/B = mind.has_antag_datum(/datum/antagonist/blob)
if(!B)
mind.add_antag_datum(/datum/antagonist/blob)
//the same but it's forced to be allowed by default as cameras usually don't allow emoting
/mob/camera/blob/emote(act, m_type=1, message = null, intentional = FALSE, forced = TRUE)
. = ..()
@@ -149,8 +149,6 @@
if(prob(50))
if(LAZYLEN(active_ais()) && prob(100/GLOB.joined_player_list.len))
add_objective(new/datum/objective/destroy, TRUE)
else if(prob(30))
add_objective(new/datum/objective/maroon, TRUE)
else
add_objective(new/datum/objective/assassinate, TRUE)
else
@@ -441,19 +441,20 @@
else
kill_objective.find_target()
objectives += kill_objective
else
/*else
var/datum/objective/maroon/maroon_objective = new
maroon_objective.owner = owner
if(team_mode)
maroon_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
else
maroon_objective.find_target()
objectives += maroon_objective
objectives += maroon_objective*/
if (!(locate(/datum/objective/escape) in objectives) && escape_objective_possible)
var/datum/objective/escape/escape_with_identity/identity_theft = new
identity_theft.owner = owner
identity_theft.target = maroon_objective.target
identity_theft.target = kill_objective.target
identity_theft.update_explanation_text()
objectives += identity_theft
escape_objective_possible = FALSE
@@ -165,14 +165,16 @@
/datum/clockwork_rite/treat_wounds/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
if(!target)
return FALSE
if(!target.all_wounds.len)
if(!target.all_wounds || !target.all_wounds.len)
to_chat(invoker, "<span class='inathneq_small'>This one does not require mending.</span>")
return FALSE
.= ..()
if(!.)
return FALSE
target.adjustToxLoss(10 * target.all_wounds.len)
QDEL_LIST(target.all_wounds)
for(var/i in target.all_wounds)
var/datum/wound/mended = i
mended.remove_wound()
to_chat(target, "<span class='warning'>You feel your wounds heal, but are overcome with deep nausea.</span>")
new /obj/effect/temp_visual/ratvar/sigil/vitality(T)
@@ -22,7 +22,7 @@
. = ..()
desc = initial(desc)
obj/item/shield/riot/ratvarian/proc/calc_bash_mult()
/obj/item/shield/riot/ratvarian/proc/calc_bash_mult()
var/bash_mult = 0
if(!dam_absorbed)
return 1
@@ -203,10 +203,10 @@
to_chat(user, "<span class='warning'>You need to hold the slab in your active hand to recite scripture!</span>")
return FALSE
var/initial_tier = initial(scripture.tier)
if(initial_tier == SCRIPTURE_PERIPHERAL)
if(initial_tier == SCRIPTURE_PERIPHERAL && !issilicon(user)) //Silicons use peripheral scripture & cannot open the slab.
to_chat(user, "<span class='warning'>Nice try using href exploits</span>")
return
if(!GLOB.ratvar_awakens && !no_cost && !SSticker.scripture_states[initial_tier])
if(!GLOB.ratvar_awakens && !no_cost && !SSticker.scripture_states[initial_tier] &&!issilicon(user)) //silicons can't choose their spells, so lets allow them to always cast their assigned ones.
to_chat(user, "<span class='warning'>That scripture is not unlocked, and cannot be recited!</span>")
return FALSE
var/datum/clockwork_scripture/scripture_to_recite = new scripture
@@ -8,7 +8,7 @@
descname = "Powers Nearby Structures"
name = "Sigil of Transmission"
desc = "Places a sigil that can drain and will store energy to power clockwork structures."
invocations = list("Divinity...", "...power our creations!")
invocations = list("Divinity...", "...power our creations.")
channel_time = 70
power_cost = 200
whispered = TRUE
@@ -28,7 +28,7 @@
descname = "Powered Structure, Delay Emergency Shuttles"
name = "Prolonging Prism"
desc = "Creates a mechanized prism which will delay the arrival of an emergency shuttle by 2 minutes at a massive power cost."
invocations = list("May this prism...", "...grant us time to enact his will!")
invocations = list("May this prism...", "...grant us time to enact his will.")
channel_time = 80
power_cost = 300
object_path = /obj/structure/destructible/clockwork/powered/prolonging_prism
@@ -60,7 +60,7 @@
descname = "Powered Structure, Area Denial"
name = "Mania Motor"
desc = "Creates a mania motor which causes minor damage and a variety of negative mental effects in nearby non-Servant humans, potentially up to and including conversion."
invocations = list("May this transmitter...", "...break the will of all who oppose us!")
invocations = list("May this transmitter...", "...break the will of all who oppose us.")
channel_time = 80
power_cost = 750
object_path = /obj/structure/destructible/clockwork/powered/mania_motor
@@ -83,7 +83,7 @@
descname = "Powered Structure, Teleportation Hub"
name = "Clockwork Obelisk"
desc = "Creates a clockwork obelisk that can broadcast messages over the Hierophant Network or open a Spatial Gateway to any living Servant or clockwork obelisk."
invocations = list("May this obelisk...", "...take us to all places!")
invocations = list("May this obelisk...", "...take us to all places.")
channel_time = 80
power_cost = 300
object_path = /obj/structure/destructible/clockwork/powered/clockwork_obelisk
@@ -163,7 +163,7 @@
descname = "Well-Rounded Combat Construct"
name = "Clockwork Marauder"
desc = "Creates a shell for a clockwork marauder, a balanced frontline construct that can deflect projectiles with its shield."
invocations = list("Arise, avatar of Arbiter!", "Defend the Ark with vengeful zeal.")
invocations = list("Arise, avatar of Arbiter!", "Defend the Ark with vengeful zeal!")
channel_time = 80
power_cost = 8000
creator_message = "<span class='brass'>Your slab disgorges several chunks of replicant alloy that form into a suit of thrumming armor.</span>"
@@ -7,7 +7,7 @@
descname = "Generates Power From Starlight"
name = "Stargazer"
desc = "Forms a weak structure that generates power every second while within three tiles of starlight."
invocations = list("Capture their inferior light for us!")
invocations = list("Capture their inferior light for us.")
channel_time = 50
power_cost = 200
object_path = /obj/structure/destructible/clockwork/stargazer
@@ -16,6 +16,7 @@
usage_tip = "For obvious reasons, make sure to place this near a window or somewhere else that can see space!"
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
whispered = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 1
quickbind = TRUE
@@ -34,7 +35,7 @@
descname = "Power Generation"
name = "Integration Cog"
desc = "Fabricates an integration cog, which can be used on an open APC to replace its innards and passively siphon its power."
invocations = list("Take that which sustains them!")
invocations = list("Take that which sustains them.")
channel_time = 10
power_cost = 10
whispered = TRUE
@@ -55,7 +56,7 @@
descname = "Trap, Stunning"
name = "Sigil of Transgression"
desc = "Wards a tile with a sigil, which will briefly stun the next non-Servant to cross it and apply Belligerent to them."
invocations = list("Divinity, smite...", "...those who trespass here!")
invocations = list("Divinity, smite...", "...those who trespass here.")
channel_time = 50
power_cost = 50
whispered = TRUE
@@ -75,7 +76,7 @@
descname = "Trap, Conversion"
name = "Sigil of Submission"
desc = "Places a luminous sigil that will convert any non-Servants that remain on it for 8 seconds."
invocations = list("Divinity, enlighten...", "...those who trespass here!")
invocations = list("Divinity, enlighten...", "...those who trespass here.")
channel_time = 60
power_cost = 125
whispered = TRUE
@@ -95,7 +96,7 @@
descname = "Short-Range Single-Target Stun"
name = "Kindle"
desc = "Charges your slab with divine energy, allowing you to overwhelm a target with Ratvar's light."
invocations = list("Divinity, show them your light!")
invocations = list("Divinity, show them your light.")
whispered = TRUE
channel_time = 25 //2.5 seconds should be a okay compromise between being able to use it when needed, and not being able to just pause in combat for a second and hardstunning your enemy
power_cost = 125
@@ -118,7 +119,7 @@
descname = "Handcuffs"
name = "Hateful Manacles"
desc = "Forms replicant manacles around a target's wrists that function like handcuffs."
invocations = list("Shackle the heretic!", "Break them in body and spirit!")
invocations = list("Shackle the heretic!", "Break them in body and spirit.")
channel_time = 15
power_cost = 25
whispered = TRUE
@@ -269,7 +270,7 @@
descname = "New Clockwork Slab"
name = "Replicant"
desc = "Creates a new clockwork slab."
invocations = list("Metal, become greater!")
invocations = list("Metal, become greater.")
channel_time = 10
power_cost = 25
whispered = TRUE
@@ -290,7 +291,7 @@
descname = "Limited Xray Vision Glasses"
name = "Wraith Spectacles"
desc = "Fabricates a pair of glasses which grant true sight but cause gradual vision loss."
invocations = list("Show the truth of this world to me!")
invocations = list("Show the truth of this world to me.")
channel_time = 10
power_cost = 50
whispered = TRUE
@@ -310,7 +311,7 @@
name = "Spatial Gateway"
desc = "Tears open a miniaturized gateway in spacetime to any conscious servant that can transport objects or creatures to its destination. \
Each servant assisting in the invocation adds one additional use and four additional seconds to the gateway's uses and duration."
invocations = list("Spatial Gateway...", "...activate!")
invocations = list("Spatial Gateway...", "...activate.")
channel_time = 30
power_cost = 400
whispered = TRUE
@@ -29,7 +29,7 @@
descname = "Structure, Turret"
name = "Ocular Warden"
desc = "Forms an automatic short-range turret which will automatically attack nearby unrestrained non-Servants that can see it."
invocations = list("Guardians of Engine...", "...judge those who would harm us!")
invocations = list("Guardians of Engine...", "...judge those who would harm us.")
channel_time = 100
power_cost = 250
object_path = /obj/structure/destructible/clockwork/ocular_warden
@@ -105,7 +105,7 @@
descname = "Delayed Area Knockdown Glasses"
name = "Judicial Visor"
desc = "Creates a visor that can smite an area, applying Belligerent and briefly stunning. The smote area will explode after 3 seconds."
invocations = list("Grant me the flames of Engine!")
invocations = list("Grant me the flames of Engine.")
channel_time = 10
power_cost = 400
whispered = TRUE
@@ -124,7 +124,7 @@
descname = "Shield with empowerable bashes"
name = "Nezbere's shield"
desc = "Creates a shield which generates charge from blocking damage, using it to empower its bashes tremendously. It is repaired with brass, and while very durable, extremely weak to lasers and, even more so, to energy weaponry."
invocations = list("Shield me...", "... from the coming dark!")
invocations = list("Shield me...", "... from the coming dark.")
channel_time = 20
power_cost = 600 //Shouldn't be too spammable but not too hard to get either
whispered = TRUE
@@ -143,7 +143,7 @@
descname = "Summonable Armor and Weapons"
name = "Clockwork Armaments"
desc = "Allows the invoker to summon clockwork armor and a Ratvarian spear at will. The spear's attacks will generate Vitality, used for healing."
invocations = list("Grant me armaments...", "...from the forge of Armorer!")
invocations = list("Grant me armaments...", "...from the forge of Armorer.")
channel_time = 20
power_cost = 250
whispered = TRUE
@@ -149,7 +149,7 @@
transform = matrix() * 2
animate(src, transform = matrix() * 0.5, time = 30, flags = ANIMATION_END_NOW)
obj/structure/destructible/clockwork/massive/celestial_gateway/Destroy()
/obj/structure/destructible/clockwork/massive/celestial_gateway/Destroy()
STOP_PROCESSING(SSprocessing, src)
if(!purpose_fulfilled)
var/area/gate_area = get_area(src)
@@ -15,13 +15,16 @@
var/ignore_holy_water = FALSE
/datum/antagonist/clockcult/silent
name = "Silent Clock Cultist"
silent = TRUE
show_in_antagpanel = FALSE //internal
/datum/antagonist/clockcult/neutered
name = "Neutered Clock Cultist"
neutered = TRUE
/datum/antagonist/clockcult/neutered/traitor
name = "Traitor Clock Cultist"
ignore_eligibility_check = TRUE
ignore_holy_water = TRUE
show_in_roundend = FALSE
@@ -185,7 +188,7 @@
/datum/antagonist/clockcult/admin_add(datum/mind/new_owner,mob/admin)
add_servant_of_ratvar(new_owner.current, TRUE)
add_servant_of_ratvar(new_owner.current, TRUE, override_type = type)
message_admins("[key_name_admin(admin)] has made [new_owner.current] into a servant of Ratvar.")
log_admin("[key_name(admin)] has made [new_owner.current] into a servant of Ratvar.")
+2
View File
@@ -19,9 +19,11 @@
var/ignore_holy_water = FALSE
/datum/antagonist/cult/neutered
name = "Neutered Cultist"
neutered = TRUE
/datum/antagonist/cult/neutered/traitor
name = "Traitor Cultist"
ignore_eligibility_checks = TRUE
ignore_holy_water = TRUE
show_in_roundend = FALSE
@@ -11,7 +11,6 @@
gender = NEUTER
health = 350
maxHealth = 350
ventcrawler = VENTCRAWLER_NONE
density = TRUE
pass_flags = 0
sight = (SEE_TURFS | SEE_OBJS)
@@ -5,6 +5,7 @@
viable_mobtypes = list(/mob/living/carbon/human)
mutable = FALSE
var/mob/camera/disease/overmind
infectable_biotypes = MOB_ORGANIC|MOB_ROBOTIC
/datum/disease/advance/sentient_disease/New()
..()
@@ -10,6 +10,8 @@
var/give_equipment = TRUE
var/list/researched_knowledge = list()
var/total_sacrifices = 0
var/list/sac_targetted = list() //Which targets did living hearts give them, but they did not sac?
var/list/actually_sacced = list() //Which targets did they actually sac?
var/ascended = FALSE
/datum/antagonist/heretic/admin_add(datum/mind/new_owner,mob/admin)
@@ -175,6 +177,17 @@
knowledge_message += "[EK.name]"
parts += knowledge_message.Join(", ")
parts += "<b>Targets assigned by living hearts, but not sacrificed:</b>"
if(!sac_targetted.len)
parts += "None."
else
parts += sac_targetted.Join(",")
parts += "<b>Sacrifices performed:</b>"
if(!actually_sacced.len)
parts += "<span class='redtext'>None!</span>"
else
parts += actually_sacced.Join(",")
return parts.Join("<br>")
////////////////
// Knowledge //
@@ -213,6 +226,23 @@
if(ascended)
. += 20
/datum/antagonist/heretic/antag_panel()
var/list/parts = list()
parts += ..()
parts += "<b>Targets currently assigned by living hearts (Can give a false negative if they stole someone elses living heart):</b>"
if(!sac_targetted.len)
parts += "None."
else
parts += sac_targetted.Join(",")
parts += "<b>Targets actually sacrificed:</b>"
if(!actually_sacced.len)
parts += "None."
else
parts += actually_sacced.Join(",")
return (parts.Join("<br>") + "<br>")
////////////////
// Objectives //
////////////////
@@ -254,6 +254,7 @@
to_chat(human_user,"<span class='userdanger'>Your brain hurts when you look at this!</span>")
human_user.adjustOrganLoss(ORGAN_SLOT_BRAIN,20,190)
SEND_SIGNAL(human_user, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
log_game("[key_name(user)] stared at a pierced reality at [AREACOORD(user)]")
/obj/effect/reality_smash
name = "/improper reality smash"
@@ -6,6 +6,17 @@
w_class = WEIGHT_CLASS_SMALL
///Target
var/mob/living/carbon/human/target
var/datum/antagonist/heretic/sac_targetter //The heretic who used this to acquire the current target - gets cleared when target gets sacrificed.
/obj/item/living_heart/Initialize()
. = ..()
GLOB.living_heart_cache.Add(src) //Add is better than +=.
/obj/item/living_heart/Destroy()
GLOB.living_heart_cache.Remove(src)
if(sac_targetter && target)
sac_targetter.sac_targetted.Remove(target.real_name)
return ..()
/obj/item/living_heart/attack_self(mob/user)
. = ..()
@@ -1,6 +1,6 @@
/**
* #Eldritch Knwoledge
* #Eldritch Knowledge
*
* Datum that makes eldritch cultist interesting.
*
@@ -252,6 +252,10 @@
LH.target = null
var/datum/antagonist/heretic/EC = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
EC.actually_sacced.Add(H.real_name)
if(LH.sac_targetter)
LH.sac_targetter.sac_targetted.Remove(H.real_name)
LH.sac_targetter = null
EC.total_sacrifices++
for(var/X in carbon_user.get_all_gear())
if(!istype(X,/obj/item/forbidden_book))
@@ -265,8 +269,14 @@
var/datum/objective/A = new
A.owner = user.mind
var/list/targets = list()
var/list/target_blacklist = list()
for(var/obj/item/living_heart/CLH in GLOB.living_heart_cache)
if(!CLH || !CLH.target || !CLH.target.mind)
continue
target_blacklist.Add(CLH.target.mind)
for(var/i in 0 to 3)
var/datum/mind/targeted = A.find_target()//easy way, i dont feel like copy pasting that entire block of code
var/datum/mind/targeted = A.find_target(blacklist = target_blacklist)//easy way, i dont feel like copy pasting that entire block of code
if(!targeted)
break
targets[targeted.current.real_name] = targeted.current
@@ -274,9 +284,24 @@
if(!LH.target && targets.len)
LH.target = pick(targets) //Tsk tsk, you can and will get another target if you want it or not.
if(LH.target)
target_blacklist = list()
for(var/obj/item/living_heart/CLH in (GLOB.living_heart_cache - LH)) //Recreate blacklist, excluding ourselves.
if(!CLH || !CLH.target || !CLH.target.mind)
continue
target_blacklist.Add(CLH.target.mind)
if(LH.target.mind in target_blacklist) //Someone was faster, or you tried to cheese the system.
to_chat(user, "<span class='warning'>It seems you were too slow, and your target of choice has already been selected by another living heart!</span>")
LH.target = null
qdel(A)
if(LH.target)
to_chat(user,"<span class='warning'>Your new target has been selected, go and sacrifice [LH.target.real_name]!</span>")
var/datum/antagonist/heretic/EC = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
LH.sac_targetter = EC
EC.sac_targetted.Add(LH.target.real_name)
else
to_chat(user,"<span class='warning'>target could not be found for living heart.</span>")
+4 -1
View File
@@ -15,7 +15,6 @@
stop_automated_movement = 1
status_flags = CANPUSH
pass_flags = PASSTABLE
ventcrawler = VENTCRAWLER_ALWAYS
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxHealth = 150
@@ -53,6 +52,10 @@
You can attack any item or dead creature to consume it - creatures will fully restore your health. \
Finally, you can restore yourself to your original form while morphed by shift-clicking yourself.</b>"
/mob/living/simple_animal/hostile/morph/Initialize()
. = ..()
src.AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
/mob/living/simple_animal/hostile/morph/examine(mob/user)
if(morphed)
. = form.examine(user)
+49 -7
View File
@@ -72,6 +72,7 @@
var/list/drained_mobs = list() //Cannot harvest the same mob twice
var/perfectsouls = 0 //How many perfect, regen-cap increasing souls the revenant has. //TODO, add objective for getting a perfect soul(s?)
var/generated_objectives_and_spells = FALSE
var/telekinesis_cooldown
/mob/living/simple_animal/revenant/Initialize(mapload)
. = ..()
@@ -93,13 +94,16 @@
/mob/living/simple_animal/revenant/Login()
..()
to_chat(src, "<span class='deadsay'><span class='big bold'>You are a revenant.</span></span>")
to_chat(src, "<b>Your formerly mundane spirit has been infused with alien energies and empowered into a revenant.</b>")
to_chat(src, "<b>You are not dead, not alive, but somewhere in between. You are capable of limited interaction with both worlds.</b>")
to_chat(src, "<b>You are invincible and invisible to everyone but other ghosts. Most abilities will reveal you, rendering you vulnerable.</b>")
to_chat(src, "<b>To function, you are to drain the life essence from humans. This essence is a resource, as well as your health, and will power all of your abilities.</b>")
to_chat(src, "<b><i>You do not remember anything of your past lives, nor will you remember anything about this one after your death.</i></b>")
to_chat(src, "<b>Be sure to read <a href=\"https://tgstation13.org/wiki/Revenant\">the wiki page</a> to learn more.</b>")
var/revenant_greet
revenant_greet += "<span class='deadsay'><span class='big bold'>You are a revenant.</span></span>"
revenant_greet += "<b>Your formerly mundane spirit has been infused with alien energies and empowered into a revenant.</b>"
revenant_greet += "<b>You are not dead, not alive, but somewhere in between. You are capable of limited interaction with both worlds.</b>"
revenant_greet += "<b>You are invincible and invisible to everyone but other ghosts. Most abilities will reveal you, rendering you vulnerable.</b>"
revenant_greet += "<b>To function, you are to drain the life essence from humans. This essence is a resource, as well as your health, and will power all of your abilities.</b>"
revenant_greet += "<b><i>You do not remember anything of your past lives, nor will you remember anything about this one after your death.</i></b>"
revenant_greet += "<b>Be sure to read <a href=\"https://tgstation13.org/wiki/Revenant\">the wiki page</a> to learn more.</b>"
revenant_greet += "<b>You are also able to telekinetically throw objects by clickdragging them.</b>"
to_chat(src, revenant_greet)
if(!generated_objectives_and_spells)
generated_objectives_and_spells = TRUE
mind.assigned_role = ROLE_REVENANT
@@ -317,6 +321,12 @@
to_chat(src, "<span class='revenminor'>Lost [essence_amt]E[source ? " from [source]":""].</span>")
return 1
/mob/living/simple_animal/revenant/proc/telekinesis_cooldown_end()
if(!telekinesis_cooldown)
CRASH("telekinesis_cooldown_end ran when telekinesis_cooldown on [src] was false")
else
telekinesis_cooldown = FALSE
/mob/living/simple_animal/revenant/proc/death_reset()
revealed = FALSE
unreveal_time = 0
@@ -431,6 +441,38 @@
qdel(revenant)
..()
/proc/RevenantThrow(over, mob/user, obj/item/throwable)
var/mob/living/simple_animal/revenant/spooker = user
if(!istype(throwable))
return
if(!throwable.anchored && !spooker.telekinesis_cooldown && spooker.essence > 20)
if(7 < get_dist(throwable, spooker))
return
if(3 >= get_dist(throwable, spooker))
spooker.stun(10)
spooker.reveal(25)
else
spooker.stun(20)
spooker.reveal(50)
spooker.change_essence_amount(-20, FALSE, "telekinesis")
spooker.telekinesis_cooldown = TRUE
throwable.float(TRUE, TRUE)
sleep(20)
throwable.DoRevenantThrowEffects(over)
throwable.throw_at(over, 10, 2)
ADD_TRAIT(throwable, TRAIT_SPOOKY_THROW, "revenant")
log_combat(throwable, over, "spooky telekinesised at", throwable)
var/obj/effect/temp_visual/telekinesis/T = new(get_turf(throwable))
T.color = "#8715b4"
addtimer(CALLBACK(spooker, /mob/living/simple_animal/revenant.proc/telekinesis_cooldown_end), 50)
sleep(5)
throwable.float(FALSE, TRUE)
//Use this for effects you want to happen when a revenant throws stuff, check the TRAIT_SPOOKY_THROW if you want to know if its still being thrown
/obj/item/proc/DoRevenantThrowEffects(atom/target)
return TRUE
//objectives
/datum/objective/revenant
var/targetAmount = 100
+1 -1
View File
@@ -93,7 +93,6 @@
AIStatus = AI_OFF
pass_flags = PASSTABLE
mob_size = MOB_SIZE_TINY
ventcrawler = VENTCRAWLER_ALWAYS
ranged = 1
projectiletype = /obj/item/projectile/beam/disabler
ranged_cooldown_time = 20
@@ -112,6 +111,7 @@
remove_verb(src, /mob/living/verb/pulled)
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_to_hud(src)
AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
/mob/living/simple_animal/hostile/swarmer/med_hud_set_health()
var/image/holder = hud_list[DIAG_HUD]
@@ -8,11 +8,9 @@
/datum/traitor_class/human/assassin/forge_single_objective(datum/antagonist/traitor/T)
.=1
var/permakill_prob = 20
var/is_dynamic = FALSE
var/datum/game_mode/dynamic/mode
if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode
is_dynamic = TRUE
permakill_prob = max(0,mode.threat_level-50)
var/list/active_ais = active_ais()
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
@@ -20,11 +18,6 @@
destroy_objective.owner = T.owner
destroy_objective.find_target()
T.add_objective(destroy_objective)
else if(prob(30) || (is_dynamic && (mode.storyteller.flags & NO_ASSASSIN)))
var/datum/objective/maroon/maroon_objective = new
maroon_objective.owner = T.owner
maroon_objective.find_target()
T.add_objective(maroon_objective)
else if(prob(permakill_prob))
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = T.owner
@@ -41,11 +41,6 @@
destroy_objective.owner = T.owner
destroy_objective.find_target()
T.add_objective(destroy_objective)
else if(prob(30) || (is_dynamic && (mode.storyteller.flags & NO_ASSASSIN)))
var/datum/objective/maroon/maroon_objective = new
maroon_objective.owner = T.owner
maroon_objective.find_target()
T.add_objective(maroon_objective)
else if(prob(max(0,assassin_prob-20)))
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = T.owner
@@ -17,11 +17,6 @@
kill_objective.owner = T.owner
kill_objective.find_target()
T.add_objective(kill_objective)
else
var/datum/objective/maroon/maroon_objective = new
maroon_objective.owner = T.owner
maroon_objective.find_target()
T.add_objective(maroon_objective)
else
var/list/weights = list()
weights["sabo"] = length(subtypesof(/datum/sabotage_objective))
@@ -43,7 +43,7 @@
var/list/to_generate = list(
CONTRACT_PAYOUT_LARGE,
CONTRACT_PAYOUT_MEDIUM,
CONTRACT_PAYOUT_SMALL,
CONTRACT_PAYOUT_MEDIUM,
CONTRACT_PAYOUT_SMALL,
CONTRACT_PAYOUT_SMALL,
CONTRACT_PAYOUT_SMALL
+8 -8
View File
@@ -103,29 +103,29 @@
name = "knotted dildo"
attack_verb = list("penetrated", "knotted", "slapped", "inseminated")
obj/item/dildo/human
/obj/item/dildo/human
dildo_shape = "human"
name = "human dildo"
attack_verb = list("penetrated", "slapped", "inseminated")
obj/item/dildo/plain
/obj/item/dildo/plain
dildo_shape = "plain"
name = "plain dildo"
attack_verb = list("penetrated", "slapped", "inseminated")
obj/item/dildo/flared
/obj/item/dildo/flared
dildo_shape = "flared"
name = "flared dildo"
attack_verb = list("penetrated", "slapped", "neighed", "gaped", "prolapsed", "inseminated")
obj/item/dildo/flared/huge
/obj/item/dildo/flared/huge
name = "literal horse cock"
desc = "THIS THING IS HUGE!"
dildo_size = 4
force = 10
hitsound = 'sound/weapons/klonk.ogg'
obj/item/dildo/custom
/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
@@ -136,9 +136,9 @@ obj/item/dildo/custom
// 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)
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")
+3
View File
@@ -30,6 +30,9 @@
attack(user,user)
return FIRELOSS
/obj/item/assembly/flash/DoRevenantThrowEffects(atom/target)
AOE_flash()
/obj/item/assembly/flash/update_icon(flash = FALSE)
cut_overlays()
attached_overlays = list()
@@ -304,7 +304,7 @@
"danger_level" = cur_tlv.get_danger_level(environment.get_moles(gas_id) * partial_pressure)
))
if(!locked || hasSiliconAccessInArea(user, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE))
if(!locked || hasSiliconAccessInArea(user, PRIVILEGES_SILICON|PRIVILEGES_DRONE))
data["vents"] = list()
for(var/id_tag in A.air_vent_names)
var/long_name = A.air_vent_names[id_tag]
@@ -385,13 +385,13 @@
if(..() || buildstage != 2)
return
var/silicon_access = hasSiliconAccessInArea(usr)
var/bot_priviledges = silicon_access || (usr.silicon_privileges & PRIVILEDGES_DRONE)
if((locked && !bot_priviledges) || (silicon_access && aidisabled))
var/bot_privileges = silicon_access || (usr.silicon_privileges & PRIVILEGES_DRONE)
if((locked && !bot_privileges) || (silicon_access && aidisabled))
return
var/device_id = params["id_tag"]
switch(action)
if("lock")
if(bot_priviledges && !wires.is_cut(WIRE_IDSCAN))
if(bot_privileges && !wires.is_cut(WIRE_IDSCAN))
locked = !locked
. = TRUE
if("power", "toggle_filter", "widenet", "scrubbing")
@@ -44,7 +44,7 @@
. = ..()
if(is_type_in_list(src, GLOB.ventcrawl_machinery) && isliving(user))
var/mob/living/L = user
if(L.ventcrawler)
if(SEND_SIGNAL(L, COMSIG_CHECK_VENTCRAWL))
. += "<span class='notice'>Alt-click to crawl through it.</span>"
/obj/machinery/atmospherics/New(loc, process = TRUE, setdir)
@@ -318,7 +318,7 @@
/obj/machinery/atmospherics/AltClick(mob/living/L)
if(is_type_in_typecache(src, GLOB.ventcrawl_machinery))
return L.handle_ventcrawl(src)
return SEND_SIGNAL(L, COMSIG_HANDLE_VENTCRAWL, src)
return ..()
@@ -261,7 +261,7 @@
new /obj/item/stack/sheet/metal (loc, 5)
qdel(src)
obj/machinery/portable_atmospherics/canister/welder_act(mob/living/user, obj/item/I)
/obj/machinery/portable_atmospherics/canister/welder_act(mob/living/user, obj/item/I)
..()
if(user.a_intent == INTENT_HARM)
return FALSE
+3 -4
View File
@@ -1,4 +1,5 @@
obj/machinery/atmospherics/pipe/simple/multiz ///This is an atmospherics pipe which can relay air up a deck (Z+1). It currently only supports being on pipe layer 1
/// This is an atmospherics pipe which can relay air up/down a deck.
/obj/machinery/atmospherics/pipe/simple/multiz
name = "multi deck pipe adapter"
desc = "An adapter which allows pipes to connect to other pipenets on different decks."
icon_state = "multiz_pipe"
@@ -24,6 +25,4 @@ obj/machinery/atmospherics/pipe/simple/multiz ///This is an atmospherics pipe wh
if(above)
nodes += above
above.nodes += src //Two way travel :)
return ..()
else
return ..()
return ..()
+3 -3
View File
@@ -35,7 +35,7 @@
name = "Simple Drink"
reward = 1500
datum/bounty/reagent/simple_drink/New()
/datum/bounty/reagent/simple_drink/New()
// Don't worry about making this comprehensive. It doesn't matter if some drinks are skipped.
var/static/list/possible_reagents = list(\
/datum/reagent/consumable/ethanol/antifreeze,\
@@ -91,7 +91,7 @@ datum/bounty/reagent/simple_drink/New()
name = "Complex Drink"
reward = 4000
datum/bounty/reagent/complex_drink/New()
/datum/bounty/reagent/complex_drink/New()
// Don't worry about making this comprehensive. It doesn't matter if some drinks are skipped.
var/static/list/possible_reagents = list(\
/datum/reagent/consumable/ethanol/atomicbomb,\
@@ -124,7 +124,7 @@ datum/bounty/reagent/complex_drink/New()
reward = 2750
required_volume = 30
datum/bounty/reagent/chemical/New()
/datum/bounty/reagent/chemical/New()
// Don't worry about making this comprehensive. It doesn't matter if some chems are skipped.
var/static/list/possible_reagents = list(\
/datum/reagent/medicine/leporazine,\
+4 -4
View File
@@ -473,7 +473,7 @@
//Soft Suits
//Blanket
datum/export/gear/space/helmet
/datum/export/gear/space/helmet
cost = 55
unit_name = "space helmet"
export_types = list(/obj/item/clothing/head/helmet/space)
@@ -485,7 +485,7 @@ datum/export/gear/space/helmet
export_types = list(/obj/item/clothing/suit/space)
include_subtypes = TRUE
datum/export/gear/space/helmet/plasma
/datum/export/gear/space/helmet/plasma
cost = 100
unit_name = "plasmaman space helmet"
export_types = list(/obj/item/clothing/suit/space/eva/plasmaman)
@@ -495,7 +495,7 @@ datum/export/gear/space/helmet/plasma
unit_name = "plasmaman space suit"
export_types = list(/obj/item/clothing/suit/space/eva/plasmaman)
datum/export/gear/space/helmet/synda
/datum/export/gear/space/helmet/synda
cost = 150 //Flash proof
unit_name = "syndicate space helmet"
export_types = list(/obj/item/clothing/head/helmet/space/syndicate)
@@ -510,7 +510,7 @@ datum/export/gear/space/helmet/synda
//Glasses
//Blanket
datum/export/gear/glasses //glasses are not worth selling
/datum/export/gear/glasses //glasses are not worth selling
cost = 3
unit_name = "glasses"
export_types = list(/obj/item/clothing/glasses)
+19
View File
@@ -334,3 +334,22 @@
/obj/item/storage/fancy/ringbox/silver) //diamond rings cost the same price as this crate via cargo so we're not giving you two for free. Wedding rings are traditionally less valuable anyway.
crate_name = "wedding crate"
/datum/supply_pack/costumes_toys/randomised/tcg
name = "Big-Ass Booster Pack Pack"
desc = "A bumper load of NT TCG Booster Packs of varying series. Collect them all!"
cost = 3000
contains = list()
crate_name = "booster pack pack"
/datum/supply_pack/costumes_toys/randomised/tcg/generate()
. = ..()
var/cardpacktype
var/list/cardtypes = subtypesof(/obj/item/cardpack)
for(var/cardtype in cardtypes)
var/obj/item/cardpack/pack = new cardtype(.)
if(pack.illegal)
cardtypes.Remove(cardtype)
qdel(pack)
for(var/i in 1 to 10)
cardpacktype = pick(cardtypes)
new cardpacktype(.)
+9 -7
View File
@@ -76,9 +76,15 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
to_chat(src, "<span class='danger'>Your previous action was ignored because you've done too many in a second</span>")
return
//Logs all hrefs, except chat pings
if(!(href_list["_src_"] == "chat" && href_list["proc"] == "ping" && LAZYLEN(href_list) == 2))
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
// Tgui Topic middleware
if(tgui_Topic(href_list))
if(CONFIG_GET(flag/emergency_tgui_logging))
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
return
//Logs all hrefs
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
//byond bug ID:2256651
if (asset_cache_job && (asset_cache_job in completed_asset_jobs))
@@ -105,10 +111,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
handle_statpanel_click(href_list)
return
// Tgui Topic middleware
if(tgui_Topic(href_list))
return
// Admin PM
if(href_list["priv_msg"])
cmd_admin_pm(href_list["priv_msg"],null)
+81 -32
View File
@@ -99,6 +99,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/be_random_body = 0 //whether we'll have a random body every round
var/gender = MALE //gender of character (well duh)
var/age = 30 //age of character
var/language = "Random" //bonus language
var/choselanguage = "Random" //language appearance
var/underwear = "Nude" //underwear type
var/undie_color = "FFFFFF"
var/undershirt = "Nude" //undershirt type
@@ -218,6 +220,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/hide_ckey = FALSE //pref for hiding if your ckey shows round-end or not
var/list/tcg_cards = list()
/datum/preferences/New(client/C)
parent = C
@@ -308,6 +312,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<b>Gender:</b> <a href='?_src_=prefs;preference=gender;task=input'>[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]</a><BR>"
dat += "<b>Age:</b> <a style='display:block;width:30px' href='?_src_=prefs;preference=age;task=input'>[age]</a><BR>"
dat += "<b>Language:</b> <a href='?_src_=prefs;preference=language;task=input'>[choselanguage]</a><BR>"
dat += "<b>Special Names:</b><BR>"
var/old_group
@@ -895,19 +900,23 @@ GLOBAL_LIST_EMPTY(preferences_datums)
continue
var/class_link = ""
var/list/loadout_item = has_loadout_gear(loadout_slot, "[gear.type]")
var/extra_color_data = ""
var/extra_loadout_data = ""
if(loadout_item)
class_link = "style='white-space:normal;' class='linkOn' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=0'"
if(gear.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC)
extra_color_data += "<BR><a href='?_src_=prefs;preference=gear;loadout_color_polychromic=1;loadout_gear_name=[html_encode(gear.name)];'>Color</a>"
extra_loadout_data += "<BR><a href='?_src_=prefs;preference=gear;loadout_color_polychromic=1;loadout_gear_name=[html_encode(gear.name)];'>Color</a>"
for(var/loadout_color in loadout_item[LOADOUT_COLOR])
extra_color_data += "<span style='border: 1px solid #161616; background-color: [loadout_color];'>&nbsp;&nbsp;&nbsp;</span>"
extra_loadout_data += "<span style='border: 1px solid #161616; background-color: [loadout_color];'>&nbsp;&nbsp;&nbsp;</span>"
else
var/loadout_color_non_poly = "#FFFFFF"
if(length(loadout_item[LOADOUT_COLOR]))
loadout_color_non_poly = loadout_item[LOADOUT_COLOR][1]
extra_color_data += "<BR><a href='?_src_=prefs;preference=gear;loadout_color=1;loadout_gear_name=[html_encode(gear.name)];'>Color</a>"
extra_color_data += "<span style='border: 1px solid #161616; background-color: [loadout_color_non_poly];'>&nbsp;&nbsp;&nbsp;</span>"
extra_loadout_data += "<BR><a href='?_src_=prefs;preference=gear;loadout_color=1;loadout_gear_name=[html_encode(gear.name)];'>Color</a>"
extra_loadout_data += "<span style='border: 1px solid #161616; background-color: [loadout_color_non_poly];'>&nbsp;&nbsp;&nbsp;</span>"
if(gear.loadout_flags & LOADOUT_CAN_NAME)
extra_loadout_data += "<BR><a href='?_src_=prefs;preference=gear;loadout_rename=1;loadout_gear_name=[html_encode(gear.name)];'>Name</a> [loadout_item[LOADOUT_CUSTOM_NAME] ? loadout_item[LOADOUT_CUSTOM_NAME] : "N/A"]"
if(gear.loadout_flags & LOADOUT_CAN_DESCRIPTION)
extra_loadout_data += "<BR><a href='?_src_=prefs;preference=gear;loadout_redescribe=1;loadout_gear_name=[html_encode(gear.name)];'>Description</a>"
else if((gear_points - gear.cost) < 0)
class_link = "style='white-space:normal;' class='linkOff'"
else if(donoritem)
@@ -916,7 +925,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
class_link = "style='white-space:normal;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'"
else
class_link = "style='white-space:normal;background:#eb2e2e;' class='linkOff'"
dat += "<tr style='vertical-align:top;'><td width=15%><a [class_link]>[name]</a>[extra_color_data]</td>"
dat += "<tr style='vertical-align:top;'><td width=15%><a [class_link]>[name]</a>[extra_loadout_data]</td>"
dat += "<td width = 5% style='vertical-align:top'>[gear.cost]</td><td>"
if(islist(gear.restricted_roles))
if(gear.restricted_roles.len)
@@ -929,14 +938,15 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += gear.restricted_roles.Join(";")
dat += "</font>"
if(!istype(gear, /datum/gear/unlockable))
dat += "</td><td><font size=2><i>[gear.description]</i></font></td></tr>"
// the below line essentially means "if the loadout item is picked by the user and has a custom description, give it the custom description, otherwise give it the default description"
dat += "</td><td><font size=2><i>[loadout_item ? (loadout_item[LOADOUT_CUSTOM_DESCRIPTION] ? loadout_item[LOADOUT_CUSTOM_DESCRIPTION] : gear.description) : gear.description]</i></font></td></tr>"
else
//we add the user's progress to the description assuming they have progress
var/datum/gear/unlockable/unlockable = gear
var/progress_made = unlockable_loadout_data[unlockable.progress_key]
if(!progress_made)
progress_made = 0
dat += "</td><td><font size=2><i>[gear.description] Progress: [min(progress_made, unlockable.progress_required)]/[unlockable.progress_required]</i></font></td></tr>"
dat += "</td><td><font size=2><i>[loadout_item ? (loadout_item[LOADOUT_CUSTOM_DESCRIPTION] ? loadout_item[LOADOUT_CUSTOM_DESCRIPTION] : gear.description) : gear.description] Progress: [min(progress_made, unlockable.progress_required)]/[unlockable.progress_required]</i></font></td></tr>"
dat += "</table>"
if(4) // Content preferences
@@ -2312,6 +2322,28 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["body_model"] = chosengender
gender = chosengender
if("language")
choselanguage = input(user, "Select a language.", "Language", language) as null|anything in list("Beachtongue","Draconic","Dwarven",
"Chimpanzee","Space Sign Language","Random")
if(!choselanguage)
return
switch(choselanguage)
if("Rachidian")
language = /datum/language/arachnid
if("Beachtongue")
language = /datum/language/beachbum
if("Draconic")
language = /datum/language/draconic
if("Dwarven")
language = /datum/language/dwarf
if("Chimpanzee")
language = /datum/language/monkey
if("Space Sign Language")
language = /datum/language/signlanguage
if("Random")
language = pick(list("Rachidian", "Beachtongue","Draconic","Dwarven",
"Chimpanzee","Space Sign Language"))
if("body_size")
var/new_body_size = input(user, "Choose your desired sprite size: (90-125%)\nWarning: This may make your character look distorted. Additionally, any size under 100% takes a 10% maximum health penalty", "Character Preference", features["body_size"]*100) as num|null
if(new_body_size)
@@ -2702,7 +2734,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
loadout_data["SAVE_[loadout_slot]"] += list(new_loadout_data) //double packed because it does the union of the CONTENTS of the lists
else
loadout_data["SAVE_[loadout_slot]"] = list(new_loadout_data) //double packed because you somehow had no save slot in your loadout?
if(href_list["loadout_color"])
if(href_list["loadout_color"] || href_list["loadout_color_polychromic"] || href_list["loadout_rename"] || href_list["loadout_redescribe"])
//if the gear doesn't exist, or they don't have it, ignore the request
var/name = html_decode(href_list["loadout_gear_name"])
var/datum/gear/G = GLOB.loadout_items[gear_category][gear_subcategory][name]
if(!G)
@@ -2710,29 +2744,44 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/user_gear = has_loadout_gear(loadout_slot, "[G.type]")
if(!user_gear)
return
if(!length(user_gear[LOADOUT_COLOR]))
user_gear[LOADOUT_COLOR] = list("#FFFFFF")
var/current_color = user_gear[LOADOUT_COLOR][1]
var/new_color = input(user, "Polychromic options", "Choose Color", current_color) as color|null
user_gear[LOADOUT_COLOR][1] = sanitize_hexcolor(new_color, 6, TRUE, current_color)
if(href_list["loadout_color_polychromic"])
var/name = html_decode(href_list["loadout_gear_name"])
var/datum/gear/G = GLOB.loadout_items[gear_category][gear_subcategory][name]
if(!G)
return
var/user_gear = has_loadout_gear(loadout_slot, "[G.type]")
if(!user_gear)
return
var/list/color_options = list()
for(var/i=1, i<=length(G.loadout_initial_colors), i++)
color_options += "Color [i]"
var/color_to_change = input(user, "Polychromic options", "Recolor [name]") as null|anything in color_options
if(color_to_change)
var/color_index = text2num(copytext(color_to_change, 7))
var/current_color = user_gear[LOADOUT_COLOR][color_index]
var/new_color = input(user, "Polychromic options", "Choose [color_to_change] Color", current_color) as color|null
if(new_color)
user_gear[LOADOUT_COLOR][color_index] = sanitize_hexcolor(new_color, 6, TRUE, current_color)
//possible requests: recolor, recolor (polychromic), rename, redescribe
//always make sure the gear allows said request before proceeding
//non-poly coloring can only be done by non-poly items
if(href_list["loadout_color"] && !(G.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC))
if(!length(user_gear[LOADOUT_COLOR]))
user_gear[LOADOUT_COLOR] = list("#FFFFFF")
var/current_color = user_gear[LOADOUT_COLOR][1]
var/new_color = input(user, "Polychromic options", "Choose Color", current_color) as color|null
user_gear[LOADOUT_COLOR][1] = sanitize_hexcolor(new_color, 6, TRUE, current_color)
//poly coloring can only be done by poly items
if(href_list["loadout_color_polychromic"] && (G.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC))
var/list/color_options = list()
for(var/i=1, i<=length(G.loadout_initial_colors), i++)
color_options += "Color [i]"
var/color_to_change = input(user, "Polychromic options", "Recolor [name]") as null|anything in color_options
if(color_to_change)
var/color_index = text2num(copytext(color_to_change, 7))
var/current_color = user_gear[LOADOUT_COLOR][color_index]
var/new_color = input(user, "Polychromic options", "Choose [color_to_change] Color", current_color) as color|null
if(new_color)
user_gear[LOADOUT_COLOR][color_index] = sanitize_hexcolor(new_color, 6, TRUE, current_color)
//both renaming and redescribing strip the input to stop html injection
//renaming is only allowed if it has the flag for it
if(href_list["loadout_rename"] && (G.loadout_flags & LOADOUT_CAN_NAME))
var/new_name = stripped_input(user, "Enter new name for item. Maximum [MAX_NAME_LEN] characters.", "Loadout Item Naming", null, MAX_NAME_LEN)
if(new_name)
user_gear[LOADOUT_CUSTOM_NAME] = new_name
//redescribing is only allowed if it has the flag for it
if(href_list["loadout_redescribe"] && (G.loadout_flags & LOADOUT_CAN_DESCRIPTION)) //redescribe isnt a real word but i can't think of the right term to use
var/new_description = stripped_input(user, "Enter new description for item. Maximum 500 characters.", "Loadout Item Redescribing", null, 500)
if(new_description)
user_gear[LOADOUT_CUSTOM_DESCRIPTION] = new_description
ShowChoices(user)
return 1
+18 -1
View File
@@ -271,7 +271,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
loadout_data["SAVE_[i]"] = list()
for(var/some_gear_item in saved_loadout_paths)
if(!ispath(text2path(some_gear_item)))
message_admins("Failed to copy item [some_gear_item] to new loadout system when migrating from version [current_version] to 40, issue: item is not a path")
log_game("Failed to copy item [some_gear_item] to new loadout system when migrating from version [current_version] to 40, issue: item is not a path")
continue
var/datum/gear/gear_item = text2path(some_gear_item)
if(!(initial(gear_item.loadout_flags) & LOADOUT_CAN_COLOR_POLYCHROMIC))
@@ -604,6 +604,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["body_model"] >> features["body_model"]
S["body_size"] >> features["body_size"]
S["age"] >> age
S["language"] >> language
S["choselanguage"] >> choselanguage
S["hair_color"] >> hair_color
S["facial_hair_color"] >> facial_hair_color
S["eye_type"] >> eye_type
@@ -656,6 +658,14 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
modified_limbs = safe_json_decode(limbmodstr)
else
modified_limbs = list()
var/tcgcardstr
S["tcg_cards"] >> tcgcardstr
if(length(tcgcardstr))
tcg_cards = safe_json_decode(tcgcardstr)
else
tcg_cards = list()
S["chosen_limb_id"] >> chosen_limb_id
S["hide_ckey"] >> hide_ckey //saved per-character
@@ -947,6 +957,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["body_model"] , features["body_model"])
WRITE_FILE(S["body_size"] , features["body_size"])
WRITE_FILE(S["age"] , age)
WRITE_FILE(S["language"] , language)
WRITE_FILE(S["choselanguage"] , choselanguage)
WRITE_FILE(S["hair_color"] , hair_color)
WRITE_FILE(S["facial_hair_color"] , facial_hair_color)
WRITE_FILE(S["eye_type"] , eye_type)
@@ -1091,6 +1103,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
else
S["loadout"] << safe_json_encode(list())
if(length(tcg_cards))
S["tcg_cards"] << safe_json_encode(tcg_cards)
else
S["tcg_cards"] << safe_json_encode(list())
cit_character_pref_save(S)
return 1
+5
View File
@@ -31,6 +31,9 @@
// What items can be consumed to repair this clothing (must by an /obj/item/stack)
var/repairable_by = /obj/item/stack/sheet/cloth
// has this item been upgraded by an upgrade kit (see: durathread armor kits)
var/upgrade_prefix
//Var modification - PLEASE be careful with this I know who you are and where you live
var/list/user_vars_to_edit //VARNAME = VARVALUE eg: "name" = "butts"
var/list/user_vars_remembered //Auto built by the above + dropped() + equipped()
@@ -120,6 +123,8 @@
update_clothes_damaged_state(CLOTHING_PRISTINE)
obj_integrity = max_integrity
name = initial(name) // remove "tattered" or "shredded" if there's a prefix
if(upgrade_prefix)
name = upgrade_prefix + " " + initial(name)
body_parts_covered = initial(body_parts_covered)
slot_flags = initial(slot_flags)
damage_by_parts = null
@@ -161,6 +161,68 @@
return NO_AUTO_CLICKDELAY_HANDLING | ATTACK_IGNORE_ACTION
/obj/item/clothing/gloves/fingerless/ablative
name = "ablative armwraps"
desc = "Armwraps made out of a highly durable, reflective metal. Has the side effect of absorbing shocks."
siemens_coefficient = 0
icon_state = "ablative_armwraps"
item_state = "ablative_armwraps"
block_parry_data = /datum/block_parry_data/ablative_armwraps
var/wornonce = FALSE
/obj/item/clothing/gloves/fingerless/ablative/proc/get_component_parry_data(datum/source, parrying_method, datum/parrying_item_mob_or_art, list/backup_items, list/override)
if(parrying_method && !(parrying_method == UNARMED_PARRY))
return
override[src] = ITEM_PARRY
/obj/item/clothing/gloves/fingerless/ablative/equipped(mob/user, slot)
. = ..()
if(current_equipped_slot == SLOT_GLOVES)
RegisterSignal(user, COMSIG_LIVING_ACTIVE_PARRY_START, .proc/get_component_parry_data)
wornonce = TRUE
/obj/item/clothing/gloves/fingerless/ablative/dropped(mob/user)
. = ..()
if(wornonce)
UnregisterSignal(user, COMSIG_LIVING_ACTIVE_PARRY_START)
wornonce = FALSE
/obj/item/clothing/gloves/fingerless/ablative/can_active_parry(mob/user)
var/mob/living/carbon/human/H = user
if(!istype(H))
return FALSE
return src == H.gloves
/obj/item/clothing/gloves/fingerless/ablative/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time)
. = ..()
if(parry_efficiency > 0)
owner.visible_message("<span class='warning'>[owner] deflects \the [object] with their armwraps!</span>")
/datum/block_parry_data/ablative_armwraps
parry_stamina_cost = 4
parry_attack_types = ATTACK_TYPE_UNARMED | ATTACK_TYPE_PROJECTILE | ATTACK_TYPE_TACKLE | ATTACK_TYPE_THROWN | ATTACK_TYPE_MELEE
parry_flags = NONE
parry_time_windup = 0
parry_time_spindown = 0
parry_time_active = 7.5
parry_time_perfect = 1
parry_time_perfect_leeway = 7.5
parry_imperfect_falloff_percent = 20
parry_efficiency_perfect = 100
parry_time_perfect_leeway_override = list(
TEXT_ATTACK_TYPE_MELEE = 1
)
parry_efficiency_considered_successful = 0.01
parry_efficiency_to_counterattack = INFINITY // no auto counter
parry_max_attacks = INFINITY
parry_failed_cooldown_duration = 2.25 SECONDS
parry_failed_stagger_duration = 2.25 SECONDS
parry_cooldown = 3 SECONDS
parry_failed_clickcd_duration = 0.5 SECONDS
/obj/item/clothing/gloves/botanic_leather
name = "botanist's leather gloves"
desc = "These leather gloves protect against thorns, barbs, prickles, spikes and other harmful objects of floral origin. They're also quite warm."
+20 -3
View File
@@ -17,7 +17,7 @@
/obj/item/clothing/mask/gas/examine(mob/user)
. = ..()
if(flavor_adjust)
. += "<span class='info'>Alt-click to toggle identity concealment. it's currently <b>[flags_inv & HIDEFACE ? "on" : "off"]</b>.</span>"
. += "<span class='info'>Alt-click to toggle identity concealment. It's currently <b>[flags_inv & HIDEFACE ? "on" : "off"]</b>.</span>"
/obj/item/clothing/mask/gas/AltClick(mob/user)
. = ..()
@@ -143,14 +143,17 @@
"Blanc" = image(icon = src.icon, icon_state = "mime"),
"Excité" = image(icon = src.icon, icon_state = "sexymime"),
"Triste" = image(icon = src.icon, icon_state = "sadmime"),
"Effrayé" = image(icon = src.icon, icon_state = "scaredmime")
"Effrayé" = image(icon = src.icon, icon_state = "scaredmime"),
"Timid Woman" = image(icon = src.icon, icon_state = "timidwoman"),
"Timid Man" = image(icon = src.icon, icon_state = "timidman")
)
/obj/item/clothing/mask/gas/mime/ui_action_click(mob/user)
if(!istype(user) || user.incapacitated())
return
var/static/list/options = list("Blanc" = "mime", "Triste" = "sadmime", "Effrayé" = "scaredmime", "Excité" ="sexymime")
var/static/list/options = list("Blanc" = "mime", "Triste" = "sadmime", "Effrayé" = "scaredmime", "Excité" ="sexymime",
"Timid Woman" = "timidwoman", "Timid Man" = "timidman")
var/choice = show_radial_menu(user,src, mimemask_designs, custom_check = FALSE, radius = 36, require_near = TRUE)
@@ -170,6 +173,20 @@
item_state = "sexymime"
actions_types = list()
/obj/item/clothing/mask/gas/timidcostume
name = "timid woman mask"
desc = "Most people who wear these are not really that timid."
clothing_flags = ALLOWINTERNALS
icon_state = "timidwoman"
item_state = "timidwoman"
flags_cover = MASKCOVERSEYES
resistance_flags = FLAMMABLE
/obj/item/clothing/mask/gas/timidcostume/man
name = "timid man mask"
icon_state = "timidman"
item_state = "timidman"
/obj/item/clothing/mask/gas/monkeymask
name = "monkey mask"
desc = "A mask used when acting as a monkey."
@@ -33,6 +33,16 @@
actions_types = list(/datum/action/item_action/adjust)
mutantrace_variation = STYLE_MUZZLE
/obj/item/clothing/mask/surgical/aesthetic
name = "aesthetic sterile mask"
desc = "A sterile mask designed to help prevent the spread of diseases. This one doesn't seem like it does a whole lot, somehow."
flags_inv = null
flags_cover = null
visor_flags_inv = null
visor_flags_cover = null
permeability_coefficient = 1
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
/obj/item/clothing/mask/surgical/attack_self(mob/user)
adjustmask(user)
+7 -1
View File
@@ -237,6 +237,12 @@
icon_state = "choker"
poly_colors = list("#222222")
/obj/item/clothing/neck/necklace/cowbell
name = "cowbell collar"
desc = "Who would wear this? Take this off, you aren't a cow, you're just an awful degenerate."
icon = 'icons/obj/clothing/neck.dmi'
icon_state = "cowbell"
/obj/item/key/collar
name = "Collar Key"
desc = "A key for a tiny lock on a collar or bag."
@@ -289,7 +295,7 @@
//VERY SUPER BADASS NECKERCHIEFS//
//////////////////////////////////
obj/item/clothing/neck/neckerchief
/obj/item/clothing/neck/neckerchief
icon = 'icons/obj/clothing/masks.dmi' //In order to reuse the bandana sprite
w_class = WEIGHT_CLASS_TINY
var/sourceBandanaType
+11
View File
@@ -51,6 +51,17 @@
desc = "Very gay shoes."
icon_state = "rain_bow"
/obj/item/clothing/shoes/sneakers/poly/polychromic
name = "polychromic shoes"
desc = "Every color."
icon_state = "poly"
item_state = "poly"
var/list/poly_colors = list("#FFFFFF", "#1D1D1D")
/obj/item/clothing/shoes/sneakers/poly/polychromic/ComponentInitialize()
. = ..()
AddElement(/datum/element/polychromic, poly_colors, 2)
/obj/item/clothing/shoes/sneakers/orange
name = "orange shoes"
icon_state = "orange"
+38 -3
View File
@@ -97,12 +97,14 @@
SEND_SIGNAL(t_loc, COMSIG_TURF_MAKE_DRY, TURF_WET_WATER, TRUE, INFINITY)
/obj/item/clothing/shoes/clown_shoes
desc = "The prankster's standard-issue clowning shoes. Damn, they're huge!"
desc = "The prankster's standard-issue clowning shoes. Damn, they're huge! Ctrl-click to toggle waddle dampeners."
name = "clown shoes"
icon_state = "clown_shoes"
slowdown = SHOES_SLOWDOWN+1
pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes/clown
lace_time = 20 SECONDS // how the hell do these laces even work??
var/datum/component/waddle
var/enabled_waddle = TRUE
/obj/item/clothing/shoes/clown_shoes/Initialize()
. = ..()
@@ -110,14 +112,31 @@
/obj/item/clothing/shoes/clown_shoes/equipped(mob/user, slot)
. = ..()
if(user.mind && HAS_TRAIT(user.mind, TRAIT_CLOWN_MENTALITY))
SEND_SIGNAL(user, COMSIG_CLEAR_MOOD_EVENT, "noshoes")
if(slot == SLOT_SHOES)
if(enabled_waddle)
waddle = user.AddComponent(/datum/component/waddling)
if(user.mind && HAS_TRAIT(user.mind, TRAIT_CLOWN_MENTALITY))
SEND_SIGNAL(user, COMSIG_CLEAR_MOOD_EVENT, "noshoes")
/obj/item/clothing/shoes/clown_shoes/dropped(mob/user)
. = ..()
QDEL_NULL(waddle)
if(user.mind && HAS_TRAIT(user.mind, TRAIT_CLOWN_MENTALITY))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "noshoes", /datum/mood_event/noshoes)
/obj/item/clothing/shoes/clown_shoes/CtrlClick(mob/living/user)
if(!isliving(user))
return
if(user.get_active_held_item() != src)
to_chat(user, "<span class='warning'>You must hold the [src] in your hand to do this!</span>")
return
if (!enabled_waddle)
to_chat(user, "<span class='notice'>You switch off the waddle dampeners!</span>")
enabled_waddle = TRUE
else
to_chat(user, "<span class='notice'>You switch on the waddle dampeners!</span>")
enabled_waddle = FALSE
/obj/item/clothing/shoes/clown_shoes/jester
name = "jester shoes"
desc = "A court jester's shoes, updated with modern squeaking technology."
@@ -413,6 +432,22 @@
var/wallcharges = 4
var/newlocobject = null
/obj/item/clothing/shoes/timidcostume
name = "timid woman boots"
desc = "Ready to rock your hips back and forth? These boots have a polychromic finish."
icon_state = "timidwoman"
item_state = "timidwoman"
/obj/item/clothing/shoes/timidcostume/ComponentInitialize()
. = ..()
AddElement(/datum/element/polychromic, list("#0094FF"), 1)
/obj/item/clothing/shoes/timidcostume/man
name = "timid man shoes"
desc = "Ready to go kart racing? These shoes have a polychromic finish."
icon_state = "timidman"
item_state = "timidman"
/obj/item/clothing/shoes/wallwalkers/equipped(mob/user,slot)
. = ..()
if(slot == SLOT_SHOES)
+12 -1
View File
@@ -102,7 +102,18 @@
/obj/item/clothing/neck/cloak/polychromic/ComponentInitialize()
. = ..()
AddElement(/datum/element/polychromic, poly_colors, 3)
/obj/item/clothing/neck/cancloak/polychromic
name = "canvas cloak"
desc = "A rugged cloak made of canvas."
icon_state = "cancloak"
item_state = "cancloak"
var/list/poly_colors = list("#585858", "#373737", "#BEBEBE")
/obj/item/clothing/neck/cancloak/polychromic/ComponentInitialize()
. = ..()
AddElement(/datum/element/polychromic, poly_colors, 3)
/obj/item/clothing/neck/cloak/alt
name = "cloak"
desc = "A ragged up white cloak. It reminds you of a place not far from here."
@@ -487,6 +487,17 @@
item_state = "militaryjacket"
allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/revolver, /obj/item/radio)
/obj/item/clothing/suit/jacket/urbanjacket/polychromic
name = "urban jacket"
desc = "A canvas jacket styled with a fur neck piece, stylish."
icon_state = "urbanjacket"
item_state = "urbanjacket"
allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/revolver, /obj/item/radio)
/obj/item/clothing/suit/jacket/urbanjacket/polychromic/ComponentInitialize()
. = ..()
AddElement(/datum/element/polychromic, list("#26321D", "#CBBDAF", "#292929"), 3)
/obj/item/clothing/suit/jacket/letterman
name = "letterman jacket"
desc = "A classic brown letterman jacket. Looks pretty hot and heavy."
@@ -1023,6 +1034,16 @@
alternate_worn_layer = UNDER_HEAD_LAYER
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
/obj/item/clothing/suit/toggle/wbreakpoly
name = "polychromic windbreaker"
desc = "Perfect for windy days."
icon_state = "wbreakpoly"
item_state = "wbreakpoly"
/obj/item/clothing/suit/toggle/wbreakpoly/polychromic/ComponentInitialize()
. = ..()
AddElement(/datum/element/polychromic, list("#464F65", "#916035", "#474747"), 3)
/obj/item/clothing/suit/flakjack
name = "flak jacket"
desc = "A dilapidated jacket made of a supposedly bullet-proof material (Hint: It isn't.). Smells faintly of napalm."
@@ -1088,6 +1109,36 @@
icon_state = "winterhood_poly"
item_state = "winterhood_poly"
/obj/item/clothing/suit/hooded/wintercoat/timidcostume
name = "timid woman hoodie"
desc = "A snug, tight yet warm outfit with belts wrapped around it. Looks to be made of polychromic materials."
icon_state = "timidwoman"
item_state = "timidwoman"
hoodtype = /obj/item/clothing/head/hooded/winterhood/timidcostume
/obj/item/clothing/suit/hooded/wintercoat/timidcostume/ComponentInitialize()
. = ..()
AddElement(/datum/element/polychromic, list("#EB0C07", "#5E2400", "#CEA100"), 3)
/obj/item/clothing/head/hooded/winterhood/timidcostume
name = "timid woman hood"
desc = "A hood attached to the hoodie."
icon_state = "timidwoman"
item_state = "timidwoman"
/obj/item/clothing/suit/hooded/wintercoat/timidcostume/man
name = "timid man hoodie"
desc = "A snug, tight yet warm outfit a belt wrapped around it. Looks to be made of polychromic materials."
icon_state = "timidman"
item_state = "timidman"
hoodtype = /obj/item/clothing/head/hooded/winterhood/timidcostume/man
/obj/item/clothing/head/hooded/winterhood/timidcostume/man
name = "timid man hood"
icon_state = "timidman"
item_state = "timidman"
/obj/item/clothing/suit/striped_sweater
name = "striped sweater"
desc = "Reminds you of someone, but you just can't put your finger on it..."
+6 -4
View File
@@ -61,8 +61,9 @@
if(!hood)
to_chat(loc, "<span class='warning'>[src] seems to be missing its hood..</span>")
return
hood.atom_colours = atom_colours.Copy()
hood.update_atom_colour()
if(atom_colours)
hood.atom_colours = atom_colours.Copy()
hood.update_atom_colour()
if(!suittoggled)
if(ishuman(src.loc))
var/mob/living/carbon/human/H = src.loc
@@ -198,8 +199,9 @@
if(!helmet)
to_chat(H, "<span class='warning'>[src] seems to be missing its helmet..</span>")
return
helmet.atom_colours = atom_colours.Copy()
helmet.update_atom_colour()
if(atom_colours)
helmet.atom_colours = atom_colours.Copy()
helmet.update_atom_colour()
if(!suittoggled)
if(ishuman(src.loc))
if(H.wear_suit != src)
+3
View File
@@ -32,6 +32,9 @@
/obj/item/clothing/under/attackby(obj/item/I, mob/user, params)
if((has_sensor == BROKEN_SENSORS) && istype(I, /obj/item/stack/cable_coil))
if(damaged_clothes)
to_chat(user,"<span class='warning'>You should repair the damage done to [src] first.</span>")
return 0
var/obj/item/stack/cable_coil/C = I
I.use_tool(src, user, 0, 1)
has_sensor = HAS_SENSORS
+163 -1
View File
@@ -79,13 +79,175 @@
if(initial(above_suit))
. += "<span class='notice'>\The [src] can be worn above or below your suit. Alt-click to toggle.</span>"
//////////////
//Waistcoats//
//////////////
/obj/item/clothing/accessory/waistcoat
name = "waistcoat"
name = "black waistcoat"
desc = "For some classy, murderous fun."
icon_state = "waistcoat"
item_state = "waistcoat"
minimize_when_attached = FALSE
/obj/item/clothing/accessory/waistcoat/red
name = "red waistcoat"
icon_state = "waistcoat_red"
item_state = "waistcoat_red"
/obj/item/clothing/accessory/waistcoat/grey
name = "grey waistcoat"
icon_state = "waistcoat_grey"
item_state = "waistcoat_grey"
/obj/item/clothing/accessory/waistcoat/brown
name = "red waistcoat"
icon_state = "waistcoat_brown"
item_state = "waistcoat_brown"
/obj/item/clothing/accessory/waistcoat/sweatervest
name = "black sweatervest"
icon_state = "sweatervest"
item_state = "sweatervest"
/obj/item/clothing/accessory/waistcoat/sweatervest/blue
name = "blue sweatervest"
icon_state = "sweatervest_blue"
item_state = "sweatervest_blue"
/obj/item/clothing/accessory/waistcoat/sweatervest/red
name = "red sweatervest"
icon_state = "sweatervest_red"
item_state = "sweatervest_red"
////////////
//Sweaters//
////////////
/obj/item/clothing/accessory/sweater
name = "grey sweater"
desc = "Nicely comfy and warm!"
icon_state = "sweater"
item_state = "sweater"
minimize_when_attached = FALSE
/obj/item/clothing/accessory/sweater/pink
name = "pink sweater"
icon_state = "sweater_pink"
item_state = "sweater_pink"
/obj/item/clothing/accessory/sweater/heart
name = "heart sweater"
icon_state = "sweater_heart"
item_state = "sweater_heart"
/obj/item/clothing/accessory/sweater/blue
name = "blue sweater"
icon_state = "sweater_blue"
item_state = "sweater_blue"
/obj/item/clothing/accessory/sweater/nt
name = "nanotrasen sweater"
icon_state = "sweater_nt"
item_state = "sweater_nt"
/obj/item/clothing/accessory/sweater/mint
name = "mint sweater"
icon_state = "sweater_mint"
item_state = "sweater_mint"
/obj/item/clothing/accessory/sweater/shoulderless
name = "shoulderless sweater"
icon_state = "sweater_shoulderless"
item_state = "sweater_shoulderless"
/obj/item/clothing/accessory/sweater/uglyxmas
name = "ugly xmas sweater"
icon_state = "sweater_uglyxmas"
item_state = "sweater_uglyxmas"
/obj/item/clothing/accessory/sweater/flower
name = "flower sweater"
icon_state = "sweater_flower"
item_state = "sweater_flower"
////////////////
//Suit Jackets//
////////////////
/obj/item/clothing/accessory/suitjacket
name = "tan suit jacket"
desc = "For those times when you have to attend a fancy business meeting without wearing your pants."
icon_state = "jacket_tan"
item_state = "jacket_tan"
minimize_when_attached = FALSE
/obj/item/clothing/accessory/suitjacket/charcoal
name = "charcoal suit jacket"
icon_state = "jacket_charcoal"
item_state = "jacket_charcoal"
/obj/item/clothing/accessory/suitjacket/navy
name = "navy suit jacket"
icon_state = "jacket_navy"
item_state = "jacket_navy"
/obj/item/clothing/accessory/suitjacket/burgundy
name = "burgundy suit jacket"
icon_state = "jacket_burgundy"
item_state = "jacket_burgundy"
/obj/item/clothing/accessory/suitjacket/checkered
name = "checkered suit jacket"
icon_state = "jacket_checkered"
item_state = "jacket_checkered"
///////////////////////
//Tactical Turtlnecks//
///////////////////////
/obj/item/clothing/accessory/turtleneck
name = "black turtleneck"
desc = "Extra cool. Extra fool."
icon_state = "turtleneck"
item_state = "turtleneck"
minimize_when_attached = FALSE
/obj/item/clothing/accessory/turtleneck/red
name = "red turtleneck"
icon_state = "turtleneck_red"
item_state = "turtleneck_red"
/obj/item/clothing/accessory/turtleneck/comfy
name = "comfy turtleneck"
icon_state = "turtleneck_comfy"
item_state = "turtleneck_comfy"
/obj/item/clothing/accessory/turtleneck/tactifool
name = "black sweaterneck"
desc = "Extra fool. Extra cool."
icon_state = "tactifool"
item_state = "tactifool"
/obj/item/clothing/accessory/turtleneck/tactifool/green
name = "green sweaterneck"
icon_state = "tactifool_green"
item_state = "tactifool_green"
/obj/item/clothing/accessory/turtleneck/tactifool/blue
name = "blue sweaterneck"
icon_state = "tactifool_blue"
item_state = "tactifool_blue"
/obj/item/clothing/accessory/turtleneck/tactifool/syndicate
name = "tactifool sweaterneck"
icon_state = "tactifool_syndicate"
item_state = "tactifool_syndicate"
/////////////////
//Miscellaneous//
/////////////////
/obj/item/clothing/accessory/maidapron
name = "maid apron"
desc = "The best part of a maid costume."
+11
View File
@@ -46,6 +46,17 @@
desc = "Some tan pants. You look like a white collar worker with these on."
icon_state = "tanpants"
/obj/item/clothing/under/pants/polypants/polychromic
name = "polychromic pants"
desc = "Some stylish pair of pants made from polychrome."
icon_state = "polypants"
item_state = "polypants"
var/list/poly_colors = list("#75634F", "#3D3D3D", "#575757")
/obj/item/clothing/under/pants/polypants/polychromic/ComponentInitialize()
. = ..()
AddElement(/datum/element/polychromic, poly_colors, 3)
/obj/item/clothing/under/pants/track
name = "track pants"
desc = "A pair of track pants, for the athletic."
+6
View File
@@ -91,6 +91,12 @@
icon_state = "tan_suit"
item_state = "tan_suit"
/obj/item/clothing/under/suit/charismatic_suit
name = "charismatic suit"
desc = "Luck is for losers, baby."
icon_state = "charismatic_suit"
item_state = "charismatic_suit"
/obj/item/clothing/under/suit/white
name = "white suit"
desc = "A white suit and jacket with a blue shirt. You wanna play rough? OKAY!"
+20 -19
View File
@@ -144,22 +144,23 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
if(L && (L.density || prob(10)))
L.ex_act(EXPLODE_HEAVY)
obj/effect/immovablerod/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(ishuman(user))
var/mob/living/carbon/human/U = user
if(U.job in list("Research Director"))
playsound(src, 'sound/effects/meteorimpact.ogg', 100, 1)
for(var/mob/M in urange(8, src))
if(!M.stat)
shake_camera(M, 2, 3)
if(wizard)
U.visible_message("<span class='boldwarning'>[src] transforms into [wizard] as [U] suplexes them!</span>", "<span class='warning'>As you grab [src], it suddenly turns into [wizard] as you suplex them!</span>")
to_chat(wizard, "<span class='boldwarning'>You're suddenly jolted out of rod-form as [U] somehow manages to grab you, slamming you into the ground!</span>")
wizard.Stun(60)
wizard.apply_damage(25, BRUTE)
qdel(src)
else
U.visible_message("<span class='boldwarning'>[U] suplexes [src] into the ground!</span>", "<span class='warning'>You suplex [src] into the ground!</span>")
new /obj/structure/festivus/anchored(drop_location())
new /obj/effect/anomaly/flux(drop_location())
qdel(src)
/obj/effect/immovablerod/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!ishuman(user))
return
var/mob/living/carbon/human/U = user
if(U.job in list("Research Director"))
playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
for(var/mob/M in urange(8, src))
if(!M.stat)
shake_camera(M, 2, 3)
if(wizard)
U.visible_message("<span class='boldwarning'>[src] transforms into [wizard] as [U] suplexes them!</span>", "<span class='warning'>As you grab [src], it suddenly turns into [wizard] as you suplex them!</span>")
to_chat(wizard, "<span class='boldwarning'>You're suddenly jolted out of rod-form as [U] somehow manages to grab you, slamming you into the ground!</span>")
wizard.Stun(60)
wizard.apply_damage(25, BRUTE)
qdel(src)
else
U.visible_message("<span class='boldwarning'>[U] suplexes [src] into the ground!</span>", "<span class='warning'>You suplex [src] into the ground!</span>")
new /obj/structure/festivus/anchored(drop_location())
new /obj/effect/anomaly/flux(drop_location())
qdel(src)
+8 -8
View File
@@ -25,14 +25,14 @@
var/datum/effect_system/smoke_spread/smoke = new
smoke.set_up(1, spawn_location)
smoke.start()
trader.visible_message("<b>[src]</b> suddenly appears in a puff of smoke!")
trader.visible_message("<b>[trader]</b> suddenly appears in a puff of smoke!")
/datum/round_event/travelling_trader/announce(fake)
priority_announce("A mysterious figure has been detected on sensors at [get_area(spawn_location)]", "Mysterious Figure")
/datum/round_event/travelling_trader/end()
if(trader)
trader.visible_message("The <b>[src]</b> has given up on waiting!")
if(trader) // the /datum/round_event/travelling_trader has given up on waiting!
trader.visible_message("The <b>[trader]</b> has given up on waiting!")
qdel(trader)
//the actual trader mob
@@ -188,18 +188,18 @@
/mob/living/simple_animal/hostile/netherworld/blankbody = 1,
/mob/living/simple_animal/hostile/retaliate/goose = 1)
mob/living/carbon/human/dummy/travelling_trader/animal_hunter/Initialize()
/mob/living/carbon/human/dummy/travelling_trader/animal_hunter/Initialize()
. = ..()
acceptance_speech = pick(list("This lifeform shall make for a great stew, thank you.", "This lifeform shall be of a true use to our cause, thank you.", "The lifeform is adequate. Goodbye.", "This lifeform shall make a great addition to my collection."))
..()
/mob/living/carbon/human/dummy/travelling_trader/animal_hunter/check_item(var/obj/item/supplied_item) //item is likely to be in contents of whats supplied
/mob/living/carbon/human/dummy/travelling_trader/animal_hunter/check_item(obj/item/supplied_item) //item is likely to be in contents of whats supplied
for(var/atom/something in supplied_item.contents)
if(istype(something, requested_item))
qdel(something) //typically things holding mobs release the mob when the container is deleted, so delete the mob first here
return TRUE
return FALSE
/mob/living/carbon/human/dummy/travelling_trader/animal_hunter/give_reward(var/mob/giver) //the reward is actually given in a jar, because releasing it onto the station might be a bad idea
/mob/living/carbon/human/dummy/travelling_trader/animal_hunter/give_reward(mob/giver) //the reward is actually given in a jar, because releasing it onto the station might be a bad idea
var/obj/item/pet_carrier/bluespace/jar = new(get_turf(src))
var/chosen_animal = pickweight(possible_rewards)
var/mob/living/new_animal = new chosen_animal(jar)
@@ -223,6 +223,7 @@ mob/living/carbon/human/dummy/travelling_trader/animal_hunter/Initialize()
/obj/structure/reagent_dispensers/keg/quintuple_sec = 3)
/mob/living/carbon/human/dummy/travelling_trader/bartender/Initialize() //pick a subtype of ethanol that isn't found in the default set of the booze dispensers reagents
. = ..() // RETURN A HINT.
requested_item = pick(subtypesof(/datum/reagent/consumable/ethanol) - list(/datum/reagent/consumable/ethanol/beer,
/datum/reagent/consumable/ethanol/kahlua,
/datum/reagent/consumable/ethanol/whiskey,
@@ -242,7 +243,6 @@ mob/living/carbon/human/dummy/travelling_trader/animal_hunter/Initialize()
/datum/reagent/consumable/ethanol/triple_sec,
/datum/reagent/consumable/ethanol/sake,
/datum/reagent/consumable/ethanol/applejack))
..()
/mob/living/carbon/human/dummy/travelling_trader/bartender/check_item(var/obj/item/supplied_item) //you need to check its reagents
if(istype(supplied_item, /obj/item/reagent_containers))
@@ -17,10 +17,7 @@
var/isGlass = TRUE //Whether the 'bottle' is made of glass or not so that milk cartons dont shatter when someone gets hit by it
/obj/item/reagent_containers/food/drinks/on_reagent_change(changetype)
if (gulp_size < 5)
gulp_size = 5
else
gulp_size = max(round(reagents.total_volume / 5), 5)
gulp_size = max(round(reagents.total_volume / 5), 5)
/obj/item/reagent_containers/food/drinks/attack(mob/living/M, mob/user, def_zone)
if(!reagents || !reagents.total_volume)
@@ -52,10 +52,7 @@
/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/on_reagent_change(changetype)
cut_overlays()
if (gulp_size < 15)
gulp_size = 15
else
gulp_size = max(round(reagents.total_volume / 15), 15)
gulp_size = max(round(reagents.total_volume / 15), 15)
if (reagents.reagent_list.len > 0)
var/datum/reagent/largest_reagent = reagents.get_master_reagent()
@@ -308,7 +308,7 @@
tastes = list("cake" = 5, "sweetness" = 1, "clouds" = 1)
foodtype = GRAIN | DAIRY | SUGAR
obj/item/reagent_containers/food/snacks/store/cake/pound_cake
/obj/item/reagent_containers/food/snacks/store/cake/pound_cake
name = "pound cake"
desc = "A condensed cake made for filling people up quickly."
icon_state = "pound_cake"
@@ -54,7 +54,7 @@
H.visible_message("<span class='warning'>[H] is creamed by [src]!</span>", "<span class='userdanger'>You've been creamed by [src]!</span>")
playsound(H, "desceration", 50, TRUE)
if(!H.is_mouth_covered())
reagents.trans_to(H, 15, log = TRUE) //Cream pie combat
reagents.trans_to(H, 15, log = "creampie hit") //Cream pie combat
if(!H.creamed) // one layer at a time
H.add_overlay(creamoverlay)
H.creamed = TRUE
@@ -79,7 +79,7 @@ God bless America.
to_chat(user, "<span class='warning'>There's nothing to dissolve [I] in!</span>")
return
user.visible_message("<span class='notice'>[user] drops [I] into [src].</span>", "<span class='notice'>You dissolve [I] in [src].</span>")
I.reagents.trans_to(src, I.reagents.total_volume)
I.reagents.trans_to(src, I.reagents.total_volume, log = "pill into deep fryer")
qdel(I)
return
if(istype(I,/obj/item/clothing/head/mob_holder))
@@ -151,10 +151,13 @@
to_chat(user, "<span class='warning'>You need more space cleaner!</span>")
return TRUE
if(istype(O, /obj/item/soap))
var/obj/item/soap/P = O
if(istype(O, /obj/item/soap) || istype(O, /obj/item/reagent_containers/rag))
var/cleanspeed = 50
if(istype(O, /obj/item/soap))
var/obj/item/soap/used_soap = O
cleanspeed = used_soap.cleanspeed
user.visible_message("[user] starts to clean \the [src].", "<span class='notice'>You start to clean \the [src]...</span>")
if(do_after(user, P.cleanspeed, target = src))
if(do_after(user, cleanspeed, target = src))
user.visible_message("[user] has cleaned \the [src].", "<span class='notice'>You clean \the [src].</span>")
dirty = 0
update_icon()
@@ -21,7 +21,7 @@
)
result = /obj/item/reagent_containers/food/snacks/donut/chaos
datum/crafting_recipe/food/donut/meat
/datum/crafting_recipe/food/donut/meat
time = 15
name = "Meat donut"
reqs = list(
+18 -2
View File
@@ -115,13 +115,20 @@
return FALSE
var/valid = FALSE
var/list/checked = program_cache.Copy()
if(obj_flags & EMAGGED)
checked |= emag_programs
for(var/prog in checked)
var/list/P = prog
if(P["type"] == program_to_load)
valid = TRUE
break
if(obj_flags & EMAGGED) //split up into separate for loops instead of together so we can adminlog it
checked = emag_programs.Copy()
for(var/prog in checked)
var/list/P = prog
if(P["type"] == program_to_load)
valid = TRUE
log_game("[key_name(usr)] has loaded the restricted holodeck program [program_to_load]")
message_admins("[ADMIN_LOOKUPFLW(usr)] has loaded the restricted holodeck program [program_to_load]")
break
if(!valid)
return FALSE
@@ -134,6 +141,14 @@
nerf(obj_flags & EMAGGED)
obj_flags ^= EMAGGED
say("Safeties restored. Restarting...")
if(obj_flags & EMAGGED)
to_chat(usr,"<span class='warning'>You vastly increase projector power and override the safety and security protocols.</span>")
log_game("[key_name(usr)] has disabled safeties on the holodeck computer")
message_admins("[ADMIN_LOOKUPFLW(usr)] has disabled safeties on the holodeck computer")
else
to_chat(usr,"<span class='notice'>You restore the safeties to the holodeck.</span>")
log_game("[key_name(usr)] has reenabled safeties on the holodeck computer")
message_admins("[ADMIN_LOOKUPFLW(usr)] has reenabled safeties on the holodeck computer")
/obj/machinery/computer/holodeck/process()
if(damaged && prob(10))
@@ -179,6 +194,7 @@
to_chat(user, "<span class='warning'>You vastly increase projector power and override the safety and security protocols.</span>")
say("Warning. Automatic shutoff and derezzing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator.")
log_game("[key_name(user)] emagged the Holodeck Control Console")
message_admins("[ADMIN_LOOKUPFLW(user)] emagged the Holodeck Control Console.")
nerf(!(obj_flags & EMAGGED))
/obj/machinery/computer/holodeck/emp_act(severity)
+8 -10
View File
@@ -34,16 +34,14 @@
/obj/item/grown/cotton/attack_self(mob/user)
user.show_message("<span class='notice'>You pull some [cotton_name] out of the [name]!</span>", MSG_VISUAL)
var/seed_modifier = 0
if(seed)
seed_modifier = round(seed.potency / 25)
var/obj/item/stack/cotton = new cotton_type(user.loc, 1 + seed_modifier)
var/old_cotton_amount = cotton.amount
for(var/obj/item/stack/ST in user.loc)
if(ST != cotton && istype(ST, cotton_type) && ST.amount < ST.max_amount)
ST.attackby(cotton, user)
if(cotton.amount > old_cotton_amount)
to_chat(user, "<span class='notice'>You add the newly-formed [cotton_name] to the stack. It now contains [cotton.amount] [cotton_name].</span>")
var/cottonAmt = 1 + round(seed.potency / 25) // cotton inhand we're holding
for(var/obj/item/grown/cotton/ctn in user.loc) // cotton on the floor
if(ctn.type != type)
continue
cottonAmt += 1 + round(ctn.seed.potency / 25)
qdel(ctn)
new cotton_type(user.drop_location(), cottonAmt)
// above code stolen from grass
qdel(src)
//reinforced mutated variant
+35 -3
View File
@@ -92,6 +92,11 @@
return BULLET_ACT_HIT
else if(istype(Proj , /obj/item/projectile/energy/florayield))
return myseed.bullet_act(Proj)
else if(istype(Proj , /obj/item/projectile/energy/florarevolution))
if(myseed)
if(myseed.mutatelist.len > 0)
myseed.instability = (myseed.instability/2)
mutatespecie()
else
return ..()
@@ -384,7 +389,6 @@
/obj/machinery/hydroponics/proc/hardmutate()
mutate(4, 10, 2, 4, 50, 4, 10, 3)
/obj/machinery/hydroponics/proc/mutatespecie() // Mutagent produced a new plant!
if(!myseed || dead)
return
@@ -523,12 +527,13 @@
else if(istype(O, /obj/item/seeds) && !istype(O, /obj/item/seeds/sample))
if(!myseed)
if(istype(O, /obj/item/seeds/kudzu))
investigate_log("had Kudzu planted in it by [key_name(user)] at [AREACOORD(src)]","kudzu")
investigate_log("had Kudzu planted in it by [key_name(user)] at [AREACOORD(src)]", INVESTIGATE_BOTANY)
if(!user.transferItemToLoc(O, src))
return
to_chat(user, "<span class='notice'>You plant [O].</span>")
dead = FALSE
myseed = O
investigate_log("planting: [user] planted [O] with traits [english_list(myseed)] and reagents [english_list_assoc(myseed.reagents_add)] and potency [myseed.potency]", INVESTIGATE_BOTANY)
TRAY_NAME_UPDATE
age = 1
plant_health = myseed.endurance
@@ -600,7 +605,34 @@
desc = initial(desc)
weedlevel = 0 //Has a side effect of cleaning up those nasty weeds
update_icon()
else if(istype(O, /obj/item/gun/energy/floragun))
var/obj/item/gun/energy/floragun/flowergun = O
if(flowergun.cell.charge < flowergun.cell.maxcharge)
to_chat(user, "<span class='notice'>[flowergun] must be fully charged to lock in a mutation!</span>")
return
if(!myseed)
to_chat(user, "<span class='warning'>[src] is empty!</span>")
return
if(myseed.endurance <= 20)
to_chat(user, "<span class='warning'>[myseed.plantname] isn't hardy enough to sequence its mutation!</span>")
return
if(!myseed.mutatelist)
to_chat(user, "<span class='warning'>[myseed.plantname] has nothing else to mutate into!</span>")
return
else
var/list/fresh_mut_list = list()
for(var/muties in myseed.mutatelist)
var/obj/item/seeds/another_mut = new muties
fresh_mut_list[another_mut.plantname] = muties
var/locked_mutation = (input(user, "Select a mutation to lock.", "Plant Mutation Locks") as null|anything in sortList(fresh_mut_list))
if(!user.canUseTopic(src, BE_CLOSE) || !locked_mutation)
return
myseed.mutatelist = list(fresh_mut_list[locked_mutation])
myseed.endurance = (myseed.endurance/2)
flowergun.cell.use(flowergun.cell.charge)
flowergun.update_icon()
to_chat(user, "<span class='notice'>[myseed.plantname]'s mutation was set to [locked_mutation], depleting [flowergun]'s cell!</span>")
return
else
return ..()
+4 -2
View File
@@ -24,7 +24,7 @@
var/yield = 3 // Amount of growns created per harvest. If is -1, the plant/shroom/weed is never meant to be harvested.
var/potency = 10 // The 'power' of a plant. Generally effects the amount of reagent in a plant, also used in other ways.
var/growthstages = 6 // Amount of growth sprites the plant has.
var/instability = 5 //Chance that a plant will mutate in each stage of it's life.
var/instability = 5 //Chance that a plant will mutate in each stage of it's life.
var/rarity = 0 // How rare the plant is. Used for giving points to cargo when shipping off to CentCom.
var/list/mutatelist = list() // The type of plants that this plant can mutate into.
var/list/genes = list() // Plant genes are stored here, see plant_genes.dm for more info.
@@ -105,7 +105,7 @@
S.reagents_add = reagents_add.Copy() // Faster than grabbing the list from genes.
return S
obj/item/seeds/proc/is_gene_forbidden(typepath)
/obj/item/seeds/proc/is_gene_forbidden(typepath)
return (typepath in forbiddengenes)
@@ -188,6 +188,8 @@ obj/item/seeds/proc/is_gene_forbidden(typepath)
///The Number of products produced by the plant, typically the yield.
var/product_count = getYield()
parent.investigate_log("manual harvest by [key_name(user)] of [getYield()] of [src], with seed traits [english_list(genes)] and reagents_add [english_list_assoc(reagents_add)] and potency [potency].", INVESTIGATE_BOTANY)
while(t_amount < product_count)
var/obj/item/reagent_containers/food/snacks/grown/t_prod
if(instability >= 30 && (seed_flags & MUTATE_EARLY) && LAZYLEN(mutatelist) && prob(instability/3))
@@ -378,7 +378,7 @@ a creative player the means to solve many problems. Circuits are held inside an
// Checks if the target object is reachable. Useful for various manipulators and manipulator-like objects.
/obj/item/integrated_circuit/proc/check_target(atom/target, exclude_contents = FALSE, exclude_components = FALSE, exclude_self = FALSE)
/obj/item/integrated_circuit/proc/check_target(atom/target, exclude_contents = FALSE, exclude_components = FALSE, exclude_self = FALSE, exclude_outside = FALSE)
if(!target)
return FALSE
@@ -394,7 +394,7 @@ a creative player the means to solve many problems. Circuits are held inside an
if(target == assembly.battery)
return FALSE
if(target.Adjacent(acting_object) && isturf(target.loc))
if(!exclude_outside && target.Adjacent(acting_object) && isturf(target.loc))
return TRUE
if(!exclude_contents && (target in acting_object.GetAllContents()))
@@ -292,7 +292,7 @@
activate_pin(2)
// Required for making the connector port script work
obj/item/integrated_circuit/atmospherics/connector/portableConnectorReturnAir()
/obj/item/integrated_circuit/atmospherics/connector/portableConnectorReturnAir()
return air_contents
@@ -186,6 +186,8 @@
AM.forceMove(src)
/obj/item/integrated_circuit/manipulation/grabber/proc/drop(obj/item/AM, turf/T = drop_location())
if(!check_target(AM, FALSE, TRUE, TRUE, TRUE))
return
var/atom/A = get_object()
A.investigate_log("dropped ([AM]) from [src].", INVESTIGATE_CIRCUIT)
AM.forceMove(T)
@@ -137,6 +137,10 @@
//Shooting Code:
A.preparePixelProjectile(target, src)
A.fire()
if(ismob(loc.loc))
installed_gun.shoot_live_shot(loc.loc)
else
installed_gun.shoot_live_shot() //Shitcode, but we don't have much of a choice
log_attack("[assembly] [REF(assembly)] has fired [installed_gun].")
return A
@@ -60,3 +60,14 @@
/datum/keybinding/carbon/select_harm_intent/down(client/user)
user.mob?.a_intent_change(INTENT_HARM)
return TRUE
/datum/keybinding/carbon/give
hotkey_keys = list("CtrlG")
name = "Give_Item"
full_name = "Give item"
description = "Give the item you're currently holding"
/datum/keybinding/carbon/give/down(client/user)
var/mob/living/carbon/C = user.mob
C.give()
return TRUE
+12
View File
@@ -0,0 +1,12 @@
/datum/language/signlanguage
name = "Space Sign Language"
desc = "Those who cannot speak can learn this instead."
speech_verb = "signs"
whisper_verb = "gestures"
key = "9"
flags = TONGUELESS_SPEECH
syllables = list(".")
icon_state = "ssl"
default_priority = 90
@@ -522,7 +522,7 @@
max_charges = 1
item_flags = NEEDS_PERMIT | NOBLUDGEON
w_class = WEIGHT_CLASS_BULKY
force = 18
force = 15
/obj/item/ammo_casing/magic/hook
name = "hook"
@@ -536,11 +536,11 @@
icon_state = "hook"
icon = 'icons/obj/lavaland/artefacts.dmi'
pass_flags = PASSTABLE
damage = 25
armour_penetration = 100
damage = 15
armour_penetration = 10
knockdown = 5
damage_type = BRUTE
hitsound = 'sound/effects/splat.ogg'
knockdown = 30
var/chain
/obj/item/projectile/hook/fire(setAngle)
@@ -250,6 +250,8 @@
if(points)
if(I)
I.mining_points += points
if(usr.client)
usr.client.increment_progress("miner", points)
points = 0
else
to_chat(usr, "<span class='warning'>No ID detected.</span>")
-19
View File
@@ -20,25 +20,6 @@
data["totalPoints"] = points
return data
/obj/machinery/mineral/ore_redemption/ui_act(action, params)
if(..())
return
switch(action)
if("Claim")
var/mob/M = usr
var/obj/item/card/id/I = M.get_idcard(TRUE)
if(points)
if(I)
I.mining_points += points
if(usr.client)
usr.client.increment_progress("miner", points)
points = 0
else
to_chat(usr, "<span class='warning'>No ID detected.</span>")
else
to_chat(usr, "<span class='warning'>No points to claim.</span>")
return TRUE
/obj/machinery/point_bank/power_change()
..()
update_icon()
+50 -2
View File
@@ -146,10 +146,58 @@
if(dbflags & DB_FLAG_AGE_CONFIRMATION_INCOMPLETE) //they have not completed age gate
var/age_verification = age_gate()
if(age_verification != 1)
client.add_system_note("Automated-Age-Gate", "Failed automatic age gate process")
client.add_system_note("Automated-Age-Gate", "Failed automatic age gate process.")
//ban them and kick them
AddBan(client.ckey, client.computer_id, "SYSTEM BAN - Inputted date during join verification was under 18 years of age. Contact administration on discord for verification.", "SYSTEM", FALSE, null, client.address)
//parameters used by sql line, easier to read:
var/bantype_str = "ADMIN_PERMABAN"
var/reason = "SYSTEM BAN - Inputted date during join verification was under 18 years of age. Contact administration on discord for verification."
var/duration = -1
var/sql_ckey = sanitizeSQL(client.ckey)
var/computerid = client.computer_id
if(!computerid)
computerid = "0"
var/sql_computerid = sanitizeSQL(computerid)
var/ip = client.address
if(!ip)
ip = "0.0.0.0"
var/sql_ip = sanitizeSQL(ip)
//parameter not used as there's no job but i want to fill out all parameters for the insert line
var/sql_job
// these are typically the banning admin's, but it's the system so we leave them null, but they're still here for the sake of a full set of values
var/sql_a_ckey
var/sql_a_computerid
var/sql_a_ip
// record all admins and non-admins online at the time
var/who
for(var/client/C in GLOB.clients)
if(!who)
who = "[C]"
else
who += ", [C]"
var/adminwho
for(var/client/C in GLOB.admins)
if(!adminwho)
adminwho = "[C]"
else
adminwho += ", [C]"
var/sql = "INSERT INTO [format_table_name("ban")] (`bantime`,`server_ip`,`server_port`,`round_id`,`bantype`,`reason`,`job`,`duration`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]', '[bantype_str]', '[reason]', '[sql_job]', [(duration)?"[duration]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[sql_ckey]', '[sql_computerid]', INET_ATON('[sql_ip]'), '[sql_a_ckey]', '[sql_a_computerid]', INET_ATON('[sql_a_ip]'), '[who]', '[adminwho]')"
var/datum/DBQuery/query_add_ban = SSdbcore.NewQuery(sql)
qdel(query_add_ban)
// announce this
message_admins("[html_encode(client.ckey)] has been banned for failing the automatic age gate.")
send2irc("[html_encode(client.ckey)] has been banned for failing the automatic age gate.")
// removing the client disconnects them
qdel(client)
return FALSE
else
//they claim to be of age, so allow them to continue and update their flags
@@ -1,4 +1,4 @@
datum/sprite_accessory/caps
/datum/sprite_accessory/caps
icon = 'icons/mob/mutant_bodyparts.dmi'
color_src = HAIR
relevant_layers = list(BODY_ADJ_LAYER)
@@ -13,6 +13,10 @@
name = "Bald"
icon_state = "bald"
/datum/sprite_accessory/hair/adhara
name = "Adhara"
icon_state = "hair_adhara"
/datum/sprite_accessory/hair/afro
name = "Afro"
icon_state = "hair_afro"
@@ -29,6 +33,14 @@
name = "Ahoge"
icon_state = "hair_antenna"
/datum/sprite_accessory/hair/amazon
name = "Amazon"
icon_state = "hair_amazon"
/datum/sprite_accessory/hair/anita
name = "Anita"
icon_state = "hair_anita"
/datum/sprite_accessory/hair/balding
name = "Balding Hair"
icon_state = "hair_e"
@@ -61,6 +73,18 @@
name = "Beehive 2"
icon_state = "hair_beehive2"
/datum/sprite_accessory/hair/belenko
name = "Beleneko"
icon_state = "hair_belenko"
/datum/sprite_accessory/hair/belenkotied
name = "Belenko (Tied)"
icon_state = "hair_belenkotied"
/datum/sprite_accessory/hair/belle
name = "Belle"
icon_state = "hair_belle"
/datum/sprite_accessory/hair/bob
name = "Bob Hair"
icon_state = "hair_bob"
@@ -309,6 +333,10 @@
name = "Flow Hair"
icon_state = "hair_f"
/datum/sprite_accessory/hair/fluffy
name = "Fluffy"
icon_state = "hair_fluffy"
/datum/sprite_accessory/hair/fringetail
name = "Fringe Tail"
icon_state = "hair_fringetail"
@@ -365,6 +393,10 @@
name = "Hitop"
icon_state = "hair_hitop"
/datum/sprite_accessory/hair/inkling
name = "Inkling"
icon_state = "hair_inkling"
/datum/sprite_accessory/hair/jade
name = "Jade"
icon_state = "hair_jade"
@@ -373,6 +405,10 @@
name = "Jensen"
icon_state = "hair_jensen"
/datum/sprite_accessory/hair/jessica
name = "Jessica"
icon_state = "hair_jessica"
/datum/sprite_accessory/hair/joestar
name = "Joestar"
icon_state = "hair_joestar"
@@ -389,6 +425,10 @@
name = "Kusanagi"
icon_state = "hair_kusanagi"
/datum/sprite_accessory/hair/kleeia
name = "Kleeia"
icon_state = "hair_kleeia"
/datum/sprite_accessory/hair/long
name = "Long Hair 1"
icon_state = "hair_long"
@@ -445,6 +485,10 @@
name = "Mohawk"
icon_state = "hair_d"
/datum/sprite_accessory/hair/newyou
name = "New You"
icon_state = "hair_newyou"
/datum/sprite_accessory/hair/reversemohawk
name = "Mohawk (Reverse)"
icon_state = "hair_reversemohawk"
@@ -575,6 +619,10 @@
name = "Ponytail (Side) 4"
icon_state = "hair_sidetail4"
/datum/sprite_accessory/hair/sharptail
name = "Ponytail (Sharp)"
icon_state = "hair_sharptail"
/datum/sprite_accessory/hair/spikytail
name = "Ponytail (Spiky)"
icon_state = "hair_spikyponytail"
@@ -679,6 +727,26 @@
name = "Skinhead"
icon_state = "hair_skinhead"
/datum/sprite_accessory/hair/simple
name = "Simple"
icon_state = "hair_simple"
/datum/sprite_accessory/hair/skrellvshort
name = "Skrell Replicant (Very Short)"
icon_state = "hair_skrellvshort"
/datum/sprite_accessory/hair/skrellshort
name = "Skrell Replicant (Short)"
icon_state = "hair_skrellshort"
/datum/sprite_accessory/hair/skrell
name = "Skrell Replicant (Average)"
icon_state = "hair_skrell"
/datum/sprite_accessory/hair/skrelllong
name = "Skrell Replicant (Long)"
icon_state = "hair_skrelllong"
/datum/sprite_accessory/hair/sleaze
name = "Sleaze"
icon_state = "hair_sleaze"
@@ -699,6 +767,10 @@
name = "Spiky 3"
icon_state = "hair_spiky2"
/datum/sprite_accessory/hair/supernova
name = "Supernova"
icon_state = "hair_supernova"
/datum/sprite_accessory/hair/swept
name = "Swept Back Hair"
icon_state = "hair_swept"
@@ -743,6 +815,10 @@
name = "Trimmed (Flat)"
icon_state = "hair_trimflat"
/datum/sprite_accessory/hair/twincurls
name = "Twincurls"
icon_state = "hair_twincurls"
/datum/sprite_accessory/hair/twintails
name = "Twintails"
icon_state = "hair_twintail"
@@ -787,6 +863,10 @@
name = "Very Short Over Eye (Alt)"
icon_state = "hair_veryshortovereyealternate"
/datum/sprite_accessory/hair/vivi
name = "Vivi"
icon_state = "hair_vivi"
/datum/sprite_accessory/hair/volaju
name = "Volaju"
icon_state = "hair_volaju"
@@ -11,6 +11,11 @@
// please make sure they're sorted alphabetically and categorized
/datum/sprite_accessory/underwear/socks/garter
name = "Garter"
icon_state = "garter"
has_color = TRUE
/datum/sprite_accessory/underwear/socks/socks_knee
name = "Knee-high"
icon_state = "socks_knee"
@@ -83,6 +88,10 @@
name = "Pantyhose"
icon_state = "pantyhose"
/datum/sprite_accessory/underwear/socks/pantyhose_ripped
name = "Pantyhose - Ripped"
icon_state = "pantyhose_ripped"
/datum/sprite_accessory/underwear/socks/socks_short
name = "Short"
icon_state = "socks_short"
@@ -757,7 +757,7 @@
icon_state = "husky"
matrixed_sections = MATRIX_RED_GREEN
datum/sprite_accessory/tails/mam_tails/insect
/datum/sprite_accessory/tails/mam_tails/insect
name = "Insect"
icon_state = "insect"
matrixed_sections = MATRIX_RED
@@ -343,6 +343,20 @@
icon_state = "fishnet_body"
gender = FEMALE
/datum/sprite_accessory/underwear/top/shibari
name = "Shibari Ropes"
icon_state = "shibari"
gender = FEMALE
has_color = TRUE
covers_chest = FALSE
/datum/sprite_accessory/underwear/top/shibari_sleeved
name = "Shibari Ropes - sleeves"
icon_state = "shibari_sleeves"
gender = FEMALE
has_color = TRUE
covers_chest = FALSE
/datum/sprite_accessory/underwear/top/swimsuit
name = "Swimsuit Top"
icon_state = "bra_swimming"
+4 -2
View File
@@ -556,7 +556,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
var/list/dest = list() //List of possible destinations (mobs)
var/target = null //Chosen target.
dest += getpois(mobs_only=1) //Fill list, prompt user with list
dest += getpois(mobs_only = TRUE) //Fill list, prompt user with list
target = input("Please, select a player!", "Jump to Mob", null, null) as null|anything in dest
if (!target)//Make sure we actually have a target
@@ -893,7 +893,9 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if (!eye_name)
return
var/mob/mob_eye = creatures[eye_name]
do_observe(creatures[eye_name])
/mob/dead/observer/proc/do_observe(mob/mob_eye)
//Istype so we filter out points of interest that are not mobs
if(client && mob_eye && istype(mob_eye))
client.eye = mob_eye
+28 -6
View File
@@ -1,5 +1,6 @@
/datum/orbit_menu
var/mob/dead/observer/owner
var/auto_observe = FALSE
/datum/orbit_menu/New(mob/dead/observer/new_owner)
if(!istype(new_owner))
@@ -10,6 +11,7 @@
return GLOB.observer_state
/datum/orbit_menu/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
ui = new(user, src, "Orbit")
ui.open()
@@ -18,15 +20,35 @@
if (..())
return
if (action == "orbit")
var/ref = params["ref"]
var/atom/movable/poi = (locate(ref) in GLOB.mob_list) || (locate(ref) in GLOB.poi_list)
if (poi != null)
switch(action)
if ("orbit")
var/ref = params["ref"]
var/atom/movable/poi = (locate(ref) in GLOB.mob_list) || (locate(ref) in GLOB.poi_list)
if (poi == null)
. = TRUE
return
owner.ManualFollow(poi)
owner.reset_perspective(null)
if (auto_observe)
owner.do_observe(poi)
. = TRUE
if ("refresh")
update_static_data(owner, ui)
. = TRUE
if ("toggle_observe")
auto_observe = !auto_observe
if (auto_observe && owner.orbit_target)
owner.do_observe(owner.orbit_target)
else
owner.reset_perspective(null)
/datum/orbit_menu/ui_data(mob/user)
var/list/data = list()
data["auto_observe"] = auto_observe
return data
/datum/orbit_menu/ui_static_data(mob/user)
var/list/data = list()
var/list/alive = list()
var/list/antagonists = list()
var/list/dead = list()
@@ -34,7 +56,7 @@
var/list/misc = list()
var/list/npcs = list()
var/list/pois = getpois(skip_mindless = 1)
var/list/pois = getpois(skip_mindless = TRUE, specify_dead_role = FALSE)
for (var/name in pois)
var/list/serialized = list()
serialized["name"] = name
@@ -80,7 +102,7 @@
data["npcs"] = npcs
return data
/datum/orbit_menu/ui_assets()
. = ..() || list()
. += get_asset_datum(/datum/asset/simple/orbit)
+1 -1
View File
@@ -1,6 +1,6 @@
//This is the proc for gibbing a mob. Cannot gib ghosts.
//added different sort of gibs and animations. N
/mob/proc/gib()
/mob/proc/gib(no_brain, no_organs, no_bodyparts, datum/explosion/was_explosion)
return
//This is the proc for turning a mob into ash. Mostly a copy of gib code (above).
@@ -4,7 +4,6 @@
gender = FEMALE //All xenos are girls!!
dna = null
faction = list(ROLE_ALIEN)
ventcrawler = VENTCRAWLER_ALWAYS
sight = SEE_MOBS
see_in_dark = 4
verb_say = "hisses"
@@ -12,6 +11,10 @@
bubble_icon = "alien"
type_of_meat = /obj/item/reagent_containers/food/snacks/meat/slab/xeno
/// Whether they can ventcrawl; this is set individually for 'humanoid' and 'royal' types
/// 'royal' types (Praetorian, Queen) cannot ventcrawl
var/can_ventcrawl
/// How much brute damage without armor piercing they do against mobs in melee
var/meleeSlashHumanPower = 20
/// How much power they have for DefaultCombatKnockdown when attacking humans
@@ -19,7 +22,6 @@
/// How much brute damage they do to simple animals
var/meleeSlashSAPower = 35
var/obj/item/card/id/wear_id = null // Fix for station bounced radios -- Skie
var/has_fine_manipulation = 0
var/move_delay_add = 0 // movement delay to add
@@ -40,6 +42,9 @@
create_internal_organs()
if(can_ventcrawl)
AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
. = ..()
/mob/living/carbon/alien/create_internal_organs()
@@ -20,6 +20,8 @@
bodyparts = list(/obj/item/bodypart/chest/alien, /obj/item/bodypart/head/alien, /obj/item/bodypart/l_arm/alien,
/obj/item/bodypart/r_arm/alien, /obj/item/bodypart/r_leg/alien, /obj/item/bodypart/l_leg/alien)
can_ventcrawl = TRUE
//This is fine right now, if we're adding organ specific damage this needs to be updated
/mob/living/carbon/alien/humanoid/Initialize()
@@ -2,7 +2,6 @@
//Common stuffs for Praetorian and Queen
icon = 'icons/mob/alienqueen.dmi'
status_flags = 0
ventcrawler = VENTCRAWLER_NONE //pull over that ass too fat
unique_name = 0
pixel_x = -16
bubble_icon = "alienroyal"
@@ -10,6 +9,7 @@
layer = LARGE_MOB_LAYER //above most mobs, but below speechbubbles
pressure_resistance = 200 //Because big, stompy xenos should not be blown around like paper.
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/xeno = 20, /obj/item/stack/sheet/animalhide/xeno = 3)
can_ventcrawl = FALSE
meleeKnockdownPower = 125
meleeSlashHumanPower = 30
@@ -11,6 +11,8 @@
maxHealth = 25
health = 25
can_ventcrawl = TRUE
var/amount_grown = 0
var/max_grown = 100
var/time_of_birth
+25
View File
@@ -434,6 +434,9 @@
legcuffed.forceMove(drop_location())
legcuffed = null
I.dropped(src)
if(istype(I, /obj/item/restraints/legcuffs))
var/obj/item/restraints/legcuffs/lgcf = I
lgcf.on_removed()
update_inv_legcuffed()
return
else
@@ -1229,3 +1232,25 @@
*/
/mob/living/carbon/proc/get_biological_state()
return BIO_FLESH_BONE
/mob/living/carbon/altattackby(obj/item/W, mob/living/carbon/user, params)
if(user.incapacitated() || !user.Adjacent(src))
return FALSE
if(W && user.a_intent == INTENT_HELP && W.can_give())
user.give()
return TRUE
/mob/living/carbon/verb/give_verb()
set src in oview(1)
set category = "IC"
set name = "Give"
if(usr.incapacitated() || !usr.Adjacent(src))
return
if(!usr.get_active_held_item()) // Let me know if this has any problems -Yota
return
var/obj/item/I = usr.get_active_held_item()
var/mob/living/carbon/C = usr
if(I.can_give())
C.give()
+9 -4
View File
@@ -17,7 +17,7 @@
if(SSticker.mode)
SSticker.mode.check_win() //Calls the rounds wincheck, mainly for wizard, malf, and changeling now
/mob/living/carbon/gib(no_brain, no_organs, no_bodyparts)
/mob/living/carbon/gib(no_brain, no_organs, no_bodyparts, datum/explosion/was_explosion)
var/atom/Tsec = drop_location()
for(var/mob/M in src)
if(M in stomach_contents)
@@ -27,7 +27,7 @@
"<span class='danger'>You burst out of [src]!</span>")
..()
/mob/living/carbon/spill_organs(no_brain, no_organs, no_bodyparts)
/mob/living/carbon/spill_organs(no_brain, no_organs, no_bodyparts, datum/explosion/was_explosion)
var/atom/Tsec = drop_location()
if(!no_bodyparts)
if(no_organs)//so the organs don't get transfered inside the bodyparts we'll drop.
@@ -41,6 +41,8 @@
qdel(O) //so the brain isn't transfered to the head when the head drops.
continue
if(!(O.organ_flags & ORGAN_NO_DISMEMBERMENT) && check_zone(O.zone) == BODY_ZONE_CHEST)
if(was_explosion)
LAZYADD(O.acted_explosions, was_explosion.explosion_id)
O.Remove()
O.forceMove(Tsec)
O.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
@@ -50,13 +52,16 @@
if(I.organ_flags & ORGAN_NO_DISMEMBERMENT || (no_brain && istype(I, /obj/item/organ/brain)) || (no_organs && !istype(I, /obj/item/organ/brain)))
qdel(I)
continue
if(was_explosion)
LAZYADD(I.acted_explosions, was_explosion.explosion_id)
I.Remove()
I.forceMove(Tsec)
I.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
/mob/living/carbon/spread_bodyparts()
/mob/living/carbon/spread_bodyparts(no_brain, no_organs, datum/explosion/was_explosion)
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
if(was_explosion)
LAZYADD(BP.acted_explosions, was_explosion.explosion_id)
BP.drop_limb()
BP.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
+19 -13
View File
@@ -281,11 +281,13 @@
dropItemToGround(pocket_item)
if(!usr.can_hold_items() || !usr.put_in_hands(pocket_item))
pocket_item.forceMove(drop_location())
log_combat(usr, src, "pickpocketed of item: [pocket_item]")
else
if(place_item)
if(place_item.mob_can_equip(src, usr, pocket_id, FALSE, TRUE))
usr.temporarilyRemoveItemFromInventory(place_item, TRUE)
equip_to_slot(place_item, pocket_id, TRUE)
log_combat(usr, src, "placed item [place_item] onto")
//do nothing otherwise
// Update strip window
@@ -293,8 +295,9 @@
show_inv(usr)
else
// Display a warning if the user mocks up
if (!strip_silence)
if(!strip_silence)
to_chat(src, "<span class='warning'>You feel your [pocket_side] pocket being fumbled with!</span>")
log_combat(usr, src, "failed to [pocket_item ? "pickpocket item [pocket_item] from" : "place item [place_item] onto "]")
if(usr.canUseTopic(src, BE_CLOSE, NO_DEXTERY, null, FALSE))
// separate from first canusetopic
@@ -938,43 +941,43 @@
admin_ticket_log(src, msg)
/mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user)
if(pulling == target && grab_state >= GRAB_AGGRESSIVE && stat == CONSCIOUS)
var/GS_needed = istype(target, /mob/living/silicon/pai)? GRAB_PASSIVE : GRAB_AGGRESSIVE
if(pulling == target && grab_state >= GS_needed && stat == CONSCIOUS)
//If they dragged themselves and we're currently aggressively grabbing them try to piggyback
if(user == target && can_piggyback(target))
piggyback(target)
return
//If you dragged them to you and you're aggressively grabbing try to fireman carry them
else if(user != target)
else if(user == src)
if(user.a_intent == INTENT_GRAB)
fireman_carry(target)
return
. = ..()
//src is the user that will be carrying, target is the mob to be carried
/mob/living/carbon/human/proc/can_piggyback(mob/living/carbon/target)
return (istype(target) && target.stat == CONSCIOUS)
/mob/living/carbon/human/proc/can_piggyback(mob/living/target)
return (iscarbon(target) || ispAI(target)) && target.stat == CONSCIOUS
/mob/living/carbon/human/proc/can_be_firemanned(mob/living/carbon/target)
return (ishuman(target) && !CHECK_MOBILITY(target, MOBILITY_STAND))
return (ishuman(target) && !CHECK_MOBILITY(target, MOBILITY_STAND)) || ispAI(target)
/mob/living/carbon/human/proc/fireman_carry(mob/living/carbon/target)
var/carrydelay = 50 //if you have latex you are faster at grabbing
var/skills_space = "" //cobby told me to do this
if(HAS_TRAIT(src, TRAIT_QUICKER_CARRY))
carrydelay = 30
skills_space = "expertly"
skills_space = "expertly "
else if(HAS_TRAIT(src, TRAIT_QUICK_CARRY))
carrydelay = 40
skills_space = "quickly"
skills_space = "quickly "
if(can_be_firemanned(target) && !incapacitated(FALSE, TRUE))
visible_message("<span class='notice'>[src] starts [skills_space] lifting [target] onto their back..</span>",
visible_message("<span class='notice'>[src] starts [skills_space]lifting [target] onto their back..</span>",
//Joe Medic starts quickly/expertly lifting Grey Tider onto their back..
"<span class='notice'>[carrydelay < 35 ? "Using your gloves' nanochips, you" : "You"] [skills_space] start to lift [target] onto your back[carrydelay == 40 ? ", while assisted by the nanochips in your gloves.." : "..."]</span>")
"<span class='notice'>[carrydelay < 35 ? "Using your gloves' nanochips, you" : "You"] [skills_space]start to lift [target] onto your back[carrydelay == 40 ? ", while assisted by the nanochips in your gloves.." : "..."]</span>")
//(Using your gloves' nanochips, you/You) ( /quickly/expertly) start to lift Grey Tider onto your back(, while assisted by the nanochips in your gloves../...)
if(do_after(src, carrydelay, TRUE, target))
//Second check to make sure they're still valid to be carried
if(can_be_firemanned(target) && !incapacitated(FALSE, TRUE))
target.set_resting(FALSE, TRUE)
buckle_mob(target, TRUE, TRUE, 90, 1, 0, TRUE)
return
visible_message("<span class='warning'>[src] fails to fireman carry [target]!")
@@ -992,13 +995,13 @@
if(target.incapacitated(FALSE, TRUE) || incapacitated(FALSE, TRUE))
target.visible_message("<span class='warning'>[target] can't hang onto [src]!</span>")
return
buckle_mob(target, TRUE, TRUE, FALSE, 1, 2, FALSE)
buckle_mob(target, TRUE, TRUE, 0, 1, 2, FALSE)
else
visible_message("<span class='warning'>[target] fails to climb onto [src]!</span>")
else
to_chat(target, "<span class='warning'>You can't piggyback ride [src] right now!</span>")
/mob/living/carbon/human/buckle_mob(mob/living/target, force = FALSE, check_loc = TRUE, lying_buckle = FALSE, hands_needed = 0, target_hands_needed = 0, fireman = FALSE)
/mob/living/carbon/human/buckle_mob(mob/living/target, force = FALSE, check_loc = TRUE, lying_buckle = 0, hands_needed = 0, target_hands_needed = 0, fireman = FALSE)
if(!force)//humans are only meant to be ridden through piggybacking and special cases
return
if(!is_type_in_typecache(target, can_ride_typecache))
@@ -1010,6 +1013,9 @@
riding_datum.ride_check_rider_restrained = TRUE
if(buckled_mobs && ((target in buckled_mobs) || (buckled_mobs.len >= max_buckled_mobs)) || buckled)
return
if(istype(target, /mob/living/silicon/pai))
hands_needed = 1
target_hands_needed = 0
var/equipped_hands_self
var/equipped_hands_target
if(hands_needed)
@@ -71,7 +71,7 @@
var/list/datum/bioware = list()
var/creamed = FALSE //to use with creampie overlays
var/static/list/can_ride_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/simple_animal/parrot))
var/static/list/can_ride_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/simple_animal/parrot, /mob/living/silicon/pai))
var/lastpuke = 0
var/account_id
var/last_fire_update
@@ -95,9 +95,10 @@
parry_efficiency_considered_successful = 0.01
parry_efficiency_to_counterattack = 0.01
parry_max_attacks = 3
parry_cooldown = 30
parry_failed_stagger_duration = 0
parry_failed_clickcd_duration = 0.4
parry_cooldown = 3 SECONDS
parry_failed_cooldown_duration = 1.5 SECONDS
parry_failed_stagger_duration = 1 SECONDS
parry_failed_clickcd_duration = 0.4 SECONDS
parry_data = list( // yeah it's snowflake
"UNARMED_PARRY_STAGGER" = 3 SECONDS,
@@ -135,16 +136,16 @@
parry_imperfect_falloff_percent = 20
parry_efficiency_perfect = 100
parry_efficiency_perfect_override = list(
ATTACK_TYPE_PROJECTILE_TEXT = 60,
TEXT_ATTACK_TYPE_PROJECTILE = 60,
)
parry_efficiency_considered_successful = 0.01
parry_efficiency_to_counterattack = 0.01
parry_max_attacks = INFINITY
parry_failed_cooldown_duration = 1.5 SECONDS
parry_failed_stagger_duration = 0
parry_cooldown = 0
parry_failed_clickcd_duration = 0.8
parry_failed_cooldown_duration = 3 SECONDS
parry_failed_stagger_duration = 2 SECONDS
parry_cooldown = 3 SECONDS
parry_failed_clickcd_duration = 0.8 SECONDS
parry_data = list( // yeah it's snowflake
"UNARMED_PARRY_STAGGER" = 3 SECONDS,
@@ -1425,8 +1425,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh)
var/punchwoundbonus = user.dna.species.punchwoundbonus
var/puncherstam = user.getStaminaLoss()
var/puncherbrute = user.getBruteLoss()
var/punchedstam = target.getStaminaLoss()
var/punchedbrute = target.getBruteLoss()
@@ -1434,7 +1432,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(!SEND_SIGNAL(target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
damage *= 1.2
if(!CHECK_MOBILITY(user, MOBILITY_STAND))
damage *= 0.8
damage *= 0.65
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
damage *= 0.8
//END OF CITADEL CHANGES
@@ -1446,19 +1444,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(!affecting) //Maybe the bodypart is missing? Or things just went wrong..
affecting = target.get_bodypart(BODY_ZONE_CHEST) //target chest instead, as failsafe. Or hugbox? You decide.
var/miss_chance = 100//calculate the odds that a punch misses entirely. considers stamina and brute damage of the puncher. punches miss by default to prevent weird cases
if(attackchain_flags & ATTACK_IS_PARRY_COUNTERATTACK)
miss_chance = 0
else
if(user.dna.species.punchdamagelow)
if(atk_verb == ATTACK_EFFECT_KICK) //kicks never miss (provided your species deals more than 0 damage)
miss_chance = 0
else if(HAS_TRAIT(user, TRAIT_PUGILIST)) //pugilists, being good at Punching People, also never miss
miss_chance = 0
else
miss_chance = min(10 + max(puncherstam * 0.5, puncherbrute * 0.5), 100) //probability of miss has a base of 10, and modified based on half brute total. Capped at max 100 to prevent weirdness in prob()
if(!damage || !affecting || prob(miss_chance))//future-proofing for species that have 0 damage/weird cases where no zone is targeted
if(!damage || !affecting)//future-proofing for species that have 0 damage/weird cases where no zone is targeted
playsound(target.loc, user.dna.species.miss_sound, 25, TRUE, -1)
target.visible_message("<span class='danger'>[user]'s [atk_verb] misses [target]!</span>", \
"<span class='danger'>You avoid [user]'s [atk_verb]!</span>", "<span class='hear'>You hear a swoosh!</span>", null, COMBAT_MESSAGE_RANGE, null, \
@@ -1796,12 +1782,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
"<span class='warning'>[M] attempted to touch you!</span>", target = M, \
target_message = "<span class='warning'>You attempted to touch [H]!</span>")
return TRUE
if(M == H)
althelp(M, H, attacker_style)
return TRUE
switch(M.a_intent)
if(INTENT_HELP)
if(M == H)
althelp(M, H, attacker_style)
return TRUE
return FALSE
if(INTENT_DISARM)
altdisarm(M, H, attacker_style)
return TRUE
@@ -1987,6 +1971,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.show_message("<span class='notice'>The radiation beam dissipates harmlessly through your body.</span>")
if(/obj/item/projectile/energy/florayield)
H.show_message("<span class='notice'>The radiation beam dissipates harmlessly through your body.</span>")
if(/obj/item/projectile/energy/florarevolution)
H.show_message("<span class='notice'>The radiation beam dissipates harmlessly through your body.</span>")
/datum/species/proc/bullet_act(obj/item/projectile/P, mob/living/carbon/human/H)
// called before a projectile hit
@@ -30,10 +30,7 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
/datum/species/dwarf/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
var/dwarf_hair = pick("Beard (Dwarf)", "Beard (Very Long)", "Beard (Long)") //beard roullette
var/mob/living/carbon/human/H = C
H.facial_hair_style = dwarf_hair
H.update_hair()
H.AddElement(/datum/element/dwarfism, COMSIG_SPECIES_LOSS, src)
RegisterSignal(C, COMSIG_MOB_SAY, .proc/handle_speech) //We register handle_speech is being used.
@@ -801,11 +801,11 @@
/datum/species/golem/plastic/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
C.ventcrawler = VENTCRAWLER_NUDE
C.AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
/datum/species/golem/plastic/on_species_loss(mob/living/carbon/C)
. = ..()
C.ventcrawler = initial(C.ventcrawler)
C.RemoveElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
/datum/species/golem/bronze
name = "Bronze Golem"
@@ -64,6 +64,11 @@
H.show_message("<span class='userdanger'>The radiation beam singes you!</span>")
if(/obj/item/projectile/energy/florayield)
H.adjust_nutrition(30, NUTRITION_LEVEL_FULL)
if(/obj/item/projectile/energy/florarevolution)
H.show_message("<span class='notice'>The radiation beam leaves you feeling disoriented!</span>")
H.Dizzy(15)
H.emote("flip")
H.emote("spin")
/datum/species/pod/pseudo_weak
name = "Anthromorphic Plant"
@@ -169,7 +169,7 @@
if(istype(H, /mob/living/simple_animal))
var/mob/living/simple_animal/SA = H
if((human_caster.blood_volume <= (BLOOD_VOLUME_BAD*human_caster.blood_ratio)) || (ventcrawl_nude_only && length(human_caster.get_equipped_items(include_pockets = TRUE))))
SA.ventcrawler = FALSE
SA.RemoveElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
if(transfer_name)
H.name = human_caster.name
@@ -159,3 +159,57 @@
/mob/living/carbon/proc/get_holding_bodypart_of_item(obj/item/I)
var/index = get_held_index_of_item(I)
return index && hand_bodyparts[index]
/**
* Proc called when giving an item to another player
*
* This handles creating an alert and adding an overlay to it
*/
/mob/living/carbon/proc/give(target)
var/obj/item/receiving = get_active_held_item()
if(!receiving)
to_chat(src, "<span class='warning'>You're not holding anything to give!</span>")
return
visible_message("<span class='notice'>[src] is offering [receiving]</span>", \
"<span class='notice'>You offer [receiving]</span>", null, 2)
var/mob/living/carbon/targets = list()
if(!target)
for(var/mob/living/carbon/C in orange(1, src))
if(!CanReach(C))
return
targets += C
else
targets += target
if(!targets)
return
for(var/mob/living/carbon/C in targets)
var/obj/screen/alert/give/G = C.throw_alert("[src]", /obj/screen/alert/give)
if(!G)
return
G.setup(C, src, receiving)
/**
* Proc called when the player clicks the give alert
*
* Handles checking if the player taking the item has open slots and is in range of the giver
* Also deals with the actual transferring of the item to the players hands
* Arguments:
* * giver - The person giving the original item
* * I - The item being given by the giver
*/
/mob/living/carbon/proc/take(mob/living/carbon/giver, obj/item/I)
clear_alert("[giver]")
if(get_dist(src, giver) > 1)
to_chat(src, "<span class='warning'>[giver] is out of range! </span>")
return
if(!I || giver.get_active_held_item() != I)
to_chat(src, "<span class='warning'>[giver] is no longer holding the item they were offering! </span>")
return
if(!get_empty_held_indexes())
to_chat(src, "<span class='warning'>You have no empty hands!</span>")
return
if(!giver.temporarilyRemoveItemFromInventory(I))
visible_message("<span class='notice'>[src] tries to hand over [I] but it's stuck to them....", \
"<span class'notice'> You make a fool of yourself trying to give away an item stuck to your hands")
return
put_in_hands(I)
@@ -6,7 +6,6 @@
icon_state = ""
gender = NEUTER
pass_flags = PASSTABLE
ventcrawler = VENTCRAWLER_NUDE
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/monkey = 5, /obj/item/stack/sheet/animalhide/monkey = 1)
type_of_meat = /obj/item/reagent_containers/food/snacks/meat/slab/monkey
@@ -29,6 +28,8 @@
create_bodyparts()
create_internal_organs()
AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_NUDE)
. = ..()
if (cubespawned)

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