Files
Bubberstation/code/datums/components/fantasy/suffixes.dm
necromanceranne 1c3c50c9e9 DRAGnet; new sprite, new functionality (#90713)
## About The Pull Request


![image](https://github.com/user-attachments/assets/9e143b3c-3435-430b-a949-04a354a46688)

New sprites for the DRAGnet, coming alongside some functionaltiy
changes.

DRAGnets are now bulky, and do not fit in backpacks. They also cannot be
dual-wielded.

DRAGnet no longer have the trap snare functionality. You know, the
infamous bola bullet.

DRAGnets now have two functions; 
The first function is straight up a scatter disabler blast that does a
total of 55 stamina damage point blank (it's like, 5 less than what it
did before).

The second function is a single bolt that, when it hits a living mob,
dealing 30 damage and staggering it briefly slowing it down. It also
places down a wide net of transportation fields around the target, much
like the net functionality does on live (but that's attached to the
scatter function, not the single bolt)


![image](https://github.com/user-attachments/assets/336585d3-924e-4d4c-9c29-c16e7de88990)

### Misc Changes

DRAGnets now cost more form cargo, at 3600 credits. (This barely matters
for departmental orders)

## Why It's Good For The Game

The sprite is ancient. Like nearing two decades old ancient. I believe
the INTENTION was this thing


![a3ec8cabc5d93992168a57ad48d21996d037e67f_00](https://github.com/user-attachments/assets/bc29c2ad-dc93-4503-83e3-8997f4e625c1)

It didn't really work out that way.


![image](https://github.com/user-attachments/assets/db2bcc94-902f-476f-8ed4-b90883519bc9)

Either way, the new sprite is cool and more unique in my opinion.
Fitting its status as an energy weapon.

As for balance changes; I've wanted to do this for a very long time but
put it off a lot. I think this is about the most moderate approach I
have. It otherwise functions pretty much as expected, maintaining the
cool niche functionality while exorcising the problem bits; one-handed
energy shotgun bit, and the bola shot bit.

I think I've spent at least the last...four or so years hammering on
about the horror that is this gun for balance? And even when it was
seeing more widespread use from people who recognized how bullshit it
really was, I didn't really want to get rid of the bit I actually quite
liked, which was the teleportation field.

I'm happy with this version.

## Changelog
🆑 NecromancerAnne (Code, Mild Sprite Edits) Michiyamenotehifunana
(Sprites)
image: Replaces the decrepit DRAGnet sprite with something newer and
more interesting.
balance: Completely reworks the DRAGnet. They are now bulky energy guns.
balance: Snare is now a single shot bolt that deals 30 stamina force,
staggers the target and lays out a net of teleportation fields that can
transport anyone standing on them for too long to a DRAGnet beacon.
balance: Net has been replaced with a scattershot disabler round. 
/🆑
2025-04-30 13:31:39 +01:00

309 lines
10 KiB
Plaintext

/datum/fantasy_affix/cosmetic_suffixes
name = "purely cosmetic suffix"
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)))
. = "[newName] of [pick(goodSuffixes)]"
if(comp.quality >= 10)
START_PROCESSING(SSprocessing, src)
if(comp.quality >= 15)
comp.parent.AddComponent(/datum/component/unusual_effect, color = "#FFEA0030", include_particles = TRUE)
else
comp.parent.AddComponent(/datum/component/unusual_effect, color = "#FFBF0030")
else
. = "[newName] of [pick(badSuffixes)]"
return .
//////////// Good suffixes
/datum/fantasy_affix/bane
name = "of <mobtype> slaying (random species, carbon or simple animal)"
placement = AFFIX_SUFFIX
alignment = AFFIX_GOOD
var/list/target_types_by_comp = list()
/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 = zebra_typecacheof(list(
/mob/living/simple_animal = TRUE,
/mob/living/carbon = TRUE,
/datum/species = TRUE,
// Some types to remove them and their subtypes
/mob/living/carbon/human/species = FALSE,
))
// Some particular types to disallow if they're too broad/abstract
// Not in the above typecache generator because it includes subtypes and this doesn't.
possible_mobtypes -= list(
/mob/living/simple_animal/hostile,
)
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
master.AddElement(/datum/element/bane, target_type = picked_mobtype)
target_types_by_comp[comp] = picked_mobtype
return "[newName] of [initial(picked_mobtype.name)] slaying"
/datum/fantasy_affix/bane/remove(datum/component/fantasy/comp)
var/picked_mobtype = target_types_by_comp[comp]
var/obj/item/master = comp.parent
master.RemoveElement(/datum/element/bane, picked_mobtype)
target_types_by_comp -= comp
/datum/fantasy_affix/summoning
name = "of <mobtype> summoning (dangerous, can pick all but megafauna tier stuff)"
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(isnull(possible_mobtypes))
possible_mobtypes = list()
var/list/mob_subtype_whitelist = list(
/mob/living/basic,
/mob/living/carbon,
/mob/living/simple_animal,
)
for(var/type in mob_subtype_whitelist)
possible_mobtypes += subtypesof(type)
var/list/mob_subtype_blacklist = list(
/mob/living/simple_animal/hostile/asteroid/elite,
/mob/living/simple_animal/hostile/megafauna,
)
for(var/type in mob_subtype_blacklist)
possible_mobtypes -= subtypesof(type)
possible_mobtypes -= GLOB.abstract_mob_types
var/mob/picked_mobtype = pick(possible_mobtypes)
var/obj/item/master = comp.parent
var/max_mobs = max(CEILING(comp.quality / 2, 1), 1)
var/spawn_delay = 30 SECONDS - (3 SECONDS * comp.quality)
comp.appliedComponents += master.AddComponent(\
/datum/component/summoning,\
mob_types = list(picked_mobtype),\
max_mobs = max_mobs,\
spawn_delay = spawn_delay,\
)
return "[newName] of [initial(picked_mobtype.name)] summoning"
/datum/fantasy_affix/shrapnel
name = "shrapnel"
placement = AFFIX_SUFFIX
alignment = AFFIX_GOOD
/datum/fantasy_affix/shrapnel/validate(obj/item/attached)
if(isgun(attached))
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/projectile/meteor = 1,
/obj/projectile/energy/nuclear_particle = 1,
/obj/projectile/beam/pulse = 1,
/obj/projectile/bullet/honker = 15,
/obj/projectile/temp = 15,
/obj/projectile/ion = 15,
/obj/projectile/magic/door = 15,
/obj/projectile/magic/locker = 15,
/obj/projectile/magic/fetch = 15,
/obj/projectile/beam/emitter = 15,
/obj/projectile/magic/flying = 15,
/obj/projectile/bullet/incendiary/c9mm = 15,
/obj/projectile/temp/hot = 15,
/obj/projectile/beam/disabler = 15)
var/obj/projectile/picked_projectiletype = pick_weight(weighted_projectile_types)
var/obj/item/master = comp.parent
comp.appliedComponents += master.AddComponent(/datum/component/mirv, picked_projectiletype)
return "[newName] of [initial(picked_projectiletype.name)] shrapnel"
/datum/fantasy_affix/strength
name = "of strength (knockback)"
placement = AFFIX_SUFFIX
alignment = AFFIX_GOOD
/datum/fantasy_affix/strength/apply(datum/component/fantasy/comp, newName)
. = ..()
var/obj/item/master = comp.parent
master.AddElement(/datum/element/knockback, CEILING(comp.quality/2, 1), FLOOR(comp.quality/10, 1))
return "[newName] of strength"
/datum/fantasy_affix/strength/remove(datum/component/fantasy/comp)
var/obj/item/master = comp.parent
master.RemoveElement(/datum/element/knockback, CEILING(comp.quality/2, 1), FLOOR(comp.quality/10, 1))
//////////// Bad suffixes
/datum/fantasy_affix/fool
name = "of the fool (honking)"
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, falloff_exponent = 20)
return "[newName] of the fool"
/datum/fantasy_affix/curse_of_hunger
name = "curse of hunger"
placement = AFFIX_SUFFIX
alignment = AFFIX_EVIL
weight = 5
/datum/fantasy_affix/curse_of_hunger/validate(obj/item/attached)
// Curse of hunger can be really unbearable to deal with,
// so it should not start on someone or in a bag.
if(!isturf(attached.loc))
return FALSE
return TRUE
/datum/fantasy_affix/curse_of_hunger/apply(datum/component/fantasy/comp, newName)
. = ..()
var/obj/item/master = comp.parent
var/filter_color = "#8a0c0ca1" //clarified args
var/new_name = pick(", eternally hungry", " of the glutton", " cursed with hunger", ", consumer of all", " of the feast")
master.AddElement(/datum/element/curse_announcement, "[master] is cursed with the curse of hunger!", filter_color, "", comp)
comp.appliedComponents += master.AddComponent(/datum/component/curse_of_hunger)
return "[newName][new_name]"
/datum/fantasy_affix/curse_of_hunger/remove(datum/component/fantasy/comp)
var/obj/item/master = comp.parent
master.RemoveElement(/datum/element/curse_announcement) //just in case
/datum/fantasy_affix/curse_of_polymorph
name = "curse of polymorph"
placement = AFFIX_SUFFIX
alignment = AFFIX_EVIL
/datum/fantasy_affix/curse_of_polymorph/validate(obj/item/attached)
// Don't start on someone so that it doesn't immediately polymorph them.
if(ismob(attached.loc))
return FALSE
if(!isclothing(attached))
return FALSE
return TRUE
/datum/fantasy_affix/curse_of_polymorph/apply(datum/component/fantasy/comp, newName)
. = ..()
var/obj/item/master = comp.parent
var/filter_color = "#800080a1" //clarified args
var/new_name = pick(", transforming", " of the polymorph", " cursed with polymorphing", ", changer of all", " of changing")
var/static/list/possible_results = list(
WABBAJACK_MONKEY,
WABBAJACK_ROBOT,
WABBAJACK_SLIME,
WABBAJACK_XENO,
WABBAJACK_HUMAN,
WABBAJACK_ANIMAL,
)
master.AddElement(/datum/element/curse_announcement, "[master] is cursed with the curse of polymorph!", filter_color, "", comp)
comp.appliedComponents += master.AddComponent(/datum/component/curse_of_polymorph, pick(possible_results))
return "[newName][new_name]"
/datum/fantasy_affix/curse_of_polymorph/remove(datum/component/fantasy/comp)
var/obj/item/master = comp.parent
master.RemoveElement(/datum/element/curse_announcement) //just in case
/datum/fantasy_affix/speed
name = "of speed"
placement = AFFIX_SUFFIX
alignment = AFFIX_GOOD
/datum/fantasy_affix/speed/validate(obj/item/attached)
if(!istype(attached, /obj/item/clothing/shoes))
return FALSE
return TRUE
/datum/fantasy_affix/speed/apply(datum/component/fantasy/comp, newName)
. = ..()
var/obj/item/master = comp.parent
master.slowdown = min(-comp.quality / 5, master.slowdown)
return "[newName] of speed"
/datum/fantasy_affix/speed/remove(datum/component/fantasy/comp)
var/obj/item/master = comp.parent
master.slowdown = initial(master.slowdown)
/datum/fantasy_affix/doot
name = "of dooting"
placement = AFFIX_SUFFIX
alignment = AFFIX_GOOD
weight = 1
/datum/fantasy_affix/doot/apply(datum/component/fantasy/comp, newName)
. = ..()
comp.parent.AddElement(/datum/element/spooky, too_spooky = comp.quality > 17, stam_damage_mult = comp.quality * 0.15)
return "[newName] of [pick("dooting", "spooks", "rattling", "the bones")]"
/datum/fantasy_affix/doot/remove(datum/component/fantasy/comp)
comp.parent.RemoveElement(/datum/element/spooky, too_spooky = comp.quality > 17, stam_damage_mult = comp.quality * 0.15)
// On hitting a mob their click cd is slowed marginally
/datum/fantasy_affix/windseeker
name = "of the Windseeker"
placement = AFFIX_SUFFIX
alignment = AFFIX_GOOD
weight = 3
/datum/fantasy_affix/windseeker/apply(datum/component/fantasy/comp, newName)
comp.parent.AddElement(/datum/element/slow_target_click_cd_attack, get_cd_penalty(comp))
return "[newName] of the Windseeker"
/datum/fantasy_affix/windseeker/remove(datum/component/fantasy/comp)
comp.parent.RemoveElement(/datum/element/slow_target_click_cd_attack, get_cd_penalty(comp))
/datum/fantasy_affix/windseeker/proc/get_cd_penalty(datum/component/fantasy/comp)
return min(round(sqrt(comp.quality) * 0.1, 0.1), 0.4) // this gives you a small number, like 0.2 seconds