diff --git a/code/__DEFINES/achievements.dm b/code/__DEFINES/achievements.dm index 3502a6f4fc4..f8f33c46644 100644 --- a/code/__DEFINES/achievements.dm +++ b/code/__DEFINES/achievements.dm @@ -34,6 +34,7 @@ #define MEDAL_RUST_ASCENSION "Rust" #define MEDAL_VOID_ASCENSION "Void" #define MEDAL_BLADE_ASCENSION "Blade" +#define MEDAL_COSMOS_ASCENSION "Cosmos" #define MEDAL_TOOLBOX_SOUL "Toolsoul" #define MEDAL_CHEM_TUT "Beginner Chemist" #define MEDAL_HOT_DAMN "Hot Damn!" diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 9c217f8907c..63b7267e6be 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -77,6 +77,7 @@ #define PATH_FLESH "Flesh Path" #define PATH_VOID "Void Path" #define PATH_BLADE "Blade Path" +#define PATH_COSMIC "Cosmic Path" /// Defines are used in /proc/has_living_heart() to report if the heretic has no heart period, no living heart, or has a living heart. #define HERETIC_NO_HEART_ORGAN -1 diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 144054efbaa..258aa884b2e 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -42,6 +42,8 @@ GLOBAL_LIST_INIT(turfs_openspace, typecacheof(list( #define isfloorturf(A) (istype(A, /turf/open/floor)) +#define ismiscturf(A) (istype(A, /turf/open/misc)) + #define isclosedturf(A) (istype(A, /turf/closed)) #define isindestructiblewall(A) (istype(A, /turf/closed/indestructible)) diff --git a/code/datums/achievements/misc_achievements.dm b/code/datums/achievements/misc_achievements.dm index 582dc34770a..8d9e5e2db3d 100644 --- a/code/datums/achievements/misc_achievements.dm +++ b/code/datums/achievements/misc_achievements.dm @@ -158,6 +158,12 @@ database_id = MEDAL_BLADE_ASCENSION icon = "bladeascend" +/datum/award/achievement/misc/cosmic_ascension + name = "It arrived" + desc = "You managed to teleport an entity on the station that really shouldn't be there." + database_id = MEDAL_COSMOS_ASCENSION + icon = "cosmicascend" + /datum/award/achievement/misc/toolbox_soul name = "SOUL'd Out" desc = "My eternal soul was destroyed to make a toolbox look funny and all I got was this achievement..." diff --git a/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm b/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm index 151a4125bf6..32809ba652b 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm @@ -25,8 +25,8 @@ action_cooldown = 2 SECONDS /// If we should attack walls, be prepared for complaints about breaches var/can_attack_turfs = FALSE - /// Tries to bump open airlocks with an attack - var/bump_open_airlock = FALSE + /// For if you want your mob to be able to attack dense objects + var/can_attack_dense_objects = FALSE /datum/ai_behavior/attack_obstructions/perform(delta_time, datum/ai_controller/controller, target_key) . = ..() @@ -71,7 +71,7 @@ return FALSE /datum/ai_behavior/attack_obstructions/proc/can_smash_object(mob/living/basic/basic_mob, obj/object) - if (!object.density) + if (!object.density && !can_attack_dense_objects) return FALSE if (object.IsObscured()) return FALSE @@ -81,3 +81,6 @@ /datum/ai_planning_subtree/attack_obstacle_in_path/low_priority_target target_key = BB_LOW_PRIORITY_HUNTING_TARGET + +/datum/ai_planning_subtree/attack_obstacle_in_path/pet_target + target_key = BB_CURRENT_PET_TARGET diff --git a/code/datums/ai/basic_mobs/targetting_datums/dont_target_friends.dm b/code/datums/ai/basic_mobs/targetting_datums/dont_target_friends.dm index fdad4ac8b59..d7985ab9d62 100644 --- a/code/datums/ai/basic_mobs/targetting_datums/dont_target_friends.dm +++ b/code/datums/ai/basic_mobs/targetting_datums/dont_target_friends.dm @@ -2,13 +2,19 @@ /datum/targetting_datum/not_friends /// Stop regarding someone as a valid target once they pass this stat level, setting it to DEAD means you will happily attack corpses var/attack_until_past_stat = HARD_CRIT + /// If we can try to closed turfs or not + var/attack_closed_turf = FALSE ///Returns true or false depending on if the target can be attacked by the mob /datum/targetting_datum/not_friends/can_attack(mob/living/living_mob, atom/target) if (!target) return FALSE - if (isturf(target)) - return FALSE + if (attack_closed_turf) + if (isopenturf(target)) + return FALSE + else + if (isturf(target)) + return FALSE if (ismob(target)) var/mob/mob_target = target @@ -31,3 +37,6 @@ return TRUE // This is not our friend, fire at will return FALSE + +/datum/targetting_datum/not_friends/attack_closed_turfs + attack_closed_turf = TRUE diff --git a/code/datums/elements/death_gases.dm b/code/datums/elements/death_gases.dm new file mode 100644 index 00000000000..67bf2c6da17 --- /dev/null +++ b/code/datums/elements/death_gases.dm @@ -0,0 +1,36 @@ +/** + * ## death gasses element! + * + * Bespoke element that spawns one type of gas when a mob is killed + */ +/datum/element/death_gases + element_flags = ELEMENT_BESPOKE + argument_hash_start_idx = 3 + ///What gas the target spawns when killed + var/datum/gas/gas_type + ///The amount of gas spawned on death + var/amount_of_gas + +/datum/element/death_gases/Attach(datum/target, datum/gas/gas_type, amount_of_gas = 10) + . = ..() + if(!isliving(target)) + return ELEMENT_INCOMPATIBLE + if(!gas_type) + stack_trace("[type] added to [target] with NO GAS TYPE.") + src.gas_type = gas_type + src.amount_of_gas = amount_of_gas + RegisterSignal(target, COMSIG_LIVING_DEATH, PROC_REF(on_death)) + +/datum/element/death_gases/Detach(datum/target) + . = ..() + UnregisterSignal(target, COMSIG_LIVING_DEATH) + +///signal called by the stat of the target changing +/datum/element/death_gases/proc/on_death(mob/living/target, gibbed) + SIGNAL_HANDLER + var/datum/gas_mixture/mix_to_spawn = new() + mix_to_spawn.add_gas(gas_type) + mix_to_spawn.gases[gas_type][MOLES] = amount_of_gas + mix_to_spawn.temperature = T20C + var/turf/open/our_turf = get_turf(target) + our_turf.assume_air(mix_to_spawn) diff --git a/code/datums/elements/effect_trail.dm b/code/datums/elements/effect_trail.dm new file mode 100644 index 00000000000..6e51188c0e4 --- /dev/null +++ b/code/datums/elements/effect_trail.dm @@ -0,0 +1,28 @@ +/* + * An element used for making a trail of effects appear behind a movable atom when it moves. + */ + +/datum/element/effect_trail + /// The effect used for the trail generation. + var/obj/effect/chosen_effect + +/datum/element/effect_trail/Attach(datum/target) + . = ..() + if(!ismovable(target)) + return ELEMENT_INCOMPATIBLE + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(generate_effect)) + +/datum/element/effect_trail/Detach(datum/target) + . = ..() + UnregisterSignal(target, COMSIG_MOVABLE_MOVED) + +/// Generates an effect +/datum/element/effect_trail/proc/generate_effect(atom/movable/target_object) + SIGNAL_HANDLER + + var/turf/open/open_turf = get_turf(target_object) + if(istype(open_turf)) + new chosen_effect(open_turf) + +/datum/element/effect_trail/cosmic_trail + chosen_effect = /obj/effect/forcefield/cosmic_field/fast diff --git a/code/game/objects/effects/forcefields.dm b/code/game/objects/effects/forcefields.dm index 6d7f82a47e7..5d591738ec9 100644 --- a/code/game/objects/effects/forcefields.dm +++ b/code/game/objects/effects/forcefields.dm @@ -76,3 +76,34 @@ flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD receive_ricochet_chance_mod = INFINITY //we do ricochet a lot! initial_duration = 10 SECONDS + +/// The cosmic heretics forcefield +/obj/effect/forcefield/cosmic_field + name = "Cosmic Field" + desc = "A field that cannot be passed by people marked with a cosmic star." + icon = 'icons/effects/eldritch.dmi' + icon_state = "cosmic_carpet" + anchored = TRUE + layer = LOW_SIGIL_LAYER + density = FALSE + can_atmos_pass = ATMOS_PASS_NO + initial_duration = 30 SECONDS + /// Flags for what antimagic can just ignore our forcefields + var/antimagic_flags = MAGIC_RESISTANCE + +/obj/effect/forcefield/cosmic_field/Initialize(mapload, flags = MAGIC_RESISTANCE) + . = ..() + antimagic_flags = flags + +/obj/effect/forcefield/cosmic_field/CanAllowThrough(atom/movable/mover, border_dir) + if(!isliving(mover)) + return ..() + var/mob/living/living_mover = mover + if(living_mover.can_block_magic(antimagic_flags, charge_cost = 0)) + return ..() + if(living_mover.has_status_effect(/datum/status_effect/star_mark)) + return FALSE + return ..() + +/obj/effect/forcefield/cosmic_field/fast + initial_duration = 5 SECONDS diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 2e2542679b3..36a3a643fde 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -29,7 +29,7 @@ success_forcesay = "BEGONE FOUL MAGIKS!!", \ tip_text = "Clear rune", \ on_clear_callback = CALLBACK(src, PROC_REF(on_cult_rune_removed)), \ - effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune)) + effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune, /obj/effect/cosmic_rune)) AddElement(/datum/element/bane, target_type = /mob/living/simple_animal/revenant, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE) diff --git a/code/modules/antagonists/heretic/cosmic_effect.dm b/code/modules/antagonists/heretic/cosmic_effect.dm new file mode 100644 index 00000000000..1b0a82b14f6 --- /dev/null +++ b/code/modules/antagonists/heretic/cosmic_effect.dm @@ -0,0 +1,39 @@ +/obj/effect/cosmic_diamond + name = "Cosmic Diamond" + icon = 'icons/effects/eldritch.dmi' + icon_state = "cosmic_diamond" + anchored = TRUE + +/obj/effect/temp_visual/cosmic_cloud + name = "Cosmic Cloud" + icon = 'icons/effects/eldritch.dmi' + icon_state = "cosmic_cloud" + anchored = TRUE + duration = 8 + +/obj/effect/temp_visual/cosmic_explosion + name = "Cosmic Explosion" + icon = 'icons/effects/64x64.dmi' + icon_state = "cosmic_explosion" + anchored = TRUE + duration = 5 + pixel_x = -16 + pixel_y = -16 + +/obj/effect/temp_visual/space_explosion + name = "Space Explosion" + icon = 'icons/effects/64x64.dmi' + icon_state = "space_explosion" + anchored = TRUE + duration = 5 + pixel_x = -16 + pixel_y = -16 + +/obj/effect/temp_visual/cosmic_domain + name = "Cosmic Domain" + icon = 'icons/effects/160x160.dmi' + icon_state = "cosmic_domain" + anchored = TRUE + duration = 6 + pixel_x = -64 + pixel_y = -64 diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm index dfc31cc16b8..19d60f0f7eb 100644 --- a/code/modules/antagonists/heretic/heretic_antag.dm +++ b/code/modules/antagonists/heretic/heretic_antag.dm @@ -58,6 +58,7 @@ PATH_ASH = "white", PATH_VOID = "blue", PATH_BLADE = "label", // my favorite color is label + PATH_COSMIC = "purple", ) var/static/list/path_to_rune_color = list( PATH_START = COLOR_LIME, @@ -65,7 +66,8 @@ PATH_FLESH = COLOR_SOFT_RED, PATH_ASH = COLOR_VIVID_RED, PATH_VOID = COLOR_CYAN, - PATH_BLADE = COLOR_SILVER + PATH_BLADE = COLOR_SILVER, + PATH_COSMIC = COLOR_PURPLE, ) /datum/antagonist/heretic/Destroy() diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm index 61acc5f2cd1..aef5fd3771a 100644 --- a/code/modules/antagonists/heretic/items/heretic_blades.dm +++ b/code/modules/antagonists/heretic/items/heretic_blades.dm @@ -105,3 +105,12 @@ icon_state = "dark_blade" inhand_icon_state = "dark_blade" after_use_message = "The Torn Champion hears your call..." + +// Path of Cosmos's blade +/obj/item/melee/sickly_blade/cosmic + name = "\improper cosmic blade" + desc = "A mote of celestial resonance, shaped into a star-woven blade. \ + An iridescent exile, carving radiant trails, desperately seeking unification." + icon_state = "cosmic_blade" + inhand_icon_state = "cosmic_blade" + after_use_message = "The Stargazer hears your call..." diff --git a/code/modules/antagonists/heretic/knowledge/ash_lore.dm b/code/modules/antagonists/heretic/knowledge/ash_lore.dm index a23bf86ac84..dcb23609da1 100644 --- a/code/modules/antagonists/heretic/knowledge/ash_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/ash_lore.dm @@ -7,7 +7,7 @@ * Grasp of Ash * Ashen Passage * > Sidepaths: - * Priest's Ritual + * Scorching Shark * Ashen Eyes * * Mark of Ash @@ -15,14 +15,14 @@ * Fire Blast * Mask of Madness * > Sidepaths: - * Curse of Corrosion + * Space Phase * Curse of Paralysis * * Fiery Blade * Nightwatcher's Rebirth * > Sidepaths: * Ashen Ritual - * Rusted Ritual + * Eldritch Coin * * Ashlord's Rite */ @@ -75,7 +75,7 @@ next_knowledge = list( /datum/heretic_knowledge/mark/ash_mark, /datum/heretic_knowledge/codex_cicatrix, - /datum/heretic_knowledge/essence, + /datum/heretic_knowledge/summon/fire_shark, /datum/heretic_knowledge/medallion, ) spell_to_add = /datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash @@ -130,7 +130,7 @@ next_knowledge = list( /datum/heretic_knowledge/blade_upgrade/ash, /datum/heretic_knowledge/reroll_targets, - /datum/heretic_knowledge/curse/corrosion, + /datum/heretic_knowledge/spell/space_phase, /datum/heretic_knowledge/curse/paralysis, ) required_atoms = list( @@ -168,7 +168,7 @@ next_knowledge = list( /datum/heretic_knowledge/ultimate/ash_final, /datum/heretic_knowledge/summon/ashy, - /datum/heretic_knowledge/summon/rusty, + /datum/heretic_knowledge/eldritch_coin, ) spell_to_add = /datum/action/cooldown/spell/aoe/fiery_rebirth cost = 1 diff --git a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm new file mode 100644 index 00000000000..5b42033b8cb --- /dev/null +++ b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm @@ -0,0 +1,252 @@ +/** + * # The path of Cosmos. + * + * Goes as follows: + * + * Eternal Gate + * Grasp of Cosmos + * Cosmic Runes + * > Sidepaths: + * Priest's Ritual + * Scorching Shark + * + * Mark of Cosmos + * Ritual of Knowledge + * Star Touch + * Star Blast + * > Sidepaths: + * Curse of Corrosion + * Space Phase + * + * Cosmic Blade + * Cosmic Expansion + * > Sidepaths: + * Eldritch Coin + * Rusted Ritual + * + * Creators's Gift + */ +/datum/heretic_knowledge/limited_amount/starting/base_cosmic + name = "Eternal Gate" + desc = "Opens up the Path of Cosmos to you. \ + Allows you to transmute a sheet of plasma and a knife into an Cosmic Blade. \ + You can only create two at a time." + gain_text = "It looked at the stars to guide himself." + next_knowledge = list(/datum/heretic_knowledge/cosmic_grasp) + required_atoms = list( + /obj/item/knife = 1, + /obj/item/stack/sheet/mineral/plasma = 1, + ) + result_atoms = list(/obj/item/melee/sickly_blade/cosmic) + route = PATH_COSMIC + +/datum/heretic_knowledge/cosmic_grasp + name = "Grasp of Cosmos" + desc = "Your Mansus Grasp will give people a star mark (cosmic ring) and create a cosmic field where you stand." + gain_text = "The more he looked the more everything made sense. \ + The stars traced out the path forward to his home." + next_knowledge = list(/datum/heretic_knowledge/spell/cosmic_runes) + cost = 1 + route = PATH_COSMIC + +/datum/heretic_knowledge/cosmic_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic) + RegisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK, PROC_REF(on_mansus_grasp)) + +/datum/heretic_knowledge/cosmic_grasp/on_lose(mob/user, datum/antagonist/heretic/our_heretic) + UnregisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK) + +/// Aplies the effect of the mansus grasp when it hits a target. +/datum/heretic_knowledge/cosmic_grasp/proc/on_mansus_grasp(mob/living/source, mob/living/target) + SIGNAL_HANDLER + + to_chat(target, span_danger("A cosmic ring appeared above your head!")) + target.apply_status_effect(/datum/status_effect/star_mark, source) + new /obj/effect/forcefield/cosmic_field(get_turf(source)) + +/datum/heretic_knowledge/spell/cosmic_runes + name = "Cosmic Runes" + desc = "Grants you Cosmic Runes, a spell that creates two runes linked with eachother for easy teleportation. \ + Only the entity activating the rune will get transported, and it can be used by anyone without a star mark. \ + However, people with a star mark will get transported along with another person using the rune." + gain_text = "When day came, the Sleeper got lost. \ + The sun outshone the stars, so he lost his guide." + next_knowledge = list( + /datum/heretic_knowledge/mark/cosmic_mark, + /datum/heretic_knowledge/codex_cicatrix, + /datum/heretic_knowledge/essence, + /datum/heretic_knowledge/summon/fire_shark, + ) + spell_to_add = /datum/action/cooldown/spell/cosmic_rune + cost = 1 + route = PATH_COSMIC + +/datum/heretic_knowledge/mark/cosmic_mark + name = "Mark of Cosmos" + desc = "Your Mansus Grasp now applies the Mark of Cosmos. The mark is triggered from an attack with your Cosmic Blade. \ + When triggered, the victim is returned to the location where the mark was originally applied to them. \ + They will then be paralyzed for 2 seconds." + gain_text = "As the guide was lost he found a new. The energy increased as the gaze he threw. \ + He didn't know, but with focus, the Sleepers energy began to flow." + next_knowledge = list(/datum/heretic_knowledge/knowledge_ritual/cosmic) + route = PATH_COSMIC + mark_type = /datum/status_effect/eldritch/cosmic + +/datum/heretic_knowledge/knowledge_ritual/cosmic + next_knowledge = list(/datum/heretic_knowledge/spell/star_touch) + route = PATH_COSMIC + +/datum/heretic_knowledge/spell/star_touch + name = "Star Touch" + desc = "Grants you Star Touch, a spell which places a star mark upon your target \ + and creates a cosmic field at your feet and to the turfs next to you. Targets which already have a star mark \ + will be forced to sleep for 4 seconds. When the victim is hit it also creates a beam that \ + deals a bit of fire damage and damages the cells. \ + The beam lasts a minute, until the beam is obstructed or until a new target has been found." + gain_text = "He dreamed to know how the matter travelled from star to star. \ + He lost interest in wanting to find out." + next_knowledge = list(/datum/heretic_knowledge/spell/star_blast) + spell_to_add = /datum/action/cooldown/spell/touch/star_touch + cost = 1 + route = PATH_COSMIC + +/datum/heretic_knowledge/spell/star_blast + name = "Star Blast" + desc = "Fires a projectile that moves very slowly and creates cosmic fields on impact. \ + Anyone hit by the projectile will recieve burn damage, a knockdown, and give people in a three tile range a star mark." + gain_text = "He didn't try, yet felt the call of the night's Creator." + next_knowledge = list( + /datum/heretic_knowledge/blade_upgrade/cosmic, + /datum/heretic_knowledge/reroll_targets, + /datum/heretic_knowledge/curse/corrosion, + /datum/heretic_knowledge/spell/space_phase, + ) + spell_to_add = /datum/action/cooldown/spell/pointed/projectile/star_blast + cost = 1 + route = PATH_COSMIC + +/datum/heretic_knowledge/blade_upgrade/cosmic + name = "Cosmic Blade" + desc = "Your blade now deals damage to people's cells through cosmic radiation. \ + Your attacks will chain bonus damage to up to two previous victims. \ + The combo is reset after two seconds without making an attack, \ + or if you attack someone already marked. If you combo more than four attacks you will recieve, \ + a cosmic trail and increase your combo timer up to ten seconds." + gain_text = "As he ascended to be a watcher, he needed to gather knowledge. \ + He started to draw it at his home." + next_knowledge = list(/datum/heretic_knowledge/spell/cosmic_expansion) + route = PATH_COSMIC + /// Storage for the second target. + var/datum/weakref/second_target + /// Storage for the third target. + var/datum/weakref/third_target + /// When this timer completes we reset our combo. + var/combo_timer + /// The duration of the combo. + var/combo_duration = 3 SECONDS + /// The hits we have on a mob with a mind. + var/combo_counter = 0 + +/datum/heretic_knowledge/blade_upgrade/cosmic/do_melee_effects(mob/living/source, mob/living/target, obj/item/melee/sickly_blade/blade) + if(source == target) + return + if(combo_timer) + deltimer(combo_timer) + combo_timer = addtimer(CALLBACK(src, PROC_REF(reset_combo), source), combo_duration, TIMER_STOPPABLE) + var/mob/living/second_target_resolved = second_target?.resolve() + var/mob/living/third_target_resolved = third_target?.resolve() + target.adjustFireLoss(4) + target.adjustCloneLoss(2) + if(target == second_target_resolved || target == third_target_resolved) + reset_combo(source) + return + if(target.mind && target.stat != DEAD) + combo_counter += 1 + if(second_target_resolved) + new /obj/effect/temp_visual/cosmic_explosion(get_turf(second_target_resolved)) + playsound(get_turf(second_target_resolved), 'sound/magic/cosmic_energy.ogg', 25, FALSE) + second_target_resolved.adjustFireLoss(10) + second_target_resolved.adjustCloneLoss(6) + if(third_target_resolved) + new /obj/effect/temp_visual/cosmic_domain(get_turf(third_target_resolved)) + playsound(get_turf(third_target_resolved), 'sound/magic/cosmic_energy.ogg', 50, FALSE) + third_target_resolved.adjustFireLoss(20) + third_target_resolved.adjustCloneLoss(12) + if(combo_counter > 3) + target.apply_status_effect(/datum/status_effect/star_mark, source) + if(target.mind && target.stat != DEAD) + increase_combo_duration() + if(combo_counter == 4) + source.AddElement(/datum/element/effect_trail/cosmic_trail) + third_target = second_target + second_target = WEAKREF(target) + +/// Resets the combo. +/datum/heretic_knowledge/blade_upgrade/cosmic/proc/reset_combo(mob/living/source) + second_target = null + third_target = null + if(combo_counter > 3) + source.RemoveElement(/datum/element/effect_trail/cosmic_trail) + combo_duration = 2 SECONDS + combo_counter = 0 + new /obj/effect/temp_visual/cosmic_cloud(get_turf(source)) + if(combo_timer) + deltimer(combo_timer) + +/// Increases the combo duration. +/datum/heretic_knowledge/blade_upgrade/cosmic/proc/increase_combo_duration() + if(combo_duration < 10 SECONDS) + combo_duration += 0.5 SECONDS + +/datum/heretic_knowledge/spell/cosmic_expansion + name = "Cosmic Expansion" + desc = "Grants you Cosmic Expansion, a spell that creates a 3x3 area of cosmic fields around you. \ + Nearby beings will also receive a star mark." + gain_text = "He was well known, so he had a lot of drawing to do to gather as much of the things he forgot." + next_knowledge = list( + /datum/heretic_knowledge/ultimate/cosmic_final, + /datum/heretic_knowledge/eldritch_coin, + /datum/heretic_knowledge/summon/rusty, + ) + spell_to_add = /datum/action/cooldown/spell/conjure/cosmic_expansion + cost = 1 + route = PATH_COSMIC + +/datum/heretic_knowledge/ultimate/cosmic_final + name = "Creators's Gift" + desc = "The ascension ritual of the Path of Cosmos. \ + Bring 3 corpses with bluespace dust in their body to a transmutation rune to complete the ritual. \ + When completed, you become the owner of a Star Gazer. \ + You will be able to command the Star Gazer with Alt+click. \ + You can also give it commands through speech. \ + The Star Gazer is a strong ally who can even break down reinforced walls. \ + Star Touch can now teleport you to the Star Gazer when activated in your hand." + gain_text = "The past is gone, the Star Gazer became a vessel to watch over the universe. \ + The Creator made this his path and he forgot his purpose. \ + THE TIME IS NOW, WITNESS MY ASCENSION, THE STAR GAZER HAS GAINED PURPOSE ONCE MORE!" + route = PATH_COSMIC + /// A static list of command we can use with our mob. + var/static/list/star_gazer_commands = list( + /datum/pet_command/idle, + /datum/pet_command/free, + /datum/pet_command/follow, + /datum/pet_command/point_targetting/attack/star_gazer + ) + +/datum/heretic_knowledge/ultimate/cosmic_final/is_valid_sacrifice(mob/living/carbon/human/sacrifice) + . = ..() + if(!.) + return FALSE + + return sacrifice.has_reagent(/datum/reagent/bluespace) + +/datum/heretic_knowledge/ultimate/cosmic_final/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc) + . = ..() + priority_announce("[generate_heretic_text()] A Star Gazer has arrived into the station, [user.real_name] has ascended! This station is the domain of the Cosmos! [generate_heretic_text()]","[generate_heretic_text()]", ANNOUNCER_SPANOMALIES) + var/mob/living/basic/star_gazer/star_gazer_mob = new /mob/living/basic/star_gazer(loc) + star_gazer_mob.AddComponent(/datum/component/obeys_commands, star_gazer_commands) + star_gazer_mob.befriend(user) + var/datum/action/cooldown/spell/touch/star_touch/star_touch_spell = locate() in user.actions + if(star_touch_spell) + star_touch_spell.set_star_gazer(star_gazer_mob) + + user.client?.give_award(/datum/award/achievement/misc/cosmic_ascension, user) diff --git a/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm b/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm new file mode 100644 index 00000000000..f602bc94d53 --- /dev/null +++ b/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm @@ -0,0 +1,51 @@ +// Sidepaths for knowledge between Cosmos and Ash. + +/datum/heretic_knowledge/summon/fire_shark + name = "Scorching Shark" + desc = "Allows you to transmute a pool of ash, a pool of salt, and a sheet of plasma into a Fire Shark. \ + Fire Sharks are fast and strong in groups, but die quickly. They are also highly resistant against fire attacks. \ + Fire Sharks inject phlogiston into its victims and spawn plasma once they die." + gain_text = "My knowledge of the universe with the energy of remains, constructed it. It gave the Fire Shark life." + next_knowledge = list( + /datum/heretic_knowledge/spell/cosmic_runes, + /datum/heretic_knowledge/spell/ash_passage, + ) + required_atoms = list( + /obj/effect/decal/cleanable/ash = 1, + /obj/effect/decal/cleanable/food/salt = 1, + /obj/item/stack/sheet/mineral/plasma = 1, + ) + mob_to_summon = /mob/living/basic/fire_shark + cost = 1 + route = PATH_SIDE + +/datum/heretic_knowledge/spell/space_phase + name = "Space Phase" + desc = "Grants you Space Phase, a spell that allows you to move freely through space. \ + You can only phase in and out when you are on a space or misc turf." + gain_text = "You feel like your body can move through space as if you where dust." + next_knowledge = list( + /datum/heretic_knowledge/spell/star_blast, + /datum/heretic_knowledge/mad_mask, + ) + spell_to_add = /datum/action/cooldown/spell/jaunt/space_crawl + cost = 1 + route = PATH_SIDE + +/datum/heretic_knowledge/eldritch_coin + name = "Eldritch Coin" + desc = "Allows you to transmute a sheet of plasma and a diamond to create an Eldritch Coin. \ + The coin will open or close nearby doors when landing on heads and bolt or unbolt nearby doors \ + when landing on tails. If the coin gets inserted into an airlock it emags the door destroying the coin." + gain_text = "It tossed the coin and won its bet, now it gains..." + next_knowledge = list( + /datum/heretic_knowledge/spell/cosmic_expansion, + /datum/heretic_knowledge/spell/flame_birth, + ) + required_atoms = list( + /obj/item/stack/sheet/mineral/diamond = 1, + /obj/item/stack/sheet/mineral/plasma = 1, + ) + result_atoms = list(/obj/item/coin/eldritch) + cost = 1 + route = PATH_SIDE diff --git a/code/modules/antagonists/heretic/knowledge/side_rust_ash.dm b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm similarity index 89% rename from code/modules/antagonists/heretic/knowledge/side_rust_ash.dm rename to code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm index 37f29a6472b..9b714971672 100644 --- a/code/modules/antagonists/heretic/knowledge/side_rust_ash.dm +++ b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm @@ -1,4 +1,4 @@ -// Sidepaths for knowledge between Rust and Ash. +// Sidepaths for knowledge between Rust and Cosmos. /datum/heretic_knowledge/essence name = "Priest's Ritual" @@ -8,7 +8,7 @@ Created by the Priest - the Liquid that both was and is not." next_knowledge = list( /datum/heretic_knowledge/rust_regen, - /datum/heretic_knowledge/spell/ash_passage, + /datum/heretic_knowledge/spell/cosmic_runes, ) required_atoms = list( /obj/structure/reagent_dispensers/watertank = 1, @@ -25,8 +25,8 @@ that a victim has touched or is covered in the victim's blood to empower the curse." gain_text = "The body of humanity is temporary. Their weaknesses cannot be stopped, like iron falling to rust. Show them all." next_knowledge = list( - /datum/heretic_knowledge/mad_mask, /datum/heretic_knowledge/spell/area_conversion, + /datum/heretic_knowledge/spell/star_blast, ) required_atoms = list( /obj/item/wirecutters = 1, @@ -56,10 +56,10 @@ name = "Rusted Ritual" desc = "Allows you to transmute a pool of vomit, a book, and a head into a Rust Walker. \ Rust Walkers excel at spreading rust and are moderately strong in combat." - gain_text = "I combined my principle of hunger with my desire for corruption. The Marshal knew my name, and the Rusted Hills echoed out." + gain_text = "I combined my knowledge of creation with my desire for corruption. The Marshal knew my name, and the Rusted Hills echoed out." next_knowledge = list( /datum/heretic_knowledge/spell/entropic_plume, - /datum/heretic_knowledge/spell/flame_birth, + /datum/heretic_knowledge/spell/cosmic_expansion, ) required_atoms = list( /obj/effect/decal/cleanable/vomit = 1, diff --git a/code/modules/antagonists/heretic/magic/cosmic_expansion.dm b/code/modules/antagonists/heretic/magic/cosmic_expansion.dm new file mode 100644 index 00000000000..7329652b75a --- /dev/null +++ b/code/modules/antagonists/heretic/magic/cosmic_expansion.dm @@ -0,0 +1,32 @@ +/datum/action/cooldown/spell/conjure/cosmic_expansion + name = "Cosmic Expansion" + desc = "This spell generates a 3x3 domain of cosmic fields. \ + Creatures up to 7 tiles away will also recieve a star mark." + background_icon_state = "bg_heretic" + overlay_icon_state = "bg_heretic_border" + button_icon = 'icons/mob/actions/actions_ecult.dmi' + button_icon_state = "cosmic_domain" + + sound = 'sound/magic/cosmic_expansion.ogg' + school = SCHOOL_FORBIDDEN + cooldown_time = 45 SECONDS + + invocation = "C'SM'S 'XP'ND" + invocation_type = INVOCATION_SHOUT + spell_requirements = NONE + + summon_amount = 9 + summon_radius = 1 + summon_type = list(/obj/effect/forcefield/cosmic_field) + /// The range at which people will get marked with a star mark. + var/star_mark_range = 7 + /// Effect for when the spell triggers + var/obj/effect/expansion_effect = /obj/effect/temp_visual/cosmic_domain + +/datum/action/cooldown/spell/conjure/cosmic_expansion/cast(mob/living/cast_on) + new expansion_effect(get_turf(cast_on)) + for(var/mob/living/nearby_mob in range(star_mark_range, cast_on)) + if(cast_on == nearby_mob) + continue + nearby_mob.apply_status_effect(/datum/status_effect/star_mark, cast_on) + return ..() diff --git a/code/modules/antagonists/heretic/magic/cosmic_runes.dm b/code/modules/antagonists/heretic/magic/cosmic_runes.dm new file mode 100644 index 00000000000..3aac48887bb --- /dev/null +++ b/code/modules/antagonists/heretic/magic/cosmic_runes.dm @@ -0,0 +1,139 @@ +/datum/action/cooldown/spell/cosmic_rune + name = "Cosmic Rune" + desc = "Creates a cosmic rune at your position, only two can exist at a time. Invoking one rune transports you to the other." + background_icon_state = "bg_heretic" + overlay_icon_state = "bg_heretic_border" + button_icon = 'icons/mob/actions/actions_ecult.dmi' + button_icon_state = "cosmic_rune" + + sound = 'sound/magic/forcewall.ogg' + school = SCHOOL_FORBIDDEN + cooldown_time = 15 SECONDS + + invocation = "ST'R R'N'" + invocation_type = INVOCATION_WHISPER + spell_requirements = NONE + + /// Storage for the first rune. + var/datum/weakref/first_rune + /// Storage for the second rune. + var/datum/weakref/second_rune + /// Rune removal effect. + var/obj/effect/rune_remove_effect = /obj/effect/temp_visual/cosmic_rune_fade + +/datum/action/cooldown/spell/cosmic_rune/cast(atom/cast_on) + . = ..() + var/obj/effect/cosmic_rune/first_rune_resolved = first_rune?.resolve() + var/obj/effect/cosmic_rune/second_rune_resolved = second_rune?.resolve() + if(first_rune_resolved && second_rune_resolved) + var/obj/effect/cosmic_rune/new_rune = new /obj/effect/cosmic_rune(get_turf(cast_on)) + new rune_remove_effect(get_turf(first_rune_resolved)) + QDEL_NULL(first_rune_resolved) + first_rune = WEAKREF(second_rune_resolved) + second_rune = WEAKREF(new_rune) + second_rune_resolved.link_rune(new_rune) + new_rune.link_rune(second_rune_resolved) + return + if(!first_rune_resolved) + first_rune = make_new_rune(get_turf(cast_on), second_rune_resolved) + return + if(!second_rune_resolved) + second_rune = make_new_rune(get_turf(cast_on), first_rune_resolved) + +/// Returns a weak reference to a new rune, linked to an existing rune if provided +/datum/action/cooldown/spell/cosmic_rune/proc/make_new_rune(turf/target_turf, obj/effect/cosmic_rune/other_rune) + var/obj/effect/cosmic_rune/new_rune = new /obj/effect/cosmic_rune(target_turf) + if(other_rune) + other_rune.link_rune(new_rune) + new_rune.link_rune(other_rune) + return WEAKREF(new_rune) + +/// A rune that allows you to teleport to the location of a linked rune. +/obj/effect/cosmic_rune + name = "cosmic rune" + desc = "A strange rune, that can instantly transport people to another location." + anchored = TRUE + icon = 'icons/obj/hand_of_god_structures.dmi' + icon_state = "cosmic_rune" + resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF + layer = SIGIL_LAYER + /// The other rune this rune is linked with + var/datum/weakref/linked_rune + /// Effect for when someone teleports + var/obj/effect/rune_effect = /obj/effect/temp_visual/rune_light + +/obj/effect/cosmic_rune/attack_paw(mob/living/user, list/modifiers) + return attack_hand(user, modifiers) + +/obj/effect/cosmic_rune/attack_hand(mob/living/user, list/modifiers) + . = ..() + if(.) + return + if(!linked_rune) + balloon_alert(user, "no linked rune!") + fail_invoke() + return + if(!(user in get_turf(src))) + balloon_alert(user, "not close enough!") + fail_invoke() + return + if(user.has_status_effect(/datum/status_effect/star_mark)) + balloon_alert(user, "blocked by star mark!") + fail_invoke() + return + invoke(user) + +/// For invoking the rune +/obj/effect/cosmic_rune/proc/invoke(mob/living/user) + var/obj/effect/cosmic_rune/linked_rune_resolved = linked_rune?.resolve() + new rune_effect(get_turf(src)) + do_teleport( + user, + get_turf(linked_rune_resolved), + no_effects = TRUE, + channel = TELEPORT_CHANNEL_MAGIC, + asoundin = 'sound/magic/cosmic_energy.ogg', + asoundout = 'sound/magic/cosmic_energy.ogg', + ) + for(var/mob/living/person_on_rune in get_turf(src)) + if(person_on_rune.has_status_effect(/datum/status_effect/star_mark)) + do_teleport(person_on_rune, get_turf(linked_rune_resolved), no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC) + new rune_effect(get_turf(linked_rune_resolved)) + +/// For if someone failed to invoke the rune +/obj/effect/cosmic_rune/proc/fail_invoke() + visible_message(span_warning("The rune pulses with a small flash of purple light, then returns to normal.")) + var/oldcolor = rgb(255, 255, 255) + color = rgb(150, 50, 200) + animate(src, color = oldcolor, time = 5) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_atom_colour)), 5) + +/// For linking a new rune +/obj/effect/cosmic_rune/proc/link_rune(datum/weakref/new_rune) + linked_rune = WEAKREF(new_rune) + +/obj/effect/cosmic_rune/Destroy() + var/obj/effect/cosmic_rune/linked_rune_resolved = linked_rune?.resolve() + if(linked_rune_resolved) + linked_rune_resolved.unlink_rune() + return ..() + +/// Used for unlinking the other rune if this rune gets destroyed +/obj/effect/cosmic_rune/proc/unlink_rune() + linked_rune = null + +/obj/effect/temp_visual/cosmic_rune_fade + name = "cosmic rune" + icon = 'icons/obj/hand_of_god_structures.dmi' + icon_state = "cosmic_rune_fade" + layer = SIGIL_LAYER + anchored = TRUE + duration = 5 + +/obj/effect/temp_visual/rune_light + name = "cosmic rune" + icon = 'icons/obj/hand_of_god_structures.dmi' + icon_state = "cosmic_rune_light" + layer = SIGIL_LAYER + anchored = TRUE + duration = 5 diff --git a/code/modules/antagonists/heretic/magic/space_crawl.dm b/code/modules/antagonists/heretic/magic/space_crawl.dm new file mode 100644 index 00000000000..56fafdf86fb --- /dev/null +++ b/code/modules/antagonists/heretic/magic/space_crawl.dm @@ -0,0 +1,121 @@ +/** + * ### Space Crawl + * + * Lets the caster enter and exit tiles of space or misc turfs. + */ +/datum/action/cooldown/spell/jaunt/space_crawl + name = "Space Phase" + desc = "Allows you to phase in and out of existance while in space or misc tiles." + background_icon_state = "bg_heretic" + overlay_icon_state = "bg_heretic_border" + + button_icon = 'icons/mob/actions/actions_ecult.dmi' + button_icon_state = "space_crawl" + + school = SCHOOL_FORBIDDEN + + invocation_type = INVOCATION_NONE + spell_requirements = NONE + +/datum/action/cooldown/spell/jaunt/space_crawl/Grant(mob/grant_to) + . = ..() + RegisterSignal(grant_to, COMSIG_MOVABLE_MOVED, PROC_REF(update_status_on_signal)) + +/datum/action/cooldown/spell/jaunt/space_crawl/Remove(mob/remove_from) + . = ..() + UnregisterSignal(remove_from, COMSIG_MOVABLE_MOVED) + +/datum/action/cooldown/spell/jaunt/space_crawl/can_cast_spell(feedback = TRUE) + . = ..() + if(!.) + return FALSE + if(isspaceturf(get_turf(owner)) || ismiscturf(get_turf(owner))) + return TRUE + if(feedback) + to_chat(owner, span_warning("You must stand on a space or misc turf!")) + return FALSE + +/datum/action/cooldown/spell/jaunt/space_crawl/cast(mob/living/cast_on) + . = ..() + // Should always return something because we checked that in can_cast_spell before arriving here + var/turf/our_turf = get_turf(cast_on) + do_spacecrawl(our_turf, cast_on) + +/** + * Attempts to enter or exit the passed space or misc turf. + * Returns TRUE if we successfully entered or exited said turf, FALSE otherwise + */ +/datum/action/cooldown/spell/jaunt/space_crawl/proc/do_spacecrawl(turf/our_turf, mob/living/jaunter) + if(is_jaunting(jaunter)) + . = try_exit_jaunt(our_turf, jaunter) + else + . = try_enter_jaunt(our_turf, jaunter) + + if(!.) + reset_spell_cooldown() + to_chat(jaunter, span_warning("You are unable to space crawl!")) + +/** + * Attempts to enter the passed space or misc turfs. + */ +/datum/action/cooldown/spell/jaunt/space_crawl/proc/try_enter_jaunt(turf/our_turf, mob/living/jaunter) + // Begin the jaunt + jaunter.notransform = TRUE + var/obj/effect/dummy/phased_mob/holder = enter_jaunt(jaunter, our_turf) + if(!holder) + jaunter.notransform = FALSE + return FALSE + + RegisterSignal(holder, COMSIG_MOVABLE_MOVED, PROC_REF(update_status_on_signal)) + if(iscarbon(jaunter)) + jaunter.drop_all_held_items() + // Give them some space hands to prevent them from doing things + var/obj/item/space_crawl/left_hand = new(jaunter) + var/obj/item/space_crawl/right_hand = new(jaunter) + left_hand.icon_state = "spacehand_right" // Icons swapped intentionally.. + right_hand.icon_state = "spacehand_left" // ..because perspective, or something + jaunter.put_in_hands(left_hand) + jaunter.put_in_hands(right_hand) + + our_turf.visible_message(span_warning("[jaunter] sinks into [our_turf]!")) + playsound(our_turf, 'sound/magic/cosmic_energy.ogg', 50, TRUE, -1) + new /obj/effect/temp_visual/space_explosion(our_turf) + jaunter.extinguish_mob() + + jaunter.notransform = FALSE + return TRUE + +/** + * Attempts to Exit the passed space or misc turf. + */ +/datum/action/cooldown/spell/jaunt/space_crawl/proc/try_exit_jaunt(turf/our_turf, mob/living/jaunter) + if(jaunter.notransform) + to_chat(jaunter, span_warning("You cannot exit yet!!")) + return FALSE + + if(!exit_jaunt(jaunter, our_turf)) + return FALSE + + our_turf.visible_message(span_boldwarning("[jaunter] rises out of [our_turf]!")) + return TRUE + +/datum/action/cooldown/spell/jaunt/space_crawl/on_jaunt_exited(obj/effect/dummy/phased_mob/jaunt, mob/living/unjaunter) + UnregisterSignal(jaunt, COMSIG_MOVABLE_MOVED) + playsound(get_turf(unjaunter), 'sound/magic/cosmic_energy.ogg', 50, TRUE, -1) + new /obj/effect/temp_visual/space_explosion(get_turf(unjaunter)) + if(iscarbon(unjaunter)) + for(var/obj/item/space_crawl/space_hand in unjaunter.held_items) + unjaunter.temporarilyRemoveItemFromInventory(space_hand, force = TRUE) + qdel(space_hand) + return ..() + +/// Spacecrawl "hands", prevent the user from holding items in spacecrawl +/obj/item/space_crawl + name = "space crawl" + desc = "You are unable to hold anything while in this form." + icon = 'icons/obj/eldritch.dmi' + item_flags = ABSTRACT | DROPDEL + +/obj/item/space_crawl/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT) diff --git a/code/modules/antagonists/heretic/magic/star_blast.dm b/code/modules/antagonists/heretic/magic/star_blast.dm new file mode 100644 index 00000000000..236fa313daf --- /dev/null +++ b/code/modules/antagonists/heretic/magic/star_blast.dm @@ -0,0 +1,55 @@ +/datum/action/cooldown/spell/pointed/projectile/star_blast + name = "Star Blast" + desc = "This spell fires a disk with cosmic energies at a target." + background_icon_state = "bg_heretic" + overlay_icon_state = "bg_heretic_border" + button_icon = 'icons/mob/actions/actions_ecult.dmi' + button_icon_state = "star_blast" + + sound = 'sound/magic/cosmic_energy.ogg' + school = SCHOOL_FORBIDDEN + cooldown_time = 20 SECONDS + + invocation = "R'T'T' ST'R!" + invocation_type = INVOCATION_SHOUT + spell_requirements = NONE + + active_msg = "You prepare to cast your star blast!" + deactive_msg = "You stop swirling cosmic energies from the palm of your hand... for now." + cast_range = 12 + projectile_type = /obj/projectile/magic/star_ball + +/obj/projectile/magic/star_ball + name = "star ball" + icon_state = "star_ball" + damage = 20 + damage_type = BURN + speed = 1 + range = 100 + knockdown = 4 SECONDS + pixel_speed_multiplier = 0.2 + /// Effect for when the ball hits something + var/obj/effect/explosion_effect = /obj/effect/temp_visual/cosmic_explosion + /// The range at which people will get marked with a star mark. + var/star_mark_range = 3 + +/obj/projectile/magic/star_ball/Initialize(mapload) + . = ..() + AddElement(/datum/element/effect_trail/cosmic_trail) + +/obj/projectile/magic/star_ball/on_hit(atom/target, blocked = FALSE, pierce_hit) + . = ..() + var/mob/living/cast_on = firer + for(var/mob/living/nearby_mob in range(star_mark_range, target)) + if(cast_on == nearby_mob) + continue + nearby_mob.apply_status_effect(/datum/status_effect/star_mark, cast_on) + +/obj/projectile/magic/star_ball/Destroy() + playsound(get_turf(src), 'sound/magic/cosmic_energy.ogg', 50, FALSE) + for(var/turf/cast_turf as anything in get_turfs()) + new /obj/effect/forcefield/cosmic_field(cast_turf) + return ..() + +/obj/projectile/magic/star_ball/proc/get_turfs() + return list(get_turf(src), pick(get_step(src, NORTH), get_step(src, SOUTH)), pick(get_step(src, EAST), get_step(src, WEST))) diff --git a/code/modules/antagonists/heretic/magic/star_touch.dm b/code/modules/antagonists/heretic/magic/star_touch.dm new file mode 100644 index 00000000000..01dee4e136a --- /dev/null +++ b/code/modules/antagonists/heretic/magic/star_touch.dm @@ -0,0 +1,238 @@ +/datum/action/cooldown/spell/touch/star_touch + name = "Star Touch" + desc = "Marks someone with a star mark or puts someone with a star mark to sleep for 4 seconds, removing the star mark. \ + You and your target are linked with a cosmic ray, burning them for up to a minute, or \ + until they can escape your sight. Star Touch can also remove Cosmic Runes, or teleport you \ + to your Star Gazer when used on yourself." + background_icon_state = "bg_heretic" + overlay_icon_state = "bg_heretic_border" + button_icon = 'icons/mob/actions/actions_ecult.dmi' + button_icon_state = "star_touch" + + sound = 'sound/items/welder.ogg' + school = SCHOOL_FORBIDDEN + cooldown_time = 15 SECONDS + invocation = "ST'R 'N'RG'!" + invocation_type = INVOCATION_SHOUT + spell_requirements = NONE + antimagic_flags = MAGIC_RESISTANCE + + hand_path = /obj/item/melee/touch_attack/star_touch + /// Stores the weakref for the Star Gazer after ascending + var/datum/weakref/star_gazer + +/datum/action/cooldown/spell/touch/star_touch/is_valid_target(atom/cast_on) + if(!isliving(cast_on)) + return FALSE + return TRUE + +/datum/action/cooldown/spell/touch/star_touch/on_antimagic_triggered(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/caster) + victim.visible_message( + span_danger("The spell bounces off of you!"), + ) + +/datum/action/cooldown/spell/touch/star_touch/cast_on_hand_hit(obj/item/melee/touch_attack/hand, mob/living/victim, mob/living/carbon/caster) + if(victim.has_status_effect(/datum/status_effect/star_mark)) + victim.apply_effect(4 SECONDS, effecttype = EFFECT_UNCONSCIOUS) + victim.remove_status_effect(/datum/status_effect/star_mark) + else + victim.apply_status_effect(/datum/status_effect/star_mark, caster) + for(var/turf/cast_turf as anything in get_turfs(victim)) + new /obj/effect/forcefield/cosmic_field(cast_turf) + caster.apply_status_effect(/datum/status_effect/cosmic_beam, victim) + return TRUE + +/datum/action/cooldown/spell/touch/star_touch/proc/get_turfs(mob/living/victim) + return list(get_turf(owner), get_step(owner, turn(owner.dir, 90)), get_step(owner, turn(owner.dir, 270))) + +/// To set the star gazer +/datum/action/cooldown/spell/touch/star_touch/proc/set_star_gazer(mob/living/basic/star_gazer/star_gazer_mob) + star_gazer = WEAKREF(star_gazer_mob) + +/// To obtain the star gazer if there is one +/datum/action/cooldown/spell/touch/star_touch/proc/get_star_gazer() + var/mob/living/basic/star_gazer/star_gazer_resolved = star_gazer?.resolve() + if(star_gazer_resolved) + return star_gazer_resolved + return FALSE + +/obj/item/melee/touch_attack/star_touch + name = "Star Touch" + desc = "A sinister looking aura that distorts the flow of reality around it. \ + Causes people with a star mark to sleep for 4 seconds, and causes people without a star mark to get one." + icon_state = "star" + inhand_icon_state = "star" + +/obj/item/melee/touch_attack/star_touch/Initialize(mapload) + . = ..() + AddComponent(/datum/component/effect_remover, \ + success_feedback = "You remove %THEEFFECT.", \ + tip_text = "Clear rune", \ + on_clear_callback = CALLBACK(src, PROC_REF(after_clear_rune)), \ + effects_we_clear = list(/obj/effect/cosmic_rune)) + +/* + * Callback for effect_remover component. + */ +/obj/item/melee/touch_attack/star_touch/proc/after_clear_rune(obj/effect/target, mob/living/user) + new /obj/effect/temp_visual/cosmic_rune_fade(get_turf(target)) + var/datum/action/cooldown/spell/touch/star_touch/star_touch_spell = spell_which_made_us?.resolve() + star_touch_spell?.spell_feedback() + remove_hand_with_no_refund(user) + +/obj/item/melee/touch_attack/star_touch/ignition_effect(atom/to_light, mob/user) + . = span_notice("[user] effortlessly snaps [user.p_their()] fingers near [to_light], igniting it with cosmic energies. Fucking badass!") + remove_hand_with_no_refund(user) + +/obj/item/melee/touch_attack/star_touch/attack_self(mob/living/user) + var/datum/action/cooldown/spell/touch/star_touch/star_touch_spell = spell_which_made_us?.resolve() + var/mob/living/basic/star_gazer/star_gazer_mob = star_touch_spell?.get_star_gazer() + if(!star_gazer_mob) + balloon_alert(user, "no linked star gazer!") + return ..() + new /obj/effect/temp_visual/cosmic_explosion(get_turf(user)) + do_teleport( + user, + get_turf(star_gazer_mob), + no_effects = TRUE, + channel = TELEPORT_CHANNEL_MAGIC, + asoundin = 'sound/magic/cosmic_energy.ogg', + asoundout = 'sound/magic/cosmic_energy.ogg', + ) + remove_hand_with_no_refund(user) + +/obj/effect/ebeam/cosmic + name = "cosmic beam" + +/datum/status_effect/cosmic_beam + id = "cosmic_beam" + tick_interval = 0.1 SECONDS + duration = 1 MINUTES + status_type = STATUS_EFFECT_REPLACE + alert_type = null + /// Stores the current beam target + var/mob/living/current_target + /// Checks the time of the last check + var/last_check = 0 + /// The delay of when the beam gets checked + var/check_delay = 10 //Check los as often as possible, max resolution is SSobj tick though + /// The maximum range of the beam + var/max_range = 8 + /// Wether the beam is active or not + var/active = FALSE + /// The storage for the beam + var/datum/beam/current_beam = null + +/datum/status_effect/cosmic_beam/on_creation(mob/living/new_owner, mob/living/current_target) + src.current_target = current_target + start_beam(current_target, new_owner) + return ..() + +/datum/status_effect/cosmic_beam/be_replaced() + if(active) + QDEL_NULL(current_beam) + active = FALSE + return ..() + +/datum/status_effect/cosmic_beam/tick(delta_time, times_fired) + if(!current_target) + lose_target() + return + + if(world.time <= last_check+check_delay) + return + + last_check = world.time + + if(!los_check(owner, current_target)) + QDEL_NULL(current_beam)//this will give the target lost message + return + + if(current_target) + on_beam_tick(current_target) + +/** + * Proc that always is called when we want to end the beam and makes sure things are cleaned up, see beam_died() + */ +/datum/status_effect/cosmic_beam/proc/lose_target() + if(active) + QDEL_NULL(current_beam) + active = FALSE + if(current_target) + on_beam_release(current_target) + current_target = null + +/** + * Proc that is only called when the beam fails due to something, so not when manually ended. + * manual disconnection = lose_target, so it can silently end + * automatic disconnection = beam_died, so we can give a warning message first + */ +/datum/status_effect/cosmic_beam/proc/beam_died() + SIGNAL_HANDLER + to_chat(owner, span_warning("You lose control of the beam!")) + lose_target() + duration = 0 + +/// Used for starting the beam when a target has been acquired +/datum/status_effect/cosmic_beam/proc/start_beam(atom/target, mob/living/user) + + if(current_target) + lose_target() + if(!isliving(target)) + return + + current_target = target + active = TRUE + current_beam = user.Beam(current_target, icon_state="cosmic_beam", time = 1 MINUTES, maxdistance = max_range, beam_type = /obj/effect/ebeam/cosmic) + RegisterSignal(current_beam, COMSIG_PARENT_QDELETING, PROC_REF(beam_died)) + + SSblackbox.record_feedback("tally", "gun_fired", 1, type) + if(current_target) + on_beam_hit(current_target) + +/// Checks if the beam is going through an invalid turf +/datum/status_effect/cosmic_beam/proc/los_check(atom/movable/user, mob/target) + var/turf/user_turf = user.loc + if(!istype(user_turf)) + return FALSE + var/obj/dummy = new(user_turf) + dummy.pass_flags |= PASSTABLE|PASSGLASS|PASSGRILLE //Grille/Glass so it can be used through common windows + var/turf/previous_step = user_turf + var/first_step = TRUE + for(var/turf/next_step as anything in (get_line(user_turf, target) - user_turf)) + if(first_step) + for(var/obj/blocker in user_turf) + if(!blocker.density || !(blocker.flags_1 & ON_BORDER_1)) + continue + if(blocker.CanPass(dummy, get_dir(user_turf, next_step))) + continue + return FALSE // Could not leave the first turf. + first_step = FALSE + if(next_step.density) + qdel(dummy) + return FALSE + for(var/atom/movable/movable as anything in next_step) + if(!movable.CanPass(dummy, get_dir(next_step, previous_step))) + qdel(dummy) + return FALSE + previous_step = next_step + qdel(dummy) + return TRUE + +/// What to add when the beam connects to a target +/datum/status_effect/cosmic_beam/proc/on_beam_hit(mob/living/target) + if(!istype(target, /mob/living/basic/star_gazer)) + target.AddElement(/datum/element/effect_trail/cosmic_trail) + return + +/// What to process when the beam is connected to a target +/datum/status_effect/cosmic_beam/proc/on_beam_tick(mob/living/target) + target.adjustFireLoss(3) + target.adjustCloneLoss(1) + return + +/// What to remove when the beam disconnects from a target +/datum/status_effect/cosmic_beam/proc/on_beam_release(mob/living/target) + if(!istype(target, /mob/living/basic/star_gazer)) + target.RemoveElement(/datum/element/effect_trail/cosmic_trail) + return diff --git a/code/modules/antagonists/heretic/status_effects/debuffs.dm b/code/modules/antagonists/heretic/status_effects/debuffs.dm index 4d95f666bf4..6f285dd5ffb 100644 --- a/code/modules/antagonists/heretic/status_effects/debuffs.dm +++ b/code/modules/antagonists/heretic/status_effects/debuffs.dm @@ -137,3 +137,58 @@ human_owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 20, 190) if(95 to 100) human_owner.adjust_confusion_up_to(12 SECONDS, 24 SECONDS) + +/datum/status_effect/star_mark + id = "star_mark" + alert_type = /atom/movable/screen/alert/status_effect/star_mark + duration = 30 SECONDS + status_type = STATUS_EFFECT_REPLACE + ///overlay used to indicate that someone is marked + var/mutable_appearance/cosmic_overlay + /// icon file for the overlay + var/effect_icon = 'icons/effects/eldritch.dmi' + /// icon state for the overlay + var/effect_icon_state = "cosmic_ring" + /// Storage for the spell caster + var/datum/weakref/spell_caster + +/atom/movable/screen/alert/status_effect/star_mark + name = "Star Mark" + desc = "A ring above your head prevents you from entering cosmic fields or teleporting through cosmic runes..." + icon_state = "star_mark" + +/datum/status_effect/star_mark/on_creation(mob/living/new_owner, mob/living/new_spell_caster) + cosmic_overlay = mutable_appearance(effect_icon, effect_icon_state, BELOW_MOB_LAYER) + if(new_spell_caster) + spell_caster = WEAKREF(new_spell_caster) + return ..() + +/datum/status_effect/star_mark/Destroy() + QDEL_NULL(cosmic_overlay) + return ..() + +/datum/status_effect/star_mark/on_apply() + if(istype(owner, /mob/living/basic/star_gazer)) + return FALSE + var/mob/living/spell_caster_resolved = spell_caster?.resolve() + var/datum/antagonist/heretic_monster/monster = owner.mind?.has_antag_datum(/datum/antagonist/heretic_monster) + if(spell_caster_resolved && monster) + if(monster.master?.current == spell_caster_resolved) + return FALSE + RegisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(update_owner_overlay)) + owner.update_appearance(UPDATE_OVERLAYS) + return TRUE + +/// Updates the overlay of the owner +/datum/status_effect/star_mark/proc/update_owner_overlay(atom/source, list/overlays) + SIGNAL_HANDLER + + overlays += cosmic_overlay + +/datum/status_effect/star_mark/on_remove() + UnregisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS) + owner.update_appearance(UPDATE_OVERLAYS) + return ..() + +/datum/status_effect/star_mark/extended + duration = 3 MINUTES diff --git a/code/modules/antagonists/heretic/status_effects/mark_effects.dm b/code/modules/antagonists/heretic/status_effects/mark_effects.dm index b6f1ca5dcd9..a4acd413e40 100644 --- a/code/modules/antagonists/heretic/status_effects/mark_effects.dm +++ b/code/modules/antagonists/heretic/status_effects/mark_effects.dm @@ -219,3 +219,31 @@ source.Stun(1 SECONDS) source.throw_at(further_behind_old_loc, 3, 1, gentle = TRUE) // Keeping this gentle so they don't smack into the heretic max speed + +/datum/status_effect/eldritch/cosmic + effect_icon_state = "emark6" + /// For storing the location when the mark got applied. + var/obj/effect/cosmic_diamond/cosmic_diamond + /// Effect when triggering mark. + var/obj/effect/teleport_effect = /obj/effect/temp_visual/cosmic_cloud + +/datum/status_effect/eldritch/cosmic/on_creation(mob/living/new_owner) + . = ..() + cosmic_diamond = new(get_turf(owner)) + +/datum/status_effect/eldritch/cosmic/Destroy() + QDEL_NULL(cosmic_diamond) + return ..() + +/datum/status_effect/eldritch/cosmic/on_effect() + new teleport_effect(get_turf(owner)) + new /obj/effect/forcefield/cosmic_field(get_turf(owner)) + do_teleport( + owner, + get_turf(cosmic_diamond), + no_effects = TRUE, + channel = TELEPORT_CHANNEL_MAGIC, + ) + new teleport_effect(get_turf(owner)) + owner.Paralyze(2 SECONDS) + return ..() diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 8796e8e913b..1542b8de292 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -351,6 +351,10 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ item_flags = NO_MAT_REDEMPTION //You know, it's kind of a problem that money is worth more extrinsicly than intrinsically in this universe. ///If you do not want this coin to be valued based on its materials and instead set a custom value set this to TRUE and set value to the desired value. var/override_material_worth = FALSE + /// The name of the heads side of the coin + var/heads_name = "heads" + /// If the coin has an action or not + var/has_action = FALSE /obj/item/coin/Initialize(mapload) . = ..() @@ -438,8 +442,19 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ user.visible_message(span_notice("[user] flips [src]. It lands on [coinflip]."), \ span_notice("You flip [src]. It lands on [coinflip]."), \ span_hear("You hear the clattering of loose change.")) + if(has_action) + if(coinflip == heads_name) + heads_action(user) + else + tails_action(user) return TRUE//did the coin flip? useful for suicide_act +/obj/item/coin/proc/heads_action(mob/user) + return + +/obj/item/coin/proc/tails_action(mob/user) + return + /obj/item/coin/gold custom_materials = list(/datum/material/gold = 400) @@ -483,6 +498,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ icon_state = "coin_valid" custom_materials = list(/datum/material/plastic = 400) sideslist = list("valid", "salad") + heads_name = "valid" material_flags = NONE override_material_worth = TRUE @@ -523,6 +539,55 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ /obj/item/coin/adamantine/doubloon name = "doubloon" +/obj/item/coin/eldritch + name = "eldritch coin" + desc = "Everytime it lands it bolts or opens doors, except for you." + icon_state = "coin_heretic" + custom_materials = list(/datum/material/diamond = 1000, /datum/material/plasma = 1000) + sideslist = list("heretic", "blade") + heads_name = "heretic" + has_action = TRUE + material_flags = NONE + /// The range at which airlocks are effected. + var/airlock_range = 5 + +/obj/item/coin/eldritch/heads_action(mob/user) + var/mob/living/living_user = user + if(!IS_HERETIC(user)) + living_user.adjustBruteLoss(5) + return + for(var/obj/machinery/door/airlock/target_airlock in range(airlock_range, user)) + if(target_airlock.density) + target_airlock.open() + continue + target_airlock.close(force_crush = TRUE) + +/obj/item/coin/eldritch/tails_action(mob/user) + var/mob/living/living_user = user + if(!IS_HERETIC(user)) + living_user.adjustFireLoss(5) + return + for(var/obj/machinery/door/airlock/target_airlock in range(airlock_range, user)) + if(target_airlock.locked) + target_airlock.unlock() + continue + target_airlock.lock() + +/obj/item/coin/eldritch/afterattack(atom/target_atom, mob/user, proximity) + . = ..() + if(!proximity) + return + if(!IS_HERETIC(user)) + var/mob/living/living_user = user + living_user.adjustBruteLoss(5) + living_user.adjustFireLoss(5) + return + if(istype(target_atom, /obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/target_airlock = target_atom + to_chat(user, span_warning("You put insert the [src] into the airlock.")) + target_airlock.emag_act(user, src) + qdel(src) + #undef GIBTONITE_QUALITY_HIGH #undef GIBTONITE_QUALITY_LOW #undef GIBTONITE_QUALITY_MEDIUM diff --git a/code/modules/mob/living/basic/heretic/fire_shark.dm b/code/modules/mob/living/basic/heretic/fire_shark.dm new file mode 100644 index 00000000000..9e1dfc1cfe3 --- /dev/null +++ b/code/modules/mob/living/basic/heretic/fire_shark.dm @@ -0,0 +1,55 @@ +/mob/living/basic/fire_shark + name = "fire shark" + desc = "It is a eldritch dwarf space shark, also known as a fire shark." + icon = 'icons/mob/nonhuman-player/eldritch_mobs.dmi' + icon_state = "fire_shark" + icon_living = "fire_shark" + pass_flags = PASSTABLE | PASSMOB + combat_mode = TRUE + mob_biotypes = MOB_ORGANIC | MOB_BEAST + basic_mob_flags = DEL_ON_DEATH + unsuitable_atmos_damage = 0 + unsuitable_cold_damage = 0 + unsuitable_heat_damage = 0 + speed = -0.5 + health = 16 + maxHealth = 16 + melee_damage_lower = 8 + melee_damage_upper = 8 + attack_sound = 'sound/weapons/bite.ogg' + attack_vis_effect = ATTACK_EFFECT_BITE + obj_damage = 0 + attack_verb_continuous = "bites" + attack_verb_simple = "bite" + damage_coeff = list(BRUTE = 1, BURN = 0.2, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) + faction = list(FACTION_HERETIC) + mob_size = MOB_SIZE_TINY + speak_emote = list("screams") + basic_mob_flags = DEL_ON_DEATH + death_message = "implodes into itself." + ai_controller = /datum/ai_controller/basic_controller/fire_shark + +/mob/living/basic/fire_shark/Initialize(mapload) + . = ..() + AddElement(/datum/element/death_drops, list(/obj/effect/gibspawner/human)) + AddElement(/datum/element/death_gases, /datum/gas/plasma, 40) + AddElement(/datum/element/simple_flying) + AddElement(/datum/element/venomous, /datum/reagent/phlogiston, 5) + AddComponent(/datum/component/swarming) + AddComponent(/datum/component/regenerator, regeneration_delay = 2 SECONDS, outline_colour = COLOR_DARK_RED) + ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) + +/datum/ai_controller/basic_controller/fire_shark + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic(), + ) + + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree/average_speed, + ) diff --git a/code/modules/mob/living/basic/heretic/star_gazer.dm b/code/modules/mob/living/basic/heretic/star_gazer.dm new file mode 100644 index 00000000000..195e8773a98 --- /dev/null +++ b/code/modules/mob/living/basic/heretic/star_gazer.dm @@ -0,0 +1,118 @@ +/mob/living/basic/star_gazer + name = "Star Gazer" + desc = "A creature that has been tasked to watch over the stars." + icon = 'icons/mob/nonhuman-player/96x96eldritch_mobs.dmi' + icon_state = "star_gazer" + icon_living = "star_gazer" + pixel_x = -32 + base_pixel_x = -32 + basic_mob_flags = DEL_ON_DEATH + mob_biotypes = MOB_HUMANOID + faction = list(FACTION_HERETIC) + response_help_continuous = "passes through" + response_help_simple = "pass through" + speed = -0.2 + maxHealth = 3500 + health = 3500 + + obj_damage = 400 + melee_damage_lower = 35 + melee_damage_upper = 35 + combat_mode = TRUE + sentience_type = SENTIENCE_BOSS + attack_verb_continuous = "ravages" + attack_verb_simple = "ravage" + attack_vis_effect = ATTACK_EFFECT_SLASH + attack_sound = 'sound/weapons/bladeslice.ogg' + speak_emote = list("growls") + damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) + death_sound = 'sound/magic/cosmic_expansion.ogg' + + unsuitable_atmos_damage = 0 + unsuitable_cold_damage = 0 + unsuitable_heat_damage = 0 + + gold_core_spawnable = NO_SPAWN + slowed_by_drag = FALSE + move_force = MOVE_FORCE_OVERPOWERING + move_resist = MOVE_FORCE_OVERPOWERING + pull_force = MOVE_FORCE_OVERPOWERING + mob_size = MOB_SIZE_HUGE + layer = LARGE_MOB_LAYER + plane = GAME_PLANE_UPPER_FOV_HIDDEN + flags_1 = PREVENT_CONTENTS_EXPLOSION_1 + + ai_controller = /datum/ai_controller/basic_controller/star_gazer + +/mob/living/basic/star_gazer/Initialize(mapload) + . = ..() + AddElement(/datum/element/death_drops, list(/obj/effect/temp_visual/cosmic_domain)) + AddElement(/datum/element/footstep, FOOTSTEP_MOB_SHOE) + AddElement(/datum/element/wall_smasher, ENVIRONMENT_SMASH_RWALLS) + AddElement(/datum/element/simple_flying) + AddElement(/datum/element/effect_trail/cosmic_trail) + ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_LAVA_IMMUNE, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_ASHSTORM_IMMUNE, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_NO_TELEPORT, MEGAFAUNA_TRAIT) + ADD_TRAIT(src, TRAIT_MARTIAL_ARTS_IMMUNE, MEGAFAUNA_TRAIT) + ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT) + set_light(4, l_color = "#dcaa5b") + +/datum/ai_controller/basic_controller/star_gazer + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/star_gazer(), + BB_PET_TARGETTING_DATUM = new /datum/targetting_datum/not_friends/attack_closed_turfs(), + ) + + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + planning_subtrees = list( + /datum/ai_planning_subtree/attack_obstacle_in_path/pet_target/star_gazer, + /datum/ai_planning_subtree/pet_planning, + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/attack_obstacle_in_path/star_gazer, + /datum/ai_planning_subtree/basic_melee_attack_subtree/star_gazer, + ) + +/datum/targetting_datum/basic/star_gazer + stat_attack = HARD_CRIT + +/datum/ai_planning_subtree/basic_melee_attack_subtree/star_gazer + melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/star_gazer + +/datum/ai_behavior/basic_melee_attack/star_gazer + action_cooldown = 0.6 SECONDS + +/datum/ai_behavior/basic_melee_attack/star_gazer/perform(delta_time, datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key) + . = ..() + var/datum/weakref/weak_target = controller.blackboard[target_key] + var/atom/target = weak_target?.resolve() + var/mob/living/living_pawn = controller.pawn + + if(!isliving(target)) + return + var/mob/living/living_target = target + living_target.apply_status_effect(/datum/status_effect/star_mark) + living_target.apply_damage_type(damage = 5, damagetype = CLONE) + if(living_target.pulledby != living_pawn) + if(living_pawn.Adjacent(living_target) && isturf(living_target.loc) && living_target.stat == SOFT_CRIT) + living_target.grabbedby(living_pawn) + +/datum/ai_planning_subtree/attack_obstacle_in_path/star_gazer + attack_behaviour = /datum/ai_behavior/attack_obstructions/star_gazer + +/datum/ai_planning_subtree/attack_obstacle_in_path/pet_target/star_gazer + attack_behaviour = /datum/ai_behavior/attack_obstructions/star_gazer + +/datum/ai_behavior/attack_obstructions/star_gazer + action_cooldown = 0.4 SECONDS + can_attack_turfs = TRUE + can_attack_dense_objects = TRUE + +/datum/pet_command/point_targetting/attack/star_gazer + speech_commands = list("attack", "sic", "kill", "slash them") + command_feedback = "stares!" + pointed_reaction = "stares intensely!" + refuse_reaction = "..." + attack_behaviour = /datum/ai_behavior/basic_melee_attack/star_gazer diff --git a/icons/effects/160x160.dmi b/icons/effects/160x160.dmi index be2fb69ca7a..7a9d9041162 100644 Binary files a/icons/effects/160x160.dmi and b/icons/effects/160x160.dmi differ diff --git a/icons/effects/64x64.dmi b/icons/effects/64x64.dmi index 83ce324c4f1..fab69031841 100644 Binary files a/icons/effects/64x64.dmi and b/icons/effects/64x64.dmi differ diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi index b050a6dfd59..c7d3d8951e2 100644 Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ diff --git a/icons/effects/eldritch.dmi b/icons/effects/eldritch.dmi index 87f691cd49f..3d4ee7dd71d 100644 Binary files a/icons/effects/eldritch.dmi and b/icons/effects/eldritch.dmi differ diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi index 9b9b5dc66a8..24fa2193e2b 100755 Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ diff --git a/icons/mob/actions/actions_ecult.dmi b/icons/mob/actions/actions_ecult.dmi index a5890f65d50..f3c091eb3a1 100644 Binary files a/icons/mob/actions/actions_ecult.dmi and b/icons/mob/actions/actions_ecult.dmi differ diff --git a/icons/mob/inhands/64x64_lefthand.dmi b/icons/mob/inhands/64x64_lefthand.dmi index 2bed6d7e0e1..61d8948a98f 100644 Binary files a/icons/mob/inhands/64x64_lefthand.dmi and b/icons/mob/inhands/64x64_lefthand.dmi differ diff --git a/icons/mob/inhands/64x64_righthand.dmi b/icons/mob/inhands/64x64_righthand.dmi index 890beeb4479..9d3f6e679d0 100644 Binary files a/icons/mob/inhands/64x64_righthand.dmi and b/icons/mob/inhands/64x64_righthand.dmi differ diff --git a/icons/mob/inhands/items/touchspell_lefthand.dmi b/icons/mob/inhands/items/touchspell_lefthand.dmi index 12b3c22fa8a..1fc8d962aec 100644 Binary files a/icons/mob/inhands/items/touchspell_lefthand.dmi and b/icons/mob/inhands/items/touchspell_lefthand.dmi differ diff --git a/icons/mob/inhands/items/touchspell_righthand.dmi b/icons/mob/inhands/items/touchspell_righthand.dmi index 2b9e5a67c1a..cc3adf5eb10 100644 Binary files a/icons/mob/inhands/items/touchspell_righthand.dmi and b/icons/mob/inhands/items/touchspell_righthand.dmi differ diff --git a/icons/mob/nonhuman-player/96x96eldritch_mobs.dmi b/icons/mob/nonhuman-player/96x96eldritch_mobs.dmi new file mode 100644 index 00000000000..c20779ff246 Binary files /dev/null and b/icons/mob/nonhuman-player/96x96eldritch_mobs.dmi differ diff --git a/icons/mob/nonhuman-player/eldritch_mobs.dmi b/icons/mob/nonhuman-player/eldritch_mobs.dmi index 8a16d53f885..4e640694b2a 100644 Binary files a/icons/mob/nonhuman-player/eldritch_mobs.dmi and b/icons/mob/nonhuman-player/eldritch_mobs.dmi differ diff --git a/icons/obj/economy.dmi b/icons/obj/economy.dmi index 40b043aba7f..dc90265b6e9 100644 Binary files a/icons/obj/economy.dmi and b/icons/obj/economy.dmi differ diff --git a/icons/obj/eldritch.dmi b/icons/obj/eldritch.dmi index cf631c2b195..f3049a088cc 100644 Binary files a/icons/obj/eldritch.dmi and b/icons/obj/eldritch.dmi differ diff --git a/icons/obj/hand_of_god_structures.dmi b/icons/obj/hand_of_god_structures.dmi index 3d712eab1dd..cbbc36d7718 100644 Binary files a/icons/obj/hand_of_god_structures.dmi and b/icons/obj/hand_of_god_structures.dmi differ diff --git a/icons/obj/weapons/guns/projectiles.dmi b/icons/obj/weapons/guns/projectiles.dmi index be5899b3109..7d065335fae 100644 Binary files a/icons/obj/weapons/guns/projectiles.dmi and b/icons/obj/weapons/guns/projectiles.dmi differ diff --git a/icons/obj/weapons/hand.dmi b/icons/obj/weapons/hand.dmi index 5b165b60855..c5dd384575d 100644 Binary files a/icons/obj/weapons/hand.dmi and b/icons/obj/weapons/hand.dmi differ diff --git a/icons/ui_icons/achievements/achievements.dmi b/icons/ui_icons/achievements/achievements.dmi index 9e8b97f3e28..29a85c0b3d2 100644 Binary files a/icons/ui_icons/achievements/achievements.dmi and b/icons/ui_icons/achievements/achievements.dmi differ diff --git a/sound/magic/cosmic_energy.ogg b/sound/magic/cosmic_energy.ogg new file mode 100644 index 00000000000..9bb70bd0707 Binary files /dev/null and b/sound/magic/cosmic_energy.ogg differ diff --git a/sound/magic/cosmic_expansion.ogg b/sound/magic/cosmic_expansion.ogg new file mode 100644 index 00000000000..a63c2a63707 Binary files /dev/null and b/sound/magic/cosmic_expansion.ogg differ diff --git a/tgstation.dme b/tgstation.dme index dab74ac0f93..c0c154f18ac 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1153,6 +1153,7 @@ #include "code\datums\elements\curse_announcement.dm" #include "code\datums\elements\cursed.dm" #include "code\datums\elements\death_drops.dm" +#include "code\datums\elements\death_gases.dm" #include "code\datums\elements\delete_on_drop.dm" #include "code\datums\elements\deliver_first.dm" #include "code\datums\elements\diggable.dm" @@ -1161,6 +1162,7 @@ #include "code\datums\elements\dryable.dm" #include "code\datums\elements\earhealing.dm" #include "code\datums\elements\easily_fragmented.dm" +#include "code\datums\elements\effect_trail.dm" #include "code\datums\elements\embed.dm" #include "code\datums\elements\empprotection.dm" #include "code\datums\elements\eyestab.dm" @@ -2513,6 +2515,7 @@ #include "code\modules\antagonists\fugitive\fugitive_ship.dm" #include "code\modules\antagonists\fugitive\hunter.dm" #include "code\modules\antagonists\greentext\greentext.dm" +#include "code\modules\antagonists\heretic\cosmic_effect.dm" #include "code\modules\antagonists\heretic\heretic_antag.dm" #include "code\modules\antagonists\heretic\heretic_focus.dm" #include "code\modules\antagonists\heretic\heretic_knowledge.dm" @@ -2531,13 +2534,15 @@ #include "code\modules\antagonists\heretic\items\madness_mask.dm" #include "code\modules\antagonists\heretic\knowledge\ash_lore.dm" #include "code\modules\antagonists\heretic\knowledge\blade_lore.dm" +#include "code\modules\antagonists\heretic\knowledge\cosmic_lore.dm" #include "code\modules\antagonists\heretic\knowledge\flesh_lore.dm" #include "code\modules\antagonists\heretic\knowledge\general_side.dm" #include "code\modules\antagonists\heretic\knowledge\rust_lore.dm" #include "code\modules\antagonists\heretic\knowledge\side_ash_flesh.dm" #include "code\modules\antagonists\heretic\knowledge\side_blade_rust.dm" +#include "code\modules\antagonists\heretic\knowledge\side_cosmos_ash.dm" #include "code\modules\antagonists\heretic\knowledge\side_flesh_void.dm" -#include "code\modules\antagonists\heretic\knowledge\side_rust_ash.dm" +#include "code\modules\antagonists\heretic\knowledge\side_rust_cosmos.dm" #include "code\modules\antagonists\heretic\knowledge\side_void_blade.dm" #include "code\modules\antagonists\heretic\knowledge\starting_lore.dm" #include "code\modules\antagonists\heretic\knowledge\void_lore.dm" @@ -2550,6 +2555,8 @@ #include "code\modules\antagonists\heretic\magic\ash_jaunt.dm" #include "code\modules\antagonists\heretic\magic\blood_cleave.dm" #include "code\modules\antagonists\heretic\magic\blood_siphon.dm" +#include "code\modules\antagonists\heretic\magic\cosmic_expansion.dm" +#include "code\modules\antagonists\heretic\magic\cosmic_runes.dm" #include "code\modules\antagonists\heretic\magic\eldritch_blind.dm" #include "code\modules\antagonists\heretic\magic\eldritch_emplosion.dm" #include "code\modules\antagonists\heretic\magic\eldritch_shapeshift.dm" @@ -2568,6 +2575,9 @@ #include "code\modules\antagonists\heretic\magic\rust_construction.dm" #include "code\modules\antagonists\heretic\magic\rust_wave.dm" #include "code\modules\antagonists\heretic\magic\shadow_cloak.dm" +#include "code\modules\antagonists\heretic\magic\space_crawl.dm" +#include "code\modules\antagonists\heretic\magic\star_blast.dm" +#include "code\modules\antagonists\heretic\magic\star_touch.dm" #include "code\modules\antagonists\heretic\magic\void_cold_cone.dm" #include "code\modules\antagonists\heretic\magic\void_phase.dm" #include "code\modules\antagonists\heretic\magic\void_pull.dm" @@ -3844,6 +3854,8 @@ #include "code\modules\mob\living\basic\farm_animals\cow\cow_ai.dm" #include "code\modules\mob\living\basic\farm_animals\cow\cow_moonicorn.dm" #include "code\modules\mob\living\basic\farm_animals\cow\cow_wisdom.dm" +#include "code\modules\mob\living\basic\heretic\fire_shark.dm" +#include "code\modules\mob\living\basic\heretic\star_gazer.dm" #include "code\modules\mob\living\basic\lavaland\mining.dm" #include "code\modules\mob\living\basic\lavaland\bileworm\_bileworm.dm" #include "code\modules\mob\living\basic\lavaland\bileworm\bileworm_actions.dm"