This commit is contained in:
EmeraldSundisk
2020-07-04 21:33:29 -07:00
parent 99168e3ed0
commit c6b5bac0d8
871 changed files with 411113 additions and 96055 deletions
+1 -2
View File
@@ -38,9 +38,8 @@
parent = raw_args[1]
var/list/arguments = raw_args.Copy(2)
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
stack_trace("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]")
qdel(src, TRUE, TRUE)
return
CRASH("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]")
_JoinParent(parent)
+1 -1
View File
@@ -109,7 +109,7 @@
AM.visible_message("<span class='boldwarning'>[AM] falls into [parent]!</span>", "<span class='userdanger'>[oblivion_message]</span>")
if (isliving(AM))
var/mob/living/L = AM
L.mob_transforming = TRUE
L.notransform = TRUE
L.Paralyze(200)
var/oldtransform = AM.transform
-2
View File
@@ -119,8 +119,6 @@
if(hud_icon)
hud_icon.combat_on = FALSE
hud_icon.update_icon()
source.stop_active_blocking()
source.end_parry_sequence()
///Changes the user direction to (try) keep match the pointer.
/datum/component/combat_mode/proc/on_move(atom/movable/source, dir, atom/oldloc, forced)
@@ -92,7 +92,7 @@
qdel(src)
/obj/item/glasswork/glasses
name = "Handmade Glasses"
desc = "Handmade glasses that have not been polished at all making them useless. Selling them could still be worth a few credits."
name = "Hand Made Glasses"
desc = "Hande made glasses that have not been polished at all making them useless. Selling them could still be worth a bit of credits."
icon = 'icons/obj/glass_ware.dmi'
icon_state = "frames_2"
+24 -12
View File
@@ -1,4 +1,4 @@
// PARTS //
k// PARTS //
/obj/item/weaponcrafting
icon = 'icons/obj/improvised.dmi'
@@ -8,33 +8,44 @@
custom_materials = list(/datum/material/wood = MINERAL_MATERIAL_AMOUNT * 6)
icon_state = "riflestock"
/obj/item/weaponcrafting/string
name = "wound thread"
desc = "A long piece of thread with some resemblance to cable coil."
/obj/item/weaponcrafting/durathread_string
name = "durathread string"
desc = "A long piece of durathread with some resemblance to cable coil."
icon_state = "durastring"
////////////////////////////////
// IMPROVISED WEAPON PARTS//
// KAT IMPROVISED WEAPON PARTS//
////////////////////////////////
/obj/item/weaponcrafting/improvised_parts
name = "Debug Improvised Gun Part"
desc = "A badly coded gun part. You should report coders if you see this."
name = "Eerie bunch of coloured dots."
desc = "You feel the urge to report to Central that the parent type of guncrafting, which should never appear in this reality, has appeared. Whatever that means."
icon = 'icons/obj/guns/gun_parts.dmi'
icon_state = "palette"
// BARRELS
/obj/item/weaponcrafting/improvised_parts/barrel_rifle
name = "rifle barrel"
desc = "A pipe with a diameter just the right size to fire 7.62 rounds out of."
icon_state = "barrel_rifle"
/obj/item/weaponcrafting/improvised_parts/barrel_shotgun
name = "shotgun barrel"
desc = "A twenty bore shotgun barrel."
icon_state = "barrel_shotgun"
// RECEIVERS
/obj/item/weaponcrafting/improvised_parts/rifle_receiver
name = "rifle receiver"
desc = "A crudely constructed receiver to create an improvised bolt-action breechloaded rifle." // removed some text implying that the item had more uses than it does
name = "bolt action receiver"
desc = "A crudely constructed receiver to create an improvised bolt-action breechloaded rifle."
icon_state = "receiver_rifle"
w_class = WEIGHT_CLASS_SMALL
/obj/item/weaponcrafting/improvised_parts/shotgun_receiver
name = "shotgun reciever"
desc = "An improvised receiver to create a break-action breechloaded shotgun." // removed some text implying that the item had more uses than it does
name = "break-action assembly"
desc = "An improvised receiver to create a break-action breechloaded shotgun."
icon_state = "receiver_shotgun"
w_class = WEIGHT_CLASS_SMALL
@@ -50,3 +61,4 @@
name = "wooden firearm body"
desc = "A crudely fashioned wooden body to help keep higher calibre improvised weapons from blowing themselves apart."
icon_state = "wooden_body"
@@ -92,7 +92,7 @@
time = 150
subcategory = CAT_MISCELLANEOUS
category = CAT_MISC
always_availible = FALSE // Disabled until learned
always_availible = FALSE // Disabled til learned
/datum/crafting_recipe/bloodsucker/candelabrum
@@ -124,9 +124,9 @@
category = CAT_MISC
subcategory = CAT_TOOL
/datum/crafting_recipe/brute_pack
name = "Suture Pack"
result = /obj/item/stack/medical/suture/one
/datum/crafting_recipe/bruise_pack
name = "Bruise Pack"
result = /obj/item/stack/medical/bruise_pack/one
time = 1
reqs = list(/obj/item/stack/medical/gauze = 1,
/datum/reagent/medicine/styptic_powder = 10)
@@ -134,8 +134,8 @@
subcategory = CAT_TOOL
/datum/crafting_recipe/burn_pack
name = "Regenerative Mesh"
result = /obj/item/stack/medical/mesh/one
name = "Burn Ointment"
result = /obj/item/stack/medical/ointment/one
time = 1
reqs = list(/obj/item/stack/medical/gauze = 1,
/datum/reagent/medicine/silver_sulfadiazine = 10)
@@ -244,7 +244,7 @@
/datum/crafting_recipe/rcl
name = "Makeshift Rapid Cable Layer"
result = /obj/item/rcl/ghetto
result = /obj/item/twohanded/rcl/ghetto
time = 40
tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WRENCH)
reqs = list(/obj/item/stack/sheet/metal = 15)
@@ -50,7 +50,7 @@
/datum/crafting_recipe/bonespear
name = "Bone Spear"
result = /obj/item/spear/bonespear
result = /obj/item/twohanded/bonespear
time = 30
reqs = list(/obj/item/stack/sheet/bone = 4,
/obj/item/stack/sheet/sinew = 1)
@@ -58,7 +58,7 @@
/datum/crafting_recipe/boneaxe
name = "Bone Axe"
result = /obj/item/fireaxe/boneaxe
result = /obj/item/twohanded/fireaxe/boneaxe
time = 50
reqs = list(/obj/item/stack/sheet/bone = 6,
/obj/item/stack/sheet/sinew = 3)
@@ -74,20 +74,20 @@
/datum/crafting_recipe/headpike
name = "Spike Head (Glass Spear)"
time = 65
reqs = list(/obj/item/spear = 1,
reqs = list(/obj/item/twohanded/spear = 1,
/obj/item/bodypart/head = 1)
parts = list(/obj/item/bodypart/head = 1,
/obj/item/spear = 1)
/obj/item/twohanded/spear = 1)
result = /obj/structure/headpike
category = CAT_PRIMAL
/datum/crafting_recipe/headpikebone
name = "Spike Head (Bone Spear)"
time = 65
reqs = list(/obj/item/spear/bonespear = 1,
reqs = list(/obj/item/twohanded/bonespear = 1,
/obj/item/bodypart/head = 1)
parts = list(/obj/item/bodypart/head = 1,
/obj/item/spear/bonespear = 1)
/obj/item/twohanded/bonespear = 1)
result = /obj/structure/headpike/bone
category = CAT_PRIMAL
@@ -103,7 +103,7 @@
/datum/crafting_recipe/bone_bow
name = "Bone Bow"
result = /obj/item/gun/ballistic/bow/ashen
time = 120 // 80+120 = 200
time = 200
always_availible = FALSE
reqs = list(/obj/item/stack/sheet/bone = 8,
/obj/item/stack/sheet/sinew = 4)
@@ -112,7 +112,7 @@
/datum/crafting_recipe/bow_tablet
name = "Sandstone Bow Making Manual"
result = /obj/item/book/granter/crafting_recipe/bone_bow
time = 200 //Scribing // don't care
time = 600 //Scribing
always_availible = FALSE
reqs = list(/obj/item/stack/rods = 1,
/obj/item/stack/sheet/mineral/sandstone = 4)
@@ -6,7 +6,7 @@
tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
time = 50
category = CAT_WEAPONRY
subcategory = CAT_OTHER
subcategory = CAT_WEAPON
/datum/crafting_recipe/pin_removal/check_requirements(mob/user, list/collected_requirements)
var/obj/item/gun/G = collected_requirements[/obj/item/gun][1]
@@ -22,7 +22,7 @@
/obj/item/shield/riot = 1)
time = 40
category = CAT_WEAPONRY
subcategory = CAT_MELEE
subcategory = CAT_WEAPON
/datum/crafting_recipe/strobeshield/New()
..()
@@ -38,18 +38,18 @@
tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
time = 100
category = CAT_WEAPONRY
subcategory = CAT_MELEE
subcategory = CAT_WEAPON
/datum/crafting_recipe/spear
name = "Spear"
result = /obj/item/spear
result = /obj/item/twohanded/spear
reqs = list(/obj/item/restraints/handcuffs/cable = 1,
/obj/item/shard = 1,
/obj/item/stack/rods = 1)
parts = list(/obj/item/shard = 1)
time = 40
category = CAT_WEAPONRY
subcategory = CAT_MELEE
subcategory = CAT_WEAPON
/datum/crafting_recipe/stunprod
name = "Stunprod"
@@ -59,7 +59,7 @@
/obj/item/assembly/igniter = 1)
time = 40
category = CAT_WEAPONRY
subcategory = CAT_MELEE
subcategory = CAT_WEAPON
/datum/crafting_recipe/teleprod
name = "Teleprod"
@@ -70,7 +70,7 @@
/obj/item/stack/ore/bluespace_crystal = 1)
time = 40
category = CAT_WEAPONRY
subcategory = CAT_MELEE
subcategory = CAT_WEAPON
/datum/crafting_recipe/bola
name = "Bola"
@@ -88,7 +88,7 @@
/obj/item/stack/sheet/metal = 1)
time = 40
category = CAT_WEAPONRY
subcategory = CAT_MELEE
subcategory = CAT_WEAPON
/datum/crafting_recipe/tailwhip
name = "Liz O' Nine Tails"
@@ -97,7 +97,7 @@
/obj/item/stack/cable_coil = 1)
time = 40
category = CAT_WEAPONRY
subcategory = CAT_MELEE
subcategory = CAT_WEAPON
/datum/crafting_recipe/catwhip
name = "Cat O' Nine Tails"
@@ -106,18 +106,18 @@
/obj/item/stack/cable_coil = 1)
time = 40
category = CAT_WEAPONRY
subcategory = CAT_MELEE
subcategory = CAT_WEAPON
/datum/crafting_recipe/chainsaw
name = "Chainsaw"
result = /obj/item/chainsaw
result = /obj/item/twohanded/required/chainsaw
reqs = list(/obj/item/circular_saw = 1,
/obj/item/stack/cable_coil = 3,
/obj/item/stack/sheet/plasteel = 5)
tools = list(TOOL_WELDER)
time = 50
category = CAT_WEAPONRY
subcategory = CAT_MELEE
subcategory = CAT_WEAPON
//////////////////
///BOMB CRAFTING//
@@ -134,20 +134,20 @@
parts = list(/obj/item/stock_parts/matter_bin = 1, /obj/item/grenade/chem_grenade = 2)
time = 30
category = CAT_WEAPONRY
subcategory = CAT_OTHER
subcategory = CAT_WEAPON
/datum/crafting_recipe/chemical_payload2
name = "Chemical Payload (Gibtonite)"
result = /obj/item/bombcore/chemical
reqs = list(
/obj/item/stock_parts/matter_bin = 1,
/obj/item/gibtonite = 1,
/obj/item/twohanded/required/gibtonite = 1,
/obj/item/grenade/chem_grenade = 2
)
parts = list(/obj/item/stock_parts/matter_bin = 1, /obj/item/grenade/chem_grenade = 2)
time = 50
category = CAT_WEAPONRY
subcategory = CAT_OTHER
subcategory = CAT_WEAPON
/datum/crafting_recipe/molotov
name = "Molotov"
@@ -169,18 +169,18 @@
parts = list(/obj/item/reagent_containers/food/drinks/soda_cans = 1)
time = 15
category = CAT_WEAPONRY
subcategory = CAT_OTHER
subcategory = CAT_WEAPON
/datum/crafting_recipe/lance
name = "Explosive Lance (Grenade)"
result = /obj/item/spear
reqs = list(/obj/item/spear = 1,
result = /obj/item/twohanded/spear
reqs = list(/obj/item/twohanded/spear = 1,
/obj/item/grenade = 1)
parts = list(/obj/item/spear = 1,
parts = list(/obj/item/twohanded/spear = 1,
/obj/item/grenade = 1)
time = 15
category = CAT_WEAPONRY
subcategory = CAT_MELEE
subcategory = CAT_WEAPON
//////////////////
///GUNS CRAFTING//
@@ -192,8 +192,8 @@
result = /obj/item/gun/ballistic/bow/pipe
reqs = list(/obj/item/pipe = 5,
/obj/item/stack/sheet/plastic = 15,
/obj/item/weaponcrafting/string = 5)
time = 150
/obj/item/weaponcrafting/durathread_string = 5)
time = 450
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
@@ -248,10 +248,10 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
/datum/crafting_recipe/ishotgun // smaller and more versatile gun requires some better materials
/datum/crafting_recipe/ishotgun
name = "Improvised Shotgun"
result = /obj/item/gun/ballistic/revolver/doublebarrel/improvised
reqs = list(/obj/item/pipe = 2, // putting a large amount of meaningless timegates by forcing people to turn base resources into upgraded resources kinda sucks
reqs = list(/obj/item/weaponcrafting/improvised_parts/barrel_shotgun = 1,
/obj/item/weaponcrafting/improvised_parts/shotgun_receiver = 1,
/obj/item/weaponcrafting/improvised_parts/trigger_assembly = 1,
/obj/item/weaponcrafting/improvised_parts/wooden_body = 1,
@@ -262,10 +262,10 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
/datum/crafting_recipe/irifle // larger and less versatile gun, but a bit easier to make
/datum/crafting_recipe/irifle
name = "Improvised Rifle (7.62mm)"
result = /obj/item/gun/ballistic/shotgun/boltaction/improvised
reqs = list(/obj/item/pipe = 2, // above
reqs = list(/obj/item/weaponcrafting/improvised_parts/barrel_rifle = 1,
/obj/item/weaponcrafting/improvised_parts/rifle_receiver = 1,
/obj/item/weaponcrafting/improvised_parts/trigger_assembly = 1,
/obj/item/weaponcrafting/improvised_parts/wooden_body = 1,
@@ -283,9 +283,9 @@
/datum/crafting_recipe/arrow
name = "Arrow"
result = /obj/item/ammo_casing/caseless/arrow/wood
time = 5 // these only do 15 damage
time = 30
reqs = list(/obj/item/stack/sheet/mineral/wood = 1,
/obj/item/stack/sheet/cloth = 1,
/obj/item/stack/sheet/durathread = 1,
/obj/item/stack/rods = 1) // 1 metal sheet = 2 rods = 2 arrows
category = CAT_WEAPONRY
subcategory = CAT_AMMO
@@ -293,7 +293,7 @@
/datum/crafting_recipe/bone_arrow
name = "Bone Arrow"
result = /obj/item/ammo_casing/caseless/arrow/bone
time = 5
time = 30
always_availible = FALSE
reqs = list(/obj/item/stack/sheet/bone = 1,
/obj/item/stack/sheet/sinew = 1,
@@ -305,7 +305,7 @@
name = "Ashen Arrow"
result = /obj/item/ammo_casing/caseless/arrow/ash
tools = list(TOOL_WELDER)
time = 10 // 1.5 seconds minimum per actually worthwhile arrow excluding interface lag
time = 30
always_availible = FALSE
reqs = list(/obj/item/ammo_casing/caseless/arrow/wood = 1)
category = CAT_WEAPONRY
@@ -403,24 +403,44 @@
// PARTS CRAFTING //
////////////////////
// BARRELS
/datum/crafting_recipe/rifle_barrel
name = "Improvised Rifle Barrel"
result = /obj/item/weaponcrafting/improvised_parts/barrel_rifle
reqs = list(/obj/item/pipe = 2)
tools = list(TOOL_WELDER,TOOL_SAW)
time = 150
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/shotgun_barrel
name = "Improvised Shotgun Barrel"
result = /obj/item/weaponcrafting/improvised_parts/barrel_shotgun
reqs = list(/obj/item/pipe = 2)
tools = list(TOOL_WELDER,TOOL_SAW)
time = 150
category = CAT_WEAPONRY
subcategory = CAT_PARTS
// RECEIVERS
/datum/crafting_recipe/rifle_receiver
name = "Improvised Rifle Receiver"
result = /obj/item/weaponcrafting/improvised_parts/rifle_receiver
reqs = list(/obj/item/stack/sheet/metal = 15) // you can carry multiple shotguns
tools = list(TOOL_SCREWDRIVER, TOOL_WELDER)
time = 25
reqs = list(/obj/item/stack/sheet/metal = 20)
tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) // Rifle is the easiest to craft and can be made at an autolathe, this is a very light kick in the shin for dual-wielding ishotguns.
time = 50
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/shotgun_receiver
name = "Improvised Shotgun Receiver"
result = /obj/item/weaponcrafting/improvised_parts/shotgun_receiver
reqs = list(/obj/item/stack/sheet/metal = 15,
/obj/item/stack/sheet/plasteel = 1) // requires access or hacking since shotgun is better
tools = list(TOOL_SCREWDRIVER, TOOL_WELDER)
time = 25
reqs = list(/obj/item/stack/sheet/metal = 10,
/obj/item/stack/sheet/plasteel = 1)
tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) // Increased cost is to stop dual-wield alpha striking. ishotgun is a rvolver and can be duel-wielded
time = 50
category = CAT_WEAPONRY
subcategory = CAT_PARTS
@@ -432,6 +452,6 @@
reqs = list(/obj/item/stack/sheet/metal = 3,
/obj/item/assembly/igniter = 1)
tools = list(TOOL_SCREWDRIVER, TOOL_WELDER)
time = 25
time = 150
category = CAT_WEAPONRY
subcategory = CAT_PARTS
-244
View File
@@ -1,244 +0,0 @@
/*!
This component makes it possible to make things edible. What this means is that you can take a bite or force someone to take a bite (in the case of items).
These items take a specific time to eat, and can do most of the things our original food items could.
Behavior that's still missing from this component that original food items had that should either be put into seperate components or somewhere else:
Components:
Drying component (jerky etc)
Customizable component (custom pizzas etc)
Processable component (Slicing and cooking behavior essentialy, making it go from item A to B when conditions are met.)
Dunkable component (Dunking things into reagent containers to absorb a specific amount of reagents)
Misc:
Something for cakes (You can store things inside)
*/
/datum/component/edible
///Amount of reagents taken per bite
var/bite_consumption = 2
///Amount of bites taken so far
var/bitecount = 0
///Flags for food
var/food_flags = NONE
///Bitfield of the types of this food
var/foodtypes = NONE
///Amount of seconds it takes to eat this food
var/eat_time = 30
///Defines how much it lowers someones satiety (Need to eat, essentialy)
var/junkiness = 0
///Message to send when eating
var/list/eatverbs
///Callback to be ran for when you take a bite of something
var/datum/callback/after_eat
///Last time we checked for food likes
var/last_check_time
/datum/component/edible/Initialize(list/initial_reagents, food_flags = NONE, foodtypes = NONE, volume = 50, eat_time = 30, list/tastes, list/eatverbs = list("bite","chew","nibble","gnaw","gobble","chomp"), bite_consumption = 2, datum/callback/after_eat)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examine)
RegisterSignal(parent, COMSIG_ATOM_ATTACK_ANIMAL, .proc/UseByAnimal)
if(isitem(parent))
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/UseFromHand)
else if(isturf(parent))
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, .proc/TryToEatTurf)
src.bite_consumption = bite_consumption
src.food_flags = food_flags
src.foodtypes = foodtypes
src.eat_time = eat_time
src.eatverbs = eatverbs
src.junkiness = junkiness
src.after_eat = after_eat
var/atom/owner = parent
owner.create_reagents(volume, INJECTABLE)
if(initial_reagents)
for(var/rid in initial_reagents)
var/amount = initial_reagents[rid]
if(tastes && tastes.len && (rid == /datum/reagent/consumable/nutriment || rid == /datum/reagent/consumable/nutriment/vitamin))
owner.reagents.add_reagent(rid, amount, tastes.Copy())
else
owner.reagents.add_reagent(rid, amount)
/datum/component/edible/proc/examine(datum/source, mob/user, list/examine_list)
if(!(food_flags & FOOD_IN_CONTAINER))
switch (bitecount)
if (0)
return
if(1)
examine_list += "[parent] was bitten by someone!"
if(2,3)
examine_list += "[parent] was bitten [bitecount] times!"
else
examine_list += "[parent] was bitten multiple times!"
/datum/component/edible/proc/UseFromHand(obj/item/source, mob/living/M, mob/living/user)
return TryToEat(M, user)
/datum/component/edible/proc/TryToEatTurf(datum/source, mob/user)
return TryToEat(user, user)
///All the checks for the act of eating itself and
/datum/component/edible/proc/TryToEat(mob/living/eater, mob/living/feeder)
set waitfor = FALSE
var/atom/owner = parent
if(feeder.a_intent == INTENT_HARM)
return
if(!owner.reagents.total_volume)//Shouldn't be needed but it checks to see if it has anything left in it.
to_chat(feeder, "<span class='warning'>None of [owner] left, oh no!</span>")
if(isturf(parent))
var/turf/T = parent
T.ScrapeAway(1, CHANGETURF_INHERIT_AIR)
else
qdel(parent)
return
if(!CanConsume(eater, feeder))
return
var/fullness = eater.nutrition + 10 //The theoretical fullness of the person eating if they were to eat this
for(var/datum/reagent/consumable/C in eater.reagents.reagent_list) //we add the nutrition value of what we're currently digesting
fullness += C.nutriment_factor * C.volume / C.metabolization_rate
. = COMPONENT_ITEM_NO_ATTACK //Point of no return I suppose
if(eater == feeder)//If you're eating it yourself.
if(!do_mob(feeder, eater, eat_time)) //Gotta pass the minimal eat time
return
var/eatverb = pick(eatverbs)
if(junkiness && eater.satiety < -150 && eater.nutrition > NUTRITION_LEVEL_STARVING + 50 && !HAS_TRAIT(eater, TRAIT_VORACIOUS))
to_chat(eater, "<span class='warning'>You don't feel like eating any more junk food at the moment!</span>")
return
else if(fullness <= 50)
eater.visible_message("<span class='notice'>[eater] hungrily [eatverb]s \the [parent], gobbling it down!</span>", "<span class='notice'>You hungrily [eatverb] \the [parent], gobbling it down!</span>")
else if(fullness > 50 && fullness < 150)
eater.visible_message("<span class='notice'>[eater] hungrily [eatverb]s \the [parent].</span>", "<span class='notice'>You hungrily [eatverb] \the [parent].</span>")
else if(fullness > 150 && fullness < 500)
eater.visible_message("<span class='notice'>[eater] [eatverb]s \the [parent].</span>", "<span class='notice'>You [eatverb] \the [parent].</span>")
else if(fullness > 500 && fullness < 600)
eater.visible_message("<span class='notice'>[eater] unwillingly [eatverb]s a bit of \the [parent].</span>", "<span class='notice'>You unwillingly [eatverb] a bit of \the [parent].</span>")
else if(fullness > (600 * (1 + eater.overeatduration / 2000))) // The more you eat - the more you can eat
eater.visible_message("<span class='warning'>[eater] cannot force any more of \the [parent] to go down [eater.p_their()] throat!</span>", "<span class='warning'>You cannot force any more of \the [parent] to go down your throat!</span>")
return
else //If you're feeding it to someone else.
if(isbrain(eater))
to_chat(feeder, "<span class='warning'>[eater] doesn't seem to have a mouth!</span>")
return
if(fullness <= (600 * (1 + eater.overeatduration / 1000)))
eater.visible_message("<span class='danger'>[feeder] attempts to feed [eater] [parent].</span>", \
"<span class='userdanger'>[feeder] attempts to feed you [parent].</span>")
else
eater.visible_message("<span class='warning'>[feeder] cannot force any more of [parent] down [eater]'s throat!</span>", \
"<span class='warning'>[feeder] cannot force any more of [parent] down your throat!</span>")
return
if(!do_mob(feeder, eater)) //Wait 3 seconds before you can feed
return
log_combat(feeder, eater, "fed", owner.reagents.log_list())
eater.visible_message("<span class='danger'>[feeder] forces [eater] to eat [parent]!</span>", \
"<span class='userdanger'>[feeder] forces you to eat [parent]!</span>")
TakeBite(eater, feeder)
///This function lets the eater take a bite and transfers the reagents to the eater.
/datum/component/edible/proc/TakeBite(mob/living/eater, mob/living/feeder)
var/atom/owner = parent
if(!owner?.reagents)
return FALSE
if(eater.satiety > -200)
eater.satiety -= junkiness
playsound(eater.loc,'sound/items/eatfood.ogg', rand(10,50), TRUE)
if(owner.reagents.total_volume)
SEND_SIGNAL(parent, COMSIG_FOOD_EATEN, eater, feeder)
var/fraction = min(bite_consumption / owner.reagents.total_volume, 1)
owner.reagents.reaction(eater, INGEST, fraction)
owner.reagents.trans_to(eater, bite_consumption)
bitecount++
On_Consume(eater)
checkLiked(fraction, eater)
//Invoke our after eat callback if it is valid
if(after_eat)
after_eat.Invoke(eater, feeder)
return TRUE
///Checks whether or not the eater can actually consume the food
/datum/component/edible/proc/CanConsume(mob/living/eater, mob/living/feeder)
if(!iscarbon(eater))
return FALSE
var/mob/living/carbon/C = eater
var/covered = ""
if(C.is_mouth_covered(head_only = 1))
covered = "headgear"
else if(C.is_mouth_covered(mask_only = 1))
covered = "mask"
if(covered)
var/who = (isnull(feeder) || eater == feeder) ? "your" : "[eater.p_their()]"
to_chat(feeder, "<span class='warning'>You have to remove [who] [covered] first!</span>")
return FALSE
return TRUE
///Check foodtypes to see if we should send a moodlet
/datum/component/edible/proc/checkLiked(var/fraction, mob/M)
if(last_check_time + 50 > world.time)
return FALSE
if(!ishuman(M))
return FALSE
var/mob/living/carbon/human/H = M
if(HAS_TRAIT(H, TRAIT_AGEUSIA) && foodtypes & H.dna.species.toxic_food)
to_chat(H, "<span class='warning'>You don't feel so good...</span>")
H.adjust_disgust(25 + 30 * fraction)
else
if(foodtypes & H.dna.species.toxic_food)
to_chat(H,"<span class='warning'>What the hell was that thing?!</span>")
H.adjust_disgust(25 + 30 * fraction)
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "toxic_food", /datum/mood_event/disgusting_food)
else if(foodtypes & H.dna.species.disliked_food)
to_chat(H,"<span class='notice'>That didn't taste very good...</span>")
H.adjust_disgust(11 + 15 * fraction)
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "gross_food", /datum/mood_event/gross_food)
else if(foodtypes & H.dna.species.liked_food)
to_chat(H,"<span class='notice'>I love this taste!</span>")
H.adjust_disgust(-5 + -2.5 * fraction)
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "fav_food", /datum/mood_event/favorite_food)
if((foodtypes & BREAKFAST) && world.time - SSticker.round_start_time < STOP_SERVING_BREAKFAST)
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "breakfast", /datum/mood_event/breakfast)
last_check_time = world.time
///Delete the item when it is fully eaten
/datum/component/edible/proc/On_Consume(mob/living/eater)
var/atom/owner = parent
if(!eater)
return
if(!owner.reagents.total_volume)
if(isturf(parent))
var/turf/T = parent
T.ScrapeAway(1, CHANGETURF_INHERIT_AIR)
else
qdel(parent)
///Ability to feed food to puppers
/datum/component/edible/proc/UseByAnimal(datum/source, mob/user)
var/atom/owner = parent
if(!isdog(user))
return
var/mob/living/L = user
if(bitecount == 0 || prob(50))
L.emote("me", 1, "nibbles away at \the [parent]")
bitecount++
. = COMPONENT_ITEM_NO_ATTACK
L.taste(owner.reagents) // why should carbons get all the fun?
if(bitecount >= 5)
var/sattisfaction_text = pick("burps from enjoyment", "yaps for more", "woofs twice", "looks at the area where \the [parent] was")
if(sattisfaction_text)
L.emote("me", 1, "[sattisfaction_text]")
qdel(parent)
-363
View File
@@ -1,363 +0,0 @@
/*
This component is responsible for handling individual instances of embedded objects. The embeddable element is what allows an item to be embeddable and stores its embedding stats,
and when it impacts and meets the requirements to stick into something, it instantiates an embedded component. Once the item falls out, the component is destroyed, while the
element survives to embed another day.
There are 2 different things that can be embedded presently: carbons, and closed turfs (see: walls)
- Carbon embedding has all the classical embedding behavior, and tracks more events and signals. The main behaviors and hooks to look for are:
-- Every process tick, there is a chance to randomly proc pain, controlled by pain_chance. There may also be a chance for the object to fall out randomly, per fall_chance
-- Every time the mob moves, there is a chance to proc jostling pain, controlled by jostle_chance (and only 50% as likely if the mob is walking or crawling)
-- Various signals hooking into carbon topic() and the embed removal surgery in order to handle removals.
- Turf embedding is much simpler. All we do here is draw an overlay of the item's inhand on the turf, hide the item, and create an HTML link in the turf's inspect
that allows you to rip the item out. There's nothing dynamic about this, so far less checks.
In addition, there are 2 cases of embedding: embedding, and sticking
- Embedding involves harmful and dangerous embeds, whether they cause brute damage, stamina damage, or a mix. This is the default behavior for embeddings, for when something is "pointy"
- Sticking occurs when an item should not cause any harm while embedding (imagine throwing a sticky ball of tape at someone, rather than a shuriken). An item is considered "sticky"
when it has 0 for both pain multiplier and jostle pain multiplier. It's a bit arbitrary, but fairly straightforward.
Stickables differ from embeds in the following ways:
-- Text descriptors use phrasing like "X is stuck to Y" rather than "X is embedded in Y"
-- There is no slicing sound on impact
-- All damage checks and bloodloss are skipped for carbons
-- Pointy objects create sparks when embedding into a turf
*/
/datum/component/embedded
dupe_mode = COMPONENT_DUPE_ALLOWED
var/obj/item/bodypart/limb
var/obj/item/weapon
// all of this stuff is explained in _DEFINES/combat.dm
var/embed_chance // not like we really need it once we're already stuck in but hey
var/fall_chance
var/pain_chance
var/pain_mult
var/impact_pain_mult
var/remove_pain_mult
var/rip_time
var/ignore_throwspeed_threshold
var/jostle_chance
var/jostle_pain_mult
var/pain_stam_pct
var/embed_chance_turf_mod
///if both our pain multiplier and jostle pain multiplier are 0, we're harmless and can omit most of the damage related stuff
var/harmful
var/mutable_appearance/overlay
/datum/component/embedded/Initialize(obj/item/I,
datum/thrownthing/throwingdatum,
obj/item/bodypart/part,
embed_chance = EMBED_CHANCE,
fall_chance = EMBEDDED_ITEM_FALLOUT,
pain_chance = EMBEDDED_PAIN_CHANCE,
pain_mult = EMBEDDED_PAIN_MULTIPLIER,
remove_pain_mult = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
impact_pain_mult = EMBEDDED_IMPACT_PAIN_MULTIPLIER,
rip_time = EMBEDDED_UNSAFE_REMOVAL_TIME,
ignore_throwspeed_threshold = FALSE,
jostle_chance = EMBEDDED_JOSTLE_CHANCE,
jostle_pain_mult = EMBEDDED_JOSTLE_PAIN_MULTIPLIER,
pain_stam_pct = EMBEDDED_PAIN_STAM_PCT,
embed_chance_turf_mod = EMBED_CHANCE_TURF_MOD)
if((!iscarbon(parent) && !isclosedturf(parent)) || !isitem(I))
return COMPONENT_INCOMPATIBLE
if(part)
limb = part
src.embed_chance = embed_chance
src.fall_chance = fall_chance
src.pain_chance = pain_chance
src.pain_mult = pain_mult
src.remove_pain_mult = remove_pain_mult
src.rip_time = rip_time
src.impact_pain_mult = impact_pain_mult
src.ignore_throwspeed_threshold = ignore_throwspeed_threshold
src.jostle_chance = jostle_chance
src.jostle_pain_mult = jostle_pain_mult
src.pain_stam_pct = pain_stam_pct
src.embed_chance_turf_mod = embed_chance_turf_mod
src.weapon = I
if(!weapon.isEmbedHarmless())
harmful = TRUE
weapon.embedded(parent)
if(iscarbon(parent))
initCarbon()
else if(isclosedturf(parent))
initTurf(throwingdatum)
/datum/component/embedded/RegisterWithParent()
if(iscarbon(parent))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/jostleCheck)
RegisterSignal(parent, COMSIG_CARBON_EMBED_RIP, .proc/ripOutCarbon)
RegisterSignal(parent, COMSIG_CARBON_EMBED_REMOVAL, .proc/safeRemoveCarbon)
else if(isclosedturf(parent))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examineTurf)
RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/itemMoved)
/datum/component/embedded/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_MOVABLE_MOVED, COMSIG_CARBON_EMBED_RIP, COMSIG_CARBON_EMBED_REMOVAL, COMSIG_PARENT_EXAMINE))
/datum/component/embedded/process()
if(iscarbon(parent))
processCarbon()
/datum/component/embedded/Destroy()
if(weapon)
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))
if(overlay)
var/atom/A = parent
A.cut_overlay(overlay, TRUE)
qdel(overlay)
return ..()
////////////////////////////////////////
/////////////HUMAN PROCS////////////////
////////////////////////////////////////
/// Set up an instance of embedding for a carbon. This is basically an extension of Initialize() so not much to say
/datum/component/embedded/proc/initCarbon()
START_PROCESSING(SSdcs, src)
var/mob/living/carbon/victim = parent
if(!istype(limb))
limb = pick(victim.bodyparts)
limb.embedded_objects |= weapon // on the inside... on the inside...
weapon.forceMove(victim)
RegisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING), .proc/byeItemCarbon)
if(harmful)
victim.visible_message("<span class='danger'>[weapon] embeds itself in [victim]'s [limb.name]!</span>",ignored_mobs=victim)
to_chat(victim, "<span class='userdanger'>[weapon] embeds itself in your [limb.name]!</span>")
victim.throw_alert("embeddedobject", /obj/screen/alert/embeddedobject)
playsound(victim,'sound/weapons/bladeslice.ogg', 40)
weapon.add_mob_blood(victim)//it embedded itself in you, of course it's bloody!
var/damage = weapon.w_class * impact_pain_mult
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
SEND_SIGNAL(victim, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded)
else
victim.visible_message("<span class='danger'>[weapon] sticks itself to [victim]'s [limb.name]!</span>",ignored_mobs=victim)
to_chat(victim, "<span class='userdanger'>[weapon] sticks itself to your [limb.name]!</span>")
/// Called every time a carbon with a harmful embed moves, rolling a chance for the item to cause pain. The chance is halved if the carbon is crawling or walking.
/datum/component/embedded/proc/jostleCheck()
var/mob/living/carbon/victim = parent
var/chance = jostle_chance
if(victim.m_intent == MOVE_INTENT_WALK || !(victim.mobility_flags & MOBILITY_STAND))
chance *= 0.5
if(harmful && prob(chance))
var/damage = weapon.w_class * jostle_pain_mult
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
to_chat(victim, "<span class='userdanger'>[weapon] embedded in your [limb.name] jostles and stings!</span>")
/// Called when then item randomly falls out of a carbon. This handles the damage and descriptors, then calls safe_remove()
/datum/component/embedded/proc/fallOutCarbon()
var/mob/living/carbon/victim = parent
if(harmful)
var/damage = weapon.w_class * remove_pain_mult
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
victim.visible_message("<span class='danger'>[weapon] falls out of [victim.name]'s [limb.name]!</span>", ignored_mobs=victim)
to_chat(victim, "<span class='userdanger'>[weapon] falls out of your [limb.name]!</span>")
else
victim.visible_message("<span class='danger'>[weapon] falls off of [victim.name]'s [limb.name]!</span>", ignored_mobs=victim)
to_chat(victim, "<span class='userdanger'>[weapon] falls off of your [limb.name]!</span>")
safeRemoveCarbon()
/// Called when a carbon with an object embedded/stuck to them inspects themselves and clicks the appropriate link to begin ripping the item out. This handles the ripping attempt, descriptors, and dealing damage, then calls safe_remove()
/datum/component/embedded/proc/ripOutCarbon(datum/source, obj/item/I, obj/item/bodypart/limb)
if(I != weapon || src.limb != limb)
return
var/mob/living/carbon/victim = parent
var/time_taken = rip_time * weapon.w_class
victim.visible_message("<span class='warning'>[victim] attempts to remove [weapon] from [victim.p_their()] [limb.name].</span>","<span class='notice'>You attempt to remove [weapon] from your [limb.name]... (It will take [DisplayTimeText(time_taken)].)</span>")
if(do_after(victim, time_taken, target = victim))
if(!weapon || !limb || weapon.loc != victim || !(weapon in limb.embedded_objects))
qdel(src)
return
if(harmful)
var/damage = weapon.w_class * remove_pain_mult
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) //It hurts to rip it out, get surgery you dingus.
victim.emote("scream")
victim.visible_message("<span class='notice'>[victim] successfully rips [weapon] out of [victim.p_their()] [limb.name]!</span>", "<span class='notice'>You successfully remove [weapon] from your [limb.name].</span>")
else
victim.visible_message("<span class='notice'>[victim] successfully rips [weapon] off of [victim.p_their()] [limb.name]!</span>", "<span class='notice'>You successfully remove [weapon] from your [limb.name].</span>")
safeRemoveCarbon(TRUE)
/// This proc handles the final step and actual removal of an embedded/stuck item from a carbon, whether or not it was actually removed safely.
/// Pass TRUE for to_hands if we want it to go to the victim's hands when they pull it out
/datum/component/embedded/proc/safeRemoveCarbon(to_hands)
var/mob/living/carbon/victim = parent
limb.embedded_objects -= weapon
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING)) // have to unhook these here so they don't also register as having disappeared
if(!weapon)
if(!victim.has_embedded_objects())
victim.clear_alert("embeddedobject")
SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded")
qdel(src)
return
if(weapon.unembedded()) // if it deleted itself
weapon = null
if(!victim.has_embedded_objects())
victim.clear_alert("embeddedobject")
SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded")
qdel(src)
return
if(to_hands)
victim.put_in_hands(weapon)
else
weapon.forceMove(get_turf(victim))
if(!victim.has_embedded_objects())
victim.clear_alert("embeddedobject")
SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded")
qdel(src)
/// Something deleted or moved our weapon while it was embedded, how rude!
/datum/component/embedded/proc/byeItemCarbon()
var/mob/living/carbon/victim = parent
limb.embedded_objects -= weapon
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))
if(victim)
to_chat(victim, "<span class='userdanger'>\The [weapon] that was embedded in your [limb.name] disappears!</span>")
if(!victim.has_embedded_objects())
victim.clear_alert("embeddedobject")
SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded")
weapon = null
qdel(src)
/// Items embedded/stuck to carbons both check whether they randomly fall out (if applicable), as well as if the target mob and limb still exists.
/// Items harmfully embedded in carbons have an additional check for random pain (if applicable)
/datum/component/embedded/proc/processCarbon()
var/mob/living/carbon/victim = parent
if(!victim || !limb) // in case the victim and/or their limbs exploded (say, due to a sticky bomb)
weapon.forceMove(get_turf(weapon))
qdel(src)
if(victim.stat == DEAD)
return
var/damage = weapon.w_class * pain_mult
var/chance = pain_chance
if(pain_stam_pct && IS_STAMCRIT(victim)) //if it's a less-lethal embed, give them a break if they're already stamcritted
chance *= 0.3
damage *= 0.7
if(harmful && prob(chance))
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
to_chat(victim, "<span class='userdanger'>[weapon] embedded in your [limb.name] hurts!</span>")
if(prob(fall_chance))
fallOutCarbon()
////////////////////////////////////////
//////////////TURF PROCS////////////////
////////////////////////////////////////
/// Turfs are much lower maintenance, since we don't care if they're in pain, but since they don't bleed or scream, we draw an overlay to show their status.
/// The only difference pointy/sticky items make here is text descriptors and pointy objects making a spark shower on impact.
/datum/component/embedded/proc/initTurf(datum/thrownthing/throwingdatum)
var/turf/closed/hit = parent
// we can't store the item IN the turf (cause turfs are just kinda... there), so we fake it by making the item invisible and bailing if it moves due to a blast
weapon.forceMove(hit)
weapon.invisibility = INVISIBILITY_ABSTRACT
RegisterSignal(weapon, COMSIG_MOVABLE_MOVED, .proc/itemMoved)
var/pixelX = rand(-2, 2)
var/pixelY = rand(-1, 3) // bias this upwards since in-hands are usually on the lower end of the sprite
switch(throwingdatum.init_dir)
if(NORTH)
pixelY -= 2
if(SOUTH)
pixelY += 2
if(WEST)
pixelX += 2
if(EAST)
pixelX -= 2
if(throwingdatum.init_dir in list(NORTH, WEST, NORTHWEST, SOUTHWEST))
overlay = mutable_appearance(icon=weapon.righthand_file,icon_state=weapon.item_state)
else
overlay = mutable_appearance(icon=weapon.lefthand_file,icon_state=weapon.item_state)
var/matrix/M = matrix()
M.Translate(pixelX, pixelY)
overlay.transform = M
hit.add_overlay(overlay, TRUE)
if(harmful)
hit.visible_message("<span class='danger'>[weapon] embeds itself in [hit]!</span>")
playsound(hit,'sound/weapons/bladeslice.ogg', 70)
var/datum/effect_system/spark_spread/sparks = new
sparks.set_up(1, 1, parent)
sparks.attach(parent)
sparks.start()
else
hit.visible_message("<span class='danger'>[weapon] sticks itself to [hit]!</span>")
/datum/component/embedded/proc/examineTurf(datum/source, mob/user, list/examine_list)
if(harmful)
examine_list += "\t <a href='?src=[REF(src)];embedded_object=[REF(weapon)]' class='warning'>There is \a [weapon] embedded in [parent]!</a>"
else
examine_list += "\t <a href='?src=[REF(src)];embedded_object=[REF(weapon)]' class='warning'>There is \a [weapon] stuck to [parent]!</a>"
/// Someone is ripping out the item from the turf by hand
/datum/component/embedded/Topic(datum/source, href_list)
var/mob/living/us = usr
if(in_range(us, parent) && locate(href_list["embedded_object"]) == weapon)
if(harmful)
us.visible_message("<span class='notice'>[us] begins unwedging [weapon] from [parent].</span>", "<span class='notice'>You begin unwedging [weapon] from [parent]...</span>")
else
us.visible_message("<span class='notice'>[us] begins unsticking [weapon] from [parent].</span>", "<span class='notice'>You begin unsticking [weapon] from [parent]...</span>")
if(do_after(us, 30, target = parent))
us.put_in_hands(weapon)
weapon.unembedded()
qdel(src)
/// This proc handles if something knocked the invisible item loose from the turf somehow (probably an explosion). Just make it visible and say it fell loose, then get outta here.
/datum/component/embedded/proc/itemMoved()
weapon.invisibility = initial(weapon.invisibility)
weapon.visible_message("<span class='notice'>[weapon] falls loose from [parent].</span>")
weapon.unembedded()
qdel(src)
+1 -1
View File
@@ -144,7 +144,7 @@
var/obj/item/projectile/picked_projectiletype = pickweight(weighted_projectile_types)
var/obj/item/master = comp.parent
comp.appliedComponents += master.AddComponent(/datum/component/mirv, picked_projectiletype)
comp.appliedComponents += master.AddComponent(/datum/component/shrapnel, picked_projectiletype)
return "[newName] of [initial(picked_projectiletype.name)] shrapnel"
/datum/fantasy_affix/strength
-74
View File
@@ -1,74 +0,0 @@
/**
* omen.dm: For when you want someone to have a really bad day
*
* When you attach an omen component to someone, they start running the risk of all sorts of bad environmental injuries, like nearby vending machines randomly falling on you,
* or hitting your head really hard when you slip and fall, or... well, for now those two are all I have. More will come.
*
* Omens are removed once the victim is either maimed by one of the possible injuries, or if they receive a blessing (read: bashing with a bible) from the chaplain.
*/
/datum/component/omen
dupe_mode = COMPONENT_DUPE_UNIQUE
/// Whatever's causing the omen, if there is one. Destroying the vessel won't stop the omen, but we destroy the vessel (if one exists) upon the omen ending
var/obj/vessel
/datum/component/omen/Initialize(silent=FALSE, vessel)
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
var/mob/person = parent
if(!silent)
to_chat(person, "<span class='warning'>You get a bad feeling...</span>")
src.vessel = vessel
/datum/component/omen/Destroy(force, silent)
if(vessel)
vessel.visible_message("<span class='warning'>[vessel] burns up in a sinister flash, taking an evil energy with it...</span>")
vessel = null
return ..()
/datum/component/omen/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/check_accident)
RegisterSignal(parent, COMSIG_LIVING_STATUS_KNOCKDOWN, .proc/check_slip)
RegisterSignal(parent, COMSIG_ADD_MOOD_EVENT, .proc/check_bless)
/datum/component/omen/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_LIVING_STATUS_KNOCKDOWN, COMSIG_MOVABLE_MOVED, COMSIG_ADD_MOOD_EVENT))
/**
* check_accident() is called each step we take
*
* While we're walking around, roll to see if there's any environmental hazards (currently only vending machines) on one of the adjacent tiles we can trigger.
* We do the prob() at the beginning to A. add some tension for /when/ it will strike, and B. (more importantly) ameliorate the fact that we're checking up to 5 turfs's contents each time
*/
/datum/component/omen/proc/check_accident(atom/movable/our_guy)
if(!prob(15))
return
for(var/t in get_adjacent_open_turfs(our_guy))
var/turf/the_turf = t
for(var/obj/machinery/vending/darth_vendor in the_turf)
if(darth_vendor.tiltable)
darth_vendor.tilt(our_guy)
qdel(src)
return
/// If we get knocked down, see if we have a really bad slip and bash our head hard
/datum/component/omen/proc/check_slip(mob/living/our_guy, amount)
if(amount <= 0 || prob(50)) // 50% chance to bonk our head
return
var/obj/item/bodypart/the_head = our_guy.get_bodypart(BODY_ZONE_HEAD)
if(!the_head)
return
playsound(get_turf(our_guy), "sound/effects/tableheadsmash.ogg", 90, TRUE)
our_guy.visible_message("<span class='danger'>[our_guy] hits [our_guy.p_their()] head really badly falling down!</span>", "<span class='userdanger'>You hit your head really badly falling down!</span>")
the_head.receive_damage(75)
our_guy.adjustOrganLoss(ORGAN_SLOT_BRAIN, 100)
qdel(src)
/// Hijack the mood system to see if we get the blessing mood event to cancel the omen
/datum/component/omen/proc/check_bless(mob/living/our_guy, category)
if(category != "blessing")
return
to_chat(our_guy, "<span class='nicegreen'>You feel a horrible omen lifted off your shoulders!</span>")
qdel(src)
-283
View File
@@ -1,283 +0,0 @@
/*
* This component is used when you want to create a bunch of shrapnel or projectiles (say, shrapnel from a fragmentation grenade, or buckshot from a shotgun) from a central point,
* without necessarily printing a separate message for every single impact. This component should be instantiated right when you need it (like the moment of firing), then activated
* by signal.
*
* Pellet cloud currently works on two classes of sources: directed (ammo casings), and circular (grenades, landmines).
* -Directed: This means you're shooting multiple pellets, like buckshot. If an ammo casing is defined as having multiple pellets, it will automatically create a pellet cloud
* and call COMSIG_PELLET_CLOUD_INIT (see [/obj/item/ammo_casing/proc/fire_casing]). Thus, the only projectiles fired will be the ones fired here.
* The magnitude var controls how many pellets are created.
* -Circular: This results in a big spray of shrapnel flying all around the detonation point when the grenade fires COMSIG_GRENADE_PRIME or landmine triggers COMSIG_MINE_TRIGGERED.
* The magnitude var controls how big the detonation radius is (the bigger the magnitude, the more shrapnel is created). Grenades can be covered with bodies to reduce shrapnel output.
*
* Once all of the fired projectiles either hit a target or disappear due to ranging out/whatever else, we resolve the list of all the things we hit and print aggregate messages so we get
* one "You're hit by 6 buckshot pellets" vs 6x "You're hit by the buckshot blah blah" messages.
*
* Note that this is how all guns handle shooting ammo casings with multiple pellets, in case such a thing comes up.
*/
/datum/component/pellet_cloud
/// What's the projectile path of the shrapnel we're shooting?
var/projectile_type
/// How many shrapnel projectiles are we responsible for tracking? May be reduced for grenades if someone dives on top of it. Defined by ammo casing for casings, derived from magnitude otherwise
var/num_pellets
/// For grenades/landmines, how big is the radius of turfs we're targeting? Note this does not effect the projectiles range, only how many we generate
var/radius = 4
/// The list of pellets we're responsible for tracking, once these are all accounted for, we finalize.
var/list/pellets = list()
/// An associated list with the atom hit as the key and how many pellets they've eaten for the value, for printing aggregate messages
var/list/targets_hit = list()
/// For grenades, any /mob/living's the grenade is moved onto, see [/datum/component/pellet_cloud/proc/handle_martyrs()]
var/list/bodies
/// For grenades, tracking people who die covering a grenade for achievement purposes, see [/datum/component/pellet_cloud/proc/handle_martyrs()]
var/list/purple_hearts
/// For grenades, tracking how many pellets are removed due to martyrs and how many pellets are added due to the last person to touch it being on top of it
var/pellet_delta = 0
/// how many pellets ranged out without hitting anything
var/terminated
/// how many pellets impacted something
var/hits
/// If the parent tried deleting and we're not done yet, we send it to nullspace then delete it after
var/queued_delete = FALSE
/// for if we're an ammo casing being fired
var/mob/living/shooter
/datum/component/pellet_cloud/Initialize(projectile_type=/obj/item/shrapnel, magnitude=5)
if(!isammocasing(parent) && !isgrenade(parent) && !islandmine(parent))
return COMPONENT_INCOMPATIBLE
if(magnitude < 1)
stack_trace("Invalid magnitude [magnitude] < 1 on pellet_cloud, parent: [parent]")
magnitude = 1
src.projectile_type = projectile_type
if(isammocasing(parent))
num_pellets = magnitude
else if(isgrenade(parent) || islandmine(parent))
radius = magnitude
/datum/component/pellet_cloud/Destroy(force, silent)
purple_hearts = null
pellets = null
targets_hit = null
bodies = null
return ..()
/datum/component/pellet_cloud/RegisterWithParent()
RegisterSignal(parent, COMSIG_PARENT_PREQDELETED, .proc/nullspace_parent)
if(isammocasing(parent))
RegisterSignal(parent, COMSIG_PELLET_CLOUD_INIT, .proc/create_casing_pellets)
else if(isgrenade(parent))
RegisterSignal(parent, COMSIG_GRENADE_ARMED, .proc/grenade_armed)
RegisterSignal(parent, COMSIG_GRENADE_PRIME, .proc/create_blast_pellets)
else if(islandmine(parent))
RegisterSignal(parent, COMSIG_MINE_TRIGGERED, .proc/create_blast_pellets)
/datum/component/pellet_cloud/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_PELLET_CLOUD_INIT, COMSIG_GRENADE_PRIME, COMSIG_GRENADE_ARMED, COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_UNCROSSED, COMSIG_MINE_TRIGGERED, COMSIG_ITEM_DROPPED))
/**
* create_casing_pellets() is for directed pellet clouds for ammo casings that have multiple pellets (buckshot and scatter lasers for instance)
*
* Honestly this is mostly just a rehash of [/obj/item/ammo_casing/proc/fire_casing()] for pellet counts > 1, except this lets us tamper with the pellets and hook onto them for tracking purposes.
* The arguments really don't matter, this proc is triggered by COMSIG_PELLET_CLOUD_INIT which is only for this really, it's just a big mess of the state vars we need for doing the stuff over here.
*/
/datum/component/pellet_cloud/proc/create_casing_pellets(obj/item/ammo_casing/shell, atom/target, mob/living/user, fired_from, randomspread, spread, zone_override, params, distro)
shooter = user
var/targloc = get_turf(target)
if(!zone_override)
zone_override = shooter.zone_selected
for(var/i in 1 to num_pellets)
shell.ready_proj(target, user, SUPPRESSED_VERY, zone_override, fired_from)
if(distro)
if(randomspread)
spread = round((rand() - 0.5) * distro)
else //Smart spread
spread = round((i / num_pellets - 0.5) * distro)
RegisterSignal(shell.BB, COMSIG_PROJECTILE_SELF_ON_HIT, .proc/pellet_hit)
RegisterSignal(shell.BB, list(COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PARENT_QDELETING), .proc/pellet_range)
pellets += shell.BB
if(!shell.throw_proj(target, targloc, shooter, params, spread))
return
if(i != num_pellets)
shell.newshot()
/**
* create_blast_pellets() is for when we have a central point we want to shred the surroundings of with a ring of shrapnel, namely frag grenades and landmines.
*
* Note that grenades have extra handling for someone throwing themselves/being thrown on top of it, while landmines do not (obviously, it's a landmine!). See [/datum/component/pellet_cloud/proc/handle_martyrs()]
*/
/datum/component/pellet_cloud/proc/create_blast_pellets(obj/O, mob/living/lanced_by)
var/atom/A = parent
if(isgrenade(parent)) // handle_martyrs can reduce the radius and thus the number of pellets we produce if someone dives on top of a frag grenade
handle_martyrs(lanced_by) // note that we can modify radius in this proc
if(radius < 1)
return
var/list/all_the_turfs_were_gonna_lacerate = RANGE_TURFS(radius, A) - RANGE_TURFS(radius-1, A)
num_pellets = all_the_turfs_were_gonna_lacerate.len + pellet_delta
for(var/T in all_the_turfs_were_gonna_lacerate)
var/turf/shootat_turf = T
pew(shootat_turf)
/**
* handle_martyrs() is used for grenades that shoot shrapnel to check if anyone threw themselves/were thrown on top of the grenade, thus absorbing a good chunk of the shrapnel
*
* Between the time the grenade is armed and the actual detonation, we set var/list/bodies to the list of mobs currently on the new tile, as if the grenade landed on top of them, tracking if any of them move off the tile and removing them from the "under" list
* Once the grenade detonates, handle_martyrs() is called and gets all the new mobs on the tile, and add the ones not in var/list/bodies to var/list/martyrs
* We then iterate through the martyrs and reduce the shrapnel magnitude for each mob on top of it, shredding each of them with some of the shrapnel they helped absorb. This can snuff out all of the shrapnel if there's enough bodies
*
* Note we track anyone who's alive and client'd when they get shredded in var/list/purple_hearts, for achievement checking later
*/
/datum/component/pellet_cloud/proc/handle_martyrs(mob/living/lanced_by)
var/magnitude_absorbed
var/list/martyrs = list()
var/self_harm_radius_mult = 3
if(lanced_by && prob(60))
to_chat(lanced_by, "<span class='userdanger'>Your plan to whack someone with a grenade on a stick backfires on you, literally!</span>")
self_harm_radius_mult = 1 // we'll still give the guy who got hit some extra shredding, but not 3*radius
pellet_delta += radius
for(var/i in 1 to radius)
pew(lanced_by) // thought you could be tricky and lance someone with no ill effects!!
for(var/mob/living/body in get_turf(parent))
if(body == shooter)
pellet_delta += radius * self_harm_radius_mult
for(var/i in 1 to radius * self_harm_radius_mult)
pew(body) // free shrapnel if it goes off in your hand, and it doesn't even count towards the absorbed. fun!
else if(!(body in bodies))
martyrs += body // promoted from a corpse to a hero
for(var/M in martyrs)
var/mob/living/martyr = M
if(radius > 4)
martyr.visible_message("<b><span class='danger'>[martyr] heroically covers \the [parent] with [martyr.p_their()] body, absorbing a load of the shrapnel!</span></b>", "<span class='userdanger'>You heroically cover \the [parent] with your body, absorbing a load of the shrapnel!</span>")
magnitude_absorbed += round(radius * 0.5)
else if(radius >= 2)
martyr.visible_message("<b><span class='danger'>[martyr] heroically covers \the [parent] with [martyr.p_their()] body, absorbing some of the shrapnel!</span></b>", "<span class='userdanger'>You heroically cover \the [parent] with your body, absorbing some of the shrapnel!</span>")
magnitude_absorbed += 2
else
martyr.visible_message("<b><span class='danger'>[martyr] heroically covers \the [parent] with [martyr.p_their()] body, snuffing out the shrapnel!</span></b>", "<span class='userdanger'>You heroically cover \the [parent] with your body, snuffing out the shrapnel!</span>")
magnitude_absorbed = radius
var/pellets_absorbed = (radius ** 2) - ((radius - magnitude_absorbed - 1) ** 2)
radius -= magnitude_absorbed
pellet_delta -= round(pellets_absorbed * 0.5)
if(martyr.stat != DEAD && martyr.client)
LAZYADD(purple_hearts, martyr)
RegisterSignal(martyr, COMSIG_PARENT_QDELETING, .proc/on_target_qdel, override=TRUE)
for(var/i in 1 to round(pellets_absorbed * 0.5))
pew(martyr)
if(radius < 1)
break
///One of our pellets hit something, record what it was and check if we're done (terminated == num_pellets)
/datum/component/pellet_cloud/proc/pellet_hit(obj/item/projectile/P, atom/movable/firer, atom/target, Angle)
pellets -= P
terminated++
hits++
targets_hit[target]++
if(targets_hit[target] == 1)
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/on_target_qdel, override=TRUE)
UnregisterSignal(P, list(COMSIG_PARENT_QDELETING, COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PROJECTILE_SELF_ON_HIT))
if(terminated == num_pellets)
finalize()
///One of our pellets disappeared due to hitting their max range (or just somehow got qdel'd), remove it from our list and check if we're done (terminated == num_pellets)
/datum/component/pellet_cloud/proc/pellet_range(obj/item/projectile/P)
pellets -= P
terminated++
UnregisterSignal(P, list(COMSIG_PARENT_QDELETING, COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PROJECTILE_SELF_ON_HIT))
if(terminated == num_pellets)
finalize()
/// Minor convenience function for creating each shrapnel piece with circle explosions, mostly stolen from the MIRV component
/datum/component/pellet_cloud/proc/pew(atom/target, spread=0)
var/obj/item/projectile/P = new projectile_type(get_turf(parent))
//Shooting Code:
P.spread = spread
P.original = target
P.fired_from = parent
P.firer = parent // don't hit ourself that would be really annoying
P.permutated += parent // don't hit the target we hit already with the flak
P.suppressed = SUPPRESSED_VERY // set the projectiles to make no message so we can do our own aggregate message
P.preparePixelProjectile(target, parent)
RegisterSignal(P, COMSIG_PROJECTILE_SELF_ON_HIT, .proc/pellet_hit)
RegisterSignal(P, list(COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PARENT_QDELETING), .proc/pellet_range)
pellets += P
P.fire()
///All of our pellets are accounted for, time to go target by target and tell them how many things they got hit by.
/datum/component/pellet_cloud/proc/finalize()
var/obj/item/projectile/P = projectile_type
var/proj_name = initial(P.name)
for(var/atom/target in targets_hit)
var/num_hits = targets_hit[target]
UnregisterSignal(target, COMSIG_PARENT_QDELETING)
if(num_hits > 1)
target.visible_message("<span class='danger'>[target] is hit by [num_hits] [proj_name]s!</span>", null, null, COMBAT_MESSAGE_RANGE, target)
to_chat(target, "<span class='userdanger'>You're hit by [num_hits] [proj_name]s!</span>")
else
target.visible_message("<span class='danger'>[target] is hit by a [proj_name]!</span>", null, null, COMBAT_MESSAGE_RANGE, target)
to_chat(target, "<span class='userdanger'>You're hit by a [proj_name]!</span>")
UnregisterSignal(parent, COMSIG_PARENT_PREQDELETED)
if(queued_delete)
qdel(parent)
qdel(src)
/// Look alive, we're armed! Now we start watching to see if anyone's covering us
/datum/component/pellet_cloud/proc/grenade_armed(obj/item/nade)
if(ismob(nade.loc))
shooter = nade.loc
LAZYINITLIST(bodies)
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/grenade_dropped)
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/grenade_moved)
RegisterSignal(parent, COMSIG_MOVABLE_UNCROSSED, .proc/grenade_uncrossed)
/// Someone dropped the grenade, so set them to the shooter in case they're on top of it when it goes off
/datum/component/pellet_cloud/proc/grenade_dropped(obj/item/nade, mob/living/slick_willy)
shooter = slick_willy
grenade_moved()
/// Our grenade has moved, reset var/list/bodies so we're "on top" of any mobs currently on the tile
/datum/component/pellet_cloud/proc/grenade_moved()
LAZYCLEARLIST(bodies)
for(var/mob/living/L in get_turf(parent))
RegisterSignal(L, COMSIG_PARENT_QDELETING, .proc/on_target_qdel, override=TRUE)
bodies += L
/// Someone who was originally "under" the grenade has moved off the tile and is now eligible for being a martyr and "covering" it
/datum/component/pellet_cloud/proc/grenade_uncrossed(datum/source, atom/movable/AM)
bodies -= AM
/// Our grenade or landmine or caseless shell or whatever tried deleting itself, so we intervene and nullspace it until we're done here
/datum/component/pellet_cloud/proc/nullspace_parent()
var/atom/movable/AM = parent
AM.moveToNullspace()
queued_delete = TRUE
return TRUE
/// Someone who was originally "under" the grenade has moved off the tile and is now eligible for being a martyr and "covering" it
/datum/component/pellet_cloud/proc/on_target_qdel(atom/target)
UnregisterSignal(target, COMSIG_PARENT_QDELETING)
targets_hit -= target
bodies -= target
purple_hearts -= target
+2 -3
View File
@@ -69,9 +69,8 @@
out += "[out ? " and it " : "[master] "]seems to be glowing a bit."
if(RAD_AMOUNT_HIGH to INFINITY) //At this level the object can contaminate other objects
out += "[out ? " and it " : "[master] "]hurts to look at."
if(!LAZYLEN(out))
return
out += "."
else
out += "."
examine_list += out.Join()
/datum/component/radioactive/proc/rad_attack(datum/source, atom/movable/target, mob/living/user)
@@ -1,37 +1,36 @@
/datum/component/mirv
/datum/component/shrapnel
var/projectile_type
var/radius // shoots a projectile for every turf on this radius from the hit target
var/override_projectile_range
/datum/component/mirv/Initialize(projectile_type, radius=1, override_projectile_range)
if(!isgun(parent) && !ismachinery(parent) && !isstructure(parent) && !isgrenade(parent))
/datum/component/shrapnel/Initialize(projectile_type, radius=1, override_projectile_range)
if(!isgun(parent) && !ismachinery(parent) && !isstructure(parent))
return COMPONENT_INCOMPATIBLE
src.projectile_type = projectile_type
src.radius = radius
src.override_projectile_range = override_projectile_range
if(isgrenade(parent))
parent.AddComponent(/datum/component/pellet_cloud, projectile_type=projectile_type)
/datum/component/mirv/RegisterWithParent()
/datum/component/shrapnel/RegisterWithParent()
. = ..()
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
/datum/component/mirv/UnregisterFromParent()
/datum/component/shrapnel/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_PROJECTILE_ON_HIT))
/datum/component/mirv/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
/datum/component/shrapnel/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
do_shrapnel(firer, target)
/datum/component/mirv/proc/do_shrapnel(mob/firer, atom/target)
/datum/component/shrapnel/proc/do_shrapnel(mob/firer, atom/target)
if(radius < 1)
return
var/turf/target_turf = get_turf(target)
for(var/turf/shootat_turf in RANGE_TURFS(radius, target) - RANGE_TURFS(radius-1, target))
var/obj/item/projectile/P = new projectile_type(target_turf)
//Shooting Code:
P.range = radius+1
if(override_projectile_range)
+3 -10
View File
@@ -8,8 +8,6 @@
var/list/faction = list("mining")
/datum/component/spawner/Initialize(_mob_types, _spawn_time, _faction, _spawn_text, _max_mobs)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
if(_spawn_time)
spawn_time=_spawn_time
if(_mob_types)
@@ -21,25 +19,20 @@
if(_max_mobs)
max_mobs=_max_mobs
RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/stop_spawning)
RegisterSignal(parent, COMSIG_OBJ_ATTACK_GENERIC, .proc/on_attack_generic)
RegisterSignal(parent, list(COMSIG_PARENT_QDELETING), .proc/stop_spawning)
START_PROCESSING(SSprocessing, src)
/datum/component/spawner/process()
try_spawn_mob()
/datum/component/spawner/proc/stop_spawning(datum/source, force, hint)
/datum/component/spawner/proc/stop_spawning(force, hint)
STOP_PROCESSING(SSprocessing, src)
for(var/mob/living/simple_animal/L in spawned_mobs)
if(L.nest == src)
L.nest = null
spawned_mobs = null
// Stopping clientless simple mobs' from indiscriminately bashing their own spawners due DestroySurroundings() et similars.
/datum/component/spawner/proc/on_attack_generic(datum/source, mob/user, damage_amount, damage_type, damage_flag, sound_effect, armor_penetration)
if(!user.client && ((user.faction & faction) || (user in spawned_mobs)))
return COMPONENT_STOP_GENERIC_ATTACK
/datum/component/spawner/proc/try_spawn_mob()
var/atom/P = parent
if(spawned_mobs.len >= max_mobs)
@@ -34,8 +34,3 @@
qdel(A)
return
. = ..()
/datum/component/storage/concrete/bluespace/bag_of_holding/can_be_inserted(obj/item/I, stop_messages = FALSE, mob/M)
if(I.GetComponent(/datum/component/storage/concrete/bluespace/bag_of_holding))
return TRUE
return ..()
-11
View File
@@ -11,17 +11,6 @@
RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, .proc/join_swarm)
RegisterSignal(parent, COMSIG_MOVABLE_UNCROSSED, .proc/leave_swarm)
/datum/component/swarming/Destroy()
if(is_swarming)
for(var/A in swarm_members)
var/datum/component/swarming/other_swarm = A
other_swarm.swarm_members -= src
swarm_members -= other_swarm
if(!length(other_swarm.swarm_members))
other_swarm.unswarm()
unswarm()
return ..()
/datum/component/swarming/proc/join_swarm(datum/source, atom/movable/AM)
var/datum/component/swarming/other_swarm = AM.GetComponent(/datum/component/swarming)
if(!other_swarm)
+5 -5
View File
@@ -206,7 +206,7 @@
user.set_resting(FALSE, TRUE, FALSE)
user.forceMove(get_turf(target))
target.adjustStaminaLoss(65)
target.Paralyze(10)
target.Paralyze(10)
target.DefaultCombatKnockdown(20)
if(ishuman(target) && iscarbon(user))
target.grabbedby(user)
@@ -354,7 +354,7 @@
playsound(user, 'sound/effects/blobattack.ogg', 60, TRUE)
playsound(user, 'sound/effects/splat.ogg', 70, TRUE)
user.emote("scream")
user.gain_trauma(/datum/brain_trauma/severe/paralysis/spinesnapped) // oopsie indeed!
user.gain_trauma(/datum/brain_trauma/severe/paralysis/paraplegic) // oopsie indeed!
shake_camera(user, 7, 7)
user.overlay_fullscreen("flash", /obj/screen/fullscreen/flash)
user.clear_fullscreen("flash", 4.5)
@@ -415,10 +415,10 @@
for(var/i = 0, i < speed, i++)
var/obj/item/shard/shard = new /obj/item/shard(get_turf(user))
//shard.embedding = list(embed_chance = 100, ignore_throwspeed_threshold = TRUE, impact_pain_mult=3, pain_chance=5)
shard.updateEmbedding()
//shard.AddElement(/datum/element/embed, shard.embedding)
user.hitby(shard, skipcatch = TRUE, hitpush = FALSE)
shard.embedding = list()
shard.updateEmbedding()
//shard.embedding = list()
//shard.AddElement(/datum/element/embed, shard.embedding)
W.obj_destruction()
user.adjustStaminaLoss(10 * speed)
user.DefaultCombatKnockdown(40)
-311
View File
@@ -1,311 +0,0 @@
/**
* Two Handed Component
*
* When applied to an item it will make it two handed
*
*/
/datum/component/two_handed
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS // Only one of the component can exist on an item
var/wielded = FALSE /// Are we holding the two handed item properly
var/force_multiplier = 0 /// The multiplier applied to force when wielded, does not work with force_wielded, and force_unwielded
var/force_wielded = 0 /// The force of the item when weilded
var/force_unwielded = 0 /// The force of the item when unweilded
var/wieldsound = FALSE /// Play sound when wielded
var/unwieldsound = FALSE /// Play sound when unwielded
var/attacksound = FALSE /// Play sound on attack when wielded
var/require_twohands = FALSE /// Does it have to be held in both hands
var/icon_wielded = FALSE /// The icon that will be used when wielded
var/obj/item/offhand/offhand_item = null /// Reference to the offhand created for the item
var/sharpened_increase = 0 /// The amount of increase recived from sharpening the item
/**
* Two Handed component
*
* vars:
* * require_twohands (optional) Does the item need both hands to be carried
* * wieldsound (optional) The sound to play when wielded
* * unwieldsound (optional) The sound to play when unwielded
* * attacksound (optional) The sound to play when wielded and attacking
* * force_multiplier (optional) The force multiplier when wielded, do not use with force_wielded, and force_unwielded
* * force_wielded (optional) The force setting when the item is wielded, do not use with force_multiplier
* * force_unwielded (optional) The force setting when the item is unwielded, do not use with force_multiplier
* * icon_wielded (optional) The icon to be used when wielded
*/
/datum/component/two_handed/Initialize(require_twohands=FALSE, wieldsound=FALSE, unwieldsound=FALSE, attacksound=FALSE, \
force_multiplier=0, force_wielded=0, force_unwielded=0, icon_wielded=FALSE)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
src.require_twohands = require_twohands
src.wieldsound = wieldsound
src.unwieldsound = unwieldsound
src.attacksound = attacksound
src.force_multiplier = force_multiplier
src.force_wielded = force_wielded
src.force_unwielded = force_unwielded
src.icon_wielded = icon_wielded
// Inherit the new values passed to the component
/datum/component/two_handed/InheritComponent(datum/component/two_handed/new_comp, original, require_twohands, wieldsound, unwieldsound, \
force_multiplier, force_wielded, force_unwielded, icon_wielded)
if(!original)
return
if(require_twohands)
src.require_twohands = require_twohands
if(wieldsound)
src.wieldsound = wieldsound
if(unwieldsound)
src.unwieldsound = unwieldsound
if(attacksound)
src.attacksound = attacksound
if(force_multiplier)
src.force_multiplier = force_multiplier
if(force_wielded)
src.force_wielded = force_wielded
if(force_unwielded)
src.force_unwielded = force_unwielded
if(icon_wielded)
src.icon_wielded = icon_wielded
// register signals withthe parent item
/datum/component/two_handed/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop)
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/on_attack_self)
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/on_attack)
RegisterSignal(parent, COMSIG_ATOM_UPDATE_ICON, .proc/on_update_icon)
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_moved)
RegisterSignal(parent, COMSIG_ITEM_SHARPEN_ACT, .proc/on_sharpen)
// Remove all siginals registered to the parent item
/datum/component/two_handed/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED,
COMSIG_ITEM_DROPPED,
COMSIG_ITEM_ATTACK_SELF,
COMSIG_ITEM_ATTACK,
COMSIG_ATOM_UPDATE_ICON,
COMSIG_MOVABLE_MOVED,
COMSIG_ITEM_SHARPEN_ACT))
/// Triggered on equip of the item containing the component
/datum/component/two_handed/proc/on_equip(datum/source, mob/user, slot)
if(require_twohands && slot == SLOT_HANDS) // force equip the item
wield(user)
if(!user.is_holding(parent) && wielded && !require_twohands)
unwield(user)
/// Triggered on drop of item containing the component
/datum/component/two_handed/proc/on_drop(datum/source, mob/user)
if(require_twohands)
unwield(user, show_message=TRUE)
if(wielded)
unwield(user)
if(source == offhand_item && !QDELETED(src))
qdel(src)
/// Triggered on attack self of the item containing the component
/datum/component/two_handed/proc/on_attack_self(datum/source, mob/user)
if(wielded)
unwield(user)
else
wield(user)
/**
* Wield the two handed item in both hands
*
* vars:
* * user The mob/living/carbon that is wielding the item
*/
/datum/component/two_handed/proc/wield(mob/living/carbon/user)
if(wielded)
return
if(ismonkey(user))
to_chat(user, "<span class='warning'>It's too heavy for you to wield fully.</span>")
return
if(user.get_inactive_held_item())
if(require_twohands)
to_chat(user, "<span class='notice'>[parent] is too cumbersome to carry in one hand!</span>")
user.dropItemToGround(parent, force=TRUE)
else
to_chat(user, "<span class='warning'>You need your other hand to be empty!</span>")
return
if(user.get_num_arms() < 2)
if(require_twohands)
user.dropItemToGround(parent, force=TRUE)
to_chat(user, "<span class='warning'>You don't have enough intact hands.</span>")
return
// wield update status
if(SEND_SIGNAL(parent, COMSIG_TWOHANDED_WIELD, user) & COMPONENT_TWOHANDED_BLOCK_WIELD)
return // blocked wield from item
wielded = TRUE
RegisterSignal(user, COMSIG_MOB_SWAP_HANDS, .proc/on_swap_hands)
// update item stats and name
var/obj/item/parent_item = parent
if(force_multiplier)
parent_item.force *= force_multiplier
else if(force_wielded)
parent_item.force = force_wielded
if(sharpened_increase)
parent_item.force += sharpened_increase
parent_item.name = "[parent_item.name] (Wielded)"
parent_item.update_icon()
if(iscyborg(user))
to_chat(user, "<span class='notice'>You dedicate your module to [parent].</span>")
else
to_chat(user, "<span class='notice'>You grab [parent] with both hands.</span>")
// Play sound if one is set
if(wieldsound)
playsound(parent_item.loc, wieldsound, 50, TRUE)
// Let's reserve the other hand
offhand_item = new(user)
offhand_item.name = "[parent_item.name] - offhand"
offhand_item.desc = "Your second grip on [parent_item]."
offhand_item.wielded = TRUE
RegisterSignal(offhand_item, COMSIG_ITEM_DROPPED, .proc/on_drop)
user.put_in_inactive_hand(offhand_item)
/**
* Unwield the two handed item
*
* vars:
* * user The mob/living/carbon that is unwielding the item
* * show_message (option) show a message to chat on unwield
*/
/datum/component/two_handed/proc/unwield(mob/living/carbon/user, show_message=TRUE)
if(!wielded || !user)
return
// wield update status
wielded = FALSE
UnregisterSignal(user, COMSIG_MOB_SWAP_HANDS)
SEND_SIGNAL(parent, COMSIG_TWOHANDED_UNWIELD, user)
// update item stats
var/obj/item/parent_item = parent
if(sharpened_increase)
parent_item.force -= sharpened_increase
if(force_multiplier)
parent_item.force /= force_multiplier
else if(force_unwielded)
parent_item.force = force_unwielded
// update the items name to remove the wielded status
var/sf = findtext(parent_item.name, " (Wielded)", -10) // 10 == length(" (Wielded)")
if(sf)
parent_item.name = copytext(parent_item.name, 1, sf)
else
parent_item.name = "[initial(parent_item.name)]"
// Update icons
parent_item.update_icon()
if(user.get_item_by_slot(ITEM_SLOT_BACK) == parent)
user.update_inv_back()
else
user.update_inv_hands()
// if the item requires two handed drop the item on unwield
if(require_twohands)
user.dropItemToGround(parent, force=TRUE)
// Show message if requested
if(show_message)
if(iscyborg(user))
to_chat(user, "<span class='notice'>You free up your module.</span>")
else if(require_twohands)
to_chat(user, "<span class='notice'>You drop [parent].</span>")
else
to_chat(user, "<span class='notice'>You are now carrying [parent] with one hand.</span>")
// Play sound if set
if(unwieldsound)
playsound(parent_item.loc, unwieldsound, 50, TRUE)
// Remove the object in the offhand
if(offhand_item)
UnregisterSignal(offhand_item, COMSIG_ITEM_DROPPED)
qdel(offhand_item)
// Clear any old refrence to an item that should be gone now
offhand_item = null
/**
* on_attack triggers on attack with the parent item
*/
/datum/component/two_handed/proc/on_attack(obj/item/source, mob/living/target, mob/living/user)
if(wielded && attacksound)
var/obj/item/parent_item = parent
playsound(parent_item.loc, attacksound, 50, TRUE)
/**
* on_update_icon triggers on call to update parent items icon
*
* Updates the icon using icon_wielded if set
*/
/datum/component/two_handed/proc/on_update_icon(datum/source)
if(icon_wielded && wielded)
var/obj/item/parent_item = parent
if(parent_item)
parent_item.icon_state = icon_wielded
return COMSIG_ATOM_NO_UPDATE_ICON_STATE
/**
* on_moved Triggers on item moved
*/
/datum/component/two_handed/proc/on_moved(datum/source, mob/user, dir)
unwield(user)
/**
* on_swap_hands Triggers on swapping hands, blocks swap if the other hand is busy
*/
/datum/component/two_handed/proc/on_swap_hands(mob/user, obj/item/held_item)
if(!held_item)
return
if(held_item == parent)
return COMPONENT_BLOCK_SWAP
/**
* on_sharpen Triggers on usage of a sharpening stone on the item
*/
/datum/component/two_handed/proc/on_sharpen(obj/item/item, amount, max_amount)
if(!item)
return COMPONENT_BLOCK_SHARPEN_BLOCKED
if(sharpened_increase)
return COMPONENT_BLOCK_SHARPEN_ALREADY
var/wielded_val = 0
if(force_multiplier)
var/obj/item/parent_item = parent
if(wielded)
wielded_val = parent_item.force
else
wielded_val = parent_item.force * force_multiplier
else
wielded_val = force_wielded
if(wielded_val > max_amount)
return COMPONENT_BLOCK_SHARPEN_MAXED
sharpened_increase = min(amount, (max_amount - wielded_val))
return COMPONENT_BLOCK_SHARPEN_APPLIED
/**
* The offhand dummy item for two handed items
*
*/
/obj/item/offhand
name = "offhand"
icon_state = "offhand"
w_class = WEIGHT_CLASS_HUGE
item_flags = ABSTRACT
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/wielded = FALSE // Off Hand tracking of wielded status
/obj/item/offhand/Destroy()
wielded = FALSE
return ..()
/obj/item/offhand/equipped(mob/user, slot)
. = ..()
if(wielded && !user.is_holding(src) && !QDELETED(src))
qdel(src)