mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-11 18:22:14 +00:00
[READY][FINALLY] Various Crossbreed Updates (#38039)
* A Variety Of Updates * Lovey Sprites! * Adds implementation for listening status effects. * Fixes implementation, uses defines instead of magic strings. * Completely reworks status effect listeners * Fixes Love Potions * Fixes Approximately All The Things.jpg
This commit is contained in:
committed by
vuonojenmustaturska
parent
7e38b8635e
commit
0d429e01b8
@@ -87,6 +87,11 @@
|
||||
#define COMSIG_MOVABLE_THROW "movable_throw" //from base of atom/movable/throw_at(): (datum/thrownthing, spin)
|
||||
#define COMSIG_MOVABLE_Z_CHANGED "movable_ztransit" //from base of atom/movable/onTransitZ(): (old_z, new_z)
|
||||
|
||||
// /mob/living signals
|
||||
#define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living)
|
||||
#define COMSIG_LIVING_IGNITED "living_ignite" //from base of mob/living/IgniteMob() (/mob/living)
|
||||
#define COMSIG_LIVING_EXTINGUISHED "living_extinguished" //from base of mob/living/ExtinguishMob() (/mob/living)
|
||||
|
||||
// /mob/living/carbon signals
|
||||
#define COMSIG_CARBON_SOUNDBANG "carbon_soundbang" //from base of mob/living/carbon/soundbang_act(): (list(intensity))
|
||||
|
||||
|
||||
@@ -80,6 +80,8 @@
|
||||
|
||||
#define STATUS_EFFECT_SYPHONMARK /datum/status_effect/syphon_mark //tracks kills for the KA death syphon module
|
||||
|
||||
#define STATUS_EFFECT_INLOVE /datum/status_effect/in_love //Displays you as being in love with someone else, and makes hearts appear around them.
|
||||
|
||||
/////////////
|
||||
// SLIME //
|
||||
/////////////
|
||||
|
||||
@@ -50,6 +50,9 @@ Stands have a lot of procs which mimic mob procs. Rather than inserting hooks fo
|
||||
1. `/datum/component/var/datum/parent` (protected, read-only)
|
||||
* The datum this component belongs to
|
||||
* Never `null` in child procs
|
||||
1. `report_signal_origin` (protected, boolean)
|
||||
* If `TRUE`, will invoke the callback when signalled with the signal type as the first argument.
|
||||
* `FALSE` by default.
|
||||
|
||||
### Procs
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
var/dupe_mode = COMPONENT_DUPE_HIGHLANDER
|
||||
var/dupe_type
|
||||
var/list/signal_procs
|
||||
var/report_signal_origin = FALSE
|
||||
var/datum/parent
|
||||
|
||||
/datum/component/New(datum/P, ...)
|
||||
@@ -129,6 +130,8 @@
|
||||
var/datum/callback/CB = C.signal_procs[sigtype]
|
||||
if(!CB)
|
||||
return NONE
|
||||
if(initial(C.report_signal_origin))
|
||||
arguments = list(sigtype) + arguments
|
||||
return CB.InvokeAsync(arglist(arguments))
|
||||
. = NONE
|
||||
for(var/I in target)
|
||||
@@ -138,6 +141,9 @@
|
||||
var/datum/callback/CB = C.signal_procs[sigtype]
|
||||
if(!CB)
|
||||
continue
|
||||
if(initial(C.report_signal_origin))
|
||||
. |= CB.InvokeAsync(arglist(list(sigtype) + arguments))
|
||||
else
|
||||
. |= CB.InvokeAsync(arglist(arguments))
|
||||
|
||||
/datum/proc/GetComponent(c_type)
|
||||
|
||||
38
code/datums/components/status_effect_listener.dm
Normal file
38
code/datums/components/status_effect_listener.dm
Normal file
@@ -0,0 +1,38 @@
|
||||
/datum/component/status_effect_listener
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE
|
||||
report_signal_origin = TRUE
|
||||
var/list/effect_signals = list()
|
||||
|
||||
/datum/component/status_effect_listener/proc/RegisterEffectSignal(datum/status_effect/se, sig_type_or_types, proc_or_callback)
|
||||
var/list/sig_types = islist(sig_type_or_types) ? sig_type_or_types : list(sig_type_or_types)
|
||||
RegisterSignal(sig_type_or_types, .proc/signal, override = TRUE)
|
||||
for(var/type in sig_types)
|
||||
var/list/callbacks = effect_signals[type]
|
||||
if(!callbacks)
|
||||
callbacks = list()
|
||||
effect_signals[type] = callbacks
|
||||
if(!istype(proc_or_callback, /datum/callback)) //if it wasnt a callback before, it is now
|
||||
proc_or_callback = CALLBACK(se, proc_or_callback)
|
||||
effect_signals[type] += proc_or_callback
|
||||
|
||||
/datum/component/status_effect_listener/proc/ClearSignalRegister(datum/status_effect/se)
|
||||
for(var/type in effect_signals)
|
||||
for(var/datum/callback/cb in effect_signals[type])
|
||||
if(cb.object == se)
|
||||
effect_signals[type] -= cb
|
||||
QDEL_NULL(cb)
|
||||
if(effect_signals[type].len <= 0)
|
||||
QDEL_LIST(effect_signals[type])
|
||||
effect_signals[type] = null
|
||||
effect_signals -= type
|
||||
if(effect_signals.len <= 0)
|
||||
QDEL_LIST(effect_signals)
|
||||
qdel(src)
|
||||
|
||||
/datum/component/status_effect_listener/proc/signal(sigtype, ...)
|
||||
var/list/arguments = args.Copy(2)
|
||||
if(effect_signals[sigtype])
|
||||
for(var/datum/callback/CB in effect_signals[sigtype])
|
||||
if(!CB)
|
||||
continue
|
||||
. |= CB.InvokeAsync(arglist(arguments))
|
||||
@@ -12,6 +12,7 @@
|
||||
icon_state = "frozen"
|
||||
|
||||
/datum/status_effect/freon/on_apply()
|
||||
RegisterEffectSignal(COMSIG_LIVING_RESIST, .proc/owner_resist)
|
||||
if(!owner.stat)
|
||||
to_chat(owner, "<span class='userdanger'>You become frozen in a cube!</span>")
|
||||
cube = icon('icons/effects/freeze.dmi', "ice_cube")
|
||||
@@ -24,6 +25,14 @@
|
||||
if(can_melt && owner.bodytemperature >= BODYTEMP_NORMAL)
|
||||
qdel(src)
|
||||
|
||||
/datum/status_effect/freon/proc/owner_resist()
|
||||
to_chat(owner, "You start breaking out of the ice cube!")
|
||||
if(do_mob(owner, owner, 40))
|
||||
if(!QDELETED(src))
|
||||
to_chat(owner, "You break out of the ice cube!")
|
||||
owner.remove_status_effect(/datum/status_effect/freon)
|
||||
owner.update_canmove()
|
||||
|
||||
/datum/status_effect/freon/on_remove()
|
||||
if(!owner.stat)
|
||||
to_chat(owner, "The cube melts!")
|
||||
|
||||
@@ -47,3 +47,25 @@
|
||||
/datum/status_effect/syphon_mark/on_remove()
|
||||
get_kill()
|
||||
. = ..()
|
||||
|
||||
/obj/screen/alert/status_effect/in_love
|
||||
name = "In Love"
|
||||
desc = "You feel so wonderfully in love!"
|
||||
icon_state = "in_love"
|
||||
|
||||
/datum/status_effect/in_love
|
||||
id = "in_love"
|
||||
duration = -1
|
||||
status_type = STATUS_EFFECT_UNIQUE
|
||||
alert_type = /obj/screen/alert/status_effect/in_love
|
||||
var/mob/living/date
|
||||
|
||||
/datum/status_effect/in_love/on_creation(mob/living/new_owner, mob/living/love_interest)
|
||||
. = ..()
|
||||
if(.)
|
||||
date = love_interest
|
||||
linked_alert.desc = "You're in love with [date.real_name]! How lovely."
|
||||
|
||||
/datum/status_effect/in_love/tick()
|
||||
if(date)
|
||||
new /obj/effect/temp_visual/love_heart/invisible(get_turf(date.loc), owner)
|
||||
|
||||
@@ -13,9 +13,20 @@
|
||||
var/alert_type = /obj/screen/alert/status_effect //the alert thrown by the status effect, contains name and description
|
||||
var/obj/screen/alert/status_effect/linked_alert = null //the alert itself, if it exists
|
||||
|
||||
var/listening = FALSE //Whether or not the status effect listens to mob interaction, using the status_effect_listener. Don't touch!
|
||||
|
||||
/datum/status_effect/New(list/arguments)
|
||||
on_creation(arglist(arguments))
|
||||
|
||||
/datum/status_effect/proc/RegisterEffectSignal(sig_type_or_types, proc_or_callback)
|
||||
if(!owner)
|
||||
CRASH("[src] attempted to apply a status effect listener before it had an owner.")
|
||||
GET_COMPONENT_FROM(listener, /datum/component/status_effect_listener, owner)
|
||||
if(!listener)
|
||||
listener = owner.AddComponent(/datum/component/status_effect_listener)
|
||||
listener.RegisterEffectSignal(src,sig_type_or_types, proc_or_callback)
|
||||
listening = TRUE
|
||||
|
||||
/datum/status_effect/proc/on_creation(mob/living/new_owner, ...)
|
||||
if(new_owner)
|
||||
owner = new_owner
|
||||
@@ -40,6 +51,10 @@
|
||||
owner.clear_alert(id)
|
||||
LAZYREMOVE(owner.status_effects, src)
|
||||
on_remove()
|
||||
if(listening)
|
||||
GET_COMPONENT_FROM(listener, /datum/component/status_effect_listener, owner)
|
||||
if(listener)
|
||||
listener.ClearSignalRegister(src)
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
@@ -56,6 +71,7 @@
|
||||
/datum/status_effect/proc/on_apply() //Called whenever the buff is applied; returning FALSE will cause it to autoremove itself.
|
||||
return TRUE
|
||||
/datum/status_effect/proc/tick() //Called every tick.
|
||||
/datum/status_effect/proc/receiveSignal(var/sigtype) //Called when a listener recieves a signal, if the effect is listening.
|
||||
/datum/status_effect/proc/on_remove() //Called whenever the buff expires or is removed; do note that at the point this is called, it is out of the owner's status_effects but owner is not yet null
|
||||
/datum/status_effect/proc/be_replaced() //Called instead of on_remove when a status effect is replaced by itself or when a status effect with on_remove_on_mob_delete = FALSE has its mob deleted
|
||||
owner.clear_alert(id)
|
||||
|
||||
@@ -167,3 +167,16 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances)
|
||||
if(isrevenant(M) || iseminence(M) || iswizard(M))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
datum/atom_hud/alternate_appearance/basic/onePerson
|
||||
var/mob/seer
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/onePerson/mobShouldSee(mob/M)
|
||||
if(M == seer)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/onePerson/New(key, image/I, mob/living/M)
|
||||
..(key, I, FALSE)
|
||||
seer = M
|
||||
add_hud_to(seer)
|
||||
|
||||
@@ -358,6 +358,29 @@
|
||||
pixel_y = rand(-4,4)
|
||||
animate(src, pixel_y = pixel_y + 32, alpha = 0, time = 25)
|
||||
|
||||
/obj/effect/temp_visual/love_heart
|
||||
name = "love heart"
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "heart"
|
||||
duration = 25
|
||||
|
||||
/obj/effect/temp_visual/love_heart/Initialize(mapload)
|
||||
. = ..()
|
||||
pixel_x = rand(-10,10)
|
||||
pixel_y = rand(-10,10)
|
||||
animate(src, pixel_y = pixel_y + 32, alpha = 0, time = duration)
|
||||
|
||||
/obj/effect/temp_visual/love_heart/invisible
|
||||
icon_state = null
|
||||
|
||||
/obj/effect/temp_visual/love_heart/invisible/Initialize(mapload, mob/seer)
|
||||
. = ..()
|
||||
var/image/I = image(icon = 'icons/effects/effects.dmi', icon_state = "heart", layer = ABOVE_MOB_LAYER, loc = src)
|
||||
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/onePerson, "heart", I, seer)
|
||||
I.alpha = 255
|
||||
I.appearance_flags = RESET_ALPHA
|
||||
animate(I, alpha = 0, time = duration)
|
||||
|
||||
/obj/effect/temp_visual/bleed
|
||||
name = "bleed"
|
||||
icon = 'icons/effects/bleed.dmi'
|
||||
|
||||
@@ -16,8 +16,17 @@
|
||||
|
||||
/datum/antagonist/valentine/on_gain()
|
||||
forge_objectives()
|
||||
if(isliving(owner))
|
||||
var/mob/living/L = owner
|
||||
L.apply_status_effect(STATUS_EFFECT_INLOVE, date)
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/valentine/on_removal()
|
||||
. = ..()
|
||||
if(isliving(owner))
|
||||
var/mob/living/L = owner
|
||||
L.remove_status_effect(STATUS_EFFECT_INLOVE)
|
||||
|
||||
/datum/antagonist/valentine/greet()
|
||||
to_chat(owner, "<span class='warning'><B>You're on a date with [date.name]! Protect [date.p_them()] at all costs. This takes priority over all other loyalties.</B></span>")
|
||||
|
||||
|
||||
@@ -723,8 +723,7 @@
|
||||
to_chat(src, "<span class='warning'>You can't do that right now!</span>")
|
||||
return FALSE
|
||||
if(!Adjacent(M) && (M.loc != src))
|
||||
if((be_close == 0) && (dna.check_mutation(TK)))
|
||||
if(tkMaxRangeCheck(src, M))
|
||||
if((be_close == 0) || (dna.check_mutation(TK) && tkMaxRangeCheck(src, M)))
|
||||
return TRUE
|
||||
to_chat(src, "<span class='warning'>You are too far away!</span>")
|
||||
return FALSE
|
||||
|
||||
@@ -583,6 +583,7 @@
|
||||
return
|
||||
changeNext_move(CLICK_CD_RESIST)
|
||||
|
||||
SendSignal(COMSIG_LIVING_RESIST, src)
|
||||
//resisting grabs (as if it helps anyone...)
|
||||
if(!restrained(ignore_grab = 1) && pulledby)
|
||||
visible_message("<span class='danger'>[src] resists against [pulledby]'s grip!</span>")
|
||||
@@ -599,14 +600,6 @@
|
||||
var/obj/C = loc
|
||||
C.container_resist(src)
|
||||
|
||||
else if(IsFrozen())
|
||||
to_chat(src, "You start breaking out of the ice cube!")
|
||||
if(do_mob(src, src, 40))
|
||||
if(IsFrozen())
|
||||
to_chat(src, "You break out of the ice cube!")
|
||||
remove_status_effect(/datum/status_effect/freon)
|
||||
update_canmove()
|
||||
|
||||
else if(canmove)
|
||||
if(on_fire)
|
||||
resist_fire() //stop, drop, and roll
|
||||
@@ -907,6 +900,7 @@
|
||||
new/obj/effect/dummy/fire(src)
|
||||
throw_alert("fire", /obj/screen/alert/fire)
|
||||
update_fire()
|
||||
SendSignal(COMSIG_LIVING_IGNITED,src)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -918,6 +912,7 @@
|
||||
qdel(F)
|
||||
clear_alert("fire")
|
||||
SendSignal(COMSIG_CLEAR_MOOD_EVENT, "on_fire")
|
||||
SendSignal(COMSIG_LIVING_EXTINGUISHED, src)
|
||||
update_fire()
|
||||
|
||||
/mob/living/proc/adjust_fire_stacks(add_fire_stacks) //Adjusting the amount of fire_stacks we have on person
|
||||
|
||||
@@ -39,7 +39,7 @@ To add a crossbreed:
|
||||
|
||||
/obj/item/slimecross/Initialize()
|
||||
..()
|
||||
name = colour + " " + name
|
||||
name = effect + " " + colour + " extract"
|
||||
var/itemcolor = "#FFFFFF"
|
||||
switch(colour)
|
||||
if("orange")
|
||||
|
||||
@@ -564,6 +564,7 @@ datum/status_effect/stabilized/blue/on_remove()
|
||||
/datum/status_effect/stabilized/cerulean/on_remove()
|
||||
if(clone)
|
||||
clone.visible_message("<span class='warning'>[clone] dissolves into a puddle of goo!</span>")
|
||||
clone.unequip_everything()
|
||||
qdel(clone)
|
||||
|
||||
/datum/status_effect/stabilized/pyrite
|
||||
@@ -760,9 +761,16 @@ datum/status_effect/stabilized/blue/on_remove()
|
||||
var/mob/living/simple_animal/familiar
|
||||
|
||||
/datum/status_effect/stabilized/gold/tick()
|
||||
var/obj/item/slimecross/stabilized/gold/linked = linked_extract
|
||||
if(!familiar)
|
||||
familiar = create_random_mob(get_turf(owner.loc), FRIENDLY_SPAWN)
|
||||
familiar = new linked.mob_type(get_turf(owner.loc))
|
||||
familiar.del_on_death = TRUE
|
||||
if(linked.saved_mind)
|
||||
linked.saved_mind.transfer_to(familiar)
|
||||
familiar.ckey = linked.saved_mind.key
|
||||
else
|
||||
if(familiar.mind)
|
||||
linked.saved_mind = familiar.mind
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/stabilized/gold/on_remove()
|
||||
|
||||
@@ -386,6 +386,9 @@ Charged extracts:
|
||||
if(user == M)
|
||||
to_chat(user, "<span class='warning'>You can't drink the love potion. What are you, a narcissist?</span>")
|
||||
return ..()
|
||||
if(M.has_status_effect(STATUS_EFFECT_INLOVE))
|
||||
to_chat(user, "<span class='warning'>[M] is already lovestruck!</span>")
|
||||
return ..()
|
||||
|
||||
M.visible_message("<span class='danger'>[user] starts to feed [M] a love potion!</span>",
|
||||
"<span class='userdanger'>[user] starts to feed you a love potion!</span>")
|
||||
@@ -393,10 +396,11 @@ Charged extracts:
|
||||
if(!do_after(user, 50, target = M))
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You feed [M] the love potion!</span>")
|
||||
to_chat(M, "<span class='notice'>You develop feelings for [user], and anyone [p_they(user)] like.</span>")
|
||||
if(!("[REF(user)]" in M.faction) && M.mind)
|
||||
M.mind.store_memory("You have strong feelings for [user].")
|
||||
to_chat(M, "<span class='notice'>You develop feelings for [user], and anyone [user.p_they()] like.</span>")
|
||||
if(M.mind)
|
||||
M.mind.store_memory("You are in love with [user].")
|
||||
M.faction |= "[REF(user)]"
|
||||
M.apply_status_effect(STATUS_EFFECT_INLOVE, user)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/slimepotion/peacepotion
|
||||
|
||||
@@ -21,6 +21,7 @@ Recurring extracts:
|
||||
extract.desc = desc
|
||||
extract.icon = icon
|
||||
extract.icon_state = icon_state
|
||||
extract.color = color
|
||||
extract.recurring = TRUE
|
||||
src.forceMove(extract)
|
||||
START_PROCESSING(SSobj,src)
|
||||
|
||||
@@ -4,7 +4,7 @@ Self-sustaining extracts:
|
||||
*/
|
||||
/obj/item/slimecross/selfsustaining
|
||||
name = "self-sustaining extract"
|
||||
effect = "selfsustaining"
|
||||
effect = "self-sustaining"
|
||||
icon_state = "selfsustaining"
|
||||
var/extract_type = /obj/item/slime_extract
|
||||
|
||||
@@ -23,6 +23,7 @@ Self-sustaining extracts:
|
||||
A.extract = X
|
||||
A.icon = icon
|
||||
A.icon_state = icon_state
|
||||
A.color = color
|
||||
qdel(src)
|
||||
|
||||
/obj/item/autoslime/Initialize()
|
||||
|
||||
@@ -96,6 +96,38 @@ Stabilized extracts:
|
||||
|
||||
/obj/item/slimecross/stabilized/gold
|
||||
colour = "gold"
|
||||
var/mob_type
|
||||
var/datum/mind/saved_mind
|
||||
|
||||
/obj/item/slimecross/stabilized/gold/proc/generate_mobtype()
|
||||
var/static/list/mob_spawn_pets = list()
|
||||
if(mob_spawn_pets.len <= 0)
|
||||
for(var/T in typesof(/mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SA = T
|
||||
switch(initial(SA.gold_core_spawnable))
|
||||
if(FRIENDLY_SPAWN)
|
||||
mob_spawn_pets += T
|
||||
mob_type = pick(mob_spawn_pets)
|
||||
|
||||
/obj/item/slimecross/stabilized/gold/Initialize()
|
||||
..()
|
||||
generate_mobtype()
|
||||
|
||||
/obj/item/slimecross/stabilized/gold/attack_self(mob/user)
|
||||
var/choice = input(user, "Which do you want to reset?", "Familiar Adjustment") as null|anything in list("Familiar Species", "Familiar Sentience")
|
||||
if(!user.canUseTopic(src, BE_CLOSE))
|
||||
return
|
||||
if(isliving(user))
|
||||
var/mob/living/L = user
|
||||
if(L.has_status_effect(/datum/status_effect/stabilized/gold))
|
||||
L.remove_status_effect(/datum/status_effect/stabilized/gold)
|
||||
START_PROCESSING(SSobj, src)
|
||||
if(choice == "Familiar Species")
|
||||
to_chat(user, "<span class='notice'>You squeeze [src], and a shape seems to shift around inside.</span>")
|
||||
generate_mobtype()
|
||||
if(choice == "Familiar Sentience")
|
||||
to_chat(user, "<span class='notice'>You poke [src], and it lets out a glowing pulse.</span>")
|
||||
saved_mind = null
|
||||
|
||||
/obj/item/slimecross/stabilized/oil
|
||||
colour = "oil"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 865 KiB After Width: | Height: | Size: 865 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 103 KiB |
@@ -341,6 +341,7 @@
|
||||
#include "code\datums\components\spooky.dm"
|
||||
#include "code\datums\components\squeek.dm"
|
||||
#include "code\datums\components\stationloving.dm"
|
||||
#include "code\datums\components\status_effect_listener.dm"
|
||||
#include "code\datums\components\swarming.dm"
|
||||
#include "code\datums\components\thermite.dm"
|
||||
#include "code\datums\components\wearertargeting.dm"
|
||||
|
||||
Reference in New Issue
Block a user