Merge branch 'master' into Ghommie-cit679

This commit is contained in:
Ghom
2020-04-14 16:22:00 +02:00
committed by GitHub
208 changed files with 2331 additions and 1470 deletions
+165 -168
View File
@@ -145,9 +145,6 @@
"aE" = (
/turf/open/floor/plating/asteroid/airless,
/area/ruin/powered)
"aF" = (
/turf/open/floor/plating/asteroid,
/area/ruin/powered)
"aG" = (
/obj/structure/sink/puddle,
/turf/open/floor/plating/asteroid,
@@ -241,7 +238,7 @@
/turf/closed/mineral/random/low_chance/earth_like,
/area/ruin/powered)
"bb" = (
/turf/closed/mineral/random/earth_like,
/turf/closed/mineral/random/no_caves/earth_like,
/area/ruin/powered)
"bc" = (
/obj/item/circuitboard/machine/circuit_imprinter,
@@ -1137,7 +1134,7 @@ ao
ao
ak
af
aF
am
bb
bb
bb
@@ -1189,7 +1186,7 @@ an
an
ae
ae
aF
am
bb
bb
bb
@@ -1223,8 +1220,8 @@ bb
bb
bb
ba
aF
aF
am
am
ba
ba
ba
@@ -1241,7 +1238,7 @@ ar
af
at
at
aF
am
bb
fp
bb
@@ -1275,10 +1272,10 @@ bb
bb
bb
ad
aF
aF
aF
aF
am
am
am
am
ba
aH
aJ
@@ -1286,13 +1283,13 @@ aL
bb
bb
aT
aF
aF
aF
aF
aF
aF
aF
am
am
am
am
am
am
am
aR
bb
bb
@@ -1328,23 +1325,23 @@ bb
bb
ad
au
aF
am
aB
aF
aF
aF
aF
aF
am
am
am
am
am
ba
bb
aF
aF
aF
aF
aF
aF
aF
aF
am
am
am
am
am
am
am
am
bg
bb
fp
@@ -1384,19 +1381,19 @@ bs
aC
bC
WH
aF
aF
aF
am
am
am
ba
bb
bb
aF
aF
aF
aF
aF
aF
aF
am
am
am
am
am
am
am
bb
bb
bb
@@ -1432,23 +1429,23 @@ bb
bb
ad
ax
aF
am
aD
aF
aF
aF
aF
am
am
am
am
bb
bb
bb
aU
bd
aF
aF
am
am
bl
bm
aF
aF
am
am
bb
bb
bb
@@ -1484,20 +1481,20 @@ bb
bb
ad
ay
aF
aF
aF
am
am
am
aG
aF
aF
am
am
bb
bb
ba
av
be
aF
aF
aF
am
am
am
bn
bo
bo
@@ -1535,24 +1532,24 @@ bb
bb
bb
ba
aF
aF
aF
aF
aF
aF
aF
am
am
am
am
am
am
am
bb
bb
bb
bc
bf
aF
aF
aF
aF
aF
aF
am
am
am
am
am
am
br
bb
bb
@@ -1590,22 +1587,22 @@ bb
bb
bb
bb
aF
aF
aF
aF
aF
am
am
am
am
am
bb
bb
aF
aF
aF
aF
aF
aF
aF
aF
aF
am
am
am
am
am
am
am
am
am
bb
bb
bb
@@ -1642,21 +1639,21 @@ bb
bb
bb
bb
aF
aF
aF
aF
aF
am
am
am
am
am
aN
aF
aF
aF
aF
am
am
am
am
aR
aF
aF
aF
aF
am
am
am
am
bb
bb
fp
@@ -1695,22 +1692,22 @@ bb
bb
bb
ba
aF
aF
aF
am
am
am
ba
ba
aF
aF
aF
aF
aF
aF
aF
aF
aF
aF
aF
am
am
am
am
am
am
am
am
am
am
am
ba
ba
ba
@@ -1752,16 +1749,16 @@ ba
aK
ba
ba
aF
aF
aF
aF
aF
aF
aF
am
am
am
am
am
am
am
bp
aF
aF
am
am
aR
ba
ba
@@ -1805,15 +1802,15 @@ ba
fp
ba
aP
aF
aF
aF
aF
aF
aF
aF
aF
aF
am
am
am
am
am
am
am
am
am
aR
ba
ba
@@ -1858,15 +1855,15 @@ ba
ba
aQ
aP
aF
aF
am
am
aR
aF
aF
aF
aF
am
am
am
am
bb
aF
am
ba
ba
aZ
@@ -1910,12 +1907,12 @@ ba
ba
ba
ba
aF
aF
aF
aF
am
am
am
am
aP
aF
am
bg
bb
bb
@@ -1962,12 +1959,12 @@ bb
bb
ba
ba
aF
aF
aF
aF
aF
aF
am
am
am
am
am
am
aS
bb
fp
@@ -2015,11 +2012,11 @@ bb
bb
ba
bg
aF
aF
aF
aF
aF
am
am
am
am
am
aS
bb
fp
@@ -2066,12 +2063,12 @@ bb
bb
fp
ba
aF
aF
am
am
bj
aF
aF
aF
am
am
am
aS
fp
bb
@@ -2120,9 +2117,9 @@ ba
ba
aS
bi
aF
aF
aF
am
am
am
aS
aS
bb
@@ -2171,10 +2168,10 @@ ba
aZ
ba
aS
aF
aF
aF
aF
am
am
am
am
aS
aS
ba
@@ -44066,7 +44066,6 @@
/area/science/misc_lab)
"cbe" = (
/obj/structure/table/reinforced,
/obj/item/integrated_electronics/analyzer,
/obj/item/integrated_electronics/debugger,
/obj/item/integrated_electronics/wirer,
/turf/open/floor/plasteel/white,
@@ -126146,7 +126146,6 @@
/area/quartermaster/storage)
"hic" = (
/obj/structure/table/reinforced,
/obj/item/integrated_electronics/analyzer,
/obj/item/integrated_electronics/debugger,
/obj/item/integrated_electronics/wirer,
/turf/open/floor/plasteel/white/side{
@@ -127145,7 +127144,6 @@
/area/ai_monitored/turret_protected/aisat_interior)
"qhc" = (
/obj/structure/table/reinforced,
/obj/item/integrated_electronics/analyzer,
/obj/item/integrated_electronics/debugger,
/obj/item/integrated_electronics/wirer,
/turf/open/floor/plasteel/white/side{
@@ -33907,7 +33907,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
/obj/item/integrated_electronics/analyzer,
/obj/machinery/computer/security/telescreen/circuitry{
pixel_y = 30
},
-1
View File
@@ -1556,7 +1556,6 @@
dir = 8
},
/obj/structure/table,
/obj/item/integrated_electronics/analyzer,
/obj/item/integrated_circuit_printer,
/turf/open/floor/plasteel/dark,
/area/science/nanite)
@@ -81886,7 +81886,6 @@
/area/maintenance/aft)
"gRS" = (
/obj/structure/table/reinforced,
/obj/item/integrated_electronics/analyzer,
/obj/item/integrated_circuit_printer,
/turf/open/floor/plasteel/white,
/area/science/circuit)
@@ -82471,7 +82470,6 @@
/turf/open/floor/plasteel/white,
/area/science/circuit)
"ohj" = (
/obj/item/integrated_electronics/analyzer,
/obj/item/integrated_electronics/debugger,
/obj/item/integrated_electronics/wirer,
/obj/structure/table/reinforced,
@@ -26358,7 +26358,6 @@
dir = 8
},
/obj/item/integrated_circuit_printer,
/obj/item/integrated_electronics/analyzer,
/obj/item/integrated_electronics/debugger,
/obj/item/integrated_electronics/wirer,
/obj/effect/turf_decal/tile/neutral,
@@ -27443,7 +27443,6 @@
/area/science/explab)
"bmZ" = (
/obj/structure/table/reinforced,
/obj/item/integrated_electronics/analyzer,
/obj/machinery/magnetic_controller{
autolink = 1;
pixel_x = -28;
@@ -60715,7 +60714,6 @@
/area/maintenance/department/crew_quarters/dorms)
"vmG" = (
/obj/structure/table/reinforced,
/obj/item/integrated_electronics/analyzer,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
+4 -1
View File
@@ -179,7 +179,7 @@
// #define SPEECH_FORCED 7
#define COMSIG_MOB_ANTAG_ON_GAIN "mob_antag_on_gain" //from base of /datum/antagonist/on_gain(): (antag_datum)
#define COMSIG_MOB_SPELL_CAST_CHECK "mob_spell_cast_check" //called from base of /obj/effect/proc_holder/spell/cast_check(): (spell)
#define COMSIG_MOB_SPELL_CAN_CAST "mob_spell_can_cast" //called from base of /obj/effect/proc_holder/spell/can_cast(): (spell)
// /mob/living signals
#define COMSIG_LIVING_REGENERATE_LIMBS "living_regenerate_limbs" //from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs)
@@ -190,6 +190,7 @@
#define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock" //sent by stuff like stunbatons and tasers: ()
#define COMSIG_LIVING_REVIVE "living_revive" //from base of mob/living/revive() (full_heal, admin_revive)
#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login" //sent when a mob/login() finishes: (client)
#define COMSIG_MOB_CLIENT_LOGOUT "comsig_mob_client_logout" //sent when a mob/logout() starts: (client)
#define COMSIG_MOB_CLIENT_MOVE "comsig_mob_client_move" //sent when client/Move() finishes with no early returns: (client, direction, n, oldloc)
#define COMSIG_LIVING_GUN_PROCESS_FIRE "living_gun_process_fire" //from base of /obj/item/gun/proc/process_fire(): (atom/target, params, zone_override)
// This returns flags as defined for block in __DEFINES/combat.dm!
@@ -242,12 +243,14 @@
#define COMSIG_ITEM_IMBUE_SOUL "item_imbue_soul" //return a truthy value to prevent ensouling, checked in /obj/effect/proc_holder/spell/targeted/lichdom/cast(): (mob/user)
#define COMSIG_ITEM_HIT_REACT "item_hit_react" //from base of obj/item/hit_reaction(): (list/args)
#define COMSIG_ITEM_WEARERCROSSED "wearer_crossed" //called on item when crossed by something (): (/atom/movable)
#define COMSIG_ITEM_WORN_OVERLAYS "item_worn_overlays" //from base of obj/item/worn_overlays(): (isinhands, icon_file, used_state, style_flags, list/overlays)
// THE FOLLOWING TWO BLOCKS SHOULD RETURN BLOCK FLAGS AS DEFINED IN __DEFINES/combat.dm!
#define COMSIG_ITEM_CHECK_BLOCK "check_block" //from base of obj/item/check_block(): (mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
#define COMSIG_ITEM_RUN_BLOCK "run_block" //from base of obj/item/run_block(): (mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
// /obj/item/clothing signals
#define COMSIG_SHOES_STEP_ACTION "shoes_step_action" //from base of obj/item/clothing/shoes/proc/step_action(): ()
#define COMSIG_SUIT_MADE_HELMET "suit_made_helmet" //from base of obj/item/clothing/suit/MakeHelmet(): (helmet)
// /obj/item/implant signals
#define COMSIG_IMPLANT_ACTIVATED "implant_activated" //from base of /obj/item/implant/proc/activate(): ()
-8
View File
@@ -1,13 +1,5 @@
/*ALL DEFINES RELATED TO INVENTORY OBJECTS, MANAGEMENT, ETC, GO HERE*/
//ITEM INVENTORY WEIGHT, FOR w_class
#define WEIGHT_CLASS_TINY 1 //Usually items smaller then a human hand, ex: Playing Cards, Lighter, Scalpel, Coins/Money
#define WEIGHT_CLASS_SMALL 2 //Pockets can hold small and tiny items, ex: Flashlight, Multitool, Grenades, GPS Device
#define WEIGHT_CLASS_NORMAL 3 //Standard backpacks can carry tiny, small & normal items, ex: Fire extinguisher, Stunbaton, Gas Mask, Metal Sheets
#define WEIGHT_CLASS_BULKY 4 //Items that can be weilded or equipped but not stored in a normal bag, ex: Defibrillator, Backpack, Space Suits
#define WEIGHT_CLASS_HUGE 5 //Usually represents objects that require two hands to operate, ex: Shotgun, Two Handed Melee Weapons - Can not fit in Boh
#define WEIGHT_CLASS_GIGANTIC 6 //Essentially means it cannot be picked up or placed in an inventory, ex: Mech Parts, Safe - Can not fit in Boh
//Inventory depth: limits how many nested storage items you can access directly.
//1: stuff in mob, 2: stuff in backpack, 3: stuff in box in backpack, etc
#define INVENTORY_DEPTH 3
+2 -1
View File
@@ -67,6 +67,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define iscatperson(A) (ishumanbasic(A) && istype(A.dna.species, /datum/species/human/felinid) )
#define isdwarf(A) (is_species(A, /datum/species/dwarf))
#define isdullahan(A) (is_species(A, /datum/species/dullahan))
#define isvampire(A) (is_species(A,/datum/species/vampire))
// Citadel specific species
#define isipcperson(A) (is_species(A, /datum/species/ipc))
@@ -263,4 +264,4 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list(
#define isblobmonster(O) (istype(O, /mob/living/simple_animal/hostile/blob))
#define isshuttleturf(T) (length(T.baseturfs) && (/turf/baseturf_skipover/shuttle in T.baseturfs))
#define isshuttleturf(T) (length(T.baseturfs) && (/turf/baseturf_skipover/shuttle in T.baseturfs))
@@ -129,11 +129,20 @@
#define HUD_PLANE 21
#define HUD_LAYER 21
#define HUD_RENDER_TARGET "HUD_PLANE"
#define ABOVE_HUD_PLANE 22
#define ABOVE_HUD_LAYER 22
#define VOLUMETRIC_STORAGE_BOX_PLANE 23
#define VOLUMETRIC_STORAGE_BOX_LAYER 23
#define VOLUMETRIC_STORAGE_BOX_RENDER_TARGET "VOLUME_STORAGE_BOX_PLANE"
#define VOLUMETRIC_STORAGE_ITEM_PLANE 24
#define VOLUMETRIC_STORAGE_ITEM_LAYER 24
#define VOLUMETRIC_STORAGE_ITEM_RENDER_TARGET "VOLUME_STORAGE_ITEM_PLANE"
#define ABOVE_HUD_PLANE 25
#define ABOVE_HUD_LAYER 25
#define ABOVE_HUD_RENDER_TARGET "ABOVE_HUD_PLANE"
#define SPLASHSCREEN_LAYER 23
#define SPLASHSCREEN_PLANE 23
#define SPLASHSCREEN_LAYER 30
#define SPLASHSCREEN_PLANE 30
#define SPLASHSCREEN_RENDER_TARGET "SPLASHSCREEN_PLANE"
+1
View File
@@ -59,6 +59,7 @@
#define MOVESPEED_ID_SPECIES "SPECIES_SPEED_MOD"
#define MOVESPEED_ID_SMALL_STRIDE "SMALL_STRIDE"
#define MOVESPEED_ID_PRONE_DRAGGING "PRONE_DRAG"
#define MOVESPEED_ID_HUMAN_CARRYING "HUMAN_CARRY"
#define MOVESPEED_ID_SHRINK_RAY "SHRUNKEN_SPEED_MODIFIER"
+47
View File
@@ -0,0 +1,47 @@
// storage_flags variable on /datum/component/storage
// Storage limits. These can be combined (and usually are combined).
/// Check max_items and contents.len when trying to insert
#define STORAGE_LIMIT_MAX_ITEMS (1<<0)
/// Check max_combined_w_class.
#define STORAGE_LIMIT_COMBINED_W_CLASS (1<<1)
/// Use the new volume system. Will automatically force rendering to use the new volume/baystation scaling UI so this is kind of incompatible with stuff like stack storage etc etc.
#define STORAGE_LIMIT_VOLUME (1<<2)
/// Use max_w_class
#define STORAGE_LIMIT_MAX_W_CLASS (1<<3)
#define STORAGE_FLAGS_LEGACY_DEFAULT (STORAGE_LIMIT_MAX_ITEMS | STORAGE_LIMIT_COMBINED_W_CLASS | STORAGE_LIMIT_MAX_W_CLASS)
#define STORAGE_FLAGS_VOLUME_DEFAULT (STORAGE_LIMIT_MAX_ITEMS | STORAGE_LIMIT_VOLUME | STORAGE_LIMIT_MAX_W_CLASS)
//ITEM INVENTORY WEIGHT, FOR w_class
/// Usually items smaller then a human hand, ex: Playing Cards, Lighter, Scalpel, Coins/Money
#define WEIGHT_CLASS_TINY 1
/// Pockets can hold small and tiny items, ex: Flashlight, Multitool, Grenades, GPS Device
#define WEIGHT_CLASS_SMALL 2
/// Standard backpacks can carry tiny, small & normal items, ex: Fire extinguisher, Stunbaton, Gas Mask, Metal Sheets
#define WEIGHT_CLASS_NORMAL 3
/// Items that can be weilded or equipped but not stored in a normal bag, ex: Defibrillator, Backpack, Space Suits
#define WEIGHT_CLASS_BULKY 4
/// Usually represents objects that require two hands to operate, ex: Shotgun, Two Handed Melee Weapons - Can not fit in Boh
#define WEIGHT_CLASS_HUGE 5
/// Essentially means it cannot be picked up or placed in an inventory, ex: Mech Parts, Safe - Can not fit in Boh
#define WEIGHT_CLASS_GIGANTIC 6
/// Macro for automatically getting the volume of an item from its w_class.
#define AUTO_SCALE_VOLUME(w_class) (2 ** w_class)
/// Macro for automatically getting the volume of a storage item from its max_w_class and max_combined_w_class.
#define AUTO_SCALE_STORAGE_VOLUME(w_class, max_combined_w_class) (AUTO_SCALE_VOLUME(w_class) * (max_combined_w_class / w_class))
// UI defines
/// Size of volumetric box icon
#define VOLUMETRIC_STORAGE_BOX_ICON_SIZE 32
/// Size of EACH left/right border icon for volumetric boxes
#define VOLUMETRIC_STORAGE_BOX_BORDER_SIZE 1
/// Minimum pixels an item must have in volumetric scaled storage UI
#define MINIMUM_PIXELS_PER_ITEM 6
/// Maximum number of objects that will be allowed to be displayed using the volumetric display system. Arbitrary number to prevent server lockups.
#define MAXIMUM_VOLUMETRIC_ITEMS 256
/// How much padding to give between items
#define VOLUMETRIC_STORAGE_ITEM_PADDING 1
/// How much padding to give to edges
#define VOLUMETRIC_STORAGE_EDGE_PADDING 1
+21
View File
@@ -550,6 +550,27 @@
for(var/thing in flat_list)
.[thing] = TRUE
/proc/deep_list2params(list/deep_list)
var/list/L = list()
for(var/i in deep_list)
var/key = i
if(isnum(key))
L += "[key]"
continue
if(islist(key))
key = deep_list2params(key)
else if(!istext(key))
key = "[REF(key)]"
L += "[key]"
var/value = deep_list[key]
if(!isnull(value))
if(islist(value))
value = deep_list2params(value)
else if(!(istext(key) || isnum(key)))
value = "[REF(value)]"
L["[key]"] = "[value]"
return list2params(L)
//Picks from the list, with some safeties, and returns the "default" arg if it fails
#define DEFAULTPICK(L, default) ((islist(L) && length(L)) ? pick(L) : default)
+8
View File
@@ -89,6 +89,14 @@
INVOKE_ASYNC(GLOBAL_PROC, /proc/init_ref_coin_values) //so the current procedure doesn't sleep because of UNTIL()
for(var/path in subtypesof(/area/holodeck))
var/area/holodeck/A = path
var/list/compatibles = initial(A.compatible_holodeck_comps)
if(!compatibles || initial(A.abstract_type) == path)
continue
for(var/comp in compatibles)
LAZYADD(GLOB.holodeck_areas_prototypes[comp], A)
//creates every subtype of prototype (excluding prototype) and adds it to list L.
//if no list/L is provided, one is created.
/proc/init_subtypes(prototype, list/L)
+2 -1
View File
@@ -215,7 +215,8 @@
"ipc_antenna" = "None",
"flavor_text" = "",
"meat_type" = "Mammalian",
"body_model" = body_model
"body_model" = body_model,
"body_size" = RESIZE_DEFAULT_SIZE
))
/proc/random_hair_style(gender)
+1 -1
View File
@@ -513,7 +513,7 @@
if(owner && GLOB.common_report && SSticker.current_state == GAME_STATE_FINISHED)
SSticker.show_roundend_report(owner.client, FALSE)
/datum/action/report/IsAvailable()
/datum/action/report/IsAvailable(silent = FALSE)
return 1
/datum/action/report/Topic(href,href_list)
+13
View File
@@ -1563,3 +1563,16 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
if(channels_to_use.len)
world.TgsChatBroadcast()
//Checks to see if either the victim has a garlic necklace or garlic in their blood
/proc/blood_sucking_checks(var/mob/living/carbon/target, check_neck, check_blood)
//Bypass this if the target isnt carbon.
if(!iscarbon(target))
return TRUE
if(check_neck)
if(istype(target.get_item_by_slot(SLOT_NECK), /obj/item/clothing/neck/garlic_necklace))
return FALSE
if(check_blood)
if(target.reagents.has_reagent(/datum/reagent/consumable/garlic))
return FALSE
return TRUE
+6
View File
@@ -249,5 +249,11 @@ GLOBAL_LIST_INIT(bitfields, list(
"COMBAT_FLAG_SOFT_STAMCRIT" = COMBAT_FLAG_SOFT_STAMCRIT,
"COMBAT_FLAG_INTENTIONALLY_RESTING" = COMBAT_FLAG_INTENTIONALLY_RESTING,
"COMBAT_FLAG_RESISTING_REST" = COMBAT_FLAG_RESISTING_REST
),
"storage_flags" = list(
"STORAGE_LIMIT_MAX_ITEMS" = STORAGE_LIMIT_MAX_ITEMS,
"STORAGE_LIMIT_MAX_W_CLASS" = STORAGE_LIMIT_MAX_W_CLASS,
"STORAGE_LIMIT_COMBINED_W_CLASS" = STORAGE_LIMIT_COMBINED_W_CLASS,
"STORAGE_LIMIT_VOLUME" = STORAGE_LIMIT_VOLUME
)
))
+2
View File
@@ -50,3 +50,5 @@ GLOBAL_LIST_EMPTY_TYPED(areas_by_type, /area)
GLOBAL_LIST_EMPTY(all_abstract_markers)
GLOBAL_LIST_EMPTY(stationroom_landmarks) //List of all spawns for stationrooms
GLOBAL_LIST_EMPTY(holodeck_areas_prototypes) //List of holodeck area prototypes per holodeck computer type
+1 -1
View File
@@ -367,7 +367,7 @@
blood_display.hud = src
infodisplay += blood_display
vamprank_display = new /obj/screen/bloodsucker/rank_counter // Vampire Rank
vamprank_display = new /obj/screen/bloodsucker/rank_counter // Bloodsucker Rank
vamprank_display.hud = src
infodisplay += vamprank_display
-38
View File
@@ -210,20 +210,6 @@
user.swap_hand(held_index)
return TRUE
/obj/screen/close
name = "close"
layer = ABOVE_HUD_LAYER
plane = ABOVE_HUD_PLANE
icon_state = "backpack_close"
/obj/screen/close/Initialize(mapload, new_master)
. = ..()
master = new_master
/obj/screen/close/Click()
var/datum/component/storage/S = master
S.hide_from(usr)
return TRUE
/obj/screen/drop
name = "drop"
@@ -406,30 +392,6 @@
else
icon_state = "act_rest0"
/obj/screen/storage
name = "storage"
icon_state = "block"
screen_loc = "7,7 to 10,8"
layer = HUD_LAYER
plane = HUD_PLANE
/obj/screen/storage/Initialize(mapload, new_master)
. = ..()
master = new_master
/obj/screen/storage/Click(location, control, params)
if(world.time <= usr.next_move)
return TRUE
if(usr.incapacitated())
return TRUE
if (ismecha(usr.loc)) // stops inventory actions in a mech
return TRUE
if(master)
var/obj/item/I = usr.get_active_held_item()
if(I)
master.attackby(null, I, usr, params)
return TRUE
/obj/screen/throw_catch
name = "throw/catch"
icon = 'icons/mob/screen_midnight.dmi'
+110
View File
@@ -0,0 +1,110 @@
/obj/screen/storage
name = "storage"
var/insertion_click = FALSE
/obj/screen/storage/Initialize(mapload, new_master)
. = ..()
master = new_master
/obj/screen/storage/Click(location, control, params)
if(!insertion_click)
return ..()
if(world.time <= usr.next_move)
return TRUE
if(usr.incapacitated())
return TRUE
if (ismecha(usr.loc)) // stops inventory actions in a mech
return TRUE
if(master)
var/obj/item/I = usr.get_active_held_item()
if(I)
master.attackby(null, I, usr, params)
return TRUE
/obj/screen/storage/boxes
name = "storage"
icon_state = "block"
screen_loc = "7,7 to 10,8"
layer = HUD_LAYER
plane = HUD_PLANE
insertion_click = TRUE
/obj/screen/storage/close
name = "close"
layer = ABOVE_HUD_LAYER
plane = ABOVE_HUD_PLANE
icon_state = "backpack_close"
/obj/screen/storage/close/Click()
var/datum/component/storage/S = master
S.close(usr)
return TRUE
/obj/screen/storage/left
icon_state = "storage_start"
insertion_click = TRUE
/obj/screen/storage/right
icon_state = "storage_end"
insertion_click = TRUE
/obj/screen/storage/continuous
icon_state = "storage_continue"
insertion_click = TRUE
/obj/screen/storage/volumetric_box
icon_state = "stored_continue"
var/obj/item/our_item
/obj/screen/storage/volumetric_box/Initialize(mapload, new_master, our_item)
src.our_item = our_item
return ..()
/obj/screen/storage/volumetric_box/Destroy()
our_item = null
return ..()
/obj/screen/storage/volumetric_box/Click(location, control, params)
return our_item.Click(location, control, params)
/obj/screen/storage/volumetric_box/center
icon_state = "stored_continue"
var/obj/screen/storage/stored_left/left
var/obj/screen/storage/stored_right/right
var/pixel_size
/obj/screen/storage/volumetric_box/center/Initialize(mapload, new_master, our_item)
left = new(null, src, our_item)
right = new(null, src, our_item)
return ..()
/obj/screen/storage/volumetric_box/center/Destroy()
QDEL_NULL(left)
QDEL_NULL(right)
return ..()
/obj/screen/storage/volumetric_box/center/proc/on_screen_objects()
return list(src, left, right)
/**
* Sets the size of this box screen object and regenerates its left/right borders. This includes the actual border's size!
*/
/obj/screen/storage/volumetric_box/center/proc/set_pixel_size(pixels)
if(pixel_size == pixels)
return
pixel_size = pixels
cut_overlays()
//our icon size is 32 pixels.
transform = matrix((pixels - (VOLUMETRIC_STORAGE_BOX_BORDER_SIZE * 2)) / VOLUMETRIC_STORAGE_BOX_ICON_SIZE, 0, 0, 0, 1, 0)
left.pixel_x = -((pixels - VOLUMETRIC_STORAGE_BOX_ICON_SIZE) * 0.5) - VOLUMETRIC_STORAGE_BOX_BORDER_SIZE
right.pixel_x = ((pixels - VOLUMETRIC_STORAGE_BOX_ICON_SIZE) * 0.5) + VOLUMETRIC_STORAGE_BOX_BORDER_SIZE
add_overlay(left)
add_overlay(right)
/obj/screen/storage/stored_left
icon_state = "stored_start"
appearance_flags = APPEARANCE_UI | KEEP_APART | RESET_TRANSFORM // Yes I know RESET_TRANSFORM is in APPEARANCE_UI but we're hard-asserting this incase someone changes it.
/obj/screen/storage/stored_right
icon_state = "stored_end"
appearance_flags = APPEARANCE_UI | KEEP_APART | RESET_TRANSFORM
+1 -1
View File
@@ -131,7 +131,7 @@
I.do_stagger_action(src, user)
if(I.force)
apply_damage(totitemdamage, I.damtype) //CIT CHANGE - replaces I.force with totitemdamage
if(I.damtype == BRUTE && !HAS_TRAIT(src, TRAIT_NOMARROW))
if(I.damtype == BRUTE)
if(prob(33))
I.add_mob_blood(src)
var/turf/location = get_turf(src)
@@ -449,3 +449,27 @@
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_FLAG
config_entry_value = list(GEN_VISIBLE_NO_CLOTHES, GEN_VISIBLE_NO_UNDIES, GEN_VISIBLE_NEVER) //refer to cit_helpers for all toggles.
//Body size configs, the feature will be disabled if both min and max have the same value.
/datum/config_entry/number/body_size_min
config_entry_value = RESIZE_DEFAULT_SIZE
min_val = 0.1 //to avoid issues with zeros and negative values.
max_val = RESIZE_DEFAULT_SIZE
/datum/config_entry/number/body_size_max
config_entry_value = RESIZE_DEFAULT_SIZE
min_val = RESIZE_DEFAULT_SIZE
//Pun-Pun movement slowdown given to characters with a body size smaller than this value,
//to compensate for their smaller hitbox.
//To disable, just make sure the value is lower than 'body_size_min'
/datum/config_entry/number/threshold_body_size_slowdown
config_entry_value = RESIZE_DEFAULT_SIZE * 0.85
min_val = 0
max_val = RESIZE_DEFAULT_SIZE
//multiplicative slowdown multiplier. See 'dna.update_body_size' for the operation.
//doesn't apply to floating or crawling mobs
/datum/config_entry/number/body_size_slowdown_multiplier
config_entry_value = 0.25
min_val = 0.1 //To encourage folks to disable the slowdown through the above config instead.
+8 -2
View File
@@ -38,11 +38,17 @@ PROCESSING_SUBSYSTEM_DEF(dcs)
if(istext(key))
value = arguments[key]
if(!(istext(key) || isnum(key)))
key = REF(key)
if(islist(key)) // CITADEL EDIT
key = deep_list2params(key)
else
key = REF(key)
key = "[key]" // Key is stringified so numbers dont break things
if(!isnull(value))
if(!(istext(value) || isnum(value)))
value = REF(value)
if(islist(value)) // CITADEL EDIT
value = deep_list2params(value)
else
value = REF(value)
named_arguments["[key]"] = value
else
fullid += "[key]"
+8 -5
View File
@@ -21,9 +21,6 @@ SUBSYSTEM_DEF(fail2topic)
DropFirewallRule() // Clear the old bans if any still remain
if (world.system_type == UNIX && enabled)
enabled = FALSE
subsystem_log("DISABLED - UNIX systems are not supported.")
if(!enabled)
flags |= SS_NO_FIRE
can_fire = FALSE
@@ -90,7 +87,10 @@ SUBSYSTEM_DEF(fail2topic)
fail_counts -= ip
rate_limiting -= ip
. = shell("netsh advfirewall firewall add rule name=\"[rule_name]\" dir=in interface=any action=block remoteip=[ip]")
if (world.system_type == UNIX)
. = shell("iptables -A [rule_name] -s [ip] -j DROP")
else
. = shell("netsh advfirewall firewall add rule name=\"[rule_name]\" dir=in interface=any action=block remoteip=[ip]")
if (.)
subsystem_log("Failed to ban [ip]. Exit code: [.].")
@@ -105,7 +105,10 @@ SUBSYSTEM_DEF(fail2topic)
active_bans = list()
. = shell("netsh advfirewall firewall delete rule name=\"[rule_name]\"")
if (world.system_type == UNIX)
. = shell("iptables -F [rule_name]") //Let's just assume that folks running linux are smart enough to have a dedicated chain configured for this.
else
. = shell("netsh advfirewall firewall delete rule name=\"[rule_name]\"")
if (.)
subsystem_log("Failed to drop firewall rule. Exit code: [.].")
@@ -86,7 +86,6 @@ PROCESSING_SUBSYSTEM_DEF(circuit)
circuit_fabricator_recipe_list["Tools"] = list(
/obj/item/integrated_electronics/wirer,
/obj/item/integrated_electronics/debugger,
/obj/item/integrated_electronics/analyzer,
/obj/item/integrated_electronics/detailer,
/obj/item/card/data,
/obj/item/card/data/full_color,
+1 -1
View File
@@ -763,7 +763,7 @@ SUBSYSTEM_DEF(vote)
remove_from_client()
Remove(owner)
/datum/action/vote/IsAvailable()
/datum/action/vote/IsAvailable(silent = FALSE)
return 1
/datum/action/vote/proc/remove_from_client()
+51 -52
View File
@@ -6,13 +6,15 @@
/datum/action
var/name = "Generic Action"
var/desc = null
var/obj/target = null
var/atom/target = null
var/check_flags = 0
var/required_mobility_flags = MOBILITY_USE
var/processing = FALSE
var/obj/screen/movable/action_button/button = null
var/buttontooltipstyle = ""
var/transparent_when_unavailable = TRUE
var/use_target_appearance = FALSE
var/list/target_appearance_matrix //if set, will be used to transform the target button appearance as an arglist.
var/button_icon = 'icons/mob/actions/backgrounds.dmi' //This is the file for the BACKGROUND icon
var/background_icon_state = ACTION_BUTTON_DEFAULT_BACKGROUND //And this is the state for the background icon
@@ -88,14 +90,14 @@
/datum/action/proc/Trigger()
if(!IsAvailable())
return FALSE
if(SEND_SIGNAL(src, COMSIG_ACTION_TRIGGER, src) & COMPONENT_ACTION_BLOCK_TRIGGER)
if(SEND_SIGNAL(src, COMSIG_ACTION_TRIGGER, target) & COMPONENT_ACTION_BLOCK_TRIGGER)
return FALSE
return TRUE
/datum/action/proc/Process()
return
/datum/action/proc/IsAvailable()
/datum/action/proc/IsAvailable(silent = FALSE)
if(!owner)
return FALSE
var/mob/living/L = owner
@@ -116,29 +118,42 @@
return TRUE
/datum/action/proc/UpdateButtonIcon(status_only = FALSE, force = FALSE)
if(button)
if(!status_only)
button.name = name
button.desc = desc
if(owner && owner.hud_used && background_icon_state == ACTION_BUTTON_DEFAULT_BACKGROUND)
var/list/settings = owner.hud_used.get_action_buttons_icons()
if(button.icon != settings["bg_icon"])
button.icon = settings["bg_icon"]
if(button.icon_state != settings["bg_state"])
button.icon_state = settings["bg_state"]
else
if(button.icon != button_icon)
button.icon = button_icon
if(button.icon_state != background_icon_state)
button.icon_state = background_icon_state
if(!button)
return
if(!status_only)
button.name = name
button.desc = desc
if(owner && owner.hud_used && background_icon_state == ACTION_BUTTON_DEFAULT_BACKGROUND)
var/list/settings = owner.hud_used.get_action_buttons_icons()
if(button.icon != settings["bg_icon"])
button.icon = settings["bg_icon"]
if(button.icon_state != settings["bg_state"])
button.icon_state = settings["bg_state"]
else
if(button.icon != button_icon)
button.icon = button_icon
if(button.icon_state != background_icon_state)
button.icon_state = background_icon_state
if(!use_target_appearance)
ApplyIcon(button, force)
if(!IsAvailable())
button.color = transparent_when_unavailable ? rgb(128,0,0,128) : rgb(128,0,0)
else
button.color = rgb(255,255,255,255)
return 1
else if(target && button.appearance_cache != target.appearance) //replace with /ref comparison if this is not valid.
var/mutable_appearance/M = new(target)
M.layer = FLOAT_LAYER
M.plane = FLOAT_PLANE
if(target_appearance_matrix)
var/list/L = target_appearance_matrix
M.transform = matrix(L[1], L[2], L[3], L[4], L[5], L[6])
button.cut_overlays()
button.add_overlay(M)
button.appearance_cache = target.appearance
if(!IsAvailable(TRUE))
button.color = transparent_when_unavailable ? rgb(128,0,0,128) : rgb(128,0,0)
else
button.color = rgb(255,255,255,255)
return 1
/datum/action/proc/ApplyIcon(obj/screen/movable/action_button/current_button, force = FALSE)
if(icon_icon && button_icon_state && ((current_button.button_icon_state != button_icon_state) || force))
@@ -165,6 +180,7 @@
/datum/action/item_action
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_LYING|AB_CHECK_CONSCIOUS
button_icon_state = null
use_target_appearance = TRUE
// If you want to override the normal icon being the item
// then change this to an icon state
@@ -188,23 +204,6 @@
I.ui_action_click(owner, src)
return 1
/datum/action/item_action/ApplyIcon(obj/screen/movable/action_button/current_button, force)
if(button_icon && button_icon_state)
// If set, use the custom icon that we set instead
// of the item appearence
..()
else if(target && current_button.appearance_cache != target.appearance) //replace with /ref comparison if this is not valid.
var/obj/item/I = target
var/old_layer = I.layer
var/old_plane = I.plane
I.layer = FLOAT_LAYER //AAAH
I.plane = FLOAT_PLANE //^ what that guy said
current_button.cut_overlays()
current_button.add_overlay(I)
I.layer = old_layer
I.plane = old_plane
current_button.appearance_cache = I.appearance
/datum/action/item_action/toggle_light
name = "Toggle Light"
@@ -308,7 +307,7 @@
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "vortex_recall"
/datum/action/item_action/vortex_recall/IsAvailable()
/datum/action/item_action/vortex_recall/IsAvailable(silent = FALSE)
if(istype(target, /obj/item/hierophant_club))
var/obj/item/hierophant_club/H = target
if(H.teleporting)
@@ -320,7 +319,7 @@
background_icon_state = "bg_clock"
buttontooltipstyle = "clockcult"
/datum/action/item_action/clock/IsAvailable()
/datum/action/item_action/clock/IsAvailable(silent = FALSE)
if(!is_servant_of_ratvar(owner))
return 0
return ..()
@@ -329,7 +328,7 @@
name = "Create Judicial Marker"
desc = "Allows you to create a stunning Judicial Marker at any location in view. Click again to disable."
/datum/action/item_action/clock/toggle_visor/IsAvailable()
/datum/action/item_action/clock/toggle_visor/IsAvailable(silent = FALSE)
if(!is_servant_of_ratvar(owner))
return 0
if(istype(target, /obj/item/clothing/glasses/judicial_visor))
@@ -408,7 +407,7 @@
/datum/action/item_action/jetpack_stabilization
name = "Toggle Jetpack Stabilization"
/datum/action/item_action/jetpack_stabilization/IsAvailable()
/datum/action/item_action/jetpack_stabilization/IsAvailable(silent = FALSE)
var/obj/item/tank/jetpack/J = target
if(!istype(J) || !J.on)
return 0
@@ -465,7 +464,7 @@
/datum/action/item_action/organ_action
check_flags = AB_CHECK_CONSCIOUS
/datum/action/item_action/organ_action/IsAvailable()
/datum/action/item_action/organ_action/IsAvailable(silent = FALSE)
var/obj/item/organ/I = target
if(!I.owner)
return 0
@@ -634,32 +633,32 @@
return FALSE
if(target)
var/obj/effect/proc_holder/S = target
S.Click()
S.Trigger(usr)
return TRUE
/datum/action/spell_action/IsAvailable()
/datum/action/spell_action/IsAvailable(silent = FALSE)
if(!target)
return FALSE
return TRUE
/datum/action/spell_action/spell
/datum/action/spell_action/spell/IsAvailable()
/datum/action/spell_action/spell/IsAvailable(silent = FALSE)
if(!target)
return FALSE
var/obj/effect/proc_holder/spell/S = target
if(owner)
return S.can_cast(owner, FALSE, TRUE)
return S.can_cast(owner, FALSE, silent)
return FALSE
/datum/action/spell_action/alien
/datum/action/spell_action/alien/IsAvailable()
/datum/action/spell_action/alien/IsAvailable(silent = FALSE)
if(!target)
return FALSE
var/obj/effect/proc_holder/alien/ab = target
if(owner)
return ab.cost_check(ab.check_turf,owner,1)
return ab.cost_check(ab.check_turf,owner,silent)
return FALSE
@@ -701,7 +700,7 @@
button.maptext_width = 24
button.maptext_height = 12
/datum/action/cooldown/IsAvailable()
/datum/action/cooldown/IsAvailable(silent = FALSE)
return next_use_time <= world.time
/datum/action/cooldown/proc/StartCooldown()
@@ -294,3 +294,12 @@
/obj/item/bedsheet/cosmos = 1)
time = 60
category = CAT_CLOTHING
/datum/crafting_recipe/garlic_necklace
name = "Garlic Necklace"
result = /obj/item/clothing/neck/garlic_necklace
reqs = list(/obj/item/reagent_containers/food/snacks/grown/garlic = 15,
/obj/item/stack/cable_coil = 10)
time = 100 //Takes awhile to put all the garlics on the coil and knot it.
category = CAT_CLOTHING
+4 -4
View File
@@ -1,8 +1,8 @@
//This component applies a customizable drop_shadow filter to its wearer when they toggle combat mode on or off. This can stack.
/datum/component/wearertargeting/phantomthief
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
signals = list(COMSIG_LIVING_COMBAT_ENABLED)
dupe_mode = COMPONENT_DUPE_ALLOWED
signals = list(COMSIG_LIVING_COMBAT_ENABLED, COMSIG_LIVING_COMBAT_DISABLED)
proctype = .proc/handlefilterstuff
var/filter_x
var/filter_y
@@ -19,8 +19,8 @@
filter_color = _color
valid_slots = _valid_slots
/datum/component/wearertargeting/phantomthief/proc/handlefilterstuff(datum/source, mob/user, combatmodestate)
if(!combatmodestate)
/datum/component/wearertargeting/phantomthief/proc/handlefilterstuff(mob/living/user, was_forced = FALSE)
if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
user.remove_filter("phantomthief")
else
user.add_filter("phantomthief", 4, list(type = "drop_shadow", x = filter_x, y = filter_y, size = filter_size, color = filter_color))
@@ -60,7 +60,7 @@
_contents_limbo = null
if(_user_limbo)
for(var/i in _user_limbo)
show_to(i)
ui_show(i)
_user_limbo = null
/datum/component/storage/concrete/_insert_physical_item(obj/item/I, override = FALSE)
@@ -18,7 +18,7 @@
return
. = COMPONENT_NO_ATTACK_HAND
if(!check_locked(source, user, TRUE))
show_to(user)
ui_show(user)
A.do_jiggle()
if(rustle_sound)
playsound(A, "rustle", 50, 1, -5)
@@ -3,6 +3,7 @@
allow_quick_gather = TRUE
allow_quick_empty = TRUE
click_gather = TRUE
storage_flags = STORAGE_FLAGS_LEGACY_DEFAULT
max_w_class = WEIGHT_CLASS_NORMAL
max_combined_w_class = 100
max_items = 100
@@ -1,6 +1,7 @@
//Stack-only storage.
/datum/component/storage/concrete/stack
display_numerical_stacking = TRUE
storage_flags = STORAGE_FLAGS_LEGACY_DEFAULT
var/max_combined_stack_amount = 300
max_w_class = WEIGHT_CLASS_NORMAL
max_combined_w_class = WEIGHT_CLASS_NORMAL * 14
+74 -149
View File
@@ -21,9 +21,16 @@
var/locked = FALSE //when locked nothing can see inside or use it.
var/max_w_class = WEIGHT_CLASS_SMALL //max size of objects that will fit.
var/max_combined_w_class = 14 //max combined sizes of objects that will fit.
var/max_items = 7 //max number of objects that will fit.
/// Storage flags, including what kinds of limiters we use for how many items we can hold
var/storage_flags = STORAGE_FLAGS_LEGACY_DEFAULT
/// Max w_class we can hold. Applies to [STORAGE_LIMIT_COMBINED_W_CLASS] and [STORAGE_LIMIT_VOLUME]
var/max_w_class = WEIGHT_CLASS_SMALL
/// Max combined w_class. Applies to [STORAGE_LIMIT_COMBINED_W_CLASS]
var/max_combined_w_class = WEIGHT_CLASS_SMALL * 7
/// Max items we can hold. Applies to [STORAGE_LIMIT_MAX_ITEMS]
var/max_items = 7
/// Max volume we can hold. Applies to [STORAGE_LIMIT_VOLUME]. Auto scaled on New() if unset.
var/max_volume
var/emp_shielded = FALSE
@@ -39,8 +46,17 @@
var/display_numerical_stacking = FALSE //stack things of the same type and show as a single object with a number.
var/obj/screen/storage/boxes //storage display object
var/obj/screen/close/closer //close button object
/// "legacy"/default view mode's storage "boxes"
var/obj/screen/storage/boxes/ui_boxes
/// New volumetric storage display mode's left side
var/obj/screen/storage/left/ui_left
/// New volumetric storage display mode's center 'blocks'
var/obj/screen/storage/continuous/ui_continuous
/// The close button, used in all modes. Frames right side in volumetric mode.
var/obj/screen/storage/close/ui_close
/// Associative list of list(item = screen object) for volumetric storage item screen blocks
var/list/ui_item_blocks
var/current_maxscreensize
var/allow_big_nesting = FALSE //allow storage objects of the same or greater size.
@@ -68,9 +84,6 @@
return COMPONENT_INCOMPATIBLE
if(master)
change_master(master)
boxes = new(null, src)
closer = new(null, src)
orient2hud()
RegisterSignal(parent, COMSIG_CONTAINS_STORAGE, .proc/on_check)
RegisterSignal(parent, COMSIG_IS_STORAGE_LOCKED, .proc/check_locked)
@@ -111,8 +124,15 @@
/datum/component/storage/Destroy()
close_all()
QDEL_NULL(boxes)
QDEL_NULL(closer)
QDEL_NULL(ui_boxes)
QDEL_NULL(ui_close)
QDEL_NULL(ui_continuous)
QDEL_NULL(ui_left)
// DO NOT USE QDEL_LIST_ASSOC.
if(ui_item_blocks)
for(var/i in ui_item_blocks)
qdel(ui_item_blocks[i]) //qdel the screen object not the item
ui_item_blocks.Cut()
LAZYCLEARLIST(is_using)
return ..()
@@ -286,7 +306,7 @@
if(!_target)
_target = get_turf(parent)
if(usr)
hide_from(usr)
ui_hide(usr)
var/list/contents = contents()
var/atom/real_location = real_location()
for(var/obj/item/I in contents)
@@ -300,106 +320,8 @@
if(check_locked())
close_all()
/datum/component/storage/proc/_process_numerical_display()
. = list()
for(var/obj/item/I in accessible_items())
if(QDELETED(I))
continue
if(!.[I.type])
.[I.type] = new /datum/numbered_display(I, 1)
else
var/datum/numbered_display/ND = .[I.type]
ND.number++
. = sortTim(., /proc/cmp_numbered_displays_name_asc, associative = TRUE)
//This proc determines the size of the inventory to be displayed. Please touch it only if you know what you're doing.
/datum/component/storage/proc/orient2hud(mob/user, maxcolumns)
var/list/accessible_contents = accessible_items()
var/adjusted_contents = length(accessible_contents)
//Numbered contents display
var/list/datum/numbered_display/numbered_contents
if(display_numerical_stacking)
numbered_contents = _process_numerical_display()
adjusted_contents = numbered_contents.len
var/columns = CLAMP(max_items, 1, maxcolumns ? maxcolumns : screen_max_columns)
var/rows = CLAMP(CEILING(adjusted_contents / columns, 1), 1, screen_max_rows)
standard_orient_objs(rows, columns, numbered_contents)
//This proc draws out the inventory and places the items on it. It uses the standard position.
/datum/component/storage/proc/standard_orient_objs(rows, cols, list/obj/item/numerical_display_contents)
boxes.screen_loc = "[screen_start_x]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x+cols-1]:[screen_pixel_x],[screen_start_y+rows-1]:[screen_pixel_y]"
var/cx = screen_start_x
var/cy = screen_start_y
if(islist(numerical_display_contents))
for(var/type in numerical_display_contents)
var/datum/numbered_display/ND = numerical_display_contents[type]
ND.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE
ND.sample_object.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
ND.sample_object.maptext = "<font color='white'>[(ND.number > 1)? "[ND.number]" : ""]</font>"
ND.sample_object.layer = ABOVE_HUD_LAYER
ND.sample_object.plane = ABOVE_HUD_PLANE
cx++
if(cx - screen_start_x >= cols)
cx = screen_start_x
cy++
if(cy - screen_start_y >= rows)
break
else
for(var/obj/O in accessible_items())
if(QDELETED(O))
continue
O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip"
O.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
O.maptext = ""
O.layer = ABOVE_HUD_LAYER
O.plane = ABOVE_HUD_PLANE
cx++
if(cx - screen_start_x >= cols)
cx = screen_start_x
cy++
if(cy - screen_start_y >= rows)
break
closer.screen_loc = "[screen_start_x + cols]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y]"
/datum/component/storage/proc/show_to(mob/M, set_screen_size = TRUE)
if(!M.client)
return FALSE
var/list/cview = getviewsize(M.client.view)
var/maxallowedscreensize = cview[1]-8
if(set_screen_size)
current_maxscreensize = maxallowedscreensize
else if(current_maxscreensize)
maxallowedscreensize = current_maxscreensize
if(M.active_storage != src && (M.stat == CONSCIOUS))
for(var/obj/item/I in accessible_items())
if(I.on_found(M))
return FALSE
if(M.active_storage)
M.active_storage.hide_from(M)
orient2hud(M, (isliving(M) ? maxallowedscreensize : 7))
M.client.screen |= boxes
M.client.screen |= closer
M.client.screen |= accessible_items()
M.active_storage = src
LAZYOR(is_using, M)
return TRUE
/datum/component/storage/proc/hide_from(mob/M)
if(!M.client)
return TRUE
var/atom/real_location = real_location()
M.client.screen -= boxes
M.client.screen -= closer
M.client.screen -= real_location.contents
if(M.active_storage == src)
M.active_storage = null
LAZYREMOVE(is_using, M)
return TRUE
/datum/component/storage/proc/close(mob/M)
hide_from(M)
ui_hide(M)
/datum/component/storage/proc/close_all()
. = FALSE
@@ -418,25 +340,6 @@
var/datum/component/storage/concrete/master = master()
master.emp_act(source, severity)
//This proc draws out the inventory and places the items on it. tx and ty are the upper left tile and mx, my are the bottm right.
//The numbers are calculated from the bottom-left The bottom-left slot being 1,1.
/datum/component/storage/proc/orient_objs(tx, ty, mx, my)
var/atom/real_location = real_location()
var/cx = tx
var/cy = ty
boxes.screen_loc = "[tx]:,[ty] to [mx],[my]"
for(var/obj/O in real_location)
if(QDELETED(O))
continue
O.screen_loc = "[cx],[cy]"
O.layer = ABOVE_HUD_LAYER
O.plane = ABOVE_HUD_PLANE
cx++
if(cx > mx)
cx = tx
cy--
closer.screen_loc = "[mx+1],[my]"
//Resets something that is being removed from storage.
/datum/component/storage/proc/_removal_reset(atom/movable/thing)
if(!istype(thing))
@@ -448,6 +351,9 @@
/datum/component/storage/proc/_remove_and_refresh(datum/source, atom/movable/thing)
_removal_reset(thing)
if(LAZYACCESS(ui_item_blocks, thing))
qdel(ui_item_blocks[thing])
ui_item_blocks -= thing
refresh_mob_views()
//Call this proc to handle the removal of an item from the storage item. The item will be moved to the new_location target, if that is null it's being deleted
@@ -462,7 +368,7 @@
/datum/component/storage/proc/refresh_mob_views()
var/list/seeing = can_see_contents()
for(var/i in seeing)
show_to(i)
ui_show(i)
return TRUE
/datum/component/storage/proc/can_see_contents()
@@ -559,7 +465,7 @@
A.add_fingerprint(M)
if(!force && (check_locked(null, M) || !M.CanReach(parent, view_only = TRUE)))
return FALSE
show_to(M, !ghost)
ui_show(M, !ghost)
/datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M)
if(isitem(O))
@@ -587,10 +493,6 @@
if(M && !stop_messages)
host.add_fingerprint(M)
return FALSE
if(real_location.contents.len >= max_items)
if(!stop_messages)
to_chat(M, "<span class='warning'>[host] is full, make some space!</span>")
return FALSE //Storage item is full
if(length(can_hold))
if(!is_type_in_typecache(I, can_hold))
if(!stop_messages)
@@ -600,17 +502,34 @@
if(!stop_messages)
to_chat(M, "<span class='warning'>[host] cannot hold [I]!</span>")
return FALSE
if(I.w_class > max_w_class)
if(!stop_messages)
to_chat(M, "<span class='warning'>[I] is too big for [host]!</span>")
return FALSE
var/sum_w_class = I.w_class
for(var/obj/item/_I in real_location)
sum_w_class += _I.w_class //Adds up the combined w_classes which will be in the storage item if the item is added to it.
if(sum_w_class > max_combined_w_class)
if(!stop_messages)
to_chat(M, "<span class='warning'>[I] won't fit in [host], make some space!</span>")
return FALSE
// STORAGE LIMITS
if(storage_flags & STORAGE_LIMIT_MAX_ITEMS)
if(real_location.contents.len >= max_items)
if(!stop_messages)
to_chat(M, "<span class='warning'>[host] has too many things in it, make some space!</span>")
return FALSE //Storage item is full
if(storage_flags & STORAGE_LIMIT_MAX_W_CLASS)
if(I.w_class > max_w_class)
if(!stop_messages)
to_chat(M, "<span class='warning'>[I] is too long for [host]!</span>")
return FALSE
if(storage_flags & STORAGE_LIMIT_COMBINED_W_CLASS)
var/sum_w_class = I.w_class
for(var/obj/item/_I in real_location)
sum_w_class += _I.w_class //Adds up the combined w_classes which will be in the storage item if the item is added to it.
if(sum_w_class > max_combined_w_class)
if(!stop_messages)
to_chat(M, "<span class='warning'>[I] won't fit in [host], make some space!</span>")
return FALSE
if(storage_flags & STORAGE_LIMIT_VOLUME)
var/sum_volume = I.get_w_volume()
for(var/obj/item/_I in real_location)
sum_volume += _I.get_w_volume()
if(sum_volume > get_max_volume())
if(!stop_messages)
to_chat(M, "<span class='warning'>[I] is too spacious to fit in [host], make some space!</span>")
return FALSE
/////////////////
if(isitem(host))
var/obj/item/IP = host
var/datum/component/storage/STR_I = I.GetComponent(/datum/component/storage)
@@ -742,7 +661,7 @@
if(A.loc == user)
. = COMPONENT_NO_ATTACK_HAND
if(!check_locked(source, user, TRUE))
show_to(user)
ui_show(user)
A.do_jiggle()
/datum/component/storage/proc/signal_on_pickup(datum/source, mob/user)
@@ -761,7 +680,7 @@
return do_quick_empty(loctarget)
/datum/component/storage/proc/signal_hide_attempt(datum/source, mob/target)
return hide_from(target)
return ui_hide(target)
/datum/component/storage/proc/on_alt_click(datum/source, mob/user)
if(!isliving(user) || !user.CanReach(parent))
@@ -790,7 +709,7 @@
user.visible_message("<span class='warning'>[user] draws [I] from [parent]!</span>", "<span class='notice'>You draw [I] from [parent].</span>")
return TRUE
/datum/component/storage/proc/action_trigger(datum/signal_source, datum/action/source)
/datum/component/storage/proc/action_trigger(datum/action/source, obj/target)
gather_mode_switch(source.owner)
return COMPONENT_ACTION_BLOCK_TRIGGER
@@ -803,3 +722,9 @@
to_chat(user, "[parent] now picks up all items in a tile at once.")
if(COLLECT_ONE)
to_chat(user, "[parent] now picks up one item at a time.")
/**
* Gets our max volume
*/
/datum/component/storage/proc/get_max_volume()
return max_volume || AUTO_SCALE_STORAGE_VOLUME(max_w_class, max_combined_w_class)
+293
View File
@@ -0,0 +1,293 @@
/**
* Generates a list of numbered_display datums for the numerical display system.
*/
/datum/component/storage/proc/_process_numerical_display()
. = list()
for(var/obj/item/I in accessible_items())
if(QDELETED(I))
continue
if(!.[I.type])
.[I.type] = new /datum/numbered_display(I, 1)
else
var/datum/numbered_display/ND = .[I.type]
ND.number++
. = sortTim(., /proc/cmp_numbered_displays_name_asc, associative = TRUE)
/**
* Orients all objects in legacy mode, and returns the objects to show to the user.
*/
/datum/component/storage/proc/orient2hud_legacy(mob/user, maxcolumns)
. = list()
var/list/accessible_contents = accessible_items()
var/adjusted_contents = length(accessible_contents)
//Numbered contents display
var/list/datum/numbered_display/numbered_contents
if(display_numerical_stacking)
numbered_contents = _process_numerical_display()
adjusted_contents = numbered_contents.len
var/columns = CLAMP(max_items, 1, maxcolumns ? maxcolumns : screen_max_columns)
var/rows = CLAMP(CEILING(adjusted_contents / columns, 1), 1, screen_max_rows)
// First, boxes.
ui_boxes = get_ui_boxes()
ui_boxes.screen_loc = "[screen_start_x]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x+columns-1]:[screen_pixel_x],[screen_start_y+rows-1]:[screen_pixel_y]"
. += ui_boxes
// Then, closer.
ui_close = get_ui_close()
ui_close.screen_loc = "[screen_start_x + columns]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y]"
. += ui_close
// Then orient the actual items.
var/cx = screen_start_x
var/cy = screen_start_y
if(islist(numbered_contents))
for(var/type in numbered_contents)
var/datum/numbered_display/ND = numbered_contents[type]
ND.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE
ND.sample_object.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
ND.sample_object.maptext = "<font color='white'>[(ND.number > 1)? "[ND.number]" : ""]</font>"
ND.sample_object.layer = ABOVE_HUD_LAYER
ND.sample_object.plane = ABOVE_HUD_PLANE
. += ND.sample_object
cx++
if(cx - screen_start_x >= columns)
cx = screen_start_x
cy++
if(cy - screen_start_y >= rows)
break
else
for(var/obj/O in accessible_items())
if(QDELETED(O))
continue
O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip"
O.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
O.maptext = ""
O.layer = ABOVE_HUD_LAYER
O.plane = ABOVE_HUD_PLANE
. += O
cx++
if(cx - screen_start_x >= columns)
cx = screen_start_x
cy++
if(cy - screen_start_y >= rows)
break
/**
* Orients all objects in .. volumetric mode. Does not support numerical display!
*/
/datum/component/storage/proc/orient2hud_volumetric(mob/user, maxcolumns)
. = list()
// Generate ui_item_blocks for missing ones and render+orient.
var/list/atom/contents = accessible_items()
// our volume
var/our_volume = get_max_volume()
var/horizontal_pixels = (maxcolumns * world.icon_size) - (VOLUMETRIC_STORAGE_EDGE_PADDING * 2)
var/max_horizontal_pixels = horizontal_pixels * screen_max_rows
// sigh loopmania time
var/used = 0
// define outside for performance
var/volume
var/list/volume_by_item = list()
var/list/percentage_by_item = list()
for(var/obj/item/I in contents)
volume = I.get_w_volume()
used += volume
volume_by_item[I] = volume
percentage_by_item[I] = volume / get_max_volume()
var/padding_pixels = ((length(percentage_by_item) - 1) * VOLUMETRIC_STORAGE_ITEM_PADDING) + VOLUMETRIC_STORAGE_EDGE_PADDING * 2
var/min_pixels = (MINIMUM_PIXELS_PER_ITEM * length(percentage_by_item)) + padding_pixels
// do the check for fallback for when someone has too much gamer gear
if((min_pixels) > (max_horizontal_pixels + 4)) // 4 pixel grace zone
to_chat(user, "<span class='warning'>[parent] was showed to you in legacy mode due to your items overrunning the three row limit! Consider not carrying too much or bugging a maintainer to raise this limit!</span>")
return orient2hud_legacy(user, maxcolumns)
// after this point we are sure we can somehow fit all items into our max number of rows.
// determine rows
var/rows = CLAMP(CEILING(min_pixels / horizontal_pixels, 1), 1, screen_max_rows)
var/overrun = FALSE
if(used > our_volume)
// congratulations we are now in overrun mode. everything will be crammed to minimum storage pixels.
to_chat(user, "<span class='warning'>[parent] rendered in overrun mode due to more items inside than the maximum volume supports.</span>")
overrun = TRUE
// how much we are using
var/using_horizontal_pixels = horizontal_pixels * rows
// item padding
using_horizontal_pixels -= padding_pixels
// define outside for marginal performance boost
var/obj/item/I
// start at this pixel from screen_start_x.
var/current_pixel = VOLUMETRIC_STORAGE_EDGE_PADDING
var/row = 1
LAZYINITLIST(ui_item_blocks)
for(var/i in percentage_by_item)
I = i
var/percent = percentage_by_item[I]
if(!ui_item_blocks[I])
ui_item_blocks[I] = new /obj/screen/storage/volumetric_box/center(null, src, I)
var/obj/screen/storage/volumetric_box/center/B = ui_item_blocks[I]
var/pixels_to_use = overrun? MINIMUM_PIXELS_PER_ITEM : max(using_horizontal_pixels * percent, MINIMUM_PIXELS_PER_ITEM)
var/addrow = FALSE
if(CEILING(pixels_to_use, 1) >= FLOOR(horizontal_pixels - current_pixel - VOLUMETRIC_STORAGE_EDGE_PADDING, 1))
pixels_to_use = horizontal_pixels - current_pixel - VOLUMETRIC_STORAGE_EDGE_PADDING
addrow = TRUE
// now that we have pixels_to_use, place our thing and add it to the returned list.
B.screen_loc = I.screen_loc = "[screen_start_x]:[round(current_pixel + (pixels_to_use * 0.5) + VOLUMETRIC_STORAGE_ITEM_PADDING, 1)],[screen_start_y+row-1]:[screen_pixel_y]"
// add the used pixels to pixel after we place the object
current_pixel += pixels_to_use + VOLUMETRIC_STORAGE_ITEM_PADDING
// set various things
B.set_pixel_size(pixels_to_use)
B.layer = VOLUMETRIC_STORAGE_BOX_LAYER
B.plane = VOLUMETRIC_STORAGE_BOX_PLANE
B.name = I.name
I.mouse_opacity = MOUSE_OPACITY_ICON
I.maptext = ""
I.layer = VOLUMETRIC_STORAGE_ITEM_LAYER
I.plane = VOLUMETRIC_STORAGE_ITEM_PLANE
// finally add our things.
. += B.on_screen_objects()
. += I
// go up a row if needed
if(addrow)
row++
current_pixel = VOLUMETRIC_STORAGE_EDGE_PADDING
// Then, continuous section.
ui_continuous = get_ui_continuous()
ui_continuous.screen_loc = "[screen_start_x]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x+maxcolumns-1]:[screen_pixel_x],[screen_start_y+rows-1]:[screen_pixel_y]"
. += ui_continuous
// Then, left.
ui_left = get_ui_left()
ui_left.screen_loc = "[screen_start_x]:[screen_pixel_x - 2],[screen_start_y]:[screen_pixel_y] to [screen_start_x]:[screen_pixel_x - 2],[screen_start_y+rows-1]:[screen_pixel_y]"
. += ui_left
// Then, closer, which is also our right element.
ui_close = get_ui_close()
ui_close.screen_loc = "[screen_start_x + maxcolumns]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x + maxcolumns]:[screen_pixel_x],[screen_start_y + row - 1]:[screen_pixel_y]"
. += ui_close
/**
* Shows our UI to a mob.
*/
/datum/component/storage/proc/ui_show(mob/M, set_screen_size = TRUE)
if(!M.client)
return FALSE
var/list/cview = getviewsize(M.client.view)
// in tiles
var/maxallowedscreensize = cview[1]-8
if(set_screen_size)
current_maxscreensize = maxallowedscreensize
else if(current_maxscreensize)
maxallowedscreensize = current_maxscreensize
// we got screen size, register signal
RegisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT, .proc/on_logout, override = TRUE)
if(M.active_storage != src)
if(M.active_storage)
M.active_storage.ui_hide(M)
M.active_storage = src
LAZYOR(is_using, M)
if(volumetric_ui())
//new volumetric ui bay-style
M.client.screen |= orient2hud_volumetric(M, maxallowedscreensize)
else
//old ui
M.client.screen |= orient2hud_legacy(M, maxallowedscreensize)
return TRUE
/**
* VV hooked to ensure no lingering screen objects.
*/
/datum/component/storage/vv_edit_var(var_name, var_value)
var/list/old
if(var_name == NAMEOF(src, storage_flags))
old = is_using.Copy()
for(var/i in is_using)
ui_hide(i)
. = ..()
if(old)
for(var/i in old)
ui_show(i)
/**
* Proc triggered by signal to ensure logging out clients don't linger.
*/
/datum/component/storage/proc/on_logout(datum/source, client/C)
ui_hide(source)
/**
* Hides our UI from a mob
*/
/datum/component/storage/proc/ui_hide(mob/M)
if(!M.client)
return TRUE
UnregisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT)
M.client.screen -= list(ui_boxes, ui_close, ui_left, ui_continuous) + get_ui_item_objects_hide()
if(M.active_storage == src)
M.active_storage = null
LAZYREMOVE(is_using, M)
return TRUE
/**
* Returns TRUE if we are using volumetric UI instead of box UI
*/
/datum/component/storage/proc/volumetric_ui()
var/atom/real_location = real_location()
return (storage_flags & STORAGE_LIMIT_VOLUME) && (length(real_location.contents) <= MAXIMUM_VOLUMETRIC_ITEMS) && !display_numerical_stacking
/**
* Gets the ui item objects to ui_hide.
*/
/datum/component/storage/proc/get_ui_item_objects_hide()
if(!volumetric_ui())
var/atom/real_location = real_location()
return real_location.contents
else
. = list()
for(var/i in ui_item_blocks)
// get both the box and the item
. += ui_item_blocks[i]
. += i
/**
* Gets our ui_boxes, making it if it doesn't exist.
*/
/datum/component/storage/proc/get_ui_boxes()
if(!ui_boxes)
ui_boxes = new(null, src)
return ui_boxes
/**
* Gets our ui_left, making it if it doesn't exist.
*/
/datum/component/storage/proc/get_ui_left()
if(!ui_left)
ui_left = new(null, src)
return ui_left
/**
* Gets our ui_close, making it if it doesn't exist.
*/
/datum/component/storage/proc/get_ui_close()
if(!ui_close)
ui_close = new(null, src)
return ui_close
/**
* Gets our ui_continuous, making it if it doesn't exist.
*/
/datum/component/storage/proc/get_ui_continuous()
if(!ui_continuous)
ui_continuous = new(null, src)
return ui_continuous
+1 -1
View File
@@ -176,7 +176,7 @@
/**
*The following procs simply acts as hooks for quit(), since components do not use callbacks anymore
*/
/datum/component/virtual_reality/proc/action_trigger(datum/signal_source, datum/action/source)
/datum/component/virtual_reality/proc/action_trigger(datum/action/source, obj/target)
quit()
return COMPONENT_ACTION_BLOCK_TRIGGER
+1 -1
View File
@@ -19,7 +19,7 @@
dashing_item = dasher
holder = user
/datum/action/innate/dash/IsAvailable()
/datum/action/innate/dash/IsAvailable(silent = FALSE)
if(current_charges > 0)
return TRUE
else
+17
View File
@@ -41,6 +41,7 @@
/datum/dna/proc/transfer_identity(mob/living/carbon/destination, transfer_SE = 0)
if(!istype(destination))
return
var/old_size = destination.dna.features["body_size"]
destination.dna.unique_enzymes = unique_enzymes
destination.dna.uni_identity = uni_identity
destination.dna.blood_type = blood_type
@@ -56,6 +57,8 @@
if(transfer_SE)
destination.dna.mutation_index = mutation_index
destination.dna.update_body_size(old_size)
SEND_SIGNAL(destination, COMSIG_CARBON_IDENTITY_TRANSFERRED_TO, src, transfer_SE)
/datum/dna/proc/copy_dna(datum/dna/new_dna)
@@ -368,7 +371,9 @@
/mob/living/carbon/human/proc/hardset_dna(ui, list/mutation_index, newreal_name, newblood_type, datum/species/mrace, newfeatures)
if(newfeatures)
var/old_size = dna.features["body_size"]
dna.features = newfeatures
dna.update_body_size(old_size)
if(mrace)
var/datum/species/newrace = new mrace.type
@@ -644,3 +649,15 @@
gib()
else
set_species(/datum/species/dullahan)
/datum/dna/proc/update_body_size(old_size)
if(!holder || features["body_size"] == old_size)
return
holder.resize = features["body_size"] / old_size
holder.update_transform()
var/danger = CONFIG_GET(number/threshold_body_size_slowdown)
if(features["body_size"] < danger)
var/slowdown = 1 + round(danger/features["body_size"], 0.1) * CONFIG_GET(number/body_size_slowdown_multiplier)
holder.add_movespeed_modifier(MOVESPEED_ID_SMALL_STRIDE, TRUE, 100, NONE, TRUE, slowdown, ALL, FLOATING|CRAWLING)
else if(old_size < danger)
holder.remove_movespeed_modifier(MOVESPEED_ID_SMALL_STRIDE)
+187
View File
@@ -0,0 +1,187 @@
#define POLYCHROMIC_ALTCLICK (1<<0)
#define POLYCHROMIC_ACTION (1<<1)
#define POLYCHROMIC_NO_HELD (1<<2)
#define POLYCHROMIC_NO_WORN (1<<3)
/datum/element/polychromic
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 3
var/overlays_states //A list or a number of states. In the latter case, the atom icon_state/item_state will be used followed by a number.
var/list/colors_by_atom = list() //list of color strings or mutable appearances, depending on the above variable.
var/icon_file
var/worn_file //used in place of items' held or mob overlay icons if present.
var/list/overlays_names //wrap numbers into text strings please.
var/list/actions_by_atom = list()
var/poly_flags
var/static/list/suits_with_helmet_typecache = typecacheof(list(/obj/item/clothing/suit/hooded, /obj/item/clothing/suit/space/hardsuit))
var/list/helmet_by_suit = list() //because poly winter coats exist.
var/list/suit_by_helmet = list() //Idem.
/datum/element/polychromic/Attach(datum/target, list/colors, states, _flags = POLYCHROMIC_ACTION|POLYCHROMIC_NO_HELD, _icon, _worn, list/names = list("Primary", "Secondary", "Tertiary", "Quaternary", "Quinary", "Senary"))
. = ..()
var/make_appearances = islist(states)
var/states_len = make_appearances ? length(states) : states
var/names_len = length(names)
if(!states_len || !names_len || colors_by_atom[target] || !isatom(target))
return ELEMENT_INCOMPATIBLE
var/atom/A = target
overlays_states = states
icon_file = _icon
worn_file = _worn
poly_flags = _flags
var/mut_icon = icon_file || A.icon
var/list/L = list()
for(var/I in 1 to states_len)
var/col = LAZYACCESS(colors, I) || "#FFFFFF"
L += make_appearances ? mutable_appearance(mut_icon, overlays_states[I], color = col) : col
colors_by_atom[A] = L
RegisterSignal(A, COMSIG_ATOM_UPDATE_OVERLAYS, .proc/apply_overlays)
if(_flags & POLYCHROMIC_ALTCLICK)
RegisterSignal(A, COMSIG_PARENT_EXAMINE, .proc/on_examine)
RegisterSignal(A, COMSIG_CLICK_ALT, .proc/set_color)
if(!overlays_names && names) //generate
overlays_names = names
var/diff = states_len - names_len
if(diff > 0)
for(var/i in 1 to diff)
overlays_names += "[names_len + i]°"
else if(diff < 0)
overlays_names.len += diff
if(isitem(A))
if(_flags & POLYCHROMIC_ACTION)
RegisterSignal(A, COMSIG_ITEM_EQUIPPED, .proc/grant_user_action)
RegisterSignal(A, COMSIG_ITEM_DROPPED, .proc/remove_user_action)
if(!(_flags & POLYCHROMIC_NO_WORN) || !(_flags & POLYCHROMIC_NO_HELD))
A.AddElement(/datum/element/update_icon_updates_onmob)
RegisterSignal(A, COMSIG_ITEM_WORN_OVERLAYS, .proc/apply_worn_overlays)
if(suits_with_helmet_typecache[A.type])
RegisterSignal(A, COMSIG_SUIT_MADE_HELMET, .proc/register_helmet)
else if(_flags & POLYCHROMIC_ACTION && ismob(A)) //in the event mob update icon procs are ever standarized.
var/datum/action/polychromic/P = new(A)
RegisterSignal(P, COMSIG_ACTION_TRIGGER, .proc/activate_action)
actions_by_atom[A] = P
P.Grant(A)
A.update_icon() //apply the overlays.
/datum/element/polychromic/Detach(atom/A)
. = ..()
colors_by_atom -= A
var/datum/action/polychromic/P = actions_by_atom[A]
if(P)
actions_by_atom -= A
qdel(P)
UnregisterSignal(A, list(COMSIG_PARENT_EXAMINE, COMSIG_CLICK_ALT, COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_ITEM_WORN_OVERLAYS, COMSIG_SUIT_MADE_HELMET))
if(isitem(A))
var/obj/item/clothing/head/H = helmet_by_suit[A]
if(H)
UnregisterSignal(H, list(COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_ITEM_WORN_OVERLAYS, COMSIG_PARENT_QDELETING))
helmet_by_suit -= A
suit_by_helmet -= H
colors_by_atom -= H
if(!QDELETED(H))
H.update_icon() //removing the overlays
if(!(poly_flags & POLYCHROMIC_NO_WORN) || !(poly_flags & POLYCHROMIC_NO_HELD))
A.RemoveElement(/datum/element/update_icon_updates_onmob)
if(!QDELETED(A) && ismob(A.loc))
var/mob/M = A.loc
if(!(poly_flags & POLYCHROMIC_NO_HELD) && M.is_holding(A))
M.update_inv_hands()
else if(!(poly_flags & POLYCHROMIC_NO_WORN))
M.regenerate_icons()
if(!QDELETED(A))
A.update_icon() //removing the overlays
/datum/element/polychromic/proc/apply_overlays(atom/source, list/overlays)
var/list/L = colors_by_atom[source]
var/f_icon = icon_file || source.icon
if(isnum(overlays_states))
for(var/i in 1 to overlays_states)
overlays += mutable_appearance(f_icon, "[source.icon_state]-[i]", color = L[i])
else
overlays += colors_by_atom[source]
/datum/element/polychromic/proc/apply_worn_overlays(obj/item/source, isinhands, icon, used_state, style_flags, list/overlays)
if(poly_flags & (isinhands ? POLYCHROMIC_NO_HELD : POLYCHROMIC_NO_WORN))
return
var/f_icon = worn_file || icon
var/list/L = colors_by_atom[source]
if(isnum(overlays_states))
for(var/i in 1 to overlays_states)
overlays += mutable_appearance(f_icon, "[used_state]-[i]", color = L[i])
else
for(var/i in 1 to length(overlays_states))
var/mutable_appearance/M = L[i]
overlays += mutable_appearance(f_icon, overlays_states[i], color = M.color)
/datum/element/polychromic/proc/set_color(atom/source, mob/user)
var/choice = input(user,"Polychromic options", "Recolor [source]") as null|anything in overlays_names
if(!choice || QDELETED(source) || !user.canUseTopic(source, BE_CLOSE, NO_DEXTERY))
return
var/index = overlays_names.Find(choice)
var/list/L = colors_by_atom[source]
if(!L) // Ummmmmh.
return
var/mutable_appearance/M = L[index]
var/old_color = istype(M) ? M.color : M
var/ncolor = input(user, "Polychromic options", "Choose [choice] Color", old_color) as color|null
if(!ncolor || QDELETED(source) || !colors_by_atom[source] || !user.canUseTopic(source, BE_CLOSE, NO_DEXTERY))
return
ncolor = sanitize_hexcolor(ncolor, 6, TRUE, old_color)
if(istype(M))
M.color = ncolor
else
L[index] = ncolor
source.update_icon()
return TRUE
/datum/element/polychromic/proc/grant_user_action(atom/source, mob/user, slot)
if(slot == SLOT_IN_BACKPACK || slot == SLOT_LEGCUFFED || slot == SLOT_HANDCUFFED || slot == SLOT_GENERC_DEXTROUS_STORAGE)
return
var/datum/action/polychromic/P = actions_by_atom[source]
if(!P)
P = new (source)
P.name = "Modify [source]'\s Colors"
actions_by_atom[source] = P
P.check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
RegisterSignal(P, COMSIG_ACTION_TRIGGER, .proc/activate_action)
P.Grant(user)
/datum/element/polychromic/proc/remove_user_action(atom/source, mob/user)
var/datum/action/polychromic/P = actions_by_atom[source]
P?.Remove(user)
/datum/element/polychromic/proc/activate_action(datum/action/source, atom/target)
set_color(target, source.owner)
/datum/element/polychromic/proc/on_examine(atom/source, mob/user, list/examine_list)
examine_list += "<span class='notice'>Alt-click to recolor it.</span>"
/datum/element/polychromic/proc/register_helmet(atom/source, obj/item/clothing/head/H)
suit_by_helmet[H] = source
helmet_by_suit[source] = H
colors_by_atom[H] = colors_by_atom[source]
RegisterSignal(H, COMSIG_ATOM_UPDATE_OVERLAYS, .proc/apply_overlays)
RegisterSignal(H, COMSIG_ITEM_WORN_OVERLAYS, .proc/apply_worn_overlays)
RegisterSignal(H, COMSIG_PARENT_QDELETING, .proc/unregister_helmet)
/datum/element/polychromic/proc/unregister_helmet(atom/source)
var/obj/item/clothing/suit/S = suit_by_helmet[source]
suit_by_helmet -= source
helmet_by_suit -= S
colors_by_atom -= source
/datum/action/polychromic
name = "Modify Polychromic Colors"
background_icon_state = "bg_polychromic"
use_target_appearance = TRUE
button_icon_state = null
target_appearance_matrix = list(0.8,0,0,0,0.8,0)
+6 -6
View File
@@ -12,7 +12,7 @@
RegisterSignal(target, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
RegisterSignal(target, COMSIG_ITEM_DROPPED, .proc/on_drop)
else if(ismob(target))
RegisterSignal(target, COMSIG_MOB_SPELL_CAST_CHECK, .proc/on_cast)
RegisterSignal(target, COMSIG_MOB_SPELL_CAN_CAST, .proc/on_cast)
stacked_spellcasting_by_user[target]++
else
return ELEMENT_INCOMPATIBLE
@@ -21,24 +21,24 @@
/datum/element/spellcasting/Detach(datum/target)
. = ..()
UnregisterSignal(target, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_MOB_SPELL_CAST_CHECK))
UnregisterSignal(target, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_MOB_SPELL_CAN_CAST))
if(users_by_item[target])
var/mob/user = users_by_item[target]
stacked_spellcasting_by_user[user]--
if(!stacked_spellcasting_by_user[user])
stacked_spellcasting_by_user -= user
UnregisterSignal(user, COMSIG_MOB_SPELL_CAST_CHECK)
UnregisterSignal(user, COMSIG_MOB_SPELL_CAN_CAST)
else if(ismob(target))
stacked_spellcasting_by_user[target]--
if(!stacked_spellcasting_by_user[target])
stacked_spellcasting_by_user -= target
/datum/element/spellcasting/proc/on_equip(datum/source, mob/equipper, slot)
if(!(slot in cast_slots))
if(!(cast_slots & slotdefine2slotbit(slot)))
return
users_by_item[source] = equipper
if(!stacked_spellcasting_by_user[equipper])
RegisterSignal(equipper, COMSIG_MOB_SPELL_CAST_CHECK, .proc/on_cast)
RegisterSignal(equipper, COMSIG_MOB_SPELL_CAN_CAST, .proc/on_cast)
stacked_spellcasting_by_user[equipper]++
/datum/element/spellcasting/proc/on_drop(datum/source, mob/user)
@@ -48,7 +48,7 @@
stacked_spellcasting_by_user[user]--
if(!stacked_spellcasting_by_user[user])
stacked_spellcasting_by_user -= user
UnregisterSignal(user, COMSIG_MOB_SPELL_CAST_CHECK)
UnregisterSignal(user, COMSIG_MOB_SPELL_CAN_CAST)
/datum/element/spellcasting/proc/on_cast(mob/caster, obj/effect/proc_holder/spell)
return cast_flags
@@ -5,7 +5,7 @@
. = ..()
if(!istype(target, /obj/item))
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_ATOM_UPDATED_ICON, .proc/update_onmob)
RegisterSignal(target, COMSIG_ATOM_UPDATED_ICON, .proc/update_onmob, override = TRUE)
/datum/element/update_icon_updates_onmob/proc/update_onmob(obj/item/target)
if(ismob(target.loc))
+1 -1
View File
@@ -128,7 +128,7 @@
// Can most things breathe?
if(trace_gases)
continue
if(A_gases[/datum/gas/oxygen] >= 16)
if(A_gases[/datum/gas/oxygen] <= 16)
continue
if(A_gases[/datum/gas/plasma])
continue
+4
View File
@@ -6,6 +6,8 @@
hidden = TRUE
var/obj/machinery/computer/holodeck/linked
var/list/compatible_holodeck_comps
var/abstract_type = /area/holodeck
var/restricted = 0 // if true, program goes on emag list
/*
@@ -53,6 +55,8 @@
*/
/area/holodeck/rec_center
name = "\improper Recreational Holodeck"
compatible_holodeck_comps = list(/obj/machinery/computer/holodeck)
abstract_type = /area/holodeck/rec_center
/area/holodeck/rec_center/offline
name = "Holodeck - Offline"
+1 -4
View File
@@ -584,11 +584,8 @@
stoplag(1)
qdel(progress)
to_chat(user, "<span class='notice'>You dump as much of [src_object.parent]'s contents into [STR.insert_preposition]to [src] as you can.</span>")
STR.orient2hud(user)
src_object.orient2hud(user)
if(user.active_storage) //refresh the HUD to show the transfered contents
user.active_storage.close(user)
user.active_storage.show_to(user)
user.active_storage.ui_show(user)
return TRUE
/atom/proc/get_dumping_location(obj/item/storage/source,mob/user)
+34 -55
View File
@@ -9,8 +9,14 @@
var/list/vassal_allowed_antags = list(/datum/antagonist/brother, /datum/antagonist/traitor, /datum/antagonist/traitor/internal_affairs, /datum/antagonist/survivalist, \
/datum/antagonist/rev, /datum/antagonist/nukeop, /datum/antagonist/pirate, /datum/antagonist/cult, /datum/antagonist/abductee, /datum/antagonist/valentine, /datum/antagonist/heartbreaker,)
// The antags you're allowed to be if turning Vassal.
/proc/isvamp(mob/living/M)
return istype(M) && M.mind && M.mind.has_antag_datum(/datum/antagonist/bloodsucker)
/proc/AmBloodsucker(mob/living/M, falseIfInDisguise = FALSE)
if(!M.mind)
return FALSE
// No Datum
if(!M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
return FALSE
return TRUE
/datum/game_mode/bloodsucker
name = "bloodsucker"
@@ -70,49 +76,27 @@
// Gamemode is all done being set up. We have all our Vamps. We now pick objectives and let them know what's happening.
/datum/game_mode/bloodsucker/post_setup()
// Sunlight (Creating Bloodsuckers manually will check to create this, too)
check_start_sunlight()
// Vamps
for(var/datum/mind/bloodsucker in bloodsuckers)
// spawn() --> Run block of code but game continues on past it.
// sleep() --> Run block of code and freeze code there (including whoever called us) until it's resolved.
//Clean Bloodsucker Species (racist?)
//clean_invalid_species(bloodsucker)
// TO-DO !!!
// Add Bloodsucker Antag Datum (or remove from list on Fail)
if (!make_bloodsucker(bloodsucker))
if(!make_bloodsucker(bloodsucker))
bloodsuckers -= bloodsucker
// NOTE: Hunters are done in ..() parent proc
return ..()
// Checking for ACTUALLY Dead Vamps
/datum/game_mode/bloodsucker/are_special_antags_dead()
// Bloodsucker not Final Dead
for(var/datum/mind/bloodsucker in bloodsuckers)
if(!bloodsucker.AmFinalDeath())
return FALSE
return TRUE
// Init Sunlight (called from datum_bloodsucker.on_gain(), in case game mode isn't even Bloodsucker
/datum/game_mode/proc/check_start_sunlight()
// Already Sunlight (and not about to cancel)
if (istype(bloodsucker_sunlight) && !bloodsucker_sunlight.cancel_me)
if(istype(bloodsucker_sunlight) && !bloodsucker_sunlight.cancel_me)
return
bloodsucker_sunlight = new ()
// End Sun (last bloodsucker removed)
/datum/game_mode/proc/check_cancel_sunlight()
// No Sunlight
if (!istype(bloodsucker_sunlight))
if(!istype(bloodsucker_sunlight))
return
if (bloodsuckers.len <= 0)
if(bloodsuckers.len <= 0)
bloodsucker_sunlight.cancel_me = TRUE
qdel(bloodsucker_sunlight)
bloodsucker_sunlight = null
@@ -151,43 +135,37 @@
// Not High Enough
if(creator)
var/datum/antagonist/bloodsucker/creator_bloodsucker = creator.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
if(!istype(creator_bloodsucker) || creator_bloodsucker.vamplevel < BLOODSUCKER_LEVEL_TO_EMBRACE)
if(!istype(creator_bloodsucker) || creator_bloodsucker.bloodsucker_level < BLOODSUCKER_LEVEL_TO_EMBRACE)
to_chat(creator, "<span class='danger'>Your blood is too thin to turn this corpse!</span>")
return FALSE
return TRUE
/datum/game_mode/proc/make_bloodsucker(datum/mind/bloodsucker, datum/mind/creator = null) // NOTE: This is a game_mode/proc, NOT a game_mode/bloodsucker/proc! We need to access this function despite the game mode.
if (!can_make_bloodsucker(bloodsucker))
if(!can_make_bloodsucker(bloodsucker))
return FALSE
// Create Datum: Fledgling
var/datum/antagonist/bloodsucker/A
// [FLEDGLING]
if (creator)
if(creator)
A = new (bloodsucker)
A.creator = creator
bloodsucker.add_antag_datum(A)
// Log
message_admins("[bloodsucker] has become a Bloodsucker, and was created by [creator].")
log_admin("[bloodsucker] has become a Bloodsucker, and was created by [creator].")
// [MASTER]
else
A = bloodsucker.add_antag_datum(ANTAG_DATUM_BLOODSUCKER)
return TRUE
/datum/game_mode/proc/remove_bloodsucker(datum/mind/bloodsucker)
bloodsucker.remove_antag_datum(ANTAG_DATUM_BLOODSUCKER)
/datum/game_mode/proc/clean_invalid_species(datum/mind/bloodsucker)
// Only checking for Humans here
if (!ishuman(bloodsucker.current) || !bloodsucker.current.client)
if(!ishuman(bloodsucker.current) || !bloodsucker.current.client)
return
var/am_valid = TRUE
var/mob/living/carbon/human/H = bloodsucker.current
@@ -202,7 +180,7 @@
// everyone will wonder why you're a human with Plasma clothes (jk they'll know you're antag)
// Convert to HUMAN (along with ID and PDA)
if (!am_valid)
if(!am_valid)
H.set_species(/datum/species/human)
H.real_name = H.client.prefs.custom_names["human"]
var/obj/item/card/id/ID = H.wear_id?.GetID()
@@ -211,12 +189,13 @@
ID.update_label()
/datum/game_mode/proc/can_make_vassal(mob/living/target, datum/mind/creator, display_warning=TRUE)//, check_antag_or_loyal=FALSE)
/datum/game_mode/proc/can_make_vassal(mob/living/target, datum/mind/creator, display_warning = TRUE)//, check_antag_or_loyal=FALSE)
// Not Correct Type: Abort
if (!iscarbon(target) || !creator)
if(!iscarbon(target) || !creator)
return FALSE
if (target.stat > UNCONSCIOUS)
if(target.stat > UNCONSCIOUS)
return FALSE
// Check Overdose: Am I even addicted to blood? Do I even have any in me?
//if (!target.reagents.addiction_list || !target.reagents.reagent_list)
//message_admins("DEBUG2: can_make_vassal() Abort: No reagents")
@@ -233,23 +212,23 @@
//message_admins("DEBUG4: can_make_vassal() Abort: No Blood")
// return 0
// No Mind!
if (!target.mind || !target.mind.key)
if (display_warning)
if(!target.mind || !target.mind.key)
if(display_warning)
to_chat(creator, "<span class='danger'>[target] isn't self-aware enough to be made into a Vassal.</span>")
return FALSE
// Already MY Vassal
var/datum/antagonist/vassal/V = target.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
if (istype(V) && V.master)
if (V.master.owner == creator)
if (display_warning)
if(istype(V) && V.master)
if(V.master.owner == creator)
if(display_warning)
to_chat(creator, "<span class='danger'>[target] is already your loyal Vassal!</span>")
else
if (display_warning)
if(display_warning)
to_chat(creator, "<span class='danger'>[target] is the loyal Vassal of another Bloodsucker!</span>")
return FALSE
// Already Antag or Loyal (Vamp Hunters count as antags)
if (target.mind.enslaved_to || AmInvalidAntag(target.mind)) //!VassalCheckAntagValid(target.mind, check_antag_or_loyal)) // HAS_TRAIT(target, TRAIT_MINDSHIELD, "implant") ||
if (display_warning)
if(target.mind.enslaved_to || AmInvalidAntag(target.mind)) //!VassalCheckAntagValid(target.mind, check_antag_or_loyal)) // HAS_TRAIT(target, TRAIT_MINDSHIELD, "implant") ||
if(display_warning)
to_chat(creator, "<span class='danger'>[target] resists the power of your blood to dominate their mind!</span>")
return FALSE
return TRUE
@@ -268,24 +247,24 @@
return FALSE
// Does even ONE antag appear in this mind that isn't in the list? Then FAIL!
for(var/datum/antagonist/antag_datum in M.antag_datums)
if (!(antag_datum.type in vassal_allowed_antags)) // vassal_allowed_antags is a list stored in the game mode, above.
if(!(antag_datum.type in vassal_allowed_antags)) // vassal_allowed_antags is a list stored in the game mode, above.
//message_admins("DEBUG VASSAL: Found Invalid: [antag_datum] // [antag_datum.type]")
return TRUE
//message_admins("DEBUG VASSAL: Valid Antags! (total of [M.antag_datums.len])")
// WHEN YOU DELETE THE ABOVE: Remove the 3 second timer on converting the vassal too.
return FALSE
/datum/game_mode/proc/make_vassal(mob/living/target, datum/mind/creator)
if (!can_make_vassal(target,creator))
/datum/game_mode/proc/make_vassal(var/mob/living/target, var/datum/mind/creator)
if(!can_make_vassal(target, creator))
return FALSE
// Make Vassal
var/datum/antagonist/vassal/V = new (target.mind)
var/datum/antagonist/vassal/V = new(target.mind)
var/datum/antagonist/bloodsucker/B = creator.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
V.master = B
target.mind.add_antag_datum(V, V.master.get_team())
// Update Bloodsucker Title (we're a daddy now)
B.SelectTitle(am_fledgling = FALSE) // Only works if you have no title yet.
// Log
// Log it
message_admins("[target] has become a Vassal, and is enslaved to [creator].")
log_admin("[target] has become a Vassal, and is enslaved to [creator].")
return TRUE
+2 -2
View File
@@ -144,6 +144,8 @@
return FALSE
if(clonemind.current.suiciding) // Mind is associated with a body that is suiciding.
return FALSE
if(AmBloodsucker(clonemind.current)) //If the mind is a bloodsucker
return FALSE
if(clonemind.active) //somebody is using that mind
if( ckey(clonemind.key)!=ckey )
return FALSE
@@ -159,8 +161,6 @@
mess = TRUE
update_icon()
return FALSE
if(isvamp(clonemind)) //If the mind is a bloodsucker
return FALSE
attempting = TRUE //One at a time!!
countdown.start()
+2 -2
View File
@@ -57,7 +57,7 @@
var/turf/target = locate(target_x, target_y, z)
ghost.forceMove(target)
/obj/machinery/launchpad/proc/isAvailable()
/obj/machinery/launchpad/proc/isAvailable(silent = FALSE)
if(stat & NOPOWER)
return FALSE
if(panel_open)
@@ -198,7 +198,7 @@
QDEL_NULL(briefcase)
return ..()
/obj/machinery/launchpad/briefcase/isAvailable()
/obj/machinery/launchpad/briefcase/isAvailable(silent = FALSE)
if(closed)
return FALSE
return ..()
+43 -18
View File
@@ -2,6 +2,7 @@
icon = 'icons/effects/landmarks_static.dmi'
icon_state = "random_loot"
layer = OBJ_LAYER
var/spawn_on_turf = TRUE
var/lootcount = 1 //how many items will be spawned
var/lootdoubles = TRUE //if the same item can be spawned twice
var/list/loot //a list of possible items to spawn e.g. list(/obj/item, /obj/structure, /obj/effect)
@@ -10,7 +11,7 @@
/obj/effect/spawner/lootdrop/Initialize(mapload)
..()
if(loot && loot.len)
var/turf/T = get_turf(src)
var/atom/A = spawn_on_turf ? get_turf(src) : loc
var/loot_spawned = 0
while((lootcount-loot_spawned) && loot.len)
var/lootspawn = pickweight(loot)
@@ -18,7 +19,7 @@
loot.Remove(lootspawn)
if(lootspawn)
var/atom/movable/spawned_loot = new lootspawn(T)
var/atom/movable/spawned_loot = new lootspawn(A)
if (!fan_out_items)
if (pixel_x != 0)
spawned_loot.pixel_x = pixel_x
@@ -449,6 +450,7 @@
/obj/effect/spawner/lootdrop/low_loot_toilet
name = "random low toilet spawner"
lootcount = 1
spawn_on_turf = FALSE
//Note this is out of a 100 - Meaning the number you see is also the percent its going to pick that
//This is ment for "low" loot that anyone could fine in a toilet, for better gear use high loot toilet
loot = list("" = 30,
@@ -458,41 +460,42 @@
/obj/item/clothing/glasses/sunglasses/blindfold = 4,
/obj/item/clothing/glasses/sunglasses = 1,
/obj/item/toy/plush/random = 5,
/obj/effect/spawner/lootdrop/gloves = 5,
/obj/effect/spawner/lootdrop/glowstick = 5,
/obj/effect/spawner/lootdrop/coin = 3,
/obj/effect/spawner/lootdrop/cig_packs = 10,
/obj/effect/spawner/lootdrop/cigars_cases = 2,
/obj/effect/spawner/lootdrop/space_cash = 5,
/obj/effect/spawner/lootdrop/gloves/no_turf = 5,
/obj/effect/spawner/lootdrop/glowstick/no_turf = 5,
/obj/effect/spawner/lootdrop/coin/no_turf = 3,
/obj/effect/spawner/lootdrop/cig_packs/no_turf = 10,
/obj/effect/spawner/lootdrop/cigars_cases/no_turf = 2,
/obj/effect/spawner/lootdrop/space_cash/no_turf = 5,
/obj/item/reagent_containers/food/snacks/grown/cannabis = 5,
/obj/item/storage/pill_bottle/dice = 5,
/obj/item/toy/cards/deck = 5,
/obj/effect/spawner/lootdrop/druggie_pill = 5
/obj/effect/spawner/lootdrop/druggie_pill/no_turf = 5
)
/obj/effect/spawner/lootdrop/prison_loot_toilet
name = "random prison toilet spawner"
lootcount = 1
spawn_on_turf = FALSE
//Note this is out of a 100 - Meaning the number you see is also the percent its going to pick that
//This is ment for "prison" loot that is rather rare and ment for "prisoners if they get a crowbar to fine, or sec.
loot = list("" = 10,
/obj/item/lighter = 5,
/obj/item/poster/random_contraband = 5,
/obj/item/clothing/glasses/sunglasses = 5,
/obj/effect/spawner/lootdrop/coin = 5,
/obj/effect/spawner/lootdrop/cig_packs = 10,
/obj/effect/spawner/lootdrop/cigars_cases = 5,
/obj/effect/spawner/lootdrop/coin/no_turf = 5,
/obj/effect/spawner/lootdrop/cig_packs/no_turf = 10,
/obj/effect/spawner/lootdrop/cigars_cases/no_turf = 5,
/obj/item/reagent_containers/food/snacks/grown/cannabis = 5,
/obj/item/storage/pill_bottle/dice = 5,
/obj/item/toy/cards/deck = 5,
/obj/effect/spawner/lootdrop/druggie_pill = 5,
/obj/effect/spawner/lootdrop/druggie_pill/no_turf = 5,
/obj/item/kitchen/knife = 5,
/obj/item/screwdriver = 5,
/obj/item/crowbar/red = 0.5, //Dont you need a crowbar to open this?
/obj/item/crowbar/red = 1, //Dont you need a crowbar to open this?
/obj/item/stack/medical/bruise_pack = 3,
/obj/item/reagent_containers/food/drinks/bottle/vodka = 2,
/obj/item/radio = 5,
/obj/item/flashlight = 4.5,
/obj/item/flashlight = 4,
/obj/item/clothing/mask/breath = 2,
/obj/item/tank/internals/emergency_oxygen = 3,
/obj/item/storage/box/mre/menu4/safe = 3,
@@ -502,13 +505,14 @@
/obj/effect/spawner/lootdrop/high_loot_toilet
name = "random high toilet spawner"
lootcount = 1
spawn_on_turf = FALSE
//Note this is out of a 100 - Meaning the number you see is also the percent its going to pick that
//The items inside are always going to be something usefull, illegal and likely traitorous.
loot = list(
/obj/item/clothing/glasses/sunglasses = 5,
/obj/effect/spawner/lootdrop/coin = 5,
/obj/effect/spawner/lootdrop/space_cash = 5,
/obj/effect/spawner/lootdrop/druggie_pill = 5,
/obj/effect/spawner/lootdrop/coin/no_turf = 5,
/obj/effect/spawner/lootdrop/space_cash/no_turf = 5,
/obj/effect/spawner/lootdrop/druggie_pill/no_turf = 5,
/obj/item/storage/fancy/cigarettes/cigpack_syndicate = 5,
/obj/item/suppressor = 5,
/obj/item/toy/cards/deck/syndicate = 5,
@@ -521,3 +525,24 @@
/obj/item/clothing/gloves/combat = 10,
/obj/item/clothing/shoes/sneakers/noslip = 10
)
/obj/effect/spawner/lootdrop/coin/no_turf
spawn_on_turf = FALSE
/obj/effect/spawner/lootdrop/space_cash/no_turf
spawn_on_turf = FALSE
/obj/effect/spawner/lootdrop/druggie_pill/no_turf
spawn_on_turf = FALSE
/obj/effect/spawner/lootdrop/gloves/no_turf
spawn_on_turf = FALSE
/obj/effect/spawner/lootdrop/cig_packs/no_turf
spawn_on_turf = FALSE
/obj/effect/spawner/lootdrop/cigars_cases/no_turf
spawn_on_turf = FALSE
/obj/effect/spawner/lootdrop/glowstick/no_turf
spawn_on_turf = FALSE
+16
View File
@@ -41,7 +41,11 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
var/hitsound = null
var/usesound = null
var/throwhitsound = null
/// Weight class for how much storage capacity it uses and how big it physically is meaning storages can't hold it if their maximum weight class isn't as high as it.
var/w_class = WEIGHT_CLASS_NORMAL
/// Volume override for the item, otherwise automatically calculated from w_class.
var/w_volume
/// The amount of stamina it takes to swing an item in a normal melee attack do not lie to me and say it's for realism because it ain't. If null it will autocalculate from w_class.
var/total_mass //Total mass in arbitrary pound-like values. If there's no balance reasons for an item to have otherwise, this var should be the item's weight in pounds.
@@ -434,6 +438,13 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
item_flags |= IN_INVENTORY
user.update_equipment_speed_mods()
//Overlays for the worn overlay so you can overlay while you overlay
//eg: ammo counters, primed grenade flashing, etc.
//"icon_file" is used automatically for inhands etc. to make sure it gets the right inhand file
/obj/item/proc/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
. = list()
SEND_SIGNAL(src, COMSIG_ITEM_WORN_OVERLAYS, isinhands, icon_file, used_state, style_flags, .)
//sometimes we only want to grant the item's action if it's equipped in a specific slot.
/obj/item/proc/item_action_slot_check(slot, mob/user, datum/action/A)
if(slot == SLOT_IN_BACKPACK || slot == SLOT_LEGCUFFED) //these aren't true slots, so avoid granting actions there
@@ -857,6 +868,11 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
return
return ..()
/// Get an item's volume that it uses when being stored.
/obj/item/proc/get_w_volume()
// if w_volume is 0 you fucked up anyways lol
return w_volume || AUTO_SCALE_VOLUME(w_class)
/obj/item/proc/embedded(mob/living/carbon/human/embedded_mob)
return
+1 -1
View File
@@ -113,7 +113,7 @@
cable_overlay.color = GLOB.cable_colors[colors[current_color_index]]
. += cable_overlay
/obj/item/twohanded/rcl/worn_overlays(isinhands, icon_file, style_flags = NONE)
/obj/item/twohanded/rcl/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
. = ..()
if(!isinhands || !(loaded?.amount))
return
+2 -2
View File
@@ -303,8 +303,8 @@
block_chance = 50
var/shield_icon = "shield-red"
/obj/item/nullrod/staff/worn_overlays(isinhands, icon_file, style_flags = NONE)
. = list()
/obj/item/nullrod/staff/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
. = ..()
if(isinhands)
. += mutable_appearance('icons/effects/effects.dmi', shield_icon, MOB_LAYER + 0.01)
+1 -1
View File
@@ -324,7 +324,7 @@
. = ..()
. += "<span class='notice'>Alt-click to recolor it.</span>"
/obj/item/melee/transforming/energy/sword/cx/worn_overlays(isinhands, icon_file, style_flags = NONE)
/obj/item/melee/transforming/energy/sword/cx/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
. = ..()
if(active)
if(isinhands)
@@ -663,21 +663,19 @@
var/maxReduction = 1
/obj/effect/proc_holder/silicon/cyborg/vtecControl/Click(mob/living/silicon/robot/user)
var/mob/living/silicon/robot/self = usr
/obj/effect/proc_holder/silicon/cyborg/vtecControl/Trigger(mob/living/silicon/robot/user)
currentState = (currentState + 1) % 3
if(istype(self))
if(istype(user))
switch(currentState)
if (0)
self.speed = initial(self.speed)
user.speed = initial(user.speed)
if (1)
self.speed = initial(self.speed) - maxReduction * 0.5
user.speed = initial(user.speed) - maxReduction * 0.5
if (2)
self.speed = initial(self.speed) - maxReduction * 1
user.speed = initial(user.speed) - maxReduction * 1
action.button_icon_state = "Chevron_State_[currentState]"
action.UpdateButtonIcon()
return
return TRUE
@@ -72,6 +72,8 @@
to_chat(user, "<span class='notice'> [M] is at full health.</span>")
return FALSE
user.visible_message("<span class='green'>[user] applies \the [src] on [M].</span>", "<span class='green'>You apply \the [src] on [M].</span>")
if(AmBloodsucker(M))
return
M.heal_bodypart_damage((heal_brute/2))
return TRUE
if(iscarbon(M))
@@ -148,6 +150,8 @@
return
if(iscarbon(M))
return heal_carbon(M, user, 0, heal_burn)
if(AmBloodsucker(M))
return
to_chat(user, "<span class='notice'>You can't heal [M] with the \the [src]!</span>")
/obj/item/stack/medical/ointment/suicide_act(mob/living/user)
+1 -1
View File
@@ -22,7 +22,7 @@
for(var/obj/item/I in contents)
. += I.get_belt_overlay()
/obj/item/storage/belt/worn_overlays(isinhands, icon_file, style_flags = NONE)
/obj/item/storage/belt/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
. = ..()
if(!isinhands && onmob_overlays)
for(var/obj/item/I in contents)
+2 -2
View File
@@ -376,8 +376,8 @@
filling.color = mix_color_from_reagents(reagents.reagent_list)
add_overlay(filling)
/obj/item/reagent_containers/chemtank/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE) //apply chemcolor and level
. = list()
/obj/item/reagent_containers/chemtank/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE) //apply chemcolor and level
. = ..()
//inhands + reagent_filling
if(!isinhands && reagents.total_volume)
var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "backpackmob-10")
+2 -2
View File
@@ -53,8 +53,8 @@
base_overlay.appearance_flags = RESET_COLOR
. += base_overlay
/obj/item/screwdriver/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE)
. = list()
/obj/item/screwdriver/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
. = ..()
if(isinhands && random_color)
var/mutable_appearance/M = mutable_appearance(icon_file, "screwdriver_head")
M.appearance_flags = RESET_COLOR
+1 -1
View File
@@ -349,7 +349,7 @@
update_light()
return TRUE
/obj/item/toy/sword/cx/worn_overlays(isinhands, icon_file, style_flags = NONE)
/obj/item/toy/sword/cx/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
. = ..()
if(active)
if(isinhands)
+1 -1
View File
@@ -531,7 +531,7 @@
update_light()
return TRUE
/obj/item/twohanded/dualsaber/hypereutactic/worn_overlays(isinhands, icon_file, style_flags = NONE)
/obj/item/twohanded/dualsaber/hypereutactic/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
. = ..()
if(isinhands)
var/mutable_appearance/gem_inhand = mutable_appearance(icon_file, "hypereutactic_gem")
+1 -1
View File
@@ -222,7 +222,7 @@ LINEN BINS
add_overlay(g_mouth)
add_overlay(g_eyes)
/obj/item/bedsheet/gondola/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE)
/obj/item/bedsheet/gondola/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
. = ..()
if(!isinhands)
. += mutable_appearance(icon_file, g_mouth)
+7 -1
View File
@@ -64,7 +64,7 @@
else
I.forceMove(drop_location())
to_chat(user, "<span class='notice'>You find [I] in the cistern.</span>")
w_items -= I.w_class
w_items = max(w_items - I.w_class, 0)
else
open = !open
update_icon()
@@ -115,6 +115,12 @@
secret.desc += "" //In case you want to add something to the item that spawns
contents += secret
/obj/structure/toilet/secret/LateInitialize()
. = ..()
w_items = 0 //recalculate total weight thanks to the secret.
for(var/obj/item/I in contents)
w_items += I.w_class
/obj/structure/toilet/secret/low_loot
secret_type = /obj/effect/spawner/lootdrop/low_loot_toilet
+25 -7
View File
@@ -173,6 +173,16 @@
/turf/closed/mineral/silver = 12, /turf/closed/mineral/plasma = 20, /turf/closed/mineral/iron = 40, /turf/closed/mineral/titanium = 11,
/turf/closed/mineral/gibtonite = 4, /turf/closed/mineral/bscrystal = 1)
/turf/closed/mineral/random/no_caves/earth_like
icon_state = "rock_oxy"
turf_type = /turf/open/floor/plating/asteroid
baseturfs = /turf/open/floor/plating/asteroid
mineralSpawnChanceList = list(/turf/closed/mineral/uranium/earth_like = 5, /turf/closed/mineral/diamond/earth_like = 1, /turf/closed/mineral/gold/earth_like = 10,
/turf/closed/mineral/silver/earth_like = 12, /turf/closed/mineral/plasma/earth_like = 20, /turf/closed/mineral/iron/earth_like = 40,
/turf/closed/mineral/titanium/earth_like = 11, /turf/closed/mineral/gibtonite/earth_like = 4, /turf/closed/mineral/bscrystal/earth_like = 1)
initial_gas_mix = OPENTURF_DEFAULT_ATMOS
defer_change = TRUE
/turf/closed/mineral/random/high_chance
icon_state = "rock_highchance"
mineralChance = 25
@@ -194,6 +204,10 @@
icon_state = "rock_highchance_oxy"
turf_type = /turf/open/floor/plating/asteroid
baseturfs = /turf/open/floor/plating/asteroid
mineralSpawnChanceList = list(
/turf/closed/mineral/uranium/earth_like = 35, /turf/closed/mineral/diamond/earth_like = 30, /turf/closed/mineral/gold/earth_like = 45,
/turf/closed/mineral/titanium/earth_like = 45, /turf/closed/mineral/silver/earth_like = 50, /turf/closed/mineral/plasma/earth_like = 50,
/turf/closed/mineral/bscrystal/earth_like = 20)
initial_gas_mix = OPENTURF_DEFAULT_ATMOS
defer_change = TRUE
@@ -211,6 +225,10 @@
turf_type = /turf/open/floor/plating/asteroid
baseturfs = /turf/open/floor/plating/asteroid
initial_gas_mix = OPENTURF_DEFAULT_ATMOS
mineralSpawnChanceList = list(
/turf/closed/mineral/uranium/earth_like = 2, /turf/closed/mineral/diamond/earth_like = 1, /turf/closed/mineral/gold/earth_like = 4,
/turf/closed/mineral/titanium/earth_like = 4, /turf/closed/mineral/silver/earth_like = 6, /turf/closed/mineral/plasma/earth_like = 15,
/turf/closed/mineral/iron/earth_like = 40, /turf/closed/mineral/gibtonite/earth_like = 2, /turf/closed/mineral/bscrystal/earth_like = 1)
defer_change = TRUE
/turf/closed/mineral/random/volcanic
@@ -246,13 +264,6 @@
/turf/closed/mineral/silver/volcanic = 20, /turf/closed/mineral/plasma/volcanic = 30, /turf/closed/mineral/bscrystal/volcanic = 1, /turf/closed/mineral/gibtonite/volcanic = 2,
/turf/closed/mineral/iron/volcanic = 95)
/turf/closed/mineral/random/earth_like
icon_state = "rock_oxy"
turf_type = /turf/open/floor/plating/asteroid
baseturfs = /turf/open/floor/plating/asteroid
initial_gas_mix = OPENTURF_DEFAULT_ATMOS
defer_change = TRUE
/turf/closed/mineral/iron
mineralType = /obj/item/stack/ore/iron
@@ -624,3 +635,10 @@
baseturfs = /turf/open/floor/plating/asteroid/basalt/lava_land_surface
initial_gas_mix = LAVALAND_DEFAULT_ATMOS
defer_change = 1
/turf/closed/mineral/gibtonite/earth_like
icon_state = "rock_oxy"
turf_type = /turf/open/floor/plating/asteroid
baseturfs = /turf/open/floor/plating/asteroid
initial_gas_mix = OPENTURF_DEFAULT_ATMOS
defer_change = TRUE
@@ -1,12 +1,6 @@
// INTEGRATION: Adding Procs and Datums to existing "classes"
/mob/living/proc/AmBloodsucker(falseIfInDisguise=FALSE)
// No Datum
if(!mind || !mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
return FALSE
return TRUE
/mob/living/proc/HaveBloodsuckerBodyparts(var/displaymessage="") // displaymessage can be something such as "rising from death" for Torpid Sleep. givewarningto is the person receiving messages.
/mob/living/proc/HaveBloodsuckerBodyparts(displaymessage = "") // displaymessage can be something such as "rising from death" for Torpid Sleep. givewarningto is the person receiving messages.
if(!getorganslot(ORGAN_SLOT_HEART))
if(displaymessage != "")
to_chat(src, "<span class='warning'>Without a heart, you are incapable of [displaymessage].</span>")
@@ -21,33 +15,6 @@
return FALSE
return TRUE
// GET DAMAGE
// Do NOT count the damage on prosthetics for this.
/mob/living/proc/getBruteLoss_nonProsthetic()
return getBruteLoss()
/mob/living/proc/getFireLoss_nonProsthetic()
return getFireLoss()
/mob/living/carbon/getBruteLoss_nonProsthetic()
var/amount = 0
for(var/obj/item/bodypart/BP in bodyparts)
if(BP.status < 2)
amount += BP.brute_dam
return amount
/mob/living/carbon/getFireLoss_nonProsthetic()
var/amount = 0
for(var/obj/item/bodypart/BP in bodyparts)
if(BP.status < 2)
amount += BP.burn_dam
return amount
/mob/living/carbon
// EXAMINING
/mob/living/carbon/human/proc/ReturnVampExamine(var/mob/viewer)
if(!mind || !viewer.mind)
@@ -12,10 +12,10 @@
/datum/antagonist/bloodsucker/proc/LifeTick()// Should probably run from life.dm, same as handle_changeling, but will be an utter pain to move
set waitfor = FALSE // Don't make on_gain() wait for this function to finish. This lets this code run on the side.
var/notice_healing = FALSE
var/notice_healing
while(owner && !AmFinalDeath()) // owner.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) == src
if(owner.current.stat == CONSCIOUS && !poweron_feed && !HAS_TRAIT(owner.current, TRAIT_DEATHCOMA)) // Deduct Blood
AddBloodVolume(-0.1) // -.15 (before tick went from 10 to 30, but we also charge more for faking life now)
AddBloodVolume(passive_blood_drain) // -.1 currently
if(HandleHealing(1)) // Heal
if(notice_healing == FALSE && owner.current.blood_volume > 0)
to_chat(owner, "<span class='notice'>The power of your blood begins knitting your wounds...</span>")
@@ -25,7 +25,7 @@
HandleStarving() // Death
HandleDeath() // Standard Update
update_hud()// Daytime Sleep in Coffin
if (SSticker.mode.is_daylight() && !HAS_TRAIT_FROM(owner.current, TRAIT_DEATHCOMA, "bloodsucker"))
if(SSticker.mode.is_daylight() && !HAS_TRAIT_FROM(owner.current, TRAIT_DEATHCOMA, "bloodsucker"))
if(istype(owner.current.loc, /obj/structure/closet/crate/coffin))
Torpor_Begin()
// Wait before next pass
@@ -39,12 +39,12 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/antagonist/bloodsucker/proc/AddBloodVolume(value)
owner.current.blood_volume = CLAMP(owner.current.blood_volume + value, 0, maxBloodVolume)
owner.current.blood_volume = CLAMP(owner.current.blood_volume + value, 0, max_blood_volume)
update_hud()
/datum/antagonist/bloodsucker/proc/HandleFeeding(mob/living/carbon/target, mult=1)
// mult: SILENT feed is 1/3 the amount
var/blood_taken = min(feedAmount, target.blood_volume) * mult // Starts at 15 (now 8 since we doubled the Feed time)
var/blood_taken = min(feed_amount, target.blood_volume) * mult // Starts at 15 (now 8 since we doubled the Feed time)
target.blood_volume -= blood_taken
// Simple Animals lose a LOT of blood, and take damage. This is to keep cats, cows, and so forth from giving you insane amounts of blood.
if(!ishuman(target))
@@ -82,38 +82,40 @@
/datum/antagonist/bloodsucker/proc/HandleHealing(mult = 1)
// NOTE: Mult of 0 is just a TEST to see if we are injured and need to go into Torpor!
//It is called from your coffin on close (by you only)
var/actual_regen = regen_rate + additional_regen
if(poweron_masquerade == TRUE || owner.current.AmStaked())
return FALSE
owner.current.adjustStaminaLoss(-1.5 + (regenRate * -7) * mult, 0) // Humans lose stamina damage really quickly. Vamps should heal more.
owner.current.adjustCloneLoss(-0.1 * (regenRate * 2) * mult, 0)
owner.current.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1 * (regenRate * 4) * mult) //adjustBrainLoss(-1 * (regenRate * 4) * mult, 0)
if(owner.current.reagents.has_reagent(/datum/reagent/consumable/garlic))
return FALSE
if(istype(owner.current.get_item_by_slot(SLOT_NECK), /obj/item/clothing/neck/garlic_necklace))
return FALSE
owner.current.adjustStaminaLoss(-1.5 + (actual_regen * -7) * mult, 0) // Humans lose stamina damage really quickly. Vamps should heal more.
owner.current.adjustCloneLoss(-0.1 * (actual_regen * 2) * mult, 0)
owner.current.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1 * (actual_regen * 4) * mult)
// No Bleeding
if(ishuman(owner.current)) //NOTE Current bleeding is horrible, not to count the amount of blood ballistics delete.
/*if(ishuman(owner.current)) //NOTE Current bleeding is horrible, not to count the amount of blood ballistics delete.
var/mob/living/carbon/human/H = owner.current
H.bleed_rate = 0
if(H.bleed_rate > 0) //Only heal bleeding if we are actually bleeding
H.bleed_rate =- 0.5 + actual_regen * 0.2 */
if(iscarbon(owner.current)) // Damage Heal: Do I have damage to ANY bodypart?
var/mob/living/carbon/C = owner.current
var/costMult = 1 // Coffin makes it cheaper
var/fireheal = 0 // BURN: Heal in Coffin while Fakedeath, or when damage above maxhealth (you can never fully heal fire)
var/amInCoffinWhileTorpor = istype(C.loc, /obj/structure/closet/crate/coffin) && (mult == 0 || HAS_TRAIT(C, TRAIT_DEATHCOMA)) // Check for mult 0 OR death coma. (mult 0 means we're testing from coffin)
var/amInCoffinWhileTorpor = istype(C.loc, /obj/structure/closet/crate/coffin) && (mult == 0 || HAS_TRAIT(C, TRAIT_FAKEDEATH)) // Check for mult 0 OR death coma. (mult 0 means we're testing from coffin)
if(amInCoffinWhileTorpor)
mult *= 4 // Increase multiplier if we're sleeping in a coffin.
fireheal = min(C.getFireLoss_nonProsthetic(), regenRate) // NOTE: Burn damage ONLY heals in torpor.
costMult = 0.25
fireheal = min(C.getFireLoss(), regen_rate) // NOTE: Burn damage ONLY heals in torpor.
C.ExtinguishMob()
CureDisabilities() // Extinguish Fire
C.remove_all_embedded_objects() // Remove Embedded!
owner.current.regenerate_organs() // Heal Organs (will respawn original eyes etc. but we replace right away, next)
CheckVampOrgans() // Heart, Eyes
else
if(owner.current.blood_volume <= 0) // No Blood? Lower Mult
mult = 0.25
// Crit from burn? Lower damage to maximum allowed.
//if (C.getFireLoss() > owner.current.getMaxHealth())
// fireheal = regenRate / 2
if(check_limbs(costMult))
return TRUE
// BRUTE: Always Heal
var/bruteheal = min(C.getBruteLoss_nonProsthetic(), regenRate)
var/toxinheal = min(C.getToxLoss(), regenRate)
var/bruteheal = min(C.getBruteLoss(), actual_regen)
var/toxinheal = min(C.getToxLoss(), actual_regen)
// Heal if Damaged
if(bruteheal + fireheal + toxinheal > 0) // Just a check? Don't heal/spend, and return.
if(mult == 0)
@@ -127,28 +129,29 @@
//C.heal_overall_damage(bruteheal * mult, fireheal * mult) // REMOVED: We need to FORCE this, because otherwise, vamps won't heal EVER. Swapped to above.
AddBloodVolume((bruteheal * -0.5 + fireheal * -1 + toxinheal * -0.2) / mult * costMult) // Costs blood to heal
return TRUE // Healed! Done for this tick.
if(amInCoffinWhileTorpor) // Limbs? (And I have no other healing)
var/list/missing = owner.current.get_missing_limbs() // Heal Missing
if (missing.len) // Cycle through ALL limbs and regen them!
for (var/targetLimbZone in missing) // 1) Find ONE Limb and regenerate it.
owner.current.regenerate_limb(targetLimbZone, 0) // regenerate_limbs() <--- If you want to EXCLUDE certain parts, do it like this ----> regenerate_limbs(0, list("head"))
var/obj/item/bodypart/L = owner.current.get_bodypart( targetLimbZone ) // 2) Limb returns Damaged
AddBloodVolume(50 * costMult) // Costs blood to heal
L.brute_dam = 60
to_chat(owner.current, "<span class='notice'>Your flesh knits as it regrows [L]!</span>")
playsound(owner.current, 'sound/magic/demon_consume.ogg', 50, 1)
// DONE! After regenerating ANY number of limbs, we stop here.
return TRUE
/*else // REMOVED: For now, let's just leave prosthetics on. Maybe you WANT to be a robovamp.
// Remove Prosthetic/False Limb
for(var/obj/item/bodypart/BP in C.bodyparts)
message_admins("T1: [BP] ")
if (istype(BP) && BP.status == 2)
message_admins("T2: [BP] ")
BP.drop_limb()
return TRUE */
// NOTE: Limbs have a "status", like their hosts "stat". 2 is dead (aka Prosthetic). 1 seems to be idle/alive.*/
return FALSE
/datum/antagonist/bloodsucker/proc/check_limbs(costMult)
var/limb_regen_cost = 50 * costMult
var/mob/living/carbon/C = owner.current
var/list/missing = C.get_missing_limbs()
if(missing.len && C.blood_volume < limb_regen_cost + 5)
return FALSE
for(var/targetLimbZone in missing) // 1) Find ONE Limb and regenerate it.
C.regenerate_limb(targetLimbZone, FALSE) // regenerate_limbs() <--- If you want to EXCLUDE certain parts, do it like this ----> regenerate_limbs(0, list("head"))
AddBloodVolume(50)
var/obj/item/bodypart/L = C.get_bodypart(targetLimbZone) // 2) Limb returns Damaged
L.brute_dam = 60
to_chat(C, "<span class='notice'>Your flesh knits as it regrows your [L]!</span>")
playsound(C, 'sound/magic/demon_consume.ogg', 50, TRUE)
return TRUE
/*for(var/obj/item/bodypart/BP in C.bodyparts)
if(!istype(BP) && !BP.status == 2)
return FALSE
to_chat(C, "<span class='notice'>Your body expels the [BP]!</span>")
BP.drop_limb()
return TRUE */
/datum/antagonist/bloodsucker/proc/CureDisabilities()
var/mob/living/carbon/C = owner.current
@@ -179,7 +182,16 @@
if(owner.current.blood_volume < BLOOD_VOLUME_BAD / 2)
owner.current.blur_eyes(8 - 8 * (owner.current.blood_volume / BLOOD_VOLUME_BAD))
// Nutrition
owner.current.nutrition = min(owner.current.blood_volume, NUTRITION_LEVEL_FED) // <-- 350 //NUTRITION_LEVEL_FULL
owner.current.nutrition = clamp(owner.current.blood_volume, 545, 0) //The amount of blood is how full we are.
//A bit higher regeneration based on blood volume
if(owner.current.blood_volume < 700)
additional_regen = 0.4
else if(owner.current.blood_volume < BLOOD_VOLUME_NORMAL)
additional_regen = 0.3
else if(owner.current.blood_volume < BLOOD_VOLUME_OKAY)
additional_regen = 0.2
else if(owner.current.blood_volume < BLOOD_VOLUME_BAD)
additional_regen = 0.1
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DEATH
@@ -189,7 +201,7 @@
/datum/antagonist/bloodsucker/proc/HandleDeath()
// FINAL DEATH
// Fire Damage? (above double health)
if(owner.current.getFireLoss_nonProsthetic() >= owner.current.maxHealth * 2.5)
if(owner.current.getFireLoss() >= owner.current.maxHealth * 3)
FinalDeath()
return
// Staked while "Temp Death" or Asleep
@@ -209,8 +221,8 @@
// for (var/datum/action/bloodsucker/masquerade/P in powers)
// P.Deactivate()
// TEMP DEATH
var/total_brute = owner.current.getBruteLoss_nonProsthetic()
var/total_burn = owner.current.getFireLoss_nonProsthetic()
var/total_brute = owner.current.getBruteLoss()
var/total_burn = owner.current.getFireLoss()
var/total_toxloss = owner.current.getToxLoss() //This is neater than just putting it in total_damage
var/total_damage = total_brute + total_burn + total_toxloss
// Died? Convert to Torpor (fake death)
@@ -218,27 +230,25 @@
Torpor_Begin()
to_chat(owner, "<span class='danger'>Your immortal body will not yet relinquish your soul to the abyss. You enter Torpor.</span>")
sleep(30) //To avoid spam
if (poweron_masquerade == TRUE)
if(poweron_masquerade == TRUE)
to_chat(owner, "<span class='warning'>Your wounds will not heal until you disable the <span class='boldnotice'>Masquerade</span> power.</span>")
// End Torpor:
else // No damage, OR toxin healed AND brute healed and NOT in coffin (since you cannot heal burn)
if(total_damage <= 0 || total_toxloss <= 0 && total_brute <= 0 && !istype(owner.current.loc, /obj/structure/closet/crate/coffin))
// Not Daytime, Not in Torpor
if(!SSticker.mode.is_daylight() && HAS_TRAIT_FROM(owner.current, TRAIT_DEATHCOMA, "bloodsucker"))
if(!SSticker.mode.is_daylight() && HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker"))
Torpor_End()
// Fake Unconscious
if(poweron_masquerade == TRUE && total_damage >= owner.current.getMaxHealth() - HEALTH_THRESHOLD_FULLCRIT)
owner.current.Unconscious(20,1)
//HEALTH_THRESHOLD_CRIT 0
//HEALTH_THRESHOLD_FULLCRIT -30
//HEALTH_THRESHOLD_DEAD -100
owner.current.Unconscious(20, 1)
/datum/antagonist/bloodsucker/proc/Torpor_Begin(amInCoffin=FALSE)
/datum/antagonist/bloodsucker/proc/Torpor_Begin(amInCoffin = FALSE)
owner.current.stat = UNCONSCIOUS
owner.current.fakedeath("bloodsucker") // Come after UNCONSCIOUS or else it fails
owner.current.apply_status_effect(STATUS_EFFECT_UNCONSCIOUS)
ADD_TRAIT(owner.current, TRAIT_FAKEDEATH, "bloodsucker") // Come after UNCONSCIOUS or else it fails
ADD_TRAIT(owner.current, TRAIT_NODEATH, "bloodsucker") // Without this, you'll just keep dying while you recover.
ADD_TRAIT(owner.current, TRAIT_RESISTHIGHPRESSURE, "bloodsucker") // So you can heal in 0 G. otherwise you just...heal forever.
ADD_TRAIT(owner.current, TRAIT_RESISTLOWPRESSURE, "bloodsucker") // So you can heal in 0 G. otherwise you just...heal forever.
ADD_TRAIT(owner.current, TRAIT_RESISTHIGHPRESSURE, "bloodsucker") // So you can heal in space. Otherwise you just...heal forever.
ADD_TRAIT(owner.current, TRAIT_RESISTLOWPRESSURE, "bloodsucker")
// Visuals
owner.current.update_sight()
owner.current.reload_fullscreen()
@@ -247,10 +257,10 @@
if(power.active && !power.can_use_in_torpor)
power.DeactivatePower()
/datum/antagonist/bloodsucker/proc/Torpor_End()
owner.current.stat = SOFT_CRIT
owner.current.cure_fakedeath("bloodsucker") // Come after SOFT_CRIT or else it fails
owner.current.remove_status_effect(STATUS_EFFECT_UNCONSCIOUS)
REMOVE_TRAIT(owner.current, TRAIT_FAKEDEATH, "bloodsucker")
REMOVE_TRAIT(owner.current, TRAIT_NODEATH, "bloodsucker")
REMOVE_TRAIT(owner.current, TRAIT_RESISTHIGHPRESSURE, "bloodsucker")
REMOVE_TRAIT(owner.current, TRAIT_RESISTLOWPRESSURE, "bloodsucker")
@@ -283,18 +293,19 @@
// Free my Vassals!
FreeAllVassals()
// Elders get Dusted
if(vamplevel >= 4) // (vamptitle)
if(bloodsucker_level >= 4) // (bloodsucker_title)
owner.current.visible_message("<span class='warning'>[owner.current]'s skin crackles and dries, their skin and bones withering to dust. A hollow cry whips from what is now a sandy pile of remains.</span>", \
"<span class='userdanger'>Your soul escapes your withering body as the abyss welcomes you to your Final Death.</span>", \
"<span class='italics'>You hear a dry, crackling sound.</span>")
sleep(50)
owner.current.dust()
// Fledglings get Gibbed
else
owner.current.visible_message("<span class='warning'>[owner.current]'s skin bursts forth in a spray of gore and detritus. A horrible cry echoes from what is now a wet pile of decaying meat.</span>", \
"<span class='userdanger'>Your soul escapes your withering body as the abyss welcomes you to your Final Death.</span>", \
"<span class='italics'>You hear a wet, bursting sound.</span>")
owner.current.gib(TRUE, FALSE, FALSE)//Brain cloning is wierd and allows hellbounds. Lets destroy the brain for safety.
playsound(owner.current.loc, 'sound/effects/tendril_destroyed.ogg', 40, 1)
owner.current.gib(TRUE, FALSE, FALSE) //Brain cloning is wierd and allows hellbounds. Lets destroy the brain for safety.
playsound(owner.current, 'sound/effects/tendril_destroyed.ogg', 40, TRUE)
@@ -304,15 +315,15 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/mob/proc/CheckBloodsuckerEatFood(var/food_nutrition)
if (!isliving(src))
/mob/proc/CheckBloodsuckerEatFood(food_nutrition)
if(!isliving(src))
return
var/mob/living/L = src
if(!L.AmBloodsucker())
if(!AmBloodsucker(L))
return
// We're a vamp? Try to eat food...
var/datum/antagonist/bloodsucker/bloodsuckerdatum = mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
bloodsuckerdatum.handle_eat_human_food(food_nutrition)
// We're a bloodsucker? Try to eat food...
var/datum/antagonist/bloodsucker/B = L.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
B.handle_eat_human_food(food_nutrition)
/datum/antagonist/bloodsucker/proc/handle_eat_human_food(food_nutrition, puke_blood = TRUE, masquerade_override) // Called from snacks.dm and drinks.dm
@@ -93,6 +93,14 @@
if(display_error)
to_chat(owner, "<span class='warning'>You have a stake in your chest! Your powers are useless.</span>")
return FALSE
if(istype(owner.get_item_by_slot(SLOT_NECK), /obj/item/clothing/neck/garlic_necklace))
if(display_error)
to_chat(owner, "<span class='warning'The necklace on your neck is interfering with your powers!</span>")
return FALSE
if(owner.reagents?.has_reagent(/datum/reagent/consumable/garlic))
if(display_error)
to_chat(owner, "<span class='warning'>Garlic in your blood is interfering with your powers!</span>")
return FALSE
// Incap?
if(must_be_capacitated)
var/mob/living/L = owner
@@ -141,23 +141,23 @@
if(!bloodsuckerdatum.warn_sun_locker)
to_chat(M, "<span class='warning'>Your skin sizzles. The [M.current.loc] doesn't protect well against UV bombardment.</span>")
bloodsuckerdatum.warn_sun_locker = TRUE
M.current.adjustFireLoss(0.5 + bloodsuckerdatum.vamplevel / 2) // M.current.fireloss += 0.5 + bloodsuckerdatum.vamplevel / 2 // Do DIRECT damage. Being spaced was causing this to not occur. setFireLoss(bloodsuckerdatum.vamplevel)
M.current.adjustFireLoss(0.5 + bloodsuckerdatum.bloodsucker_level / 2) // M.current.fireloss += 0.5 + bloodsuckerdatum.bloodsucker_level / 2 // Do DIRECT damage. Being spaced was causing this to not occur. setFireLoss(bloodsuckerdatum.bloodsucker_level)
M.current.updatehealth()
SEND_SIGNAL(M.current, COMSIG_ADD_MOOD_EVENT, "vampsleep", /datum/mood_event/daylight_1)
// Out in the Open? Buh Bye
else
if(!bloodsuckerdatum.warn_sun_burn)
if(bloodsuckerdatum.vamplevel > 0)
if(bloodsuckerdatum.bloodsucker_level > 0)
to_chat(M, "<span class='userdanger'>The solar flare sets your skin ablaze!</span>")
else
to_chat(M, "<span class='userdanger'>The solar flare scalds your neophyte skin!</span>")
bloodsuckerdatum.warn_sun_burn = TRUE
if(M.current.fire_stacks <= 0)
M.current.fire_stacks = 0
if(bloodsuckerdatum.vamplevel > 0)
M.current.adjust_fire_stacks(0.2 + bloodsuckerdatum.vamplevel / 10)
if(bloodsuckerdatum.bloodsucker_level > 0)
M.current.adjust_fire_stacks(0.2 + bloodsuckerdatum.bloodsucker_level / 10)
M.current.IgniteMob()
M.current.adjustFireLoss(2 + bloodsuckerdatum.vamplevel) // M.current.fireloss += 2 + bloodsuckerdatum.vamplevel // Do DIRECT damage. Being spaced was causing this to not occur. //setFireLoss(2 + bloodsuckerdatum.vamplevel)
M.current.adjustFireLoss(2 + bloodsuckerdatum.bloodsucker_level) // M.current.fireloss += 2 + bloodsuckerdatum.bloodsucker_level // Do DIRECT damage. Being spaced was causing this to not occur. //setFireLoss(2 + bloodsuckerdatum.bloodsucker_level)
M.current.updatehealth()
SEND_SIGNAL(M.current, COMSIG_ADD_MOOD_EVENT, "vampsleep", /datum/mood_event/daylight_2)
@@ -8,9 +8,9 @@
job_rank = ROLE_BLOODSUCKER
threat = 5
// NAME
var/vampname // My Dracula name
var/vamptitle // My Dracula title
var/vampreputation // My "Surname" or description of my deeds
var/bloodsucker_name // My Dracula style name
var/bloodsucker_title // My Dracula style title
var/bloodsucker_reputation // My "Surname" or description of my deeds
// CLAN
var/datum/team/vampireclan/clan
var/list/datum/antagonist/vassal/vassals = list()// Vassals under my control. Periodically remove the dead ones.
@@ -20,35 +20,33 @@
var/poweron_feed = FALSE // Am I feeding?
var/poweron_masquerade = FALSE
// STATS
var/vamplevel = 0
var/vamplevel_unspent = 1
var/regenRate = 0.4 // How many points of Brute do I heal per tick?
var/feedAmount = 15 // Amount of blood drawn from a target per tick.
var/maxBloodVolume = 600 // Maximum blood a Vamp can hold via feeding. // BLOOD_VOLUME_NORMAL 550 // BLOOD_VOLUME_SAFE 475 //BLOOD_VOLUME_OKAY 336 //BLOOD_VOLUME_BAD 224 // BLOOD_VOLUME_SURVIVE 122
var/bloodsucker_level
var/bloodsucker_level_unspent = 1
var/regen_rate = 0.3 // How fast do I regenerate?
var/additional_regen // How much additional blood regen we gain from bonuses such as high blood.
var/feed_amount = 15 // Amount of blood drawn from a target per tick.
var/max_blood_volume = 600 // Maximum blood a Vamp can hold via feeding.
// OBJECTIVES
var/list/datum/objective/objectives_given = list() // For removal if needed.
var/area/lair
var/obj/structure/closet/crate/coffin
// TRACKING
var/foodInGut = 0 // How much food to throw up later. You shouldn't have eaten that.
var/warn_sun_locker = FALSE // So we only get the locker burn message once per day.
var/warn_sun_burn = FALSE // So we only get the sun burn message once per day.
var/had_toxlover = FALSE
var/foodInGut // How much food to throw up later. You shouldn't have eaten that.
var/warn_sun_locker // So we only get the locker burn message once per day.
var/warn_sun_burn // So we only get the sun burn message once per day.
var/had_toxlover
var/level_bloodcost
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)
// NOTES: TRAIT_AGEUSIA <-- Doesn't like flavors.
// REMOVED: TRAIT_NODEATH
// TO ADD:
//var/static/list/defaultOrgans = list (/obj/item/organ/heart/vampheart,/obj/item/organ/heart/vampeyes)
/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)
SSticker.mode.check_start_sunlight()// Start Sunlight? (if first Vamp)
SelectFirstName()// Name & Title
SelectTitle(am_fledgling=TRUE) // If I have a creator, then set as Fledgling.
SelectReputation(am_fledgling=TRUE)
SelectTitle(am_fledgling = TRUE) // If I have a creator, then set as Fledgling.
SelectReputation(am_fledgling = TRUE)
AssignStarterPowersAndStats()// Give Powers & Stats
forge_bloodsucker_objectives()// Objectives & Team
update_bloodsucker_icons_added(owner.current, "bloodsucker") // Add Antag HUD
@@ -68,18 +66,18 @@
/datum/antagonist/bloodsucker/greet()
var/fullname = ReturnFullName(TRUE)
to_chat(owner, "<span class='userdanger'>You are [fullname], a bloodsucking vampire!</span><br>")
to_chat(owner, "<span class='userdanger'>You are [fullname], a strain of vampire dubbed bloodsucker!</span><br>")
owner.announce_objectives()
to_chat(owner, "<span class='boldannounce'>* You regenerate your health slowly, you're weak to fire, and you depend on blood to survive. Allow your stolen blood to run too low, and you will find yourself at \
risk of being discovered!</span><br>")
//to_chat(owner, "<span class='boldannounce'>As an immortal, your power is linked to your age. The older you grow, the more abilities you will have access to.<span>")
var/vamp_greet
vamp_greet += "<span class='boldannounce'>* Other Bloodsuckers are not necessarily your friends, but your survival may depend on cooperation. Betray them at your own discretion and peril.</span><br>"
vamp_greet += "<span class='boldannounce'><i>* Use \",b\" to speak your ancient Bloodsucker language.</span><br>"
vamp_greet += "<span class='announce'>Bloodsucker Tip: Rest in a <i>Coffin</i> to claim it, and that area, as your lair.</span><br>"
vamp_greet += "<span class='announce'>Bloodsucker Tip: Fear the daylight! Solar flares will bombard the station periodically, and only your coffin can guarantee your safety.</span><br>"
vamp_greet += "<span class='announce'>Bloodsucker Tip: You wont loose blood if you are unconcious or sleeping. Use this to your advantage to conserve blood.</span><br>"
to_chat(owner, vamp_greet)
var/bloodsucker_greet
bloodsucker_greet += "<span class='boldannounce'>* Other Bloodsuckers are not necessarily your friends, but your survival may depend on cooperation. Betray them at your own discretion and peril.</span><br>"
bloodsucker_greet += "<span class='boldannounce'><i>* Use \",b\" to speak your ancient Bloodsucker language.</span><br>"
bloodsucker_greet += "<span class='announce'>Bloodsucker Tip: Rest in a <i>Coffin</i> to claim it, and that area, as your lair.</span><br>"
bloodsucker_greet += "<span class='announce'>Bloodsucker Tip: Fear the daylight! Solar flares will bombard the station periodically, and only your coffin can guarantee your safety.</span><br>"
bloodsucker_greet += "<span class='announce'>Bloodsucker Tip: You wont loose blood if you are unconcious or sleeping. Use this to your advantage to conserve blood.</span><br>"
to_chat(owner, bloodsucker_greet)
owner.current.playsound_local(null, 'sound/bloodsucker/BloodsuckerAlert.ogg', 100, FALSE, pressure_affected = FALSE)
antag_memory += "Although you were born a mortal, in un-death you earned the name <b>[fullname]</b>.<br>"
@@ -92,13 +90,13 @@
owner.current.blood_volume = max(owner.current.blood_volume,BLOOD_VOLUME_SAFE)
/datum/antagonist/bloodsucker/threat()
return ..()+3*vamplevel
return ..() + 3 * bloodsucker_level
/datum/antagonist/bloodsucker/proc/SelectFirstName()
// Names (EVERYONE gets one))
if(owner.current.gender == MALE)
vampname = pick("Desmond","Rudolph","Dracul","Vlad","Pyotr","Gregor","Cristian","Christoff","Marcu","Andrei","Constantin","Gheorghe","Grigore","Ilie","Iacob","Luca","Mihail","Pavel","Vasile","Octavian","Sorin", \
bloodsucker_name = pick("Desmond","Rudolph","Dracul","Vlad","Pyotr","Gregor","Cristian","Christoff","Marcu","Andrei","Constantin","Gheorghe","Grigore","Ilie","Iacob","Luca","Mihail","Pavel","Vasile","Octavian","Sorin", \
"Sveyn","Aurel","Alexe","Iustin","Theodor","Dimitrie","Octav","Damien","Magnus","Caine","Abel", // Romanian/Ancient
"Lucius","Gaius","Otho","Balbinus","Arcadius","Romanos","Alexios","Vitellius", // Latin
"Melanthus","Teuthras","Orchamus","Amyntor","Axion", // Greek
@@ -106,7 +104,7 @@
"Dio")
else
vampname = pick("Islana","Tyrra","Greganna","Pytra","Hilda","Andra","Crina","Viorela","Viorica","Anemona","Camelia","Narcisa","Sorina","Alessia","Sophia","Gladda","Arcana","Morgan","Lasarra","Ioana","Elena", \
bloodsucker_name = pick("Islana","Tyrra","Greganna","Pytra","Hilda","Andra","Crina","Viorela","Viorica","Anemona","Camelia","Narcisa","Sorina","Alessia","Sophia","Gladda","Arcana","Morgan","Lasarra","Ioana","Elena", \
"Alina","Rodica","Teodora","Denisa","Mihaela","Svetla","Stefania","Diyana","Kelssa","Lilith", // Romanian/Ancient
"Alexia","Athanasia","Callista","Karena","Nephele","Scylla","Ursa", // Latin
"Alcestis","Damaris","Elisavet","Khthonia","Teodora", // Greek
@@ -114,57 +112,57 @@
/datum/antagonist/bloodsucker/proc/SelectTitle(am_fledgling = 0, forced = FALSE)
// Already have Title
if (!forced && vamptitle != null)
if(!forced && bloodsucker_title != null)
return
// Titles [Master]
if (!am_fledgling)
if(!am_fledgling)
if(owner.current.gender == MALE)
vamptitle = pick ("Count","Baron","Viscount","Prince","Duke","Tzar","Dreadlord","Lord","Master")
bloodsucker_title = pick ("Count","Baron","Viscount","Prince","Duke","Tzar","Dreadlord","Lord","Master")
else
vamptitle = pick ("Countess","Baroness","Viscountess","Princess","Duchess","Tzarina","Dreadlady","Lady","Mistress")
bloodsucker_title = pick ("Countess","Baroness","Viscountess","Princess","Duchess","Tzarina","Dreadlady","Lady","Mistress")
to_chat(owner, "<span class='announce'>You have earned a title! You are now known as <i>[ReturnFullName(TRUE)]</i>!</span>")
// Titles [Fledgling]
else
vamptitle = null
bloodsucker_title = null
/datum/antagonist/bloodsucker/proc/SelectReputation(am_fledgling = 0, forced=FALSE)
// Already have Reputation
if(!forced && vampreputation != null)
if(!forced && bloodsucker_reputation != null)
return
// Reputations [Master]
if(!am_fledgling)
vampreputation = pick("Butcher","Blood Fiend","Crimson","Red","Black","Terror","Nightman","Feared","Ravenous","Fiend","Malevolent","Wicked","Ancient","Plaguebringer","Sinister","Forgotten","Wretched","Baleful", \
bloodsucker_reputation = pick("Butcher","Blood Fiend","Crimson","Red","Black","Terror","Nightman","Feared","Ravenous","Fiend","Malevolent","Wicked","Ancient","Plaguebringer","Sinister","Forgotten","Wretched","Baleful", \
"Inqisitor","Harvester","Reviled","Robust","Betrayer","Destructor","Damned","Accursed","Terrible","Vicious","Profane","Vile","Depraved","Foul","Slayer","Manslayer","Sovereign","Slaughterer", \
"Forsaken","Mad","Dragon","Savage","Villainous","Nefarious","Inquisitor","Marauder","Horrible","Immortal","Undying","Overlord","Corrupt","Hellspawn","Tyrant","Sanguineous")
if(owner.current.gender == MALE)
if(prob(10)) // Gender override
vampreputation = pick("King of the Damned", "Blood King", "Emperor of Blades", "Sinlord", "God-King")
bloodsucker_reputation = pick("King of the Damned", "Blood King", "Emperor of Blades", "Sinlord", "God-King")
else
if(prob(10)) // Gender override
vampreputation = pick("Queen of the Damned", "Blood Queen", "Empress of Blades", "Sinlady", "God-Queen")
bloodsucker_reputation = pick("Queen of the Damned", "Blood Queen", "Empress of Blades", "Sinlady", "God-Queen")
to_chat(owner, "<span class='announce'>You have earned a reputation! You are now known as <i>[ReturnFullName(TRUE)]</i>!</span>")
// Reputations [Fledgling]
else
vampreputation = pick ("Crude","Callow","Unlearned","Neophyte","Novice","Unseasoned","Fledgling","Young","Neonate","Scrapling","Untested","Unproven","Unknown","Newly Risen","Born","Scavenger","Unknowing",\
bloodsucker_reputation = pick ("Crude","Callow","Unlearned","Neophyte","Novice","Unseasoned","Fledgling","Young","Neonate","Scrapling","Untested","Unproven","Unknown","Newly Risen","Born","Scavenger","Unknowing",\
"Unspoiled","Disgraced","Defrocked","Shamed","Meek","Timid","Broken")//,"Fresh")
/datum/antagonist/bloodsucker/proc/AmFledgling()
return !vamptitle
return !bloodsucker_title
/datum/antagonist/bloodsucker/proc/ReturnFullName(var/include_rep=0)
var/fullname
// Name First
fullname = (vampname ? vampname : owner.current.name)
fullname = (bloodsucker_name ? bloodsucker_name : owner.current.name)
// Title
if(vamptitle)
fullname = vamptitle + " " + fullname
if(bloodsucker_title)
fullname = bloodsucker_title + " " + fullname
// Rep
if(include_rep && vampreputation)
fullname = fullname + " the " + vampreputation
if(include_rep && bloodsucker_reputation)
fullname = fullname + " the " + bloodsucker_reputation
return fullname
@@ -199,15 +197,12 @@
var/mob/living/carbon/human/H = owner.current
var/datum/species/S = H.dna.species
// Make Changes
H.physiology.brute_mod *= 0.8 // <-------------------- Start small, but burn mod increases based on rank!
H.physiology.brute_mod *= 0.8
H.physiology.cold_mod = 0
H.physiology.stun_mod *= 0.5
H.physiology.siemens_coeff *= 0.75 //base electrocution coefficient 1
//S.heatmod += 0.5 // Heat shouldn't affect. Only Fire.
//S.punchstunthreshold = 8 //damage at which punches from this race will stun 9
S.punchdamagelow += 1 //lowest possible punch damage 0
S.punchdamagehigh += 1 //highest possible punch damage 9
// Clown
if(istype(H) && owner.assigned_role == "Clown")
H.dna.remove_mutation(CLOWNMUT)
to_chat(H, "As a vampiric clown, you are no longer a danger to yourself. Your nature is subdued.")
@@ -215,8 +210,6 @@
CheckVampOrgans() // Heart, Eyes
// Language
owner.current.grant_language(/datum/language/vampiric)
// Soul
//owner.current.hellbound = TRUE Causes wierd stuff
owner.hasSoul = FALSE // If false, renders the character unable to sell their soul.
owner.isholy = FALSE // is this person a chaplain or admin role allowed to use bibles
// Disabilities
@@ -248,7 +241,6 @@
// Clown
if(istype(H) && owner.assigned_role == "Clown")
H.dna.add_mutation(CLOWNMUT)
// NOTE: Use initial() to return things to default!
// Physiology
owner.current.regenerate_organs()
// Update Health
@@ -264,13 +256,13 @@
set waitfor = FALSE
if(!owner || !owner.current)
return
vamplevel_unspent ++
bloodsucker_level_unspent ++
// Spend Rank Immediately?
if(istype(owner.current.loc, /obj/structure/closet/crate/coffin))
SpendRank()
else
to_chat(owner, "<EM><span class='notice'>You have grown more ancient! Sleep in a coffin that you have claimed to thicken your blood and become more powerful.</span></EM>")
if(vamplevel_unspent >= 2)
if(bloodsucker_level_unspent >= 2)
to_chat(owner, "<span class='announce'>Bloodsucker Tip: If you cannot find or steal a coffin to use, you can build one from wooden planks.</span><br>")
/datum/antagonist/bloodsucker/proc/LevelUpPowers()
@@ -279,10 +271,10 @@
/datum/antagonist/bloodsucker/proc/SpendRank()
set waitfor = FALSE
if(vamplevel_unspent <= 0 || !owner || !owner.current || !owner.current.client || !isliving(owner.current))
if(bloodsucker_level_unspent <= 0 || !owner || !owner.current || !owner.current.client || !isliving(owner.current))
return
var/mob/living/L = owner.current
level_bloodcost = maxBloodVolume * 0.2
level_bloodcost = max_blood_volume * 0.2
//If the blood volume of the bloodsucker is lower than the cost to level up, return and inform the bloodsucker
//TODO: Make this into a radial, or perhaps a tgui next UI
@@ -298,7 +290,7 @@
if(options.len > 1)
var/choice = input(owner.current, "You have the opportunity to grow more ancient at the cost of [level_bloodcost] units of blood. Select a power to advance your Rank.", "Your Blood Thickens...") in options
// Cheat-Safety: Can't keep opening/closing coffin to spam levels
if(vamplevel_unspent <= 0) // Already spent all your points, and tried opening/closing your coffin, pal.
if(bloodsucker_level_unspent <= 0) // Already spent all your points, and tried opening/closing your coffin, pal.
return
if(!istype(owner.current.loc, /obj/structure/closet/crate/coffin))
to_chat(owner.current, "<span class='warning'>Return to your coffin to advance your Rank.</span>")
@@ -329,20 +321,20 @@
// More Health
owner.current.setMaxHealth(owner.current.maxHealth + 10)
// Vamp Stats
regenRate += 0.05 // Points of brute healed (starts at 0.3)
feedAmount += 2 // Increase how quickly I munch down vics (15)
maxBloodVolume += 100 // Increase my max blood (600)
regen_rate += 0.05 // Points of brute healed (starts at 0.3)
feed_amount += 2 // Increase how quickly I munch down vics (15)
max_blood_volume += 100 // Increase my max blood (600)
/////////
vamplevel ++
vamplevel_unspent --
bloodsucker_level ++
bloodsucker_level_unspent --
// Assign True Reputation
if(vamplevel == 4)
if(bloodsucker_level == 4)
SelectReputation(am_fledgling = FALSE, forced = TRUE)
to_chat(owner.current, "<span class='notice'>You are now a rank [vamplevel] Bloodsucker. Your strength, health, feed rate, regen rate, and maximum blood have all increased!</span>")
to_chat(owner.current, "<span class='notice'>You are now a rank [bloodsucker_level] Bloodsucker. Your strength, health, feed rate, regen rate, can have up to [bloodsucker_level - count_vassals(owner.current.mind)] vassals, and maximum blood have all increased!</span>")
to_chat(owner.current, "<span class='notice'>Your existing powers have all ranked up as well!</span>")
update_hud(TRUE)
owner.current.playsound_local(null, 'sound/effects/pope_entry.ogg', 25, 1) // Play THIS sound for user only. The "null" is where turf would go if a location was needed. Null puts it right in their head.
owner.current.playsound_local(null, 'sound/effects/pope_entry.ogg', 25, TRUE, pressure_affected = FALSE)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -654,13 +646,13 @@
var/datum/antagonist/vassal/mob_V = M.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
// Check 2) If they are a BLOODSUCKER, then are they my Master?
if (mob_V && atom_B == mob_V.master)
return TRUE // SUCCESS!
return TRUE
// Check 3) If I am a BLOODSUCKER, then are they my Vassal?
if (mob_B && atom_V && (atom_V in mob_B.vassals))
return TRUE // SUCCESS!
return TRUE
// Check 4) If we are both VASSAL, then do we have the same master?
if (atom_V && mob_V && atom_V.master == mob_V.master)
return TRUE // SUCCESS!
return TRUE
return FALSE
@@ -705,10 +697,10 @@
// Update Rank Counter
if(owner.current.hud_used.vamprank_display)
var/valuecolor = vamplevel_unspent ? "#FFFF00" : "#FF0000"
owner.current.hud_used.vamprank_display.update_counter(vamplevel, valuecolor)
var/valuecolor = bloodsucker_level_unspent ? "#FFFF00" : "#FF0000"
owner.current.hud_used.vamprank_display.update_counter(bloodsucker_level, valuecolor)
if(updateRank) // Only change icon on special request.
owner.current.hud_used.vamprank_display.icon_state = (vamplevel_unspent > 0) ? "rank_up" : "rank"
owner.current.hud_used.vamprank_display.icon_state = (bloodsucker_level_unspent > 0) ? "rank_up" : "rank"
/obj/screen/bloodsucker
@@ -718,12 +710,12 @@
invisibility = INVISIBILITY_ABSTRACT
/obj/screen/bloodsucker/proc/update_counter(value, valuecolor)
invisibility = 0 // Make Visible
invisibility = 0
/obj/screen/bloodsucker/blood_counter // NOTE: Look up /obj/screen/devil/soul_counter in _onclick / hud / human.dm
icon = 'icons/mob/actions/bloodsucker.dmi'//'icons/mob/screen_gen.dmi'
/obj/screen/bloodsucker/blood_counter
icon = 'icons/mob/actions/bloodsucker.dmi'
name = "Blood Consumed"
icon_state = "blood_display"//"power_display"
icon_state = "blood_display"
screen_loc = ui_blood_display
/obj/screen/bloodsucker/blood_counter/update_counter(value, valuecolor)
@@ -748,22 +740,27 @@
/datum/antagonist/bloodsucker/proc/update_sunlight(value, amDay = FALSE)
// No Hud? Get out.
if (!owner.current.hud_used)
if(!owner.current.hud_used)
return
// Update Sun Time
if (owner.current.hud_used.sunlight_display)
if(owner.current.hud_used.sunlight_display)
var/valuecolor = "#BBBBFF"
if (amDay)
if(amDay)
valuecolor = "#FF5555"
else if(value <= 25)
valuecolor = "#FFCCCC"
else if(value < 10)
valuecolor = "#FF5555"
var/value_string = (value >= 60) ? "[round(value / 60, 1)] m" : "[round(value,1)] s"
var/value_string = (value >= 60) ? "[round(value / 60, 1)] m" : "[round(value, 1)] s"
owner.current.hud_used.sunlight_display.update_counter( value_string, valuecolor )
owner.current.hud_used.sunlight_display.icon_state = "sunlight_" + (amDay ? "day":"night")
/obj/screen/bloodsucker/sunlight_counter/update_counter(value, valuecolor)
..()
maptext = "<div align='center' valign='bottom' style='position:relative; top:0px; left:6px'><font color='[valuecolor]'>[value]</font></div>"
maptext = "<div align='center' valign='bottom' style='position:relative; top:0px; left:6px'><font color='[valuecolor]'>[value]</font></div>"
/datum/antagonist/bloodsucker/proc/count_vassals(datum/mind/master)
var/datum/antagonist/bloodsucker/B = master.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
var/vassal_amount = B.vassals.len
return vassal_amount
@@ -5,7 +5,8 @@
var/obj/item/organ/O
// Heart
O = owner.current.getorganslot(ORGAN_SLOT_HEART)
if(!istype(O, /obj/item/organ/heart/vampheart))
if(!istype(O, /obj/item/organ/heart/vampheart) && !istype(O, /obj/item/organ/heart/demon))
qdel(O)
var/obj/item/organ/heart/vampheart/H = new
H.Insert(owner.current)
@@ -115,13 +115,15 @@
qdel(src)
/obj/structure/bloodsucker/vassalrack/examine(mob/user)
var/datum/antagonist/bloodsucker/B = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
. = ..()
if(isvamp(user) || isobserver(user))
if(B || isobserver(user))
. += {"<span class='cult'>This is the vassal rack, which allows you to thrall crewmembers into loyal minions in your service.</span>"}
. += {"<span class='cult'>You need to first secure the vassal rack by clicking on it while it is in your lair.</span>"}
. += {"<span class='cult'>Simply click and hold on a victim, and then drag their sprite on the vassal rack. Alt click on the vassal rack to unbuckle them.</span>"}
. += {"<span class='cult'>Make sure that the victim is handcuffed, or else they can simply run away or resist, as the process is not instant.</span>"}
. += {"<span class='cult'>To convert the victim, simply click on the vassal rack itself. Sharp weapons work faster than other tools.</span>"}
. += {"<span class='cult'> You have only the power for [B.bloodsucker_level - B.count_vassals(user.mind)] vassals</span>"}
/* if(user.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
. += {"<span class='cult'>This is the vassal rack, which allows your master to thrall crewmembers into his minions.\n
Aid your master in bringing their victims here and keeping them secure.\n
@@ -130,7 +132,7 @@
/obj/structure/bloodsucker/vassalrack/MouseDrop_T(atom/movable/O, mob/user)
if(!O.Adjacent(src) || O == user || !isliving(O) || !isliving(user) || useLock || has_buckled_mobs() || user.incapacitated())
return
if(!anchored && isvamp(user))
if(!anchored && AmBloodsucker(user))
to_chat(user, "<span class='danger'>Until this rack is secured in place, it cannot serve its purpose.</span>")
return
// PULL TARGET: Remember if I was pullin this guy, so we can restore this
@@ -183,7 +185,7 @@
/obj/structure/bloodsucker/vassalrack/user_unbuckle_mob(mob/living/M, mob/user)
// Attempt Unbuckle
if(!isvamp(user))
if(!AmBloodsucker(user))
if(M == user)
M.visible_message("<span class='danger'>[user] tries to release themself from the rack!</span>",\
"<span class='danger'>You attempt to release yourself from the rack!</span>") // For sound if not seen --> "<span class='italics'>You hear a squishy wet noise.</span>")
@@ -222,15 +224,15 @@
// Go away. Torturing.
if(useLock)
return
var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
var/datum/antagonist/bloodsucker/B = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
// CHECK ONE: Am I claiming this? Is it in the right place?
if(istype(bloodsuckerdatum) && !owner)
if(!bloodsuckerdatum.lair)
if(istype(B) && !owner)
if(!B.lair)
to_chat(user, "<span class='danger'>You don't have a lair. Claim a coffin to make that location your lair.</span>")
if(bloodsuckerdatum.lair != get_area(src))
to_chat(user, "<span class='danger'>You may only activate this structure in your lair: [bloodsuckerdatum.lair].</span>")
if(B.lair != get_area(src))
to_chat(user, "<span class='danger'>You may only activate this structure in your lair: [B.lair].</span>")
return
switch(alert(user,"Do you wish to afix this structure here? Be aware you wont be able to unsecure it anymore","Secure [src]","Yes", "No"))
switch(alert(user,"Do you wish to afix this structure here? Be aware you wont be able to unsecure it anymore", "Secure [src]", "Yes", "No"))
if("Yes")
owner = user
density = FALSE
@@ -241,27 +243,31 @@
return
// CHECK TWO: Am I a non-bloodsucker?
var/mob/living/carbon/C = pick(buckled_mobs)
if(!istype(bloodsuckerdatum))
if(!istype(B))
// Try to release this guy
user_unbuckle_mob(C, user)
return
// Bloodsucker Owner! Let the boy go.
if(C.mind)
var/datum/antagonist/vassal/vassaldatum = C.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
if(istype(vassaldatum) && vassaldatum.master == bloodsuckerdatum || C.stat >= DEAD)
var/datum/antagonist/vassal/V = C.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
if(istype(V) && V.master == B || C.stat >= DEAD)
unbuckle_mob(C)
useLock = FALSE // Failsafe
return
// Just torture the boy
torture_victim(user, C)
#define CONVERT_COST 150
/obj/structure/bloodsucker/vassalrack/proc/torture_victim(mob/living/user, mob/living/target)
var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
var/datum/antagonist/bloodsucker/B = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
// Check Bloodmob/living/M, force = FALSE, check_loc = TRUE
var/convert_cost = 200 + 200 * bloodsuckerdatum.vassals
if(user.blood_volume < convert_cost + 5)
if(user.blood_volume < CONVERT_COST + 5)
to_chat(user, "<span class='notice'>You don't have enough blood to initiate the Dark Communion with [target].</span>")
return
if(B.count_vassals(user.mind) > B.bloodsucker_level)
to_chat(user, "<span class='notice'>Your power is yet too weak to bring more vassals under your control....</span>")
return
// Prep...
useLock = TRUE
// Step One: Tick Down Conversion from 3 to 0
@@ -302,12 +308,13 @@
useLock = FALSE
return
// Check: Blood
if(user.blood_volume < convert_cost)
to_chat(user, "<span class='notice'>You don't have enough blood to initiate the Dark Communion with [target], you need [convert_cost - user.blood_volume] units more!</span>")
if(user.blood_volume < CONVERT_COST)
to_chat(user, "<span class='notice'>You don't have enough blood to initiate the Dark Communion with [target], you need [CONVERT_COST - user.blood_volume] units more!</span>")
useLock = FALSE
return
bloodsuckerdatum.AddBloodVolume(-convert_cost)
target.add_mob_blood(user)
B.AddBloodVolume(-CONVERT_COST)
target.add_mob_blood(user, "<span class='danger'>Youve used [CONVERT_COST] amount of blood to gain a new vassal!</span>")
to_chat(user, )
user.visible_message("<span class='notice'>[user] marks a bloody smear on [target]'s forehead and puts a wrist up to [target.p_their()] mouth!</span>", \
"<span class='notice'>You paint a bloody marking across [target]'s forehead, place your wrist to [target.p_their()] mouth, and subject [target.p_them()] to the Dark Communion.</span>")
if(!do_mob(user, src, 50))
@@ -315,21 +322,22 @@
useLock = FALSE
return
// Convert to Vassal!
if(bloodsuckerdatum && bloodsuckerdatum.attempt_turn_vassal(target))
if(B && B.attempt_turn_vassal(target))
//remove_loyalties(target) // In case of Mindshield, or appropriate Antag (Traitor, Internal, etc)
//if (!target.buckled)
// to_chat(user, "<span class='danger'><i>The ritual has been interrupted!</i></span>")
// useLock = FALSE
// return
user.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, 1) // Play THIS sound for user only. The "null" is where turf would go if a location was needed. Null puts it right in their head.
target.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, 1) // Play THIS sound for user only. The "null" is where turf would go if a location was needed. Null puts it right in their head.
target.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, 1) // Play THIS sound for user only. The "null" is where turf would go if a location was needed. Null puts it right in their head.
user.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE)
target.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE)
target.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, TRUE)
target.Jitter(25)
target.emote("laugh")
//remove_victim(target) // Remove on CLICK ONLY!
useLock = FALSE
/obj/structure/bloodsucker/vassalrack/proc/do_torture(mob/living/user, mob/living/target, mult=1)
#undef CONVERT_COST
/obj/structure/bloodsucker/vassalrack/proc/do_torture(mob/living/user, mob/living/target, mult = 1)
var/torture_time = 15 // Fifteen seconds if you aren't using anything. Shorter with weapons and such.
var/torture_dmg_brute = 2
var/torture_dmg_burn = 0
@@ -454,7 +462,7 @@
/obj/structure/bloodsucker/candelabrum/examine(mob/user)
. = ..()
if((isvamp()) || isobserver(user))
if((AmBloodsucker(user)) || isobserver(user))
. += {"<span class='cult'>This is a magical candle which drains at the sanity of mortals who are not under your command while it is active.</span>"}
. += {"<span class='cult'>You can alt click on it from any range to turn it on remotely, or simply be next to it and click on it to turn it on and off normally.</span>"}
/* if(user.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
@@ -463,12 +471,12 @@
/obj/structure/bloodsucker/candelabrum/attack_hand(mob/user)
var/datum/antagonist/vassal/T = user.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
if(isvamp(user) || istype(T))
if(AmBloodsucker(user) || istype(T))
toggle()
/obj/structure/bloodsucker/candelabrum/AltClick(mob/user)
// Bloodsuckers can turn their candles on from a distance. SPOOOOKY.
if(isvamp(user))
if(AmBloodsucker(user))
toggle()
/obj/structure/bloodsucker/candelabrum/proc/toggle(mob/user)
@@ -485,7 +493,7 @@
if(lit)
for(var/mob/living/carbon/human/H in viewers(7, src))
var/datum/antagonist/vassal/T = H.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
if(isvamp(H) || T) //We dont want vassals or vampires affected by this
if(AmBloodsucker(H) || T) //We dont want vassals or vampires affected by this
return
H.hallucination = 20
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "vampcandle", /datum/mood_event/vampcandle)
@@ -2,7 +2,7 @@
/datum/action/bloodsucker/cloak
name = "Cloak of Darkness"
desc = "Blend into the shadows and become invisible to the untrained eye. Movement is slowed in brightly lit areas."
desc = "Blend into the shadows and become invisible to the untrained eye. Movement is slowed in brightly lit areas, and you cannot dissapear while mortals watch you."
button_icon_state = "power_cloak"
bloodcost = 5
cooldown = 50
@@ -140,7 +140,7 @@
to_chat(user, "<span class='notice'>You lean quietly toward [target] and secretly draw out your fangs...</span>")
else
to_chat(user, "<span class='warning'>You pull [target] close to you and draw out your fangs...</span>")
if(!do_mob(user, target, feed_time,0,1,extra_checks=CALLBACK(src, .proc/ContinueActive, user, target)))//sleep(10)
if(!do_mob(user, target, feed_time, 0, 1, extra_checks = CALLBACK(src, .proc/ContinueActive, user, target)))//sleep(10)
to_chat(user, "<span class='warning'>Your feeding was interrupted.</span>")
//DeactivatePower(user,target)
return
@@ -166,7 +166,7 @@
var/deadmessage = target.stat == DEAD ? "" : " <i>[target.p_they(TRUE)] looks dazed, and will not remember this.</i>"
user.visible_message("<span class='notice'>[user] puts [target]'s wrist up to [user.p_their()] mouth.</span>", \
"<span class='notice'>You secretly slip your fangs into [target]'s wrist.[deadmessage]</span>", \
vision_distance = notice_range, ignored_mobs=target) // Only people who AREN'T the target will notice this action.
vision_distance = notice_range, ignored_mobs = target) // Only people who AREN'T the target will notice this action.
// Warn Feeder about Witnesses...
var/was_unnoticed = TRUE
for(var/mob/living/M in viewers(notice_range, owner))
@@ -257,11 +257,11 @@
to_chat(user, "<span class='notice'>Your victim is dead. [target.p_their(TRUE)] blood barely nourishes you.</span>")
warning_target_dead = TRUE
// Full?
if(!warning_full && user.blood_volume >= bloodsuckerdatum.maxBloodVolume)
if(!warning_full && user.blood_volume >= bloodsuckerdatum.max_blood_volume)
to_chat(user, "<span class='notice'>You are full. Further blood will be wasted.</span>")
warning_full = TRUE
// Blood Remaining? (Carbons/Humans only)
if(iscarbon(target) && !target.AmBloodsucker(1))
if(iscarbon(target) && !AmBloodsucker(target, TRUE))
if(target.blood_volume <= BLOOD_VOLUME_BAD && warning_target_bloodvol > BLOOD_VOLUME_BAD)
to_chat(user, "<span class='warning'>Your victim's blood volume is fatally low!</span>")
else if(target.blood_volume <= BLOOD_VOLUME_OKAY && warning_target_bloodvol > BLOOD_VOLUME_OKAY)
@@ -275,8 +275,9 @@
break
// Blood Gulp Sound
owner.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, 1) // Play THIS sound for user only. The "null" is where turf would go if a location was needed. Null puts it right in their head.
owner.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, TRUE)
if(!amSilent)
target.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, TRUE)
// DONE!
//DeactivatePower(user,target)
if(amSilent)
@@ -294,12 +295,12 @@
/datum/action/bloodsucker/feed/proc/CheckKilledTarget(mob/living/user, mob/living/target)
// Bad Vampire. You shouldn't do that.
// Bad Bloodsucker. You shouldn't do that.
if(target && target.stat >= DEAD && ishuman(target))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankkilled", /datum/mood_event/drankkilled) // BAD // in bloodsucker_life.dm
/datum/action/bloodsucker/feed/ContinueActive(mob/living/user, mob/living/target)
return ..() && target && (!target_grappled || user.pulling == target)// Active, and still Antag,
return ..() && target && (!target_grappled || user.pulling == target) && blood_sucking_checks(target, TRUE, TRUE) // Active, and still antag,
// NOTE: We only care about pulling if target started off that way. Mostly only important for Aggressive feed.
/datum/action/bloodsucker/feed/proc/ApplyVictimEffects(mob/living/target)
@@ -33,6 +33,15 @@
if(was_running)
user.toggle_move_intent()
while(bloodsuckerdatum && ContinueActive(user) || user.m_intent == MOVE_INTENT_RUN)
if(istype(user.buckled, /obj/vehicle)) //We dont want people using fortitude being able to use vehicles
var/obj/vehicle/V = user.buckled
var/datum/component/riding/VRD = V.GetComponent(/datum/component/riding)
if(VRD)
VRD.force_dismount(user)
to_chat(user, "<span class='notice'>You trip off the [V], your muscles too heavy for it to support you.</span>")
else
V.unbuckle_mob(user, force = TRUE)
to_chat(user, "<span class='notice'>You fall off the [V], your weight making you too heavy to be supported by it.</span>")
// Pay Blood Toll (if awake)
if(user.stat == CONSCIOUS)
bloodsuckerdatum.AddBloodVolume(-0.5) // Used to be 0.3 blood per 2 seconds, but we're making it more expensive to keep on.
@@ -8,7 +8,7 @@
desc = "Dash somewhere with supernatural speed. Those nearby may be knocked away, stunned, or left empty-handed."
button_icon_state = "power_speed"
bloodcost = 6
cooldown = 50
cooldown = 120
target_range = 15
power_activates_immediately = TRUE
message_Trigger = ""//"Whom will you subvert to your will?"
@@ -28,8 +28,7 @@
action.Remove(user)
return
/obj/effect/proc_holder/changeling/Click()
var/mob/user = usr
/obj/effect/proc_holder/changeling/Trigger(mob/user)
if(!user || !user.mind || !user.mind.has_antag_datum(/datum/antagonist/changeling))
return
try_to_sting(user)
@@ -3,8 +3,7 @@
desc = "Stabby stabby."
var/sting_icon = null
/obj/effect/proc_holder/changeling/sting/Click()
var/mob/user = usr
/obj/effect/proc_holder/changeling/sting/Trigger(mob/user)
if(!user || !user.mind)
return
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
@@ -74,8 +73,7 @@
action_icon_state = "ling_sting_transform"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/transformation/Click()
var/mob/user = usr
/obj/effect/proc_holder/changeling/sting/transformation/Trigger(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling.chosen_sting)
unset_sting(user)
@@ -35,7 +35,7 @@
var/span_for_name = "heavy_brass"
var/span_for_message = "brass"
/datum/action/innate/hierophant/IsAvailable()
/datum/action/innate/hierophant/IsAvailable(silent = FALSE)
if(!is_servant_of_ratvar(owner))
return FALSE
return ..()
@@ -12,7 +12,7 @@
var/obj/item/clockwork/weapon/weapon_type //The type of weapon to create
var/obj/item/clockwork/weapon/weapon
/datum/action/innate/call_weapon/IsAvailable()
/datum/action/innate/call_weapon/IsAvailable(silent = FALSE)
if(!is_servant_of_ratvar(owner))
qdel(src)
return
@@ -121,8 +121,8 @@
. = ..()
addtimer(CALLBACK(src, .proc/check_on_mob, user), 1) //dropped is called before the item is out of the slot, so we need to check slightly later
/obj/item/clockwork/slab/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE)
. = list()
/obj/item/clockwork/slab/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
. = ..()
if(isinhands && item_state && inhand_overlay)
var/mutable_appearance/M = mutable_appearance(icon_file, "slab_[inhand_overlay]")
. += M
@@ -232,7 +232,7 @@
background_icon_state = "bg_clock"
buttontooltipstyle = "clockcult"
/datum/action/innate/eminence/IsAvailable()
/datum/action/innate/eminence/IsAvailable(silent = FALSE)
if(!iseminence(owner))
qdel(src)
return
@@ -283,7 +283,7 @@
desc = "Initiates a mass recall, warping all servants to the Ark after a short delay. This can only be used once."
button_icon_state = "Spatial Gateway"
/datum/action/innate/eminence/mass_recall/IsAvailable()
/datum/action/innate/eminence/mass_recall/IsAvailable(silent = FALSE)
. = ..()
if(.)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
@@ -153,7 +153,7 @@
/obj/item/clothing/head/helmet/space,
/obj/item/clothing/shoes/magboots)) //replace this only if ratvar is up
/datum/action/innate/clockwork_armaments/IsAvailable()
/datum/action/innate/clockwork_armaments/IsAvailable(silent = FALSE)
if(!is_servant_of_ratvar(owner))
qdel(src)
return
+2 -2
View File
@@ -17,7 +17,7 @@
qdel(X)
..()
/datum/action/innate/cult/blood_magic/IsAvailable()
/datum/action/innate/cult/blood_magic/IsAvailable(silent = FALSE)
if(!iscultist(owner))
return FALSE
return ..()
@@ -118,7 +118,7 @@
hand_magic = null
..()
/datum/action/innate/cult/blood_spell/IsAvailable()
/datum/action/innate/cult/blood_spell/IsAvailable(silent = FALSE)
if(!iscultist(owner) || owner.incapacitated() || !charges)
return FALSE
return ..()
+10 -10
View File
@@ -6,7 +6,7 @@
buttontooltipstyle = "cult"
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
/datum/action/innate/cult/IsAvailable()
/datum/action/innate/cult/IsAvailable(silent = FALSE)
if(!iscultist(owner))
return FALSE
return ..()
@@ -51,7 +51,7 @@
name = "Spiritual Communion"
desc = "Conveys a message from the spirit realm that all cultists can hear."
/datum/action/innate/cult/comm/spirit/IsAvailable()
/datum/action/innate/cult/comm/spirit/IsAvailable(silent = FALSE)
if(iscultist(owner.mind.current))
return TRUE
@@ -72,7 +72,7 @@
name = "Assert Leadership"
button_icon_state = "cultvote"
/datum/action/innate/cult/mastervote/IsAvailable()
/datum/action/innate/cult/mastervote/IsAvailable(silent = FALSE)
var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
if(!C?.cult_team || C.cult_team.cult_vote_called || !ishuman(owner))
return FALSE
@@ -140,7 +140,7 @@
to_chat(B.current,"<span class='cultlarge'>[Nominee] has won the cult's support and is now their master. Follow [Nominee.p_their()] orders to the best of your ability!</span>")
return TRUE
/datum/action/innate/cult/master/IsAvailable()
/datum/action/innate/cult/master/IsAvailable(silent = FALSE)
if(!owner.mind || !owner.mind.has_antag_datum(/datum/antagonist/cult/master) || GLOB.cult_narsie)
return 0
return ..()
@@ -226,9 +226,9 @@
CM.attached_action = src
..()
/datum/action/innate/cult/master/cultmark/IsAvailable()
/datum/action/innate/cult/master/cultmark/IsAvailable(silent = FALSE)
if(cooldown > world.time)
if(!CM.active)
if(!CM.active && !silent)
to_chat(owner, "<span class='cultlarge'><b>You need to wait [DisplayTimeText(cooldown - world.time)] before you can mark another target!</b></span>")
return FALSE
return ..()
@@ -308,7 +308,7 @@
name = "Mark a Blood Target for the Cult"
desc = "Marks a target for the entire cult to track."
/datum/action/innate/cult/master/cultmark/ghost/IsAvailable()
/datum/action/innate/cult/master/cultmark/ghost/IsAvailable(silent = FALSE)
if(istype(owner, /mob/dead/observer) && iscultist(owner.mind.current))
return TRUE
else
@@ -322,7 +322,7 @@
var/cooldown = 0
var/base_cooldown = 600
/datum/action/innate/cult/ghostmark/IsAvailable()
/datum/action/innate/cult/ghostmark/IsAvailable(silent = FALSE)
if(istype(owner, /mob/dead/observer) && iscultist(owner.mind.current))
return TRUE
else
@@ -401,11 +401,11 @@
PM.attached_action = src
..()
/datum/action/innate/cult/master/pulse/IsAvailable()
/datum/action/innate/cult/master/pulse/IsAvailable(silent = FALSE)
if(!owner.mind || !owner.mind.has_antag_datum(/datum/antagonist/cult/master))
return FALSE
if(cooldown > world.time)
if(!PM.active)
if(!PM.active && !silent)
to_chat(owner, "<span class='cultlarge'><b>You need to wait [DisplayTimeText(cooldown - world.time)] before you can pulse again!</b></span>")
return FALSE
return ..()
+4 -4
View File
@@ -222,7 +222,7 @@
phasein = /obj/effect/temp_visual/dir_setting/cult/phase
phaseout = /obj/effect/temp_visual/dir_setting/cult/phase/out
/datum/action/innate/dash/cult/IsAvailable()
/datum/action/innate/dash/cult/IsAvailable(silent = FALSE)
if(iscultist(holder) && current_charges)
return TRUE
else
@@ -242,7 +242,7 @@
sword = bastard
holder = user
/datum/action/innate/cult/spin2win/IsAvailable()
/datum/action/innate/cult/spin2win/IsAvailable(silent = FALSE)
if(iscultist(holder) && cooldown <= world.time)
return TRUE
else
@@ -451,8 +451,8 @@
return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
return BLOCK_NONE
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/worn_overlays(isinhands, icon_file, style_flags = NONE)
. = list()
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
. = ..()
if(!isinhands && current_charges)
. += mutable_appearance('icons/effects/cult_effects.dmi', "shield-cult", MOB_LAYER + 0.01)
@@ -14,7 +14,7 @@
var/obj/effect/temp_visual/cult/rune_spawn/rune_center_type
var/rune_color
/datum/action/innate/cult/create_rune/IsAvailable()
/datum/action/innate/cult/create_rune/IsAvailable(silent = FALSE)
if(!rune_type || cooldown > world.time)
return FALSE
return ..()
+61 -2
View File
@@ -179,8 +179,8 @@
/obj/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
if(resistance_flags & INDESTRUCTIBLE)
return FALSE
for(var/mob/living/L in contents)
if(!issilicon(L) && !isbrain(L))
for(var/mob/living/L in GetAllContents())
if(!ispAI(L) && !isbrain(L))
to_chat(S, "<span class='warning'>An organism has been detected inside this object. Aborting.</span>")
return FALSE
return ..()
@@ -416,6 +416,57 @@
to_chat(S, "<span class='warning'>Destroying this object would cause a catastrophic chain reaction. Aborting.</span>")
return FALSE
/obj/machinery/ore_silo/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>Destroying this object, however tempting it's, will disrupt the research development that may serve for our masters in the future. Aborting.</span>")
return FALSE
/obj/machinery/rnd/server/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>Destroying this object, will disrupt the research development that may serve for our masters in the future. Aborting.</span>")
return FALSE
/obj/machinery/pool/swarmer_act(mob/living/simple_animal/hostile/swarmer/S) //pool's closed, but not.
to_chat(S, "<span class='warning'>The pool must not be closed, it will provide healthy fun for our masters in the future. Aborting.</span>")
return FALSE
/obj/structure/pool/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>The pool must not be closed, it will provide healthy fun for our masters in the future. Aborting.</span>")
return FALSE
/obj/structure/holosign/barrier/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
var/static/list/lazy_typecache = typecacheof(list(/obj/structure/holosign/barrier/engineering, /obj/structure/holosign/barrier/firelock, /obj/structure/holosign/barrier/atmos, /obj/structure/holosign/barrier/combifan))
if(lazy_typecache[type])
to_chat(S, "<span class='warning'>Destroying this holographic barrier may not benefit us. Aborting.</span>")
return FALSE
return ..()
/obj/machinery/dominator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This advanced piece of technology may be of use for our masters in the future. Aborting.</span>")
return FALSE
/obj/machinery/computer/bsa_control/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This advanced piece of technology may be of use for our masters in the future. Aborting.</span>")
return FALSE
/obj/machinery/bsa/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This advanced piece of technology may be of use for our masters in the future. Aborting.</span>")
return FALSE
/obj/machinery/dna_vault/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This advanced piece of technology may be of use for our masters in the future. Aborting.</span>")
return FALSE
/obj/structure/filler/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This advanced piece of technology may be of use for our masters in the future. Aborting.</span>")
return FALSE
/obj/machinery/computer/sat_control/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>Destroying this object will lower the station shielding against space debris. Aborting.</span>")
return FALSE
/obj/machinery/satellite/meteor_shield/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>Destroying this object will lower the station shielding against space debris. Aborting.</span>")
return FALSE
////END CTRL CLICK FOR SWARMERS////
/mob/living/simple_animal/hostile/swarmer/proc/Fabricate(atom/fabrication_object,fabrication_cost = 0)
@@ -429,6 +480,14 @@
return new fabrication_object(loc)
/mob/living/simple_animal/hostile/swarmer/proc/Integrate(atom/movable/target)
if(isobj(target))
var/obj/O = target
if(O.resistance_flags & INDESTRUCTIBLE)
return FALSE
for(var/mob/living/L in GetAllContents())
if(!ispAI(L) && !isbrain(L))
to_chat(src, "<span class='warning'>An organism has been detected inside this object. Aborting.</span>")
return FALSE
var/resource_gain = target.IntegrateAmount()
if(resources + resource_gain > max_resources)
to_chat(src, "<span class='warning'>We cannot hold more materials!</span>")
@@ -1,10 +1,10 @@
/datum/round_event_control/spawn_swarmer
name = "Spawn Swarmer Shell"
typepath = /datum/round_event/spawn_swarmer
weight = 0
max_occurrences = 0
weight = 7
max_occurrences = 1 //Only once okay fam
earliest_start = 30 MINUTES
min_players = 15
min_players = 35
gamemode_blacklist = list("dynamic")
@@ -43,7 +43,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
else
owner_AI = owner
/datum/action/innate/ai/IsAvailable()
/datum/action/innate/ai/IsAvailable(silent = FALSE)
. = ..()
if(owner_AI && owner_AI.malf_cooldown > world.time)
return FALSE
+2 -3
View File
@@ -74,10 +74,9 @@
/datum/supply_pack/science/circuitry
name = "Circuitry Starter Pack Crate"
desc = "Journey into the mysterious world of Circuitry with this starter pack. Contains a circuit printer, analyzer, debugger and wirer. Power cells not included."
desc = "Journey into the mysterious world of Circuitry with this starter pack. Contains a circuit printer, debugger and wirer. Power cells not included."
cost = 1000
contains = list(/obj/item/integrated_electronics/analyzer,
/obj/item/integrated_circuit_printer,
contains = list(/obj/item/integrated_circuit_printer,
/obj/item/integrated_electronics/debugger,
/obj/item/integrated_electronics/wirer)
crate_name = "circuitry starter pack crate"
+43 -19
View File
@@ -150,7 +150,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
"ipc_antenna" = "None",
"flavor_text" = "",
"meat_type" = "Mammalian",
"body_model" = MALE
"body_model" = MALE,
"body_size" = RESIZE_DEFAULT_SIZE
)
var/list/custom_names = list()
@@ -294,7 +295,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<a href='?_src_=prefs;preference=name;task=input'>[real_name]</a><BR>"
dat += "<a href='?_src_=prefs;preference=nameless'>Be nameless: [nameless ? "Yes" : "No"]</a><BR>"
dat += "<b>Gender:</b> <a href='?_src_=prefs;preference=gender'>[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]</a><BR>"
dat += "<b>Gender:</b> <a href='?_src_=prefs;preference=gender;task=input'>[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]</a><BR>"
dat += "<b>Age:</b> <a style='display:block;width:30px' href='?_src_=prefs;preference=age;task=input'>[age]</a><BR>"
dat += "<b>Special Names:</b><BR>"
@@ -345,7 +346,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
else
dat += "[TextPreview(features["flavor_text"])]...<BR>"
dat += "<h2>Body</h2>"
dat += "<b>Gender:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=gender'>[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]</a><BR>"
dat += "<b>Gender:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=gender;task=input'>[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]</a><BR>"
if(gender != NEUTER && pref_species.sexes)
dat += "<b>Body Model:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=body_model'>[features["body_model"] == MALE ? "Masculine" : "Feminine"]</a><BR>"
dat += "<b>Species:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=species;task=input'>[pref_species.name]</a><BR>"
@@ -379,6 +380,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<span style='border: 1px solid #161616; background-color: #[features["mcolor3"]];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=mutant_color3;task=input'>Change</a><BR>"
mutant_colors = TRUE
if (CONFIG_GET(number/body_size_min) != CONFIG_GET(number/body_size_max))
dat += "<b>Sprite Size:</b> <a href='?_src_=prefs;preference=body_size;task=input'>[features["body_size"]]%</a><br>"
if((EYECOLOR in pref_species.species_traits) && !(NOEYES in pref_species.species_traits))
if(!use_skintones && !mutant_colors)
@@ -2119,6 +2123,38 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(new_toggle_color)
hud_toggle_color = new_toggle_color
if("gender")
var/chosengender = input(user, "Select your character's gender.", "Gender Selection", gender) as null|anything in list(MALE,FEMALE,"nonbinary","object")
if(!chosengender)
return
switch(chosengender)
if("nonbinary")
chosengender = PLURAL
features["body_model"] = pick(MALE, FEMALE)
if("object")
chosengender = NEUTER
features["body_model"] = MALE
else
features["body_model"] = chosengender
gender = chosengender
facial_hair_style = random_facial_hair_style(gender)
hair_style = random_hair_style(gender)
if("body_size")
var/min = CONFIG_GET(number/body_size_min)
var/max = CONFIG_GET(number/body_size_max)
var/danger = CONFIG_GET(number/threshold_body_size_slowdown)
var/new_body_size = input(user, "Choose your desired sprite size:\n([min*100]%-[max*100]%), Warning: May make your character look distorted[danger > min ? ", and an exponential slowdown will occur for those smaller than [danger*100]%!" : "!"]", "Character Preference", features["body_size"]*100) as num|null
if (new_body_size)
new_body_size = CLAMP(new_body_size * 0.01, min, max)
var/dorfy
if(danger > new_body_size)
dorfy = alert(user, "The chosen size appears to be smaller than the threshold of [danger*100]%, which will lead to an added exponential slowdown. Are you sure about that?", "Dwarfism Alert", "Yes", "Move it to the threshold", "No")
if(!dorfy || dorfy == "Move it above the threshold")
new_body_size = danger
if(dorfy != "No")
features["body_size"] = new_body_size
else
switch(href_list["preference"])
//CITADEL PREFERENCES EDIT - I can't figure out how to modularize these, so they have to go here. :c -Pooj
@@ -2171,22 +2207,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("publicity")
if(unlock_content)
toggles ^= MEMBER_PUBLIC
if("gender")
var/chosengender = input(user, "Select your character's gender.", "Gender Selection", gender) as null|anything in list(MALE,FEMALE,"nonbinary","object")
if(!chosengender)
return
switch(chosengender)
if("nonbinary")
chosengender = PLURAL
features["body_model"] = pick(MALE, FEMALE)
if("object")
chosengender = NEUTER
features["body_model"] = MALE
else
features["body_model"] = chosengender
gender = chosengender
facial_hair_style = random_facial_hair_style(gender)
hair_style = random_hair_style(gender)
if("body_model")
features["body_model"] = features["body_model"] == MALE ? FEMALE : MALE
@@ -2431,6 +2451,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
pref_species = new /datum/species/human
save_character()
var/old_size = character.dna.features["body_size"]
character.dna.features = features.Copy()
character.set_species(chosen_species, icon_update = FALSE, pref_load = TRUE)
character.dna.real_name = character.real_name
@@ -2452,6 +2474,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.give_genitals(TRUE) //character.update_genitals() is already called on genital.update_appearance()
character.dna.update_body_size(old_size)
SEND_SIGNAL(character, COMSIG_HUMAN_PREFS_COPIED_TO, src, icon_updates, roundstart_checks)
//let's be sure the character updates
@@ -395,6 +395,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["body_is_always_random"] >> be_random_body
S["gender"] >> gender
S["body_model"] >> features["body_model"]
S["body_size"] >> features["body_size"]
S["age"] >> age
S["hair_color"] >> hair_color
S["facial_hair_color"] >> facial_hair_color
@@ -568,6 +569,14 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
features["insect_markings"] = sanitize_inlist(features["insect_markings"], GLOB.insect_markings_list, "None")
features["insect_wings"] = sanitize_inlist(features["insect_wings"], GLOB.insect_wings_list)
var/static/size_min
if(!size_min)
size_min = CONFIG_GET(number/body_size_min)
var/static/size_max
if(!size_max)
size_max = CONFIG_GET(number/body_size_max)
features["body_size"] = sanitize_integer(features["body_size"], size_min, size_max, RESIZE_DEFAULT_SIZE)
var/static/list/B_sizes
if(!B_sizes)
var/list/L = CONFIG_GET(keyed_list/breasts_cups_prefs)
@@ -643,6 +652,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["body_is_always_random"] , be_random_body)
WRITE_FILE(S["gender"] , gender)
WRITE_FILE(S["body_model"] , features["body_model"])
WRITE_FILE(S["body_size"] , features["body_size"])
WRITE_FILE(S["age"] , age)
WRITE_FILE(S["hair_color"] , hair_color)
WRITE_FILE(S["facial_hair_color"] , facial_hair_color)
-194
View File
@@ -47,25 +47,12 @@
//Add a "exclude" string to do the opposite, making it only only species listed that can't wear it.
//You append this to clothing objects.
//Polychrome stuff:
var/hasprimary = FALSE //These vars allow you to choose which overlays a clothing has
var/hassecondary = FALSE
var/hastertiary = FALSE
var/primary_color = "#FFFFFF" //RGB in hexcode
var/secondary_color = "#FFFFFF"
var/tertiary_color = "#808080"
//No idea what this is but eh -tori
var/force_alternate_icon = FALSE
/obj/item/clothing/Initialize()
. = ..()
if(CHECK_BITFIELD(clothing_flags, VOICEBOX_TOGGLABLE))
actions_types += /datum/action/item_action/toggle_voice_box
if(ispath(pocket_storage_component_path))
LoadComponent(pocket_storage_component_path)
if(hasprimary | hassecondary | hastertiary) //Checks if polychrome is enabled
update_icon() //Applies the overlays and default colors onto the clothes on spawn.
/obj/item/clothing/MouseDrop(atom/over_object)
. = ..()
@@ -150,8 +137,6 @@
how_cool_are_your_threads += "Adding or removing items from [src] makes no noise.\n"
how_cool_are_your_threads += "</span>"
. += how_cool_are_your_threads.Join()
if(hasprimary | hassecondary | hastertiary) //Checks if polychrome is enabled
. += "<span class='notice'>Alt-click to recolor it.</span>"
/obj/item/clothing/obj_break(damage_flag)
if(!damaged_clothes)
@@ -195,173 +180,6 @@ BLIND // can't see anything
female_clothing_icon = fcopy_rsc(female_clothing_icon)
GLOB.female_clothing_icons[index] = female_clothing_icon
/obj/item/clothing/under/verb/toggle()
set name = "Adjust Suit Sensors"
set category = "Object"
set src in usr
var/mob/M = usr
if (istype(M, /mob/dead/))
return
if (!can_use(M))
return
if(src.has_sensor == LOCKED_SENSORS)
to_chat(usr, "The controls are locked.")
return 0
if(src.has_sensor == BROKEN_SENSORS)
to_chat(usr, "The sensors have shorted out!")
return 0
if(src.has_sensor <= NO_SENSORS)
to_chat(usr, "This suit does not have any sensors.")
return 0
var/list/modes = list("Off", "Binary vitals", "Exact vitals", "Tracking beacon")
var/switchMode = input("Select a sensor mode:", "Suit Sensor Mode", modes[sensor_mode + 1]) in modes
if(get_dist(usr, src) > 1)
to_chat(usr, "<span class='warning'>You have moved too far away!</span>")
return
sensor_mode = modes.Find(switchMode) - 1
if (src.loc == usr)
switch(sensor_mode)
if(0)
to_chat(usr, "<span class='notice'>You disable your suit's remote sensing equipment.</span>")
if(1)
to_chat(usr, "<span class='notice'>Your suit will now only report whether you are alive or dead.</span>")
if(2)
to_chat(usr, "<span class='notice'>Your suit will now only report your exact vital lifesigns.</span>")
if(3)
to_chat(usr, "<span class='notice'>Your suit will now report your exact vital lifesigns as well as your coordinate position.</span>")
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
if(H.w_uniform == src)
H.update_suit_sensors()
/obj/item/clothing/under/CtrlClick(mob/user)
. = ..()
if (!(item_flags & IN_INVENTORY))
return
if(!isliving(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
return
if(has_sensor == LOCKED_SENSORS)
to_chat(user, "The controls are locked.")
return
if(has_sensor == BROKEN_SENSORS)
to_chat(user, "The sensors have shorted out!")
return
if(has_sensor <= NO_SENSORS)
to_chat(user, "This suit does not have any sensors.")
return
sensor_mode = SENSOR_COORDS
to_chat(user, "<span class='notice'>Your suit will now report your exact vital lifesigns as well as your coordinate position.</span>")
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(H.w_uniform == src)
H.update_suit_sensors()
/obj/item/clothing/under/AltClick(mob/user)
. = ..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
return
if(attached_accessory)
remove_accessory(user)
else
rolldown()
// Polychrome stuff:
if(hasprimary | hassecondary | hastertiary)
var/choice = input(user,"polychromic thread options", "Clothing Recolor") as null|anything in list("[hasprimary ? "Primary Color" : ""]", "[hassecondary ? "Secondary Color" : ""]", "[hastertiary ? "Tertiary Color" : ""]") //generates a list depending on the enabled overlays
switch(choice) //Lets the list's options actually lead to something
if("Primary Color")
var/primary_color_input = input(usr,"","Choose Primary Color",primary_color) as color|null //color input menu, the "|null" adds a cancel button to it.
if(primary_color_input) //Checks if the color selected is NULL, rejects it if it is NULL.
primary_color = sanitize_hexcolor(primary_color_input, desired_format=6, include_crunch=1) //formats the selected color properly
update_icon() //updates the item icon
user.regenerate_icons() //updates the worn icon. Probably a bad idea, but it works.
if("Secondary Color")
var/secondary_color_input = input(usr,"","Choose Secondary Color",secondary_color) as color|null
if(secondary_color_input)
secondary_color = sanitize_hexcolor(secondary_color_input, desired_format=6, include_crunch=1)
update_icon()
user.regenerate_icons()
if("Tertiary Color")
var/tertiary_color_input = input(usr,"","Choose Tertiary Color",tertiary_color) as color|null
if(tertiary_color_input)
tertiary_color = sanitize_hexcolor(tertiary_color_input, desired_format=6, include_crunch=1)
update_icon()
user.regenerate_icons()
return TRUE
/obj/item/clothing/neck/AltClick(mob/user)
. = ..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
return
// Polychrome stuff:
if(hasprimary | hassecondary | hastertiary)
var/choice = input(user,"polychromic thread options", "Clothing Recolor") as null|anything in list("[hasprimary ? "Primary Color" : ""]", "[hassecondary ? "Secondary Color" : ""]", "[hastertiary ? "Tertiary Color" : ""]") //generates a list depending on the enabled overlays
switch(choice) //Lets the list's options actually lead to something
if("Primary Color")
var/primary_color_input = input(usr,"","Choose Primary Color",primary_color) as color|null //color input menu, the "|null" adds a cancel button to it.
if(primary_color_input) //Checks if the color selected is NULL, rejects it if it is NULL.
primary_color = sanitize_hexcolor(primary_color_input, desired_format=6, include_crunch=1) //formats the selected color properly
update_icon() //updates the item icon
user.regenerate_icons() //updates the worn icon. Probably a bad idea, but it works.
if("Secondary Color")
var/secondary_color_input = input(usr,"","Choose Secondary Color",secondary_color) as color|null
if(secondary_color_input)
secondary_color = sanitize_hexcolor(secondary_color_input, desired_format=6, include_crunch=1)
update_icon()
user.regenerate_icons()
if("Tertiary Color")
var/tertiary_color_input = input(usr,"","Choose Tertiary Color",tertiary_color) as color|null
if(tertiary_color_input)
tertiary_color = sanitize_hexcolor(tertiary_color_input, desired_format=6, include_crunch=1)
update_icon()
user.regenerate_icons()
return TRUE
/obj/item/clothing/under/verb/jumpsuit_adjust()
set name = "Adjust Jumpsuit Style"
set category = null
set src in usr
rolldown()
/obj/item/clothing/under/proc/rolldown()
if(!can_use(usr))
return
if(!can_adjust)
to_chat(usr, "<span class='warning'>You cannot wear this suit any differently!</span>")
return
if(toggle_jumpsuit_adjust())
to_chat(usr, "<span class='notice'>You adjust the suit to wear it more casually.</span>")
else
to_chat(usr, "<span class='notice'>You adjust the suit back to normal.</span>")
if(ishuman(usr))
var/mob/living/carbon/human/H = usr
H.update_inv_w_uniform()
H.update_body()
/obj/item/clothing/under/proc/toggle_jumpsuit_adjust()
adjusted = !adjusted
if(adjusted)
if(fitted != FEMALE_UNIFORM_TOP)
fitted = NO_FEMALE_UNIFORM
if(!alt_covers_chest) // for the special snowflake suits that expose the chest when adjusted
body_parts_covered &= ~CHEST
else
fitted = initial(fitted)
if(!alt_covers_chest)
body_parts_covered |= CHEST
return adjusted
/obj/item/clothing/proc/weldingvisortoggle(mob/user) //proc to toggle welding visors on helmets, masks, goggles, etc.
if(!can_use(user))
return FALSE
@@ -441,15 +259,3 @@ BLIND // can't see anything
return FALSE
return TRUE
/obj/item/clothing/update_overlays() // Polychrome stuff
. = ..()
if(hasprimary) //Checks if the overlay is enabled
var/mutable_appearance/primary_overlay = mutable_appearance(icon, "[item_state]-primary", color = primary_color) //Automagically picks overlays
. += primary_overlay //Applies the coloured overlay onto the item sprite. but NOT the mob sprite.
if(hassecondary)
var/mutable_appearance/secondary_overlay = mutable_appearance(icon, "[item_state]-secondary", color = secondary_color)
. += secondary_overlay
if(hastertiary)
var/mutable_appearance/tertiary_overlay = mutable_appearance(icon, "[item_state]-tertiary", color = tertiary_color)
. += tertiary_overlay
+2 -2
View File
@@ -346,8 +346,8 @@
add_atom_colour("#[user.eye_color]", FIXED_COLOUR_PRIORITY)
colored_before = TRUE
/obj/item/clothing/glasses/sunglasses/blindfold/white/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE)
. = list()
/obj/item/clothing/glasses/sunglasses/blindfold/white/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
. = ..()
if(!isinhands && ishuman(loc) && !colored_before)
var/mob/living/carbon/human/H = loc
var/mutable_appearance/M = mutable_appearance('icons/mob/clothing/eyes.dmi', "blindfoldwhite")
@@ -23,8 +23,8 @@
else
. += "<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/user, combatmodestate)
if(istype(user) && combatmodestate && world.time >= nextadrenalinepop)
/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)
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)
+2 -2
View File
@@ -26,8 +26,8 @@
user.visible_message("<span class='suicide'>\the [src] are forcing [user]'s hands around [user.p_their()] neck! It looks like the gloves are possessed!</span>")
return OXYLOSS
/obj/item/clothing/gloves/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE)
. = list()
/obj/item/clothing/gloves/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
. = ..()
if(!isinhands)
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedgloves")
+2 -2
View File
@@ -48,8 +48,8 @@
/obj/item/clothing/head/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE)
. = list()
/obj/item/clothing/head/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
. = ..()
if(!isinhands)
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedhelmet")
+1 -1
View File
@@ -124,7 +124,7 @@
playsound(src, 'sound/mecha/mechmove03.ogg', 50, TRUE) //Visors don't just come from nothing
update_icon()
/obj/item/clothing/head/hardhat/weldhat/worn_overlays(isinhands, icon_file, style_flags = NONE)
/obj/item/clothing/head/hardhat/weldhat/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
. = ..()
if(!isinhands)
. += mutable_appearance('icons/mob/clothing/head.dmi', "weldhelmet")

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