This commit is contained in:
Ghommie
2020-05-31 16:08:52 +02:00
363 changed files with 10012 additions and 3157 deletions
+48
View File
@@ -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)]"
@@ -1,4 +1,5 @@
#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing )
/// Get displayed variable in VV variable list
/proc/debug_variable(name, value, level, datum/D, sanitize = TRUE) //if D is a list, name will be index, and value will be assoc value.
var/header
if(D)
@@ -35,6 +36,19 @@
else if (isfile(value))
item = "[VV_HTML_ENCODE(name)] = <span class='value'>'[value]'</span>"
else if(istype(value, /matrix)) // Needs to be before datum
var/matrix/M = value
item = {"[VV_HTML_ENCODE(name)] = <span class='value'>
<table class='matrixbrak'><tbody><tr>
<td class='lbrak'>&nbsp;</td>
<td><table class='matrix'><tbody>
<tr><td>[M.a]</td><td>[M.d]</td><td>0</td></tr>
<tr><td>[M.b]</td><td>[M.e]</td><td>0</td></tr>
<tr><td>[M.c]</td><td>[M.f]</td><td>1</td></tr>
</tbody></table></td>
<td class='rbrak'>&nbsp;</td>
</tr></tbody></table></span>"} //TODO link to modify_transform wrapper for all matrices
else if (istype(value, /datum))
var/datum/DV = value
if ("[DV]" != "[DV.type]") //if the thing as a name var, lets use it.
@@ -96,16 +96,7 @@
<head>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
<title>[title]</title>
<style>
body {
font-family: Verdana, sans-serif;
font-size: 9pt;
}
.value {
font-family: "Courier New", monospace;
font-size: 8pt;
}
</style>
<link rel="stylesheet" type="text/css" href="view_variables.css">
</head>
<body onload='selectTextField()' onkeydown='return handle_keydown()' onkeyup='handle_keyup()'>
<script type="text/javascript">
@@ -177,7 +177,7 @@
if(owner.current.blood_volume < BLOOD_VOLUME_BAD / 2)
owner.current.blur_eyes(8 - 8 * (owner.current.blood_volume / BLOOD_VOLUME_BAD))
// Nutrition
owner.current.set_nutrition(min(owner.current.blood_volume, NUTRITION_LEVEL_FULL)) //The amount of blood is how full we are.
owner.current.set_nutrition(min(owner.current.blood_volume, NUTRITION_LEVEL_FED)) //The amount of blood is how full we are.
//A bit higher regeneration based on blood volume
if(owner.current.blood_volume < 700)
additional_regen = 0.4
@@ -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)
+7
View File
@@ -748,3 +748,10 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/spritesheet/simple/skills
name = "skills"
/datum/asset/spritesheet/simple/skills/register()
InsertAll("", 'icons/UI_icons/skills.dmi')
/datum/asset/simple/vv
assets = list(
"view_variables.css" = 'html/admin/view_variables.css'
)
+1 -6
View File
@@ -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
+32
View File
@@ -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
+7 -1
View File
@@ -789,7 +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])", TRUE)
else
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>")
@@ -809,8 +811,12 @@ 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])", TRUE)
return
if(prefs.log_clicks)
log_click(object, location, control, params, src)
if (prefs.hotkeys)
// If hotkey mode is enabled, then clicking the map will automatically
// unfocus the text bar. This removes the red color from the text bar
+1
View File
@@ -15,6 +15,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/muted = 0
var/last_ip
var/last_id
var/log_clicks = FALSE
var/icon/custom_holoform_icon
var/list/cached_holoform_icons
@@ -317,8 +317,23 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
no_tetris_storage = sanitize_integer(no_tetris_storage, 0, 1, initial(no_tetris_storage))
key_bindings = sanitize_islist(key_bindings, list())
verify_keybindings_valid() // one of these days this will runtime and you'll be glad that i put it in a different proc so no one gets their saves wiped
return 1
/datum/preferences/proc/verify_keybindings_valid()
// Sanitize the actual keybinds to make sure they exist.
for(var/key in key_bindings)
if(!islist(key_bindings[key]))
key_bindings -= key
var/list/binds = key_bindings[key]
for(var/bind in binds)
if(!GLOB.keybindings_by_name[bind])
binds -= bind
if(!length(binds))
key_bindings -= key
// End
/datum/preferences/proc/save_preferences()
if(!path)
return 0
@@ -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)
+58
View File
@@ -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."
+10
View File
@@ -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"
+1
View File
@@ -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
+121 -18
View File
@@ -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
+1
View File
@@ -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,
+7 -6
View File
@@ -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
+22
View File
@@ -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"
@@ -403,6 +403,12 @@ h1.alert, h2.alert {color: #000000;}
.his_grace {color: #15D512; font-family: "Courier New", cursive, sans-serif; font-style: italic;}
.spooky {color: #FF6100;}
.velvet {color: #660015; font-weight: bold; animation: velvet 5000ms infinite;}
.lethal {color: #bf3d3d; font-weight: bold;}
.stun {color: #0f81bc; font-weight: bold;}
.ion {color: #d084d6; font-weight: bold;}
.xray {color: #32c025; font-weight: bold;}
@keyframes velvet {
0% { color: #400020; }
40% { color: #FF0000; }
@@ -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,
+1 -5
View File
@@ -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
+34 -51
View File
@@ -2,11 +2,11 @@
// And corners get shared between multiple turfs (unless you're on the corners of the map, then 1 corner doesn't).
// For the record: these should never ever ever be deleted, even if the turf doesn't have dynamic lighting.
// This list is what the code that assigns corners listens to, the order in this list is the order in which corners are added to the /turf/corners list.
GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, NORTHWEST))
/datum/lighting_corner
var/list/turf/masters
var/turf/northeast
var/turf/northwest
var/turf/southeast
var/turf/southwest
var/list/datum/light_source/affecting // Light sources affecting us.
var/active = FALSE // TRUE if one of our masters has dynamic lighting.
@@ -25,10 +25,18 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST,
var/cache_b = LIGHTING_SOFT_THRESHOLD
var/cache_mx = 0
/datum/lighting_corner/New(var/turf/new_turf, var/diagonal)
// Diagonal is our direction FROM them, not to.
/datum/lighting_corner/New(turf/new_turf, diagonal)
. = ..()
masters = list()
masters[new_turf] = turn(diagonal, 180)
#define SET_DIAGONAL(turf, diagonal) \
switch(diagonal){ \
if(SOUTHWEST) { northeast = turf; turf.lc_bottomleft = src; } \
if(SOUTHEAST) { northwest = turf; turf.lc_bottomright = src; } \
if(NORTHEAST) { southwest = turf; turf.lc_topright = src; } \
if(NORTHWEST) { southeast = turf; turf.lc_topleft = src; } \
}
SET_DIAGONAL(new_turf, diagonal)
z = new_turf.z
var/vertical = diagonal & ~(diagonal - 1) // The horizontal directions (4 and 8) are bigger than the vertical ones (1 and 2), so we can reliably say the lsb is the horizontal direction.
@@ -37,52 +45,28 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST,
x = new_turf.x + (horizontal == EAST ? 0.5 : -0.5)
y = new_turf.y + (vertical == NORTH ? 0.5 : -0.5)
// My initial plan was to make this loop through a list of all the dirs (horizontal, vertical, diagonal).
// Issue being that the only way I could think of doing it was very messy, slow and honestly overengineered.
// So we'll have this hardcode instead.
var/turf/T
var/i
// Diagonal one is easy.
// Build diagonal one
T = get_step(new_turf, diagonal)
if (T) // In case we're on the map's border.
if (!T.corners)
T.corners = list(null, null, null, null)
masters[T] = diagonal
i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180))
T.corners[i] = src
// Now the horizontal one.
if(T)
SET_DIAGONAL(T, turn(diagonal, 180))
// Build horizontal
T = get_step(new_turf, horizontal)
if (T) // Ditto.
if (!T.corners)
T.corners = list(null, null, null, null)
masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
T.corners[i] = src
// And finally the vertical one.
if(T)
SET_DIAGONAL(T, turn(((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH), 180))
// Build vertical
T = get_step(new_turf, vertical)
if (T)
if (!T.corners)
T.corners = list(null, null, null, null)
masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
T.corners[i] = src
if(T)
SET_DIAGONAL(T, turn(((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH), 180))
update_active()
#undef SET_DIAGONAL
/datum/lighting_corner/proc/update_active()
active = FALSE
var/turf/T
var/thing
for (thing in masters)
T = thing
if (T.lighting_object)
active = TRUE
if(northeast?.lighting_object || northwest?.lighting_object || southeast?.lighting_object || southwest?.lighting_object)
active = TRUE
// God that was a mess, now to do the rest of the corner code! Hooray!
/datum/lighting_corner/proc/update_lumcount(var/delta_r, var/delta_g, var/delta_b)
@@ -122,13 +106,12 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST,
#endif
cache_mx = round(mx, LIGHTING_ROUND_VALUE)
for (var/TT in masters)
var/turf/T = TT
if (T.lighting_object)
if (!T.lighting_object.needs_update)
T.lighting_object.needs_update = TRUE
GLOB.lighting_update_objects += T.lighting_object
#define QUEUE(turf) if(turf?.lighting_object && !turf.lighting_object.needs_update) { turf.lighting_object.needs_update = TRUE; GLOB.lighting_update_objects += turf.lighting_object }
QUEUE(northeast)
QUEUE(northwest)
QUEUE(southeast)
QUEUE(southwest)
#undef QUEUE
/datum/lighting_corner/dummy/New()
return
+5 -11
View File
@@ -55,7 +55,7 @@
if (loc)
var/turf/oldturf = get_turf(myturf)
var/turf/newturf = get_turf(loc)
warning("A lighting object realised it's loc had changed in update() ([myturf]\[[myturf ? myturf.type : "null"]]([COORD(oldturf)]) -> [loc]\[[ loc ? loc.type : "null"]]([COORD(newturf)]))!")
stack_trace("A lighting object realised it's loc had changed in update() ([myturf]\[[myturf ? myturf.type : "null"]]([COORD(oldturf)]) -> [loc]\[[ loc ? loc.type : "null"]]([COORD(newturf)]))!")
qdel(src, TRUE)
return
@@ -71,16 +71,10 @@
// See LIGHTING_CORNER_DIAGONAL in lighting_corner.dm for why these values are what they are.
var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new
var/list/corners = myturf.corners
var/datum/lighting_corner/cr = dummy_lighting_corner
var/datum/lighting_corner/cg = dummy_lighting_corner
var/datum/lighting_corner/cb = dummy_lighting_corner
var/datum/lighting_corner/ca = dummy_lighting_corner
if (corners) //done this way for speed
cr = corners[3] || dummy_lighting_corner
cg = corners[2] || dummy_lighting_corner
cb = corners[4] || dummy_lighting_corner
ca = corners[1] || dummy_lighting_corner
var/datum/lighting_corner/cr = myturf.lc_bottomleft || dummy_lighting_corner
var/datum/lighting_corner/cg = myturf.lc_bottomright || dummy_lighting_corner
var/datum/lighting_corner/cb = myturf.lc_topleft || dummy_lighting_corner
var/datum/lighting_corner/ca = myturf.lc_topright || dummy_lighting_corner
var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx)
+11 -4
View File
@@ -237,18 +237,25 @@
return //nothing's changed
var/list/datum/lighting_corner/corners = list()
var/datum/lighting_corner/C
var/list/turf/turfs = list()
var/thing
var/datum/lighting_corner/C
var/turf/T
if (source_turf)
var/oldlum = source_turf.luminosity
source_turf.luminosity = CEILING(light_range, 1)
for(T in view(CEILING(light_range, 1), source_turf))
for (thing in T.get_corners(source_turf))
C = thing
corners[C] = 0
turfs += T
if(!IS_DYNAMIC_LIGHTING(T) && !T.light_sources)
continue
if(!T.lighting_corners_initialised)
T.generate_missing_corners()
if(T.has_opaque_atom)
continue
corners[T.lc_topright] = 0
corners[T.lc_bottomright] = 0
corners[T.lc_bottomleft] = 0
corners[T.lc_topleft] = 0
source_turf.luminosity = oldlum
LAZYINITLIST(affecting_turfs)
+37 -47
View File
@@ -6,9 +6,15 @@
var/tmp/list/datum/light_source/affecting_lights // List of light sources affecting this turf.
var/tmp/atom/movable/lighting_object/lighting_object // Our lighting object.
var/tmp/list/datum/lighting_corner/corners
var/tmp/datum/lighting_corner/lc_topleft
var/tmp/datum/lighting_corner/lc_topright
var/tmp/datum/lighting_corner/lc_bottomleft
var/tmp/datum/lighting_corner/lc_bottomright
var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile.
// counterclockwisse 0 to 360
#define PROC_ON_CORNERS(operation) lc_topright?.##operation;lc_bottomright?.##operation;lc_bottomleft?.##operation;lc_topleft?.##operation
// Causes any affecting light sources to be queued for a visibility update, for example a door got opened.
/turf/proc/reconsider_lights()
var/datum/light_source/L
@@ -21,13 +27,7 @@
if (lighting_object)
qdel(lighting_object, TRUE)
var/datum/lighting_corner/C
var/thing
for (thing in corners)
if(!thing)
continue
C = thing
C.update_active()
PROC_ON_CORNERS(update_active())
// Builds a lighting object for us, but only if our area is dynamic.
/turf/proc/lighting_build_overlay()
@@ -43,32 +43,31 @@
new/atom/movable/lighting_object(src)
var/thing
var/datum/lighting_corner/C
var/datum/light_source/S
for (thing in corners)
if(!thing)
continue
C = thing
if (!C.active) // We would activate the corner, calculate the lighting for it.
for (thing in C.affecting)
S = thing
S.recalc_corner(C)
C.active = TRUE
var/i
#define OPERATE(corner) \
if(corner && !corner.active) { \
for(i in corner.affecting) { \
S = i ; \
S.recalc_corner(corner) \
} \
corner.active = TRUE \
}
OPERATE(lc_topright)
OPERATE(lc_bottomright)
OPERATE(lc_bottomleft)
OPERATE(lc_topleft)
#undef OPERATE
// Used to get a scaled lumcount.
/turf/proc/get_lumcount(var/minlum = 0, var/maxlum = 1)
if (!lighting_object)
if(!lighting_object)
return 1
var/totallums = 0
var/thing
var/datum/lighting_corner/L
for (thing in corners)
if(!thing)
continue
L = thing
totallums += L.lum_r + L.lum_b + L.lum_g
var/totallums = (lc_topright? (lc_topright.lum_r + lc_topright.lum_g + lc_topright.lum_b) : 0) \
+ (lc_bottomright? (lc_bottomright.lum_r + lc_bottomright.lum_g + lc_bottomright.lum_b) : 0) \
+ (lc_bottomleft? (lc_bottomleft.lum_r + lc_bottomleft.lum_g + lc_bottomleft.lum_b) : 0) \
+ (lc_topleft? (lc_topleft.lum_r + lc_topleft.lum_g + lc_topleft.lum_b) : 0)
totallums /= 12 // 4 corners, each with 3 channels, get the average.
@@ -110,27 +109,18 @@
else
lighting_clear_overlay()
/turf/proc/get_corners()
if (!IS_DYNAMIC_LIGHTING(src) && !light_sources)
return null
if (!lighting_corners_initialised)
generate_missing_corners()
if (has_opaque_atom)
return null // Since this proc gets used in a for loop, null won't be looped though.
return corners
/turf/proc/generate_missing_corners()
if (!IS_DYNAMIC_LIGHTING(src) && !light_sources)
return
lighting_corners_initialised = TRUE
if (!corners)
corners = list(null, null, null, null)
for (var/i = 1 to 4)
if (corners[i]) // Already have a corner on this direction.
continue
corners[i] = new/datum/lighting_corner(src, GLOB.LIGHTING_CORNER_DIAGONAL[i])
// counterclockwise from 0 to 360.
if(!lc_topright)
new /datum/lighting_corner(src, NORTHEAST)
if(!lc_bottomright)
new /datum/lighting_corner(src, SOUTHEAST)
if(!lc_bottomleft)
new /datum/lighting_corner(src, SOUTHWEST)
if(!lc_topleft)
new /datum/lighting_corner(src, NORTHWEST)
#undef PROC_ON_CORNERS
@@ -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)
@@ -417,7 +417,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"
@@ -1,7 +1,7 @@
/datum/sprite_accessory/horns
icon = 'icons/mob/mutant_bodyparts.dmi'
color_src = HORNCOLOR
relevant_layers = list(BODY_ADJ_LAYER)
relevant_layers = list(HORNS_LAYER)
/datum/sprite_accessory/horns/none
name = "None"
+5 -5
View File
@@ -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
+10 -10
View File
@@ -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()
+7 -10
View File
@@ -443,14 +443,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)
@@ -828,16 +828,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()
@@ -1123,10 +1124,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
-6
View File
@@ -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 ..()
+1 -1
View File
@@ -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
@@ -628,6 +628,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.remove_overlay(BODY_ADJ_LAYER)
H.remove_overlay(BODY_ADJ_UPPER_LAYER)
H.remove_overlay(BODY_FRONT_LAYER)
H.remove_overlay(HORNS_LAYER)
if(!mutant_bodyparts)
return
@@ -839,8 +840,13 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(!S.mutant_part_string)
dna_feature_as_text_string[S] = bodypart
var/static/list/layer_text = list("[BODY_BEHIND_LAYER]" = "BEHIND", "[BODY_ADJ_LAYER]" = "ADJ", \
"[BODY_ADJ_UPPER_LAYER]" = "ADJUP", "[BODY_FRONT_LAYER]" = "FRONT")
var/static/list/layer_text = list(
"[BODY_BEHIND_LAYER]" = "BEHIND",
"[BODY_ADJ_LAYER]" = "ADJ",
"[BODY_ADJ_UPPER_LAYER]" = "ADJUP",
"[BODY_FRONT_LAYER]" = "FRONT",
"[HORNS_LAYER]" = "HORNS",
)
var/g = (H.dna.features["body_model"] == FEMALE) ? "f" : "m"
var/list/colorlist = list()
@@ -1020,6 +1026,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.apply_overlay(BODY_ADJ_LAYER)
H.apply_overlay(BODY_ADJ_UPPER_LAYER)
H.apply_overlay(BODY_FRONT_LAYER)
H.apply_overlay(HORNS_LAYER)
/*
@@ -1473,11 +1480,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 +1632,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 +1834,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()
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
-93
View File
@@ -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>")
+1 -1
View File
@@ -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 --
+3 -2
View File
@@ -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)
+4 -8
View File
@@ -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()
+7
View File
@@ -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()
+2 -2
View File
@@ -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()
+8 -8
View File
@@ -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)
+3
View File
@@ -384,3 +384,6 @@
/obj/item/paper/crumpled/bloody
icon_state = "scrap_bloodied"
/obj/item/paper/crumpled/muddy
icon_state = "scrap_mud"
+14
View File
@@ -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.
+12
View File
@@ -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
+2 -1
View File
@@ -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
+1
View File
@@ -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
+5 -1
View File
@@ -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
+5 -2
View File
@@ -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)
+1
View File
@@ -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
+1 -1
View File
@@ -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)
@@ -12,3 +12,105 @@
/obj/item/ammo_box/magazine/recharge/attack_self() //No popping out the "bullets"
return
// MWS Magazine //
/obj/item/ammo_box/magazine/mws_mag
name = "microbattery magazine"
desc = "A microbattery holder for the 'Big Iron'"
icon = 'icons/obj/ammo.dmi'
icon_state = "mws_mag"
caliber = "mws"
ammo_type = /obj/item/ammo_casing/mws_batt
start_empty = TRUE
max_ammo = 3
var/list/modes = list()
/obj/item/ammo_box/magazine/mws_mag/update_overlays()
.=..()
if(!stored_ammo.len)
return //Why bother
var/x_offset = 5
var/current = 0
for(var/B in stored_ammo)
var/obj/item/ammo_casing/mws_batt/batt = B
var/mutable_appearance/cap = mutable_appearance(icon, "[initial(icon_state)]_cap", color = batt.type_color)
cap.pixel_x = current * x_offset //Caps don't need a pixel_y offset
. += cap
if(batt.cell.charge > 0)
var/ratio = CEILING(clamp(batt.cell.charge / batt.cell.maxcharge, 0, 1) * 4, 1) //4 is how many lights we have a sprite for
var/mutable_appearance/charge = mutable_appearance(icon, "[initial(icon_state)]_charge-[ratio]", color = "#29EAF4") //Could use battery color but eh.
charge.pixel_x = current * x_offset
. += charge
current++ //Increment for offsets
// MWS Batteries //
/obj/item/ammo_casing/mws_batt
name = "\'MWS\' microbattery - UNKNOWN"
desc = "A miniature battery for an energy weapon."
icon = 'icons/obj/ammo.dmi'
icon_state = "mws_batt"
slot_flags = SLOT_BELT | SLOT_EARS
throwforce = 1
caliber = "mws"
var/type_color = null
var/type_name = null
var/obj/item/stock_parts/cell/cell
var/cell_type = /obj/item/stock_parts/cell{charge = 600; maxcharge = 600}
var/e_cost = 100
projectile_type = /obj/item/projectile/beam
/obj/item/ammo_casing/mws_batt/Initialize()
. = ..()
pixel_x = rand(-10, 10)
pixel_y = rand(-10, 10)
cell = new cell_type(src)
cell.give(cell.maxcharge)
update_icon()
/obj/item/ammo_casing/mws_batt/update_overlays()
.=..()
var/mutable_appearance/ends = mutable_appearance(icon, "[initial(icon_state)]_ends", color = type_color)
. += ends
/obj/item/ammo_casing/mws_batt/get_cell()
return cell
/obj/item/ammo_casing/mws_batt/proc/chargeshot()
if(cell.charge >= e_cost)
cell.use(e_cost)
newshot()
return
// Specific batteries //
/obj/item/ammo_casing/mws_batt/lethal
name = "'MWS' microbattery - LETHAL"
type_color = "#bf3d3d"
type_name = "<span class='lethal'>LETHAL</span>"
projectile_type = /obj/item/projectile/beam
/obj/item/ammo_casing/mws_batt/stun
name = "'MWS' microbattery - STUN"
type_color = "#0f81bc"
type_name = "<span class='stun'>STUN</span>"
projectile_type = /obj/item/projectile/beam/disabler
/obj/item/ammo_casing/mws_batt/xray
name = "'MWS' microbattery - XRAY"
type_color = "#32c025"
type_name = "<span class='xray'>XRAY</span>"
projectile_type = /obj/item/projectile/beam/xray
/obj/item/ammo_casing/mws_batt/ion
name = "'MWS' microbattery - ION"
type_color = "#d084d6"
type_name = "<span class='ion'>ION</span>"
projectile_type = /obj/item/projectile/ion
+17 -20
View File
@@ -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)
@@ -127,7 +128,7 @@
zoom(user, FALSE) //we can only stay zoomed in if it's in our hands //yeah and we only unzoom if we're actually zoomed using the gun!!
//called after the gun has successfully fired its chambered ammo.
/obj/item/gun/proc/process_chamber()
/obj/item/gun/proc/process_chamber(mob/living/user)
return FALSE
//check if there's enough ammo/energy/whatever to shoot one time
@@ -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)
@@ -304,7 +306,7 @@
else
shoot_with_empty_chamber(user)
return
process_chamber()
process_chamber(user)
update_icon()
SSblackbox.record_feedback("tally", "gun_fired", 1, type)
@@ -343,7 +345,7 @@
shoot_with_empty_chamber(user)
firing = FALSE
return FALSE
process_chamber()
process_chamber(user)
update_icon()
return TRUE
@@ -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)
@@ -372,3 +377,114 @@
user.emote("scream")
user.drop_all_held_items()
user.DefaultCombatKnockdown(80)
// -------------- HoS Modular Weapon System -------------
// ---------- Code originally from VoreStation ----------
/obj/item/gun/ballistic/revolver/mws
name = "MWS-01 'Big Iron'"
desc = "Modular Weapons System"
icon = 'icons/obj/guns/projectile.dmi'
icon_state = "mws"
fire_sound = 'sound/weapons/Taser.ogg'
mag_type = /obj/item/ammo_box/magazine/mws_mag
spawnwithmagazine = FALSE
recoil = 0
var/charge_sections = 6
/obj/item/gun/ballistic/revolver/mws/examine(mob/user)
. = ..()
. += "<span class='notice'>Alt-click to remove the magazine.</span>"
/obj/item/gun/ballistic/revolver/mws/shoot_with_empty_chamber(mob/living/user as mob|obj)
process_chamber(user)
if(!chambered || !chambered.BB)
to_chat(user, "<span class='danger'>*click*</span>")
playsound(src, "gun_dry_fire", 30, 1)
/obj/item/gun/ballistic/revolver/mws/process_chamber(mob/living/user)
if(chambered && !chambered.BB) //if BB is null, i.e the shot has been fired...
var/obj/item/ammo_casing/mws_batt/shot = chambered
if(shot.cell.charge >= shot.e_cost)
shot.chargeshot()
else
for(var/B in magazine.stored_ammo)
var/obj/item/ammo_casing/mws_batt/other_batt = B
if(istype(other_batt,shot) && other_batt.cell.charge >= other_batt.e_cost)
switch_to(other_batt, user)
break
update_icon()
/obj/item/gun/ballistic/revolver/mws/proc/switch_to(obj/item/ammo_casing/mws_batt/new_batt, mob/living/user)
if(ishuman(user))
if(chambered && new_batt.type == chambered.type)
to_chat(user,"<span class='warning'>[src] is now using the next [new_batt.type_name] power cell.</span>")
else
to_chat(user,"<span class='warning'>[src] is now firing [new_batt.type_name].</span>")
chambered = new_batt
update_icon()
/obj/item/gun/ballistic/revolver/mws/attack_self(mob/living/user)
if(!chambered)
return
var/list/stored_ammo = magazine.stored_ammo
if(stored_ammo.len == 1)
return //silly you.
//Find an ammotype that ISN'T the same, or exhaust the list and don't change.
var/our_slot = stored_ammo.Find(chambered)
for(var/index in 1 to stored_ammo.len)
var/true_index = ((our_slot + index - 1) % stored_ammo.len) + 1 // Stupid ONE BASED lists!
var/obj/item/ammo_casing/mws_batt/next_batt = stored_ammo[true_index]
if(chambered != next_batt && !istype(next_batt, chambered.type) && next_batt.cell.charge >= next_batt.e_cost)
switch_to(next_batt, user)
break
/obj/item/gun/ballistic/revolver/mws/AltClick(mob/living/user)
.=..()
if(magazine)
user.put_in_hands(magazine)
magazine.update_icon()
if(magazine.ammo_count())
playsound(src, 'sound/weapons/gun_magazine_remove_full.ogg', 70, 1)
else
playsound(src, "gun_remove_empty_magazine", 70, 1)
magazine = null
to_chat(user, "<span class='notice'>You pull the magazine out of [src].</span>")
if(chambered)
chambered = null
update_icon()
/obj/item/gun/ballistic/revolver/mws/update_overlays()
.=..()
if(!chambered)
return
var/obj/item/ammo_casing/mws_batt/batt = chambered
var/batt_color = batt.type_color //Used many times
//Mode bar
var/image/mode_bar = image(icon, icon_state = "[initial(icon_state)]_type")
mode_bar.color = batt_color
. += mode_bar
//Barrel color
var/mutable_appearance/barrel_color = mutable_appearance(icon, "[initial(icon_state)]_barrel", color = batt_color)
barrel_color.alpha = 150
. += barrel_color
//Charge bar
var/ratio = can_shoot() ? CEILING(clamp(batt.cell.charge / batt.cell.maxcharge, 0, 1) * charge_sections, 1) : 0
for(var/i = 0, i < ratio, i++)
var/mutable_appearance/charge_bar = mutable_appearance(icon, "[initial(icon_state)]_charge", color = batt_color)
charge_bar.pixel_x = i
. += charge_bar
@@ -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()
+22 -20
View File
@@ -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
+90 -44
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
)

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