Skip to content

Commit

Permalink
Replaces the rpg loot datum with a component and makes some suffixes …
Browse files Browse the repository at this point in the history
…have real effects (tgstation#44044)

* Replaces the rpg loot datum with a component

* Makes bane accept species types

And cleans up some other code
  • Loading branch information
ninjanomnom authored and AnturK committed May 22, 2019
1 parent ec4a7dd commit 66cab55
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 101 deletions.
5 changes: 5 additions & 0 deletions code/__DEFINES/fantasy_affixes.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#define AFFIX_PREFIX (1 << 0)
#define AFFIX_SUFFIX (1 << 1)

#define AFFIX_GOOD (1 << 0)
#define AFFIX_EVIL (1 << 1)
45 changes: 45 additions & 0 deletions code/datums/components/bane.dm
Original file line number Diff line number Diff line change
@@ -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)
137 changes: 137 additions & 0 deletions code/datums/components/fantasy/_fantasy.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/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

var/usedSlots = NONE
for(var/i in affixes)
var/datum/fantasy_affix/affix = i
usedSlots |= affix.placement

var/alignment
if(quality >= 0)
alignment |= AFFIX_GOOD
if(quality <= 0)
alignment |= AFFIX_EVIL

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
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='inathneq_large'>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='umbra_emphasis'>mottled black glow</span>"

location.visible_message("[span][originalName] is covered by a [effect_description] and then transforms into [parent]!</span>")
9 changes: 9 additions & 0 deletions code/datums/components/fantasy/affix.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/datum/fantasy_affix
var/placement // A bitflag of "slots" this affix takes up, for example pre/suffix
var/alignment
var/weight = 10

/datum/fantasy_affix/proc/apply(datum/component/fantasy/comp, newName)
return newName

/datum/fantasy_affix/proc/remove(datum/component/fantasy/comp)
49 changes: 49 additions & 0 deletions code/datums/components/fantasy/prefixes.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/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]"
101 changes: 101 additions & 0 deletions code/datums/components/fantasy/suffixes.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/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/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))
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/bikehorn)
return "[newName] of the fool"
22 changes: 22 additions & 0 deletions code/datums/components/knockback.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/datum/component/knockback
var/throw_distance

/datum/component/knockback/Initialize(throw_distance=1)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE

src.throw_distance = throw_distance

/datum/component/knockback/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/afterattack_react)

/datum/component/knockback/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_ITEM_AFTERATTACK)

/datum/component/knockback/proc/afterattack_react(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
if(!ismovableatom(target) || !proximity_flag)
return
var/obj/item/master = parent
var/atom/movable/throwee = target
var/atom/throw_target = get_edge_target_turf(throwee, get_dir(master, throwee))
throwee.safe_throw_at(throw_target, throw_distance, 1, user)
Loading

0 comments on commit 66cab55

Please sign in to comment.