diff --git a/code/modules/xenoarcheaology/effect_master.dm b/code/modules/xenoarcheaology/effect_master.dm new file mode 100644 index 00000000000..f654724b328 --- /dev/null +++ b/code/modules/xenoarcheaology/effect_master.dm @@ -0,0 +1,421 @@ + +/* + * Here there be the base component for artifacts. + */ + +/atom/proc/is_anomalous() + return (GetComponent(/datum/component/artifact_master)) + +/atom/proc/become_anomalous() + if(!is_anomalous()) + AddComponent(/datum/component/artifact_master) + if(istype(src, /obj/item)) + var/obj/item/I = src + LAZYINITLIST(I.origin_tech) + if(prob(50)) + I.origin_tech[TECH_PRECURSOR] += 1 + else + I.origin_tech[TECH_ARCANE] += 1 + var/rand_tech = pick(\ + TECH_MATERIAL,\ + TECH_ENGINEERING,\ + TECH_PHORON,\ + TECH_POWER,\ + TECH_BLUESPACE,\ + TECH_BIO,\ + TECH_COMBAT,\ + TECH_MAGNET,\ + TECH_DATA,\ + TECH_ILLEGAL\ + ) + LAZYSET(I.origin_tech, rand_tech, rand(4,7)) + +/datum/component/artifact_master + var/atom/holder + var/list/my_effects + + dupe_type = /datum/component/artifact_master + + var/effect_generation_chance = 100 + + var/list/make_effects + + var/artifact_id + +/datum/component/artifact_master/New() + . = ..() + holder = parent + + if(!holder) + qdel(src) + return + + my_effects = list() + + START_PROCESSING(SSobj, src) + + do_setup() + return + +/* + * Component System Registry. + * Here be dragons. + */ + +/datum/component/artifact_master/proc/DoRegistry() +//Melee Hit + RegisterSignal(holder, COMSIG_PARENT_ATTACKBY, /datum/component/artifact_master/proc/on_attackby, override = FALSE) +//Explosions + RegisterSignal(holder, COMSIG_ATOM_EX_ACT, /datum/component/artifact_master/proc/on_exact, override = FALSE) +//Bullets + RegisterSignal(holder, COMSIG_ATOM_BULLET_ACT, /datum/component/artifact_master/proc/on_bullet, override = FALSE) + +//Attackhand + RegisterSignal(holder, COMSIG_ATOM_ATTACK_HAND, /datum/component/artifact_master/proc/on_attack_hand, override = FALSE) + +//Bumped / Bumping + RegisterSignal(holder, COMSIG_MOVABLE_BUMP, /datum/component/artifact_master/proc/on_bump, override = FALSE) + RegisterSignal(holder, COMSIG_ATOM_BUMPED, /datum/component/artifact_master/proc/on_bumped, override = FALSE) + +//Moved + RegisterSignal(holder, COMSIG_MOVABLE_MOVED, /datum/component/artifact_master/proc/on_moved, override = FALSE) + +//Splashed with a reagent. + RegisterSignal(holder, COMSIG_REAGENTS_TOUCH, /datum/component/artifact_master/proc/on_reagent, override = FALSE) + +/* + * + */ + +/datum/component/artifact_master/proc/get_active_effects() + var/list/active_effects = list() + for(var/datum/artifact_effect/my_effect in my_effects) + if(my_effect.activated) + active_effects |= my_effect + + return active_effects + +/datum/component/artifact_master/proc/add_effect() + var/effect_type = input(usr, "What type do you want?", "Effect Type") as null|anything in subtypesof(/datum/artifact_effect) + if(effect_type) + var/datum/artifact_effect/my_effect = new effect_type(src) + if(istype(holder, my_effect.req_type)) + my_effects += my_effect + + else + to_chat(usr, "This effect can not be applied to this atom type.") + qdel(my_effect) + +/datum/component/artifact_master/proc/remove_effect() + var/to_remove_effect = input(usr, "What effect do you want to remove?", "Remove Effect") as null|anything in my_effects + + if(to_remove_effect) + var/datum/artifact_effect/AE = to_remove_effect + my_effects.Remove(to_remove_effect) + qdel(AE) + +/datum/component/artifact_master/Destroy() + holder = null + for(var/datum/artifact_effect/AE in my_effects) + AE.master = null + qdel(AE) + + STOP_PROCESSING(SSobj,src) + + . = ..() + +/datum/component/artifact_master/proc/do_setup() + if(LAZYLEN(make_effects)) + for(var/path in make_effects) + var/datum/artifact_effect/new_effect = new path(src) + if(istype(holder, new_effect.req_type)) + my_effects += new_effect + + else + generate_effects() + + DoRegistry() + +/datum/component/artifact_master/proc/generate_effects() + while(effect_generation_chance > 0) + var/chosen_path = pick(subtypesof(/datum/artifact_effect)) + if(effect_generation_chance >= 100) // If we're above 100 percent, just cut a flat amount and add an effect. + var/datum/artifact_effect/AE = new chosen_path(src) + if(istype(holder, AE.req_type)) + my_effects += AE + effect_generation_chance -= 30 + else + AE.master = src + qdel(AE) + continue + + effect_generation_chance /= 2 + + if(prob(effect_generation_chance)) // Otherwise, add effects as normal, with decreasing probability. + my_effects += new chosen_path(src) + + effect_generation_chance = round(effect_generation_chance) + +/datum/component/artifact_master/proc/get_holder() // Returns the holder. + return holder + +/datum/component/artifact_master/proc/get_primary() + if(LAZYLEN(my_effects)) + return my_effects[1] + return FALSE + +/* + * Trigger code. + */ + +/datum/component/artifact_master/proc/on_exact() + var/severity = args[2] + var/triggered = FALSE + for(var/datum/artifact_effect/my_effect in my_effects) + switch(severity) + if(1.0) + if(my_effect.trigger == TRIGGER_FORCE || my_effect.trigger == TRIGGER_HEAT || my_effect.trigger == TRIGGER_ENERGY) + my_effect.ToggleActivate() + triggered = TRUE + if(2.0) + if(my_effect.trigger == TRIGGER_FORCE || my_effect.trigger == TRIGGER_HEAT) + my_effect.ToggleActivate() + triggered = TRUE + if(3.0) + if (my_effect.trigger == TRIGGER_FORCE) + my_effect.ToggleActivate() + triggered = TRUE + + if(triggered) + return COMPONENT_IGNORE_EXPLOSION + + return + +/datum/component/artifact_master/proc/on_bullet() + var/obj/item/projectile/P = args[2] + var/triggered = TRUE + for(var/datum/artifact_effect/my_effect in my_effects) + if(istype(P,/obj/item/projectile/bullet)) + if(my_effect.trigger == TRIGGER_FORCE) + my_effect.ToggleActivate() + triggered = TRUE + + else if(istype(P,/obj/item/projectile/beam) ||\ + istype(P,/obj/item/projectile/ion) ||\ + istype(P,/obj/item/projectile/energy)) + if(my_effect.trigger == TRIGGER_ENERGY) + my_effect.ToggleActivate() + triggered = TRUE + + if(triggered) + return COMPONENT_CANCEL_ATTACK_CHAIN + + return + +/datum/component/artifact_master/proc/on_bump() + var/atom/bumped = args[2] + var/warn = FALSE + for(var/datum/artifact_effect/my_effect in my_effects) + if(istype(bumped,/obj)) + if(bumped:throwforce >= 10) + if(my_effect.trigger == TRIGGER_FORCE) + my_effect.ToggleActivate() + + else if(ishuman(bumped) && GetAnomalySusceptibility(bumped) >= 0.5) + if (my_effect.trigger == TRIGGER_TOUCH && prob(50)) + my_effect.ToggleActivate() + warn = 1 + + if (my_effect.effect == EFFECT_TOUCH && prob(50)) + my_effect.DoEffectTouch(bumped) + warn = 1 + + if(warn && isliving(bumped)) + to_chat(bumped, "You accidentally touch \the [holder] as it hits you.") + +/datum/component/artifact_master/proc/on_bumped() + var/atom/movable/M = args[2] + var/warn = FALSE + for(var/datum/artifact_effect/my_effect in my_effects) + if(istype(M,/obj)) + if(M:throwforce >= 10) + if(my_effect.trigger == TRIGGER_FORCE) + my_effect.ToggleActivate() + + else if(ishuman(M) && !istype(M:gloves,/obj/item/clothing/gloves)) + if (my_effect.trigger == TRIGGER_TOUCH && prob(50)) + my_effect.ToggleActivate() + warn = 1 + + if (my_effect.effect == EFFECT_TOUCH && prob(50)) + my_effect.DoEffectTouch(M) + warn = 1 + + if(warn && isliving(M)) + to_chat(M, "You accidentally touch \the [holder].") + +/datum/component/artifact_master/proc/on_attack_hand() + var/mob/living/user = args[2] + if(!istype(user)) + return + + if (get_dist(user, holder) > 1) + to_chat(user, "You can't reach [holder] from here.") + return + if(ishuman(user) && user:gloves) + to_chat(user, "You touch [holder] with your gloved hands, [pick("but nothing of note happens","but nothing happens","but nothing interesting happens","but you notice nothing different","but nothing seems to have happened")].") + return + + var/triggered = FALSE + + for(var/datum/artifact_effect/my_effect in my_effects) + + if(my_effect.trigger == TRIGGER_TOUCH) + triggered = TRUE + my_effect.ToggleActivate() + + if (my_effect.effect == EFFECT_TOUCH) + triggered = TRUE + my_effect.DoEffectTouch(user) + + if(triggered) + to_chat(user, "You touch [holder].") + + else + to_chat(user, "You touch [holder], [pick("but nothing of note happens","but nothing happens","but nothing interesting happens","but you notice nothing different","but nothing seems to have happened")].") + + +/datum/component/artifact_master/proc/on_attackby() + var/obj/item/weapon/W = args[2] + for(var/datum/artifact_effect/my_effect in my_effects) + + if (istype(W, /obj/item/weapon/reagent_containers)) + if(W.reagents.has_reagent("hydrogen", 1) || W.reagents.has_reagent("water", 1)) + if(my_effect.trigger == TRIGGER_WATER) + my_effect.ToggleActivate() + else if(W.reagents.has_reagent("sacid", 1) || W.reagents.has_reagent("pacid", 1) || W.reagents.has_reagent("diethylamine", 1)) + if(my_effect.trigger == TRIGGER_ACID) + my_effect.ToggleActivate() + else if(W.reagents.has_reagent("phoron", 1) || W.reagents.has_reagent("thermite", 1)) + if(my_effect.trigger == TRIGGER_VOLATILE) + my_effect.ToggleActivate() + else if(W.reagents.has_reagent("toxin", 1) || W.reagents.has_reagent("cyanide", 1) || W.reagents.has_reagent("amatoxin", 1) || W.reagents.has_reagent("neurotoxin", 1)) + if(my_effect.trigger == TRIGGER_TOXIN) + my_effect.ToggleActivate() + else if(istype(W,/obj/item/weapon/melee/baton) && W:status ||\ + istype(W,/obj/item/weapon/melee/energy) ||\ + istype(W,/obj/item/weapon/melee/cultblade) ||\ + istype(W,/obj/item/weapon/card/emag) ||\ + istype(W,/obj/item/device/multitool)) + if (my_effect.trigger == TRIGGER_ENERGY) + my_effect.ToggleActivate() + + else if (istype(W,/obj/item/weapon/flame) && W:lit ||\ + istype(W,/obj/item/weapon/weldingtool) && W:welding) + if(my_effect.trigger == TRIGGER_HEAT) + my_effect.ToggleActivate() + else + if (my_effect.trigger == TRIGGER_FORCE && W.force >= 10) + my_effect.ToggleActivate() + +/datum/component/artifact_master/proc/on_reagent() + var/datum/reagent/Touching = args[2] + + var/list/water = list("hydrogen", "water") + var/list/acid = list("sacid", "pacid", "diethylamine") + var/list/volatile = list("phoron","thermite") + var/list/toxic = list("toxin","cyanide","amatoxin","neurotoxin") + + for(var/datum/artifact_effect/my_effect in my_effects) + if(Touching.id in water) + if(my_effect.trigger == TRIGGER_WATER) + my_effect.ToggleActivate() + else if(Touching.id in acid) + if(my_effect.trigger == TRIGGER_ACID) + my_effect.ToggleActivate() + else if(Touching.id in volatile) + if(my_effect.trigger == TRIGGER_VOLATILE) + my_effect.ToggleActivate() + else if(Touching.id in toxic) + if(my_effect.trigger == TRIGGER_TOXIN) + my_effect.ToggleActivate() + +/datum/component/artifact_master/proc/on_moved() + for(var/datum/artifact_effect/my_effect in my_effects) + if(my_effect) + my_effect.UpdateMove() + +/datum/component/artifact_master/process() + if(!holder) // Some instances can be created and rapidly lose their holder, if they are destroyed rapidly on creation. IE, during excavation. + STOP_PROCESSING(SSobj, src) + if(!QDELETED(src)) + qdel(src) + return + + var/turf/L = holder.loc + if(!istype(L) && !isliving(L)) // We're inside a non-mob container or on null turf, either way stop processing effects + return + + if(istype(holder, /atom/movable)) + var/atom/movable/HA = holder + if(HA.pulledby) + on_bumped(holder, HA.pulledby) + + for(var/datum/artifact_effect/my_effect in my_effects) + if(my_effect) + my_effect.UpdateMove() + + //if any of our effects rely on environmental factors, work that out + var/trigger_cold = 0 + var/trigger_hot = 0 + var/trigger_phoron = 0 + var/trigger_oxy = 0 + var/trigger_co2 = 0 + var/trigger_nitro = 0 + + var/turf/T = get_turf(holder) + var/datum/gas_mixture/env = T.return_air() + if(env) + if(env.temperature < 225) + trigger_cold = 1 + else if(env.temperature > 375) + trigger_hot = 1 + + if(env.gas["phoron"] >= 10) + trigger_phoron = 1 + if(env.gas["oxygen"] >= 10) + trigger_oxy = 1 + if(env.gas["carbon_dioxide"] >= 10) + trigger_co2 = 1 + if(env.gas["nitrogen"] >= 10) + trigger_nitro = 1 + + for(var/datum/artifact_effect/my_effect in my_effects) + my_effect.artifact_id = artifact_id + + my_effect.process() + + //COLD ACTIVATION + if(my_effect.trigger == TRIGGER_COLD && (trigger_cold ^ my_effect.activated)) + my_effect.ToggleActivate() + + //HEAT ACTIVATION + if(my_effect.trigger == TRIGGER_HEAT && (trigger_hot ^ my_effect.activated)) + my_effect.ToggleActivate() + + //PHORON GAS ACTIVATION + if(my_effect.trigger == TRIGGER_PHORON && (trigger_phoron ^ my_effect.activated)) + my_effect.ToggleActivate() + + //OXYGEN GAS ACTIVATION + if(my_effect.trigger == TRIGGER_OXY && (trigger_oxy ^ my_effect.activated)) + my_effect.ToggleActivate() + + //CO2 GAS ACTIVATION + if(my_effect.trigger == TRIGGER_CO2 && (trigger_co2 ^ my_effect.activated)) + my_effect.ToggleActivate() + + //NITROGEN GAS ACTIVATION + if(my_effect.trigger == TRIGGER_NITRO && (trigger_nitro ^ my_effect.activated)) + my_effect.ToggleActivate() + diff --git a/code/modules/xenoarcheaology/effects/vampire.dm b/code/modules/xenoarcheaology/effects/vampire.dm index cf0ae9a2466..e5f55c1ad94 100644 --- a/code/modules/xenoarcheaology/effects/vampire.dm +++ b/code/modules/xenoarcheaology/effects/vampire.dm @@ -61,13 +61,13 @@ if(charges >= 10) charges -= 10 var/manifestation = pick(/obj/item/device/soulstone, /mob/living/simple_mob/faithless/cult/strong, /mob/living/simple_mob/creature/cult/strong, /mob/living/simple_mob/animal/space/bats/cult/strong) - new manifestation(get_turf(pick(view(1,T)))) + new manifestation(pick(RANGE_TURFS(1,T))) if(charges >= 3) if(prob(5)) charges -= 1 var/spawn_type = pick(/mob/living/simple_mob/animal/space/bats, /mob/living/simple_mob/creature, /mob/living/simple_mob/faithless) - new spawn_type(get_turf(pick(view(1,T)))) + new spawn_type(pick(RANGE_TURFS(1,T))) playsound(holder, pick('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg'), 50, 1, -3) if(charges >= 1 && nearby_mobs.len && prob(15 * nearby_mobs.len))