diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 5ee1f4e6439..135fb00dea1 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -12,6 +12,37 @@ GLOBAL_LIST_INIT(dangerous_turfs, typecacheof(list( /turf/open/space, /turf/open/openspace))) +/// List of types of abstract mob which shouldn't usually exist in the world on its own if we're spawning random mobs +GLOBAL_LIST_INIT(abstract_mob_types, list( + /mob/living/basic/blob_minion, + /mob/living/basic/construct, + /mob/living/basic/heretic_summon, + /mob/living/basic/mining, + /mob/living/basic/pet, + /mob/living/basic, + /mob/living/basic/spider, + /mob/living/carbon/alien/adult, + /mob/living/carbon/alien, + /mob/living/carbon/human/consistent, + /mob/living/carbon/human/dummy/consistent, + /mob/living/carbon/human/dummy, + /mob/living/carbon/human/species, + /mob/living/carbon, + /mob/living/silicon, + /mob/living/simple_animal/bot, + /mob/living/simple_animal/hostile/asteroid/elite, + /mob/living/simple_animal/hostile/asteroid, + /mob/living/simple_animal/hostile/construct, + /mob/living/simple_animal/hostile/guardian, + /mob/living/simple_animal/hostile/megafauna, + /mob/living/simple_animal/hostile/mimic, // Cannot exist if spawned without being passed an item reference + /mob/living/simple_animal/hostile/retaliate, + /mob/living/simple_animal/hostile, + /mob/living/simple_animal/pet, + /mob/living/simple_animal/soulscythe, // As mimic, can't exist if spawned outside an item + /mob/living/simple_animal, +)) + //Since it didn't really belong in any other category, I'm putting this here //This is for procs to replace all the goddamn 'in world's that are chilling around the code diff --git a/code/datums/components/fantasy/suffixes.dm b/code/datums/components/fantasy/suffixes.dm index c8809efae49..69d9d0a7ebb 100644 --- a/code/datums/components/fantasy/suffixes.dm +++ b/code/datums/components/fantasy/suffixes.dm @@ -103,30 +103,35 @@ . = ..() // 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, - /mob/living/simple_animal/hostile/asteroid/elite = FALSE, - /mob/living/simple_animal/hostile/megafauna = 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, + 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) - // 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) + 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 diff --git a/code/datums/components/summoning.dm b/code/datums/components/summoning.dm index 54eea882e8a..220a4baca5f 100644 --- a/code/datums/components/summoning.dm +++ b/code/datums/components/summoning.dm @@ -1,16 +1,32 @@ /datum/component/summoning + /// Types of mob we can create var/list/mob_types = list() - var/spawn_chance // chance for the mob to spawn on hit in percent + /// Percentage chance to spawn a mob + var/spawn_chance + /// Maximum mobs we can have active at once var/max_mobs - var/spawn_delay // delay in spawning between mobs (deciseconds) + /// Cooldown between spawning mobs + var/spawn_delay + /// Text to display when spawning a mob var/spawn_text + /// Sound to play when spawning a mob var/spawn_sound + /// Factions to assign to a summoned mob var/list/faction - - var/last_spawned_time = 0 + /// Cooldown tracker for when we can summon another mob + COOLDOWN_DECLARE(summon_cooldown) + /// List containing all of our mobs 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) +/datum/component/summoning/Initialize( + mob_types, + spawn_chance = 100, + max_mobs = 3, + spawn_delay = 10 SECONDS, + spawn_text = "appears out of nowhere", + spawn_sound = 'sound/magic/summon_magic.ogg', + list/faction, +) if(!isitem(parent) && !ishostile(parent) && !isgun(parent) && !ismachinery(parent) && !isstructure(parent) && !isprojectilespell(parent)) return COMPONENT_INCOMPATIBLE @@ -54,26 +70,22 @@ do_spawn_mob(get_turf(target), firer) /datum/component/summoning/proc/do_spawn_mob(atom/spawn_location, summoner) - if(spawned_mobs.len >= max_mobs) + if(length(spawned_mobs) >= max_mobs || !COOLDOWN_FINISHED(src, summon_cooldown) || !prob(spawn_chance)) return - if(last_spawned_time > world.time) - return - if(!prob(spawn_chance)) - return - last_spawned_time = world.time + spawn_delay + COOLDOWN_START(src, summon_cooldown, 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 + var/mob/living/summoned = new chosen_mob_type(spawn_location) + if(ishostile(summoned)) + var/mob/living/simple_animal/hostile/angry_boy = summoned + angry_boy.friends |= summoner // do not attack our summon boy + spawned_mobs |= summoned if(faction != null) - L.faction = faction - RegisterSignal(L, COMSIG_LIVING_DEATH, PROC_REF(on_spawned_death)) // so we can remove them from the list, etc (for mobs with corpses) - playsound(spawn_location,spawn_sound, 50, TRUE) - spawn_location.visible_message(span_danger("[L] [spawn_text].")) + summoned.faction = faction.Copy() + RegisterSignals(summoned, list(COMSIG_LIVING_DEATH, COMSIG_QDELETING), PROC_REF(on_spawned_death)) + spawn_location.visible_message(span_danger("[summoned] [spawn_text]!")) +/// When a spawned thing dies, remove it from our list /datum/component/summoning/proc/on_spawned_death(mob/killed, gibbed) SIGNAL_HANDLER - + UnregisterSignal(killed, list(COMSIG_LIVING_DEATH, COMSIG_QDELETING)) spawned_mobs -= killed diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index c10c04a9fa1..a8aeab47614 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -520,7 +520,6 @@ return var/datum/fantasy_affix/affix = affixes[picked_affix_name] affixes.Remove(affix) - QDEL_LIST_ASSOC(affixes) //remove the rest, we didn't use them var/fantasy_quality = 0 if(affix.alignment & AFFIX_GOOD) fantasy_quality++