Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into Ghommie-cit792
This commit is contained in:
@@ -142,3 +142,51 @@ GLOBAL_LIST(round_end_notifiees)
|
||||
log_admin("[sender.friendly_name] has added [params] to the current round's bunker bypass list.")
|
||||
message_admins("[sender.friendly_name] has added [params] to the current round's bunker bypass list.")
|
||||
return "[params] has been added to the current round's bunker bypass list."
|
||||
|
||||
// More (silly) chat commands citadel added.
|
||||
/datum/tgs_chat_command/wheelofsalt
|
||||
name = "wheelofsalt"
|
||||
help_text = "What are Citadel Station 13 players salting about today? Spin the wheel and find out!"
|
||||
|
||||
/datum/tgs_chat_command/wheelofsalt/Run(datum/tgs_chat_user/sender, params)
|
||||
var/saltresult = "The wheel of salt [pick("clatters","screams","vibrates","clanks","resonates","groans","moans","squeaks","emits a[pick(" god-forsaken"," lewd"," creepy"," generic","n orgasmic"," demonic")] [pick("airhorn","bike horn","trumpet","clown","latex","vore","dog","laughing")] noise")] as it spins violently... And it seems the salt of the day is the "
|
||||
var/saltprimarysubject = "[pick("combat","medical","grab","furry","wall","orgasm","cat","ERP","lizard","dog","latex","vision cone","atmospherics","table","chem","vore","dogborg","Skylar Lineman","Mekhi Anderson","Peppermint","rework","cum","dick","cockvore","Medihound","sleeper","belly sleeper","door wires","flightsuit","coder privilege","Developer abuse","ban reason","github self merge","red panda","beret","male catgirl","powergame","hexacrocin","Discord server","Clitadel","Cargonia","Solarian Republic","Main and RP merger","bluespace","salt","chem dispenser theft","Botany","moth","BWOINK","anal vore","stamina","Mason Jakops","mining","noodle","milf","Lavaland","Necropolis","Ashwalker","Chase Redtail","Drew Mint","Pavel Marsk","Joker Amari","Durgit","chaplain","Antag","nanite","Syndicate","Nar-Sie","Ratvar","Cult","maint","Foam-Force","AI","cyborg","ghost","clockwork","cyberpunk","vaporwave","Clown","Leon Beech","Mime","security","research","Megafauna","Bubblegum","Ash Drake","Legion","Colossus","White Shuttle","Changeling","Cowboy","Space Ninja","Poly","Revolutionary","Skyrim","forbidden fruits","xenomorph","blob","Nuclear Operative","crossdressing")]"
|
||||
var/saltsecondarysubject = "[pick("rework","changes","r34","ban","removal","addition","leak","proposal","fanart","introduction","tabling","ERP","bikeshedding","crossdressing","sprites","semen keg","argument","theft","nerf","screeching","salt","creampie","lewding","murder","kissing","marriage","replacement","fucking","ship","netflix adaptation","dance","remaster","system","voyeur","decoration","pre-order","bukkake","seduction","worship","gangbang","handholding")]"
|
||||
if(prob(10))
|
||||
saltresult += "@here for your salt, all day every day"
|
||||
if(prob(1))
|
||||
saltresult += " @everyone gets some salt this time too"
|
||||
else
|
||||
saltresult += "[saltprimarysubject] [saltsecondarysubject]"
|
||||
return "[saltresult]!"
|
||||
|
||||
/datum/tgs_chat_command/valentine
|
||||
name = "valentine"
|
||||
help_text = "Get a random flirt line."
|
||||
|
||||
/datum/tgs_chat_command/valentine/Run(datum/tgs_chat_user/sender, params)
|
||||
return "[pick(GLOB.flirts)]"
|
||||
|
||||
/datum/tgs_chat_command/despacito
|
||||
name = "despacito" //someone please high effort this sometime and make it a full on ytdl search
|
||||
help_text = "This is so sad."
|
||||
|
||||
/datum/tgs_chat_command/despacito/Run()
|
||||
return "https://www.youtube.com/watch?v=kJQP7kiw5Fk"
|
||||
|
||||
/datum/tgs_chat_command/poly
|
||||
name = "poly"
|
||||
help_text = "The Lewder, more applicable Poly speak for Citadel Station 13."
|
||||
var/list/speech_buffer
|
||||
|
||||
/datum/tgs_chat_command/poly/Run()
|
||||
LAZYINITLIST(speech_buffer) //I figure this is just safe to do for everything at this point
|
||||
if(length(speech_buffer)) //Let's not look up the whole json EVERY TIME, just the first time.
|
||||
return "[pick(speech_buffer)]"
|
||||
else
|
||||
var/json_file = file("data/npc_saves/Poly.json")
|
||||
if(!fexists(json_file))
|
||||
return "**BAWWWWWK!** LEAVE THE HEADSET! ***BAWKKKKK!!***"
|
||||
var/list/json = json_decode(file2text(json_file))
|
||||
speech_buffer = json["phrases"]
|
||||
return "[pick(speech_buffer)]"
|
||||
|
||||
@@ -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'> </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'> </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)
|
||||
|
||||
@@ -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'
|
||||
)
|
||||
|
||||
@@ -17,11 +17,6 @@
|
||||
//eg: "Bloody screen" > "goggles colour" as the former is much more important
|
||||
|
||||
|
||||
/mob
|
||||
var/list/client_colours = list()
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Adds an instance of colour_type to the mob's client_colours list
|
||||
colour_type - a typepath (subtyped from /datum/client_colour)
|
||||
@@ -113,4 +108,4 @@
|
||||
priority = INFINITY //we can't see colors anyway!
|
||||
|
||||
/datum/client_colour/monochrome/trance
|
||||
priority = 1
|
||||
priority = 1
|
||||
|
||||
@@ -99,5 +99,37 @@
|
||||
/// custom movement keys for this client
|
||||
var/list/movement_keys = list()
|
||||
|
||||
///Autoclick list of two elements, first being the clicked thing, second being the parameters.
|
||||
var/list/atom/selected_target[2]
|
||||
///Autoclick variable referencing the associated item.
|
||||
var/obj/item/active_mousedown_item = null
|
||||
///Used in MouseDrag to preserve the original mouse click parameters
|
||||
var/mouseParams = ""
|
||||
///Used in MouseDrag to preserve the last mouse-entered location.
|
||||
var/mouseLocation = null
|
||||
///Used in MouseDrag to preserve the last mouse-entered object.
|
||||
var/mouseObject = null
|
||||
var/mouseControlObject = null
|
||||
//Middle-mouse-button click dragtime control for aimbot exploit detection.
|
||||
var/middragtime = 0
|
||||
//Middle-mouse-button clicked object control for aimbot exploit detection.
|
||||
var/atom/middragatom
|
||||
|
||||
/// Messages currently seen by this client
|
||||
var/list/seen_messages
|
||||
///When was the last time we warned them about not cryoing without an ahelp, set to -5 minutes so that rounstart cryo still warns
|
||||
var/cryo_warned = -5 MINUTES
|
||||
|
||||
var/list/parallax_layers
|
||||
var/list/parallax_layers_cached
|
||||
var/atom/movable/movingmob
|
||||
var/turf/previous_turf
|
||||
///world.time of when we can state animate()ing parallax again
|
||||
var/dont_animate_parallax
|
||||
///world.time of last parallax update
|
||||
var/last_parallax_shift
|
||||
///ds between parallax updates
|
||||
var/parallax_throttle = 0
|
||||
var/parallax_movedir = 0
|
||||
var/parallax_layers_max = 3
|
||||
var/parallax_animate_timer
|
||||
|
||||
@@ -789,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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -13,6 +13,53 @@
|
||||
custom_price = 1200
|
||||
custom_premium_price = 1200
|
||||
|
||||
/obj/item/toy/sprayoncan
|
||||
name = "spray-on insulation applicator"
|
||||
desc = "What is the number one problem facing our station today?"
|
||||
icon = 'icons/obj/clothing/gloves.dmi'
|
||||
icon_state = "sprayoncan"
|
||||
|
||||
/obj/item/toy/sprayoncan/afterattack(atom/target, mob/living/carbon/user, proximity)
|
||||
if(iscarbon(target) && proximity)
|
||||
var/mob/living/carbon/C = target
|
||||
var/mob/living/carbon/U = user
|
||||
var/success = C.equip_to_slot_if_possible(new /obj/item/clothing/gloves/color/yellow/sprayon, ITEM_SLOT_GLOVES, TRUE, TRUE)
|
||||
if(success)
|
||||
if(C == user)
|
||||
C.visible_message("<span class='notice'>[U] sprays their hands with glittery rubber!</span>")
|
||||
else
|
||||
C.visible_message("<span class='warning'>[U] sprays glittery rubber on the hands of [C]!</span>")
|
||||
else
|
||||
C.visible_message("<span class='warning'>The rubber fails to stick to [C]'s hands!</span>")
|
||||
|
||||
qdel(src)
|
||||
|
||||
/obj/item/clothing/gloves/color/yellow/sprayon
|
||||
desc = "How're you gonna get 'em off, nerd?"
|
||||
name = "spray-on insulated gloves"
|
||||
icon_state = "sprayon"
|
||||
item_state = "sprayon"
|
||||
permeability_coefficient = 0
|
||||
resistance_flags = ACID_PROOF
|
||||
var/shocks_remaining = 10
|
||||
|
||||
/obj/item/clothing/gloves/color/yellow/sprayon/Initialize()
|
||||
.=..()
|
||||
ADD_TRAIT(src, TRAIT_NODROP, GLOVE_TRAIT)
|
||||
|
||||
/obj/item/clothing/gloves/color/yellow/sprayon/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
RegisterSignal(user, COMSIG_LIVING_SHOCK_PREVENTED, .proc/Shocked)
|
||||
|
||||
/obj/item/clothing/gloves/color/yellow/sprayon/proc/Shocked()
|
||||
shocks_remaining--
|
||||
if(shocks_remaining < 0)
|
||||
qdel(src) //if we run out of uses, the gloves crumble away into nothing, just like my dreams after working with .dm
|
||||
|
||||
/obj/item/clothing/gloves/color/yellow/sprayon/dropped()
|
||||
.=..()
|
||||
qdel(src) //loose nodrop items bad
|
||||
|
||||
/obj/item/clothing/gloves/color/fyellow //Cheap Chinese Crap
|
||||
desc = "These gloves are cheap knockoffs of the coveted ones - no way this can end badly."
|
||||
name = "budget insulated gloves"
|
||||
@@ -202,6 +249,17 @@
|
||||
permeability_coefficient = 0.3
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
|
||||
/obj/item/clothing/gloves/color/latex/engineering
|
||||
name = "tinker's gloves"
|
||||
desc = "Overdesigned engineering gloves that have automated construction subrutines dialed in, allowing for faster construction while worn."
|
||||
icon = 'icons/obj/clothing/clockwork_garb.dmi'
|
||||
icon_state = "clockwork_gauntlets"
|
||||
item_state = "clockwork_gauntlets"
|
||||
siemens_coefficient = 0.8
|
||||
permeability_coefficient = 0.3
|
||||
carrytrait = TRAIT_QUICK_BUILD
|
||||
custom_materials = list(/datum/material/iron=2000, /datum/material/silver=1500, /datum/material/gold = 1000)
|
||||
|
||||
/obj/item/clothing/gloves/color/white
|
||||
name = "white gloves"
|
||||
desc = "These look pretty fancy."
|
||||
|
||||
@@ -60,6 +60,16 @@
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
|
||||
|
||||
/obj/item/clothing/head/hardhat/red/upgraded
|
||||
name = "workplace-ready firefighter helmet"
|
||||
desc = "By applying state of the art lighting technology to a fire helmet, and using photo-chemical hardening methods, this hardhat will protect you from robust workplace hazards."
|
||||
icon_state = "hardhat0_purple"
|
||||
item_state = "hardhat0_purple"
|
||||
brightness_on = 5
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
custom_materials = list(/datum/material/iron = 4000, /datum/material/glass = 1000, /datum/material/plastic = 3000, /datum/material/silver = 500)
|
||||
hat_type = "purple"
|
||||
|
||||
/obj/item/clothing/head/hardhat/white
|
||||
icon_state = "hardhat0_white"
|
||||
item_state = "hardhat0_white"
|
||||
|
||||
@@ -198,6 +198,7 @@
|
||||
desc = "A creepy wooden mask. Surprisingly expressive for a poorly carved bit of wood."
|
||||
icon_state = "tiki_eyebrow"
|
||||
item_state = "tiki_eyebrow"
|
||||
custom_materials = list(/datum/material/wood = MINERAL_MATERIAL_AMOUNT * 1.25)
|
||||
resistance_flags = FLAMMABLE
|
||||
max_integrity = 100
|
||||
actions_types = list(/datum/action/item_action/adjust)
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
desc = "A pair of rather plain wooden sandals."
|
||||
name = "sandals"
|
||||
icon_state = "wizard"
|
||||
custom_materials = list(/datum/material/wood = MINERAL_MATERIAL_AMOUNT * 0.5)
|
||||
strip_delay = 50
|
||||
equip_delay_other = 50
|
||||
permeability_coefficient = 0.9
|
||||
|
||||
@@ -444,7 +444,6 @@ Contains:
|
||||
desc = "Powerful wards are built into this hardsuit, protecting the user from all manner of paranormal threats. Alas, this one looks pretty worn out and rusted."
|
||||
armor = list("melee" = 55, "bullet" = 40, "laser" = 40, "energy" = 40, "bomb" = 40, "bio" = 80, "rad" = 80, "fire" = 60, "acid" = 60)
|
||||
slowdown = 0.8
|
||||
obj_flags = IMMUTABLE_SLOW //rest in peace rusty joints.
|
||||
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/inquisitor/old
|
||||
charges = 12
|
||||
|
||||
@@ -470,7 +469,6 @@ Contains:
|
||||
desc = "Voices echo from the hardsuit, driving the user insane. This one is pretty battle-worn, but still fearsome."
|
||||
armor = list("melee" = 55, "bullet" = 40, "laser" = 40, "energy" = 40, "bomb" = 40, "bio" = 80, "rad" = 80, "fire" = 60, "acid" = 60)
|
||||
slowdown = 0.8
|
||||
obj_flags = IMMUTABLE_SLOW //rest in peace rusty joints.
|
||||
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/inquisitor/old
|
||||
charges = 6
|
||||
|
||||
|
||||
@@ -165,31 +165,134 @@
|
||||
var/robe_charge = TRUE
|
||||
actions_types = list(/datum/action/item_action/stickmen)
|
||||
|
||||
/obj/item/clothing/suit/wizrobe/paper/item_action_slot_check(slot, mob/user, datum/action/A)
|
||||
if(A.type == /datum/action/item_action/stickmen && slot != SLOT_WEAR_SUIT)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/suit/wizrobe/paper/ui_action_click(mob/user, action)
|
||||
stickmen()
|
||||
//Stickmemes. VV-friendly.
|
||||
/datum/action/item_action/stickmen
|
||||
name = "Summon Stick Minions"
|
||||
desc = "Allows you to summon faithful stickmen allies to aide you in battle."
|
||||
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
|
||||
button_icon_state = "art_summon"
|
||||
var/ready = TRUE
|
||||
var/list/summoned_stickmen = list()
|
||||
var/summoned_mob_path = /mob/living/simple_animal/hostile/stickman //Must be an hostile animal path.
|
||||
var/max_stickmen = 8
|
||||
var/cooldown = 3 SECONDS
|
||||
var/list/book_of_grudges = list()
|
||||
|
||||
/datum/action/item_action/stickmen/New(Target)
|
||||
..()
|
||||
if(isitem(Target))
|
||||
RegisterSignal(Target, COMSIG_PARENT_EXAMINE, .proc/give_infos)
|
||||
|
||||
/datum/action/item_action/stickmen/Destroy()
|
||||
for(var/A in summoned_stickmen)
|
||||
var/mob/living/simple_animal/hostile/S = A
|
||||
if(S.client)
|
||||
to_chat(S, "<span class='danger'>A dizzying sensation strikes you as the comglomerate of pencil lines you call \
|
||||
your body crumbles under the pressure of an invisible eraser, soon to join bilions discarded sketches. \
|
||||
It seems whatever was keeping you in this realm has come to an end, like all things.</span>")
|
||||
animate(S, alpha = 0, time = 5 SECONDS)
|
||||
QDEL_IN(S, 5 SECONDS)
|
||||
return ..()
|
||||
|
||||
/datum/action/item_action/stickmen/proc/give_infos(atom/source, mob/user, list/examine_list)
|
||||
examine_list += "<span class='notice'>Making sure you are properly wearing or holding it, \
|
||||
point at whatever you want to rally your minions to its position."
|
||||
examine_list += "While on <b>harm</b> intent, pointed mobs (minus you and the minions) \
|
||||
will also be marked as foes for your minions to attack for the next 2 minutes.</span>"
|
||||
|
||||
/datum/action/item_action/stickmen/Grant(mob/M)
|
||||
. = ..()
|
||||
if(owner)
|
||||
RegisterSignal(M, COMSIG_MOB_POINTED, .proc/rally)
|
||||
if(book_of_grudges[M]) //Stop attacking your new master.
|
||||
book_of_grudges -= M
|
||||
for(var/A in summoned_stickmen)
|
||||
var/mob/living/simple_animal/hostile/S = A
|
||||
if(!S.mind)
|
||||
S.LoseTarget()
|
||||
|
||||
|
||||
/obj/item/clothing/suit/wizrobe/paper/verb/stickmen()
|
||||
set category = "Object"
|
||||
set name = "Summon Stick Minions"
|
||||
set src in usr
|
||||
if(!isliving(usr))
|
||||
/datum/action/item_action/stickmen/Remove(mob/M)
|
||||
. = ..()
|
||||
UnregisterSignal(M, COMSIG_MOB_POINTED)
|
||||
|
||||
/datum/action/item_action/stickmen/Trigger()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!robe_charge)
|
||||
to_chat(usr, "<span class='warning'>\The robe's internal magic supply is still recharging!</span>")
|
||||
if(!ready)
|
||||
to_chat(owner, "<span class='warning'>[src]'s internal magic supply is still recharging!</span>")
|
||||
return FALSE
|
||||
var/summon = TRUE
|
||||
if(length(summoned_stickmen) >= max_stickmen)
|
||||
var/mob/living/simple_animal/hostile/S = popleft(summoned_stickmen)
|
||||
if(!S.client)
|
||||
qdel(S)
|
||||
else
|
||||
S.forceMove(owner.drop_location())
|
||||
S.revive(TRUE)
|
||||
summoned_stickmen[S] = TRUE
|
||||
summon = FALSE
|
||||
|
||||
owner.say("Rise, my creation! Off your page into this realm!", forced = "stickman summoning")
|
||||
playsound(owner, 'sound/magic/summon_magic.ogg', 50, 1, 1)
|
||||
if(summon)
|
||||
var/mob/living/simple_animal/hostile/S = new summoned_mob_path (get_turf(usr))
|
||||
S.faction = owner.faction
|
||||
S.foes = book_of_grudges
|
||||
RegisterSignal(S, COMSIG_PARENT_QDELETING, .proc/remove_from_list)
|
||||
ready = FALSE
|
||||
addtimer(CALLBACK(src, .proc/ready_again), cooldown)
|
||||
|
||||
/datum/action/item_action/stickmen/proc/remove_from_list(datum/source, forced)
|
||||
summoned_stickmen -= source
|
||||
|
||||
/datum/action/item_action/stickmen/proc/ready_again()
|
||||
ready = TRUE
|
||||
if(owner)
|
||||
to_chat(owner, "<span class='notice'>[src] hums, its internal magic supply restored.</span>")
|
||||
|
||||
/**
|
||||
* Rallies your army of stickmen to whichever target the user is pointing.
|
||||
* Should the user be on harm intent and the target be a living mob that's not the user or a fellow stickman,
|
||||
* said target will be added to a list of foes which the stickmen will gladly dispose regardless of faction.
|
||||
* This is designed so stickmen will move toward whatever you point at even when you don't want to, that's the downside.
|
||||
*/
|
||||
/datum/action/item_action/stickmen/proc/rally(mob/source, atom/A)
|
||||
var/turf/T = get_turf(A)
|
||||
var/list/surrounding_turfs = block(locate(T.x - 1, T.y - 1, T.z), locate(T.x + 1, T.y + 1, T.z))
|
||||
if(!surrounding_turfs.len)
|
||||
return
|
||||
if(source.a_intent == INTENT_HARM && A != source && !summoned_stickmen[A])
|
||||
var/mob/living/L
|
||||
if(isliving(A)) //Gettem boys!
|
||||
L = A
|
||||
else if(ismecha(A))
|
||||
var/obj/mecha/M = A
|
||||
L = M.occupant
|
||||
if(L && L.stat != DEAD && !HAS_TRAIT(L, TRAIT_DEATHCOMA)) //Taking revenge on the deads would be proposterous.
|
||||
addtimer(CALLBACK(src, .proc/clear_grudge, L), 2 MINUTES, TIMER_OVERRIDE|TIMER_UNIQUE)
|
||||
if(!book_of_grudges[L])
|
||||
RegisterSignal(L, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_DEATH), .proc/grudge_settled)
|
||||
book_of_grudges[L] = TRUE
|
||||
for(var/k in summoned_stickmen) //Shamelessly copied from the blob rally power
|
||||
var/mob/living/simple_animal/hostile/S = k
|
||||
if(!S.mind && isturf(S.loc) && get_dist(S, T) <= 10)
|
||||
S.LoseTarget()
|
||||
S.Goto(pick(surrounding_turfs), S.move_to_delay)
|
||||
|
||||
usr.say("Rise, my creation! Off your page into this realm!", forced = "stickman summoning")
|
||||
playsound(src.loc, 'sound/magic/summon_magic.ogg', 50, 1, 1)
|
||||
var/mob/living/M = new /mob/living/simple_animal/hostile/stickman(get_turf(usr))
|
||||
var/list/factions = usr.faction
|
||||
M.faction = factions
|
||||
src.robe_charge = FALSE
|
||||
sleep(30)
|
||||
src.robe_charge = TRUE
|
||||
to_chat(usr, "<span class='notice'>\The robe hums, its internal magic supply restored.</span>")
|
||||
/datum/action/item_action/stickmen/proc/clear_grudge(mob/living/L)
|
||||
if(!QDELETED(L))
|
||||
book_of_grudges -= L
|
||||
|
||||
/datum/action/item_action/stickmen/proc/grudge_settled(mob/living/L)
|
||||
UnregisterSignal(L, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_DEATH))
|
||||
book_of_grudges -= L
|
||||
|
||||
//Shielded Armour
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
/datum/reagent/pax,
|
||||
/datum/reagent/consumable/laughter,
|
||||
/datum/reagent/concentrated_barbers_aid,
|
||||
/datum/reagent/baldium,
|
||||
/datum/reagent/colorful_reagent,
|
||||
/datum/reagent/peaceborg_confuse,
|
||||
/datum/reagent/peaceborg_tire,
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
name = "modified gravity zone"
|
||||
setup_field_turfs = TRUE
|
||||
var/gravity_value = 0
|
||||
var/list/grav_components = list()
|
||||
var/list/modified_turfs = list()
|
||||
field_shape = FIELD_SHAPE_RADIUS_SQUARE
|
||||
|
||||
/datum/proximity_monitor/advanced/gravity/setup_field_turf(turf/T)
|
||||
. = ..()
|
||||
grav_components[T] = T.AddComponent(/datum/component/forced_gravity,gravity_value)
|
||||
T.AddElement(/datum/element/forced_gravity, gravity_value)
|
||||
modified_turfs[T] = gravity_value
|
||||
|
||||
/datum/proximity_monitor/advanced/gravity/cleanup_field_turf(turf/T)
|
||||
. = ..()
|
||||
var/datum/component/forced_gravity/G = grav_components[T]
|
||||
grav_components -= T
|
||||
if(G)
|
||||
qdel(G)
|
||||
if(isnull(modified_turfs[T]))
|
||||
return
|
||||
T.RemoveElement(/datum/element/forced_gravity, modified_turfs[T])
|
||||
modified_turfs -= T
|
||||
@@ -15,7 +15,7 @@
|
||||
result = /obj/item/reagent_containers/food/snacks/banana_split
|
||||
subcategory = CAT_ICE
|
||||
|
||||
/datum/crafting_recipe/food/bluecharrie_float
|
||||
/datum/crafting_recipe/food/bluecharrie_float
|
||||
name = "Blue Cherry Shake"
|
||||
always_availible = FALSE
|
||||
reqs = list(
|
||||
@@ -276,4 +276,4 @@
|
||||
/datum/reagent/consumable/laughter = 5
|
||||
)
|
||||
result = /obj/item/reagent_containers/food/snacks/snowcones/clown
|
||||
subcategory = CAT_ICE
|
||||
subcategory = CAT_ICE
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/// A deck of unum cards. Classic.
|
||||
/obj/item/toy/cards/deck/unum
|
||||
name = "\improper UNUM deck"
|
||||
desc = "A deck of unum cards. House rules to argue over not included."
|
||||
icon = 'icons/obj/toy.dmi'
|
||||
icon_state = "deck_unum_full"
|
||||
deckstyle = "unum"
|
||||
original_size = 108
|
||||
|
||||
//Populate the deck.
|
||||
/obj/item/toy/cards/deck/unum/populate_deck()
|
||||
for(var/colour in list("Red","Yellow","Green","Blue"))
|
||||
cards += "[colour] 0" //Uno, i mean, cough cough, Unum decks have only one colour of each 0, weird huh?
|
||||
for(var/k in 0 to 1) //two of each colour of number
|
||||
cards += "[colour] skip"
|
||||
cards += "[colour] reverse"
|
||||
cards += "[colour] draw 2"
|
||||
for(var/i in 1 to 9)
|
||||
cards += "[colour] [i]"
|
||||
for(var/k in 0 to 3) //4 wilds and draw 4s
|
||||
cards += "Wildcard"
|
||||
cards += "Draw 4"
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -287,12 +287,12 @@ Works together with spawning an observer, noted above.
|
||||
var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes
|
||||
if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
|
||||
penalty = CANT_REENTER_ROUND
|
||||
if(!(ckey in GLOB.client_ghost_timeouts))
|
||||
GLOB.client_ghost_timeouts += ckey
|
||||
GLOB.client_ghost_timeouts[ckey] = 0
|
||||
else if(GLOB.client_ghost_timeouts[ckey] == CANT_REENTER_ROUND)
|
||||
if(!(ghost.ckey in GLOB.client_ghost_timeouts))
|
||||
GLOB.client_ghost_timeouts += ghost.ckey
|
||||
GLOB.client_ghost_timeouts[ghost.ckey] = 0
|
||||
else if(GLOB.client_ghost_timeouts[ghost.ckey] == CANT_REENTER_ROUND)
|
||||
return
|
||||
GLOB.client_ghost_timeouts[ckey] = max(GLOB.client_ghost_timeouts[ckey],penalty)
|
||||
GLOB.client_ghost_timeouts[ghost.ckey] = max(GLOB.client_ghost_timeouts[ghost.ckey],penalty)
|
||||
// needs to be done AFTER the ckey transfer, too
|
||||
return ghost
|
||||
|
||||
|
||||
@@ -137,11 +137,11 @@
|
||||
if(cached_Bdamage <= HEALTH_THRESHOLD_DEAD) //Fixing dead brains yeilds a trauma
|
||||
if((cached_Bdamage <= HEALTH_THRESHOLD_DEAD) && (brainmob.health > HEALTH_THRESHOLD_DEAD))
|
||||
if(prob(80))
|
||||
gain_trauma_type(BRAIN_TRAUMA_MILD)
|
||||
gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE)
|
||||
else if(prob(50))
|
||||
gain_trauma_type(BRAIN_TRAUMA_SEVERE)
|
||||
gain_trauma_type(BRAIN_TRAUMA_SEVERE, natural_gain = TRUE)
|
||||
else
|
||||
gain_trauma_type(BRAIN_TRAUMA_SPECIAL)
|
||||
gain_trauma_type(BRAIN_TRAUMA_SPECIAL, natural_gain = TRUE)
|
||||
return
|
||||
|
||||
if((organ_flags & ORGAN_FAILING) && O.is_drainable() && O.reagents.has_reagent(/datum/reagent/medicine/mannitol)) //attempt to heal the brain
|
||||
@@ -253,16 +253,16 @@
|
||||
damage_delta = damage - prev_damage
|
||||
if(damage > BRAIN_DAMAGE_MILD)
|
||||
if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% //learn how to do your bloody math properly goddamnit
|
||||
gain_trauma_type(BRAIN_TRAUMA_MILD)
|
||||
gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE)
|
||||
if(prev_damage <= BRAIN_DAMAGE_MILD && owner)
|
||||
var/datum/skill_modifier/S
|
||||
ADD_SKILL_MODIFIER_BODY(/datum/skill_modifier/brain_damage, null, owner, S)
|
||||
if(damage > BRAIN_DAMAGE_SEVERE)
|
||||
if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1%
|
||||
if(prob(20))
|
||||
gain_trauma_type(BRAIN_TRAUMA_SPECIAL)
|
||||
gain_trauma_type(BRAIN_TRAUMA_SPECIAL, natural_gain = TRUE)
|
||||
else
|
||||
gain_trauma_type(BRAIN_TRAUMA_SEVERE)
|
||||
gain_trauma_type(BRAIN_TRAUMA_SEVERE, natural_gain = TRUE)
|
||||
if(prev_damage <= BRAIN_DAMAGE_SEVERE && owner)
|
||||
var/datum/skill_modifier/S
|
||||
ADD_SKILL_MODIFIER_BODY(/datum/skill_modifier/heavy_brain_damage, null, owner, S)
|
||||
@@ -308,7 +308,7 @@
|
||||
if(istype(BT, brain_trauma_type) && (BT.resilience <= resilience))
|
||||
. += BT
|
||||
|
||||
/obj/item/organ/brain/proc/can_gain_trauma(datum/brain_trauma/trauma, resilience)
|
||||
/obj/item/organ/brain/proc/can_gain_trauma(datum/brain_trauma/trauma, resilience, natural_gain = FALSE)
|
||||
if(!ispath(trauma))
|
||||
trauma = trauma.type
|
||||
if(!initial(trauma.can_gain))
|
||||
@@ -341,7 +341,7 @@
|
||||
if(TRAUMA_RESILIENCE_ABSOLUTE)
|
||||
max_traumas = TRAUMA_LIMIT_ABSOLUTE
|
||||
|
||||
if(resilience_tier_count >= max_traumas)
|
||||
if(natural_gain && resilience_tier_count >= max_traumas)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -381,11 +381,11 @@
|
||||
return actual_trauma
|
||||
|
||||
//Add a random trauma of a certain subtype
|
||||
/obj/item/organ/brain/proc/gain_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience)
|
||||
/obj/item/organ/brain/proc/gain_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience, natural_gain = FALSE)
|
||||
var/list/datum/brain_trauma/possible_traumas = list()
|
||||
for(var/T in subtypesof(brain_trauma_type))
|
||||
var/datum/brain_trauma/BT = T
|
||||
if(can_gain_trauma(BT, resilience) && initial(BT.random_gain))
|
||||
if(can_gain_trauma(BT, resilience, natural_gain) && initial(BT.random_gain))
|
||||
possible_traumas += BT
|
||||
|
||||
if(!LAZYLEN(possible_traumas))
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
if(!eye_blind)
|
||||
blind_eyes(1)
|
||||
update_mobility()
|
||||
else
|
||||
if(stat == UNCONSCIOUS)
|
||||
stat = CONSCIOUS
|
||||
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT))
|
||||
set_resting(FALSE, TRUE)
|
||||
else if(stat == UNCONSCIOUS)
|
||||
stat = CONSCIOUS
|
||||
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT))
|
||||
set_resting(FALSE, TRUE)
|
||||
if(eye_blind <= 1)
|
||||
adjust_blindness(-1)
|
||||
update_mobility()
|
||||
update_mobility()
|
||||
update_damage_hud()
|
||||
update_health_hud()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
if(!gibbed)
|
||||
emote("deathgasp")
|
||||
|
||||
disable_intentional_combat_mode(TRUE, FALSE)
|
||||
|
||||
. = ..()
|
||||
|
||||
for(var/T in get_traumas())
|
||||
@@ -62,7 +60,3 @@
|
||||
var/obj/item/bodypart/BP = X
|
||||
BP.drop_limb()
|
||||
BP.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
|
||||
|
||||
/mob/living/carbon/ghostize(can_reenter_corpse = TRUE, special = FALSE, penalize = FALSE, voluntary = FALSE)
|
||||
disable_intentional_combat_mode(TRUE, FALSE)
|
||||
return ..()
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
if(digitalcamo)
|
||||
. += "[t_He] [t_is] moving [t_his] body in an unnatural and blatantly unsimian manner."
|
||||
|
||||
if(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE))
|
||||
. += "[t_He] [t_is] visibly tense[CHECK_MOBILITY(src, MOBILITY_STAND) ? "." : ", and [t_is] standing in combative stance."]"
|
||||
|
||||
var/trait_exam = common_trait_examine()
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
. = ..()
|
||||
if(!CONFIG_GET(flag/disable_human_mood))
|
||||
AddComponent(/datum/component/mood)
|
||||
AddComponent(/datum/component/combat_mode)
|
||||
AddElement(/datum/element/flavor_text/carbon, _name = "Flavor Text", _save_key = "flavor_text")
|
||||
AddElement(/datum/element/flavor_text, "", "Temporary Flavor Text", "This should be used only for things pertaining to the current round!")
|
||||
AddElement(/datum/element/flavor_text, _name = "OOC Notes", _addendum = "Put information on ERP/vore/lewd-related preferences here. THIS SHOULD NOT CONTAIN REGULAR FLAVORTEXT!!", _always_show = TRUE, _save_key = "ooc_notes", _examine_no_preview = TRUE)
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
if(!SSI)
|
||||
SSI = CONFIG_GET_ENTRY(number/movedelay/sprint_speed_increase)
|
||||
. -= SSI.config_entry_value
|
||||
if(wrongdirmovedelay)
|
||||
. += 1
|
||||
if (m_intent == MOVE_INTENT_WALK && HAS_TRAIT(src, TRAIT_SPEEDY_STEP))
|
||||
. -= 1.5
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -523,7 +523,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
|
||||
//this updates all special effects: stun, sleeping, knockdown, druggy, stuttering, etc..
|
||||
/mob/living/carbon/handle_status_effects()
|
||||
..()
|
||||
if(getStaminaLoss() && !(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) //CIT CHANGE - prevents stamina regen while combat mode is active
|
||||
if(getStaminaLoss() && !SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) //CIT CHANGE - prevents stamina regen while combat mode is active
|
||||
adjustStaminaLoss(!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? -7.5 : -6) : -3)//CIT CHANGE - decreases adjuststaminaloss to stop stamina damage from being such a joke
|
||||
|
||||
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && incomingstammult != 1)
|
||||
|
||||
@@ -322,7 +322,7 @@
|
||||
set_pull_offsets(M, state)
|
||||
|
||||
/mob/living/proc/set_pull_offsets(mob/living/M, grab_state = GRAB_PASSIVE)
|
||||
if(M.buckled || M.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
if(M.buckled || SEND_SIGNAL(M, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE))
|
||||
return //don't make them change direction or offset them if they're buckled into something or in combat mode.
|
||||
var/offset = 0
|
||||
switch(grab_state)
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
/mob/living/ComponentInitialize()
|
||||
. = ..()
|
||||
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_COMBAT_MODE_LOCKED), .proc/update_combat_lock)
|
||||
|
||||
/mob/living/proc/update_combat_lock()
|
||||
var/locked = HAS_TRAIT(src, TRAIT_COMBAT_MODE_LOCKED)
|
||||
var/desired = (combat_flags & COMBAT_FLAG_COMBAT_TOGGLED)
|
||||
var/actual = (combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
if(actual)
|
||||
if(locked)
|
||||
disable_combat_mode(FALSE, TRUE, FALSE, FALSE)
|
||||
else if(!desired)
|
||||
disable_combat_mode(TRUE, TRUE, FALSE, FALSE)
|
||||
else
|
||||
if(desired && !locked)
|
||||
enable_combat_mode(FALSE, TRUE, FALSE, FALSE)
|
||||
update_combat_mode_icon()
|
||||
|
||||
/mob/living/proc/disable_combat_mode(silent = TRUE, was_forced = FALSE, visible = FALSE, update_icon = TRUE)
|
||||
if(!(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) || (combat_flags & COMBAT_FLAG_COMBAT_FORCED))
|
||||
return
|
||||
DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
SEND_SIGNAL(src, COMSIG_LIVING_COMBAT_DISABLED, was_forced)
|
||||
if(visible)
|
||||
visible_message("<span class='warning'>[src] goes limp.</span>", "<span class='warning'>Your muscles are forcibly relaxed!</span>")
|
||||
else if(!silent)
|
||||
to_chat(src, was_forced? "<span class='warning'>Your muscles are forcibly relaxed!</span>" : "<span class='warning'>You relax your muscles.</span>")
|
||||
if(update_icon)
|
||||
update_combat_mode_icon()
|
||||
|
||||
/mob/living/proc/enable_combat_mode(silent = TRUE, was_forced = FALSE, visible = FALSE, update_icon = TRUE)
|
||||
if(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
return
|
||||
ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
SEND_SIGNAL(src, COMSIG_LIVING_COMBAT_ENABLED, was_forced)
|
||||
if(visible)
|
||||
visible_message("<span class='warning'>[src] drops into a combative stance!</span>", "<span class='warning'>You drop into a combative stance!</span>")
|
||||
else if(!silent)
|
||||
to_chat(src, was_forced? "<span class='warning'>Your muscles reflexively tighten!</span>" : "<span class='warning'>You tighten your muscles.</span>")
|
||||
if(update_icon)
|
||||
update_combat_mode_icon()
|
||||
|
||||
/// Updates the combat mode HUD icon.
|
||||
/mob/living/proc/update_combat_mode_icon()
|
||||
var/obj/screen/combattoggle/T = locate() in hud_used?.static_inventory
|
||||
T?.update_icon()
|
||||
|
||||
/// Enables intentionally being in combat mode. Please try not to use this proc for feedback whenever possible.
|
||||
/mob/living/proc/enable_intentional_combat_mode(silent = TRUE, visible = FALSE)
|
||||
if((combat_flags & COMBAT_FLAG_COMBAT_TOGGLED) && (combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
return
|
||||
ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_TOGGLED)
|
||||
if(!HAS_TRAIT(src, TRAIT_COMBAT_MODE_LOCKED) && !(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
enable_combat_mode(silent, FALSE, visible, FALSE)
|
||||
update_combat_mode_icon()
|
||||
client?.show_popup_menus = FALSE
|
||||
return TRUE
|
||||
|
||||
/// Disables intentionally being in combat mode. Please try not to use this proc for feedback whenever possible.
|
||||
/mob/living/proc/disable_intentional_combat_mode(silent = TRUE, visible = FALSE)
|
||||
if(!(combat_flags & COMBAT_FLAG_COMBAT_TOGGLED) && !(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
return
|
||||
if(combat_flags & COMBAT_FLAG_COMBAT_FORCED)
|
||||
return
|
||||
DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_COMBAT_TOGGLED)
|
||||
if(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)
|
||||
disable_combat_mode(silent, FALSE, visible, FALSE)
|
||||
update_combat_mode_icon()
|
||||
client?.show_popup_menus = TRUE
|
||||
return TRUE
|
||||
|
||||
/// Toggles whether the user is intentionally in combat mode. THIS should be the proc you generally use! Has built in visual/to other player feedback, as well as an audible cue to ourselves.
|
||||
/mob/living/proc/user_toggle_intentional_combat_mode(visible = TRUE)
|
||||
var/old = (combat_flags & COMBAT_FLAG_COMBAT_TOGGLED)
|
||||
if(old)
|
||||
if(combat_flags & COMBAT_FLAG_COMBAT_FORCED)
|
||||
to_chat(src, "<span class='warning'>You are unable to relax your muscles.</span>")
|
||||
return
|
||||
disable_intentional_combat_mode()
|
||||
playsound_local(src, 'sound/misc/ui_toggleoff.ogg', 50, FALSE, pressure_affected = FALSE) //Slightly modified version of the above!
|
||||
else if(CAN_TOGGLE_COMBAT_MODE(src))
|
||||
enable_intentional_combat_mode()
|
||||
var/current = (combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) //because we could be locked
|
||||
if(current != old) //only sound effect if you succeeded. Could have the feedback system be better but shrug, someone else can do it.
|
||||
if(current)
|
||||
playsound_local(src, 'sound/misc/ui_toggle.ogg', 50, FALSE, pressure_affected = FALSE) //Sound from interbay!
|
||||
if(visible)
|
||||
if(world.time >= combatmessagecooldown)
|
||||
combatmessagecooldown = world.time + 10 SECONDS
|
||||
if(a_intent != INTENT_HELP)
|
||||
visible_message("<span class='warning'>[src] [resting ? "tenses up" : (prob(95)? "drops into a combative stance" : (prob(95)? "poses aggressively" : "asserts dominance with their pose"))].</span>")
|
||||
else
|
||||
visible_message("<span class='notice'>[src] [pick("looks","seems","goes")] [pick("alert","attentive","vigilant")].</span>")
|
||||
@@ -122,7 +122,7 @@
|
||||
|
||||
//// CITADEL STATION COMBAT ////
|
||||
/// See __DEFINES/combat.dm
|
||||
var/combat_flags = COMBAT_FLAGS_STAMSYSTEM_EXEMPT
|
||||
var/combat_flags = COMBAT_FLAGS_SPRINT_EXEMPT
|
||||
/// Next world.time when we will show a visible message on entering combat mode voluntarily again.
|
||||
var/combatmessagecooldown = 0
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
spark_system.attach(src)
|
||||
|
||||
wires = new /datum/wires/robot(src)
|
||||
AddComponent(/datum/component/empprotection, EMP_PROTECT_WIRES)
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_WIRES)
|
||||
|
||||
robot_modules_background = new()
|
||||
robot_modules_background.icon_state = "block"
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
maxHealth = 100
|
||||
health = 100
|
||||
|
||||
combat_flags = COMBAT_FLAGS_DEFAULT
|
||||
|
||||
var/custom_name = ""
|
||||
var/braintype = "Cyborg"
|
||||
var/obj/item/robot_suit/robot_suit = null //Used for deconstruction to remember what the borg was constructed out of..
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
speech_span = SPAN_ROBOT
|
||||
flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1
|
||||
vore_flags = NO_VORE
|
||||
/// Enable sprint system but not stamina
|
||||
combat_flags = COMBAT_FLAGS_STAMEXEMPT_YESSPRINT
|
||||
|
||||
var/datum/ai_laws/laws = null//Now... THEY ALL CAN ALL HAVE LAWS
|
||||
var/last_lawchange_announce = 0
|
||||
|
||||
@@ -500,3 +500,33 @@
|
||||
user.visible_message("[user] milks [src] using \the [O].", "<span class='notice'>You milk [src] using \the [O].</span>")
|
||||
else
|
||||
to_chat(user, "<span class='danger'>The udder is dry. Wait a bit longer...</span>")
|
||||
|
||||
/mob/living/simple_animal/deer
|
||||
name = "doe"
|
||||
desc = "A gentle, peaceful forest animal. How did this get into space?"
|
||||
icon_state = "deer-doe"
|
||||
icon_living = "deer-doe"
|
||||
icon_dead = "deer-doe-dead"
|
||||
gender = FEMALE
|
||||
mob_biotypes = MOB_ORGANIC|MOB_BEAST
|
||||
speak = list("Weeeeeeee?","Weeee","WEOOOOOOOOOO")
|
||||
speak_emote = list("grunts","grunts lowly")
|
||||
emote_hear = list("brays.")
|
||||
emote_see = list("shakes its head.")
|
||||
speak_chance = 1
|
||||
turns_per_move = 5
|
||||
see_in_dark = 6
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 3)
|
||||
response_help_continuous = "pets"
|
||||
response_help_simple = "pet"
|
||||
response_disarm_continuous = "gently nudges"
|
||||
response_disarm_simple = "gently nudge"
|
||||
response_harm_continuous = "kicks"
|
||||
response_harm_simple = "kick"
|
||||
attack_verb_continuous = "bucks"
|
||||
attack_verb_simple = "buck"
|
||||
attack_sound = 'sound/weapons/punch1.ogg'
|
||||
health = 75
|
||||
maxHealth = 75
|
||||
blood_volume = BLOOD_VOLUME_NORMAL
|
||||
footstep_type = FOOTSTEP_MOB_SHOE
|
||||
@@ -27,7 +27,7 @@
|
||||
glide_size = 2
|
||||
footstep_type = FOOTSTEP_MOB_CLAW
|
||||
|
||||
/mob/living/simple_animal/pet/fox/ComponentInitialize()
|
||||
/mob/living/simple_animal/pet/sloth/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/mob_holder, "sloth") //finally oranges can be held
|
||||
|
||||
|
||||
@@ -23,29 +23,21 @@
|
||||
for(var/ab in boss_abilities)
|
||||
boss_abilities -= ab
|
||||
var/datum/action/boss/AB = new ab()
|
||||
AB.boss = src
|
||||
AB.Grant(src)
|
||||
boss_abilities += AB
|
||||
|
||||
atb.assign_abilities(boss_abilities)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/boss/Destroy()
|
||||
qdel(atb)
|
||||
atb = null
|
||||
for(var/ab in boss_abilities)
|
||||
var/datum/action/boss/AB = ab
|
||||
AB.boss = null
|
||||
AB.Remove(src)
|
||||
qdel(AB)
|
||||
boss_abilities.Cut()
|
||||
QDEL_NULL(atb)
|
||||
QDEL_LIST(boss_abilities)
|
||||
return ..()
|
||||
|
||||
|
||||
//Action datum for bosses
|
||||
//Override Trigger() as shown below to do things
|
||||
/datum/action/boss
|
||||
check_flags = AB_CHECK_CONSCIOUS //Incase the boss is given a player
|
||||
required_mobility_flags = NONE
|
||||
var/boss_cost = 100 //Cost of usage for the boss' AI 1-100
|
||||
var/usage_probability = 100
|
||||
var/mob/living/simple_animal/hostile/boss/boss
|
||||
@@ -53,23 +45,34 @@
|
||||
var/needs_target = TRUE //Does the boss need to have a target? (Only matters for the AI)
|
||||
var/say_when_triggered = "" //What does the boss Say() when the ability triggers?
|
||||
|
||||
/datum/action/boss/Destroy()
|
||||
boss = null
|
||||
return ..()
|
||||
|
||||
/datum/action/boss/Grant(mob/M)
|
||||
. = ..()
|
||||
boss = owner
|
||||
|
||||
/datum/action/boss/Remove(mob/M)
|
||||
. = ..()
|
||||
boss = null
|
||||
|
||||
/datum/action/boss/Trigger()
|
||||
. = ..()
|
||||
if(.)
|
||||
if(!istype(boss, boss_type))
|
||||
return 0
|
||||
if(!boss.atb)
|
||||
return 0
|
||||
if(boss.atb.points < boss_cost)
|
||||
return 0
|
||||
if(!boss.client)
|
||||
if(needs_target && !boss.target)
|
||||
return 0
|
||||
if(boss)
|
||||
if(say_when_triggered)
|
||||
boss.say(say_when_triggered, forced = "boss action")
|
||||
if(!boss.atb.spend(boss_cost))
|
||||
return 0
|
||||
if(!.)
|
||||
return
|
||||
if(!istype(boss, boss_type))
|
||||
return FALSE
|
||||
if(!boss.atb)
|
||||
return FALSE
|
||||
if(boss.atb.points < boss_cost)
|
||||
return FALSE
|
||||
if(!boss.client && needs_target && !boss.target)
|
||||
return FALSE
|
||||
if(!boss.atb.spend(boss_cost))
|
||||
return FALSE
|
||||
if(say_when_triggered)
|
||||
boss.say(say_when_triggered, forced = "boss action")
|
||||
|
||||
//Example:
|
||||
/*
|
||||
@@ -83,7 +86,8 @@
|
||||
/datum/boss_active_timed_battle
|
||||
var/list/abilities //a list of /datum/action/boss owned by a boss mob
|
||||
var/point_regen_delay = 5
|
||||
var/points = 50 //1-100, start with 50 so we can use some abilities but not insta-buttfug somebody
|
||||
var/max_points = 100
|
||||
var/points = 50 //start with 50 so we can use some abilities but not insta-buttfug somebody
|
||||
var/next_point_time = 0
|
||||
var/chance_to_hold_onto_points = 50
|
||||
var/highest_cost = 0
|
||||
@@ -108,22 +112,22 @@
|
||||
/datum/boss_active_timed_battle/proc/spend(cost)
|
||||
if(cost <= points)
|
||||
points = max(0,points-cost)
|
||||
return 1
|
||||
return 0
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
/datum/boss_active_timed_battle/proc/refund(cost)
|
||||
points = min(points+cost, 100)
|
||||
points = min(points+cost, max_points)
|
||||
|
||||
|
||||
/datum/boss_active_timed_battle/process()
|
||||
if(world.time >= next_point_time)
|
||||
if(world.time >= next_point_time && points < max_points)
|
||||
next_point_time = world.time + point_regen_delay
|
||||
points = min(100, ++points) //has to be out of 100
|
||||
points = min(max_points, ++points) //has to be out of 100
|
||||
|
||||
if(abilities)
|
||||
chance_to_hold_onto_points = highest_cost*0.5
|
||||
if(points != 100 && prob(chance_to_hold_onto_points))
|
||||
if(points != max_points && prob(chance_to_hold_onto_points))
|
||||
return //Let's save our points for a better ability (unless we're at max points, in which case we can't save anymore!)
|
||||
if(!boss.client)
|
||||
abilities = shuffle(abilities)
|
||||
@@ -135,5 +139,5 @@
|
||||
|
||||
/datum/boss_active_timed_battle/Destroy()
|
||||
abilities = null
|
||||
SSobj.processing.Remove(src)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
@@ -32,25 +32,47 @@
|
||||
name = "Summon Minions"
|
||||
icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
|
||||
button_icon_state = "art_summon"
|
||||
usage_probability = 40
|
||||
usage_probability = 20
|
||||
boss_cost = 30
|
||||
boss_type = /mob/living/simple_animal/hostile/boss/paper_wizard
|
||||
needs_target = FALSE
|
||||
say_when_triggered = "Rise, my creations! Jump off your pages and into this realm!"
|
||||
var/static/summoned_minions = 0
|
||||
var/list/summoned_minions = list()
|
||||
var/maximum_stickmen = 6
|
||||
var/stickmen_to_summon = 3
|
||||
|
||||
/datum/action/boss/wizard_summon_minions/Trigger()
|
||||
if(summoned_minions <= 6 && ..())
|
||||
var/list/minions = list(
|
||||
/mob/living/simple_animal/hostile/stickman,
|
||||
/mob/living/simple_animal/hostile/stickman/ranged,
|
||||
/mob/living/simple_animal/hostile/stickman/dog)
|
||||
var/list/directions = GLOB.cardinals.Copy()
|
||||
for(var/i in 1 to 3)
|
||||
var/minions_chosen = pick_n_take(minions)
|
||||
new minions_chosen (get_step(boss,pick_n_take(directions)), 1)
|
||||
summoned_minions += 3;
|
||||
. =..()
|
||||
if(!.)
|
||||
return
|
||||
var/to_summon = stickmen_to_summon
|
||||
var/current_len = length(summoned_minions)
|
||||
if(current_len > maximum_stickmen - stickmen_to_summon)
|
||||
for(var/a in (maximum_stickmen - stickmen_to_summon) to current_len)
|
||||
var/mob/living/simple_animal/hostile/stickman/S = popleft(summoned_minions)
|
||||
if(!S.client)
|
||||
qdel(S)
|
||||
else
|
||||
S.forceMove(boss.drop_location())
|
||||
S.revive(TRUE)
|
||||
summoned_minions += S
|
||||
to_summon--
|
||||
|
||||
var/static/list/minions = list(
|
||||
/mob/living/simple_animal/hostile/stickman,
|
||||
/mob/living/simple_animal/hostile/stickman/ranged,
|
||||
/mob/living/simple_animal/hostile/stickman/dog)
|
||||
|
||||
var/list/directions = GLOB.cardinals.Copy()
|
||||
for(var/i in 1 to to_summon)
|
||||
var/minions_chosen = pick(minions)
|
||||
var/mob/living/simple_animal/hostile/stickman/S = new minions_chosen (get_step(boss,pick_n_take(directions)), 1)
|
||||
S.faction = boss.faction
|
||||
RegisterSignal(S, COMSIG_PARENT_QDELETING, .proc/remove_from_list)
|
||||
summoned_minions += S
|
||||
|
||||
/datum/action/boss/wizard_summon_minions/proc/remove_from_list(datum/source, forced)
|
||||
summoned_minions -= source
|
||||
|
||||
//Mimic Ability
|
||||
//Summons mimics of himself with magical papercraft
|
||||
@@ -66,28 +88,32 @@
|
||||
say_when_triggered = ""
|
||||
|
||||
/datum/action/boss/wizard_mimic/Trigger()
|
||||
if(..())
|
||||
var/mob/living/target
|
||||
if(!boss.client) //AI's target
|
||||
target = boss.target
|
||||
else //random mob
|
||||
var/list/threats = boss.PossibleThreats()
|
||||
if(threats.len)
|
||||
target = pick(threats)
|
||||
if(target)
|
||||
var/mob/living/simple_animal/hostile/boss/paper_wizard/wiz = boss
|
||||
var/directions = GLOB.cardinals.Copy()
|
||||
for(var/i in 1 to 3)
|
||||
var/mob/living/simple_animal/hostile/boss/paper_wizard/copy/C = new (get_step(target,pick_n_take(directions)))
|
||||
wiz.copies += C
|
||||
C.original = wiz
|
||||
C.say("My craft defines me, you could even say it IS me!")
|
||||
wiz.say("My craft defines me, you could even say it IS me!")
|
||||
wiz.forceMove(get_step(target,pick_n_take(directions)))
|
||||
wiz.minimum_distance = 1 //so he doesn't run away and ruin everything
|
||||
wiz.retreat_distance = 0
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/mob/living/target
|
||||
if(!boss.client) //AI's target
|
||||
target = boss.target
|
||||
else //random mob
|
||||
var/list/threats = boss.PossibleThreats()
|
||||
if(threats.len)
|
||||
target = pick(threats)
|
||||
else
|
||||
boss.atb.refund(boss_cost)
|
||||
to_chat(owner, "<span class='warning'>There is no potential foe of different faction around to attack</span>")
|
||||
if(target)
|
||||
var/mob/living/simple_animal/hostile/boss/paper_wizard/wiz = boss
|
||||
var/directions = GLOB.cardinals.Copy()
|
||||
for(var/i in 1 to 3)
|
||||
var/mob/living/simple_animal/hostile/boss/paper_wizard/copy/C = new (get_step(target,pick_n_take(directions)))
|
||||
wiz.copies += C
|
||||
C.original = wiz
|
||||
C.say("My craft defines me, you could even say it IS me!")
|
||||
wiz.say("My craft defines me, you could even say it IS me!")
|
||||
wiz.forceMove(get_step(target,pick_n_take(directions)))
|
||||
wiz.minimum_distance = 1 //so he doesn't run away and ruin everything
|
||||
wiz.retreat_distance = 0
|
||||
else
|
||||
boss.atb.refund(boss_cost)
|
||||
|
||||
/mob/living/simple_animal/hostile/boss/paper_wizard/copy
|
||||
desc = "'Tis a ruse!"
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
var/casingtype //set ONLY it and NULLIFY projectiletype, if we have projectile IN CASING
|
||||
var/move_to_delay = 3 //delay for the automated movement.
|
||||
var/list/friends = list()
|
||||
var/list/foes = list()
|
||||
var/list/emote_taunt = list()
|
||||
var/taunt_chance = 0
|
||||
|
||||
@@ -62,6 +63,8 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/Destroy()
|
||||
targets_from = null
|
||||
friends = null
|
||||
foes = null
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/Life()
|
||||
@@ -193,7 +196,7 @@
|
||||
|
||||
// Please do not add one-off mob AIs here, but override this function for your mob
|
||||
/mob/living/simple_animal/hostile/CanAttack(atom/the_target)//Can we actually attack a possible target?
|
||||
if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids
|
||||
if(!the_target || the_target.type == /atom/movable/lighting_object || isturf(the_target)) // bail out on invalids
|
||||
return FALSE
|
||||
|
||||
if(ismob(the_target)) //Target is in godmode, ignore it.
|
||||
@@ -208,13 +211,13 @@
|
||||
if(search_objects < 2)
|
||||
if(isliving(the_target))
|
||||
var/mob/living/L = the_target
|
||||
var/faction_check = faction_check_mob(L)
|
||||
var/faction_check = !foes[L] && faction_check_mob(L)
|
||||
if(robust_searching)
|
||||
if(faction_check && !attack_same)
|
||||
return FALSE
|
||||
if(L.stat > stat_attack)
|
||||
if(L.stat > stat_attack || (L.stat == UNCONSCIOUS && stat_attack == UNCONSCIOUS && HAS_TRAIT(L, TRAIT_DEATHCOMA)))
|
||||
return FALSE
|
||||
if(L in friends)
|
||||
if(friends[L] > 0 && foes[L] < 1)
|
||||
return FALSE
|
||||
else
|
||||
if((faction_check && !attack_same) || L.stat)
|
||||
|
||||
@@ -75,7 +75,7 @@ Difficulty: Medium
|
||||
|
||||
/obj/item/projectile/kinetic/miner
|
||||
damage = 40
|
||||
speed = 0.9
|
||||
pixels_per_second = TILES_TO_PIXELS(11.111)
|
||||
icon_state = "ka_tracer"
|
||||
range = MINER_DASH_RANGE
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ Difficulty: Very Hard
|
||||
icon_state= "chronobolt"
|
||||
damage = 25
|
||||
armour_penetration = 100
|
||||
speed = 2
|
||||
pixels_per_second = TILES_TO_PIXELS(5)
|
||||
eyeblur = 0
|
||||
damage_type = BRUTE
|
||||
pass_flags = PASSTABLE
|
||||
|
||||
@@ -71,7 +71,10 @@ Difficulty: Extremely Hard
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/OpenFire()
|
||||
check_enraged()
|
||||
projectile_speed_multiplier = 1 - enraged * 0.25
|
||||
if(enraged)
|
||||
projectile_speed_multiplier = 1.33
|
||||
else
|
||||
projectile_speed_multiplier = 1
|
||||
SetRecoveryTime(100, 100)
|
||||
|
||||
if(client)
|
||||
@@ -109,7 +112,7 @@ Difficulty: Extremely Hard
|
||||
icon_state = "ice_1"
|
||||
damage = 20
|
||||
armour_penetration = 100
|
||||
speed = 10
|
||||
pixels_per_second = TILES_TO_PIXELS(1)
|
||||
homing_turn_speed = 30
|
||||
damage_type = BURN
|
||||
|
||||
@@ -123,7 +126,7 @@ Difficulty: Extremely Hard
|
||||
icon_state = "nuclear_particle"
|
||||
damage = 5
|
||||
armour_penetration = 100
|
||||
speed = 4
|
||||
pixels_per_second = TILES_TO_PIXELS(2.5)
|
||||
damage_type = BRUTE
|
||||
|
||||
/obj/item/projectile/ice_blast
|
||||
@@ -131,7 +134,7 @@ Difficulty: Extremely Hard
|
||||
icon_state = "ice_2"
|
||||
damage = 15
|
||||
armour_penetration = 100
|
||||
speed = 4
|
||||
pixels_per_second = TILES_TO_PIXELS(2.5)
|
||||
damage_type = BRUTE
|
||||
|
||||
/obj/item/projectile/ice_blast/on_hit(atom/target, blocked = FALSE)
|
||||
@@ -185,7 +188,7 @@ Difficulty: Extremely Hard
|
||||
if(!startloc || !endloc)
|
||||
break
|
||||
var/obj/item/projectile/ice_blast/P = new(startloc)
|
||||
P.speed *= projectile_speed_multiplier
|
||||
P.pixels_per_second *= projectile_speed_multiplier
|
||||
P.preparePixelProjectile(endloc, startloc, null, angle + rand(-10, 10))
|
||||
P.firer = firer
|
||||
if(original)
|
||||
@@ -201,7 +204,7 @@ Difficulty: Extremely Hard
|
||||
if(!endloc)
|
||||
break
|
||||
var/obj/item/projectile/P = new /obj/item/projectile/snowball(startloc)
|
||||
P.speed *= projectile_speed_multiplier
|
||||
P.pixels_per_second *= projectile_speed_multiplier
|
||||
P.preparePixelProjectile(endloc, startloc, null, rand(-spread, spread))
|
||||
P.firer = src
|
||||
if(target)
|
||||
@@ -220,7 +223,7 @@ Difficulty: Extremely Hard
|
||||
if(!endloc)
|
||||
break
|
||||
var/obj/item/projectile/P = new /obj/item/projectile/ice_blast(startloc)
|
||||
P.speed *= projectile_speed_multiplier
|
||||
P.pixels_per_second *= projectile_speed_multiplier
|
||||
P.preparePixelProjectile(endloc, startloc, null, spread)
|
||||
P.firer = src
|
||||
if(target)
|
||||
|
||||
@@ -145,6 +145,7 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/proc/SetRecoveryTime(buffer_time)
|
||||
recovery_time = world.time + buffer_time
|
||||
ranged_cooldown = max(ranged_cooldown, world.time + buffer_time) // CITADEL BANDAID FIX FOR MEGAFAUNA NOT RESPECTING RECOVERY TIME.
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill)
|
||||
if(!medal_type || (flags_1 & ADMIN_SPAWNED_1)) //Don't award medals if the medal type isn't set
|
||||
|
||||
@@ -84,8 +84,33 @@
|
||||
crusher_loot = /obj/item/crusher_trophy/watcher_wing
|
||||
loot = list()
|
||||
butcher_results = list(/obj/item/stack/ore/diamond = 2, /obj/item/stack/sheet/sinew = 2, /obj/item/stack/sheet/bone = 1)
|
||||
search_objects = 1
|
||||
wanted_objects = list(/obj/item/pen/survival, /obj/item/stack/ore/diamond)
|
||||
field_of_vision_type = FOV_270_DEGREES //Obviously, it's one eyeball.
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/Life()
|
||||
. = ..()
|
||||
if(stat == CONSCIOUS)
|
||||
consume_bait()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/proc/consume_bait()
|
||||
var/obj/item/stack/ore/diamond/diamonds = locate(/obj/item/stack/ore/diamond) in oview(src, 9)
|
||||
var/obj/item/pen/survival/bait = locate(/obj/item/pen/survival) in oview(src, 9)
|
||||
if(!diamonds && !bait)
|
||||
return
|
||||
if(diamonds)
|
||||
var/distanced = 0
|
||||
distanced = get_dist(loc,diamonds.loc)
|
||||
if(distanced <= 1 && diamonds)
|
||||
qdel(diamonds)
|
||||
src.visible_message("<span class='notice'>[src] consumes [diamonds], and it disappears! ...At least, you think.</span>")
|
||||
if(bait)
|
||||
var/distanceb = 0
|
||||
distanceb = get_dist(loc,bait.loc)
|
||||
if(distanceb <= 1 && bait)
|
||||
qdel(bait)
|
||||
visible_message("<span class='notice'>[src] examines [bait] closer, and telekinetically shatters the pen.</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random/Initialize()
|
||||
. = ..()
|
||||
if(prob(1))
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
icon_state= "chronobolt"
|
||||
damage = 15
|
||||
armour_penetration = 60
|
||||
speed = 2
|
||||
pixels_per_second = TILES_TO_PIXELS(5)
|
||||
eyeblur = 0
|
||||
damage_type = BRUTE
|
||||
pass_flags = PASSTABLE
|
||||
|
||||
@@ -298,5 +298,5 @@
|
||||
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion(user.loc)
|
||||
A.flags_1 |= (flags_1 & ADMIN_SPAWNED_1)
|
||||
A.GiveTarget(target)
|
||||
A.friends = user
|
||||
A.friends[user]++
|
||||
A.faction = user.faction.Copy()
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
speed = 0
|
||||
blood_volume = 0
|
||||
stat_attack = UNCONSCIOUS
|
||||
robust_searching = 1
|
||||
robust_searching = TRUE //This is also required for the paper robe rallying to work.
|
||||
environment_smash = ENVIRONMENT_SMASH_NONE
|
||||
maxHealth = 100
|
||||
health = 100
|
||||
@@ -30,8 +30,6 @@
|
||||
faction = list("hostile","stickman")
|
||||
check_friendly_fire = 1
|
||||
status_flags = CANPUSH
|
||||
var/datum/action/boss/wizard_summon_minions/changesummons = /datum/action/boss/wizard_summon_minions
|
||||
var/summoned_by_wizard = 0
|
||||
|
||||
/mob/living/simple_animal/hostile/stickman/ranged
|
||||
ranged = 1
|
||||
@@ -43,7 +41,6 @@
|
||||
projectilesound = 'sound/misc/bang.ogg'
|
||||
loot = list(/obj/item/gun/ballistic/automatic/pistol/stickman)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/stickman/dog
|
||||
name = "Angry Stick Dog"
|
||||
desc = "Stickman's best friend, if he could see him at least."
|
||||
@@ -52,12 +49,6 @@
|
||||
icon_dead = "stickdog_dead"
|
||||
mob_biotypes = MOB_BEAST
|
||||
|
||||
/mob/living/simple_animal/hostile/stickman/Initialize(mapload, var/wizard_summoned)
|
||||
/mob/living/simple_animal/hostile/stickman/Initialize(mapload)
|
||||
. = ..()
|
||||
new /obj/effect/temp_visual/paper_scatter(src)
|
||||
summoned_by_wizard = wizard_summoned
|
||||
|
||||
/mob/living/simple_animal/hostile/stickman/death()
|
||||
..()
|
||||
if(summoned_by_wizard == 1)
|
||||
changesummons.summoned_minions --
|
||||
|
||||
@@ -571,12 +571,13 @@
|
||||
/mob/living/proc/cure_blind(source)
|
||||
REMOVE_TRAIT(src, TRAIT_BLIND, source)
|
||||
if(!HAS_TRAIT(src, TRAIT_BLIND))
|
||||
update_blindness()
|
||||
if(eye_blind <= 1) //little hack now that we don't actively check for trait and unconsciousness on update_blindness.
|
||||
adjust_blindness(-1)
|
||||
|
||||
/mob/living/proc/become_blind(source)
|
||||
if(!HAS_TRAIT(src, TRAIT_BLIND)) // not blind already, add trait then overlay
|
||||
ADD_TRAIT(src, TRAIT_BLIND, source)
|
||||
update_blindness()
|
||||
blind_eyes(1)
|
||||
else
|
||||
ADD_TRAIT(src, TRAIT_BLIND, source)
|
||||
|
||||
|
||||
@@ -128,15 +128,13 @@
|
||||
* * target (optional) is the other mob involved with the visible message. For example, the attacker in many combat messages.
|
||||
* * target_message (optional) is what the target mob will see e.g. "[src] does something to you!"
|
||||
*/
|
||||
/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, mob/target, target_message)
|
||||
/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, ignored_mobs, mob/target, target_message)
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
return
|
||||
var/list/hearers = get_hearers_in_view(vision_distance, src) //caches the hearers and then removes ignored mobs.
|
||||
if(!length(hearers))
|
||||
return
|
||||
if(!islist(ignored_mobs))
|
||||
ignored_mobs = list(ignored_mobs)
|
||||
hearers -= ignored_mobs
|
||||
|
||||
if(target_message && target && istype(target) && target.client)
|
||||
@@ -150,7 +148,7 @@
|
||||
msg = blind_message
|
||||
if(msg)
|
||||
target.show_message(msg, MSG_VISUAL,blind_message, MSG_AUDIBLE)
|
||||
else if(self_message)
|
||||
if(self_message)
|
||||
hearers -= src
|
||||
for(var/mob/M in hearers)
|
||||
if(!M.client)
|
||||
@@ -185,15 +183,13 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA
|
||||
* * hearing_distance (optional) is the range, how many tiles away the message can be heard.
|
||||
* * ignored_mobs (optional) doesn't show any message to any given mob in the list.
|
||||
*/
|
||||
/atom/proc/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, list/ignored_mobs)
|
||||
/atom/proc/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, ignored_mobs)
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
return
|
||||
var/list/hearers = get_hearers_in_view(hearing_distance, src)
|
||||
if(!length(hearers))
|
||||
return
|
||||
if(!islist(ignored_mobs))
|
||||
ignored_mobs = list(ignored_mobs)
|
||||
hearers -= ignored_mobs
|
||||
if(self_message)
|
||||
hearers -= src
|
||||
@@ -377,7 +373,7 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA
|
||||
return FALSE
|
||||
|
||||
new /obj/effect/temp_visual/point(A,invisibility)
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_MOB_POINTED, A)
|
||||
return TRUE
|
||||
|
||||
/mob/proc/can_resist()
|
||||
|
||||
@@ -127,6 +127,13 @@
|
||||
|
||||
var/registered_z
|
||||
|
||||
var/list/alerts = list() // contains /obj/screen/alert only // On /mob so clientless mobs will throw alerts properly
|
||||
var/list/screens = list()
|
||||
var/list/client_colours = list()
|
||||
var/hud_type = /datum/hud
|
||||
|
||||
var/datum/hSB/sandbox = null
|
||||
|
||||
var/mob/audiovisual_redirect //Mob to redirect messages, speech, and sounds to
|
||||
|
||||
var/siliconaccessareas = list()
|
||||
|
||||
@@ -101,10 +101,10 @@
|
||||
mob.throwing.finalize(FALSE)
|
||||
|
||||
var/atom/movable/AM = L.pulling
|
||||
if(AM && AM.density && !(L.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) && !ismob(AM))
|
||||
if(AM && AM.density && !SEND_SIGNAL(L, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE) && !ismob(AM))
|
||||
L.setDir(turn(L.dir, 180))
|
||||
|
||||
SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOVE, src, direction, n, oldloc)
|
||||
SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOVE, src, direction, n, oldloc, add_delay)
|
||||
|
||||
/// Process_Grab(): checks for grab, attempts to break if so. Return TRUE to prevent movement.
|
||||
/client/proc/Process_Grab()
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
* Sets a mob's blindness to an amount if it was not above it already, similar to how status effects work
|
||||
*/
|
||||
/mob/proc/blind_eyes(amount)
|
||||
var/old_blind = eye_blind || HAS_TRAIT(src, TRAIT_BLIND)
|
||||
eye_blind = max(eye_blind, amount)
|
||||
var/new_blind = eye_blind || HAS_TRAIT(src, TRAIT_BLIND)
|
||||
var/old_blind = eye_blind
|
||||
eye_blind = max((!eye_blind && stat == UNCONSCIOUS || HAS_TRAIT(src, TRAIT_BLIND)) ? 1 : eye_blind , amount)
|
||||
var/new_blind = eye_blind
|
||||
if(old_blind != new_blind)
|
||||
update_blindness()
|
||||
|
||||
@@ -36,21 +36,21 @@
|
||||
*/
|
||||
/mob/proc/adjust_blindness(amount)
|
||||
var/old_eye_blind = eye_blind
|
||||
eye_blind = max(0, eye_blind + amount)
|
||||
if(!old_eye_blind || !eye_blind && !HAS_TRAIT(src, TRAIT_BLIND))
|
||||
eye_blind = max((stat == UNCONSCIOUS || HAS_TRAIT(src, TRAIT_BLIND)) ? 1 : 0, eye_blind + amount)
|
||||
if(!old_eye_blind || !eye_blind)
|
||||
update_blindness()
|
||||
/**
|
||||
* Force set the blindness of a mob to some level
|
||||
*/
|
||||
/mob/proc/set_blindness(amount)
|
||||
var/old_eye_blind = eye_blind
|
||||
eye_blind = max(amount, 0)
|
||||
if(!old_eye_blind || !eye_blind && !HAS_TRAIT(src, TRAIT_BLIND))
|
||||
eye_blind = max(amount, (stat == UNCONSCIOUS || HAS_TRAIT(src, TRAIT_BLIND)) ? 1 : 0)
|
||||
if(!old_eye_blind || !eye_blind)
|
||||
update_blindness()
|
||||
|
||||
/// proc that adds and removes blindness overlays when necessary
|
||||
/mob/proc/update_blindness()
|
||||
if(stat == UNCONSCIOUS || HAS_TRAIT(src, TRAIT_BLIND) || eye_blind) // UNCONSCIOUS or has blind trait, or has temporary blindness
|
||||
if(eye_blind) // UNCONSCIOUS or has blind trait, or has temporary blindness
|
||||
if(stat == CONSCIOUS || stat == SOFT_CRIT)
|
||||
throw_alert("blind", /obj/screen/alert/blind)
|
||||
overlay_fullscreen("blind", /obj/screen/fullscreen/blind)
|
||||
|
||||
@@ -384,3 +384,6 @@
|
||||
|
||||
/obj/item/paper/crumpled/bloody
|
||||
icon_state = "scrap_bloodied"
|
||||
|
||||
/obj/item/paper/crumpled/muddy
|
||||
icon_state = "scrap_mud"
|
||||
|
||||
@@ -220,3 +220,17 @@
|
||||
item_state = initial(item_state)
|
||||
lefthand_file = initial(lefthand_file)
|
||||
righthand_file = initial(righthand_file)
|
||||
|
||||
/obj/item/pen/survival
|
||||
name = "survival pen"
|
||||
desc = "The latest in portable survival technology, this pen was designed as a miniature diamond pickaxe. Watchers find them very desirable for their diamond exterior."
|
||||
icon = 'icons/obj/bureaucracy.dmi'
|
||||
icon_state = "digging_pen"
|
||||
item_state = "pen"
|
||||
force = 3
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
custom_materials = list(/datum/material/iron=10, /datum/material/diamond=100, /datum/material/titanium = 10)
|
||||
pressure_resistance = 2
|
||||
grind_results = list(/datum/reagent/iron = 2, /datum/reagent/iodine = 1)
|
||||
tool_behaviour = TOOL_MINING //For the classic "digging out of prison with a spoon but you're in space so this analogy doesn't work" situation.
|
||||
toolspeed = 10 //You will never willingly choose to use one of these over a shovel.
|
||||
|
||||
@@ -337,3 +337,15 @@
|
||||
grind_results = list(/datum/reagent/iodine = 40, /datum/reagent/iron = 10)
|
||||
var/charges = 5
|
||||
var/max_charges = 5
|
||||
|
||||
/obj/item/toner/large
|
||||
name = "large toner cartridge"
|
||||
grind_results = list(/datum/reagent/iodine = 90, /datum/reagent/iron = 10)
|
||||
charges = 15
|
||||
max_charges = 15
|
||||
|
||||
/obj/item/toner/extreme
|
||||
name = "extremely large toner cartridge"
|
||||
desc = "Why would ANYONE need THIS MUCH TONER?"
|
||||
charges = 200
|
||||
max_charges = 200
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
name = "picture frame"
|
||||
desc = "The perfect showcase for your favorite deathtrap memories."
|
||||
icon = 'icons/obj/decals.dmi'
|
||||
custom_materials = null
|
||||
custom_materials = list(/datum/material/wood = 2000)
|
||||
flags_1 = 0
|
||||
icon_state = "frame-empty"
|
||||
result_path = /obj/structure/sign/picture_frame
|
||||
@@ -64,6 +64,7 @@
|
||||
desc = "Every time you look it makes you laugh."
|
||||
icon = 'icons/obj/decals.dmi'
|
||||
icon_state = "frame-empty"
|
||||
custom_materials = list(/datum/material/wood = 2000)
|
||||
var/obj/item/photo/framed
|
||||
var/persistence_id
|
||||
var/can_decon = TRUE
|
||||
|
||||
@@ -1411,6 +1411,7 @@
|
||||
if(WIRE_POWER1, WIRE_POWER2)
|
||||
if(!wires.is_cut(WIRE_POWER1) && !wires.is_cut(WIRE_POWER2))
|
||||
shorted = FALSE
|
||||
update()
|
||||
if(WIRE_AI)
|
||||
if(!wires.is_cut(WIRE_AI))
|
||||
aidisabled = FALSE
|
||||
|
||||
@@ -227,6 +227,7 @@
|
||||
icon_state = "h+cell"
|
||||
maxcharge = 15000
|
||||
chargerate = 2250
|
||||
rating = 2
|
||||
|
||||
/obj/item/stock_parts/cell/high/empty
|
||||
start_charged = FALSE
|
||||
@@ -237,6 +238,7 @@
|
||||
maxcharge = 20000
|
||||
custom_materials = list(/datum/material/glass=300)
|
||||
chargerate = 2000
|
||||
rating = 3
|
||||
|
||||
/obj/item/stock_parts/cell/super/empty
|
||||
start_charged = FALSE
|
||||
@@ -247,6 +249,7 @@
|
||||
maxcharge = 30000
|
||||
custom_materials = list(/datum/material/glass=400)
|
||||
chargerate = 3000
|
||||
rating = 4
|
||||
|
||||
/obj/item/stock_parts/cell/hyper/empty
|
||||
start_charged = FALSE
|
||||
@@ -258,6 +261,7 @@
|
||||
maxcharge = 40000
|
||||
custom_materials = list(/datum/material/glass=600)
|
||||
chargerate = 4000
|
||||
rating = 5
|
||||
|
||||
/obj/item/stock_parts/cell/bluespace/empty
|
||||
start_charged = FALSE
|
||||
@@ -313,7 +317,7 @@
|
||||
|
||||
/obj/item/stock_parts/cell/emproof/ComponentInitialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF)
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_SELF)
|
||||
|
||||
/obj/item/stock_parts/cell/emproof/empty
|
||||
start_charged = FALSE
|
||||
|
||||
@@ -314,8 +314,11 @@
|
||||
. = ..()
|
||||
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
|
||||
if(on && status == LIGHT_OK)
|
||||
SSvis_overlays.add_vis_overlay(src, overlayicon, base_state, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE, dir, clamp(light_power*250, 30, 200))
|
||||
|
||||
var/overlay_alpha = clamp(light_power*250, 30, 200)
|
||||
SSvis_overlays.add_vis_overlay(src, overlayicon, base_state, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE, dir, overlay_alpha)
|
||||
var/mutable_appearance/M = mutable_appearance(overlayicon, base_state)
|
||||
M.alpha = overlay_alpha
|
||||
. += M
|
||||
|
||||
// update the icon_state and luminosity of the light depending on its state
|
||||
/obj/machinery/light/proc/update(trigger = TRUE)
|
||||
|
||||
@@ -310,6 +310,7 @@
|
||||
if(H.gloves)
|
||||
var/obj/item/clothing/gloves/G = H.gloves
|
||||
if(G.siemens_coefficient == 0)
|
||||
SEND_SIGNAL(M, COMSIG_LIVING_SHOCK_PREVENTED, power_source, source, siemens_coeff, dist_check)
|
||||
return 0 //to avoid spamming with insulated glvoes on
|
||||
|
||||
var/area/source_area
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
/obj/machinery/power/emitter/ComponentInitialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
||||
|
||||
/obj/machinery/power/emitter/RefreshParts()
|
||||
var/max_firedelay = 120
|
||||
|
||||
@@ -61,7 +61,7 @@ field_generator power level display
|
||||
|
||||
/obj/machinery/field/generator/ComponentInitialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
||||
|
||||
/obj/machinery/field/generator/process()
|
||||
if(active == FG_ONLINE)
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
@@ -13,11 +13,16 @@
|
||||
var/pin_removeable = 0 // Can be replaced by any pin.
|
||||
var/obj/item/gun/gun
|
||||
|
||||
/obj/item/firing_pin/New(newloc)
|
||||
..()
|
||||
/obj/item/firing_pin/Initialize(newloc)
|
||||
. = ..()
|
||||
if(istype(newloc, /obj/item/gun))
|
||||
gun = newloc
|
||||
|
||||
/obj/item/firing_pin/Destroy()
|
||||
if(gun)
|
||||
gun.pin = null
|
||||
return ..()
|
||||
|
||||
/obj/item/firing_pin/afterattack(atom/target, mob/user, proximity_flag)
|
||||
. = ..()
|
||||
if(proximity_flag)
|
||||
@@ -224,24 +229,6 @@
|
||||
suit_requirement = /obj/item/clothing/suit/bluetag
|
||||
tagcolor = "blue"
|
||||
|
||||
/obj/item/firing_pin/Destroy()
|
||||
if(gun)
|
||||
gun.pin = null
|
||||
return ..()
|
||||
|
||||
//Station Locked
|
||||
|
||||
/obj/item/firing_pin/away
|
||||
name = "station locked pin"
|
||||
desc = "A firing pin that only will fire when off the station."
|
||||
|
||||
/obj/item/firing_pin/away/pin_auth(mob/living/user)
|
||||
var/area/station_area = get_area(src)
|
||||
if(!station_area || is_station_level(station_area.z))
|
||||
to_chat(user, "<span class='warning'>The pin beeps, refusing to fire.</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/firing_pin/security_level
|
||||
name = "security level firing pin"
|
||||
desc = "A sophisticated firing pin that authorizes operation based on its settings and current security level."
|
||||
@@ -325,3 +312,18 @@
|
||||
|
||||
/obj/item/firing_pin/security_level/pin_auth(mob/living/user)
|
||||
return (only_lethals && !(gun.chambered?.harmful)) || ISINRANGE(GLOB.security_level, min_sec_level, max_sec_level)
|
||||
|
||||
// Explorer Firing Pin- Prevents use on station Z-Level, so it's justifiable to give Explorers guns that don't suck.
|
||||
/obj/item/firing_pin/explorer
|
||||
name = "outback firing pin"
|
||||
desc = "A firing pin used by the austrailian defense force, retrofit to prevent weapon discharge on the station."
|
||||
icon_state = "firing_pin_explorer"
|
||||
fail_message = "<span class='warning'>CANNOT FIRE WHILE ON STATION, MATE!</span>"
|
||||
|
||||
// This checks that the user isn't on the station Z-level.
|
||||
/obj/item/firing_pin/explorer/pin_auth(mob/living/user)
|
||||
var/turf/station_check = get_turf(user)
|
||||
if(!station_check||is_station_level(station_check.z))
|
||||
to_chat(user, "<span class='warning'>You cannot use your weapon while on the station!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
|
||||
#define MOVES_HITSCAN -1 //Not actually hitscan but close as we get without actual hitscan.
|
||||
#define MUZZLE_EFFECT_PIXEL_INCREMENT 17 //How many pixels to move the muzzle flash up so your character doesn't look like they're shitting out lasers.
|
||||
/// Minimum projectile pixels to move before it animate()S, below this it's a direct set.
|
||||
#define MINIMUM_PIXELS_TO_ANIMATE 4
|
||||
/// Pixels to instantly travel on firing.
|
||||
#define PROJECTILE_FIRING_INSTANT_TRAVEL_AMOUNT 16
|
||||
|
||||
/obj/item/projectile
|
||||
name = "projectile"
|
||||
@@ -12,7 +16,9 @@
|
||||
pass_flags = PASSTABLE
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
movement_type = FLYING
|
||||
animate_movement = NO_STEPS
|
||||
hitsound = 'sound/weapons/pierce.ogg'
|
||||
appearance_flags = PIXEL_SCALE
|
||||
var/hitsound_wall = ""
|
||||
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
@@ -32,14 +38,19 @@
|
||||
//Fired processing vars
|
||||
var/fired = FALSE //Have we been fired yet
|
||||
var/paused = FALSE //for suspending the projectile midair
|
||||
var/last_projectile_move = 0
|
||||
var/time_offset = 0
|
||||
var/datum/point/vector/trajectory
|
||||
var/trajectory_ignore_forcemove = FALSE //instructs forceMove to NOT reset our trajectory to the new location!
|
||||
/// "leftover" pixels for Range() calculation as pixel_move() was moved to simulated semi-pixel movement and Range() is in tiles.
|
||||
var/pixels_range_leftover = 0
|
||||
/// "leftover" tick pixels and stuff yeah, so we don't round off things and introducing tracing inaccuracy.
|
||||
var/pixels_tick_leftover = 0
|
||||
|
||||
/// Pixels moved per second.
|
||||
var/pixels_per_second = TILES_TO_PIXELS(12.5)
|
||||
/// The number of pixels we increment by. THIS IS NOT SPEED, DO NOT TOUCH THIS UNLESS YOU KNOW WHAT YOU ARE DOING. In general, lower values means more linetrace accuracy up to a point at cost of performance.
|
||||
var/pixel_increment_amount
|
||||
|
||||
var/speed = 0.8 //Amount of deciseconds it takes for projectile to travel
|
||||
/// "leftover" ticks and stuff yeah. hey when are we rewriting projectiles for the eighth time to do something smarter like incrementing x pixels until it meets a goal instead of for(var/i in 1 to required_moves)?
|
||||
var/tick_moves_leftover = 0
|
||||
var/Angle = 0
|
||||
var/original_angle = 0 //Angle at firing
|
||||
var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle
|
||||
@@ -75,12 +86,16 @@
|
||||
//Homing
|
||||
var/homing = FALSE
|
||||
var/atom/homing_target
|
||||
var/homing_turn_speed = 10 //Angle per tick.
|
||||
/// How fast the projectile turns towards its homing targets, in angle per second.
|
||||
var/homing_turn_speed = 100
|
||||
var/homing_inaccuracy_min = 0 //in pixels for these. offsets are set once when setting target.
|
||||
var/homing_inaccuracy_max = 0
|
||||
var/homing_offset_x = 0
|
||||
var/homing_offset_y = 0
|
||||
|
||||
/// How many deciseconds are each hitscan movement considered. Used for homing and other things that use seconds for timing rather than ticks.
|
||||
var/hitscan_movement_decisecond_equivalency = 0.1
|
||||
|
||||
var/ignore_source_check = FALSE
|
||||
|
||||
var/damage = 10
|
||||
@@ -88,7 +103,8 @@
|
||||
var/nodamage = 0 //Determines if the projectile will skip any damage inflictions
|
||||
var/flag = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb
|
||||
var/projectile_type = /obj/item/projectile
|
||||
var/range = 50 //This will de-increment every step. When 0, it will deletze the projectile.
|
||||
/// Range of the projectile, de-incrementing every step. The projectile deletes itself at 0. This is in tiles.
|
||||
var/range = 50
|
||||
var/decayedRange //stores original range
|
||||
var/reflect_range_decrease = 5 //amount of original range that falls off when reflecting, so it doesn't go forever
|
||||
var/is_reflectable = FALSE // Can it be reflected or not?
|
||||
@@ -120,6 +136,10 @@
|
||||
permutated = list()
|
||||
decayedRange = range
|
||||
|
||||
/**
|
||||
* Artificially modified to be called at around every world.icon_size pixels of movement.
|
||||
* WARNING: Range() can only be called once per pixel_increment_amount pixels.
|
||||
*/
|
||||
/obj/item/projectile/proc/Range()
|
||||
range--
|
||||
if(range <= 0 && loc)
|
||||
@@ -334,14 +354,15 @@
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/// one move is a tile.
|
||||
/obj/item/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight.
|
||||
if(!trajectory && isnull(forced_angle) && isnull(Angle))
|
||||
return FALSE
|
||||
var/datum/point/vector/current = trajectory
|
||||
if(!current)
|
||||
var/turf/T = get_turf(src)
|
||||
current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, SSprojectiles.global_pixel_speed)
|
||||
var/datum/point/vector/v = current.return_vector_after_increments(moves * SSprojectiles.global_iterations_per_move)
|
||||
current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, pixel_increment_amount || SSprojectiles.global_pixel_increment_amount)
|
||||
var/datum/point/vector/v = current.return_vector_after_increments(TILES_TO_PIXELS(moves) / (pixel_increment_amount || SSprojectiles.global_pixel_increment_amount))
|
||||
return v.return_turf()
|
||||
|
||||
/obj/item/projectile/proc/return_pathing_turfs_in_moves(moves, forced_angle)
|
||||
@@ -358,16 +379,14 @@
|
||||
return PROCESS_KILL
|
||||
if(paused || !isturf(loc))
|
||||
return
|
||||
|
||||
var/ds = (SSprojectiles.flags & SS_TICKER)? (wait * world.tick_lag) : wait
|
||||
var/required_moves = ds / speed
|
||||
var/leftover = MODULUS(required_moves, 1)
|
||||
tick_moves_leftover += leftover
|
||||
required_moves = round(required_moves)
|
||||
if(tick_moves_leftover > 1)
|
||||
required_moves += round(tick_moves_leftover)
|
||||
tick_moves_leftover = MODULUS(tick_moves_leftover, 1)
|
||||
for(var/i in 1 to required_moves)
|
||||
pixel_move(1, FALSE)
|
||||
var/required_pixels = (pixels_per_second * ds * 0.1) + pixels_tick_leftover
|
||||
if(required_pixels >= pixel_increment_amount)
|
||||
pixels_tick_leftover = MODULUS(required_pixels, pixel_increment_amount)
|
||||
pixel_move(FLOOR(required_pixels / pixel_increment_amount, 1), FALSE, ds, SSprojectiles.global_projectile_speed_multiplier)
|
||||
else
|
||||
pixels_tick_leftover = required_pixels
|
||||
|
||||
/obj/item/projectile/proc/fire(angle, atom/direct_target)
|
||||
if(fired_from)
|
||||
@@ -390,7 +409,7 @@
|
||||
qdel(src)
|
||||
return
|
||||
var/turf/target = locate(clamp(starting + xo, 1, world.maxx), clamp(starting + yo, 1, world.maxy), starting.z)
|
||||
setAngle(Get_Angle(src, target))
|
||||
setAngle(get_projectile_angle(src, target))
|
||||
original_angle = Angle
|
||||
if(!nondirectional_sprite)
|
||||
var/matrix/M = new
|
||||
@@ -399,14 +418,16 @@
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(starting)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, SSprojectiles.global_pixel_speed)
|
||||
last_projectile_move = world.time
|
||||
if(isnull(pixel_increment_amount))
|
||||
pixel_increment_amount = SSprojectiles.global_pixel_increment_amount
|
||||
trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, pixel_increment_amount)
|
||||
fired = TRUE
|
||||
if(hitscan)
|
||||
process_hitscan()
|
||||
return
|
||||
if(!(datum_flags & DF_ISPROCESSING))
|
||||
START_PROCESSING(SSprojectiles, src)
|
||||
pixel_move(1, FALSE) //move it now!
|
||||
pixel_move(round(PROJECTILE_FIRING_INSTANT_TRAVEL_AMOUNT / pixel_increment_amount), FALSE, allow_animation = FALSE) //move it now!
|
||||
|
||||
/obj/item/projectile/proc/setAngle(new_angle, hitscan_store_segment = TRUE) //wrapper for overrides.
|
||||
Angle = new_angle
|
||||
@@ -451,7 +472,8 @@
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/proc/set_pixel_speed(new_speed)
|
||||
/obj/item/projectile/proc/set_pixel_increment_amount(new_speed)
|
||||
pixel_increment_amount = new_speed
|
||||
if(trajectory)
|
||||
trajectory.set_speed(new_speed)
|
||||
return TRUE
|
||||
@@ -464,6 +486,7 @@
|
||||
beam_segments[beam_index] = null //record start.
|
||||
|
||||
/obj/item/projectile/proc/process_hitscan()
|
||||
var/ttm = round(world.icon_size / pixel_increment_amount, 1)
|
||||
var/safety = range * 10
|
||||
record_hitscan_start(RETURN_POINT_VECTOR_INCREMENT(src, Angle, MUZZLE_EFFECT_PIXEL_INCREMENT, 1))
|
||||
while(loc && !QDELETED(src))
|
||||
@@ -476,20 +499,33 @@
|
||||
if(!QDELETED(src))
|
||||
qdel(src)
|
||||
return //Kill!
|
||||
pixel_move(1, TRUE)
|
||||
pixel_move(ttm, TRUE, hitscan_movement_decisecond_equivalency)
|
||||
|
||||
/obj/item/projectile/proc/pixel_move(trajectory_multiplier, hitscanning = FALSE)
|
||||
/**
|
||||
* The proc to make the projectile go, using a simulated pixel movement line trace.
|
||||
* Note: deciseconds_equivalent is currently only used for homing, times is the number of times to move pixel_increment_amount.
|
||||
* Trajectory multiplier directly modifies the factor of pixel_increment_amount to go per time.
|
||||
* It's complicated, so probably just don'ot mess with this unless you know what you're doing.
|
||||
*/
|
||||
/obj/item/projectile/proc/pixel_move(times, hitscanning = FALSE, deciseconds_equivalent = world.tick_lag, trajectory_multiplier = 1, allow_animation = TRUE)
|
||||
if(!loc || !trajectory)
|
||||
return
|
||||
last_projectile_move = world.time
|
||||
if(!nondirectional_sprite && !hitscanning)
|
||||
var/matrix/M = new
|
||||
M.Turn(Angle)
|
||||
transform = M
|
||||
if(homing)
|
||||
process_homing()
|
||||
var/forcemoved = FALSE
|
||||
for(var/i in 1 to SSprojectiles.global_iterations_per_move)
|
||||
var/turf/oldloc = loc
|
||||
var/old_px = pixel_x
|
||||
var/old_py = pixel_y
|
||||
for(var/i in 1 to times)
|
||||
// HOMING START - Too expensive to proccall at this point.
|
||||
if(homing_target)
|
||||
// No datum/points, too expensive.
|
||||
var/angle = closer_angle_difference(Angle, get_projectile_angle(src, homing_target))
|
||||
var/max_turn = homing_turn_speed * deciseconds_equivalent * 0.1
|
||||
setAngle(Angle + clamp(angle, -max_turn, max_turn))
|
||||
// HOMING END
|
||||
trajectory.increment(trajectory_multiplier)
|
||||
var/turf/T = trajectory.return_turf()
|
||||
if(!istype(T))
|
||||
@@ -509,23 +545,29 @@
|
||||
pixel_x = trajectory.return_px()
|
||||
pixel_y = trajectory.return_py()
|
||||
else if(T != loc)
|
||||
step_towards(src, T)
|
||||
var/safety = CEILING(pixel_increment_amount / world.icon_size, 1) * 2 + 1
|
||||
while(T != loc)
|
||||
if(!--safety)
|
||||
CRASH("Projectile took more than pixel incrememnt speed times 2 to get to its location, this is probably something seriously scuffed going on.")
|
||||
step_towards(src, T)
|
||||
if(QDELETED(src))
|
||||
return
|
||||
pixels_range_leftover += pixel_increment_amount
|
||||
if(pixels_range_leftover > world.icon_size)
|
||||
Range()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
pixels_range_leftover -= world.icon_size
|
||||
if(!hitscanning && !forcemoved)
|
||||
pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move
|
||||
pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move
|
||||
animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW)
|
||||
Range()
|
||||
|
||||
/obj/item/projectile/proc/process_homing() //may need speeding up in the future performance wise.
|
||||
if(!homing_target)
|
||||
return FALSE
|
||||
var/datum/point/PT = RETURN_PRECISE_POINT(homing_target)
|
||||
PT.x += clamp(homing_offset_x, 1, world.maxx)
|
||||
PT.y += clamp(homing_offset_y, 1, world.maxy)
|
||||
var/angle = closer_angle_difference(Angle, angle_between_points(RETURN_PRECISE_POINT(src), PT))
|
||||
setAngle(Angle + clamp(angle, -homing_turn_speed, homing_turn_speed))
|
||||
var/traj_px = round(trajectory.return_px(), 1)
|
||||
var/traj_py = round(trajectory.return_py(), 1)
|
||||
if(allow_animation && (pixel_increment_amount * times > MINIMUM_PIXELS_TO_ANIMATE))
|
||||
pixel_x = ((oldloc.x - x) * world.icon_size) + old_px
|
||||
pixel_y = ((oldloc.y - y) * world.icon_size) + old_py
|
||||
animate(src, pixel_x = traj_px, pixel_y = traj_py, time = 1, flags = ANIMATION_END_NOW)
|
||||
else
|
||||
pixel_x = traj_px
|
||||
pixel_y = traj_py
|
||||
|
||||
/obj/item/projectile/proc/set_homing_target(atom/A)
|
||||
if(!A || (!isturf(A) && !isturf(A.loc)))
|
||||
@@ -575,7 +617,7 @@
|
||||
if(targloc || !params)
|
||||
yo = targloc.y - curloc.y
|
||||
xo = targloc.x - curloc.x
|
||||
setAngle(Get_Angle(src, targloc) + spread)
|
||||
setAngle(get_projectile_angle(src, targloc) + spread)
|
||||
|
||||
if(isliving(source) && params)
|
||||
var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params)
|
||||
@@ -586,7 +628,7 @@
|
||||
else if(targloc)
|
||||
yo = targloc.y - curloc.y
|
||||
xo = targloc.x - curloc.x
|
||||
setAngle(Get_Angle(src, targloc) + spread)
|
||||
setAngle(get_projectile_angle(src, targloc) + spread)
|
||||
else
|
||||
stack_trace("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!")
|
||||
qdel(src)
|
||||
@@ -698,3 +740,7 @@
|
||||
/proc/is_energy_reflectable_projectile(atom/A)
|
||||
var/obj/item/projectile/P = A
|
||||
return istype(P) && P.is_reflectable
|
||||
|
||||
#undef MOVES_HITSCAN
|
||||
#undef MINIMUM_PIXELS_TO_ANIMATE
|
||||
#undef MUZZLE_EFFECT_PIXEL_INCREMENT
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
flag = "energy"
|
||||
hitsound = 'sound/weapons/tap.ogg'
|
||||
eyeblur = 0
|
||||
speed = 0.6
|
||||
pixels_per_second = TILES_TO_PIXELS(16.667)
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
|
||||
light_color = LIGHT_COLOR_BLUE
|
||||
tracer_type = /obj/effect/projectile/tracer/disabler
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
damage = 20
|
||||
armour_penetration = 20
|
||||
light_range = 3
|
||||
speed = 0.6
|
||||
pixels_per_second = TILES_TO_PIXELS(16.667)
|
||||
range = 35
|
||||
light_color = LIGHT_COLOR_RED
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
armour_penetration = 20
|
||||
movement_type = FLYING | UNSTOPPABLE
|
||||
range = 20
|
||||
speed = 0.8
|
||||
pixels_per_second = TILES_TO_PIXELS(12.5)
|
||||
light_range = 4
|
||||
light_color = LIGHT_COLOR_RED
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/obj/item/projectile/bullet/p50
|
||||
name =".50 bullet"
|
||||
speed = 0.4
|
||||
pixels_per_second = TILES_TO_PIXELS(25)
|
||||
damage = 70
|
||||
knockdown = 100
|
||||
dismemberment = 50
|
||||
@@ -43,5 +43,5 @@
|
||||
/obj/item/projectile/bullet/p50/penetrator/shuttle //Nukeop Shuttle Variety
|
||||
icon_state = "gaussstrong"
|
||||
damage = 25
|
||||
speed = 0.3
|
||||
pixels_per_second = TILES_TO_PIXELS(33.33)
|
||||
range = 16
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
|
||||
flag = "rad"
|
||||
irradiate = 5000
|
||||
speed = 0.4
|
||||
pixels_per_second = TILES_TO_PIXELS(25)
|
||||
hitsound = 'sound/weapons/emitter2.ogg'
|
||||
impact_type = /obj/effect/projectile/impact/xray
|
||||
var/static/list/particle_colors = list(
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
jitter = 20
|
||||
hitsound = 'sound/weapons/taserhit.ogg'
|
||||
range = 14
|
||||
speed = 0.6
|
||||
pixels_per_second = TILES_TO_PIXELS(16.667)
|
||||
tracer_type = /obj/effect/projectile/tracer/stun
|
||||
muzzle_type = /obj/effect/projectile/muzzle/stun
|
||||
impact_type = /obj/effect/projectile/impact/stun
|
||||
|
||||
@@ -434,7 +434,7 @@
|
||||
damage = 15
|
||||
damage_type = BURN
|
||||
nodamage = 0
|
||||
speed = 0.3
|
||||
pixels_per_second = TILES_TO_PIXELS(33.33)
|
||||
flag = "magic"
|
||||
|
||||
var/zap_power = 20000
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
damage_type = BURN
|
||||
damage = 10
|
||||
knockdown = 20
|
||||
speed = 2
|
||||
pixels_per_second = TILES_TO_PIXELS(5)
|
||||
range = 16
|
||||
movement_type = FLYING | UNSTOPPABLE
|
||||
var/datum/beam/arm
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
circuit = /obj/item/circuitboard/machine/chem_dispenser
|
||||
var/obj/item/stock_parts/cell/cell
|
||||
var/powerefficiency = 0.1
|
||||
var/powerefficiency = 0.0666666
|
||||
var/amount = 30
|
||||
var/recharge_amount = 10
|
||||
var/recharge_counter = 0
|
||||
@@ -387,7 +387,7 @@
|
||||
|
||||
/obj/machinery/chem_dispenser/RefreshParts()
|
||||
recharge_amount = initial(recharge_amount)
|
||||
var/newpowereff = 0.0666666
|
||||
var/newpowereff = initial(powerefficiency)
|
||||
for(var/obj/item/stock_parts/cell/P in component_parts)
|
||||
cell = P
|
||||
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
|
||||
@@ -720,3 +720,58 @@
|
||||
component_parts += new /obj/item/stack/sheet/glass(null)
|
||||
component_parts += new /obj/item/stock_parts/cell/bluespace(null)
|
||||
RefreshParts()
|
||||
|
||||
///An unique, less efficient model found in the medbay apothecary room.
|
||||
/obj/machinery/chem_dispenser/apothecary
|
||||
name = "apothecary chem dispenser"
|
||||
desc = "A cheaper chem dispenser meant for small scale medicine production."
|
||||
icon_state = "minidispenser"
|
||||
working_state = "minidispenser_working"
|
||||
nopower_state = "minidispenser_nopower"
|
||||
circuit = /obj/item/circuitboard/machine/chem_dispenser/apothecary
|
||||
powerefficiency = 0.0833333
|
||||
dispensable_reagents = list( //radium and stable plasma moved to upgrade tier 1 and 2, they've little to do with most medicines anyway.
|
||||
/datum/reagent/hydrogen,
|
||||
/datum/reagent/lithium,
|
||||
/datum/reagent/carbon,
|
||||
/datum/reagent/nitrogen,
|
||||
/datum/reagent/oxygen,
|
||||
/datum/reagent/fluorine,
|
||||
/datum/reagent/sodium,
|
||||
/datum/reagent/aluminium,
|
||||
/datum/reagent/silicon,
|
||||
/datum/reagent/phosphorus,
|
||||
/datum/reagent/sulfur,
|
||||
/datum/reagent/chlorine,
|
||||
/datum/reagent/potassium,
|
||||
/datum/reagent/iron,
|
||||
/datum/reagent/copper,
|
||||
/datum/reagent/mercury,
|
||||
/datum/reagent/water,
|
||||
/datum/reagent/consumable/ethanol,
|
||||
/datum/reagent/consumable/sugar,
|
||||
/datum/reagent/toxin/acid,
|
||||
/datum/reagent/fuel,
|
||||
/datum/reagent/silver,
|
||||
/datum/reagent/iodine,
|
||||
/datum/reagent/bromine
|
||||
)
|
||||
upgrade_reagents = list(
|
||||
/datum/reagent/oil,
|
||||
/datum/reagent/ammonia,
|
||||
/datum/reagent/radium
|
||||
)
|
||||
upgrade_reagents2 = list(
|
||||
/datum/reagent/acetone,
|
||||
/datum/reagent/phenol,
|
||||
/datum/reagent/stable_plasma
|
||||
)
|
||||
upgrade_reagents3 = list(
|
||||
/datum/reagent/medicine/mine_salve
|
||||
)
|
||||
|
||||
emagged_reagents = list(
|
||||
/datum/reagent/drug/space_drugs,
|
||||
/datum/reagent/toxin/carpotoxin,
|
||||
/datum/reagent/medicine/morphine
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user