mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-10 09:54:52 +00:00
Merge pull request #9679 from Ghommie/Ghommie-cit302
Ports "Replaces the rpg loot datum with a component and makes some suffixes have real effects" & co
This commit is contained in:
45
code/datums/components/bane.dm
Normal file
45
code/datums/components/bane.dm
Normal file
@@ -0,0 +1,45 @@
|
||||
/datum/component/bane
|
||||
dupe_mode = COMPONENT_DUPE_ALLOWED
|
||||
|
||||
var/mobtype
|
||||
var/speciestype
|
||||
var/damage_multiplier
|
||||
|
||||
/datum/component/bane/Initialize(mobtype, damage_multiplier=1)
|
||||
if(!isitem(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
if(ispath(mobtype, /mob/living))
|
||||
src.mobtype = mobtype
|
||||
else if(ispath(mobtype, /datum/species))
|
||||
speciestype = mobtype
|
||||
else
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.damage_multiplier = damage_multiplier
|
||||
|
||||
/datum/component/bane/RegisterWithParent()
|
||||
if(speciestype)
|
||||
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/speciesCheck)
|
||||
else
|
||||
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/mobCheck)
|
||||
|
||||
/datum/component/bane/UnregisterFromParent()
|
||||
UnregisterSignal(parent, COMSIG_ITEM_AFTERATTACK)
|
||||
|
||||
/datum/component/bane/proc/speciesCheck(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
|
||||
if(!is_species(target, speciestype))
|
||||
return
|
||||
activate(source, target, user)
|
||||
|
||||
/datum/component/bane/proc/mobCheck(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
|
||||
if(!istype(target, mobtype))
|
||||
return
|
||||
activate(source, target, user)
|
||||
|
||||
/datum/component/bane/proc/activate(obj/item/source, mob/living/target, mob/attacker)
|
||||
if(attacker.a_intent != INTENT_HARM)
|
||||
return
|
||||
|
||||
var/extra_damage = max(0, source.force * damage_multiplier)
|
||||
target.apply_damage(extra_damage, source.damtype, attacker.zone_selected)
|
||||
140
code/datums/components/fantasy/_fantasy.dm
Normal file
140
code/datums/components/fantasy/_fantasy.dm
Normal file
@@ -0,0 +1,140 @@
|
||||
/datum/component/fantasy
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
|
||||
|
||||
var/quality
|
||||
|
||||
var/canFail
|
||||
var/announce
|
||||
|
||||
var/originalName
|
||||
var/list/affixes
|
||||
var/list/appliedComponents
|
||||
|
||||
var/static/list/affixListing
|
||||
|
||||
/datum/component/fantasy/Initialize(quality, list/affixes = list(), canFail=FALSE, announce=FALSE)
|
||||
if(!isitem(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.quality = quality || randomQuality()
|
||||
src.canFail = canFail
|
||||
src.announce = announce
|
||||
|
||||
src.affixes = affixes
|
||||
appliedComponents = list()
|
||||
randomAffixes()
|
||||
|
||||
/datum/component/fantasy/Destroy()
|
||||
unmodify()
|
||||
affixes = null
|
||||
return ..()
|
||||
|
||||
/datum/component/fantasy/RegisterWithParent()
|
||||
var/obj/item/master = parent
|
||||
originalName = master.name
|
||||
modify()
|
||||
|
||||
/datum/component/fantasy/UnregisterFromParent()
|
||||
unmodify()
|
||||
|
||||
/datum/component/fantasy/InheritComponent(datum/component/fantasy/newComp, original, list/arguments)
|
||||
unmodify()
|
||||
if(newComp)
|
||||
quality += newComp.quality
|
||||
canFail = newComp.canFail
|
||||
announce = newComp.announce
|
||||
else
|
||||
arguments.len = 5 // This is done to replicate what happens when an arglist smaller than the necessary arguments is given
|
||||
quality += arguments[1]
|
||||
canFail = arguments[4] || canFail
|
||||
announce = arguments[5] || announce
|
||||
modify()
|
||||
|
||||
/datum/component/fantasy/proc/randomQuality()
|
||||
var/quality = pick(1;15, 2;14, 2;13, 2;12, 3;11, 3;10, 3;9, 4;8, 4;7, 4;6, 5;5, 5;4, 5;3, 6;2, 6;1, 6;0)
|
||||
if(prob(50))
|
||||
quality = -quality
|
||||
return quality
|
||||
|
||||
/datum/component/fantasy/proc/randomAffixes(force)
|
||||
if(!affixListing)
|
||||
affixListing = list()
|
||||
for(var/T in subtypesof(/datum/fantasy_affix))
|
||||
var/datum/fantasy_affix/affix = new T
|
||||
affixListing[affix] = affix.weight
|
||||
|
||||
if(length(affixes))
|
||||
if(!force)
|
||||
return
|
||||
affixes = list()
|
||||
|
||||
var/alignment
|
||||
if(quality >= 0)
|
||||
alignment |= AFFIX_GOOD
|
||||
if(quality <= 0)
|
||||
alignment |= AFFIX_EVIL
|
||||
|
||||
var/usedSlots = NONE
|
||||
for(var/i in 1 to max(1, abs(quality))) // We want at least 1 affix applied
|
||||
var/datum/fantasy_affix/affix = pickweight(affixListing)
|
||||
if(affix.placement & usedSlots)
|
||||
continue
|
||||
if(!(affix.alignment & alignment))
|
||||
continue
|
||||
if(!affix.validate(src))
|
||||
continue
|
||||
affixes += affix
|
||||
usedSlots |= affix.placement
|
||||
|
||||
/datum/component/fantasy/proc/modify()
|
||||
var/obj/item/master = parent
|
||||
|
||||
master.force = max(0, master.force + quality)
|
||||
master.throwforce = max(0, master.throwforce + quality)
|
||||
master.armor = master.armor?.modifyAllRatings(quality)
|
||||
|
||||
var/newName = originalName
|
||||
for(var/i in affixes)
|
||||
var/datum/fantasy_affix/affix = i
|
||||
newName = affix.apply(src, newName)
|
||||
|
||||
if(quality != 0)
|
||||
newName = "[newName] [quality > 0 ? "+" : ""][quality]"
|
||||
|
||||
if(canFail && prob((quality - 9)*10))
|
||||
var/turf/place = get_turf(parent)
|
||||
place.visible_message("<span class='danger'>[parent] <span class='blue'>violently glows blue</span> for a while, then evaporates.</span>")
|
||||
master.burn()
|
||||
return
|
||||
else if(announce)
|
||||
announce()
|
||||
|
||||
master.name = newName
|
||||
|
||||
/datum/component/fantasy/proc/unmodify()
|
||||
var/obj/item/master = parent
|
||||
|
||||
for(var/i in affixes)
|
||||
var/datum/fantasy_affix/affix = i
|
||||
affix.remove(src)
|
||||
for(var/i in appliedComponents)
|
||||
qdel(i)
|
||||
|
||||
master.force = max(0, master.force - quality)
|
||||
master.throwforce = max(0, master.throwforce - quality)
|
||||
master.armor = master.armor?.modifyAllRatings(-quality)
|
||||
|
||||
master.name = originalName
|
||||
|
||||
/datum/component/fantasy/proc/announce()
|
||||
var/turf/location = get_turf(parent)
|
||||
var/span
|
||||
var/effect_description
|
||||
if(quality >= 0)
|
||||
span = "<span class='notice'>"
|
||||
effect_description = "<span class='heavy_brass'>shimmering golden glow</span>"
|
||||
else
|
||||
span = "<span class='danger'>"
|
||||
effect_description = "<span class='bold'>mottled black glow</span>"
|
||||
|
||||
location.visible_message("[span][originalName] is covered by a [effect_description] and then transforms into [parent]!</span>")
|
||||
13
code/datums/components/fantasy/affix.dm
Normal file
13
code/datums/components/fantasy/affix.dm
Normal file
@@ -0,0 +1,13 @@
|
||||
/datum/fantasy_affix
|
||||
var/placement // A bitflag of "slots" this affix takes up, for example pre/suffix
|
||||
var/alignment
|
||||
var/weight = 10
|
||||
|
||||
// For those occasional affixes which only make sense in certain circumstances
|
||||
/datum/fantasy_affix/proc/validate(datum/component/fantasy/comp)
|
||||
return TRUE
|
||||
|
||||
/datum/fantasy_affix/proc/apply(datum/component/fantasy/comp, newName)
|
||||
return newName
|
||||
|
||||
/datum/fantasy_affix/proc/remove(datum/component/fantasy/comp)
|
||||
68
code/datums/components/fantasy/prefixes.dm
Normal file
68
code/datums/components/fantasy/prefixes.dm
Normal file
@@ -0,0 +1,68 @@
|
||||
/datum/fantasy_affix/cosmetic_prefixes
|
||||
placement = AFFIX_PREFIX
|
||||
alignment = AFFIX_GOOD | AFFIX_EVIL
|
||||
|
||||
var/list/goodPrefixes
|
||||
var/list/badPrefixes
|
||||
|
||||
/datum/fantasy_affix/cosmetic_prefixes/New()
|
||||
goodPrefixes = list(
|
||||
"greater",
|
||||
"major",
|
||||
"blessed",
|
||||
"superior",
|
||||
"empowered",
|
||||
"honed",
|
||||
"true",
|
||||
"glorious",
|
||||
"robust",
|
||||
)
|
||||
badPrefixes = list(
|
||||
"lesser",
|
||||
"minor",
|
||||
"blighted",
|
||||
"inferior",
|
||||
"enfeebled",
|
||||
"rusted",
|
||||
"unsteady",
|
||||
"tragic",
|
||||
"gimped",
|
||||
"cursed",
|
||||
)
|
||||
|
||||
weight = (length(goodPrefixes) + length(badPrefixes)) * 10
|
||||
|
||||
/datum/fantasy_affix/cosmetic_prefixes/apply(datum/component/fantasy/comp, newName)
|
||||
if(comp.quality > 0 || (comp.quality == 0 && prob(50)))
|
||||
return "[pick(goodPrefixes)] [newName]"
|
||||
else
|
||||
return "[pick(badPrefixes)] [newName]"
|
||||
|
||||
/datum/fantasy_affix/tactical
|
||||
placement = AFFIX_PREFIX
|
||||
alignment = AFFIX_GOOD
|
||||
weight = 1 // Very powerful, no one should have such power
|
||||
|
||||
/datum/fantasy_affix/tactical/apply(datum/component/fantasy/comp, newName)
|
||||
var/obj/item/master = comp.parent
|
||||
comp.appliedComponents += master.AddComponent(/datum/component/tactical)
|
||||
return "tactical [newName]"
|
||||
|
||||
/datum/fantasy_affix/pyromantic
|
||||
placement = AFFIX_PREFIX
|
||||
alignment = AFFIX_GOOD
|
||||
|
||||
/datum/fantasy_affix/pyromantic/apply(datum/component/fantasy/comp, newName)
|
||||
var/obj/item/master = comp.parent
|
||||
comp.appliedComponents += master.AddComponent(/datum/component/igniter, CLAMP(comp.quality, 1, 10))
|
||||
return "pyromantic [newName]"
|
||||
|
||||
/datum/fantasy_affix/vampiric
|
||||
placement = AFFIX_PREFIX
|
||||
alignment = AFFIX_GOOD
|
||||
weight = 5
|
||||
|
||||
/datum/fantasy_affix/vampiric/apply(datum/component/fantasy/comp, newName)
|
||||
var/obj/item/master = comp.parent
|
||||
comp.appliedComponents += master.AddComponent(/datum/component/lifesteal, comp.quality)
|
||||
return "vampiric [newName]"
|
||||
170
code/datums/components/fantasy/suffixes.dm
Normal file
170
code/datums/components/fantasy/suffixes.dm
Normal file
@@ -0,0 +1,170 @@
|
||||
/datum/fantasy_affix/cosmetic_suffixes
|
||||
placement = AFFIX_SUFFIX
|
||||
alignment = AFFIX_GOOD | AFFIX_EVIL
|
||||
|
||||
var/list/goodSuffixes
|
||||
var/list/badSuffixes
|
||||
|
||||
/datum/fantasy_affix/cosmetic_suffixes/New()
|
||||
goodSuffixes = list(
|
||||
"dexterity",
|
||||
"constitution",
|
||||
"intelligence",
|
||||
"wisdom",
|
||||
"charisma",
|
||||
"the forest",
|
||||
"the hills",
|
||||
"the plains",
|
||||
"the sea",
|
||||
"the sun",
|
||||
"the moon",
|
||||
"the void",
|
||||
"the world",
|
||||
"many secrets",
|
||||
"many tales",
|
||||
"many colors",
|
||||
"rending",
|
||||
"sundering",
|
||||
"the night",
|
||||
"the day",
|
||||
)
|
||||
badSuffixes = list(
|
||||
"draining",
|
||||
"burden",
|
||||
"discomfort",
|
||||
"awkwardness",
|
||||
"poor hygiene",
|
||||
"timidity",
|
||||
)
|
||||
|
||||
weight = (length(goodSuffixes) + length(badSuffixes)) * 10
|
||||
|
||||
/datum/fantasy_affix/cosmetic_suffixes/apply(datum/component/fantasy/comp, newName)
|
||||
if(comp.quality > 0 || (comp.quality == 0 && prob(50)))
|
||||
return "[newName] of [pick(goodSuffixes)]"
|
||||
else
|
||||
return "[newName] of [pick(badSuffixes)]"
|
||||
|
||||
//////////// Good suffixes
|
||||
/datum/fantasy_affix/bane
|
||||
placement = AFFIX_SUFFIX
|
||||
alignment = AFFIX_GOOD
|
||||
weight = 20
|
||||
|
||||
/datum/fantasy_affix/bane/apply(datum/component/fantasy/comp, newName)
|
||||
. = ..()
|
||||
// This is set up to be easy to add to these lists as I expect it will need modifications
|
||||
var/static/list/possible_mobtypes
|
||||
if(!possible_mobtypes)
|
||||
// The base list of allowed mob/species types
|
||||
possible_mobtypes = typecacheof(list(
|
||||
/mob/living/simple_animal,
|
||||
/mob/living/carbon,
|
||||
/datum/species,
|
||||
))
|
||||
// Some particular types to disallow if they're too broad/abstract
|
||||
possible_mobtypes -= list(
|
||||
/mob/living/simple_animal/hostile,
|
||||
)
|
||||
// Some types to remove them and their subtypes
|
||||
possible_mobtypes -= typecacheof(list(
|
||||
/mob/living/carbon/human/species,
|
||||
))
|
||||
|
||||
var/mob/picked_mobtype = pick(possible_mobtypes)
|
||||
// This works even with the species picks since we're only accessing the name
|
||||
|
||||
var/obj/item/master = comp.parent
|
||||
comp.appliedComponents += master.AddComponent(/datum/component/bane, picked_mobtype)
|
||||
return "[newName] of [initial(picked_mobtype.name)] slaying"
|
||||
|
||||
/datum/fantasy_affix/summoning
|
||||
placement = AFFIX_SUFFIX
|
||||
alignment = AFFIX_GOOD
|
||||
weight = 5
|
||||
|
||||
/datum/fantasy_affix/summoning/apply(datum/component/fantasy/comp, newName)
|
||||
. = ..()
|
||||
// This is set up to be easy to add to these lists as I expect it will need modifications
|
||||
var/static/list/possible_mobtypes
|
||||
if(!possible_mobtypes)
|
||||
// The base list of allowed mob/species types
|
||||
possible_mobtypes = typecacheof(list(
|
||||
/mob/living/simple_animal,
|
||||
/mob/living/carbon,
|
||||
/datum/species,
|
||||
))
|
||||
// Some particular types to disallow if they're too broad/abstract
|
||||
possible_mobtypes -= list(
|
||||
/mob/living/simple_animal/hostile,
|
||||
)
|
||||
// Some types to remove them and their subtypes
|
||||
possible_mobtypes -= typecacheof(list(
|
||||
/mob/living/carbon/human/species,
|
||||
/mob/living/simple_animal/hostile/megafauna,
|
||||
))
|
||||
|
||||
var/mob/picked_mobtype = pick(possible_mobtypes)
|
||||
// This works even with the species picks since we're only accessing the name
|
||||
|
||||
var/obj/item/master = comp.parent
|
||||
var/max_mobs = max(CEILING(comp.quality/2, 1), 1)
|
||||
var/spawn_delay = 300 - 30 * comp.quality
|
||||
comp.appliedComponents += master.AddComponent(/datum/component/summoning, list(picked_mobtype), 100, max_mobs, spawn_delay)
|
||||
return "[newName] of [initial(picked_mobtype.name)] summoning"
|
||||
|
||||
/datum/fantasy_affix/shrapnel
|
||||
placement = AFFIX_SUFFIX
|
||||
alignment = AFFIX_GOOD
|
||||
|
||||
/datum/fantasy_affix/shrapnel/validate(datum/component/fantasy/comp)
|
||||
if(isgun(comp.parent))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/fantasy_affix/shrapnel/apply(datum/component/fantasy/comp, newName)
|
||||
. = ..()
|
||||
// higher means more likely
|
||||
var/list/weighted_projectile_types = list(/obj/item/projectile/meteor = 1,
|
||||
/obj/item/projectile/energy/nuclear_particle = 1,
|
||||
/obj/item/projectile/beam/pulse = 1,
|
||||
/obj/item/projectile/bullet/honker = 15,
|
||||
/obj/item/projectile/temp = 15,
|
||||
/obj/item/projectile/ion = 15,
|
||||
/obj/item/projectile/magic/door = 15,
|
||||
/obj/item/projectile/magic/locker = 15,
|
||||
// /obj/item/projectile/magic/fetch = 15,
|
||||
/obj/item/projectile/beam/emitter = 15,
|
||||
// /obj/item/projectile/magic/flying = 15,
|
||||
/obj/item/projectile/energy/net = 15,
|
||||
/obj/item/projectile/bullet/incendiary/c9mm = 15,
|
||||
/obj/item/projectile/temp/hot = 15,
|
||||
/obj/item/projectile/beam/disabler = 15)
|
||||
|
||||
var/obj/item/projectile/picked_projectiletype = pickweight(weighted_projectile_types)
|
||||
|
||||
var/obj/item/master = comp.parent
|
||||
comp.appliedComponents += master.AddComponent(/datum/component/shrapnel, picked_projectiletype)
|
||||
return "[newName] of [initial(picked_projectiletype.name)] shrapnel"
|
||||
|
||||
/datum/fantasy_affix/strength
|
||||
placement = AFFIX_SUFFIX
|
||||
alignment = AFFIX_GOOD
|
||||
|
||||
/datum/fantasy_affix/strength/apply(datum/component/fantasy/comp, newName)
|
||||
. = ..()
|
||||
var/obj/item/master = comp.parent
|
||||
comp.appliedComponents += master.AddComponent(/datum/component/knockback, CEILING(comp.quality/2, 1), FLOOR(comp.quality/10, 1))
|
||||
return "[newName] of strength"
|
||||
|
||||
//////////// Bad suffixes
|
||||
|
||||
/datum/fantasy_affix/fool
|
||||
placement = AFFIX_SUFFIX
|
||||
alignment = AFFIX_EVIL
|
||||
|
||||
/datum/fantasy_affix/fool/apply(datum/component/fantasy/comp, newName)
|
||||
. = ..()
|
||||
var/obj/item/master = comp.parent
|
||||
comp.appliedComponents += master.AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 50)
|
||||
return "[newName] of the fool"
|
||||
37
code/datums/components/igniter.dm
Normal file
37
code/datums/components/igniter.dm
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
/datum/component/igniter
|
||||
var/fire_stacks
|
||||
|
||||
/datum/component/igniter/Initialize(fire_stacks=1)
|
||||
if(!isitem(parent) && !ishostile(parent) && !isgun(parent) && !ismachinery(parent) && !isstructure(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.fire_stacks = fire_stacks
|
||||
|
||||
/datum/component/igniter/RegisterWithParent()
|
||||
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
|
||||
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
|
||||
else if(isitem(parent))
|
||||
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/item_afterattack)
|
||||
else if(ishostile(parent))
|
||||
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
|
||||
|
||||
/datum/component/igniter/UnregisterFromParent()
|
||||
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
|
||||
|
||||
/datum/component/igniter/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
|
||||
if(!proximity_flag)
|
||||
return
|
||||
do_igniter(target)
|
||||
|
||||
/datum/component/igniter/proc/hostile_attackingtarget(mob/living/simple_animal/hostile/attacker, atom/target)
|
||||
do_igniter(target)
|
||||
|
||||
/datum/component/igniter/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
|
||||
do_igniter(target)
|
||||
|
||||
/datum/component/igniter/proc/do_igniter(atom/target)
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
L.adjust_fire_stacks(fire_stacks)
|
||||
L.IgniteMob()
|
||||
44
code/datums/components/knockback.dm
Normal file
44
code/datums/components/knockback.dm
Normal file
@@ -0,0 +1,44 @@
|
||||
/datum/component/knockback
|
||||
var/throw_distance
|
||||
var/throw_anchored
|
||||
|
||||
/datum/component/knockback/Initialize(throw_distance=1)
|
||||
if(!isitem(parent) && !ishostile(parent) && !isgun(parent) && !ismachinery(parent) && !isstructure(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.throw_distance = throw_distance
|
||||
src.throw_anchored = throw_anchored
|
||||
|
||||
/datum/component/knockback/RegisterWithParent()
|
||||
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
|
||||
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
|
||||
else if(isitem(parent))
|
||||
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/item_afterattack)
|
||||
else if(ishostile(parent))
|
||||
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
|
||||
|
||||
/datum/component/knockback/UnregisterFromParent()
|
||||
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
|
||||
|
||||
/datum/component/knockback/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
|
||||
if(!proximity_flag)
|
||||
return
|
||||
do_knockback(target, user, get_dir(source, target))
|
||||
|
||||
/datum/component/knockback/proc/hostile_attackingtarget(mob/living/simple_animal/hostile/attacker, atom/target)
|
||||
do_knockback(target, attacker, get_dir(attacker, target))
|
||||
|
||||
/datum/component/knockback/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
|
||||
do_knockback(target, null, angle2dir(Angle))
|
||||
|
||||
/datum/component/knockback/proc/do_knockback(atom/target, mob/thrower, throw_dir)
|
||||
if(!ismovableatom(target) || throw_dir == null)
|
||||
return
|
||||
var/atom/movable/throwee = target
|
||||
if(throwee.anchored && !throw_anchored)
|
||||
return
|
||||
if(throw_distance < 0)
|
||||
throw_dir = turn(throw_dir, 180)
|
||||
throw_distance *= -1
|
||||
var/atom/throw_target = get_edge_target_turf(throwee, throw_dir)
|
||||
throwee.safe_throw_at(throw_target, throw_distance, 1, thrower)
|
||||
39
code/datums/components/lifesteal.dm
Normal file
39
code/datums/components/lifesteal.dm
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
/datum/component/lifesteal
|
||||
var/flat_heal // heals a constant amount every time a hit occurs
|
||||
var/static/list/damage_heal_order = list(BRUTE, BURN, OXY)
|
||||
|
||||
/datum/component/lifesteal/Initialize(flat_heal=0)
|
||||
if(!isitem(parent) && !ishostile(parent) && !isgun(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.flat_heal = flat_heal
|
||||
|
||||
/datum/component/lifesteal/RegisterWithParent()
|
||||
if(isgun(parent))
|
||||
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
|
||||
else if(isitem(parent))
|
||||
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/item_afterattack)
|
||||
else if(ishostile(parent))
|
||||
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
|
||||
|
||||
/datum/component/lifesteal/UnregisterFromParent()
|
||||
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
|
||||
|
||||
/datum/component/lifesteal/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
|
||||
if(!proximity_flag)
|
||||
return
|
||||
do_lifesteal(user, target)
|
||||
|
||||
/datum/component/lifesteal/proc/hostile_attackingtarget(mob/living/simple_animal/hostile/attacker, atom/target)
|
||||
do_lifesteal(attacker, target)
|
||||
|
||||
/datum/component/lifesteal/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
|
||||
do_lifesteal(firer, target)
|
||||
|
||||
/datum/component/lifesteal/proc/do_lifesteal(atom/heal_target, atom/damage_target)
|
||||
if(isliving(heal_target) && isliving(damage_target))
|
||||
var/mob/living/healing = heal_target
|
||||
var/mob/living/damaging = damage_target
|
||||
if(damaging.stat != DEAD)
|
||||
healing.heal_ordered_damage(flat_heal, damage_heal_order)
|
||||
39
code/datums/components/shrapnel.dm
Normal file
39
code/datums/components/shrapnel.dm
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
/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/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
|
||||
|
||||
/datum/component/shrapnel/RegisterWithParent()
|
||||
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
|
||||
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
|
||||
|
||||
/datum/component/shrapnel/UnregisterFromParent()
|
||||
UnregisterSignal(parent, list(COMSIG_PROJECTILE_ON_HIT))
|
||||
|
||||
/datum/component/shrapnel/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
|
||||
do_shrapnel(firer, 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)
|
||||
P.range = override_projectile_range
|
||||
P.preparePixelProjectile(shootat_turf, target)
|
||||
P.firer = firer // don't hit ourself that would be really annoying
|
||||
P.permutated += target // don't hit the target we hit already with the flak
|
||||
P.fire()
|
||||
69
code/datums/components/summoning.dm
Normal file
69
code/datums/components/summoning.dm
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
/datum/component/summoning
|
||||
var/list/mob_types = list()
|
||||
var/spawn_chance // chance for the mob to spawn on hit in percent
|
||||
var/max_mobs
|
||||
var/spawn_delay // delay in spawning between mobs (deciseconds)
|
||||
var/spawn_text
|
||||
var/spawn_sound
|
||||
var/list/faction
|
||||
|
||||
var/last_spawned_time = 0
|
||||
var/list/spawned_mobs = list()
|
||||
|
||||
/datum/component/summoning/Initialize(mob_types, spawn_chance=100, max_mobs=3, spawn_delay=100, spawn_text="appears out of nowhere", spawn_sound='sound/magic/summon_magic.ogg', faction)
|
||||
if(!isitem(parent) && !ishostile(parent) && !isgun(parent) && !ismachinery(parent) && !isstructure(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.mob_types = mob_types
|
||||
src.spawn_chance = spawn_chance
|
||||
src.max_mobs = max_mobs
|
||||
src.spawn_delay = spawn_delay
|
||||
src.spawn_text = spawn_text
|
||||
src.spawn_sound = spawn_sound
|
||||
src.faction = faction
|
||||
|
||||
/datum/component/summoning/RegisterWithParent()
|
||||
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
|
||||
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
|
||||
else if(isitem(parent))
|
||||
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/item_afterattack)
|
||||
else if(ishostile(parent))
|
||||
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
|
||||
|
||||
/datum/component/summoning/UnregisterFromParent()
|
||||
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
|
||||
|
||||
/datum/component/summoning/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
|
||||
if(!proximity_flag)
|
||||
return
|
||||
do_spawn_mob(get_turf(target), user)
|
||||
|
||||
/datum/component/summoning/proc/hostile_attackingtarget(mob/living/simple_animal/hostile/attacker, atom/target)
|
||||
do_spawn_mob(get_turf(target), attacker)
|
||||
|
||||
/datum/component/summoning/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
|
||||
do_spawn_mob(get_turf(target), firer)
|
||||
|
||||
/datum/component/summoning/proc/do_spawn_mob(atom/spawn_location, summoner)
|
||||
if(spawned_mobs.len >= max_mobs)
|
||||
return 0
|
||||
if(last_spawned_time > world.time)
|
||||
return 0
|
||||
if(!prob(spawn_chance))
|
||||
return 0
|
||||
last_spawned_time = world.time + spawn_delay
|
||||
var/chosen_mob_type = pick(mob_types)
|
||||
var/mob/living/simple_animal/L = new chosen_mob_type(spawn_location)
|
||||
if(ishostile(L))
|
||||
var/mob/living/simple_animal/hostile/H = L
|
||||
H.friends += summoner // do not attack our summon boy
|
||||
spawned_mobs += L
|
||||
if(faction != null)
|
||||
L.faction = faction
|
||||
RegisterSignal(L, COMSIG_MOB_DEATH, .proc/on_spawned_death) // so we can remove them from the list, etc (for mobs with corpses)
|
||||
playsound(spawn_location,spawn_sound, 50, 1)
|
||||
spawn_location.visible_message("<span class='danger'>[L] [spawn_text].</span>")
|
||||
|
||||
/datum/component/summoning/proc/on_spawned_death(mob/killed, gibbed)
|
||||
spawned_mobs -= killed
|
||||
42
code/datums/components/tactical.dm
Normal file
42
code/datums/components/tactical.dm
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
/datum/component/tactical
|
||||
var/allowed_slot
|
||||
|
||||
/datum/component/tactical/Initialize(allowed_slot)
|
||||
if(!isitem(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.allowed_slot = allowed_slot
|
||||
|
||||
/datum/component/tactical/RegisterWithParent()
|
||||
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/modify)
|
||||
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/unmodify)
|
||||
|
||||
/datum/component/tactical/UnregisterFromParent()
|
||||
UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
|
||||
unmodify()
|
||||
|
||||
/datum/component/fantasy/Destroy()
|
||||
unmodify()
|
||||
return ..()
|
||||
|
||||
/datum/component/tactical/proc/modify(obj/item/source, mob/user, slot)
|
||||
if(allowed_slot && slot != allowed_slot)
|
||||
unmodify()
|
||||
return
|
||||
|
||||
var/obj/item/master = parent
|
||||
var/image/I = image(icon = master.icon, icon_state = master.icon_state, loc = user)
|
||||
I.copy_overlays(master)
|
||||
I.override = TRUE
|
||||
source.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, "sneaking_mission", I)
|
||||
I.layer = ABOVE_MOB_LAYER
|
||||
|
||||
/datum/component/tactical/proc/unmodify(obj/item/source, mob/user)
|
||||
var/obj/item/master = source || parent
|
||||
if(!user)
|
||||
if(!ismob(master.loc))
|
||||
return
|
||||
user = master.loc
|
||||
|
||||
user.remove_alt_appearance("sneaking_mission")
|
||||
Reference in New Issue
Block a user