Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into Ghommie-cit765
This commit is contained in:
@@ -142,3 +142,51 @@ GLOBAL_LIST(round_end_notifiees)
|
||||
log_admin("[sender.friendly_name] has added [params] to the current round's bunker bypass list.")
|
||||
message_admins("[sender.friendly_name] has added [params] to the current round's bunker bypass list.")
|
||||
return "[params] has been added to the current round's bunker bypass list."
|
||||
|
||||
// More (silly) chat commands citadel added.
|
||||
/datum/tgs_chat_command/wheelofsalt
|
||||
name = "wheelofsalt"
|
||||
help_text = "What are Citadel Station 13 players salting about today? Spin the wheel and find out!"
|
||||
|
||||
/datum/tgs_chat_command/wheelofsalt/Run(datum/tgs_chat_user/sender, params)
|
||||
var/saltresult = "The wheel of salt [pick("clatters","screams","vibrates","clanks","resonates","groans","moans","squeaks","emits a[pick(" god-forsaken"," lewd"," creepy"," generic","n orgasmic"," demonic")] [pick("airhorn","bike horn","trumpet","clown","latex","vore","dog","laughing")] noise")] as it spins violently... And it seems the salt of the day is the "
|
||||
var/saltprimarysubject = "[pick("combat","medical","grab","furry","wall","orgasm","cat","ERP","lizard","dog","latex","vision cone","atmospherics","table","chem","vore","dogborg","Skylar Lineman","Mekhi Anderson","Peppermint","rework","cum","dick","cockvore","Medihound","sleeper","belly sleeper","door wires","flightsuit","coder privilege","Developer abuse","ban reason","github self merge","red panda","beret","male catgirl","powergame","hexacrocin","Discord server","Clitadel","Cargonia","Solarian Republic","Main and RP merger","bluespace","salt","chem dispenser theft","Botany","moth","BWOINK","anal vore","stamina","Mason Jakops","mining","noodle","milf","Lavaland","Necropolis","Ashwalker","Chase Redtail","Drew Mint","Pavel Marsk","Joker Amari","Durgit","chaplain","Antag","nanite","Syndicate","Nar-Sie","Ratvar","Cult","maint","Foam-Force","AI","cyborg","ghost","clockwork","cyberpunk","vaporwave","Clown","Leon Beech","Mime","security","research","Megafauna","Bubblegum","Ash Drake","Legion","Colossus","White Shuttle","Changeling","Cowboy","Space Ninja","Poly","Revolutionary","Skyrim","forbidden fruits","xenomorph","blob","Nuclear Operative","crossdressing")]"
|
||||
var/saltsecondarysubject = "[pick("rework","changes","r34","ban","removal","addition","leak","proposal","fanart","introduction","tabling","ERP","bikeshedding","crossdressing","sprites","semen keg","argument","theft","nerf","screeching","salt","creampie","lewding","murder","kissing","marriage","replacement","fucking","ship","netflix adaptation","dance","remaster","system","voyeur","decoration","pre-order","bukkake","seduction","worship","gangbang","handholding")]"
|
||||
if(prob(10))
|
||||
saltresult += "@here for your salt, all day every day"
|
||||
if(prob(1))
|
||||
saltresult += " @everyone gets some salt this time too"
|
||||
else
|
||||
saltresult += "[saltprimarysubject] [saltsecondarysubject]"
|
||||
return "[saltresult]!"
|
||||
|
||||
/datum/tgs_chat_command/valentine
|
||||
name = "valentine"
|
||||
help_text = "Get a random flirt line."
|
||||
|
||||
/datum/tgs_chat_command/valentine/Run(datum/tgs_chat_user/sender, params)
|
||||
return "[pick(GLOB.flirts)]"
|
||||
|
||||
/datum/tgs_chat_command/despacito
|
||||
name = "despacito" //someone please high effort this sometime and make it a full on ytdl search
|
||||
help_text = "This is so sad."
|
||||
|
||||
/datum/tgs_chat_command/despacito/Run()
|
||||
return "https://www.youtube.com/watch?v=kJQP7kiw5Fk"
|
||||
|
||||
/datum/tgs_chat_command/poly
|
||||
name = "poly"
|
||||
help_text = "The Lewder, more applicable Poly speak for Citadel Station 13."
|
||||
var/list/speech_buffer
|
||||
|
||||
/datum/tgs_chat_command/poly/Run()
|
||||
LAZYINITLIST(speech_buffer) //I figure this is just safe to do for everything at this point
|
||||
if(length(speech_buffer)) //Let's not look up the whole json EVERY TIME, just the first time.
|
||||
return "[pick(speech_buffer)]"
|
||||
else
|
||||
var/json_file = file("data/npc_saves/Poly.json")
|
||||
if(!fexists(json_file))
|
||||
return "**BAWWWWWK!** LEAVE THE HEADSET! ***BAWKKKKK!!***"
|
||||
var/list/json = json_decode(file2text(json_file))
|
||||
speech_buffer = json["phrases"]
|
||||
return "[pick(speech_buffer)]"
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
var/passive_blood_drain = -0.1 //The amount of blood we loose each bloodsucker life() tick
|
||||
// LISTS
|
||||
var/static/list/defaultTraits = list (TRAIT_STABLEHEART, TRAIT_NOBREATH, TRAIT_SLEEPIMMUNE, TRAIT_NOCRITDAMAGE, TRAIT_RESISTCOLD, TRAIT_RADIMMUNE, TRAIT_NIGHT_VISION, \
|
||||
TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_AGEUSIA, TRAIT_COLDBLOODED, TRAIT_NONATURALHEAL, TRAIT_NOMARROW, TRAIT_NOPULSE, TRAIT_VIRUSIMMUNE)
|
||||
TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_AGEUSIA, TRAIT_COLDBLOODED, TRAIT_NONATURALHEAL, TRAIT_NOMARROW, TRAIT_NOPULSE, TRAIT_VIRUSIMMUNE, TRAIT_NODECAP, TRAIT_NOGUT)
|
||||
|
||||
/datum/antagonist/bloodsucker/on_gain()
|
||||
SSticker.mode.bloodsuckers |= owner // Add if not already in here (and you might be, if you were picked at round start)
|
||||
|
||||
@@ -43,10 +43,14 @@
|
||||
to_chat(owner, "<span class='warning'>Lesser beings require a tighter grip.</span>")
|
||||
return FALSE
|
||||
// Bloodsuckers:
|
||||
else if(iscarbon(target) && target.mind && target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
|
||||
if(display_error)
|
||||
to_chat(owner, "<span class='warning'>Other Bloodsuckers will not fall for your subtle approach.</span>")
|
||||
return FALSE
|
||||
else if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
if(!H.can_inject(owner, TRUE, BODY_ZONE_CHEST))
|
||||
return FALSE
|
||||
if(target.mind && target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
|
||||
if(display_error)
|
||||
to_chat(owner, "<span class='warning'>Other Bloodsuckers will not fall for your subtle approach.</span>")
|
||||
return FALSE
|
||||
// Must have Target
|
||||
if(!target) // || !ismob(target)
|
||||
if(display_error)
|
||||
@@ -63,6 +67,8 @@
|
||||
return FALSE
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
if(!H.can_inject(owner, TRUE, BODY_ZONE_HEAD) && target == owner.pulling && owner.grab_state < GRAB_AGGRESSIVE)
|
||||
return FALSE
|
||||
if(NOBLOOD in H.dna.species.species_traits)// || owner.get_blood_id() != target.get_blood_id())
|
||||
if(display_error)
|
||||
to_chat(owner, "<span class='warning'>Your victim's blood is not suitable for you to take.</span>")
|
||||
|
||||
@@ -113,6 +113,8 @@
|
||||
"<span class='danger'>You start tenderly lifting [skewee] off of [src]...</span>")
|
||||
if(!do_after(user, 60, target = skewee))
|
||||
skewee.visible_message("<span class='warning'>[skewee] painfully slides back down [src].</span>")
|
||||
if(skewee.stat >= UNCONSCIOUS)
|
||||
return //by ratvar, no more spamming my deadchat, holy fuck
|
||||
skewee.say("Oof, ouch owwie!!", forced = "fail brass skewer removal")
|
||||
return
|
||||
skewee.visible_message("<span class='danger'>[skewee] comes free of [src] with a squelching pop!</span>", \
|
||||
|
||||
@@ -244,6 +244,7 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
if(!ispath(path))
|
||||
path = gas_id2path(path) //a lot of these strings can't have embedded expressions (especially for mappers), so support for IDs needs to stick around
|
||||
gases[path] = text2num(gas[id])
|
||||
archive()
|
||||
return 1
|
||||
|
||||
/datum/gas_mixture/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
|
||||
|
||||
@@ -17,11 +17,6 @@
|
||||
//eg: "Bloody screen" > "goggles colour" as the former is much more important
|
||||
|
||||
|
||||
/mob
|
||||
var/list/client_colours = list()
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Adds an instance of colour_type to the mob's client_colours list
|
||||
colour_type - a typepath (subtyped from /datum/client_colour)
|
||||
@@ -113,4 +108,4 @@
|
||||
priority = INFINITY //we can't see colors anyway!
|
||||
|
||||
/datum/client_colour/monochrome/trance
|
||||
priority = 1
|
||||
priority = 1
|
||||
|
||||
@@ -99,5 +99,37 @@
|
||||
/// custom movement keys for this client
|
||||
var/list/movement_keys = list()
|
||||
|
||||
///Autoclick list of two elements, first being the clicked thing, second being the parameters.
|
||||
var/list/atom/selected_target[2]
|
||||
///Autoclick variable referencing the associated item.
|
||||
var/obj/item/active_mousedown_item = null
|
||||
///Used in MouseDrag to preserve the original mouse click parameters
|
||||
var/mouseParams = ""
|
||||
///Used in MouseDrag to preserve the last mouse-entered location.
|
||||
var/mouseLocation = null
|
||||
///Used in MouseDrag to preserve the last mouse-entered object.
|
||||
var/mouseObject = null
|
||||
var/mouseControlObject = null
|
||||
//Middle-mouse-button click dragtime control for aimbot exploit detection.
|
||||
var/middragtime = 0
|
||||
//Middle-mouse-button clicked object control for aimbot exploit detection.
|
||||
var/atom/middragatom
|
||||
|
||||
/// Messages currently seen by this client
|
||||
var/list/seen_messages
|
||||
///When was the last time we warned them about not cryoing without an ahelp, set to -5 minutes so that rounstart cryo still warns
|
||||
var/cryo_warned = -5 MINUTES
|
||||
|
||||
var/list/parallax_layers
|
||||
var/list/parallax_layers_cached
|
||||
var/atom/movable/movingmob
|
||||
var/turf/previous_turf
|
||||
///world.time of when we can state animate()ing parallax again
|
||||
var/dont_animate_parallax
|
||||
///world.time of last parallax update
|
||||
var/last_parallax_shift
|
||||
///ds between parallax updates
|
||||
var/parallax_throttle = 0
|
||||
var/parallax_movedir = 0
|
||||
var/parallax_layers_max = 3
|
||||
var/parallax_animate_timer
|
||||
|
||||
@@ -789,9 +789,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
log_game("[key_name(src)] is using the middle click aimbot exploit")
|
||||
message_admins("[ADMIN_LOOKUPFLW(src)] [ADMIN_KICK(usr)] is using the middle click aimbot exploit</span>")
|
||||
add_system_note("aimbot", "Is using the middle click aimbot exploit")
|
||||
log_click(object, location, control, params, src, "lockout (spam - minute ab c [ab] s [middragtime])")
|
||||
log_click(object, location, control, params, src, "lockout (spam - minute ab c [ab] s [middragtime])", TRUE)
|
||||
else
|
||||
log_click(object, location, control, params, src, "lockout (spam - minute)")
|
||||
log_click(object, location, control, params, src, "lockout (spam - minute)", TRUE)
|
||||
log_game("[key_name(src)] Has hit the per-minute click limit of [mcl] clicks in a given game minute")
|
||||
message_admins("[ADMIN_LOOKUPFLW(src)] [ADMIN_KICK(usr)] Has hit the per-minute click limit of [mcl] clicks in a given game minute")
|
||||
to_chat(src, "<span class='danger'>[msg]</span>")
|
||||
@@ -811,7 +811,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
return
|
||||
|
||||
if(ab) //Citadel edit, things with stuff.
|
||||
log_click(object, location, control, params, src, "dropped (ab c [ab] s [middragtime])")
|
||||
log_click(object, location, control, params, src, "dropped (ab c [ab] s [middragtime])", TRUE)
|
||||
return
|
||||
|
||||
if(prefs.log_clicks)
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
. += "<span class='notice'>[DisplayTimeText(nextadrenalinepop - world.time)] left before the adrenaline injector can be used again."
|
||||
|
||||
/obj/item/clothing/glasses/phantomthief/syndicate/proc/injectadrenaline(mob/living/user, was_forced = FALSE)
|
||||
if(user.combat_flags & COMBAT_FLAG_COMBAT_TOGGLED && world.time >= nextadrenalinepop)
|
||||
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_TOGGLED) && world.time >= nextadrenalinepop)
|
||||
nextadrenalinepop = world.time + 5 MINUTES
|
||||
user.reagents.add_reagent(/datum/reagent/syndicateadrenals, 5)
|
||||
user.playsound_local(user, 'sound/misc/adrenalinject.ogg', 100, 0, pressure_affected = FALSE)
|
||||
|
||||
@@ -13,6 +13,53 @@
|
||||
custom_price = 1200
|
||||
custom_premium_price = 1200
|
||||
|
||||
/obj/item/toy/sprayoncan
|
||||
name = "spray-on insulation applicator"
|
||||
desc = "What is the number one problem facing our station today?"
|
||||
icon = 'icons/obj/clothing/gloves.dmi'
|
||||
icon_state = "sprayoncan"
|
||||
|
||||
/obj/item/toy/sprayoncan/afterattack(atom/target, mob/living/carbon/user, proximity)
|
||||
if(iscarbon(target) && proximity)
|
||||
var/mob/living/carbon/C = target
|
||||
var/mob/living/carbon/U = user
|
||||
var/success = C.equip_to_slot_if_possible(new /obj/item/clothing/gloves/color/yellow/sprayon, ITEM_SLOT_GLOVES, TRUE, TRUE)
|
||||
if(success)
|
||||
if(C == user)
|
||||
C.visible_message("<span class='notice'>[U] sprays their hands with glittery rubber!</span>")
|
||||
else
|
||||
C.visible_message("<span class='warning'>[U] sprays glittery rubber on the hands of [C]!</span>")
|
||||
else
|
||||
C.visible_message("<span class='warning'>The rubber fails to stick to [C]'s hands!</span>")
|
||||
|
||||
qdel(src)
|
||||
|
||||
/obj/item/clothing/gloves/color/yellow/sprayon
|
||||
desc = "How're you gonna get 'em off, nerd?"
|
||||
name = "spray-on insulated gloves"
|
||||
icon_state = "sprayon"
|
||||
item_state = "sprayon"
|
||||
permeability_coefficient = 0
|
||||
resistance_flags = ACID_PROOF
|
||||
var/shocks_remaining = 10
|
||||
|
||||
/obj/item/clothing/gloves/color/yellow/sprayon/Initialize()
|
||||
.=..()
|
||||
ADD_TRAIT(src, TRAIT_NODROP, GLOVE_TRAIT)
|
||||
|
||||
/obj/item/clothing/gloves/color/yellow/sprayon/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
RegisterSignal(user, COMSIG_LIVING_SHOCK_PREVENTED, .proc/Shocked)
|
||||
|
||||
/obj/item/clothing/gloves/color/yellow/sprayon/proc/Shocked()
|
||||
shocks_remaining--
|
||||
if(shocks_remaining < 0)
|
||||
qdel(src) //if we run out of uses, the gloves crumble away into nothing, just like my dreams after working with .dm
|
||||
|
||||
/obj/item/clothing/gloves/color/yellow/sprayon/dropped()
|
||||
.=..()
|
||||
qdel(src) //loose nodrop items bad
|
||||
|
||||
/obj/item/clothing/gloves/color/fyellow //Cheap Chinese Crap
|
||||
desc = "These gloves are cheap knockoffs of the coveted ones - no way this can end badly."
|
||||
name = "budget insulated gloves"
|
||||
@@ -202,6 +249,17 @@
|
||||
permeability_coefficient = 0.3
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
|
||||
/obj/item/clothing/gloves/color/latex/engineering
|
||||
name = "tinker's gloves"
|
||||
desc = "Overdesigned engineering gloves that have automated construction subrutines dialed in, allowing for faster construction while worn."
|
||||
icon = 'icons/obj/clothing/clockwork_garb.dmi'
|
||||
icon_state = "clockwork_gauntlets"
|
||||
item_state = "clockwork_gauntlets"
|
||||
siemens_coefficient = 0.8
|
||||
permeability_coefficient = 0.3
|
||||
carrytrait = TRAIT_QUICK_BUILD
|
||||
custom_materials = list(/datum/material/iron=2000, /datum/material/silver=1500, /datum/material/gold = 1000)
|
||||
|
||||
/obj/item/clothing/gloves/color/white
|
||||
name = "white gloves"
|
||||
desc = "These look pretty fancy."
|
||||
|
||||
@@ -60,6 +60,16 @@
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
|
||||
|
||||
/obj/item/clothing/head/hardhat/red/upgraded
|
||||
name = "workplace-ready firefighter helmet"
|
||||
desc = "By applying state of the art lighting technology to a fire helmet, and using photo-chemical hardening methods, this hardhat will protect you from robust workplace hazards."
|
||||
icon_state = "hardhat0_purple"
|
||||
item_state = "hardhat0_purple"
|
||||
brightness_on = 5
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
custom_materials = list(/datum/material/iron = 4000, /datum/material/glass = 1000, /datum/material/plastic = 3000, /datum/material/silver = 500)
|
||||
hat_type = "purple"
|
||||
|
||||
/obj/item/clothing/head/hardhat/white
|
||||
icon_state = "hardhat0_white"
|
||||
item_state = "hardhat0_white"
|
||||
|
||||
@@ -198,6 +198,7 @@
|
||||
desc = "A creepy wooden mask. Surprisingly expressive for a poorly carved bit of wood."
|
||||
icon_state = "tiki_eyebrow"
|
||||
item_state = "tiki_eyebrow"
|
||||
custom_materials = list(/datum/material/wood = MINERAL_MATERIAL_AMOUNT * 1.25)
|
||||
resistance_flags = FLAMMABLE
|
||||
max_integrity = 100
|
||||
actions_types = list(/datum/action/item_action/adjust)
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
desc = "A pair of rather plain wooden sandals."
|
||||
name = "sandals"
|
||||
icon_state = "wizard"
|
||||
custom_materials = list(/datum/material/wood = MINERAL_MATERIAL_AMOUNT * 0.5)
|
||||
strip_delay = 50
|
||||
equip_delay_other = 50
|
||||
permeability_coefficient = 0.9
|
||||
|
||||
@@ -444,7 +444,6 @@ Contains:
|
||||
desc = "Powerful wards are built into this hardsuit, protecting the user from all manner of paranormal threats. Alas, this one looks pretty worn out and rusted."
|
||||
armor = list("melee" = 55, "bullet" = 40, "laser" = 40, "energy" = 40, "bomb" = 40, "bio" = 80, "rad" = 80, "fire" = 60, "acid" = 60)
|
||||
slowdown = 0.8
|
||||
obj_flags = IMMUTABLE_SLOW //rest in peace rusty joints.
|
||||
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/inquisitor/old
|
||||
charges = 12
|
||||
|
||||
@@ -470,7 +469,6 @@ Contains:
|
||||
desc = "Voices echo from the hardsuit, driving the user insane. This one is pretty battle-worn, but still fearsome."
|
||||
armor = list("melee" = 55, "bullet" = 40, "laser" = 40, "energy" = 40, "bomb" = 40, "bio" = 80, "rad" = 80, "fire" = 60, "acid" = 60)
|
||||
slowdown = 0.8
|
||||
obj_flags = IMMUTABLE_SLOW //rest in peace rusty joints.
|
||||
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/inquisitor/old
|
||||
charges = 6
|
||||
|
||||
|
||||
@@ -165,31 +165,134 @@
|
||||
var/robe_charge = TRUE
|
||||
actions_types = list(/datum/action/item_action/stickmen)
|
||||
|
||||
/obj/item/clothing/suit/wizrobe/paper/item_action_slot_check(slot, mob/user, datum/action/A)
|
||||
if(A.type == /datum/action/item_action/stickmen && slot != SLOT_WEAR_SUIT)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/suit/wizrobe/paper/ui_action_click(mob/user, action)
|
||||
stickmen()
|
||||
//Stickmemes. VV-friendly.
|
||||
/datum/action/item_action/stickmen
|
||||
name = "Summon Stick Minions"
|
||||
desc = "Allows you to summon faithful stickmen allies to aide you in battle."
|
||||
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
|
||||
button_icon_state = "art_summon"
|
||||
var/ready = TRUE
|
||||
var/list/summoned_stickmen = list()
|
||||
var/summoned_mob_path = /mob/living/simple_animal/hostile/stickman //Must be an hostile animal path.
|
||||
var/max_stickmen = 8
|
||||
var/cooldown = 3 SECONDS
|
||||
var/list/book_of_grudges = list()
|
||||
|
||||
/datum/action/item_action/stickmen/New(Target)
|
||||
..()
|
||||
if(isitem(Target))
|
||||
RegisterSignal(Target, COMSIG_PARENT_EXAMINE, .proc/give_infos)
|
||||
|
||||
/datum/action/item_action/stickmen/Destroy()
|
||||
for(var/A in summoned_stickmen)
|
||||
var/mob/living/simple_animal/hostile/S = A
|
||||
if(S.client)
|
||||
to_chat(S, "<span class='danger'>A dizzying sensation strikes you as the comglomerate of pencil lines you call \
|
||||
your body crumbles under the pressure of an invisible eraser, soon to join bilions discarded sketches. \
|
||||
It seems whatever was keeping you in this realm has come to an end, like all things.</span>")
|
||||
animate(S, alpha = 0, time = 5 SECONDS)
|
||||
QDEL_IN(S, 5 SECONDS)
|
||||
return ..()
|
||||
|
||||
/datum/action/item_action/stickmen/proc/give_infos(atom/source, mob/user, list/examine_list)
|
||||
examine_list += "<span class='notice'>Making sure you are properly wearing or holding it, \
|
||||
point at whatever you want to rally your minions to its position."
|
||||
examine_list += "While on <b>harm</b> intent, pointed mobs (minus you and the minions) \
|
||||
will also be marked as foes for your minions to attack for the next 2 minutes.</span>"
|
||||
|
||||
/datum/action/item_action/stickmen/Grant(mob/M)
|
||||
. = ..()
|
||||
if(owner)
|
||||
RegisterSignal(M, COMSIG_MOB_POINTED, .proc/rally)
|
||||
if(book_of_grudges[M]) //Stop attacking your new master.
|
||||
book_of_grudges -= M
|
||||
for(var/A in summoned_stickmen)
|
||||
var/mob/living/simple_animal/hostile/S = A
|
||||
if(!S.mind)
|
||||
S.LoseTarget()
|
||||
|
||||
|
||||
/obj/item/clothing/suit/wizrobe/paper/verb/stickmen()
|
||||
set category = "Object"
|
||||
set name = "Summon Stick Minions"
|
||||
set src in usr
|
||||
if(!isliving(usr))
|
||||
/datum/action/item_action/stickmen/Remove(mob/M)
|
||||
. = ..()
|
||||
UnregisterSignal(M, COMSIG_MOB_POINTED)
|
||||
|
||||
/datum/action/item_action/stickmen/Trigger()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!robe_charge)
|
||||
to_chat(usr, "<span class='warning'>\The robe's internal magic supply is still recharging!</span>")
|
||||
if(!ready)
|
||||
to_chat(owner, "<span class='warning'>[src]'s internal magic supply is still recharging!</span>")
|
||||
return FALSE
|
||||
var/summon = TRUE
|
||||
if(length(summoned_stickmen) >= max_stickmen)
|
||||
var/mob/living/simple_animal/hostile/S = popleft(summoned_stickmen)
|
||||
if(!S.client)
|
||||
qdel(S)
|
||||
else
|
||||
S.forceMove(owner.drop_location())
|
||||
S.revive(TRUE)
|
||||
summoned_stickmen[S] = TRUE
|
||||
summon = FALSE
|
||||
|
||||
owner.say("Rise, my creation! Off your page into this realm!", forced = "stickman summoning")
|
||||
playsound(owner, 'sound/magic/summon_magic.ogg', 50, 1, 1)
|
||||
if(summon)
|
||||
var/mob/living/simple_animal/hostile/S = new summoned_mob_path (get_turf(usr))
|
||||
S.faction = owner.faction
|
||||
S.foes = book_of_grudges
|
||||
RegisterSignal(S, COMSIG_PARENT_QDELETING, .proc/remove_from_list)
|
||||
ready = FALSE
|
||||
addtimer(CALLBACK(src, .proc/ready_again), cooldown)
|
||||
|
||||
/datum/action/item_action/stickmen/proc/remove_from_list(datum/source, forced)
|
||||
summoned_stickmen -= source
|
||||
|
||||
/datum/action/item_action/stickmen/proc/ready_again()
|
||||
ready = TRUE
|
||||
if(owner)
|
||||
to_chat(owner, "<span class='notice'>[src] hums, its internal magic supply restored.</span>")
|
||||
|
||||
/**
|
||||
* Rallies your army of stickmen to whichever target the user is pointing.
|
||||
* Should the user be on harm intent and the target be a living mob that's not the user or a fellow stickman,
|
||||
* said target will be added to a list of foes which the stickmen will gladly dispose regardless of faction.
|
||||
* This is designed so stickmen will move toward whatever you point at even when you don't want to, that's the downside.
|
||||
*/
|
||||
/datum/action/item_action/stickmen/proc/rally(mob/source, atom/A)
|
||||
var/turf/T = get_turf(A)
|
||||
var/list/surrounding_turfs = block(locate(T.x - 1, T.y - 1, T.z), locate(T.x + 1, T.y + 1, T.z))
|
||||
if(!surrounding_turfs.len)
|
||||
return
|
||||
if(source.a_intent == INTENT_HARM && A != source && !summoned_stickmen[A])
|
||||
var/mob/living/L
|
||||
if(isliving(A)) //Gettem boys!
|
||||
L = A
|
||||
else if(ismecha(A))
|
||||
var/obj/mecha/M = A
|
||||
L = M.occupant
|
||||
if(L && L.stat != DEAD && !HAS_TRAIT(L, TRAIT_DEATHCOMA)) //Taking revenge on the deads would be proposterous.
|
||||
addtimer(CALLBACK(src, .proc/clear_grudge, L), 2 MINUTES, TIMER_OVERRIDE|TIMER_UNIQUE)
|
||||
if(!book_of_grudges[L])
|
||||
RegisterSignal(L, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_DEATH), .proc/grudge_settled)
|
||||
book_of_grudges[L] = TRUE
|
||||
for(var/k in summoned_stickmen) //Shamelessly copied from the blob rally power
|
||||
var/mob/living/simple_animal/hostile/S = k
|
||||
if(!S.mind && isturf(S.loc) && get_dist(S, T) <= 10)
|
||||
S.LoseTarget()
|
||||
S.Goto(pick(surrounding_turfs), S.move_to_delay)
|
||||
|
||||
usr.say("Rise, my creation! Off your page into this realm!", forced = "stickman summoning")
|
||||
playsound(src.loc, 'sound/magic/summon_magic.ogg', 50, 1, 1)
|
||||
var/mob/living/M = new /mob/living/simple_animal/hostile/stickman(get_turf(usr))
|
||||
var/list/factions = usr.faction
|
||||
M.faction = factions
|
||||
src.robe_charge = FALSE
|
||||
sleep(30)
|
||||
src.robe_charge = TRUE
|
||||
to_chat(usr, "<span class='notice'>\The robe hums, its internal magic supply restored.</span>")
|
||||
/datum/action/item_action/stickmen/proc/clear_grudge(mob/living/L)
|
||||
if(!QDELETED(L))
|
||||
book_of_grudges -= L
|
||||
|
||||
/datum/action/item_action/stickmen/proc/grudge_settled(mob/living/L)
|
||||
UnregisterSignal(L, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_DEATH))
|
||||
book_of_grudges -= L
|
||||
|
||||
//Shielded Armour
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
/datum/reagent/pax,
|
||||
/datum/reagent/consumable/laughter,
|
||||
/datum/reagent/concentrated_barbers_aid,
|
||||
/datum/reagent/baldium,
|
||||
/datum/reagent/colorful_reagent,
|
||||
/datum/reagent/peaceborg_confuse,
|
||||
/datum/reagent/peaceborg_tire,
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
name = "modified gravity zone"
|
||||
setup_field_turfs = TRUE
|
||||
var/gravity_value = 0
|
||||
var/list/grav_components = list()
|
||||
var/list/modified_turfs = list()
|
||||
field_shape = FIELD_SHAPE_RADIUS_SQUARE
|
||||
|
||||
/datum/proximity_monitor/advanced/gravity/setup_field_turf(turf/T)
|
||||
. = ..()
|
||||
grav_components[T] = T.AddComponent(/datum/component/forced_gravity,gravity_value)
|
||||
T.AddElement(/datum/element/forced_gravity, gravity_value)
|
||||
modified_turfs[T] = gravity_value
|
||||
|
||||
/datum/proximity_monitor/advanced/gravity/cleanup_field_turf(turf/T)
|
||||
. = ..()
|
||||
var/datum/component/forced_gravity/G = grav_components[T]
|
||||
grav_components -= T
|
||||
if(G)
|
||||
qdel(G)
|
||||
if(isnull(modified_turfs[T]))
|
||||
return
|
||||
T.RemoveElement(/datum/element/forced_gravity, modified_turfs[T])
|
||||
modified_turfs -= T
|
||||
@@ -15,7 +15,7 @@
|
||||
result = /obj/item/reagent_containers/food/snacks/banana_split
|
||||
subcategory = CAT_ICE
|
||||
|
||||
/datum/crafting_recipe/food/bluecharrie_float
|
||||
/datum/crafting_recipe/food/bluecharrie_float
|
||||
name = "Blue Cherry Shake"
|
||||
always_availible = FALSE
|
||||
reqs = list(
|
||||
@@ -276,4 +276,4 @@
|
||||
/datum/reagent/consumable/laughter = 5
|
||||
)
|
||||
result = /obj/item/reagent_containers/food/snacks/snowcones/clown
|
||||
subcategory = CAT_ICE
|
||||
subcategory = CAT_ICE
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/// A deck of unum cards. Classic.
|
||||
/obj/item/toy/cards/deck/unum
|
||||
name = "\improper UNUM deck"
|
||||
desc = "A deck of unum cards. House rules to argue over not included."
|
||||
icon = 'icons/obj/toy.dmi'
|
||||
icon_state = "deck_unum_full"
|
||||
deckstyle = "unum"
|
||||
original_size = 108
|
||||
|
||||
//Populate the deck.
|
||||
/obj/item/toy/cards/deck/unum/populate_deck()
|
||||
for(var/colour in list("Red","Yellow","Green","Blue"))
|
||||
cards += "[colour] 0" //Uno, i mean, cough cough, Unum decks have only one colour of each 0, weird huh?
|
||||
for(var/k in 0 to 1) //two of each colour of number
|
||||
cards += "[colour] skip"
|
||||
cards += "[colour] reverse"
|
||||
cards += "[colour] draw 2"
|
||||
for(var/i in 1 to 9)
|
||||
cards += "[colour] [i]"
|
||||
for(var/k in 0 to 3) //4 wilds and draw 4s
|
||||
cards += "Wildcard"
|
||||
cards += "Draw 4"
|
||||
@@ -117,7 +117,7 @@
|
||||
/obj/item/weldingtool = 3,
|
||||
/obj/item/wirecutters = 2,
|
||||
/obj/item/wrench = 4,
|
||||
/obj/item/weaponcrafting/receiver = 1,
|
||||
/obj/item/weaponcrafting/improvised_parts/shotgun_receiver = 1,
|
||||
/obj/item/geiger_counter = 3,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/orange = 5,
|
||||
/obj/item/assembly/infra = 1,
|
||||
|
||||
@@ -22,12 +22,8 @@
|
||||
full_name = "Toggle combat mode"
|
||||
description = "Toggles whether or not you're in combat mode."
|
||||
|
||||
/datum/keybinding/living/toggle_combat_mode/can_use(client/user)
|
||||
return iscarbon(user.mob) // for now, only carbons should be using combat mode, although all livings have combat mode implemented.
|
||||
|
||||
/datum/keybinding/living/toggle_combat_mode/down(client/user)
|
||||
var/mob/living/carbon/C = user.mob
|
||||
C.user_toggle_intentional_combat_mode()
|
||||
SEND_SIGNAL(user.mob, COMSIG_TOGGLE_COMBAT_MODE)
|
||||
return TRUE
|
||||
|
||||
/datum/keybinding/living/toggle_resting
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
/obj/effect/mapping_helpers/network_builder/Initialize(mapload)
|
||||
. = ..()
|
||||
to_chat(world, "DEBUG: Initializing [COORD(src)]")
|
||||
var/conflict = check_duplicates()
|
||||
if(conflict)
|
||||
stack_trace("WARNING: [type] network building helper found check_duplicates() conflict [conflict] in its location.!")
|
||||
@@ -27,7 +26,6 @@
|
||||
|
||||
/// How this works: On LateInitialize, detect all directions that this should be applicable to, and do what it needs to do, and then inform all network builders in said directions that it's been around since it won't be around afterwards.
|
||||
/obj/effect/mapping_helpers/network_builder/LateInitialize()
|
||||
to_chat(world, "DEBUG: LateInitializing [COORD(src)]")
|
||||
scan_directions()
|
||||
build_network()
|
||||
if(!custom_spawned)
|
||||
|
||||
@@ -425,7 +425,7 @@
|
||||
marker.icon_state = "chronobolt"
|
||||
marker.damage = bonus_value
|
||||
marker.nodamage = FALSE
|
||||
marker.speed = 2
|
||||
marker.pixels_per_second = TILES_TO_PIXELS(5)
|
||||
deadly_shot = FALSE
|
||||
|
||||
/obj/item/crusher_trophy/blaster_tubes/on_mark_detonation(mob/living/target, mob/living/user)
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
if(malfunctioning)
|
||||
H.faction |= list("lazarus", "[REF(user)]")
|
||||
H.robust_searching = 1
|
||||
H.friends += user
|
||||
H.friends[user]++
|
||||
H.attack_same = 1
|
||||
log_game("[key_name(user)] has revived hostile mob [key_name(target)] with a malfunctioning lazarus injector")
|
||||
else
|
||||
|
||||
@@ -168,3 +168,17 @@
|
||||
throwforce = 7
|
||||
custom_materials = list(/datum/material/iron=50)
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
|
||||
/obj/item/shovel/serrated
|
||||
name = "serrated bone shovel"
|
||||
desc = "A wicked tool that cleaves through dirt just as easily as it does flesh. The design was styled after ancient lavaland tribal designs."
|
||||
icon_state = "shovel_bone"
|
||||
item_state = "shovel_bone"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi'
|
||||
force = 15
|
||||
throwforce = 12
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
toolspeed = 0.7
|
||||
attack_verb = list("slashed", "impaled", "stabbed", "sliced")
|
||||
sharpness = IS_SHARP
|
||||
|
||||
@@ -270,6 +270,7 @@
|
||||
desc = "A sign of relief for weary miners, and a warning for would-be competitors to Nanotrasen's mining claims."
|
||||
icon = 'icons/turf/walls/survival_pod_walls.dmi'
|
||||
icon_state = "ntpod"
|
||||
buildable_sign = FALSE
|
||||
|
||||
/obj/structure/sign/mining/survival
|
||||
name = "shelter sign"
|
||||
|
||||
@@ -287,12 +287,12 @@ Works together with spawning an observer, noted above.
|
||||
var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes
|
||||
if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
|
||||
penalty = CANT_REENTER_ROUND
|
||||
if(!(ckey in GLOB.client_ghost_timeouts))
|
||||
GLOB.client_ghost_timeouts += ckey
|
||||
GLOB.client_ghost_timeouts[ckey] = 0
|
||||
else if(GLOB.client_ghost_timeouts[ckey] == CANT_REENTER_ROUND)
|
||||
if(!(ghost.ckey in GLOB.client_ghost_timeouts))
|
||||
GLOB.client_ghost_timeouts += ghost.ckey
|
||||
GLOB.client_ghost_timeouts[ghost.ckey] = 0
|
||||
else if(GLOB.client_ghost_timeouts[ghost.ckey] == CANT_REENTER_ROUND)
|
||||
return
|
||||
GLOB.client_ghost_timeouts[ckey] = max(GLOB.client_ghost_timeouts[ckey],penalty)
|
||||
GLOB.client_ghost_timeouts[ghost.ckey] = max(GLOB.client_ghost_timeouts[ghost.ckey],penalty)
|
||||
// needs to be done AFTER the ckey transfer, too
|
||||
return ghost
|
||||
|
||||
|
||||
@@ -137,11 +137,11 @@
|
||||
if(cached_Bdamage <= HEALTH_THRESHOLD_DEAD) //Fixing dead brains yeilds a trauma
|
||||
if((cached_Bdamage <= HEALTH_THRESHOLD_DEAD) && (brainmob.health > HEALTH_THRESHOLD_DEAD))
|
||||
if(prob(80))
|
||||
gain_trauma_type(BRAIN_TRAUMA_MILD)
|
||||
gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE)
|
||||
else if(prob(50))
|
||||
gain_trauma_type(BRAIN_TRAUMA_SEVERE)
|
||||
gain_trauma_type(BRAIN_TRAUMA_SEVERE, natural_gain = TRUE)
|
||||
else
|
||||
gain_trauma_type(BRAIN_TRAUMA_SPECIAL)
|
||||
gain_trauma_type(BRAIN_TRAUMA_SPECIAL, natural_gain = TRUE)
|
||||
return
|
||||
|
||||
if((organ_flags & ORGAN_FAILING) && O.is_drainable() && O.reagents.has_reagent(/datum/reagent/medicine/mannitol)) //attempt to heal the brain
|
||||
@@ -253,16 +253,16 @@
|
||||
damage_delta = damage - prev_damage
|
||||
if(damage > BRAIN_DAMAGE_MILD)
|
||||
if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% //learn how to do your bloody math properly goddamnit
|
||||
gain_trauma_type(BRAIN_TRAUMA_MILD)
|
||||
gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE)
|
||||
if(prev_damage <= BRAIN_DAMAGE_MILD && owner)
|
||||
var/datum/skill_modifier/S
|
||||
ADD_SKILL_MODIFIER_BODY(/datum/skill_modifier/brain_damage, null, owner, S)
|
||||
if(damage > BRAIN_DAMAGE_SEVERE)
|
||||
if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1%
|
||||
if(prob(20))
|
||||
gain_trauma_type(BRAIN_TRAUMA_SPECIAL)
|
||||
gain_trauma_type(BRAIN_TRAUMA_SPECIAL, natural_gain = TRUE)
|
||||
else
|
||||
gain_trauma_type(BRAIN_TRAUMA_SEVERE)
|
||||
gain_trauma_type(BRAIN_TRAUMA_SEVERE, natural_gain = TRUE)
|
||||
if(prev_damage <= BRAIN_DAMAGE_SEVERE && owner)
|
||||
var/datum/skill_modifier/S
|
||||
ADD_SKILL_MODIFIER_BODY(/datum/skill_modifier/heavy_brain_damage, null, owner, S)
|
||||
@@ -308,7 +308,7 @@
|
||||
if(istype(BT, brain_trauma_type) && (BT.resilience <= resilience))
|
||||
. += BT
|
||||
|
||||
/obj/item/organ/brain/proc/can_gain_trauma(datum/brain_trauma/trauma, resilience)
|
||||
/obj/item/organ/brain/proc/can_gain_trauma(datum/brain_trauma/trauma, resilience, natural_gain = FALSE)
|
||||
if(!ispath(trauma))
|
||||
trauma = trauma.type
|
||||
if(!initial(trauma.can_gain))
|
||||
@@ -341,7 +341,7 @@
|
||||
if(TRAUMA_RESILIENCE_ABSOLUTE)
|
||||
max_traumas = TRAUMA_LIMIT_ABSOLUTE
|
||||
|
||||
if(resilience_tier_count >= max_traumas)
|
||||
if(natural_gain && resilience_tier_count >= max_traumas)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -381,11 +381,11 @@
|
||||
return actual_trauma
|
||||
|
||||
//Add a random trauma of a certain subtype
|
||||
/obj/item/organ/brain/proc/gain_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience)
|
||||
/obj/item/organ/brain/proc/gain_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience, natural_gain = FALSE)
|
||||
var/list/datum/brain_trauma/possible_traumas = list()
|
||||
for(var/T in subtypesof(brain_trauma_type))
|
||||
var/datum/brain_trauma/BT = T
|
||||
if(can_gain_trauma(BT, resilience) && initial(BT.random_gain))
|
||||
if(can_gain_trauma(BT, resilience, natural_gain) && initial(BT.random_gain))
|
||||
possible_traumas += BT
|
||||
|
||||
if(!LAZYLEN(possible_traumas))
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
if(!eye_blind)
|
||||
blind_eyes(1)
|
||||
update_mobility()
|
||||
else
|
||||
if(stat == UNCONSCIOUS)
|
||||
stat = CONSCIOUS
|
||||
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT))
|
||||
set_resting(FALSE, TRUE)
|
||||
else if(stat == UNCONSCIOUS)
|
||||
stat = CONSCIOUS
|
||||
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT))
|
||||
set_resting(FALSE, TRUE)
|
||||
if(eye_blind <= 1)
|
||||
adjust_blindness(-1)
|
||||
update_mobility()
|
||||
update_mobility()
|
||||
update_damage_hud()
|
||||
update_health_hud()
|
||||
|
||||
@@ -440,14 +440,14 @@
|
||||
|
||||
//dropItemToGround(I) CIT CHANGE - makes it so the item doesn't drop if the modifier rolls above 100
|
||||
|
||||
var/modifier = 0
|
||||
var/modifier = 50
|
||||
|
||||
if(HAS_TRAIT(src, TRAIT_CLUMSY))
|
||||
modifier -= 40 //Clumsy people are more likely to hit themselves -Honk!
|
||||
|
||||
//CIT CHANGES START HERE
|
||||
else if(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
modifier += 50
|
||||
else if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
modifier -= 50
|
||||
|
||||
if(modifier < 100)
|
||||
dropItemToGround(I)
|
||||
@@ -825,16 +825,17 @@
|
||||
return
|
||||
if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (HAS_TRAIT(src, TRAIT_DEATHCOMA)) || (health <= HEALTH_THRESHOLD_FULLCRIT && !HAS_TRAIT(src, TRAIT_NOHARDCRIT)))
|
||||
stat = UNCONSCIOUS
|
||||
disable_intentional_combat_mode(FALSE, FALSE)
|
||||
SEND_SIGNAL(src, COMSIG_DISABLE_COMBAT_MODE)
|
||||
if(!eye_blind)
|
||||
blind_eyes(1)
|
||||
else
|
||||
if(health <= crit_threshold && !HAS_TRAIT(src, TRAIT_NOSOFTCRIT))
|
||||
stat = SOFT_CRIT
|
||||
disable_intentional_combat_mode(FALSE, FALSE)
|
||||
SEND_SIGNAL(src, COMSIG_DISABLE_COMBAT_MODE)
|
||||
else
|
||||
stat = CONSCIOUS
|
||||
adjust_blindness(-1)
|
||||
if(eye_blind <= 1)
|
||||
adjust_blindness(-1)
|
||||
update_mobility()
|
||||
update_damage_hud()
|
||||
update_health_hud()
|
||||
@@ -1120,10 +1121,6 @@
|
||||
if(mood.sanity < SANITY_UNSTABLE)
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/transfer_ckey(mob/new_mob, send_signal = TRUE)
|
||||
disable_intentional_combat_mode(TRUE, FALSE)
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/can_see_reagents()
|
||||
. = ..()
|
||||
if(.) //No need to run through all of this if it's already true.
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
/mob/living/carbon/enable_intentional_combat_mode()
|
||||
. = ..()
|
||||
if(.)
|
||||
if(voremode)
|
||||
toggle_vore_mode()
|
||||
|
||||
@@ -78,12 +78,6 @@
|
||||
|
||||
/mob/living/carbon/attacked_by(obj/item/I, mob/living/user)
|
||||
var/totitemdamage = pre_attacked_by(I, user)
|
||||
if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
totitemdamage *= 0.5
|
||||
if(!CHECK_MOBILITY(user, MOBILITY_STAND))
|
||||
totitemdamage *= 0.5
|
||||
if(!(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
totitemdamage *= 1.5
|
||||
var/impacting_zone = (user == src)? check_zone(user.zone_selected) : ran_zone(user.zone_selected)
|
||||
if((user != src) && (mob_run_block(I, totitemdamage, "the [I]", ATTACK_TYPE_MELEE, I.armour_penetration, user, impacting_zone, null) & BLOCK_SUCCESS))
|
||||
return FALSE
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
if(!gibbed)
|
||||
emote("deathgasp")
|
||||
|
||||
disable_intentional_combat_mode(TRUE, FALSE)
|
||||
|
||||
. = ..()
|
||||
|
||||
for(var/T in get_traumas())
|
||||
@@ -62,7 +60,3 @@
|
||||
var/obj/item/bodypart/BP = X
|
||||
BP.drop_limb()
|
||||
BP.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
|
||||
|
||||
/mob/living/carbon/ghostize(can_reenter_corpse = TRUE, special = FALSE, penalize = FALSE, voluntary = FALSE)
|
||||
disable_intentional_combat_mode(TRUE, FALSE)
|
||||
return ..()
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
if(digitalcamo)
|
||||
. += "[t_He] [t_is] moving [t_his] body in an unnatural and blatantly unsimian manner."
|
||||
|
||||
if(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE))
|
||||
. += "[t_He] [t_is] visibly tense[CHECK_MOBILITY(src, MOBILITY_STAND) ? "." : ", and [t_is] standing in combative stance."]"
|
||||
|
||||
var/trait_exam = common_trait_examine()
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
. = ..()
|
||||
if(!CONFIG_GET(flag/disable_human_mood))
|
||||
AddComponent(/datum/component/mood)
|
||||
AddComponent(/datum/component/combat_mode)
|
||||
AddElement(/datum/element/flavor_text/carbon, _name = "Flavor Text", _save_key = "flavor_text")
|
||||
AddElement(/datum/element/flavor_text, "", "Temporary Flavor Text", "This should be used only for things pertaining to the current round!")
|
||||
AddElement(/datum/element/flavor_text, _name = "OOC Notes", _addendum = "Put information on ERP/vore/lewd-related preferences here. THIS SHOULD NOT CONTAIN REGULAR FLAVORTEXT!!", _always_show = TRUE, _save_key = "ooc_notes", _examine_no_preview = TRUE)
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
if(!SSI)
|
||||
SSI = CONFIG_GET_ENTRY(number/movedelay/sprint_speed_increase)
|
||||
. -= SSI.config_entry_value
|
||||
if(wrongdirmovedelay)
|
||||
. += 1
|
||||
if (m_intent == MOVE_INTENT_WALK && HAS_TRAIT(src, TRAIT_SPEEDY_STEP))
|
||||
. -= 1.5
|
||||
|
||||
|
||||
@@ -1473,11 +1473,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
var/punchedbrute = target.getBruteLoss()
|
||||
|
||||
//CITADEL CHANGES - makes resting and disabled combat mode reduce punch damage, makes being out of combat mode result in you taking more damage
|
||||
if(!(target.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
if(!SEND_SIGNAL(target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
damage *= 1.5
|
||||
if(!CHECK_MOBILITY(user, MOBILITY_STAND))
|
||||
damage *= 0.5
|
||||
if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
damage *= 0.25
|
||||
//END OF CITADEL CHANGES
|
||||
|
||||
@@ -1625,11 +1625,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
log_combat(user, target, "disarmed out of grab from")
|
||||
return
|
||||
var/randn = rand(1, 100)
|
||||
if(!(target.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) // CITADEL CHANGE
|
||||
if(SEND_SIGNAL(target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) // CITADEL CHANGE
|
||||
randn += -10 //CITADEL CHANGE - being out of combat mode makes it easier for you to get disarmed
|
||||
if(!CHECK_MOBILITY(user, MOBILITY_STAND)) //CITADEL CHANGE
|
||||
randn += 100 //CITADEL CHANGE - No kosher disarming if you're resting
|
||||
if(!(target.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) //CITADEL CHANGE
|
||||
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) //CITADEL CHANGE
|
||||
randn += 25 //CITADEL CHANGE - Makes it harder to disarm outside of combat mode
|
||||
if(user.pulling == target)
|
||||
randn -= 20 //If you have the time to get someone in a grab, you should have a greater chance at snatching the thing in their hand. Will be made completely obsolete by the grab rework but i've got a poor track record for releasing big projects on time so w/e i guess
|
||||
@@ -1827,7 +1827,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
if(IS_STAMCRIT(user))
|
||||
to_chat(user, "<span class='warning'>You're too exhausted for that.</span>")
|
||||
return
|
||||
if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
to_chat(user, "<span class='warning'>You need combat mode to be active to that!</span>")
|
||||
return
|
||||
if(user.IsKnockdown() || user.IsParalyzed() || user.IsStun())
|
||||
|
||||
@@ -6,8 +6,8 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
|
||||
name = "Dwarf"
|
||||
id = "dwarf" //Also called Homo sapiens pumilionis
|
||||
default_color = "FFFFFF"
|
||||
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,NO_UNDERWEAR,TRAIT_DWARF)
|
||||
inherent_traits = list()
|
||||
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS)
|
||||
inherent_traits = list(TRAIT_DWARF,TRAIT_SNOB)
|
||||
limbs_id = "human"
|
||||
use_skintones = USE_SKINTONES_GRAYSCALE_CUSTOM
|
||||
say_mod = "bellows" //high energy, EXTRA BIOLOGICAL FUEL
|
||||
@@ -87,9 +87,7 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
|
||||
var/heal_rate = 0.5 //The rate they heal damages over 400 alcohol stored. Default is 0.5 so we times 3 since 3 seconds.
|
||||
var/alcohol_rate = 0.25 //The rate the alcohol ticks down per each iteration of dwarf_eth_ticker completing.
|
||||
//These count in on_life ticks which should be 2 seconds per every increment of 1 in a perfect world.
|
||||
var/dwarf_filth_ticker = 0 //Currently set =< 4, that means this will fire the proc around every 4-8 seconds.
|
||||
var/dwarf_eth_ticker = 0 //Currently set =< 1, that means this will fire the proc around every 2 seconds
|
||||
var/last_filth_spam
|
||||
var/last_alcohol_spam
|
||||
|
||||
/obj/item/organ/dwarfgland/prepare_eat()
|
||||
@@ -100,64 +98,10 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
|
||||
/obj/item/organ/dwarfgland/on_life() //Primary loop to hook into to start delayed loops for other loops..
|
||||
. = ..()
|
||||
if(owner && owner.stat != DEAD)
|
||||
dwarf_cycle_ticker()
|
||||
|
||||
//Handles the delayed tick cycle by just adding on increments per each on_life() tick
|
||||
/obj/item/organ/dwarfgland/proc/dwarf_cycle_ticker()
|
||||
dwarf_eth_ticker++
|
||||
dwarf_filth_ticker++
|
||||
|
||||
if(dwarf_filth_ticker >= 4) //Should be around 4-8 seconds since a tick is around 2 seconds.
|
||||
dwarf_filth_cycle() //On_life will adjust regarding other factors, so we are along for the ride.
|
||||
dwarf_filth_ticker = 0 //We set the ticker back to 0 to go again.
|
||||
if(dwarf_eth_ticker >= 1) //Alcohol reagent check should be around 2 seconds, since a tick is around 2 seconds.
|
||||
dwarf_eth_cycle()
|
||||
dwarf_eth_ticker = 0
|
||||
|
||||
//If this still friggin uses too much CPU, I'll make a for view subsystem If I have to.
|
||||
/obj/item/organ/dwarfgland/proc/dwarf_filth_cycle()
|
||||
if(!owner?.client || !owner?.mind)
|
||||
return
|
||||
//Filth Reactions - Since miasma now exists
|
||||
var/filth_counter = 0 //Holder for the filth check cycle, basically contains how much filth dwarf sees numerically.
|
||||
for(var/fuck in owner.fov_view(7)) //hello byond for view loop.
|
||||
if(istype(fuck, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = fuck
|
||||
if(H.stat == DEAD || (HAS_TRAIT(H, TRAIT_FAKEDEATH)))
|
||||
filth_counter += 10
|
||||
if(istype(fuck, /obj/effect/decal/cleanable/blood))
|
||||
if(istype(fuck, /obj/effect/decal/cleanable/blood/gibs))
|
||||
filth_counter += 1
|
||||
else
|
||||
filth_counter += 0.1
|
||||
if(istype(fuck,/obj/effect/decal/cleanable/vomit)) //They are disgusted by their own vomit too.
|
||||
filth_counter += 10 //Dwarves could technically chainstun each other in a vomit tantrum spiral.
|
||||
switch(filth_counter)
|
||||
if(11 to 25)
|
||||
if(last_filth_spam + 40 SECONDS < world.time)
|
||||
to_chat(owner, "<span class = 'warning'>Someone should really clean up in here!</span>")
|
||||
last_filth_spam = world.time
|
||||
if(26 to 50)
|
||||
if(prob(6)) //And then the probability they vomit along with it.
|
||||
to_chat(owner, "<span class = 'danger'>The stench makes you queasy.</span>")
|
||||
owner.vomit(10) //I think vomit should stay over a disgust adjustment.
|
||||
if(51 to 75)
|
||||
if(prob(9))
|
||||
to_chat(owner, "<span class = 'danger'>By Armok! You won't be able to keep alcohol down at all!</span>")
|
||||
owner.vomit(20) //Its more funny
|
||||
if(76 to 100)
|
||||
if(prob(11))
|
||||
to_chat(owner, "<span class = 'userdanger'>You can't live in such FILTH!</span>")
|
||||
owner.adjustToxLoss(10) //Now they start dying.
|
||||
owner.vomit(20)
|
||||
if(101 to INFINITY) //Now they will really start dying
|
||||
if(last_filth_spam + 12 SECONDS < world.time)
|
||||
to_chat(owner, "<span class = 'userdanger'> THERES TOO MUCH FILTH, OH GODS THE FILTH!</span>")
|
||||
last_filth_spam = world.time
|
||||
if(prob(40))
|
||||
owner.adjustToxLoss(15)
|
||||
owner.vomit(30)
|
||||
CHECK_TICK //Check_tick right here, its motherfuckin magic. (To me at least)
|
||||
dwarf_eth_ticker++
|
||||
if(dwarf_eth_ticker >= 1) //Alcohol reagent check should be around 2 seconds, since a tick is around 2 seconds.
|
||||
dwarf_eth_cycle()
|
||||
dwarf_eth_ticker = 0
|
||||
|
||||
//Handles the dwarf alcohol cycle tied to on_life, it ticks in dwarf_cycle_ticker.
|
||||
/obj/item/organ/dwarfgland/proc/dwarf_eth_cycle()
|
||||
|
||||
@@ -523,7 +523,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
|
||||
//this updates all special effects: stun, sleeping, knockdown, druggy, stuttering, etc..
|
||||
/mob/living/carbon/handle_status_effects()
|
||||
..()
|
||||
if(getStaminaLoss() && !(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) //CIT CHANGE - prevents stamina regen while combat mode is active
|
||||
if(getStaminaLoss() && !SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) //CIT CHANGE - prevents stamina regen while combat mode is active
|
||||
adjustStaminaLoss(!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? -7.5 : -6) : -3)//CIT CHANGE - decreases adjuststaminaloss to stop stamina damage from being such a joke
|
||||
|
||||
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && incomingstammult != 1)
|
||||
|
||||
@@ -322,7 +322,7 @@
|
||||
set_pull_offsets(M, state)
|
||||
|
||||
/mob/living/proc/set_pull_offsets(mob/living/M, grab_state = GRAB_PASSIVE)
|
||||
if(M.buckled || M.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
if(M.buckled || SEND_SIGNAL(M, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE))
|
||||
return //don't make them change direction or offset them if they're buckled into something or in combat mode.
|
||||
var/offset = 0
|
||||
switch(grab_state)
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
/mob/living/ComponentInitialize()
|
||||
. = ..()
|
||||
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_COMBAT_MODE_LOCKED), .proc/update_combat_lock)
|
||||
|
||||
/mob/living/proc/update_combat_lock()
|
||||
var/locked = HAS_TRAIT(src, TRAIT_COMBAT_MODE_LOCKED)
|
||||
var/desired = (combat_flags & COMBAT_FLAG_COMBAT_TOGGLED)
|
||||
var/actual = (combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
if(actual)
|
||||
if(locked)
|
||||
disable_combat_mode(FALSE, TRUE, FALSE, FALSE)
|
||||
else if(!desired)
|
||||
disable_combat_mode(TRUE, TRUE, FALSE, FALSE)
|
||||
else
|
||||
if(desired && !locked)
|
||||
enable_combat_mode(FALSE, TRUE, FALSE, FALSE)
|
||||
update_combat_mode_icon()
|
||||
|
||||
/mob/living/proc/disable_combat_mode(silent = TRUE, was_forced = FALSE, visible = FALSE, update_icon = TRUE)
|
||||
if(!(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) || (combat_flags & COMBAT_FLAG_COMBAT_FORCED))
|
||||
return
|
||||
DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
SEND_SIGNAL(src, COMSIG_LIVING_COMBAT_DISABLED, was_forced)
|
||||
if(visible)
|
||||
visible_message("<span class='warning'>[src] goes limp.</span>", "<span class='warning'>Your muscles are forcibly relaxed!</span>")
|
||||
else if(!silent)
|
||||
to_chat(src, was_forced? "<span class='warning'>Your muscles are forcibly relaxed!</span>" : "<span class='warning'>You relax your muscles.</span>")
|
||||
if(update_icon)
|
||||
update_combat_mode_icon()
|
||||
|
||||
/mob/living/proc/enable_combat_mode(silent = TRUE, was_forced = FALSE, visible = FALSE, update_icon = TRUE)
|
||||
if(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
return
|
||||
ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
SEND_SIGNAL(src, COMSIG_LIVING_COMBAT_ENABLED, was_forced)
|
||||
if(visible)
|
||||
visible_message("<span class='warning'>[src] drops into a combative stance!</span>", "<span class='warning'>You drop into a combative stance!</span>")
|
||||
else if(!silent)
|
||||
to_chat(src, was_forced? "<span class='warning'>Your muscles reflexively tighten!</span>" : "<span class='warning'>You tighten your muscles.</span>")
|
||||
if(update_icon)
|
||||
update_combat_mode_icon()
|
||||
|
||||
/// Updates the combat mode HUD icon.
|
||||
/mob/living/proc/update_combat_mode_icon()
|
||||
var/obj/screen/combattoggle/T = locate() in hud_used?.static_inventory
|
||||
T?.update_icon()
|
||||
|
||||
/// Enables intentionally being in combat mode. Please try not to use this proc for feedback whenever possible.
|
||||
/mob/living/proc/enable_intentional_combat_mode(silent = TRUE, visible = FALSE)
|
||||
if((combat_flags & COMBAT_FLAG_COMBAT_TOGGLED) && (combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
return
|
||||
ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_TOGGLED)
|
||||
if(!HAS_TRAIT(src, TRAIT_COMBAT_MODE_LOCKED) && !(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
enable_combat_mode(silent, FALSE, visible, FALSE)
|
||||
update_combat_mode_icon()
|
||||
client?.show_popup_menus = FALSE
|
||||
return TRUE
|
||||
|
||||
/// Disables intentionally being in combat mode. Please try not to use this proc for feedback whenever possible.
|
||||
/mob/living/proc/disable_intentional_combat_mode(silent = TRUE, visible = FALSE)
|
||||
if(!(combat_flags & COMBAT_FLAG_COMBAT_TOGGLED) && !(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
return
|
||||
if(combat_flags & COMBAT_FLAG_COMBAT_FORCED)
|
||||
return
|
||||
DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_TOGGLED)
|
||||
if(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
disable_combat_mode(silent, FALSE, visible, FALSE)
|
||||
update_combat_mode_icon()
|
||||
client?.show_popup_menus = TRUE
|
||||
return TRUE
|
||||
|
||||
/// Toggles whether the user is intentionally in combat mode. THIS should be the proc you generally use! Has built in visual/to other player feedback, as well as an audible cue to ourselves.
|
||||
/mob/living/proc/user_toggle_intentional_combat_mode(visible = TRUE)
|
||||
var/old = (combat_flags & COMBAT_FLAG_COMBAT_TOGGLED)
|
||||
if(old)
|
||||
if(combat_flags & COMBAT_FLAG_COMBAT_FORCED)
|
||||
to_chat(src, "<span class='warning'>You are unable to relax your muscles.</span>")
|
||||
return
|
||||
disable_intentional_combat_mode()
|
||||
playsound_local(src, 'sound/misc/ui_toggleoff.ogg', 50, FALSE, pressure_affected = FALSE) //Slightly modified version of the above!
|
||||
else if(CAN_TOGGLE_COMBAT_MODE(src))
|
||||
enable_intentional_combat_mode()
|
||||
var/current = (combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) //because we could be locked
|
||||
if(current != old) //only sound effect if you succeeded. Could have the feedback system be better but shrug, someone else can do it.
|
||||
if(current)
|
||||
playsound_local(src, 'sound/misc/ui_toggle.ogg', 50, FALSE, pressure_affected = FALSE) //Sound from interbay!
|
||||
if(visible)
|
||||
if(world.time >= combatmessagecooldown)
|
||||
combatmessagecooldown = world.time + 10 SECONDS
|
||||
if(a_intent != INTENT_HELP)
|
||||
visible_message("<span class='warning'>[src] [resting ? "tenses up" : (prob(95)? "drops into a combative stance" : (prob(95)? "poses aggressively" : "asserts dominance with their pose"))].</span>")
|
||||
else
|
||||
visible_message("<span class='notice'>[src] [pick("looks","seems","goes")] [pick("alert","attentive","vigilant")].</span>")
|
||||
@@ -122,7 +122,7 @@
|
||||
|
||||
//// CITADEL STATION COMBAT ////
|
||||
/// See __DEFINES/combat.dm
|
||||
var/combat_flags = COMBAT_FLAGS_STAMSYSTEM_EXEMPT
|
||||
var/combat_flags = COMBAT_FLAGS_SPRINT_EXEMPT
|
||||
/// Next world.time when we will show a visible message on entering combat mode voluntarily again.
|
||||
var/combatmessagecooldown = 0
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
spark_system.attach(src)
|
||||
|
||||
wires = new /datum/wires/robot(src)
|
||||
AddComponent(/datum/component/empprotection, EMP_PROTECT_WIRES)
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_WIRES)
|
||||
|
||||
robot_modules_background = new()
|
||||
robot_modules_background.icon_state = "block"
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
maxHealth = 100
|
||||
health = 100
|
||||
|
||||
combat_flags = COMBAT_FLAGS_DEFAULT
|
||||
|
||||
var/custom_name = ""
|
||||
var/braintype = "Cyborg"
|
||||
var/obj/item/robot_suit/robot_suit = null //Used for deconstruction to remember what the borg was constructed out of..
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
speech_span = SPAN_ROBOT
|
||||
flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1
|
||||
vore_flags = NO_VORE
|
||||
/// Enable sprint system but not stamina
|
||||
combat_flags = COMBAT_FLAGS_STAMEXEMPT_YESSPRINT
|
||||
|
||||
var/datum/ai_laws/laws = null//Now... THEY ALL CAN ALL HAVE LAWS
|
||||
var/last_lawchange_announce = 0
|
||||
|
||||
@@ -500,3 +500,33 @@
|
||||
user.visible_message("[user] milks [src] using \the [O].", "<span class='notice'>You milk [src] using \the [O].</span>")
|
||||
else
|
||||
to_chat(user, "<span class='danger'>The udder is dry. Wait a bit longer...</span>")
|
||||
|
||||
/mob/living/simple_animal/deer
|
||||
name = "doe"
|
||||
desc = "A gentle, peaceful forest animal. How did this get into space?"
|
||||
icon_state = "deer-doe"
|
||||
icon_living = "deer-doe"
|
||||
icon_dead = "deer-doe-dead"
|
||||
gender = FEMALE
|
||||
mob_biotypes = MOB_ORGANIC|MOB_BEAST
|
||||
speak = list("Weeeeeeee?","Weeee","WEOOOOOOOOOO")
|
||||
speak_emote = list("grunts","grunts lowly")
|
||||
emote_hear = list("brays.")
|
||||
emote_see = list("shakes its head.")
|
||||
speak_chance = 1
|
||||
turns_per_move = 5
|
||||
see_in_dark = 6
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 3)
|
||||
response_help_continuous = "pets"
|
||||
response_help_simple = "pet"
|
||||
response_disarm_continuous = "gently nudges"
|
||||
response_disarm_simple = "gently nudge"
|
||||
response_harm_continuous = "kicks"
|
||||
response_harm_simple = "kick"
|
||||
attack_verb_continuous = "bucks"
|
||||
attack_verb_simple = "buck"
|
||||
attack_sound = 'sound/weapons/punch1.ogg'
|
||||
health = 75
|
||||
maxHealth = 75
|
||||
blood_volume = BLOOD_VOLUME_NORMAL
|
||||
footstep_type = FOOTSTEP_MOB_SHOE
|
||||
@@ -27,7 +27,7 @@
|
||||
glide_size = 2
|
||||
footstep_type = FOOTSTEP_MOB_CLAW
|
||||
|
||||
/mob/living/simple_animal/pet/fox/ComponentInitialize()
|
||||
/mob/living/simple_animal/pet/sloth/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/mob_holder, "sloth") //finally oranges can be held
|
||||
|
||||
|
||||
@@ -23,29 +23,21 @@
|
||||
for(var/ab in boss_abilities)
|
||||
boss_abilities -= ab
|
||||
var/datum/action/boss/AB = new ab()
|
||||
AB.boss = src
|
||||
AB.Grant(src)
|
||||
boss_abilities += AB
|
||||
|
||||
atb.assign_abilities(boss_abilities)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/boss/Destroy()
|
||||
qdel(atb)
|
||||
atb = null
|
||||
for(var/ab in boss_abilities)
|
||||
var/datum/action/boss/AB = ab
|
||||
AB.boss = null
|
||||
AB.Remove(src)
|
||||
qdel(AB)
|
||||
boss_abilities.Cut()
|
||||
QDEL_NULL(atb)
|
||||
QDEL_LIST(boss_abilities)
|
||||
return ..()
|
||||
|
||||
|
||||
//Action datum for bosses
|
||||
//Override Trigger() as shown below to do things
|
||||
/datum/action/boss
|
||||
check_flags = AB_CHECK_CONSCIOUS //Incase the boss is given a player
|
||||
required_mobility_flags = NONE
|
||||
var/boss_cost = 100 //Cost of usage for the boss' AI 1-100
|
||||
var/usage_probability = 100
|
||||
var/mob/living/simple_animal/hostile/boss/boss
|
||||
@@ -53,23 +45,34 @@
|
||||
var/needs_target = TRUE //Does the boss need to have a target? (Only matters for the AI)
|
||||
var/say_when_triggered = "" //What does the boss Say() when the ability triggers?
|
||||
|
||||
/datum/action/boss/Destroy()
|
||||
boss = null
|
||||
return ..()
|
||||
|
||||
/datum/action/boss/Grant(mob/M)
|
||||
. = ..()
|
||||
boss = owner
|
||||
|
||||
/datum/action/boss/Remove(mob/M)
|
||||
. = ..()
|
||||
boss = null
|
||||
|
||||
/datum/action/boss/Trigger()
|
||||
. = ..()
|
||||
if(.)
|
||||
if(!istype(boss, boss_type))
|
||||
return 0
|
||||
if(!boss.atb)
|
||||
return 0
|
||||
if(boss.atb.points < boss_cost)
|
||||
return 0
|
||||
if(!boss.client)
|
||||
if(needs_target && !boss.target)
|
||||
return 0
|
||||
if(boss)
|
||||
if(say_when_triggered)
|
||||
boss.say(say_when_triggered, forced = "boss action")
|
||||
if(!boss.atb.spend(boss_cost))
|
||||
return 0
|
||||
if(!.)
|
||||
return
|
||||
if(!istype(boss, boss_type))
|
||||
return FALSE
|
||||
if(!boss.atb)
|
||||
return FALSE
|
||||
if(boss.atb.points < boss_cost)
|
||||
return FALSE
|
||||
if(!boss.client && needs_target && !boss.target)
|
||||
return FALSE
|
||||
if(!boss.atb.spend(boss_cost))
|
||||
return FALSE
|
||||
if(say_when_triggered)
|
||||
boss.say(say_when_triggered, forced = "boss action")
|
||||
|
||||
//Example:
|
||||
/*
|
||||
@@ -83,7 +86,8 @@
|
||||
/datum/boss_active_timed_battle
|
||||
var/list/abilities //a list of /datum/action/boss owned by a boss mob
|
||||
var/point_regen_delay = 5
|
||||
var/points = 50 //1-100, start with 50 so we can use some abilities but not insta-buttfug somebody
|
||||
var/max_points = 100
|
||||
var/points = 50 //start with 50 so we can use some abilities but not insta-buttfug somebody
|
||||
var/next_point_time = 0
|
||||
var/chance_to_hold_onto_points = 50
|
||||
var/highest_cost = 0
|
||||
@@ -108,22 +112,22 @@
|
||||
/datum/boss_active_timed_battle/proc/spend(cost)
|
||||
if(cost <= points)
|
||||
points = max(0,points-cost)
|
||||
return 1
|
||||
return 0
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
/datum/boss_active_timed_battle/proc/refund(cost)
|
||||
points = min(points+cost, 100)
|
||||
points = min(points+cost, max_points)
|
||||
|
||||
|
||||
/datum/boss_active_timed_battle/process()
|
||||
if(world.time >= next_point_time)
|
||||
if(world.time >= next_point_time && points < max_points)
|
||||
next_point_time = world.time + point_regen_delay
|
||||
points = min(100, ++points) //has to be out of 100
|
||||
points = min(max_points, ++points) //has to be out of 100
|
||||
|
||||
if(abilities)
|
||||
chance_to_hold_onto_points = highest_cost*0.5
|
||||
if(points != 100 && prob(chance_to_hold_onto_points))
|
||||
if(points != max_points && prob(chance_to_hold_onto_points))
|
||||
return //Let's save our points for a better ability (unless we're at max points, in which case we can't save anymore!)
|
||||
if(!boss.client)
|
||||
abilities = shuffle(abilities)
|
||||
@@ -135,5 +139,5 @@
|
||||
|
||||
/datum/boss_active_timed_battle/Destroy()
|
||||
abilities = null
|
||||
SSobj.processing.Remove(src)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
@@ -32,25 +32,47 @@
|
||||
name = "Summon Minions"
|
||||
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
|
||||
button_icon_state = "art_summon"
|
||||
usage_probability = 40
|
||||
usage_probability = 20
|
||||
boss_cost = 30
|
||||
boss_type = /mob/living/simple_animal/hostile/boss/paper_wizard
|
||||
needs_target = FALSE
|
||||
say_when_triggered = "Rise, my creations! Jump off your pages and into this realm!"
|
||||
var/static/summoned_minions = 0
|
||||
var/list/summoned_minions = list()
|
||||
var/maximum_stickmen = 6
|
||||
var/stickmen_to_summon = 3
|
||||
|
||||
/datum/action/boss/wizard_summon_minions/Trigger()
|
||||
if(summoned_minions <= 6 && ..())
|
||||
var/list/minions = list(
|
||||
/mob/living/simple_animal/hostile/stickman,
|
||||
/mob/living/simple_animal/hostile/stickman/ranged,
|
||||
/mob/living/simple_animal/hostile/stickman/dog)
|
||||
var/list/directions = GLOB.cardinals.Copy()
|
||||
for(var/i in 1 to 3)
|
||||
var/minions_chosen = pick_n_take(minions)
|
||||
new minions_chosen (get_step(boss,pick_n_take(directions)), 1)
|
||||
summoned_minions += 3;
|
||||
. =..()
|
||||
if(!.)
|
||||
return
|
||||
var/to_summon = stickmen_to_summon
|
||||
var/current_len = length(summoned_minions)
|
||||
if(current_len > maximum_stickmen - stickmen_to_summon)
|
||||
for(var/a in (maximum_stickmen - stickmen_to_summon) to current_len)
|
||||
var/mob/living/simple_animal/hostile/stickman/S = popleft(summoned_minions)
|
||||
if(!S.client)
|
||||
qdel(S)
|
||||
else
|
||||
S.forceMove(boss.drop_location())
|
||||
S.revive(TRUE)
|
||||
summoned_minions += S
|
||||
to_summon--
|
||||
|
||||
var/static/list/minions = list(
|
||||
/mob/living/simple_animal/hostile/stickman,
|
||||
/mob/living/simple_animal/hostile/stickman/ranged,
|
||||
/mob/living/simple_animal/hostile/stickman/dog)
|
||||
|
||||
var/list/directions = GLOB.cardinals.Copy()
|
||||
for(var/i in 1 to to_summon)
|
||||
var/minions_chosen = pick(minions)
|
||||
var/mob/living/simple_animal/hostile/stickman/S = new minions_chosen (get_step(boss,pick_n_take(directions)), 1)
|
||||
S.faction = boss.faction
|
||||
RegisterSignal(S, COMSIG_PARENT_QDELETING, .proc/remove_from_list)
|
||||
summoned_minions += S
|
||||
|
||||
/datum/action/boss/wizard_summon_minions/proc/remove_from_list(datum/source, forced)
|
||||
summoned_minions -= source
|
||||
|
||||
//Mimic Ability
|
||||
//Summons mimics of himself with magical papercraft
|
||||
@@ -66,28 +88,32 @@
|
||||
say_when_triggered = ""
|
||||
|
||||
/datum/action/boss/wizard_mimic/Trigger()
|
||||
if(..())
|
||||
var/mob/living/target
|
||||
if(!boss.client) //AI's target
|
||||
target = boss.target
|
||||
else //random mob
|
||||
var/list/threats = boss.PossibleThreats()
|
||||
if(threats.len)
|
||||
target = pick(threats)
|
||||
if(target)
|
||||
var/mob/living/simple_animal/hostile/boss/paper_wizard/wiz = boss
|
||||
var/directions = GLOB.cardinals.Copy()
|
||||
for(var/i in 1 to 3)
|
||||
var/mob/living/simple_animal/hostile/boss/paper_wizard/copy/C = new (get_step(target,pick_n_take(directions)))
|
||||
wiz.copies += C
|
||||
C.original = wiz
|
||||
C.say("My craft defines me, you could even say it IS me!")
|
||||
wiz.say("My craft defines me, you could even say it IS me!")
|
||||
wiz.forceMove(get_step(target,pick_n_take(directions)))
|
||||
wiz.minimum_distance = 1 //so he doesn't run away and ruin everything
|
||||
wiz.retreat_distance = 0
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/mob/living/target
|
||||
if(!boss.client) //AI's target
|
||||
target = boss.target
|
||||
else //random mob
|
||||
var/list/threats = boss.PossibleThreats()
|
||||
if(threats.len)
|
||||
target = pick(threats)
|
||||
else
|
||||
boss.atb.refund(boss_cost)
|
||||
to_chat(owner, "<span class='warning'>There is no potential foe of different faction around to attack</span>")
|
||||
if(target)
|
||||
var/mob/living/simple_animal/hostile/boss/paper_wizard/wiz = boss
|
||||
var/directions = GLOB.cardinals.Copy()
|
||||
for(var/i in 1 to 3)
|
||||
var/mob/living/simple_animal/hostile/boss/paper_wizard/copy/C = new (get_step(target,pick_n_take(directions)))
|
||||
wiz.copies += C
|
||||
C.original = wiz
|
||||
C.say("My craft defines me, you could even say it IS me!")
|
||||
wiz.say("My craft defines me, you could even say it IS me!")
|
||||
wiz.forceMove(get_step(target,pick_n_take(directions)))
|
||||
wiz.minimum_distance = 1 //so he doesn't run away and ruin everything
|
||||
wiz.retreat_distance = 0
|
||||
else
|
||||
boss.atb.refund(boss_cost)
|
||||
|
||||
/mob/living/simple_animal/hostile/boss/paper_wizard/copy
|
||||
desc = "'Tis a ruse!"
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
var/casingtype //set ONLY it and NULLIFY projectiletype, if we have projectile IN CASING
|
||||
var/move_to_delay = 3 //delay for the automated movement.
|
||||
var/list/friends = list()
|
||||
var/list/foes = list()
|
||||
var/list/emote_taunt = list()
|
||||
var/taunt_chance = 0
|
||||
|
||||
@@ -62,6 +63,8 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/Destroy()
|
||||
targets_from = null
|
||||
friends = null
|
||||
foes = null
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/Life()
|
||||
@@ -193,7 +196,7 @@
|
||||
|
||||
// Please do not add one-off mob AIs here, but override this function for your mob
|
||||
/mob/living/simple_animal/hostile/CanAttack(atom/the_target)//Can we actually attack a possible target?
|
||||
if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids
|
||||
if(!the_target || the_target.type == /atom/movable/lighting_object || isturf(the_target)) // bail out on invalids
|
||||
return FALSE
|
||||
|
||||
if(ismob(the_target)) //Target is in godmode, ignore it.
|
||||
@@ -208,13 +211,13 @@
|
||||
if(search_objects < 2)
|
||||
if(isliving(the_target))
|
||||
var/mob/living/L = the_target
|
||||
var/faction_check = faction_check_mob(L)
|
||||
var/faction_check = !foes[L] && faction_check_mob(L)
|
||||
if(robust_searching)
|
||||
if(faction_check && !attack_same)
|
||||
return FALSE
|
||||
if(L.stat > stat_attack)
|
||||
if(L.stat > stat_attack || (L.stat == UNCONSCIOUS && stat_attack == UNCONSCIOUS && HAS_TRAIT(L, TRAIT_DEATHCOMA)))
|
||||
return FALSE
|
||||
if(L in friends)
|
||||
if(friends[L] > 0 && foes[L] < 1)
|
||||
return FALSE
|
||||
else
|
||||
if((faction_check && !attack_same) || L.stat)
|
||||
|
||||
@@ -75,7 +75,7 @@ Difficulty: Medium
|
||||
|
||||
/obj/item/projectile/kinetic/miner
|
||||
damage = 40
|
||||
speed = 0.9
|
||||
pixels_per_second = TILES_TO_PIXELS(11.111)
|
||||
icon_state = "ka_tracer"
|
||||
range = MINER_DASH_RANGE
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ Difficulty: Very Hard
|
||||
icon_state= "chronobolt"
|
||||
damage = 25
|
||||
armour_penetration = 100
|
||||
speed = 2
|
||||
pixels_per_second = TILES_TO_PIXELS(5)
|
||||
eyeblur = 0
|
||||
damage_type = BRUTE
|
||||
pass_flags = PASSTABLE
|
||||
|
||||
@@ -71,7 +71,10 @@ Difficulty: Extremely Hard
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/OpenFire()
|
||||
check_enraged()
|
||||
projectile_speed_multiplier = 1 - enraged * 0.25
|
||||
if(enraged)
|
||||
projectile_speed_multiplier = 1.33
|
||||
else
|
||||
projectile_speed_multiplier = 1
|
||||
SetRecoveryTime(100, 100)
|
||||
|
||||
if(client)
|
||||
@@ -109,7 +112,7 @@ Difficulty: Extremely Hard
|
||||
icon_state = "ice_1"
|
||||
damage = 20
|
||||
armour_penetration = 100
|
||||
speed = 10
|
||||
pixels_per_second = TILES_TO_PIXELS(1)
|
||||
homing_turn_speed = 30
|
||||
damage_type = BURN
|
||||
|
||||
@@ -123,7 +126,7 @@ Difficulty: Extremely Hard
|
||||
icon_state = "nuclear_particle"
|
||||
damage = 5
|
||||
armour_penetration = 100
|
||||
speed = 4
|
||||
pixels_per_second = TILES_TO_PIXELS(2.5)
|
||||
damage_type = BRUTE
|
||||
|
||||
/obj/item/projectile/ice_blast
|
||||
@@ -131,7 +134,7 @@ Difficulty: Extremely Hard
|
||||
icon_state = "ice_2"
|
||||
damage = 15
|
||||
armour_penetration = 100
|
||||
speed = 4
|
||||
pixels_per_second = TILES_TO_PIXELS(2.5)
|
||||
damage_type = BRUTE
|
||||
|
||||
/obj/item/projectile/ice_blast/on_hit(atom/target, blocked = FALSE)
|
||||
@@ -185,7 +188,7 @@ Difficulty: Extremely Hard
|
||||
if(!startloc || !endloc)
|
||||
break
|
||||
var/obj/item/projectile/ice_blast/P = new(startloc)
|
||||
P.speed *= projectile_speed_multiplier
|
||||
P.pixels_per_second *= projectile_speed_multiplier
|
||||
P.preparePixelProjectile(endloc, startloc, null, angle + rand(-10, 10))
|
||||
P.firer = firer
|
||||
if(original)
|
||||
@@ -201,7 +204,7 @@ Difficulty: Extremely Hard
|
||||
if(!endloc)
|
||||
break
|
||||
var/obj/item/projectile/P = new /obj/item/projectile/snowball(startloc)
|
||||
P.speed *= projectile_speed_multiplier
|
||||
P.pixels_per_second *= projectile_speed_multiplier
|
||||
P.preparePixelProjectile(endloc, startloc, null, rand(-spread, spread))
|
||||
P.firer = src
|
||||
if(target)
|
||||
@@ -220,7 +223,7 @@ Difficulty: Extremely Hard
|
||||
if(!endloc)
|
||||
break
|
||||
var/obj/item/projectile/P = new /obj/item/projectile/ice_blast(startloc)
|
||||
P.speed *= projectile_speed_multiplier
|
||||
P.pixels_per_second *= projectile_speed_multiplier
|
||||
P.preparePixelProjectile(endloc, startloc, null, spread)
|
||||
P.firer = src
|
||||
if(target)
|
||||
|
||||
@@ -145,6 +145,7 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/proc/SetRecoveryTime(buffer_time)
|
||||
recovery_time = world.time + buffer_time
|
||||
ranged_cooldown = max(ranged_cooldown, world.time + buffer_time) // CITADEL BANDAID FIX FOR MEGAFAUNA NOT RESPECTING RECOVERY TIME.
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill)
|
||||
if(!medal_type || (flags_1 & ADMIN_SPAWNED_1)) //Don't award medals if the medal type isn't set
|
||||
|
||||
@@ -84,8 +84,33 @@
|
||||
crusher_loot = /obj/item/crusher_trophy/watcher_wing
|
||||
loot = list()
|
||||
butcher_results = list(/obj/item/stack/ore/diamond = 2, /obj/item/stack/sheet/sinew = 2, /obj/item/stack/sheet/bone = 1)
|
||||
search_objects = 1
|
||||
wanted_objects = list(/obj/item/pen/survival, /obj/item/stack/ore/diamond)
|
||||
field_of_vision_type = FOV_270_DEGREES //Obviously, it's one eyeball.
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/Life()
|
||||
. = ..()
|
||||
if(stat == CONSCIOUS)
|
||||
consume_bait()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/proc/consume_bait()
|
||||
var/obj/item/stack/ore/diamond/diamonds = locate(/obj/item/stack/ore/diamond) in oview(src, 9)
|
||||
var/obj/item/pen/survival/bait = locate(/obj/item/pen/survival) in oview(src, 9)
|
||||
if(!diamonds && !bait)
|
||||
return
|
||||
if(diamonds)
|
||||
var/distanced = 0
|
||||
distanced = get_dist(loc,diamonds.loc)
|
||||
if(distanced <= 1 && diamonds)
|
||||
qdel(diamonds)
|
||||
src.visible_message("<span class='notice'>[src] consumes [diamonds], and it disappears! ...At least, you think.</span>")
|
||||
if(bait)
|
||||
var/distanceb = 0
|
||||
distanceb = get_dist(loc,bait.loc)
|
||||
if(distanceb <= 1 && bait)
|
||||
qdel(bait)
|
||||
visible_message("<span class='notice'>[src] examines [bait] closer, and telekinetically shatters the pen.</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random/Initialize()
|
||||
. = ..()
|
||||
if(prob(1))
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
icon_state= "chronobolt"
|
||||
damage = 15
|
||||
armour_penetration = 60
|
||||
speed = 2
|
||||
pixels_per_second = TILES_TO_PIXELS(5)
|
||||
eyeblur = 0
|
||||
damage_type = BRUTE
|
||||
pass_flags = PASSTABLE
|
||||
|
||||
@@ -298,5 +298,5 @@
|
||||
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion(user.loc)
|
||||
A.flags_1 |= (flags_1 & ADMIN_SPAWNED_1)
|
||||
A.GiveTarget(target)
|
||||
A.friends = user
|
||||
A.friends[user]++
|
||||
A.faction = user.faction.Copy()
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
speed = 0
|
||||
blood_volume = 0
|
||||
stat_attack = UNCONSCIOUS
|
||||
robust_searching = 1
|
||||
robust_searching = TRUE //This is also required for the paper robe rallying to work.
|
||||
environment_smash = ENVIRONMENT_SMASH_NONE
|
||||
maxHealth = 100
|
||||
health = 100
|
||||
@@ -30,8 +30,6 @@
|
||||
faction = list("hostile","stickman")
|
||||
check_friendly_fire = 1
|
||||
status_flags = CANPUSH
|
||||
var/datum/action/boss/wizard_summon_minions/changesummons = /datum/action/boss/wizard_summon_minions
|
||||
var/summoned_by_wizard = 0
|
||||
|
||||
/mob/living/simple_animal/hostile/stickman/ranged
|
||||
ranged = 1
|
||||
@@ -43,7 +41,6 @@
|
||||
projectilesound = 'sound/misc/bang.ogg'
|
||||
loot = list(/obj/item/gun/ballistic/automatic/pistol/stickman)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/stickman/dog
|
||||
name = "Angry Stick Dog"
|
||||
desc = "Stickman's best friend, if he could see him at least."
|
||||
@@ -52,12 +49,6 @@
|
||||
icon_dead = "stickdog_dead"
|
||||
mob_biotypes = MOB_BEAST
|
||||
|
||||
/mob/living/simple_animal/hostile/stickman/Initialize(mapload, var/wizard_summoned)
|
||||
/mob/living/simple_animal/hostile/stickman/Initialize(mapload)
|
||||
. = ..()
|
||||
new /obj/effect/temp_visual/paper_scatter(src)
|
||||
summoned_by_wizard = wizard_summoned
|
||||
|
||||
/mob/living/simple_animal/hostile/stickman/death()
|
||||
..()
|
||||
if(summoned_by_wizard == 1)
|
||||
changesummons.summoned_minions --
|
||||
|
||||
@@ -571,12 +571,13 @@
|
||||
/mob/living/proc/cure_blind(source)
|
||||
REMOVE_TRAIT(src, TRAIT_BLIND, source)
|
||||
if(!HAS_TRAIT(src, TRAIT_BLIND))
|
||||
update_blindness()
|
||||
if(eye_blind <= 1) //little hack now that we don't actively check for trait and unconsciousness on update_blindness.
|
||||
adjust_blindness(-1)
|
||||
|
||||
/mob/living/proc/become_blind(source)
|
||||
if(!HAS_TRAIT(src, TRAIT_BLIND)) // not blind already, add trait then overlay
|
||||
ADD_TRAIT(src, TRAIT_BLIND, source)
|
||||
update_blindness()
|
||||
blind_eyes(1)
|
||||
else
|
||||
ADD_TRAIT(src, TRAIT_BLIND, source)
|
||||
|
||||
|
||||
@@ -128,15 +128,13 @@
|
||||
* * target (optional) is the other mob involved with the visible message. For example, the attacker in many combat messages.
|
||||
* * target_message (optional) is what the target mob will see e.g. "[src] does something to you!"
|
||||
*/
|
||||
/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, mob/target, target_message)
|
||||
/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, ignored_mobs, mob/target, target_message)
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
return
|
||||
var/list/hearers = get_hearers_in_view(vision_distance, src) //caches the hearers and then removes ignored mobs.
|
||||
if(!length(hearers))
|
||||
return
|
||||
if(!islist(ignored_mobs))
|
||||
ignored_mobs = list(ignored_mobs)
|
||||
hearers -= ignored_mobs
|
||||
|
||||
if(target_message && target && istype(target) && target.client)
|
||||
@@ -150,7 +148,7 @@
|
||||
msg = blind_message
|
||||
if(msg)
|
||||
target.show_message(msg, MSG_VISUAL,blind_message, MSG_AUDIBLE)
|
||||
else if(self_message)
|
||||
if(self_message)
|
||||
hearers -= src
|
||||
for(var/mob/M in hearers)
|
||||
if(!M.client)
|
||||
@@ -185,15 +183,13 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA
|
||||
* * hearing_distance (optional) is the range, how many tiles away the message can be heard.
|
||||
* * ignored_mobs (optional) doesn't show any message to any given mob in the list.
|
||||
*/
|
||||
/atom/proc/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, list/ignored_mobs)
|
||||
/atom/proc/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, ignored_mobs)
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
return
|
||||
var/list/hearers = get_hearers_in_view(hearing_distance, src)
|
||||
if(!length(hearers))
|
||||
return
|
||||
if(!islist(ignored_mobs))
|
||||
ignored_mobs = list(ignored_mobs)
|
||||
hearers -= ignored_mobs
|
||||
if(self_message)
|
||||
hearers -= src
|
||||
@@ -377,7 +373,7 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA
|
||||
return FALSE
|
||||
|
||||
new /obj/effect/temp_visual/point(A,invisibility)
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_MOB_POINTED, A)
|
||||
return TRUE
|
||||
|
||||
/mob/proc/can_resist()
|
||||
|
||||
@@ -127,6 +127,13 @@
|
||||
|
||||
var/registered_z
|
||||
|
||||
var/list/alerts = list() // contains /obj/screen/alert only // On /mob so clientless mobs will throw alerts properly
|
||||
var/list/screens = list()
|
||||
var/list/client_colours = list()
|
||||
var/hud_type = /datum/hud
|
||||
|
||||
var/datum/hSB/sandbox = null
|
||||
|
||||
var/mob/audiovisual_redirect //Mob to redirect messages, speech, and sounds to
|
||||
|
||||
var/siliconaccessareas = list()
|
||||
|
||||
@@ -101,10 +101,10 @@
|
||||
mob.throwing.finalize(FALSE)
|
||||
|
||||
var/atom/movable/AM = L.pulling
|
||||
if(AM && AM.density && !(L.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) && !ismob(AM))
|
||||
if(AM && AM.density && !SEND_SIGNAL(L, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE) && !ismob(AM))
|
||||
L.setDir(turn(L.dir, 180))
|
||||
|
||||
SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOVE, src, direction, n, oldloc)
|
||||
SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOVE, src, direction, n, oldloc, add_delay)
|
||||
|
||||
/// Process_Grab(): checks for grab, attempts to break if so. Return TRUE to prevent movement.
|
||||
/client/proc/Process_Grab()
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
* Sets a mob's blindness to an amount if it was not above it already, similar to how status effects work
|
||||
*/
|
||||
/mob/proc/blind_eyes(amount)
|
||||
var/old_blind = eye_blind || HAS_TRAIT(src, TRAIT_BLIND)
|
||||
eye_blind = max(eye_blind, amount)
|
||||
var/new_blind = eye_blind || HAS_TRAIT(src, TRAIT_BLIND)
|
||||
var/old_blind = eye_blind
|
||||
eye_blind = max((!eye_blind && stat == UNCONSCIOUS || HAS_TRAIT(src, TRAIT_BLIND)) ? 1 : eye_blind , amount)
|
||||
var/new_blind = eye_blind
|
||||
if(old_blind != new_blind)
|
||||
update_blindness()
|
||||
|
||||
@@ -36,21 +36,21 @@
|
||||
*/
|
||||
/mob/proc/adjust_blindness(amount)
|
||||
var/old_eye_blind = eye_blind
|
||||
eye_blind = max(0, eye_blind + amount)
|
||||
if(!old_eye_blind || !eye_blind && !HAS_TRAIT(src, TRAIT_BLIND))
|
||||
eye_blind = max((stat == UNCONSCIOUS || HAS_TRAIT(src, TRAIT_BLIND)) ? 1 : 0, eye_blind + amount)
|
||||
if(!old_eye_blind || !eye_blind)
|
||||
update_blindness()
|
||||
/**
|
||||
* Force set the blindness of a mob to some level
|
||||
*/
|
||||
/mob/proc/set_blindness(amount)
|
||||
var/old_eye_blind = eye_blind
|
||||
eye_blind = max(amount, 0)
|
||||
if(!old_eye_blind || !eye_blind && !HAS_TRAIT(src, TRAIT_BLIND))
|
||||
eye_blind = max(amount, (stat == UNCONSCIOUS || HAS_TRAIT(src, TRAIT_BLIND)) ? 1 : 0)
|
||||
if(!old_eye_blind || !eye_blind)
|
||||
update_blindness()
|
||||
|
||||
/// proc that adds and removes blindness overlays when necessary
|
||||
/mob/proc/update_blindness()
|
||||
if(stat == UNCONSCIOUS || HAS_TRAIT(src, TRAIT_BLIND) || eye_blind) // UNCONSCIOUS or has blind trait, or has temporary blindness
|
||||
if(eye_blind) // UNCONSCIOUS or has blind trait, or has temporary blindness
|
||||
if(stat == CONSCIOUS || stat == SOFT_CRIT)
|
||||
throw_alert("blind", /obj/screen/alert/blind)
|
||||
overlay_fullscreen("blind", /obj/screen/fullscreen/blind)
|
||||
|
||||
@@ -384,3 +384,6 @@
|
||||
|
||||
/obj/item/paper/crumpled/bloody
|
||||
icon_state = "scrap_bloodied"
|
||||
|
||||
/obj/item/paper/crumpled/muddy
|
||||
icon_state = "scrap_mud"
|
||||
|
||||
@@ -220,3 +220,17 @@
|
||||
item_state = initial(item_state)
|
||||
lefthand_file = initial(lefthand_file)
|
||||
righthand_file = initial(righthand_file)
|
||||
|
||||
/obj/item/pen/survival
|
||||
name = "survival pen"
|
||||
desc = "The latest in portable survival technology, this pen was designed as a miniature diamond pickaxe. Watchers find them very desirable for their diamond exterior."
|
||||
icon = 'icons/obj/bureaucracy.dmi'
|
||||
icon_state = "digging_pen"
|
||||
item_state = "pen"
|
||||
force = 3
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
custom_materials = list(/datum/material/iron=10, /datum/material/diamond=100, /datum/material/titanium = 10)
|
||||
pressure_resistance = 2
|
||||
grind_results = list(/datum/reagent/iron = 2, /datum/reagent/iodine = 1)
|
||||
tool_behaviour = TOOL_MINING //For the classic "digging out of prison with a spoon but you're in space so this analogy doesn't work" situation.
|
||||
toolspeed = 10 //You will never willingly choose to use one of these over a shovel.
|
||||
|
||||
@@ -337,3 +337,15 @@
|
||||
grind_results = list(/datum/reagent/iodine = 40, /datum/reagent/iron = 10)
|
||||
var/charges = 5
|
||||
var/max_charges = 5
|
||||
|
||||
/obj/item/toner/large
|
||||
name = "large toner cartridge"
|
||||
grind_results = list(/datum/reagent/iodine = 90, /datum/reagent/iron = 10)
|
||||
charges = 15
|
||||
max_charges = 15
|
||||
|
||||
/obj/item/toner/extreme
|
||||
name = "extremely large toner cartridge"
|
||||
desc = "Why would ANYONE need THIS MUCH TONER?"
|
||||
charges = 200
|
||||
max_charges = 200
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
name = "picture frame"
|
||||
desc = "The perfect showcase for your favorite deathtrap memories."
|
||||
icon = 'icons/obj/decals.dmi'
|
||||
custom_materials = null
|
||||
custom_materials = list(/datum/material/wood = 2000)
|
||||
flags_1 = 0
|
||||
icon_state = "frame-empty"
|
||||
result_path = /obj/structure/sign/picture_frame
|
||||
@@ -64,6 +64,7 @@
|
||||
desc = "Every time you look it makes you laugh."
|
||||
icon = 'icons/obj/decals.dmi'
|
||||
icon_state = "frame-empty"
|
||||
custom_materials = list(/datum/material/wood = 2000)
|
||||
var/obj/item/photo/framed
|
||||
var/persistence_id
|
||||
var/can_decon = TRUE
|
||||
|
||||
@@ -1411,6 +1411,7 @@
|
||||
if(WIRE_POWER1, WIRE_POWER2)
|
||||
if(!wires.is_cut(WIRE_POWER1) && !wires.is_cut(WIRE_POWER2))
|
||||
shorted = FALSE
|
||||
update()
|
||||
if(WIRE_AI)
|
||||
if(!wires.is_cut(WIRE_AI))
|
||||
aidisabled = FALSE
|
||||
|
||||
@@ -227,6 +227,7 @@
|
||||
icon_state = "h+cell"
|
||||
maxcharge = 15000
|
||||
chargerate = 2250
|
||||
rating = 2
|
||||
|
||||
/obj/item/stock_parts/cell/high/empty
|
||||
start_charged = FALSE
|
||||
@@ -237,6 +238,7 @@
|
||||
maxcharge = 20000
|
||||
custom_materials = list(/datum/material/glass=300)
|
||||
chargerate = 2000
|
||||
rating = 3
|
||||
|
||||
/obj/item/stock_parts/cell/super/empty
|
||||
start_charged = FALSE
|
||||
@@ -247,6 +249,7 @@
|
||||
maxcharge = 30000
|
||||
custom_materials = list(/datum/material/glass=400)
|
||||
chargerate = 3000
|
||||
rating = 4
|
||||
|
||||
/obj/item/stock_parts/cell/hyper/empty
|
||||
start_charged = FALSE
|
||||
@@ -258,6 +261,7 @@
|
||||
maxcharge = 40000
|
||||
custom_materials = list(/datum/material/glass=600)
|
||||
chargerate = 4000
|
||||
rating = 5
|
||||
|
||||
/obj/item/stock_parts/cell/bluespace/empty
|
||||
start_charged = FALSE
|
||||
@@ -313,7 +317,7 @@
|
||||
|
||||
/obj/item/stock_parts/cell/emproof/ComponentInitialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF)
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_SELF)
|
||||
|
||||
/obj/item/stock_parts/cell/emproof/empty
|
||||
start_charged = FALSE
|
||||
|
||||
@@ -314,8 +314,11 @@
|
||||
. = ..()
|
||||
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
|
||||
if(on && status == LIGHT_OK)
|
||||
SSvis_overlays.add_vis_overlay(src, overlayicon, base_state, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE, dir, clamp(light_power*250, 30, 200))
|
||||
|
||||
var/overlay_alpha = clamp(light_power*250, 30, 200)
|
||||
SSvis_overlays.add_vis_overlay(src, overlayicon, base_state, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE, dir, overlay_alpha)
|
||||
var/mutable_appearance/M = mutable_appearance(overlayicon, base_state)
|
||||
M.alpha = overlay_alpha
|
||||
. += M
|
||||
|
||||
// update the icon_state and luminosity of the light depending on its state
|
||||
/obj/machinery/light/proc/update(trigger = TRUE)
|
||||
|
||||
@@ -310,6 +310,7 @@
|
||||
if(H.gloves)
|
||||
var/obj/item/clothing/gloves/G = H.gloves
|
||||
if(G.siemens_coefficient == 0)
|
||||
SEND_SIGNAL(M, COMSIG_LIVING_SHOCK_PREVENTED, power_source, source, siemens_coeff, dist_check)
|
||||
return 0 //to avoid spamming with insulated glvoes on
|
||||
|
||||
var/area/source_area
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
/obj/machinery/power/emitter/ComponentInitialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
||||
|
||||
/obj/machinery/power/emitter/RefreshParts()
|
||||
var/max_firedelay = 120
|
||||
|
||||
@@ -61,7 +61,7 @@ field_generator power level display
|
||||
|
||||
/obj/machinery/field/generator/ComponentInitialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
||||
|
||||
/obj/machinery/field/generator/process()
|
||||
if(active == FG_ONLINE)
|
||||
|
||||
@@ -76,17 +76,18 @@
|
||||
var/datum/action/item_action/toggle_scope_zoom/azoom
|
||||
|
||||
var/dualwield_spread_mult = 1 //dualwield spread multiplier
|
||||
|
||||
|
||||
/// Just 'slightly' snowflakey way to modify projectile damage for projectiles fired from this gun.
|
||||
var/projectile_damage_multiplier = 1
|
||||
|
||||
var/automatic = 0 //can gun use it, 0 is no, anything above 0 is the delay between clicks in ds
|
||||
|
||||
/obj/item/gun/Initialize()
|
||||
. = ..()
|
||||
if(pin)
|
||||
if(no_pin_required)
|
||||
pin = null
|
||||
else
|
||||
pin = new pin(src)
|
||||
if(no_pin_required)
|
||||
pin = null
|
||||
else if(pin)
|
||||
pin = new pin(src)
|
||||
if(gun_light)
|
||||
alight = new (src)
|
||||
if(zoomable)
|
||||
@@ -214,8 +215,9 @@
|
||||
var/bonus_spread = 0
|
||||
var/loop_counter = 0
|
||||
|
||||
bonus_spread += getinaccuracy(user) //CIT CHANGE - adds bonus spread while not aiming
|
||||
if(ishuman(user) && user.a_intent == INTENT_HARM)
|
||||
if(user)
|
||||
bonus_spread += getinaccuracy(user) //CIT CHANGE - adds bonus spread while not aiming
|
||||
if(ishuman(user) && user.a_intent == INTENT_HARM && weapon_weight <= WEAPON_LIGHT)
|
||||
var/mob/living/carbon/human/H = user
|
||||
for(var/obj/item/gun/G in H.held_items)
|
||||
if(G == src || G.weapon_weight >= WEAPON_MEDIUM)
|
||||
@@ -564,12 +566,7 @@
|
||||
chambered = null
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/proc/getinaccuracy(mob/living/user)
|
||||
if(!isliving(user))
|
||||
return FALSE
|
||||
else
|
||||
var/mob/living/holdingdude = user
|
||||
if(istype(holdingdude) && (holdingdude.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
return 0
|
||||
else
|
||||
return ((weapon_weight * 25) * inaccuracy_modifier)
|
||||
/obj/item/gun/proc/getinaccuracy(mob/user)
|
||||
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
return ((weapon_weight * 25) * inaccuracy_modifier)
|
||||
return 0
|
||||
|
||||
@@ -313,19 +313,24 @@
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[src] is empty!</span>")
|
||||
|
||||
// IMPROVISED SHOTGUN //
|
||||
/////////////////////////////
|
||||
// IMPROVISED SHOTGUN //
|
||||
/////////////////////////////
|
||||
|
||||
/obj/item/gun/ballistic/revolver/doublebarrel/improvised
|
||||
name = "improvised shotgun"
|
||||
desc = "Essentially a tube that aims shotgun shells."
|
||||
desc = "A shoddy break-action breechloaded shotgun. Its lacklustre construction will probably result in it hurting people less than a normal shotgun."
|
||||
icon_state = "ishotgun"
|
||||
item_state = "shotgun"
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
weapon_weight = WEAPON_MEDIUM
|
||||
force = 10
|
||||
slot_flags = null
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised
|
||||
sawn_desc = "I'm just here for the gasoline."
|
||||
unique_reskin = null
|
||||
projectile_damage_multiplier = 0.8
|
||||
var/slung = FALSE
|
||||
|
||||
/obj/item/gun/ballistic/revolver/doublebarrel/improvised/attackby(obj/item/A, mob/user, params)
|
||||
|
||||
@@ -126,8 +126,9 @@
|
||||
/obj/item/gun/ballistic/shotgun/boltaction/improvised
|
||||
name = "Makeshift 7.62mm Rifle"
|
||||
icon_state = "ishotgun"
|
||||
icon_state = "irifle"
|
||||
item_state = "shotgun"
|
||||
desc = "A large zip gun more or less that takes a single 7.62mm bullet"
|
||||
desc = "A bolt-action breechloaded rifle that takes 7.62mm bullets."
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/boltaction/improvised
|
||||
can_bayonet = FALSE
|
||||
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
var/static/image/drained_overlay = image(icon = 'icons/obj/guns/energy.dmi', icon_state = "esniper_empty")
|
||||
|
||||
var/datum/action/item_action/zoom_lock_action/zoom_lock_action
|
||||
var/mob/listeningTo
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/debug
|
||||
delay = 0
|
||||
@@ -178,7 +177,6 @@
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
set_user(null)
|
||||
QDEL_LIST(current_tracers)
|
||||
listeningTo = null
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/aiming_beam(force_update = FALSE)
|
||||
@@ -260,17 +258,12 @@
|
||||
if(user == current_user)
|
||||
return
|
||||
stop_aiming(current_user)
|
||||
if(listeningTo)
|
||||
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
|
||||
listeningTo = null
|
||||
if(istype(current_user))
|
||||
LAZYREMOVE(current_user.mousemove_intercept_objects, src)
|
||||
if(current_user)
|
||||
UnregisterSignal(current_user, COMSIG_MOVABLE_MOVED)
|
||||
current_user = null
|
||||
if(istype(user))
|
||||
current_user = user
|
||||
LAZYOR(current_user.mousemove_intercept_objects, src)
|
||||
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/on_mob_move)
|
||||
listeningTo = user
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
|
||||
if(aiming)
|
||||
@@ -295,7 +288,7 @@
|
||||
if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
|
||||
return
|
||||
process_aim()
|
||||
if(fire_check())
|
||||
if(fire_check() && can_trigger_gun(M))
|
||||
sync_ammo()
|
||||
do_fire(M.client.mouseObject, M, FALSE, M.client.mouseParams, M.zone_selected)
|
||||
stop_aiming()
|
||||
|
||||
@@ -13,11 +13,16 @@
|
||||
var/pin_removeable = 0 // Can be replaced by any pin.
|
||||
var/obj/item/gun/gun
|
||||
|
||||
/obj/item/firing_pin/New(newloc)
|
||||
..()
|
||||
/obj/item/firing_pin/Initialize(newloc)
|
||||
. = ..()
|
||||
if(istype(newloc, /obj/item/gun))
|
||||
gun = newloc
|
||||
|
||||
/obj/item/firing_pin/Destroy()
|
||||
if(gun)
|
||||
gun.pin = null
|
||||
return ..()
|
||||
|
||||
/obj/item/firing_pin/afterattack(atom/target, mob/user, proximity_flag)
|
||||
. = ..()
|
||||
if(proximity_flag)
|
||||
@@ -224,24 +229,6 @@
|
||||
suit_requirement = /obj/item/clothing/suit/bluetag
|
||||
tagcolor = "blue"
|
||||
|
||||
/obj/item/firing_pin/Destroy()
|
||||
if(gun)
|
||||
gun.pin = null
|
||||
return ..()
|
||||
|
||||
//Station Locked
|
||||
|
||||
/obj/item/firing_pin/away
|
||||
name = "station locked pin"
|
||||
desc = "A firing pin that only will fire when off the station."
|
||||
|
||||
/obj/item/firing_pin/away/pin_auth(mob/living/user)
|
||||
var/area/station_area = get_area(src)
|
||||
if(!station_area || is_station_level(station_area.z))
|
||||
to_chat(user, "<span class='warning'>The pin beeps, refusing to fire.</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/firing_pin/security_level
|
||||
name = "security level firing pin"
|
||||
desc = "A sophisticated firing pin that authorizes operation based on its settings and current security level."
|
||||
@@ -325,3 +312,18 @@
|
||||
|
||||
/obj/item/firing_pin/security_level/pin_auth(mob/living/user)
|
||||
return (only_lethals && !(gun.chambered?.harmful)) || ISINRANGE(GLOB.security_level, min_sec_level, max_sec_level)
|
||||
|
||||
// Explorer Firing Pin- Prevents use on station Z-Level, so it's justifiable to give Explorers guns that don't suck.
|
||||
/obj/item/firing_pin/explorer
|
||||
name = "outback firing pin"
|
||||
desc = "A firing pin used by the austrailian defense force, retrofit to prevent weapon discharge on the station."
|
||||
icon_state = "firing_pin_explorer"
|
||||
fail_message = "<span class='warning'>CANNOT FIRE WHILE ON STATION, MATE!</span>"
|
||||
|
||||
// This checks that the user isn't on the station Z-level.
|
||||
/obj/item/firing_pin/explorer/pin_auth(mob/living/user)
|
||||
var/turf/station_check = get_turf(user)
|
||||
if(!station_check||is_station_level(station_check.z))
|
||||
to_chat(user, "<span class='warning'>You cannot use your weapon while on the station!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
|
||||
#define MOVES_HITSCAN -1 //Not actually hitscan but close as we get without actual hitscan.
|
||||
#define MUZZLE_EFFECT_PIXEL_INCREMENT 17 //How many pixels to move the muzzle flash up so your character doesn't look like they're shitting out lasers.
|
||||
/// Minimum projectile pixels to move before it animate()S, below this it's a direct set.
|
||||
#define MINIMUM_PIXELS_TO_ANIMATE 4
|
||||
/// Pixels to instantly travel on firing.
|
||||
#define PROJECTILE_FIRING_INSTANT_TRAVEL_AMOUNT 16
|
||||
|
||||
/obj/item/projectile
|
||||
name = "projectile"
|
||||
@@ -12,7 +16,9 @@
|
||||
pass_flags = PASSTABLE
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
movement_type = FLYING
|
||||
animate_movement = NO_STEPS
|
||||
hitsound = 'sound/weapons/pierce.ogg'
|
||||
appearance_flags = PIXEL_SCALE
|
||||
var/hitsound_wall = ""
|
||||
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
@@ -32,14 +38,19 @@
|
||||
//Fired processing vars
|
||||
var/fired = FALSE //Have we been fired yet
|
||||
var/paused = FALSE //for suspending the projectile midair
|
||||
var/last_projectile_move = 0
|
||||
var/time_offset = 0
|
||||
var/datum/point/vector/trajectory
|
||||
var/trajectory_ignore_forcemove = FALSE //instructs forceMove to NOT reset our trajectory to the new location!
|
||||
/// "leftover" pixels for Range() calculation as pixel_move() was moved to simulated semi-pixel movement and Range() is in tiles.
|
||||
var/pixels_range_leftover = 0
|
||||
/// "leftover" tick pixels and stuff yeah, so we don't round off things and introducing tracing inaccuracy.
|
||||
var/pixels_tick_leftover = 0
|
||||
|
||||
/// Pixels moved per second.
|
||||
var/pixels_per_second = TILES_TO_PIXELS(12.5)
|
||||
/// The number of pixels we increment by. THIS IS NOT SPEED, DO NOT TOUCH THIS UNLESS YOU KNOW WHAT YOU ARE DOING. In general, lower values means more linetrace accuracy up to a point at cost of performance.
|
||||
var/pixel_increment_amount
|
||||
|
||||
var/speed = 0.8 //Amount of deciseconds it takes for projectile to travel
|
||||
/// "leftover" ticks and stuff yeah. hey when are we rewriting projectiles for the eighth time to do something smarter like incrementing x pixels until it meets a goal instead of for(var/i in 1 to required_moves)?
|
||||
var/tick_moves_leftover = 0
|
||||
var/Angle = 0
|
||||
var/original_angle = 0 //Angle at firing
|
||||
var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle
|
||||
@@ -75,12 +86,16 @@
|
||||
//Homing
|
||||
var/homing = FALSE
|
||||
var/atom/homing_target
|
||||
var/homing_turn_speed = 10 //Angle per tick.
|
||||
/// How fast the projectile turns towards its homing targets, in angle per second.
|
||||
var/homing_turn_speed = 100
|
||||
var/homing_inaccuracy_min = 0 //in pixels for these. offsets are set once when setting target.
|
||||
var/homing_inaccuracy_max = 0
|
||||
var/homing_offset_x = 0
|
||||
var/homing_offset_y = 0
|
||||
|
||||
/// How many deciseconds are each hitscan movement considered. Used for homing and other things that use seconds for timing rather than ticks.
|
||||
var/hitscan_movement_decisecond_equivalency = 0.1
|
||||
|
||||
var/ignore_source_check = FALSE
|
||||
|
||||
var/damage = 10
|
||||
@@ -88,7 +103,8 @@
|
||||
var/nodamage = 0 //Determines if the projectile will skip any damage inflictions
|
||||
var/flag = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb
|
||||
var/projectile_type = /obj/item/projectile
|
||||
var/range = 50 //This will de-increment every step. When 0, it will deletze the projectile.
|
||||
/// Range of the projectile, de-incrementing every step. The projectile deletes itself at 0. This is in tiles.
|
||||
var/range = 50
|
||||
var/decayedRange //stores original range
|
||||
var/reflect_range_decrease = 5 //amount of original range that falls off when reflecting, so it doesn't go forever
|
||||
var/is_reflectable = FALSE // Can it be reflected or not?
|
||||
@@ -120,6 +136,10 @@
|
||||
permutated = list()
|
||||
decayedRange = range
|
||||
|
||||
/**
|
||||
* Artificially modified to be called at around every world.icon_size pixels of movement.
|
||||
* WARNING: Range() can only be called once per pixel_increment_amount pixels.
|
||||
*/
|
||||
/obj/item/projectile/proc/Range()
|
||||
range--
|
||||
if(range <= 0 && loc)
|
||||
@@ -334,14 +354,15 @@
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/// one move is a tile.
|
||||
/obj/item/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight.
|
||||
if(!trajectory && isnull(forced_angle) && isnull(Angle))
|
||||
return FALSE
|
||||
var/datum/point/vector/current = trajectory
|
||||
if(!current)
|
||||
var/turf/T = get_turf(src)
|
||||
current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, SSprojectiles.global_pixel_speed)
|
||||
var/datum/point/vector/v = current.return_vector_after_increments(moves * SSprojectiles.global_iterations_per_move)
|
||||
current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, pixel_increment_amount || SSprojectiles.global_pixel_increment_amount)
|
||||
var/datum/point/vector/v = current.return_vector_after_increments(TILES_TO_PIXELS(moves) / (pixel_increment_amount || SSprojectiles.global_pixel_increment_amount))
|
||||
return v.return_turf()
|
||||
|
||||
/obj/item/projectile/proc/return_pathing_turfs_in_moves(moves, forced_angle)
|
||||
@@ -358,16 +379,14 @@
|
||||
return PROCESS_KILL
|
||||
if(paused || !isturf(loc))
|
||||
return
|
||||
|
||||
var/ds = (SSprojectiles.flags & SS_TICKER)? (wait * world.tick_lag) : wait
|
||||
var/required_moves = ds / speed
|
||||
var/leftover = MODULUS(required_moves, 1)
|
||||
tick_moves_leftover += leftover
|
||||
required_moves = round(required_moves)
|
||||
if(tick_moves_leftover > 1)
|
||||
required_moves += round(tick_moves_leftover)
|
||||
tick_moves_leftover = MODULUS(tick_moves_leftover, 1)
|
||||
for(var/i in 1 to required_moves)
|
||||
pixel_move(1, FALSE)
|
||||
var/required_pixels = (pixels_per_second * ds * 0.1) + pixels_tick_leftover
|
||||
if(required_pixels >= pixel_increment_amount)
|
||||
pixels_tick_leftover = MODULUS(required_pixels, pixel_increment_amount)
|
||||
pixel_move(FLOOR(required_pixels / pixel_increment_amount, 1), FALSE, ds, SSprojectiles.global_projectile_speed_multiplier)
|
||||
else
|
||||
pixels_tick_leftover = required_pixels
|
||||
|
||||
/obj/item/projectile/proc/fire(angle, atom/direct_target)
|
||||
if(fired_from)
|
||||
@@ -390,7 +409,7 @@
|
||||
qdel(src)
|
||||
return
|
||||
var/turf/target = locate(clamp(starting + xo, 1, world.maxx), clamp(starting + yo, 1, world.maxy), starting.z)
|
||||
setAngle(Get_Angle(src, target))
|
||||
setAngle(get_projectile_angle(src, target))
|
||||
original_angle = Angle
|
||||
if(!nondirectional_sprite)
|
||||
var/matrix/M = new
|
||||
@@ -399,14 +418,16 @@
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(starting)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, SSprojectiles.global_pixel_speed)
|
||||
last_projectile_move = world.time
|
||||
if(isnull(pixel_increment_amount))
|
||||
pixel_increment_amount = SSprojectiles.global_pixel_increment_amount
|
||||
trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, pixel_increment_amount)
|
||||
fired = TRUE
|
||||
if(hitscan)
|
||||
process_hitscan()
|
||||
return
|
||||
if(!(datum_flags & DF_ISPROCESSING))
|
||||
START_PROCESSING(SSprojectiles, src)
|
||||
pixel_move(1, FALSE) //move it now!
|
||||
pixel_move(round(PROJECTILE_FIRING_INSTANT_TRAVEL_AMOUNT / pixel_increment_amount), FALSE, allow_animation = FALSE) //move it now!
|
||||
|
||||
/obj/item/projectile/proc/setAngle(new_angle, hitscan_store_segment = TRUE) //wrapper for overrides.
|
||||
Angle = new_angle
|
||||
@@ -451,7 +472,8 @@
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/proc/set_pixel_speed(new_speed)
|
||||
/obj/item/projectile/proc/set_pixel_increment_amount(new_speed)
|
||||
pixel_increment_amount = new_speed
|
||||
if(trajectory)
|
||||
trajectory.set_speed(new_speed)
|
||||
return TRUE
|
||||
@@ -464,6 +486,7 @@
|
||||
beam_segments[beam_index] = null //record start.
|
||||
|
||||
/obj/item/projectile/proc/process_hitscan()
|
||||
var/ttm = round(world.icon_size / pixel_increment_amount, 1)
|
||||
var/safety = range * 10
|
||||
record_hitscan_start(RETURN_POINT_VECTOR_INCREMENT(src, Angle, MUZZLE_EFFECT_PIXEL_INCREMENT, 1))
|
||||
while(loc && !QDELETED(src))
|
||||
@@ -476,20 +499,33 @@
|
||||
if(!QDELETED(src))
|
||||
qdel(src)
|
||||
return //Kill!
|
||||
pixel_move(1, TRUE)
|
||||
pixel_move(ttm, TRUE, hitscan_movement_decisecond_equivalency)
|
||||
|
||||
/obj/item/projectile/proc/pixel_move(trajectory_multiplier, hitscanning = FALSE)
|
||||
/**
|
||||
* The proc to make the projectile go, using a simulated pixel movement line trace.
|
||||
* Note: deciseconds_equivalent is currently only used for homing, times is the number of times to move pixel_increment_amount.
|
||||
* Trajectory multiplier directly modifies the factor of pixel_increment_amount to go per time.
|
||||
* It's complicated, so probably just don'ot mess with this unless you know what you're doing.
|
||||
*/
|
||||
/obj/item/projectile/proc/pixel_move(times, hitscanning = FALSE, deciseconds_equivalent = world.tick_lag, trajectory_multiplier = 1, allow_animation = TRUE)
|
||||
if(!loc || !trajectory)
|
||||
return
|
||||
last_projectile_move = world.time
|
||||
if(!nondirectional_sprite && !hitscanning)
|
||||
var/matrix/M = new
|
||||
M.Turn(Angle)
|
||||
transform = M
|
||||
if(homing)
|
||||
process_homing()
|
||||
var/forcemoved = FALSE
|
||||
for(var/i in 1 to SSprojectiles.global_iterations_per_move)
|
||||
var/turf/oldloc = loc
|
||||
var/old_px = pixel_x
|
||||
var/old_py = pixel_y
|
||||
for(var/i in 1 to times)
|
||||
// HOMING START - Too expensive to proccall at this point.
|
||||
if(homing_target)
|
||||
// No datum/points, too expensive.
|
||||
var/angle = closer_angle_difference(Angle, get_projectile_angle(src, homing_target))
|
||||
var/max_turn = homing_turn_speed * deciseconds_equivalent * 0.1
|
||||
setAngle(Angle + clamp(angle, -max_turn, max_turn))
|
||||
// HOMING END
|
||||
trajectory.increment(trajectory_multiplier)
|
||||
var/turf/T = trajectory.return_turf()
|
||||
if(!istype(T))
|
||||
@@ -509,23 +545,29 @@
|
||||
pixel_x = trajectory.return_px()
|
||||
pixel_y = trajectory.return_py()
|
||||
else if(T != loc)
|
||||
step_towards(src, T)
|
||||
var/safety = CEILING(pixel_increment_amount / world.icon_size, 1) * 2 + 1
|
||||
while(T != loc)
|
||||
if(!--safety)
|
||||
CRASH("Projectile took more than pixel incrememnt speed times 2 to get to its location, this is probably something seriously scuffed going on.")
|
||||
step_towards(src, T)
|
||||
if(QDELETED(src))
|
||||
return
|
||||
pixels_range_leftover += pixel_increment_amount
|
||||
if(pixels_range_leftover > world.icon_size)
|
||||
Range()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
pixels_range_leftover -= world.icon_size
|
||||
if(!hitscanning && !forcemoved)
|
||||
pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move
|
||||
pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move
|
||||
animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW)
|
||||
Range()
|
||||
|
||||
/obj/item/projectile/proc/process_homing() //may need speeding up in the future performance wise.
|
||||
if(!homing_target)
|
||||
return FALSE
|
||||
var/datum/point/PT = RETURN_PRECISE_POINT(homing_target)
|
||||
PT.x += clamp(homing_offset_x, 1, world.maxx)
|
||||
PT.y += clamp(homing_offset_y, 1, world.maxy)
|
||||
var/angle = closer_angle_difference(Angle, angle_between_points(RETURN_PRECISE_POINT(src), PT))
|
||||
setAngle(Angle + clamp(angle, -homing_turn_speed, homing_turn_speed))
|
||||
var/traj_px = round(trajectory.return_px(), 1)
|
||||
var/traj_py = round(trajectory.return_py(), 1)
|
||||
if(allow_animation && (pixel_increment_amount * times > MINIMUM_PIXELS_TO_ANIMATE))
|
||||
pixel_x = ((oldloc.x - x) * world.icon_size) + old_px
|
||||
pixel_y = ((oldloc.y - y) * world.icon_size) + old_py
|
||||
animate(src, pixel_x = traj_px, pixel_y = traj_py, time = 1, flags = ANIMATION_END_NOW)
|
||||
else
|
||||
pixel_x = traj_px
|
||||
pixel_y = traj_py
|
||||
|
||||
/obj/item/projectile/proc/set_homing_target(atom/A)
|
||||
if(!A || (!isturf(A) && !isturf(A.loc)))
|
||||
@@ -575,7 +617,7 @@
|
||||
if(targloc || !params)
|
||||
yo = targloc.y - curloc.y
|
||||
xo = targloc.x - curloc.x
|
||||
setAngle(Get_Angle(src, targloc) + spread)
|
||||
setAngle(get_projectile_angle(src, targloc) + spread)
|
||||
|
||||
if(isliving(source) && params)
|
||||
var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params)
|
||||
@@ -586,7 +628,7 @@
|
||||
else if(targloc)
|
||||
yo = targloc.y - curloc.y
|
||||
xo = targloc.x - curloc.x
|
||||
setAngle(Get_Angle(src, targloc) + spread)
|
||||
setAngle(get_projectile_angle(src, targloc) + spread)
|
||||
else
|
||||
stack_trace("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!")
|
||||
qdel(src)
|
||||
@@ -698,3 +740,7 @@
|
||||
/proc/is_energy_reflectable_projectile(atom/A)
|
||||
var/obj/item/projectile/P = A
|
||||
return istype(P) && P.is_reflectable
|
||||
|
||||
#undef MOVES_HITSCAN
|
||||
#undef MINIMUM_PIXELS_TO_ANIMATE
|
||||
#undef MUZZLE_EFFECT_PIXEL_INCREMENT
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
flag = "energy"
|
||||
hitsound = 'sound/weapons/tap.ogg'
|
||||
eyeblur = 0
|
||||
speed = 0.6
|
||||
pixels_per_second = TILES_TO_PIXELS(16.667)
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
|
||||
light_color = LIGHT_COLOR_BLUE
|
||||
tracer_type = /obj/effect/projectile/tracer/disabler
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
damage = 20
|
||||
armour_penetration = 20
|
||||
light_range = 3
|
||||
speed = 0.6
|
||||
pixels_per_second = TILES_TO_PIXELS(16.667)
|
||||
range = 35
|
||||
light_color = LIGHT_COLOR_RED
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
armour_penetration = 20
|
||||
movement_type = FLYING | UNSTOPPABLE
|
||||
range = 20
|
||||
speed = 0.8
|
||||
pixels_per_second = TILES_TO_PIXELS(12.5)
|
||||
light_range = 4
|
||||
light_color = LIGHT_COLOR_RED
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/obj/item/projectile/bullet/p50
|
||||
name =".50 bullet"
|
||||
speed = 0.4
|
||||
pixels_per_second = TILES_TO_PIXELS(25)
|
||||
damage = 70
|
||||
knockdown = 100
|
||||
dismemberment = 50
|
||||
@@ -43,5 +43,5 @@
|
||||
/obj/item/projectile/bullet/p50/penetrator/shuttle //Nukeop Shuttle Variety
|
||||
icon_state = "gaussstrong"
|
||||
damage = 25
|
||||
speed = 0.3
|
||||
pixels_per_second = TILES_TO_PIXELS(33.33)
|
||||
range = 16
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
|
||||
flag = "rad"
|
||||
irradiate = 5000
|
||||
speed = 0.4
|
||||
pixels_per_second = TILES_TO_PIXELS(25)
|
||||
hitsound = 'sound/weapons/emitter2.ogg'
|
||||
impact_type = /obj/effect/projectile/impact/xray
|
||||
var/static/list/particle_colors = list(
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
jitter = 20
|
||||
hitsound = 'sound/weapons/taserhit.ogg'
|
||||
range = 14
|
||||
speed = 0.6
|
||||
pixels_per_second = TILES_TO_PIXELS(16.667)
|
||||
tracer_type = /obj/effect/projectile/tracer/stun
|
||||
muzzle_type = /obj/effect/projectile/muzzle/stun
|
||||
impact_type = /obj/effect/projectile/impact/stun
|
||||
|
||||
@@ -434,7 +434,7 @@
|
||||
damage = 15
|
||||
damage_type = BURN
|
||||
nodamage = 0
|
||||
speed = 0.3
|
||||
pixels_per_second = TILES_TO_PIXELS(33.33)
|
||||
flag = "magic"
|
||||
|
||||
var/zap_power = 20000
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
damage_type = BURN
|
||||
damage = 10
|
||||
knockdown = 20
|
||||
speed = 2
|
||||
pixels_per_second = TILES_TO_PIXELS(5)
|
||||
range = 16
|
||||
movement_type = FLYING | UNSTOPPABLE
|
||||
var/datum/beam/arm
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
circuit = /obj/item/circuitboard/machine/chem_dispenser
|
||||
var/obj/item/stock_parts/cell/cell
|
||||
var/powerefficiency = 0.1
|
||||
var/powerefficiency = 0.0666666
|
||||
var/amount = 30
|
||||
var/recharge_amount = 10
|
||||
var/recharge_counter = 0
|
||||
@@ -387,7 +387,7 @@
|
||||
|
||||
/obj/machinery/chem_dispenser/RefreshParts()
|
||||
recharge_amount = initial(recharge_amount)
|
||||
var/newpowereff = 0.0666666
|
||||
var/newpowereff = initial(powerefficiency)
|
||||
for(var/obj/item/stock_parts/cell/P in component_parts)
|
||||
cell = P
|
||||
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
|
||||
@@ -720,3 +720,58 @@
|
||||
component_parts += new /obj/item/stack/sheet/glass(null)
|
||||
component_parts += new /obj/item/stock_parts/cell/bluespace(null)
|
||||
RefreshParts()
|
||||
|
||||
///An unique, less efficient model found in the medbay apothecary room.
|
||||
/obj/machinery/chem_dispenser/apothecary
|
||||
name = "apothecary chem dispenser"
|
||||
desc = "A cheaper chem dispenser meant for small scale medicine production."
|
||||
icon_state = "minidispenser"
|
||||
working_state = "minidispenser_working"
|
||||
nopower_state = "minidispenser_nopower"
|
||||
circuit = /obj/item/circuitboard/machine/chem_dispenser/apothecary
|
||||
powerefficiency = 0.0833333
|
||||
dispensable_reagents = list( //radium and stable plasma moved to upgrade tier 1 and 2, they've little to do with most medicines anyway.
|
||||
/datum/reagent/hydrogen,
|
||||
/datum/reagent/lithium,
|
||||
/datum/reagent/carbon,
|
||||
/datum/reagent/nitrogen,
|
||||
/datum/reagent/oxygen,
|
||||
/datum/reagent/fluorine,
|
||||
/datum/reagent/sodium,
|
||||
/datum/reagent/aluminium,
|
||||
/datum/reagent/silicon,
|
||||
/datum/reagent/phosphorus,
|
||||
/datum/reagent/sulfur,
|
||||
/datum/reagent/chlorine,
|
||||
/datum/reagent/potassium,
|
||||
/datum/reagent/iron,
|
||||
/datum/reagent/copper,
|
||||
/datum/reagent/mercury,
|
||||
/datum/reagent/water,
|
||||
/datum/reagent/consumable/ethanol,
|
||||
/datum/reagent/consumable/sugar,
|
||||
/datum/reagent/toxin/acid,
|
||||
/datum/reagent/fuel,
|
||||
/datum/reagent/silver,
|
||||
/datum/reagent/iodine,
|
||||
/datum/reagent/bromine
|
||||
)
|
||||
upgrade_reagents = list(
|
||||
/datum/reagent/oil,
|
||||
/datum/reagent/ammonia,
|
||||
/datum/reagent/radium
|
||||
)
|
||||
upgrade_reagents2 = list(
|
||||
/datum/reagent/acetone,
|
||||
/datum/reagent/phenol,
|
||||
/datum/reagent/stable_plasma
|
||||
)
|
||||
upgrade_reagents3 = list(
|
||||
/datum/reagent/medicine/mine_salve
|
||||
)
|
||||
|
||||
emagged_reagents = list(
|
||||
/datum/reagent/drug/space_drugs,
|
||||
/datum/reagent/toxin/carpotoxin,
|
||||
/datum/reagent/medicine/morphine
|
||||
)
|
||||
|
||||
@@ -559,3 +559,128 @@
|
||||
var/mob/living/carbon/C = M
|
||||
if(!C.undergoing_cardiac_arrest())
|
||||
C.set_heartattack(TRUE)
|
||||
|
||||
//aphrodisiac & anaphrodisiac
|
||||
|
||||
/datum/reagent/drug/aphrodisiac
|
||||
name = "Crocin"
|
||||
description = "Naturally found in the crocus and gardenia flowers, this drug acts as a natural and safe aphrodisiac."
|
||||
taste_description = "strawberries"
|
||||
color = "#FFADFF"//PINK, rgb(255, 173, 255)
|
||||
can_synth = FALSE
|
||||
|
||||
/datum/reagent/drug/aphrodisiac/on_mob_life(mob/living/M)
|
||||
if(M && M.client?.prefs.arousable && !(M.client?.prefs.cit_toggles & NO_APHRO))
|
||||
if((prob(min(current_cycle/2,5))))
|
||||
M.emote(pick("moan","blush"))
|
||||
if(prob(min(current_cycle/4,10)))
|
||||
var/aroused_message = pick("You feel frisky.", "You're having trouble suppressing your urges.", "You feel in the mood.")
|
||||
to_chat(M, "<span class='userlove'>[aroused_message]</span>")
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/list/genits = H.adjust_arousal(current_cycle, aphro = TRUE) // redundant but should still be here
|
||||
for(var/g in genits)
|
||||
var/obj/item/organ/genital/G = g
|
||||
to_chat(M, "<span class='userlove'>[G.arousal_verb]!</span>")
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/aphrodisiacplus
|
||||
name = "Hexacrocin"
|
||||
description = "Chemically condensed form of basic crocin. This aphrodisiac is extremely powerful and addictive in most animals.\
|
||||
Addiction withdrawals can cause brain damage and shortness of breath. Overdosage can lead to brain damage and a \
|
||||
permanent increase in libido (commonly referred to as 'bimbofication')."
|
||||
taste_description = "liquid desire"
|
||||
color = "#FF2BFF"//dark pink
|
||||
addiction_threshold = 20
|
||||
overdose_threshold = 20
|
||||
can_synth = FALSE
|
||||
|
||||
/datum/reagent/drug/aphrodisiacplus/on_mob_life(mob/living/M)
|
||||
if(M && M.client?.prefs.arousable && !(M.client?.prefs.cit_toggles & NO_APHRO))
|
||||
if(prob(5))
|
||||
if(prob(current_cycle))
|
||||
M.say(pick("Hnnnnngghh...", "Ohh...", "Mmnnn..."))
|
||||
else
|
||||
M.emote(pick("moan","blush"))
|
||||
if(prob(5))
|
||||
var/aroused_message
|
||||
if(current_cycle>25)
|
||||
aroused_message = pick("You need to fuck someone!", "You're bursting with sexual tension!", "You can't get sex off your mind!")
|
||||
else
|
||||
aroused_message = pick("You feel a bit hot.", "You feel strong sexual urges.", "You feel in the mood.", "You're ready to go down on someone.")
|
||||
to_chat(M, "<span class='userlove'>[aroused_message]</span>")
|
||||
REMOVE_TRAIT(M,TRAIT_NEVERBONER,APHRO_TRAIT)
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/list/genits = H.adjust_arousal(100, aphro = TRUE) // redundant but should still be here
|
||||
for(var/g in genits)
|
||||
var/obj/item/organ/genital/G = g
|
||||
to_chat(M, "<span class='userlove'>[G.arousal_verb]!</span>")
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/aphrodisiacplus/addiction_act_stage2(mob/living/M)
|
||||
if(prob(30))
|
||||
M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2)
|
||||
..()
|
||||
/datum/reagent/drug/aphrodisiacplus/addiction_act_stage3(mob/living/M)
|
||||
if(prob(30))
|
||||
M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3)
|
||||
|
||||
..()
|
||||
/datum/reagent/drug/aphrodisiacplus/addiction_act_stage4(mob/living/M)
|
||||
if(prob(30))
|
||||
M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 4)
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/aphrodisiacplus/overdose_process(mob/living/M)
|
||||
if(M && M.client?.prefs.arousable && !(M.client?.prefs.cit_toggles & NO_APHRO) && prob(33))
|
||||
if(prob(5) && ishuman(M) && M.has_dna() && (M.client?.prefs.cit_toggles & BIMBOFICATION))
|
||||
if(!HAS_TRAIT(M,TRAIT_PERMABONER))
|
||||
to_chat(M, "<span class='userlove'>Your libido is going haywire!</span>")
|
||||
ADD_TRAIT(M,TRAIT_PERMABONER,APHRO_TRAIT)
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/anaphrodisiac
|
||||
name = "Camphor"
|
||||
description = "Naturally found in some species of evergreen trees, camphor is a waxy substance. When injested by most animals, it acts as an anaphrodisiac\
|
||||
, reducing libido and calming them. Non-habit forming and not addictive."
|
||||
taste_description = "dull bitterness"
|
||||
taste_mult = 2
|
||||
color = "#D9D9D9"//rgb(217, 217, 217)
|
||||
reagent_state = SOLID
|
||||
can_synth = FALSE
|
||||
|
||||
/datum/reagent/drug/anaphrodisiac/on_mob_life(mob/living/M)
|
||||
if(M && M.client?.prefs.arousable && prob(16))
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/list/genits = H.adjust_arousal(-100, aphro = TRUE)
|
||||
if(genits.len)
|
||||
to_chat(M, "<span class='notice'>You no longer feel aroused.")
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/anaphrodisiacplus
|
||||
name = "Hexacamphor"
|
||||
description = "Chemically condensed camphor. Causes an extreme reduction in libido and a permanent one if overdosed. Non-addictive."
|
||||
taste_description = "tranquil celibacy"
|
||||
color = "#D9D9D9"//rgb(217, 217, 217)
|
||||
reagent_state = SOLID
|
||||
overdose_threshold = 20
|
||||
can_synth = FALSE
|
||||
|
||||
/datum/reagent/drug/anaphrodisiacplus/on_mob_life(mob/living/M)
|
||||
if(M && M.client?.prefs.arousable)
|
||||
REMOVE_TRAIT(M,TRAIT_PERMABONER,APHRO_TRAIT)
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/list/genits = H.adjust_arousal(-100, aphro = TRUE)
|
||||
if(genits.len)
|
||||
to_chat(M, "<span class='notice'>You no longer feel aroused.")
|
||||
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/anaphrodisiacplus/overdose_process(mob/living/M)
|
||||
if(M && M.client?.prefs.arousable && prob(5))
|
||||
to_chat(M, "<span class='userlove'>You feel like you'll never feel aroused again...</span>")
|
||||
ADD_TRAIT(M,TRAIT_NEVERBONER,APHRO_TRAIT)
|
||||
..()
|
||||
|
||||
@@ -1735,6 +1735,22 @@
|
||||
H.facial_hair_style = "Very Long Beard"
|
||||
H.update_hair()
|
||||
|
||||
/datum/reagent/baldium
|
||||
name = "Baldium"
|
||||
description = "A major cause of hair loss across the world."
|
||||
reagent_state = LIQUID
|
||||
color = "#ecb2cf"
|
||||
taste_description = "bitterness"
|
||||
|
||||
/datum/reagent/baldium/reaction_mob(mob/living/M, method=TOUCH, reac_volume)
|
||||
if(method == TOUCH || method == VAPOR)
|
||||
if(M && ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
to_chat(H, "<span class='danger'>Your hair is falling out in clumps!</span>")
|
||||
H.hair_style = "Bald"
|
||||
H.facial_hair_style = "Shaved"
|
||||
H.update_hair()
|
||||
|
||||
/datum/reagent/saltpetre
|
||||
name = "Saltpetre"
|
||||
description = "Volatile. Controversial. Third Thing."
|
||||
@@ -2178,3 +2194,93 @@
|
||||
reagent_state = LIQUID
|
||||
color = "#f7685e"
|
||||
metabolization_rate = REAGENTS_METABOLISM * 0.25
|
||||
|
||||
|
||||
//body bluids
|
||||
/datum/reagent/consumable/semen
|
||||
name = "Semen"
|
||||
description = "Sperm from some animal. Useless for anything but insemination, really."
|
||||
taste_description = "something salty"
|
||||
taste_mult = 2 //Not very overpowering flavor
|
||||
data = list("donor"=null,"viruses"=null,"donor_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null)
|
||||
reagent_state = LIQUID
|
||||
color = "#FFFFFF" // rgb: 255, 255, 255
|
||||
can_synth = FALSE
|
||||
nutriment_factor = 0.5 * REAGENTS_METABOLISM
|
||||
|
||||
/datum/reagent/consumable/semen/reaction_turf(turf/T, reac_volume)
|
||||
if(!istype(T))
|
||||
return
|
||||
if(reac_volume < 10)
|
||||
return
|
||||
|
||||
var/obj/effect/decal/cleanable/semen/S = locate() in T
|
||||
if(!S)
|
||||
S = new(T)
|
||||
if(data["blood_DNA"])
|
||||
S.add_blood_DNA(list(data["blood_DNA"] = data["blood_type"]))
|
||||
|
||||
/obj/effect/decal/cleanable/semen
|
||||
name = "semen"
|
||||
desc = null
|
||||
gender = PLURAL
|
||||
density = 0
|
||||
layer = ABOVE_NORMAL_TURF_LAYER
|
||||
icon = 'icons/obj/genitals/effects.dmi'
|
||||
icon_state = "semen1"
|
||||
random_icon_states = list("semen1", "semen2", "semen3", "semen4")
|
||||
|
||||
/obj/effect/decal/cleanable/semen/Initialize(mapload)
|
||||
. = ..()
|
||||
dir = GLOB.cardinals
|
||||
add_blood_DNA(list("Non-human DNA" = "A+"))
|
||||
|
||||
/obj/effect/decal/cleanable/semen/replace_decal(obj/effect/decal/cleanable/semen/S)
|
||||
if(S.blood_DNA)
|
||||
blood_DNA |= S.blood_DNA
|
||||
return ..()
|
||||
|
||||
/datum/reagent/consumable/femcum
|
||||
name = "Female Ejaculate"
|
||||
description = "Vaginal lubricant found in most mammals and other animals of similar nature. Where you found this is your own business."
|
||||
taste_description = "something with a tang" // wew coders who haven't eaten out a girl.
|
||||
taste_mult = 2
|
||||
data = list("donor"=null,"viruses"=null,"donor_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null)
|
||||
reagent_state = LIQUID
|
||||
color = "#AAAAAA77"
|
||||
can_synth = FALSE
|
||||
nutriment_factor = 0.5 * REAGENTS_METABOLISM
|
||||
|
||||
/obj/effect/decal/cleanable/femcum
|
||||
name = "female ejaculate"
|
||||
desc = null
|
||||
gender = PLURAL
|
||||
density = 0
|
||||
layer = ABOVE_NORMAL_TURF_LAYER
|
||||
icon = 'icons/obj/genitals/effects.dmi'
|
||||
icon_state = "fem1"
|
||||
random_icon_states = list("fem1", "fem2", "fem3", "fem4")
|
||||
blood_state = null
|
||||
bloodiness = null
|
||||
|
||||
/obj/effect/decal/cleanable/femcum/Initialize(mapload)
|
||||
. = ..()
|
||||
dir = GLOB.cardinals
|
||||
add_blood_DNA(list("Non-human DNA" = "A+"))
|
||||
|
||||
/obj/effect/decal/cleanable/femcum/replace_decal(obj/effect/decal/cleanable/femcum/F)
|
||||
if(F.blood_DNA)
|
||||
blood_DNA |= F.blood_DNA
|
||||
return ..()
|
||||
|
||||
/datum/reagent/consumable/femcum/reaction_turf(turf/T, reac_volume)
|
||||
if(!istype(T))
|
||||
return
|
||||
if(reac_volume < 10)
|
||||
return
|
||||
|
||||
var/obj/effect/decal/cleanable/femcum/S = locate() in T
|
||||
if(!S)
|
||||
S = new(T)
|
||||
if(data["blood_DNA"])
|
||||
S.add_blood_DNA(list(data["blood_DNA"] = data["blood_type"]))
|
||||
|
||||
@@ -1019,3 +1019,20 @@
|
||||
to_chat(M, "<span class='notice'>[tox_message]</span>")
|
||||
. = 1
|
||||
..()
|
||||
|
||||
/datum/reagent/toxin/leadacetate
|
||||
name = "Lead Acetate"
|
||||
description = "Used hundreds of years ago as a sweetener, before it was realized that it's incredibly poisonous."
|
||||
reagent_state = SOLID
|
||||
color = "#2b2b2b" // rgb: 127, 132, 0
|
||||
toxpwr = 0.5
|
||||
taste_mult = 1.3
|
||||
taste_description = "sugary sweetness"
|
||||
|
||||
/datum/reagent/toxin/leadacetate/on_mob_life(mob/living/carbon/M)
|
||||
M.adjustOrganLoss(ORGAN_SLOT_EARS,1)
|
||||
M.adjustOrganLoss(ORGAN_SLOT_BRAIN,1)
|
||||
if(prob(1))
|
||||
to_chat(M, "<span class='notice'>Ah, what was that? You thought you heard something...</span>")
|
||||
M.confused += 5
|
||||
return ..()
|
||||
|
||||
@@ -62,3 +62,34 @@
|
||||
results = list(/datum/reagent/moonsugar = 1, /datum/reagent/medicine/morphine = 2.5)
|
||||
required_temp = 315 //a little above normal body temperature
|
||||
required_reagents = list(/datum/reagent/drug/skooma = 1)
|
||||
/datum/chemical_reaction/aphro
|
||||
name = "crocin"
|
||||
id = /datum/reagent/drug/aphrodisiac
|
||||
results = list(/datum/reagent/drug/aphrodisiac = 6)
|
||||
required_reagents = list(/datum/reagent/carbon = 2, /datum/reagent/hydrogen = 2, /datum/reagent/oxygen = 2, /datum/reagent/water = 1)
|
||||
required_temp = 400
|
||||
mix_message = "The mixture boils off a pink vapor..."//The water boils off, leaving the crocin
|
||||
|
||||
/datum/chemical_reaction/aphroplus
|
||||
name = "hexacrocin"
|
||||
id = /datum/reagent/drug/aphrodisiacplus
|
||||
results = list(/datum/reagent/drug/aphrodisiacplus = 1)
|
||||
required_reagents = list(/datum/reagent/drug/aphrodisiac = 6, /datum/reagent/phenol = 1)
|
||||
required_temp = 400
|
||||
mix_message = "The mixture rapidly condenses and darkens in color..."
|
||||
|
||||
/datum/chemical_reaction/anaphro
|
||||
name = "camphor"
|
||||
id = /datum/reagent/drug/anaphrodisiac
|
||||
results = list(/datum/reagent/drug/anaphrodisiac = 6)
|
||||
required_reagents = list(/datum/reagent/carbon = 2, /datum/reagent/hydrogen = 2, /datum/reagent/oxygen = 2, /datum/reagent/sulfur = 1)
|
||||
required_temp = 400
|
||||
mix_message = "The mixture boils off a yellow, smelly vapor..."//Sulfur burns off, leaving the camphor
|
||||
|
||||
/datum/chemical_reaction/anaphroplus
|
||||
name = "pentacamphor"
|
||||
id = /datum/reagent/drug/anaphrodisiacplus
|
||||
results = list(/datum/reagent/drug/anaphrodisiacplus = 1)
|
||||
required_reagents = list(/datum/reagent/drug/aphrodisiac = 5, /datum/reagent/acetone = 1)
|
||||
required_temp = 300
|
||||
mix_message = "The mixture thickens and heats up slighty..."
|
||||
|
||||
@@ -631,6 +631,11 @@
|
||||
results = list(/datum/reagent/concentrated_barbers_aid = 2)
|
||||
required_reagents = list(/datum/reagent/barbers_aid = 1, /datum/reagent/toxin/mutagen = 1)
|
||||
|
||||
/datum/chemical_reaction/baldium
|
||||
results = list(/datum/reagent/baldium = 1)
|
||||
required_reagents = list(/datum/reagent/radium = 1, /datum/reagent/toxin/acid = 1, /datum/reagent/lye = 1)
|
||||
required_temp = 395
|
||||
|
||||
/datum/chemical_reaction/saltpetre
|
||||
name = "saltpetre"
|
||||
id = /datum/reagent/saltpetre
|
||||
|
||||
@@ -351,7 +351,7 @@
|
||||
name = "wooden bucket"
|
||||
desc = "It's a bucket made of wood."
|
||||
icon_state = "bucket_wooden"
|
||||
custom_materials = null
|
||||
custom_materials = list(/datum/material/wood = MINERAL_MATERIAL_AMOUNT * 2)
|
||||
slot_flags = NONE
|
||||
item_flags = NO_MAT_REDEMPTION
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/obj/item/reagent_containers/glass/maunamug
|
||||
name = "mauna mug"
|
||||
desc = "A drink served in a classy mug. Now with built-in heating!"
|
||||
icon = 'icons/obj/mauna_mug.dmi'
|
||||
icon_state = "maunamug"
|
||||
spillable = TRUE
|
||||
reagent_flags = OPENCONTAINER
|
||||
// fill_icon_state = "maunafilling"
|
||||
// fill_icon_thresholds = list(25)
|
||||
var/obj/item/stock_parts/cell/cell
|
||||
var/open = FALSE
|
||||
var/on = FALSE
|
||||
|
||||
/obj/item/reagent_containers/glass/maunamug/Initialize(mapload, vol)
|
||||
. = ..()
|
||||
cell = new /obj/item/stock_parts/cell(src)
|
||||
|
||||
/obj/item/reagent_containers/glass/maunamug/examine(mob/user)
|
||||
. = ..()
|
||||
. += "<span class='notice'>The status display reads: Current temperature: <b>[reagents.chem_temp]K</b> Current Charge:[cell ? "[cell.charge / cell.maxcharge * 100]%" : "No cell found"].</span>"
|
||||
if(open)
|
||||
. += "<span class='notice'>The battery case is open.</span>"
|
||||
|
||||
/obj/item/reagent_containers/glass/maunamug/update_overlays()
|
||||
. = ..()
|
||||
if(reagents.total_volume >= 25)
|
||||
. += mutable_appearance('icons/obj/reagentfillings.dmi', "maunafilling25", color = mix_color_from_reagents(reagents.reagent_list))
|
||||
|
||||
/obj/item/reagent_containers/glass/maunamug/process()
|
||||
..()
|
||||
if(on && (!cell || cell.charge <= 0)) //Check if we ran out of power
|
||||
change_power_status(FALSE)
|
||||
return FALSE
|
||||
cell.use(10) //Basic cell goes for like 200 seconds, bluespace for 8000
|
||||
if(!reagents.total_volume)
|
||||
return FALSE
|
||||
var/max_temp = min(500 + (500 * (0.2 * cell.rating)), 1000) // 373 to 1000
|
||||
reagents.adjust_thermal_energy(0.8 * cell.maxcharge * reagents.total_volume, max_temp = max_temp) // 4 kelvin every tick on a basic cell. 160k on bluespace
|
||||
reagents.handle_reactions()
|
||||
update_icon()
|
||||
if(reagents.chem_temp >= max_temp)
|
||||
change_power_status(FALSE)
|
||||
audible_message("<span class='notice'>The Mauna Mug lets out a happy beep and turns off!</span>")
|
||||
playsound(src, 'sound/machines/chime.ogg', 50)
|
||||
|
||||
/obj/item/reagent_containers/glass/maunamug/Destroy()
|
||||
if(cell)
|
||||
QDEL_NULL(cell)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
. = ..()
|
||||
|
||||
|
||||
/obj/item/reagent_containers/glass/maunamug/attack_self(mob/user)
|
||||
if(on)
|
||||
change_power_status(FALSE)
|
||||
else
|
||||
if(!cell || cell.charge <= 0)
|
||||
return FALSE //No power, so don't turn on
|
||||
change_power_status(TRUE)
|
||||
|
||||
/obj/item/reagent_containers/glass/maunamug/proc/change_power_status(status)
|
||||
on = status
|
||||
if(on)
|
||||
START_PROCESSING(SSobj, src)
|
||||
else
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
update_icon()
|
||||
|
||||
/obj/item/reagent_containers/glass/maunamug/screwdriver_act(mob/living/user, obj/item/I)
|
||||
. = ..()
|
||||
open = !open
|
||||
to_chat(user, "<span class='notice'>You screw the battery case on [src] [open ? "open" : "closed"] .</span>")
|
||||
update_icon()
|
||||
|
||||
/obj/item/reagent_containers/glass/maunamug/attackby(obj/item/I, mob/user, params)
|
||||
add_fingerprint(user)
|
||||
if(!istype(I, /obj/item/stock_parts/cell))
|
||||
return ..()
|
||||
if(!open)
|
||||
to_chat(user, "<span class='warning'>The battery case must be open to insert a power cell!</span>")
|
||||
return FALSE
|
||||
if(cell)
|
||||
to_chat(user, "<span class='warning'>There is already a power cell inside!</span>")
|
||||
return FALSE
|
||||
else if(!user.transferItemToLoc(I, src))
|
||||
return
|
||||
cell = I
|
||||
user.visible_message("<span class='notice'>[user] inserts a power cell into [src].</span>", "<span class='notice'>You insert the power cell into [src].</span>")
|
||||
update_icon()
|
||||
|
||||
/obj/item/reagent_containers/glass/maunamug/attack_hand(mob/living/user)
|
||||
if(cell && open)
|
||||
cell.update_icon()
|
||||
user.put_in_hands(cell)
|
||||
cell = null
|
||||
to_chat(user, "<span class='notice'>You remove the power cell from [src].</span>")
|
||||
on = FALSE
|
||||
update_icon()
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/obj/item/reagent_containers/glass/maunamug/update_icon()
|
||||
..()
|
||||
if(open)
|
||||
if(cell)
|
||||
icon_state = "maunamug_bat"
|
||||
else
|
||||
icon_state = "maunamug_no_bat"
|
||||
else if(on)
|
||||
icon_state = "maunamug_on"
|
||||
else
|
||||
icon_state = "maunamug"
|
||||
if(reagents.total_volume && reagents.chem_temp >= 400)
|
||||
var/intensity = (reagents.chem_temp - 400) * 1 / 600 //Get the opacity of the incandescent overlay. Ranging from 400 to 1000
|
||||
var/mutable_appearance/mug_glow = mutable_appearance(icon, "maunamug_incand")
|
||||
mug_glow.alpha = 255 * intensity
|
||||
add_overlay(mug_glow)
|
||||
@@ -22,10 +22,17 @@
|
||||
if(reagents.total_volume && roundstart)
|
||||
name += " ([reagents.total_volume]u)"
|
||||
|
||||
|
||||
/obj/item/reagent_containers/pill/attack_self(mob/user)
|
||||
return
|
||||
|
||||
/obj/item/reagent_containers/pill/get_w_volume()
|
||||
switch(reagents.total_volume)
|
||||
if(0 to 9.5)
|
||||
return 1
|
||||
if(9.5 to 25)
|
||||
return DEFAULT_VOLUME_TINY
|
||||
else
|
||||
return DEFAULT_VOLUME_SMALL
|
||||
|
||||
/obj/item/reagent_containers/pill/attack(mob/M, mob/user, def_zone)
|
||||
if(!canconsume(M, user))
|
||||
@@ -137,14 +144,14 @@
|
||||
name = "mannitol pill"
|
||||
desc = "Used to treat brain damage."
|
||||
icon_state = "pill17"
|
||||
list_reagents = list(/datum/reagent/medicine/mannitol = 50)
|
||||
list_reagents = list(/datum/reagent/medicine/mannitol = 25)
|
||||
roundstart = 1
|
||||
|
||||
/obj/item/reagent_containers/pill/mutadone
|
||||
name = "mutadone pill"
|
||||
desc = "Used to treat genetic damage."
|
||||
icon_state = "pill20"
|
||||
list_reagents = list(/datum/reagent/medicine/mutadone = 50)
|
||||
list_reagents = list(/datum/reagent/medicine/mutadone = 25)
|
||||
roundstart = 1
|
||||
|
||||
/obj/item/reagent_containers/pill/salicyclic
|
||||
@@ -186,14 +193,14 @@
|
||||
name = "prussian blue pill"
|
||||
desc = "Used to treat heavy radition poisoning."
|
||||
icon_state = "prussian_blue"
|
||||
list_reagents = list(/datum/reagent/medicine/prussian_blue = 25, /datum/reagent/water = 10)
|
||||
list_reagents = list(/datum/reagent/medicine/prussian_blue = 25)
|
||||
roundstart = 1
|
||||
|
||||
/obj/item/reagent_containers/pill/mutarad
|
||||
name = "radiation treatment deluxe pill"
|
||||
desc = "Used to treat heavy radition poisoning and genetic defects."
|
||||
icon_state = "anit_rad_fixgene"
|
||||
list_reagents = list(/datum/reagent/medicine/prussian_blue = 15, /datum/reagent/medicine/potass_iodide = 15, /datum/reagent/medicine/mutadone = 15, /datum/reagent/water = 5)
|
||||
list_reagents = list(/datum/reagent/medicine/prussian_blue = 10, /datum/reagent/medicine/potass_iodide = 10, /datum/reagent/medicine/mutadone = 5)
|
||||
roundstart = 1
|
||||
|
||||
///////////////////////////////////////// this pill is used only in a legion mob drop
|
||||
@@ -216,7 +223,7 @@
|
||||
|
||||
/obj/item/reagent_containers/pill/lsd
|
||||
name = "hallucinogen pill"
|
||||
list_reagents = list(/datum/reagent/drug/mushroomhallucinogen = 15, /datum/reagent/toxin/mindbreaker = 15)
|
||||
list_reagents = list(/datum/reagent/drug/mushroomhallucinogen = 12.5, /datum/reagent/toxin/mindbreaker = 12.5)
|
||||
|
||||
|
||||
/obj/item/reagent_containers/pill/aranesp
|
||||
|
||||
@@ -241,12 +241,6 @@
|
||||
icon_state = "whitekeg"
|
||||
reagent_id = /datum/reagent/consumable/milk
|
||||
|
||||
/obj/structure/reagent_dispensers/keg/semen
|
||||
name = "keg of semen"
|
||||
desc = "Dear lord, where did this even come from?"
|
||||
icon_state = "whitekeg"
|
||||
reagent_id = /datum/reagent/consumable/semen
|
||||
|
||||
/obj/structure/reagent_dispensers/keg/gargle
|
||||
name = "keg of pan galactic gargleblaster"
|
||||
desc = "A keg of... wow that's a long name."
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
//This system is designed to act as an in-between for cargo and science, and the first major money sink in the game outside of just buying things from cargo (As of 10/9/19, anyway).
|
||||
|
||||
//economics defined values, subject to change should anything be too high or low in practice.
|
||||
|
||||
#define MACHINE_OPERATION 100000
|
||||
#define MACHINE_OVERLOAD 500000
|
||||
#define MAJOR_THRESHOLD 5500
|
||||
#define MINOR_THRESHOLD 3500
|
||||
#define STANDARD_DEVIATION 1000
|
||||
|
||||
/obj/machinery/rnd/bepis
|
||||
name = "\improper B.E.P.I.S. Chamber"
|
||||
desc = "A high fidelity testing device which unlocks the secrets of the known universe using the two most powerful substances available to man: excessive amounts of electricity and capital."
|
||||
icon = 'icons/obj/machines/bepis.dmi'
|
||||
icon_state = "chamber"
|
||||
density = TRUE
|
||||
layer = ABOVE_MOB_LAYER
|
||||
use_power = IDLE_POWER_USE
|
||||
active_power_usage = 1500
|
||||
circuit = /obj/item/circuitboard/machine/bepis
|
||||
|
||||
var/banking_amount = 100
|
||||
var/banked_cash = 0 //stored player cash
|
||||
var/datum/bank_account/account //payer's account.
|
||||
var/account_name //name of the payer's account.
|
||||
var/error_cause = null
|
||||
//Vars related to probability and chance of success for testing
|
||||
var/major_threshold = MAJOR_THRESHOLD
|
||||
var/minor_threshold = MINOR_THRESHOLD
|
||||
var/std = STANDARD_DEVIATION //That's Standard Deviation, what did you think it was?
|
||||
//Stock part variables
|
||||
var/power_saver = 1
|
||||
var/inaccuracy_percentage = 1.5
|
||||
var/positive_cash_offset = 0
|
||||
var/negative_cash_offset = 0
|
||||
var/minor_rewards = list(/obj/item/stack/circuit_stack/full, //To add a new minor reward, add it here.
|
||||
/obj/item/airlock_painter/decal,
|
||||
/obj/item/pen/survival,
|
||||
/obj/item/circuitboard/machine/sleeper/party,
|
||||
/obj/item/toy/sprayoncan)
|
||||
var/static/list/item_list = list()
|
||||
|
||||
/obj/machinery/rnd/bepis/attackby(obj/item/O, mob/user, params)
|
||||
if(default_deconstruction_screwdriver(user, "chamber_open", "chamber", O))
|
||||
update_icon_state()
|
||||
return
|
||||
if(default_deconstruction_crowbar(O))
|
||||
return
|
||||
if(!is_operational())
|
||||
to_chat(user, "<span class='notice'>[src] can't accept money when it's not functioning.</span>")
|
||||
return
|
||||
if(istype(O, /obj/item/holochip) || istype(O, /obj/item/stack/spacecash))
|
||||
var/deposit_value = O.get_item_credit_value()
|
||||
banked_cash += deposit_value
|
||||
qdel(O)
|
||||
say("Deposited [deposit_value] credits into storage.")
|
||||
update_icon_state()
|
||||
return
|
||||
if(istype(O, /obj/item/card/id))
|
||||
var/obj/item/card/id/Card = O
|
||||
if(Card.registered_account)
|
||||
account = Card.registered_account
|
||||
account_name = Card.registered_name
|
||||
say("New account detected. Console Updated.")
|
||||
else
|
||||
say("No account detected on card. Aborting.")
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/machinery/rnd/bepis/RefreshParts()
|
||||
var/C = 0
|
||||
var/M = 0
|
||||
var/L = 0
|
||||
var/S = 0
|
||||
for(var/obj/item/stock_parts/capacitor/Cap in component_parts)
|
||||
C += ((Cap.rating - 1) * 0.1)
|
||||
power_saver = 1 - C
|
||||
for(var/obj/item/stock_parts/manipulator/Manip in component_parts)
|
||||
M += ((Manip.rating - 1) * 250)
|
||||
positive_cash_offset = M
|
||||
for(var/obj/item/stock_parts/micro_laser/Laser in component_parts)
|
||||
L += ((Laser.rating - 1) * 250)
|
||||
negative_cash_offset = L
|
||||
for(var/obj/item/stock_parts/scanning_module/Scan in component_parts)
|
||||
S += ((Scan.rating - 1) * 0.25)
|
||||
inaccuracy_percentage = (1.5 - S)
|
||||
|
||||
/obj/machinery/rnd/bepis/proc/depositcash()
|
||||
var/deposit_value = 0
|
||||
deposit_value = banking_amount
|
||||
if(deposit_value == 0)
|
||||
update_icon_state()
|
||||
say("Attempting to deposit 0 credits. Aborting.")
|
||||
return
|
||||
deposit_value = clamp(round(deposit_value, 1), 1, 15000)
|
||||
if(!account)
|
||||
say("Cannot find user account. Please swipe a valid ID.")
|
||||
return
|
||||
if(!account.has_money(deposit_value))
|
||||
say("You do not possess enough credits.")
|
||||
return
|
||||
account.adjust_money(-deposit_value) //The money vanishes, not paid to any accounts.
|
||||
SSblackbox.record_feedback("amount", "BEPIS_credits_spent", deposit_value)
|
||||
banked_cash += deposit_value
|
||||
use_power(1000 * power_saver)
|
||||
say("Cash deposit successful. There is [banked_cash] in the chamber.")
|
||||
update_icon_state()
|
||||
return
|
||||
|
||||
/obj/machinery/rnd/bepis/proc/withdrawcash()
|
||||
var/withdraw_value = 0
|
||||
withdraw_value = banking_amount
|
||||
if(withdraw_value > banked_cash)
|
||||
say("Cannot withdraw more than stored funds. Aborting.")
|
||||
else
|
||||
banked_cash -= withdraw_value
|
||||
new /obj/item/holochip(src.loc, withdraw_value)
|
||||
say("Withdrawing [withdraw_value] credits from the chamber.")
|
||||
update_icon_state()
|
||||
return
|
||||
|
||||
/obj/machinery/rnd/bepis/proc/calcsuccess()
|
||||
var/turf/dropturf = null
|
||||
var/gauss_major = 0
|
||||
var/gauss_minor = 0
|
||||
var/gauss_real = 0
|
||||
var/list/turfs = block(locate(x-1,y-1,z),locate(x+1,y+1,z)) //NO MORE DISCS IN WINDOWS
|
||||
while(length(turfs))
|
||||
var/turf/T = pick_n_take(turfs)
|
||||
if(is_blocked_turf(T, exclude_mobs=TRUE))
|
||||
continue
|
||||
else
|
||||
dropturf = T
|
||||
break
|
||||
if (!dropturf)
|
||||
dropturf = drop_location()
|
||||
gauss_major = (gaussian(major_threshold, std) - negative_cash_offset) //This is the randomized profit value that this experiment has to surpass to unlock a tech.
|
||||
gauss_minor = (gaussian(minor_threshold, std) - negative_cash_offset) //And this is the threshold to instead get a minor prize.
|
||||
gauss_real = (gaussian(banked_cash, std*inaccuracy_percentage) + positive_cash_offset) //this is the randomized profit value that your experiment expects to give.
|
||||
say("Real: [gauss_real]. Minor: [gauss_minor]. Major: [gauss_major].")
|
||||
flick("chamber_flash",src)
|
||||
update_icon_state()
|
||||
banked_cash = 0
|
||||
if((gauss_real >= gauss_major) && (SSresearch.techweb_nodes_experimental.len > 0)) //Major Success.
|
||||
say("Experiment concluded with major success. New technology node discovered on technology disc.")
|
||||
new /obj/item/disk/tech_disk/major(dropturf,1)
|
||||
if(SSresearch.techweb_nodes_experimental.len == 0)
|
||||
say("Expended all available experimental technology nodes. Resorting to minor rewards.")
|
||||
return
|
||||
if(gauss_real >= gauss_minor) //Minor Success.
|
||||
var/reward = pick(minor_rewards)
|
||||
new reward(dropturf)
|
||||
say("Experiment concluded with partial success. Dispensing compiled research efforts.")
|
||||
return
|
||||
if(gauss_real <= -1) //Critical Failure
|
||||
say("ERROR: CRITICAL MACHIME MALFUNCTI- ON. CURRENCY IS NOT CRASH. CANNOT COMPUTE COMMAND: 'make bucks'") //not a typo, for once.
|
||||
new /mob/living/simple_animal/deer(dropturf, 1)
|
||||
use_power(MACHINE_OVERLOAD * power_saver) //To prevent gambling at low cost and also prevent spamming for infinite deer.
|
||||
return
|
||||
//Minor Failure
|
||||
error_cause = pick("attempted to sell grey products to American dominated market.","attempted to sell gray products to British dominated market.","placed wild assumption that PDAs would go out of style.","simulated product #76 damaged brand reputation mortally.","simulated business model resembled 'pyramid scheme' by 98.7%.","product accidently granted override access to all station doors.")
|
||||
say("Experiment concluded with zero product viability. Cause of error: [error_cause]")
|
||||
return
|
||||
|
||||
/obj/machinery/rnd/bepis/update_icon_state()
|
||||
if(panel_open == TRUE)
|
||||
icon_state = "chamber_open"
|
||||
return
|
||||
if((use_power == ACTIVE_POWER_USE) && (banked_cash > 0) && (is_operational()))
|
||||
icon_state = "chamber_active_loaded"
|
||||
return
|
||||
if (((use_power == IDLE_POWER_USE) && (banked_cash > 0)) || (banked_cash > 0) && (!is_operational()))
|
||||
icon_state = "chamber_loaded"
|
||||
return
|
||||
if(use_power == ACTIVE_POWER_USE && is_operational())
|
||||
icon_state = "chamber_active"
|
||||
return
|
||||
if(((use_power == IDLE_POWER_USE) && (banked_cash == 0)) || (!is_operational()))
|
||||
icon_state = "chamber"
|
||||
return
|
||||
|
||||
/obj/machinery/rnd/bepis/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "bepis", name, 500, 480, master_ui, state)
|
||||
ui.open()
|
||||
RefreshParts()
|
||||
|
||||
/obj/machinery/rnd/bepis/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
var/powered = FALSE
|
||||
var/zvalue = (banked_cash - (major_threshold - positive_cash_offset - negative_cash_offset))/(std)
|
||||
var/std_success = 0
|
||||
var/prob_success = 0
|
||||
//Admittedly this is messy, but not nearly as messy as the alternative, which is jury-rigging an entire Z-table into the code, or making an adaptive z-table.
|
||||
var/z = abs(zvalue)
|
||||
if(z > 0 && z <= 0.5)
|
||||
std_success = 19.1
|
||||
else if(z > 0.5 && z <= 1.0)
|
||||
std_success = 34.1
|
||||
else if(z > 1.0 && z <= 1.5)
|
||||
std_success = 43.3
|
||||
else if(z > 1.5 && z <= 2.0)
|
||||
std_success = 47.7
|
||||
else if(z > 2.0 && z <= 2.5)
|
||||
std_success = 49.4
|
||||
else
|
||||
std_success = 50
|
||||
if(zvalue > 0)
|
||||
prob_success = 50 + std_success
|
||||
else if(zvalue == 0)
|
||||
prob_success = 50
|
||||
else
|
||||
prob_success = 50 - std_success
|
||||
|
||||
if(use_power == ACTIVE_POWER_USE)
|
||||
powered = TRUE
|
||||
data["account_owner"] = account_name
|
||||
data["amount"] = banking_amount
|
||||
data["stored_cash"] = banked_cash
|
||||
data["mean_value"] = (major_threshold - positive_cash_offset - negative_cash_offset)
|
||||
data["error_name"] = error_cause
|
||||
data["power_saver"] = power_saver
|
||||
data["accuracy_percentage"] = inaccuracy_percentage * 100
|
||||
data["positive_cash_offset"] = positive_cash_offset
|
||||
data["negative_cash_offset"] = negative_cash_offset
|
||||
data["manual_power"] = powered ? FALSE : TRUE
|
||||
data["silicon_check"] = issilicon(user)
|
||||
data["success_estimate"] = prob_success
|
||||
return data
|
||||
|
||||
/obj/machinery/rnd/bepis/ui_act(action,params)
|
||||
if(..())
|
||||
return
|
||||
switch(action)
|
||||
if("deposit_cash")
|
||||
if(use_power == IDLE_POWER_USE)
|
||||
return
|
||||
depositcash()
|
||||
if("withdraw_cash")
|
||||
if(use_power == IDLE_POWER_USE)
|
||||
return
|
||||
withdrawcash()
|
||||
if("begin_experiment")
|
||||
if(use_power == IDLE_POWER_USE)
|
||||
return
|
||||
if(banked_cash == 0)
|
||||
say("Please deposit funds to begin testing.")
|
||||
return
|
||||
calcsuccess()
|
||||
use_power(MACHINE_OPERATION * power_saver) //This thing should eat your APC battery if you're not careful.
|
||||
use_power = IDLE_POWER_USE //Machine shuts off after use to prevent spam and look better visually.
|
||||
update_icon_state()
|
||||
if("amount")
|
||||
var/input = text2num(params["amount"])
|
||||
if(input)
|
||||
banking_amount = input
|
||||
if("toggle_power")
|
||||
if(use_power == ACTIVE_POWER_USE)
|
||||
use_power = IDLE_POWER_USE
|
||||
else
|
||||
use_power = ACTIVE_POWER_USE
|
||||
update_icon_state()
|
||||
if("account_reset")
|
||||
if(use_power == IDLE_POWER_USE)
|
||||
return
|
||||
account_name = ""
|
||||
account = null
|
||||
say("Account settings reset.")
|
||||
. = TRUE
|
||||
+11
-6
@@ -10,6 +10,7 @@
|
||||
id = "beanbag_slug"
|
||||
build_type = AUTOLATHE
|
||||
materials = list(/datum/material/iron = 250)
|
||||
materials = list(/datum/material/iron = 1000)
|
||||
build_path = /obj/item/ammo_casing/shotgun/beanbag
|
||||
category = list("initial", "Security")
|
||||
|
||||
@@ -73,12 +74,12 @@
|
||||
build_path = /obj/item/restraints/handcuffs
|
||||
category = list("hacked", "Security")
|
||||
|
||||
/datum/design/receiver
|
||||
name = "Modular Receiver"
|
||||
id = "receiver"
|
||||
build_type = AUTOLATHE | NO_PUBLIC_LATHE
|
||||
materials = list(/datum/material/iron = 15000)
|
||||
build_path = /obj/item/weaponcrafting/receiver
|
||||
/datum/design/rifle_receiver
|
||||
name = "Rifle Receiver"
|
||||
id = "rifle_receiver"
|
||||
build_type = AUTOLATHE
|
||||
materials = list(/datum/material/iron = 40000)
|
||||
build_path = /obj/item/weaponcrafting/improvised_parts/rifle_receiver
|
||||
category = list("hacked", "Security")
|
||||
|
||||
/datum/design/shotgun_slug
|
||||
@@ -113,6 +114,10 @@
|
||||
build_path = /obj/item/ammo_casing/shotgun/incendiary
|
||||
category = list("hacked", "Security")
|
||||
|
||||
/////////////////
|
||||
// Bullets //
|
||||
/////////////////
|
||||
|
||||
/datum/design/riot_dart
|
||||
name = "Foam Riot Dart"
|
||||
id = "riot_dart"
|
||||
|
||||
+10
-1
@@ -271,4 +271,13 @@
|
||||
build_type = AUTOLATHE
|
||||
materials = list(/datum/material/iron = 5000, /datum/material/glass = 2000)
|
||||
build_path = /obj/item/vending_refill/custom
|
||||
category = list("initial", "Misc")
|
||||
category = list("initial", "Misc")
|
||||
|
||||
/datum/design/trigger_assembly
|
||||
name = "Trigger Assembly"
|
||||
id = "trigger_assembly"
|
||||
build_type = AUTOLATHE
|
||||
materials = list(/datum/material/iron = 6500, /datum/material/glass = 50)
|
||||
build_path = /obj/item/weaponcrafting/improvised_parts/trigger_assembly
|
||||
category = list("initial", "Misc")
|
||||
|
||||
|
||||
@@ -157,3 +157,11 @@
|
||||
materials = list(/datum/material/iron = 150, /datum/material/glass = 150)
|
||||
build_path = /obj/item/geiger_counter
|
||||
category = list("initial", "Tools")
|
||||
|
||||
/datum/design/saw
|
||||
name = "Hand Saw"
|
||||
id = "handsaw"
|
||||
build_type = AUTOLATHE
|
||||
materials = list(/datum/material/iron = 500)
|
||||
build_path = /obj/item/hatchet/saw
|
||||
category = list("initial", "Tools")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user