diff --git a/code/_helpers/turfs.dm b/code/_helpers/turfs.dm index d74b742f97a5..c39b745f6b03 100644 --- a/code/_helpers/turfs.dm +++ b/code/_helpers/turfs.dm @@ -120,9 +120,9 @@ if(target) if(base_area) - ChangeArea(target, get_area(source)) + target.ChangeArea(get_area(source)) . += transport_turf_contents(source, target, ignore_background, translate_air, angle = angle) - ChangeArea(source, base_area) + source.ChangeArea(base_area) else . += transport_turf_contents(source, target, ignore_background, translate_air, angle = angle) //change the old turfs diff --git a/code/datums/extensions/abilities/ability_button.dm b/code/datums/extensions/abilities/ability_button.dm index 5ab2d44e66b8..7bebb71a1993 100644 --- a/code/datums/extensions/abilities/ability_button.dm +++ b/code/datums/extensions/abilities/ability_button.dm @@ -65,7 +65,7 @@ /obj/screen/ability/button/handle_click(mob/user, params) if(owning_handler.prepared_ability == ability) owning_handler.cancel_prepared_ability() - else if(ability.use_ability(user, get_turf(user), owning_handler)) // tmp, needs better/multi-step target selection + else if(ability.use_ability(user, user, owning_handler)) // tmp, needs better/multi-step target selection update_icon() addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon)), ability.get_cooldown_time(ability.get_metadata_for_user(user)) + 1) diff --git a/code/datums/extensions/abilities/ability_decl.dm b/code/datums/extensions/abilities/ability_decl.dm index a3cb3726f89f..1c11a89414d4 100644 --- a/code/datums/extensions/abilities/ability_decl.dm +++ b/code/datums/extensions/abilities/ability_decl.dm @@ -182,7 +182,7 @@ else // Otherwise, just apply to the target directly. - apply_effect(user, target, metadata) + apply_ability_effect(user, target, metadata) if(end_prep_on_cast && handler.prepared_ability == src) handler.cancel_prepared_ability() @@ -325,7 +325,7 @@ return TRUE -/decl/ability/proc/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) +/decl/ability/proc/apply_ability_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) SHOULD_CALL_PARENT(TRUE) if(use_sound) playsound(get_turf(user), use_sound, use_sound_volume, 1) @@ -339,7 +339,7 @@ show_ability_cast_msg(user, targets, metadata) while(length(targets)) var/target = targets[1] - apply_effect_to(user, target, metadata) + apply_ability_effect_to(user, target, metadata) targets = prune_targets(user, target, targets, metadata) finish_casting(user, hit_target, metadata) @@ -366,7 +366,7 @@ ability_overlay.set_density(FALSE) QDEL_IN(ability_overlay, overlay_lifespan) -/decl/ability/proc/apply_effect_to(mob/user, atom/target, list/metadata) +/decl/ability/proc/apply_ability_effect_to(mob/living/user, atom/target, list/metadata) SHOULD_CALL_PARENT(TRUE) SHOULD_NOT_SLEEP(TRUE) apply_visuals(user, target, metadata) diff --git a/code/datums/extensions/abilities/ability_handler.dm b/code/datums/extensions/abilities/ability_handler.dm index 0b773cb469d5..d81a8bd023b8 100644 --- a/code/datums/extensions/abilities/ability_handler.dm +++ b/code/datums/extensions/abilities/ability_handler.dm @@ -125,7 +125,7 @@ add_screen_element(category_toggle, "toggle", TRUE) toggle_category_visibility(TRUE) -/datum/ability_handler/proc/refresh_element_positioning(row = 1, col = 1) +/datum/ability_handler/proc/refresh_element_positioning(row = 1, col = 0) if(!LAZYLEN(screen_elements)) return 0 var/button_pos = col @@ -134,14 +134,14 @@ for(var/ability in screen_elements) var/obj/screen/element = screen_elements[ability] if(istype(element, /obj/screen/ability/category)) - element.screen_loc = "RIGHT-[col]:-4,TOP-[row]" + element.screen_loc = "RIGHT-[col]:-4,TOP-[row]:-24" else if(!element.invisibility) button_pos++ if((button_pos-col) > 5) button_row++ .++ button_pos = col+1 - element.screen_loc = "RIGHT-[button_pos]:-4,TOP-[button_row]" + element.screen_loc = "RIGHT-[button_pos]:-4,TOP-[button_row]:-24" /datum/ability_handler/proc/toggle_category_visibility(force_state) showing_abilities = isnull(force_state) ? !showing_abilities : force_state diff --git a/code/datums/extensions/abilities/ability_item.dm b/code/datums/extensions/abilities/ability_item.dm index b4838fd41458..666d6d268e36 100644 --- a/code/datums/extensions/abilities/ability_item.dm +++ b/code/datums/extensions/abilities/ability_item.dm @@ -54,8 +54,8 @@ // Fire a projectile if that is how this ability works. ability.fire_projectile_at(user, target, metadata) else - // Otherwise, apply to the target. Range checking etc. will be handled in apply_effect(). - ability.apply_effect(user, target, metadata) + // Otherwise, apply to the target. Range checking etc. will be handled in apply_ability_effect(). + ability.apply_ability_effect(user, target, metadata) // Clean up our item if needed. if(ability.item_end_on_cast) diff --git a/code/datums/extensions/abilities/ability_projectile.dm b/code/datums/extensions/abilities/ability_projectile.dm index 896acba2e95a..5b5f2f10b373 100644 --- a/code/datums/extensions/abilities/ability_projectile.dm +++ b/code/datums/extensions/abilities/ability_projectile.dm @@ -22,10 +22,10 @@ /obj/item/projectile/ability/Bump(var/atom/A, forced=0) if(loc && carried_ability && !expended) - carried_ability.apply_effect(owner, A, ability_metadata, src) + carried_ability.apply_ability_effect(owner, A, ability_metadata, src) return TRUE /obj/item/projectile/ability/on_impact(var/atom/A) if(loc && carried_ability && !expended) - carried_ability.apply_effect(owner, A, ability_metadata, src) + carried_ability.apply_ability_effect(owner, A, ability_metadata, src) return TRUE diff --git a/code/datums/extensions/abilities/ability_targeting.dm b/code/datums/extensions/abilities/ability_targeting.dm index 52f7ee8166c5..e93a59c3a793 100644 --- a/code/datums/extensions/abilities/ability_targeting.dm +++ b/code/datums/extensions/abilities/ability_targeting.dm @@ -72,19 +72,35 @@ return FALSE return TRUE +/decl/ability_targeting/target_self + target_turf = FALSE + +/decl/ability_targeting/target_self/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) + return target == user + +/decl/ability_targeting/target_self/get_affected(mob/user, atom/hit_target, list/metadata, decl/ability/ability, obj/item/projectile/ability/projectile) + return list(user) + /decl/ability_targeting/clear_turf ignore_dense_turfs = TRUE /decl/ability_targeting/clear_turf/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) . = ..() && isturf(target) if(.) - var/turf/target_turf = target - return !target_turf.contains_dense_objects(user) + var/turf/turf_to_target = target + return !turf_to_target.contains_dense_objects(user) + +/decl/ability_targeting/single_atom + target_turf = FALSE + user_is_immune = TRUE + +/decl/ability_targeting/single_atom/can_target_user + user_is_immune = FALSE -/decl/ability_targeting/living_mob - target_turf = FALSE +/decl/ability_targeting/single_atom/get_affected(mob/user, atom/hit_target, list/metadata, decl/ability/ability, obj/item/projectile/ability/projectile) + return list(hit_target) -/decl/ability_targeting/living_mob/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) +/decl/ability_targeting/single_atom/living_mob/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) . = ..() && isliving(target) if(.) var/mob/living/victim = target diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 452a1a0f3fa2..2b29252a014a 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -138,43 +138,43 @@ var/global/list/areas = list() // Changes the area of T to A. Do not do this manually. // Area is expected to be a non-null instance. -/proc/ChangeArea(var/turf/T, var/area/A) +/turf/proc/ChangeArea(var/area/A) if(!istype(A)) CRASH("Area change attempt failed: invalid area supplied.") - var/old_outside = T.is_outside() - var/area/old_area = get_area(T) + var/old_outside = is_outside() + var/area/old_area = get_area(src) if(old_area == A) return var/old_area_ambience = old_area?.interior_ambient_light_modifier - A.contents.Add(T) + A.contents.Add(src) if(old_area) - old_area.Exited(T, A) - for(var/atom/movable/AM as anything in T) + old_area.Exited(src, A) + for(var/atom/movable/AM as anything in src) old_area.Exited(AM, A) // Note: this _will_ raise exited events. - A.Entered(T, old_area) - for(var/atom/movable/AM as anything in T) + A.Entered(src, old_area) + for(var/atom/movable/AM as anything in src) A.Entered(AM, old_area) // Note: this will _not_ raise moved or entered events. If you change this, you must also change everything which uses them. - for(var/obj/machinery/M in T) + for(var/obj/machinery/M in src) M.area_changed(old_area, A) // They usually get moved events, but this is the one way an area can change without triggering one. - T.update_registrations_on_adjacent_area_change() + update_registrations_on_adjacent_area_change() for(var/direction in global.cardinal) - var/turf/adjacent_turf = get_step(T, direction) + var/turf/adjacent_turf = get_step(src, direction) if(adjacent_turf) adjacent_turf.update_registrations_on_adjacent_area_change() // Handle updating weather and atmos if the outside status of the turf changed. - if(T.is_outside == OUTSIDE_AREA) - T.update_external_atmos_participation() // Refreshes outside status and adds exterior air to turf air if necessary. + if(is_outside == OUTSIDE_AREA) + update_external_atmos_participation() // Refreshes outside status and adds exterior air to turf air if necessary. - if(T.is_outside() != old_outside) - T.update_weather() - AMBIENCE_QUEUE_TURF(T) + if(is_outside() != old_outside) + update_weather() + AMBIENCE_QUEUE_TURF(src) else if(A.interior_ambient_light_modifier != old_area_ambience) - AMBIENCE_QUEUE_TURF(T) + AMBIENCE_QUEUE_TURF(src) /turf/proc/update_registrations_on_adjacent_area_change() for(var/obj/machinery/door/firedoor/door in src) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 0c4d7107c015..f84412b077d2 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -58,7 +58,7 @@ /turf/space/LateInitialize() if(SSmapping.base_floor_area) var/area/new_area = locate(SSmapping.base_floor_area) || new SSmapping.base_floor_area - ChangeArea(src, new_area) + ChangeArea(new_area) ChangeTurf(SSmapping.base_floor_type) /turf/space/proc/toggle_transit(var/direction) diff --git a/code/modules/admin/buildmode/mode_areas.dm b/code/modules/admin/buildmode/mode_areas.dm index 4df3b7e41797..92366800d9ad 100644 --- a/code/modules/admin/buildmode/mode_areas.dm +++ b/code/modules/admin/buildmode/mode_areas.dm @@ -119,7 +119,7 @@ if(!istype(T) || !istype(area_mode)) return FALSE if (area_mode.selected_area) - ChangeArea(T, area_mode.selected_area) + T.ChangeArea(area_mode.selected_area) to_chat(build_mode.user, SPAN_NOTICE("Set area of turf [T.name] to [area_mode.selected_area.proper_name]")) return TRUE to_chat(build_mode.user, SPAN_WARNING("Pick or create an area first")) diff --git a/code/modules/item_effects/item_effect_charges.dm b/code/modules/item_effects/item_effect_charges.dm index 06e8126b16fe..1c396925f9a7 100644 --- a/code/modules/item_effects/item_effect_charges.dm +++ b/code/modules/item_effects/item_effect_charges.dm @@ -19,9 +19,26 @@ damage = 20 atom_damage_type = BURN damage_flags = DAM_DISPERSED // burn all over + layer = ABOVE_LIGHTING_LAYER + plane = ABOVE_LIGHTING_PLANE + light_range = 1 + light_power = 1 + light_color = LIGHT_COLOR_FIRE + var/datum/composite_sound/fire_crackles/fire_loop var/fire_lifetime = 2 SECONDS var/fire_temperature = (288 CELSIUS) / 0.9 + 1 // hot enough to ignite wood! divided by 0.9 and plus one to ensure we can light firepits +/obj/item/projectile/fireball/Initialize() + . = ..() + fire_loop = new(list(src), FALSE) + fire_loop.start(src) + +/obj/item/projectile/fireball/Destroy() + . = ..() + if(fire_loop?.started) + fire_loop.stop(src) + QDEL_NULL(fire_loop) + /obj/effect/fake_fire/variable name = "fire" anchored = TRUE diff --git a/code/modules/maps/reader.dm b/code/modules/maps/reader.dm index 71bedba36393..42448df2ec62 100644 --- a/code/modules/maps/reader.dm +++ b/code/modules/maps/reader.dm @@ -333,7 +333,7 @@ var/global/dmm_suite/preloader/_preloader = new instance = new atype(null) initialized_areas_by_type[atype] = instance if(crds) - ChangeArea(crds, instance) + crds.ChangeArea(instance) //then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index a296f4413444..9a7cd0ec0753 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -891,20 +891,20 @@ if(!defer_language_update) update_languages() -/mob/living/proc/get_background_datum_by_flag(background_flag) +/mob/proc/get_background_datum_by_flag(background_flag) var/list/all_categories = global.using_map.get_background_categories() for(var/cat_type in all_categories) var/decl/background_category/background_cat = all_categories[cat_type] if(background_cat.background_flags && (background_cat.background_flags & background_flag)) return get_background_datum(cat_type) -/mob/living/proc/get_background_datum(cat_type) - return null +/mob/proc/get_background_datum(cat_type) + return global.using_map.default_background_info[cat_type] /mob/living/human/get_background_datum(cat_type) . = LAZYACCESS(background_info, cat_type) if(!istype(., /decl/background_detail)) - . = global.using_map.default_background_info[cat_type] + . = ..() PRINT_STACK_TRACE("get_background_datum() tried to return a non-instance value for background category '[cat_type]' - full background list: [json_encode(background_info)] default species culture list: [json_encode(global.using_map.default_background_info)]") /mob/living/human/get_digestion_product() diff --git a/code/modules/mob/observer/eye/blueprints_eye.dm b/code/modules/mob/observer/eye/blueprints_eye.dm index c7bae7b5f9c7..6309e82b166d 100644 --- a/code/modules/mob/observer/eye/blueprints_eye.dm +++ b/code/modules/mob/observer/eye/blueprints_eye.dm @@ -66,7 +66,7 @@ var/area/A = new A.SetName(area_name) for(var/turf/T in selected_turfs) - ChangeArea(T, A) + T.ChangeArea(A) finalize_area(A) remove_selection() // Reset the selection for clarity. @@ -90,7 +90,7 @@ var/datum/level_data/our_level_data = SSmapping.levels_by_z[our_turf.z] var/area/base_area = our_level_data.get_base_area_instance() for(var/turf/T in A.contents) - ChangeArea(T, base_area) + T.ChangeArea(base_area) if(!(locate(/turf) in A)) qdel(A) // uh oh, is this safe? @@ -316,7 +316,7 @@ var/datum/level_data/our_level_data = SSmapping.levels_by_z[our_turf.z] var/area/base_area = our_level_data.get_base_area_instance() for(var/turf/T in A.contents) - ChangeArea(T, base_area) + T.ChangeArea(base_area) if(!(locate(/turf) in A)) qdel(A) // uh oh, is this safe? diff --git a/code/modules/multiz/level_data.dm b/code/modules/multiz/level_data.dm index 389f3e24923b..5eec9b40a320 100644 --- a/code/modules/multiz/level_data.dm +++ b/code/modules/multiz/level_data.dm @@ -228,7 +228,7 @@ if(change_turf) T = T.ChangeTurf(picked_turf) if(change_area) - ChangeArea(T, A) + T.ChangeArea(A) ///Prepare level for being used. Setup borders, lateral z connections, ambient lighting, atmosphere, etc.. /datum/level_data/proc/setup_level_data(var/skip_gen = FALSE) diff --git a/code/modules/overmap/_overmap.dm b/code/modules/overmap/_overmap.dm index 6f63a9edf783..64d3ecb22d51 100644 --- a/code/modules/overmap/_overmap.dm +++ b/code/modules/overmap/_overmap.dm @@ -43,7 +43,7 @@ square = square.ChangeTurf(overmap_edge_type) else square = square.ChangeTurf(overmap_turf_type) - ChangeArea(square, A) + square.ChangeArea(A) /datum/overmap/proc/generate_overmap() testing("Building overmap [name]...") diff --git a/code/modules/random_map/random_map.dm b/code/modules/random_map/random_map.dm index ad1d3c26c0ec..cea16848c171 100644 --- a/code/modules/random_map/random_map.dm +++ b/code/modules/random_map/random_map.dm @@ -177,7 +177,8 @@ var/global/list/map_count = list() . = (newpath && !istype(T, newpath)) ? T.ChangeTurf(newpath) : T get_additional_spawns(map[current_cell], ., get_spawn_dir(x, y)) if(use_area) - ChangeArea(., use_area) + var/turf/turf = . + turf.ChangeArea(use_area) /datum/random_map/proc/get_spawn_dir() return 0 diff --git a/code/modules/turbolift/turbolift_map.dm b/code/modules/turbolift/turbolift_map.dm index bbea91dd8cc4..d69a39fd5bb8 100644 --- a/code/modules/turbolift/turbolift_map.dm +++ b/code/modules/turbolift/turbolift_map.dm @@ -186,8 +186,8 @@ INITIALIZE_IMMEDIATE(/obj/abstract/turbolift_spawner) var/area_path = areas_to_use[az] var/area/A = locate(area_path) || new area_path() - for(var/T in floor_turfs) - ChangeArea(T, A) + for(var/turf/T as anything in floor_turfs) + T.ChangeArea(A) cfloor.set_area_ref("\ref[A]") // Place exterior doors. diff --git a/maps/karzerfeste/karzerfeste.dm b/maps/karzerfeste/karzerfeste.dm index 6b8304b79614..6cb4e5236e08 100644 --- a/maps/karzerfeste/karzerfeste.dm +++ b/maps/karzerfeste/karzerfeste.dm @@ -8,6 +8,7 @@ #include "../../mods/content/fantasy/_fantasy.dme" #include "../../mods/content/undead/_undead.dme" #include "../../mods/content/biomods/_biomods.dme" + #include "../../mods/content/wyrd/_wyrd.dme" #include "../../mods/pyrelight/_pyrelight.dme" // include after _fantasy.dme so overrides work #include "areas/_area.dm" diff --git a/maps/shaded_hills/shaded_hills.dm b/maps/shaded_hills/shaded_hills.dm index d680a153227f..6da42ffb4dcb 100644 --- a/maps/shaded_hills/shaded_hills.dm +++ b/maps/shaded_hills/shaded_hills.dm @@ -6,7 +6,7 @@ #include "../../mods/content/scaling_descriptors.dm" #include "../../mods/species/drakes/_drakes.dme" // include before _fantasy.dme so overrides work #include "../../mods/content/item_sharpening/_item_sharpening.dme" - #include "../../mods/content/anima/_anima.dme" // include before _fantasy.dme so skill overrides work + #include "../../mods/content/wyrd/_wyrd.dme" // include before _fantasy.dme so skill overrides work #include "../../mods/content/fantasy/_fantasy.dme" #include "../../mods/content/undead/_undead.dme" #include "../../mods/content/biomods/_biomods.dme" diff --git a/maps/shaded_hills/shaded_hills_skills.dm b/maps/shaded_hills/shaded_hills_skills.dm index a4e79ea78099..6b18d6a80b22 100644 --- a/maps/shaded_hills/shaded_hills_skills.dm +++ b/maps/shaded_hills/shaded_hills_skills.dm @@ -5,12 +5,6 @@ /obj/item/runestone work_skill = /decl/skill/crafting/artifice -/decl/material/solid/potentia - arcana_skill = SKILL_SCIENCE - -/decl/runestone_spell_archetype - arcana_skill = SKILL_SCIENCE - // Removal of space skills /datum/map/shaded_hills/get_available_skill_types() . = ..() diff --git a/mods/content/anima/_anima.dme b/mods/content/anima/_anima.dme deleted file mode 100644 index 0a690992ddfe..000000000000 --- a/mods/content/anima/_anima.dme +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MODPACK_ANIMA -#define MODPACK_ANIMA -// BEGIN_INCLUDE -#include "_anima.dm" -#include "potentia_definition.dm" -#include "potentia_stack.dm" -#include "runestones.dm" -#include "spell_archetypes.dm" -#include "spell_datum.dm" -#include "spell_effect.dm" -#include "spellcasting.dm" -#include "spellscribing.dm" -// END_INCLUDE -#endif diff --git a/mods/content/anima/spell_archetypes.dm b/mods/content/anima/spell_archetypes.dm deleted file mode 100644 index 556ebd185f69..000000000000 --- a/mods/content/anima/spell_archetypes.dm +++ /dev/null @@ -1,29 +0,0 @@ -// Handles all effects associated with a spell. -/decl/runestone_spell_archetype - abstract_type = /decl/runestone_spell_archetype - var/name - var/decl/anima_spell_effect/base_effect - var/list/masterwork_effects - var/arcana_skill = SKILL_SCIENCE - -/decl/runestone_spell_archetype/Initialize() - masterwork_effects = decls_repository.get_decls_unassociated(masterwork_effects) - base_effect = GET_DECL(base_effect) - name = base_effect.name - return ..() - -/decl/runestone_spell_archetype/proc/has_effect_type(effect_type) - return TRUE - -/decl/runestone_spell_archetype/proc/get_masterwork_effects(mob/user, obj/item/implement) - // TODO: skill/trait/mind checks on user - return masterwork_effects - -/decl/runestone_spell_archetype/flash - base_effect = /decl/anima_spell_effect/flash - masterwork_effects = list( - /decl/anima_spell_effect/gloom - ) - -/decl/runestone_spell_archetype/flare - base_effect = /decl/anima_spell_effect/flare diff --git a/mods/content/anima/spell_datum.dm b/mods/content/anima/spell_datum.dm deleted file mode 100644 index 24862921764f..000000000000 --- a/mods/content/anima/spell_datum.dm +++ /dev/null @@ -1,14 +0,0 @@ -var/global/list/_anima_spell_effect_types = list( - "area of effect" = ANIMA_SPELL_AOE, - "close-range" = ANIMA_SPELL_MELEE, - "long-range" = ANIMA_SPELL_RANGED -) - -/datum/anima_working - var/effect_type = ANIMA_SPELL_AOE - var/effect_strength = 1 - var/decl/runestone_spell_archetype/spell_master - var/decl/anima_spell_effect/masterwork_effect - -/datum/anima_working/New(_effect) - effect_type = _effect \ No newline at end of file diff --git a/mods/content/anima/spell_effect.dm b/mods/content/anima/spell_effect.dm deleted file mode 100644 index 753dd6c4a5a5..000000000000 --- a/mods/content/anima/spell_effect.dm +++ /dev/null @@ -1,100 +0,0 @@ -/decl/anima_spell_effect - abstract_type = /decl/anima_spell_effect - var/name - var/description - var/cost = 1 - -/decl/anima_spell_effect/proc/evoke_spell(mob/user, atom/target, obj/item/runestone/runestone, caster_effect, caster_strength, in_proximity, deliberate = TRUE) - // Get our spell parameters. - var/spent_cost = runestone ? min(runestone.anima_density, cost) : cost - var/evoke_effect = caster_effect || runestone?.stored_spell?.effect_type || ANIMA_SPELL_AOE - if(!in_proximity && evoke_effect == ANIMA_SPELL_MELEE) - evoke_effect = ANIMA_SPELL_RANGED - var/evoke_strength = caster_strength || runestone?.anima_density || 1 - - // Cast the actual spell. - do_evocation(user, target, evoke_effect, evoke_strength, deliberate) - - // Expend resources. - if(runestone) - if(runestone.anima_density > spent_cost && evoke_effect != ANIMA_SPELL_AOE) - runestone.anima_density -= spent_cost - runestone.update_icon() - runestone.update_strings() - else if(!QDELETED(runestone)) - to_chat(user, SPAN_DANGER("Your runestone crumbles to dust!")) - qdel(runestone) - // else - // take anima from caster's pool - -/decl/anima_spell_effect/proc/show_primed_message(mob/user, effect_type) - to_chat(user, SPAN_NOTICE("Raw anima wreathes your [parse_zone(user.get_active_held_item_slot())], ready to be directed.")) - -/decl/anima_spell_effect/proc/do_evocation(mob/user, atom/target, evoke_effect = ANIMA_SPELL_AOE, evoke_strength = 1, deliberate = TRUE) - -/decl/anima_spell_effect/flash - name = "Flash" - description = "Release the channeled anima in a blinding flash of light." - -/decl/anima_spell_effect/flash/do_evocation(mob/user, atom/target, evoke_effect = ANIMA_SPELL_AOE, evoke_strength = 1, deliberate = TRUE) - - if(evoke_effect == ANIMA_SPELL_AOE) - user.visible_message("\The [user] emits a blinding flash of light ([evoke_strength])!") - for(var/mob/living/victim in view(evoke_strength, user)) - if(deliberate && victim == user) - continue - victim.handle_flashed(rand(evoke_strength, evoke_strength*2 + 1)) // min is 1 to 2, max is 3 to 7; base flash is 2 to 7 in a range of 3 tiles - else - user.visible_message("\The [user] drowns \the [target] in a blinding flash of light ([evoke_strength])!") - if(isliving(target)) - var/mob/living/victim = target - victim.handle_flashed(evoke_strength * 2 + 1) // since we're only attacking one target, we just go with the max instead of a random value - return TRUE - -/decl/anima_spell_effect/gloom - name = "Gloom" - description = "Burn the channeled anima to create a burst of unnatural darkness." - -/decl/anima_spell_effect/gloom/do_evocation(mob/user, atom/target, evoke_effect = ANIMA_SPELL_AOE, evoke_strength = 1, deliberate = TRUE) - if(evoke_effect == ANIMA_SPELL_AOE) - user.visible_message("\The [user] emits a burst of unnatural darkness ([evoke_strength])!") - var/atom/movable/gloom/gloom = new(target, evoke_strength) // with AOE spell, target is user's turf - QDEL_IN_CLIENT_TIME(gloom, evoke_strength * 5 SECONDS) - else - user.visible_message("\The [user] drowns \the [target] in unnatural darkness ([evoke_strength])!") - if(isliving(target)) - var/mob/living/victim = target - victim.overlay_fullscreen("gloom", /obj/screen/fullscreen/blackout) - addtimer(CALLBACK(victim, TYPE_PROC_REF(/mob, clear_fullscreen), "gloom"), evoke_strength * 3 SECONDS) - return TRUE - -/atom/movable/gloom - simulated = FALSE - mouse_opacity = MOUSE_OPACITY_UNCLICKABLE - -/atom/movable/gloom/Initialize(ml, strength) - . = ..() - set_light(strength, -10, "#ffffff") // negative light, cleared on destroy - -/decl/anima_spell_effect/flare - name = "Flare" - description = "Release the channeled anima in an undirected burst of flame." - -/decl/anima_spell_effect/flare/do_evocation(mob/user, atom/target, evoke_effect = ANIMA_SPELL_AOE, evoke_strength = 1, deliberate = TRUE) - var/turf/source_turf = get_turf(user) - // level 1 flare is hot enough to boil water. level 3 flare is hot enough to light a campfire and ignite wood - // divided by 0.9 and plus one to ensure inefficiency doesn't stop us from lighting campfires - var/fire_temperature = Interpolate(T100C, /decl/material/solid/organic/wood::ignition_point / 0.9 + 1, (evoke_strength - 1) / 3) - if(evoke_effect == ANIMA_SPELL_AOE) - user.visible_message("\The [user] is wreathed in a blast of fire ([evoke_strength])!") - for(var/turf/T in RANGE_TURFS(source_turf, evoke_strength)) - T = T.resolve_to_actual_turf() - new /obj/effect/fake_fire/variable/owned(T, fire_temperature, evoke_strength SECONDS, deliberate ? user : null) - else - user.visible_message("\The [user] hurls a blast of fire over \the [target] ([evoke_strength])!") - var/obj/item/projectile/fireball/projectile = new(source_turf) - projectile.fire_lifetime = evoke_strength SECONDS - // level 1 flare is hot enough to boil water. level 3 flare is hot enough to light a campfire and ignite wood - projectile.fire_temperature = Interpolate(T100C, projectile::fire_temperature, (evoke_strength - 1) / 3) - projectile.launch_from_gun(target, user.get_target_zone(), user) - return TRUE diff --git a/mods/content/anima/spellcasting.dm b/mods/content/anima/spellcasting.dm deleted file mode 100644 index e4c57c4652ca..000000000000 --- a/mods/content/anima/spellcasting.dm +++ /dev/null @@ -1,6 +0,0 @@ -/datum/ability_handler/anima_channeling - var/datum/anima_working/prepared_spell - var/list/anima_reservoir - var/list/anima_capacity - -/datum/ability_handler/anima_channeling diff --git a/mods/content/anima/_anima.dm b/mods/content/wyrd/_wyrd.dm similarity index 60% rename from mods/content/anima/_anima.dm rename to mods/content/wyrd/_wyrd.dm index b55da0e3cbdb..d59c5e3c2ce4 100644 --- a/mods/content/anima/_anima.dm +++ b/mods/content/wyrd/_wyrd.dm @@ -1,15 +1,11 @@ -#define ANIMA_SPELL_AOE "aoe" -#define ANIMA_SPELL_MELEE "melee" -#define ANIMA_SPELL_RANGED "ranged" - -/decl/modpack/anima - name = "Anima Content" +/decl/modpack/wyrd + name = "Pyrelight Magic Content" credits_topics = list("ANCIENT MAGIC", "ANCIENT ANIMA", "MAGICAL RITUALS", "MAGIC SPELLS") - credits_nouns = list("MAGIC", "ANIMA") + credits_nouns = list("MAGIC", "ANIMA", "WYRD") credits_adjectives = list("ANCIENT", "MAGICAL", "ARCANE", "DIVINE", "BEWITCHED", "ENCHANTED") credits_crew_outcomes = list("BEWITCHED", "ENCHANTED", "MAGICKED", "CURSED") dreams = list( - "anima", "potentia", "magic", "an ancient curse", "an arcane ritual", + "wyrd", "anima", "potentia", "magic", "an ancient curse", "an arcane ritual", "a magic spell", "a magician", "a wizard", "a witch", "a necromancer", "an ancient scroll", "a magic crystal" ) \ No newline at end of file diff --git a/mods/content/wyrd/_wyrd.dme b/mods/content/wyrd/_wyrd.dme new file mode 100644 index 000000000000..895083c1e879 --- /dev/null +++ b/mods/content/wyrd/_wyrd.dme @@ -0,0 +1,21 @@ +#ifndef MODPACK_WYRD +#define MODPACK_WYRD +// BEGIN_INCLUDE +#include "_wyrd.dm" +#include "wyrd_abilities.dm" +#include "anima\_anima.dm" +#include "anima\anima_aura.dm" +#include "anima\anima_source.dm" +#include "anima\area.dm" +#include "anima\culture.dm" +#include "anima\turf.dm" +#include "potentia\_potentia.dm" +#include "potentia\potentia_stack.dm" +#include "spells\spell_archetypes.dm" +#include "spells\spell_datum.dm" +#include "spells\spell_effect.dm" +#include "spells\spellcasting.dm" +#include "spells\runestones\_runestones.dm" +#include "spells\runestones\spellscribing.dm" +// END_INCLUDE +#endif diff --git a/mods/content/wyrd/anima/_anima.dm b/mods/content/wyrd/anima/_anima.dm new file mode 100644 index 000000000000..f993362fbd68 --- /dev/null +++ b/mods/content/wyrd/anima/_anima.dm @@ -0,0 +1,70 @@ +// Anima system: radiant energy or potential of your current location. +// Influenced by natural background levels (based on area) and other sources (that may move around) +// TODO: temporary/decaying radiant anima resulting from spells cast in a location. +// TODO: visual effects for very high anima in an area/on a turf. + +// Anima: +// - Very broad categories of magical energy. +// - Influences things within the area of effect (high levels of water anima/low levels of fire anima -> harder to light things on fire). +// - Modifies the chances of spellcasting in the area of effect (high levels of sky anima makes air spells easier). +// - Higher or lower intensities associated with entities/areas. +// - Has an associated subset of potentia that can be refined from local anima (sky sign -> air and water anima, etc) + +// Local events that create temporary powerful anima sources: +// - sacrificing an animal spikes waning and blood anima in the immediate vicinity to boost/enable particular workings +// - burning incense, taking drugs, scribing a circle + +// Process notes: +// 1. mob wants to cast a spell +// 2. retrieve local anima (turf + area) +// 3. retrieve personal anima (aura extension) +// a. effective anima - local anima +/- (personal anima * strength of will) +// b. very high will + personal anima can override local anima +// 4. retrieve spellcasting condition of the mob +// a. stressors mean poor spellcasting focus +// b. nutrition/stamina are needed for fuelling the spell +// 4. if anima state or conditions do not allow spell, fizzles +// 5. spellcaster's personal aura and will are applied agains the target's personal aura/will (for mobs) or just ambient anima for inanimate objects +// 6. effect of spell is scaled based on how much stronger (or more effectively aligned) the spellcaster is + +/decl/anima + abstract_type = /decl/anima + decl_flags = DECL_FLAG_MANDATORY_UID + var/name + + var/const/ANIMA_DEPLETED = 0 + var/const/ANIMA_NEGLIGIBLE = 1 + var/const/ANIMA_NOTABLE = 2 + var/const/ANIMA_DENSE = 3 + var/const/ANIMA_RICH = 4 + var/const/ANIMA_SATURATED = 5 + +/decl/anima/proc/get_personal_anima_description(_density, decl/background_detail/_culture) + return _culture.get_personal_anima_description(src, _density) + +/decl/anima/proc/get_ambient_anima_description(_density, decl/background_detail/_culture) + return _culture.get_ambient_anima_description(src, _density) + +// burning sign: label for potentia-based spellcasting, not a 'real' type of anima +// wild sign: complex category of workings, not a specific single type of anima + +/decl/anima/sky + name = "Sky Sign" + uid = "anima_sky" + // air, stars, moon, truth, purity + +/decl/anima/waning + name = "Waning Sign" + uid = "anima_waning" + // transition, change, death, birth + +/decl/anima/hollow + name = "Hollow Sign" + uid = "anima_hollow" + // concealment, earth, mystery + +/decl/anima/blood + name = "Blood Sign" + uid = "anima_blood" + // growth, life, heat, violence + // source of volatile potentia - fire, mania diff --git a/mods/content/wyrd/anima/anima_aura.dm b/mods/content/wyrd/anima/anima_aura.dm new file mode 100644 index 000000000000..15f3a666fd4d --- /dev/null +++ b/mods/content/wyrd/anima/anima_aura.dm @@ -0,0 +1,21 @@ +// Inner anima state used for spellcasting. +/datum/extension/anima_aura + expected_type = /mob + base_type = /datum/extension/anima_aura + var/alist/pool = alist( + /decl/anima/sky = /decl/anima::ANIMA_NEGLIGIBLE, + /decl/anima/waning = /decl/anima::ANIMA_NEGLIGIBLE, + /decl/anima/hollow = /decl/anima::ANIMA_NEGLIGIBLE, + /decl/anima/blood = /decl/anima::ANIMA_NEGLIGIBLE + ) + +/mob/proc/get_personal_anima() + var/datum/extension/anima_aura/aura = get_extension(src, /datum/extension/anima_aura) + if(aura) + return aura.pool?.Copy() + return alist( + /decl/anima/sky = /decl/anima::ANIMA_DEPLETED, + /decl/anima/waning = /decl/anima::ANIMA_DEPLETED, + /decl/anima/hollow = /decl/anima::ANIMA_DEPLETED, + /decl/anima/blood = /decl/anima::ANIMA_DEPLETED + ) \ No newline at end of file diff --git a/mods/content/wyrd/anima/anima_source.dm b/mods/content/wyrd/anima/anima_source.dm new file mode 100644 index 000000000000..a7aef6589fb3 --- /dev/null +++ b/mods/content/wyrd/anima/anima_source.dm @@ -0,0 +1,28 @@ +/datum/extension/anima_source + expected_type = /atom/movable + var/radiant_range = 1 + var/alist/anima_contribution // Can also be negatives for anima suppression. + var/list/affecting_turfs = list() + +/datum/extension/anima_source/New(datum/holder) + . = ..() + if(istype(holder, expected_type)) + events_repository.register(/decl/observ/moved, holder, src, PROC_REF(update_radiant_anima)) + +/datum/extension/anima_source/Destroy() + for(var/turf/turf in affecting_turfs) + turf.remove_affecting_anima(src) + affecting_turfs.Cut() + events_repository.unregister(/decl/observ/moved, holder, src) + . = ..() + +/datum/extension/anima_source/proc/update_radiant_anima() + var/turf/my_turf = get_turf(holder) + var/list/new_turfs = istype(my_turf) ? RANGE_TURFS(my_turf, radiant_range) : null + for(var/turf/turf in affecting_turfs) + if(turf in new_turfs) + new_turfs -= turf + else + turf.remove_affecting_anima(src) + for(var/turf/turf in new_turfs) + turf.add_affecting_anima(src) diff --git a/mods/content/wyrd/anima/area.dm b/mods/content/wyrd/anima/area.dm new file mode 100644 index 000000000000..79ab22c0fc0e --- /dev/null +++ b/mods/content/wyrd/anima/area.dm @@ -0,0 +1,26 @@ +/area + var/alist/background_anima = alist( + /decl/anima/sky = /decl/anima::ANIMA_NEGLIGIBLE, + /decl/anima/waning = /decl/anima::ANIMA_NEGLIGIBLE, + /decl/anima/hollow = /decl/anima::ANIMA_NEGLIGIBLE, + /decl/anima/blood = /decl/anima::ANIMA_NEGLIGIBLE + ) + +/area/New() + ..() + // Outside areas have a higher minimum value of Sky Sign. + if(is_outside == OUTSIDE_YES) + background_anima[/decl/anima/sky] = max(background_anima[/decl/anima/sky], /decl/anima::ANIMA_DENSE) + +/area/proc/get_background_anima() + RETURN_TYPE(/alist) + return background_anima + +/area/proc/adjust_background_anima(_anima, _amount) + // Update our ambient background anima list. + if(!background_anima) + background_anima = alist() + background_anima[_anima] += _amount + // Invalidate cache for our turfs. + for(var/turf/area_turf in contents) + area_turf.last_anima = null diff --git a/mods/content/wyrd/anima/culture.dm b/mods/content/wyrd/anima/culture.dm new file mode 100644 index 000000000000..2db85832c182 --- /dev/null +++ b/mods/content/wyrd/anima/culture.dm @@ -0,0 +1,40 @@ +/decl/background_detail + var/anima_failed_working_insufficient_1p = SPAN_WARNING("The skein here is too thin to weave $SPELL$!") + var/anima_failed_working_excess_1p = SPAN_WARNING("The skein here churns too violently to weave $SPELL$!") + var/anima_failed_exhaustion_1p = SPAN_WARNING("You are too exhausted to weave $SPELL$.") + +// Plan here is that specific cultures can have different interpretations of different anima. +/decl/background_detail/proc/get_cultural_anima_name(decl/anima/_anima) + return _anima.name + +/decl/background_detail/proc/get_personal_anima_description(decl/anima/_anima, _density) + var/anima_name = get_cultural_anima_name(_anima) + switch(_density) + if(/decl/anima::ANIMA_DEPLETED) + return "The [anima_name] cannot be heard within your soul." + if(/decl/anima::ANIMA_NEGLIGIBLE) + return "The [anima_name] whispers within your soul." + if(/decl/anima::ANIMA_NOTABLE) + return "The [anima_name] hums gently within your soul." + if(/decl/anima::ANIMA_DENSE) + return "The [anima_name] fills the vault of your soul." + if(/decl/anima::ANIMA_RICH) + return "The [anima_name] sings powerfully within your soul, swelling bright and loud." + if(/decl/anima::ANIMA_SATURATED) + return "The [anima_name] colours your entire being with an almost deafening tone." + +/decl/background_detail/proc/get_ambient_anima_description(decl/anima/_anima, _density) + var/anima_name = get_cultural_anima_name(_anima) + switch(_density) + if(/decl/anima::ANIMA_DEPLETED) + return "The [anima_name] is silent here." + if(/decl/anima::ANIMA_NEGLIGIBLE) + return "The [anima_name] only whispers here." + if(/decl/anima::ANIMA_NOTABLE) + return "The [anima_name] hums and eddies quietly around you." + if(/decl/anima::ANIMA_DENSE) + return "The [anima_name] flows steadily all around you." + if(/decl/anima::ANIMA_RICH) + return "The [anima_name] suffuses all around you with a powerful song." + if(/decl/anima::ANIMA_SATURATED) + return "The [anima_name] thunders here in a continuous deluge." diff --git a/mods/content/wyrd/anima/turf.dm b/mods/content/wyrd/anima/turf.dm new file mode 100644 index 000000000000..eb76e000a448 --- /dev/null +++ b/mods/content/wyrd/anima/turf.dm @@ -0,0 +1,39 @@ +/turf + var/alist/last_anima + var/list/_affecting_anima + +/turf/ChangeTurf(turf/N, tell_universe, force_lighting_update, keep_air, update_open_turfs_above, keep_height) + . = ..() + last_anima = null + +/turf/proc/add_affecting_anima(datum/extension/anima_source/_source) + LAZYDISTINCTADD(_affecting_anima, _source) + LAZYDISTINCTADD(_source.affecting_turfs, src) + last_anima = null + +/turf/proc/remove_affecting_anima(datum/extension/anima_source/_source) + LAZYREMOVE(_affecting_anima, _source) + LAZYREMOVE(_source.affecting_turfs, src) + last_anima = null + +/turf/proc/update_anima_values() + last_anima = alist() + for(var/datum/extension/anima_source/source in _affecting_anima) + for(var/atype,avalue in source.anima_contribution) + last_anima[atype] += avalue + var/area/my_area = get_area(src) + for(var/atype,avalue in my_area?.get_background_anima()) + last_anima[atype] += avalue + +/atom/proc/get_ambient_anima() + var/turf/turf = get_turf(src) + return turf?.get_ambient_anima() + +/turf/get_ambient_anima() + // If nothing is affecting our anima, don't even bother caching it. + if(!length(_affecting_anima)) + var/area/area = get_area(src) + return area?.get_background_anima()?.Copy() + if(isnull(last_anima)) + last_anima = update_anima_values() + return last_anima diff --git a/mods/content/wyrd/icons/abilities.dmi b/mods/content/wyrd/icons/abilities.dmi new file mode 100644 index 000000000000..c30b116d94d0 Binary files /dev/null and b/mods/content/wyrd/icons/abilities.dmi differ diff --git a/mods/content/anima/icons/anima_blank.dmi b/mods/content/wyrd/icons/blanks.dmi similarity index 69% rename from mods/content/anima/icons/anima_blank.dmi rename to mods/content/wyrd/icons/blanks.dmi index c218a1e03a7e..870e8e49b660 100644 Binary files a/mods/content/anima/icons/anima_blank.dmi and b/mods/content/wyrd/icons/blanks.dmi differ diff --git a/mods/content/anima/icons/runestone_basic.dmi b/mods/content/wyrd/icons/runestone_basic.dmi similarity index 100% rename from mods/content/anima/icons/runestone_basic.dmi rename to mods/content/wyrd/icons/runestone_basic.dmi diff --git a/mods/content/anima/icons/runestone_gilded.dmi b/mods/content/wyrd/icons/runestone_gilded.dmi similarity index 100% rename from mods/content/anima/icons/runestone_gilded.dmi rename to mods/content/wyrd/icons/runestone_gilded.dmi diff --git a/mods/content/anima/icons/runestone_layered.dmi b/mods/content/wyrd/icons/runestone_layered.dmi similarity index 100% rename from mods/content/anima/icons/runestone_layered.dmi rename to mods/content/wyrd/icons/runestone_layered.dmi diff --git a/mods/content/wyrd/icons/workings.dmi b/mods/content/wyrd/icons/workings.dmi new file mode 100644 index 000000000000..c03b082752e5 Binary files /dev/null and b/mods/content/wyrd/icons/workings.dmi differ diff --git a/mods/content/anima/potentia_definition.dm b/mods/content/wyrd/potentia/_potentia.dm similarity index 58% rename from mods/content/anima/potentia_definition.dm rename to mods/content/wyrd/potentia/_potentia.dm index bdd4c1587230..5e9b26e7edf7 100644 --- a/mods/content/anima/potentia_definition.dm +++ b/mods/content/wyrd/potentia/_potentia.dm @@ -1,25 +1,30 @@ +// Potentia: +// - Purified/degenerate energy extracted or processed from anima (Novu Uso). +// - 'I cast Fireball' -> fire and air potentia expended +// - 'rune of greater flood' -> etched onto crystalline water potentia + /decl/material/solid/potentia name = "potentia" solid_name = "crystalline potentia" - uid = "mat_anima" + uid = "mat_potentia" opacity = 0.7 color = COLOR_GRAY40 abstract_type = /decl/material/solid/potentia - uid = "mat_anima_generic" - var/anima_type = "unaspected" + uid = "mat_potentia_generic" + var/potentia_type = "unaspected" var/runestone_glow_intensity = 0.3 /// Simple spells that can be scribed onto runestones or cast on the fly. var/list/cantrips /// Spell used when a blank is cracked. - var/decl/runestone_spell_archetype/undirected_spell + var/decl/wyrd_archetype/undirected_spell /// Skill used for general spell knowledge. var/arcana_skill = SKILL_SCIENCE // TODO: arcana or magic skill /decl/material/solid/potentia/Initialize() - name = "[anima_type] potentia" - solid_name = "crystalline [anima_type] potentia" - liquid_name = "molten [anima_type] potentia" - gas_name = "gaseous [anima_type] potentia" + name = "[potentia_type] potentia" + solid_name = "crystalline [potentia_type] potentia" + liquid_name = "molten [potentia_type] potentia" + gas_name = "gaseous [potentia_type] potentia" for(var/spell in cantrips) cantrips -= spell cantrips |= GET_DECL(spell) @@ -31,17 +36,17 @@ /decl/material/solid/potentia/proc/get_cantrips_by_effect_type(mob/user, effect_type) // TODO: check arcana_skill on user - for(var/decl/runestone_spell_archetype/cantrip in cantrips) + for(var/decl/wyrd_archetype/cantrip in cantrips) if(cantrip.has_effect_type(effect_type)) LAZYDISTINCTADD(., cantrip) /decl/material/solid/potentia/fire - anima_type = "fire" + potentia_type = "fire" color = COLOR_ORANGE runestone_glow_intensity = 0.6 cantrips = list( - /decl/runestone_spell_archetype/flash, - /decl/runestone_spell_archetype/flare + /decl/wyrd_archetype/flash, + /decl/wyrd_archetype/flare ) - undirected_spell = /decl/runestone_spell_archetype/flare - uid = "mat_anima_fire" + undirected_spell = /decl/wyrd_archetype/flare + uid = "mat_potentia_fire" diff --git a/mods/content/anima/potentia_stack.dm b/mods/content/wyrd/potentia/potentia_stack.dm similarity index 85% rename from mods/content/anima/potentia_stack.dm rename to mods/content/wyrd/potentia/potentia_stack.dm index c8f3897f79a3..46e521e03e4c 100644 --- a/mods/content/anima/potentia_stack.dm +++ b/mods/content/wyrd/potentia/potentia_stack.dm @@ -3,10 +3,10 @@ desc = "A condensed, crystalline form of magical energy, cut into rough, unworked rounds and ready for etching." singular_name = "blank" plural_name = "blanks" - icon_state = "anima" - icon = 'mods/content/anima/icons/anima_blank.dmi' - plural_icon_state = "anima-mult" - max_icon_state = "anima-max" + icon_state = "blank" + icon = 'mods/content/wyrd/icons/blanks.dmi' + plural_icon_state = "blank-mult" + max_icon_state = "blank-max" stack_merge_type = /obj/item/stack/material/potentia crafting_stack_type = /obj/item/stack/material/potentia material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC diff --git a/mods/content/anima/runestones.dm b/mods/content/wyrd/spells/runestones/_runestones.dm similarity index 61% rename from mods/content/anima/runestones.dm rename to mods/content/wyrd/spells/runestones/_runestones.dm index e4874bde0225..e8b4d963d3a2 100644 --- a/mods/content/anima/runestones.dm +++ b/mods/content/wyrd/spells/runestones/_runestones.dm @@ -1,7 +1,7 @@ /obj/item/runestone name = "runestone" - desc = "An etched, faceted round of crystalline anima, scribed with a complex rune. Shatter the runestone to evoke the spell scribed upon it." - icon = 'mods/content/anima/icons/runestone_basic.dmi' + desc = "An etched, faceted round of crystallised magic, scribed with a complex rune. Shatter the runestone to evoke the spell scribed upon it." + icon = 'mods/content/wyrd/icons/runestone_basic.dmi' icon_state = ICON_STATE_WORLD material = /decl/material/solid/potentia w_class = ITEM_SIZE_SMALL @@ -9,14 +9,14 @@ var/work_skill = SKILL_CONSTRUCTION var/work_tool = TOOL_SCALPEL // TODO: TOOL_CHISEL - var/anima_density = 1 + var/potentia_density = 1 var/cracked = FALSE - var/datum/anima_working/stored_spell + var/datum/wyrd_working/stored_spell - var/static/list/anima_density_labels = list( - 'mods/content/anima/icons/runestone_basic.dmi' = "basic", - 'mods/content/anima/icons/runestone_layered.dmi' = "layered", - 'mods/content/anima/icons/runestone_gilded.dmi' = "gilded" + var/static/list/potentia_density_labels = list( + 'mods/content/wyrd/icons/runestone_basic.dmi' = "basic", + 'mods/content/wyrd/icons/runestone_layered.dmi' = "layered", + 'mods/content/wyrd/icons/runestone_gilded.dmi' = "gilded" ) /obj/item/runestone/Initialize() @@ -28,15 +28,15 @@ var/list/new_name = list() if(cracked) new_name += "cracked" - new_name += anima_density_labels[icon] - var/decl/material/solid/potentia/anima = material - new_name += istype(anima) ? anima.anima_type : "unaspected" + new_name += potentia_density_labels[icon] + var/decl/material/solid/potentia/potentia = material + new_name += istype(potentia) ? potentia.potentia_type : "unaspected" new_name += initial(name) if(stored_spell) if(stored_spell.spell_master) new_name += "of" - if(stored_spell.masterwork_effect) - new_name += stored_spell.masterwork_effect.name + if(stored_spell.variant) + new_name += stored_spell.variant.name else new_name += stored_spell.spell_master.name else @@ -59,27 +59,27 @@ to_chat(user, SPAN_WARNING("\The [src] is made of [material], not [W.material].")) return TRUE - if(anima_density >= length(anima_density_labels)) + if(potentia_density >= length(potentia_density_labels)) to_chat(user, SPAN_WARNING("\The [src] is as pure and dense as it can be without shattering.")) return TRUE - var/obj/item/stack/material/potentia/anima = W - if(anima.get_amount() < anima_density) - to_chat(user, SPAN_WARNING("You need at least [anima_density] blank\s to refine \the [src] further.")) + var/obj/item/stack/material/potentia/potentia = W + if(potentia.get_amount() < potentia_density) + to_chat(user, SPAN_WARNING("You need at least [potentia_density] blank\s to refine \the [src] further.")) return TRUE if(work_skill) - if(!user.do_skilled((2 SECONDS) + (anima_density SECONDS), work_skill, src, check_holding = TRUE)) + if(!user.do_skilled((2 SECONDS) + (potentia_density SECONDS), work_skill, src, check_holding = TRUE)) return TRUE // Repeat a bunch of checks due to the time delay. - if(QDELETED(src) || QDELETED(anima) || anima.loc != user || anima.get_amount() < anima_density) + if(QDELETED(src) || QDELETED(potentia) || potentia.loc != user || potentia.get_amount() < potentia_density) return TRUE - if(anima_density >= length(anima_density_labels) || !anima.material?.type || material?.type != anima.material?.type) + if(potentia_density >= length(potentia_density_labels) || !potentia.material?.type || material?.type != potentia.material?.type) return TRUE - if(anima.use(anima_density)) - to_chat(user, SPAN_NOTICE("You fold [anima_density] blank\s into \the [src], increasing its potency.")) - anima_density++ + if(potentia.use(potentia_density)) + to_chat(user, SPAN_NOTICE("You fold [potentia_density] blank\s into \the [src], increasing its potency.")) + potentia_density++ update_strings() return TRUE @@ -107,21 +107,21 @@ // Incomplete runestones or AOE spells are activated immediately. if(!stored_spell?.spell_master) - var/decl/material/solid/potentia/anima = material - if(istype(anima) && anima.undirected_spell?.base_effect) - anima.undirected_spell.base_effect.evoke_spell(user, get_turf(user), null, deliberate = deliberate) + var/decl/material/solid/potentia/potentia = material + if(istype(potentia) && potentia.undirected_spell?.base_effect) + potentia.undirected_spell.base_effect.evoke_spell(user, get_turf(user), null, deliberate = deliberate) else new /obj/item/shard(get_turf(user), material?.type) qdel(src) return TRUE - var/decl/anima_spell_effect/casting_spell = stored_spell.masterwork_effect || stored_spell.spell_master.base_effect + var/decl/wyrd_effect/casting_spell = stored_spell.variant || stored_spell.spell_master.base_effect if(casting_spell) - if(stored_spell.effect_type == ANIMA_SPELL_AOE) + if(stored_spell.effect_type == /decl/wyrd_effect::WYRD_AOE) casting_spell.evoke_spell(user, get_turf(user), src) qdel(src) return TRUE - to_chat(user, casting_spell.show_primed_message(user, stored_spell.effect_type)) + to_chat(user, casting_spell.show_runestone_primed_message(user, src)) update_strings() return TRUE @@ -130,7 +130,7 @@ return cracked ? FALSE : ..() /obj/item/runestone/afterattack(atom/target, mob/user, proximity_flag, click_parameters) - var/decl/anima_spell_effect/casting_spell = cracked && stored_spell && (stored_spell.masterwork_effect || stored_spell.spell_master.base_effect) + var/decl/wyrd_effect/casting_spell = cracked && stored_spell && (stored_spell.variant || stored_spell.spell_master.base_effect) if(casting_spell) casting_spell.evoke_spell(user, target, src, in_proximity = proximity_flag) return TRUE @@ -143,9 +143,9 @@ if(overlay && cracked && istype(material, /decl/material/solid/potentia)) var/check_state = "[overlay.icon_state]-glow" if(check_state_in_icon(check_state, overlay.icon)) - var/decl/material/solid/potentia/anima_mat = material + var/decl/material/solid/potentia/potentia_mat = material var/image/I = image(overlay.icon, check_state) - I.alpha = 255 * anima_mat.runestone_glow_intensity + I.alpha = 255 * potentia_mat.runestone_glow_intensity I.appearance_flags |= RESET_ALPHA overlay.overlays += I return ..() @@ -153,7 +153,7 @@ /obj/item/runestone/on_update_icon() . = ..() - var/new_icon = anima_density_labels[anima_density] + var/new_icon = potentia_density_labels[potentia_density] if(icon != new_icon) icon = new_icon @@ -161,14 +161,14 @@ if(cracked) icon_state = "[icon_state]-cracked" if(istype(material, /decl/material/solid/potentia)) - var/decl/material/solid/potentia/anima_mat = material + var/decl/material/solid/potentia/potentia_mat = material var/image/I = image(icon, "[icon_state]-glow") - I.alpha = 255 * anima_mat.runestone_glow_intensity + I.alpha = 255 * potentia_mat.runestone_glow_intensity I.appearance_flags |= RESET_ALPHA add_overlay(I) compile_overlays() else if(stored_spell) - if(stored_spell.masterwork_effect) + if(stored_spell.variant) icon_state = "[icon_state]-etched" else if(stored_spell.effect_type) icon_state = "[icon_state]-complex" diff --git a/mods/content/anima/spellscribing.dm b/mods/content/wyrd/spells/runestones/spellscribing.dm similarity index 77% rename from mods/content/anima/spellscribing.dm rename to mods/content/wyrd/spells/runestones/spellscribing.dm index 8d4ac30e5a16..1d171ee51fc1 100644 --- a/mods/content/anima/spellscribing.dm +++ b/mods/content/wyrd/spells/runestones/spellscribing.dm @@ -15,7 +15,7 @@ // // All uses have a long delay and a skill check, failing the skill check: // - stage 1 will just fail -// - stage 2 will destroy the spell and produce anima shards for recycling or potions +// - stage 2 will destroy the spell and produce potentia shards for recycling or potions // - stage 3 will set the blank off like an AOE spell and destroy it in the process (bad for dangerous spells) /obj/item/runestone/proc/can_scribe(mob/user, obj/item/implement) @@ -42,11 +42,11 @@ return FALSE . = TRUE // We mostly don't care after this point. Return type is only used for attackby() return. - var/decl/material/solid/potentia/anima_mat = material + var/decl/material/solid/potentia/potentia_mat = material // Stage one: choose range. if(!stored_spell) - var/effect_type = input(user, "What type of effect do you wish this runestone to evoke?", "Spellscribing") as null|anything in global._anima_spell_effect_types + var/effect_type = input(user, "What type of effect do you wish this runestone to evoke?", "Spellscribing") as null|anything in global._wyrd_spell_effect_types if(QDELETED(src) || !effect_type || stored_spell) return if(!scribe_check(user, tool, RUNESCRIBE_DIFFICULTY_EASY)) @@ -54,32 +54,32 @@ // Recheck due to do_after if(QDELETED(src) || stored_spell) return - stored_spell = new(global._anima_spell_effect_types[effect_type]) + stored_spell = new(global._wyrd_spell_effect_types[effect_type]) to_chat(user, SPAN_NOTICE("You carefully etch \the [src] with channels suitable for a [stored_spell.effect_type] evocation.")) update_strings() return // Stage two: choose specific spell. if(!stored_spell.spell_master) - var/list/possible_spells = anima_mat.get_cantrips_by_effect_type(user, stored_spell.effect_type) + var/list/possible_spells = potentia_mat.get_cantrips_by_effect_type(user, stored_spell.effect_type) if(!length(possible_spells)) to_chat(user, SPAN_WARNING("You do not know any suitable workings to etch into \the [src].")) return - var/decl/runestone_spell_archetype/chosen_spell = input(user, "Which spell do you wish this runestone to evoke?", "Spellscribing") as null|anything in possible_spells + var/decl/wyrd_archetype/chosen_spell = input(user, "Which spell do you wish this runestone to evoke?", "Spellscribing") as null|anything in possible_spells if(!chosen_spell) return if(!scribe_check(user, tool, RUNESCRIBE_DIFFICULTY_HARD)) if(can_scribe(user, tool)) // if it's just a tool failure then be nice, if it's a skill or do_after fail have a little bit of mercy - if(anima_density <= 1 || prob(15)) + if(potentia_density <= 1 || prob(15)) to_chat(user, SPAN_DANGER("You slip up, and \the [src] cracks apart!")) crack_runestone(user, FALSE) // whoopsie poopsie else to_chat(user, SPAN_DANGER("You fumble, and the existing runework is damaged.")) - anima_density-- + potentia_density-- update_strings() return // Refresh spell list for checking. - possible_spells = anima_mat.get_cantrips_by_effect_type(user, stored_spell.effect_type) + possible_spells = potentia_mat.get_cantrips_by_effect_type(user, stored_spell.effect_type) if(!(chosen_spell in possible_spells) || QDELETED(src) || stored_spell.spell_master) return to_chat(user, SPAN_NOTICE("You painstakingly etch the runes required to evoke [chosen_spell] onto \the [src].")) @@ -87,28 +87,28 @@ update_strings() return - if(stored_spell.masterwork_effect) + if(stored_spell.variant) to_chat(user, SPAN_WARNING("\The [src] is completely covered in runework, and cannot be further etched.")) return // Stage three: choose masterwork rune effect. - var/list/masterwork_effects = stored_spell.spell_master.get_masterwork_effects(user, tool) - if(!length(masterwork_effects)) + var/list/variants = stored_spell.spell_master.get_variants(user, tool) + if(!length(variants)) to_chat(user, SPAN_WARNING("You cannot see any way to refine the runes etched into \the [src].")) return - var/decl/anima_spell_effect/chosen_effect = input(user, "Which masterwork effect do you wish this runestone to evoke?", "Spellscribing") as null|anything in masterwork_effects - if(!chosen_effect || !stored_spell || !stored_spell.spell_master || stored_spell.masterwork_effect || !(chosen_effect in stored_spell.spell_master.get_masterwork_effects(user, tool))) + var/decl/wyrd_effect/chosen_effect = input(user, "Which masterwork effect do you wish this runestone to evoke?", "Spellscribing") as null|anything in variants + if(!chosen_effect || !stored_spell || !stored_spell.spell_master || stored_spell.variant || !(chosen_effect in stored_spell.spell_master.get_variants(user, tool))) return if(!scribe_check(user, tool, RUNESCRIBE_DIFFICULTY_MASTER)) if(can_scribe(user, tool)) // if it's just a tool failure then be nice, if it's a skill or do_after fail have no mercy to_chat(user, SPAN_DANGER("You slip up, and \the [src] cracks apart!")) crack_runestone(user, FALSE) // whoopsie poopsie return - if(!chosen_effect || !stored_spell || !stored_spell.spell_master || stored_spell.masterwork_effect || !(chosen_effect in stored_spell.spell_master.get_masterwork_effects(user, tool))) + if(!chosen_effect || !stored_spell || !stored_spell.spell_master || stored_spell.variant || !(chosen_effect in stored_spell.spell_master.get_variants(user, tool))) return to_chat(user, SPAN_NOTICE("You delicately etch cross-links and esoteric runes that translate [stored_spell.spell_master] into [chosen_effect].")) - stored_spell.masterwork_effect = chosen_effect + stored_spell.variant = chosen_effect update_strings() #undef RUNESCRIBE_DIFFICULTY_EASY diff --git a/mods/content/wyrd/spells/spell_archetypes.dm b/mods/content/wyrd/spells/spell_archetypes.dm new file mode 100644 index 000000000000..5769c6ecd4ac --- /dev/null +++ b/mods/content/wyrd/spells/spell_archetypes.dm @@ -0,0 +1,28 @@ +// Handles all effects associated with a spell. +/decl/wyrd_archetype + abstract_type = /decl/wyrd_archetype + var/name + var/decl/wyrd_effect/base_effect + var/list/variants + +/decl/wyrd_archetype/Initialize() + variants = decls_repository.get_decls_unassociated(variants) + base_effect = GET_DECL(base_effect) + name = base_effect.name + return ..() + +/decl/wyrd_archetype/proc/has_effect_type(effect_type) + return TRUE + +/decl/wyrd_archetype/proc/get_variants(mob/user, obj/item/implement) + // TODO: skill/trait/mind checks on user + return variants + +/decl/wyrd_archetype/flash + base_effect = /decl/wyrd_effect/flash + variants = list( + /decl/wyrd_effect/gloom + ) + +/decl/wyrd_archetype/flare + base_effect = /decl/wyrd_effect/flare diff --git a/mods/content/wyrd/spells/spell_datum.dm b/mods/content/wyrd/spells/spell_datum.dm new file mode 100644 index 000000000000..77631c4a9a02 --- /dev/null +++ b/mods/content/wyrd/spells/spell_datum.dm @@ -0,0 +1,14 @@ +var/global/list/_wyrd_spell_effect_types = list( + "area of effect" = /decl/wyrd_effect::WYRD_AOE, + "close-range" = /decl/wyrd_effect::WYRD_MELEE, + "long-range" = /decl/wyrd_effect::WYRD_RANGED +) + +/datum/wyrd_working + var/effect_type = /decl/wyrd_effect::WYRD_AOE + var/effect_strength = 1 + var/decl/wyrd_archetype/spell_master + var/decl/wyrd_effect/variant + +/datum/wyrd_working/New(_effect) + effect_type = _effect \ No newline at end of file diff --git a/mods/content/wyrd/spells/spell_effect.dm b/mods/content/wyrd/spells/spell_effect.dm new file mode 100644 index 000000000000..e633b8ae0a41 --- /dev/null +++ b/mods/content/wyrd/spells/spell_effect.dm @@ -0,0 +1,136 @@ +/decl/wyrd_effect + abstract_type = /decl/wyrd_effect + var/name + var/description + var/cost = 1 + var/arcana_skill = SKILL_SCIENCE // TODO: arcana or magic skill + var/stamina_cost = 20 + var/alist/minimum_anima + var/alist/maximum_anima + var/cast_sound = 'sound/effects/bamf.ogg' + var/cast_volume = 60 + + var/const/WYRD_AOE = 0 + var/const/WYRD_MELEE = 1 + var/const/WYRD_RANGED = 2 + var/const/ANIMA_INSUFFICIENT = 0 + var/const/ANIMA_SUITABLE = 1 + var/const/ANIMA_OVERSATURATED = 2 + +/decl/wyrd_effect/proc/evoke_spell(mob/user, atom/target, obj/item/runestone/runestone, caster_effect, caster_strength, in_proximity, deliberate = TRUE) + // Get our spell parameters. + var/spent_cost = runestone ? min(runestone.potentia_density, cost) : cost + var/evoke_effect = caster_effect || runestone?.stored_spell?.effect_type || WYRD_AOE + if(!in_proximity && evoke_effect == WYRD_MELEE) + evoke_effect = WYRD_RANGED + var/evoke_strength = caster_strength || runestone?.potentia_density || 1 + + // Cast the actual spell. + do_evocation(user, target, evoke_effect, evoke_strength, deliberate) + + // Expend resources. + if(runestone) + if(runestone.potentia_density > spent_cost && evoke_effect != WYRD_AOE) + runestone.potentia_density -= spent_cost + runestone.update_icon() + runestone.update_strings() + else if(!QDELETED(runestone)) + to_chat(user, SPAN_DANGER("Your runestone crumbles to dust!")) + qdel(runestone) + else if(stamina_cost) + user.adjust_stamina(-(stamina_cost)) + +/decl/wyrd_effect/proc/show_runestone_primed_message(mob/user, obj/item/runestone/stone) + to_chat(user, SPAN_NOTICE("Raw potentia wisps from \the [stone] and wreathes your [parse_zone(user.get_active_held_item_slot())], ready to be directed.")) + +/decl/wyrd_effect/proc/do_evocation(mob/user, atom/target, evoke_effect = WYRD_AOE, evoke_strength = 1, deliberate = TRUE) + SHOULD_CALL_PARENT(TRUE) + if(cast_sound && cast_volume) + playsound(get_turf(user), cast_sound, cast_volume) + +/decl/wyrd_effect/proc/can_be_worked_at(mob/user, turf/location) + + // TODO: compare against user and target anima + if(!length(minimum_anima) && !length(maximum_anima)) + return ANIMA_SUITABLE + + var/alist/local_anima = location.get_ambient_anima() + if(length(maximum_anima)) + for(var/atype,avalue in maximum_anima) + if(local_anima[atype] > maximum_anima[atype]) + return ANIMA_OVERSATURATED + if(length(minimum_anima)) + for(var/atype,avalue in minimum_anima) + if(local_anima[atype] < minimum_anima[atype]) + return ANIMA_INSUFFICIENT + + return ANIMA_SUITABLE + +/decl/wyrd_effect/flash + name = "Flash" + description = "Release the channeled potentia in a blinding flash of light." + +/decl/wyrd_effect/flash/do_evocation(mob/user, atom/target, evoke_effect = WYRD_AOE, evoke_strength = 1, deliberate = TRUE) + . = ..() + if(evoke_effect == WYRD_AOE) + user.visible_message(SPAN_DANGER("\The [user] emits a blinding flash of light!")) + for(var/mob/living/victim in view(evoke_strength, user)) + if(deliberate && victim == user) + continue + victim.handle_flashed(rand(evoke_strength, evoke_strength*2 + 1)) // min is 1 to 2, max is 3 to 7; base flash is 2 to 7 in a range of 3 tiles + else + user.visible_message(SPAN_DANGER("\The [user] drowns \the [target] in a blinding flash of light!")) + if(isliving(target)) + var/mob/living/victim = target + victim.handle_flashed(evoke_strength * 2 + 1) // since we're only attacking one target, we just go with the max instead of a random value + return TRUE + +/decl/wyrd_effect/gloom + name = "Gloom" + description = "Burn the channeled potentia to create a burst of cloying darkness." + +/decl/wyrd_effect/gloom/do_evocation(mob/user, atom/target, evoke_effect = WYRD_AOE, evoke_strength = 1, deliberate = TRUE) + . = ..() + if(evoke_effect == WYRD_AOE) + user.visible_message(SPAN_DANGER("\The [user] emits a burst of cloying darkness!")) + var/atom/movable/gloom/gloom = new(target, evoke_strength) // with AOE spell, target is user's turf + QDEL_IN_CLIENT_TIME(gloom, evoke_strength * 5 SECONDS) + else + user.visible_message(SPAN_DANGER("\The [user] drowns \the [target] in darkness!")) + if(isliving(target)) + var/mob/living/victim = target + victim.overlay_fullscreen("gloom", /obj/screen/fullscreen/blackout) + addtimer(CALLBACK(victim, TYPE_PROC_REF(/mob, clear_fullscreen), "gloom"), evoke_strength * 3 SECONDS) + return TRUE + +/atom/movable/gloom + simulated = FALSE + mouse_opacity = MOUSE_OPACITY_UNCLICKABLE + +/atom/movable/gloom/Initialize(ml, strength) + . = ..() + set_light(strength, -10, "#ffffff") // negative light, cleared on destroy + +/decl/wyrd_effect/flare + name = "Flare" + description = "Release the channeled potentia in an undirected burst of flame." + +/decl/wyrd_effect/flare/do_evocation(mob/user, atom/target, evoke_effect = WYRD_AOE, evoke_strength = 1, deliberate = TRUE) + . = ..() + var/turf/source_turf = get_turf(user) + // level 1 flare is hot enough to boil water. level 3 flare is hot enough to light a campfire and ignite wood + // divided by 0.9 and plus one to ensure inefficiency doesn't stop us from lighting campfires + var/fire_temperature = Interpolate(T100C, /decl/material/solid/organic/wood::ignition_point / 0.9 + 1, (evoke_strength - 1) / 3) + if(evoke_effect == WYRD_AOE) + user.visible_message(SPAN_DANGER("\The [user] is wreathed in a blast of fire!")) + for(var/turf/T in RANGE_TURFS(source_turf, evoke_strength)) + T = T.resolve_to_actual_turf() + new /obj/effect/fake_fire/variable/owned(T, fire_temperature, evoke_strength SECONDS, deliberate ? user : null) + else + user.visible_message(SPAN_DANGER("\The [user] hurls a blast of fire at \the [target]!")) + var/obj/item/projectile/fireball/projectile = new(source_turf) + projectile.fire_lifetime = evoke_strength SECONDS + // level 1 flare is hot enough to boil water. level 3 flare is hot enough to light a campfire and ignite wood + projectile.fire_temperature = Interpolate(T100C, projectile::fire_temperature, (evoke_strength - 1) / 3) + projectile.launch_from_gun(target, user.get_target_zone(), user) + return TRUE diff --git a/mods/content/wyrd/spells/spellcasting.dm b/mods/content/wyrd/spells/spellcasting.dm new file mode 100644 index 000000000000..1bf923b090b9 --- /dev/null +++ b/mods/content/wyrd/spells/spellcasting.dm @@ -0,0 +1,80 @@ +// Use a second ability handler so that spells are categorised separately to general wyrd abilities. +/datum/ability_handler/wyrd_workings + category_toggle_type = /obj/screen/ability/category/wyrd_workings + +/obj/screen/ability/category/wyrd_workings + name = "Wyrd Workings" + icon = 'mods/content/wyrd/icons/workings.dmi' + +// TODO: condition + skill +/mob/proc/get_wyrd_casting_strength() + return 1 + +/decl/ability/wyrd/spell + abstract_type = /decl/ability/wyrd/spell + target_selector = /decl/ability_targeting/single_atom/can_target_user + prep_cast = TRUE + is_melee_invocation = TRUE + is_ranged_invocation = TRUE + end_prep_on_cast = FALSE + associated_handler_type = /datum/ability_handler/wyrd_workings + + ready_ability_1p_str = SPAN_NOTICE("You weave potential, preparing yourself to cast $SPELL$.") + cancel_ability_1p_str = SPAN_NOTICE("You shake loose the woven energy, abandoning $SPELL$.") + fail_cast_1p_str = SPAN_WARNING("Your working unravels!") + + var/decl/wyrd_effect/effect + var/force_effect_type + +/decl/ability/wyrd/spell/prepare_to_cast(mob/user, atom/target, list/metadata, datum/ability_handler/handler) + if(!(. = ..())) + return + + var/decl/background_detail/culture = user.get_background_datum(/decl/background_category/faction) + switch(effect.can_be_worked_at(user, get_turf(target))) + if(effect.ANIMA_INSUFFICIENT) + to_chat(user, replacetext(culture.anima_failed_working_insufficient_1p, "$SPELL$", "[effect.name]")) + return FALSE + if(effect.ANIMA_OVERSATURATED) + to_chat(user, replacetext(culture.anima_failed_working_excess_1p, "$SPELL$", "[effect.name]")) + return FALSE + + if(effect.stamina_cost && user.get_stamina() < effect.stamina_cost) + to_chat(user, replacetext(culture.anima_failed_exhaustion_1p, "$SPELL$", "[effect.name]")) + return FALSE + +/decl/ability/wyrd/spell/validate() + . = ..() + if(!istype(effect, /decl/wyrd_effect)) + . += "missing or malformed effect type: '[effect]'" + +/decl/ability/wyrd/spell/Initialize() + effect = GET_DECL(effect) + var/effect_name = "[effect.name]" + ready_ability_1p_str = replacetext(ready_ability_1p_str, "$SPELL$", effect_name) + cancel_ability_1p_str = replacetext(cancel_ability_1p_str, "$SPELL$", effect_name) + . = ..() + +/decl/ability/wyrd/spell/apply_ability_effect_to(mob/living/user, atom/target, list/metadata) + . = ..() + var/target_self = (user == target) + var/proximity = target_self || (get_dist(user, target) <= 1 && user.Adjacent(target)) + var/range_effect = target_self ? /decl/wyrd_effect::WYRD_AOE : (proximity ? /decl/wyrd_effect::WYRD_MELEE : /decl/wyrd_effect::WYRD_RANGED) + effect.evoke_spell(user, target, caster_effect = range_effect, caster_strength = user.get_wyrd_casting_strength(), in_proximity = proximity) + +/decl/ability/wyrd/spell/flash + name = "Flash" + effect = /decl/wyrd_effect/flash + ability_icon_state = "flash" + +/decl/ability/wyrd/spell/gloom + name = "Gloom" + effect = /decl/wyrd_effect/gloom + ability_icon_state = "gloom" + +/decl/ability/wyrd/spell/flare + name = "Flare" + effect = /decl/wyrd_effect/flare + ability_icon_state = "flare" + // don't let people set themselves alight + target_selector = /decl/ability_targeting/single_atom diff --git a/mods/content/wyrd/wyrd_abilities.dm b/mods/content/wyrd/wyrd_abilities.dm new file mode 100644 index 000000000000..005f29edd642 --- /dev/null +++ b/mods/content/wyrd/wyrd_abilities.dm @@ -0,0 +1,53 @@ +// Handler for a basic set of wyrd abilities - primarily your sensitivity to local anima and ability to check your own anima. +/datum/ability_handler/wyrd_general + category_toggle_type = /obj/screen/ability/category/wyrd_general + +/datum/ability_handler/wyrd/general + +/obj/screen/ability/category/wyrd_general + name = "Wyrd Practice" + icon = 'mods/content/wyrd/icons/abilities.dmi' + +/obj/screen/ability/button/wyrd + icon = 'mods/content/wyrd/icons/abilities.dmi' + +/decl/ability/wyrd + abstract_type = /decl/ability/wyrd + ability_icon = 'mods/content/wyrd/icons/abilities.dmi' + target_selector = /decl/ability_targeting/target_self + associated_handler_type = /datum/ability_handler/wyrd_general + ui_element_type = /obj/screen/ability/button/wyrd + +/decl/ability/wyrd/check_ambient + name = "Dowse Aura" + ability_icon_state = "outward" + +/decl/ability/wyrd/check_ambient/apply_ability_effect_to(mob/living/user, atom/target, list/metadata) + . = ..() + for(var/atype,avalue in user.get_ambient_anima()) + var/decl/anima/anima = GET_DECL(atype) + to_chat(user, anima.get_ambient_anima_description(avalue, user.get_background_datum(/decl/background_category/faction))) + +/decl/ability/wyrd/check_personal + name = "Self-Reflection" + ability_icon_state = "inward" + +/decl/ability/wyrd/check_personal/apply_ability_effect_to(mob/living/user, atom/target, list/metadata) + . = ..() + for(var/atype,avalue in user.get_personal_anima()) + var/decl/anima/anima = GET_DECL(atype) + to_chat(user, anima.get_personal_anima_description(avalue, user.get_background_datum(/decl/background_category/faction))) + +/mob/living/verb/debug_anima_verb() + set name = "Debug Anima" + set category = "Debug" + set src = usr + + set_extension(src, /datum/extension/anima_aura) + + add_ability(/decl/ability/wyrd/check_ambient) + add_ability(/decl/ability/wyrd/check_personal) + + add_ability(/decl/ability/wyrd/spell/flash) + add_ability(/decl/ability/wyrd/spell/gloom) + add_ability(/decl/ability/wyrd/spell/flare) diff --git a/mods/gamemodes/cult/abilities/construct.dm b/mods/gamemodes/cult/abilities/construct.dm index 60138eaba356..bbf1a5bc0de3 100644 --- a/mods/gamemodes/cult/abilities/construct.dm +++ b/mods/gamemodes/cult/abilities/construct.dm @@ -2,10 +2,9 @@ /decl/ability/cult/construct name = "Artificer" desc = "This spell conjures a construct which may be controlled by shades." - target_selector = /decl/ability_targeting/clear_turf + target_selector = /decl/ability_targeting/clear_turf/construct overlay_icon = 'mods/gamemodes/cult/icons/effects.dmi' overlay_icon_state = "sparkles" - target_selector = /decl/ability_targeting/clear_turf/construct var/summon_type = /obj/structure/constructshell /decl/ability_targeting/clear_turf/construct/validate_target(mob/user, atom/target, list/metadata, decl/ability/ability) @@ -14,14 +13,14 @@ return FALSE return ..() && !istype(target, cult_ability.summon_type) && !(locate(cult_ability.summon_type) in target) -/decl/ability/cult/construct/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) +/decl/ability/cult/construct/apply_ability_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) . = ..() var/turf/target_turf = get_turf(hit_target) if(istype(target_turf)) if(ispath(summon_type, /turf)) target_turf = target_turf.ChangeTurf(summon_type, TRUE, FALSE, TRUE, TRUE, FALSE) if(target_turf) // We reapply effects as target no longer exists. - apply_effect_to(user, target_turf, metadata) + apply_ability_effect_to(user, target_turf, metadata) else if(ispath(summon_type, /atom)) new summon_type(target_turf) @@ -87,7 +86,7 @@ return TRUE return FALSE -/decl/ability/cult/construct/pylon/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) +/decl/ability/cult/construct/pylon/apply_ability_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) for(var/obj/structure/cult/pylon/P in get_turf(hit_target)) if(P.isbroken) P.repair(user) diff --git a/mods/gamemodes/cult/abilities/harvest.dm b/mods/gamemodes/cult/abilities/harvest.dm index 49569d6a25ef..dc8bfc1002ff 100644 --- a/mods/gamemodes/cult/abilities/harvest.dm +++ b/mods/gamemodes/cult/abilities/harvest.dm @@ -9,7 +9,7 @@ prepare_message_3p_str = "Space around $USER$ begins to bubble and decay as a terrible vista begins to intrude..." prepare_message_1p_str = "You bore through space and time, seeking the essence of the Geometer of Blood." fail_cast_1p_str = "Reality reasserts itself, preventing your return to Nar-Sie." - target_selector = /decl/ability_targeting/living_mob + target_selector = /decl/ability_targeting/single_atom/living_mob /decl/ability/cult/construct/harvest/can_use_ability(mob/user, list/metadata, silent) . = ..() @@ -22,7 +22,7 @@ to_chat(user, SPAN_DANGER("You cannot sense the Geometer of Blood!")) return FALSE -/decl/ability/cult/construct/harvest/apply_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) +/decl/ability/cult/construct/harvest/apply_ability_effect(mob/user, atom/hit_target, list/metadata, obj/item/projectile/ability/projectile) ..() var/destination = null for(var/obj/effect/narsie/N in global.narsie_list) diff --git a/test/check-paths.sh b/test/check-paths.sh index f59aa02ce4c7..44b988aff5ea 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -41,7 +41,7 @@ exactly 0 "incorrect indentations" '^( {4,})' -P exactly 22 "text2path uses" 'text2path' exactly 4 "update_icon() overrides" '\/update_icon\(' -P exactly 0 "goto uses" '\bgoto\b' -exactly 10 "atom/New uses" '^/(obj|atom|area|mob|turf).*/New\(' +exactly 11 "atom/New uses" '^/(obj|atom|area|mob|turf).*/New\(' exactly 1 "decl/New uses" '^/decl.*/New\(' exactly 3 "tag uses" '(?