mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 21:18:37 +01:00
Breaks up mining loot into multiple files, refactors the hierophant club (#89751)
Splits up tendril_loot.dm and megafauna_loot.dm (both over a thousand lines long) into group/item specific files, and megafauna-specific files respectively. Also pulls a few loot items scattered around the codebase into those files as well. Additionally while doing so I've cleaned up the code for some of them, and rewrote the hierophant club's code because it was abominable and a potential source of harddels. Its absolutely unnavigateable. 🆑 code: Split and cleaned up mining loot's code refactor: Refactored the hierophant club. fix: Hierophant club's beacon no longer can go missing (until you try using it) /🆑
This commit is contained in:
@@ -218,127 +218,6 @@
|
||||
. = ..()
|
||||
AddComponent(/datum/component/armor_plate, upgrade_item = /obj/item/clothing/accessory/talisman)
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/drake
|
||||
name = "drake armour"
|
||||
icon_state = "dragon"
|
||||
desc = "A suit of armour fashioned from the remains of an ash drake."
|
||||
armor_type = /datum/armor/cloak_drake
|
||||
hoodtype = /obj/item/clothing/head/hooded/cloakhood/drake
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
transparent_protection = HIDEGLOVES|HIDESUITSTORAGE|HIDEJUMPSUIT|HIDESHOES
|
||||
|
||||
/datum/armor/cloak_drake
|
||||
melee = 65
|
||||
bullet = 15
|
||||
laser = 40
|
||||
energy = 40
|
||||
bomb = 70
|
||||
bio = 60
|
||||
fire = 100
|
||||
acid = 100
|
||||
wound = 10
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/drake/Initialize(mapload)
|
||||
. = ..()
|
||||
allowed = GLOB.mining_suit_allowed
|
||||
|
||||
/obj/item/clothing/head/hooded/cloakhood/drake
|
||||
name = "drake helm"
|
||||
icon = 'icons/obj/clothing/head/helmet.dmi'
|
||||
worn_icon = 'icons/mob/clothing/head/helmet.dmi'
|
||||
icon_state = "dragon"
|
||||
desc = "The skull of a dragon."
|
||||
armor_type = /datum/armor/cloak_drake
|
||||
clothing_flags = SNUG_FIT
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer
|
||||
name = "godslayer armour"
|
||||
icon_state = "godslayer"
|
||||
desc = "A suit of armour fashioned from the remnants of a knight's armor, and parts of a wendigo."
|
||||
armor_type = /datum/armor/cloak_godslayer
|
||||
clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL
|
||||
hoodtype = /obj/item/clothing/head/hooded/cloakhood/godslayer
|
||||
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF | FREEZE_PROOF
|
||||
transparent_protection = HIDEGLOVES|HIDESUITSTORAGE|HIDEJUMPSUIT|HIDESHOES
|
||||
/// Amount to heal when the effect is triggered
|
||||
var/heal_amount = 500
|
||||
/// Time until the effect can take place again
|
||||
var/effect_cooldown_time = 10 MINUTES
|
||||
/// Current cooldown for the effect
|
||||
COOLDOWN_DECLARE(effect_cooldown)
|
||||
var/static/list/damage_heal_order = list(BRUTE, BURN, OXY)
|
||||
|
||||
/datum/armor/cloak_godslayer
|
||||
melee = 50
|
||||
bullet = 25
|
||||
laser = 25
|
||||
energy = 25
|
||||
bomb = 50
|
||||
bio = 50
|
||||
fire = 100
|
||||
acid = 100
|
||||
wound = 10
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer/Initialize(mapload)
|
||||
. = ..()
|
||||
allowed = GLOB.mining_suit_allowed
|
||||
AddComponent(/datum/component/item_equipped_movement_rustle, SFX_PLATE_ARMOR_RUSTLE, 8)
|
||||
|
||||
/obj/item/clothing/head/hooded/cloakhood/godslayer
|
||||
name = "godslayer helm"
|
||||
icon = 'icons/obj/clothing/head/helmet.dmi'
|
||||
worn_icon = 'icons/mob/clothing/head/helmet.dmi'
|
||||
icon_state = "godslayer"
|
||||
desc = "The horns and skull of a wendigo, held together by the remaining icy energy of a demonic miner."
|
||||
armor_type = /datum/armor/cloak_godslayer
|
||||
clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL | SNUG_FIT
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
flash_protect = FLASH_PROTECTION_WELDER
|
||||
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF | FREEZE_PROOF
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer/examine(mob/user)
|
||||
. = ..()
|
||||
if(loc == user && !COOLDOWN_FINISHED(src, effect_cooldown))
|
||||
. += "You feel like the revival effect will be able to occur again in [COOLDOWN_TIMELEFT(src, effect_cooldown) / 10] seconds."
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(slot & ITEM_SLOT_OCLOTHING)
|
||||
RegisterSignal(user, COMSIG_MOB_STATCHANGE, PROC_REF(resurrect))
|
||||
return
|
||||
UnregisterSignal(user, COMSIG_MOB_STATCHANGE)
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer/dropped(mob/user)
|
||||
..()
|
||||
UnregisterSignal(user, COMSIG_MOB_STATCHANGE)
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer/proc/resurrect(mob/living/carbon/user, new_stat)
|
||||
SIGNAL_HANDLER
|
||||
if(new_stat > CONSCIOUS && new_stat < DEAD && COOLDOWN_FINISHED(src, effect_cooldown))
|
||||
COOLDOWN_START(src, effect_cooldown, effect_cooldown_time) //This needs to happen first, otherwise there's an infinite loop
|
||||
user.heal_ordered_damage(heal_amount, damage_heal_order)
|
||||
user.visible_message(span_notice("[user] suddenly revives, as their armor swirls with demonic energy!"), span_notice("You suddenly feel invigorated!"))
|
||||
playsound(user.loc, 'sound/effects/magic/clockwork/ratvar_attack.ogg', 50)
|
||||
|
||||
/obj/item/clothing/suit/hooded/explorer/syndicate
|
||||
name = "syndicate explorer suit"
|
||||
desc = "An armoured suit for exploring harsh environments, dyed in the sinister red and black of the Syndicate. This one seems better armored than the ones Nanotrasen gives out."
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,151 @@
|
||||
#define MAX_BERSERK_CHARGE 100
|
||||
#define PROJECTILE_HIT_MULTIPLIER 1.5
|
||||
#define DAMAGE_TO_CHARGE_SCALE 0.75
|
||||
#define CHARGE_DRAINED_PER_SECOND 5
|
||||
#define BERSERK_ATTACK_SPEED_MODIFIER 0.25
|
||||
|
||||
/obj/item/clothing/suit/hooded/berserker
|
||||
name = "berserker armor"
|
||||
desc = "This hulking armor seems to possess some kind of dark force within; howling in rage, hungry for carnage. \
|
||||
The self-sealing stem bolts that allowed this suit to be spaceworthy have long since corroded. However, the entity \
|
||||
sealed within the suit seems to hunger for the fleeting lifeforce found in the remains left in the remains of drakes. \
|
||||
Feeding it drake remains seems to empower a suit piece, though turns the remains back to lifeless ash."
|
||||
icon_state = "berserker"
|
||||
icon = 'icons/obj/clothing/suits/armor.dmi'
|
||||
worn_icon = 'icons/mob/clothing/suits/armor.dmi'
|
||||
worn_icon_digi = 'modular_skyrat/master_files/icons/mob/clothing/suit_digi.dmi' // SKYRAT EDIT - DIGIS
|
||||
hoodtype = /obj/item/clothing/head/hooded/berserker
|
||||
armor_type = /datum/armor/hooded_berserker
|
||||
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
clothing_flags = THICKMATERIAL|HEADINTERNALS
|
||||
|
||||
/datum/armor/hooded_berserker
|
||||
melee = 30
|
||||
bullet = 30
|
||||
laser = 10
|
||||
energy = 20
|
||||
bomb = 50
|
||||
bio = 60
|
||||
fire = 100
|
||||
acid = 100
|
||||
wound = 10
|
||||
|
||||
/datum/armor/drake_empowerment
|
||||
melee = 35
|
||||
laser = 30
|
||||
energy = 20
|
||||
bomb = 20
|
||||
|
||||
/obj/item/clothing/suit/hooded/berserker/Initialize(mapload)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/anti_magic, ALL, inventory_flags = ITEM_SLOT_OCLOTHING)
|
||||
AddComponent(/datum/component/armor_plate, maxamount = 1, upgrade_item = /obj/item/drake_remains, armor_mod = /datum/armor/drake_empowerment, upgrade_prefix = "empowered")
|
||||
allowed = GLOB.mining_suit_allowed
|
||||
|
||||
/obj/item/clothing/head/hooded/berserker
|
||||
name = "berserker helmet"
|
||||
desc = "This burdensome helmet seems to possess some kind of dark force within; howling in rage, hungry for carnage. \
|
||||
The self-sealing stem bolts that allowed this helmet to be spaceworthy have long since corroded. However, the entity \
|
||||
sealed within the suit seems to hunger for the fleeting lifeforce found in the remains left in the remains of drakes. \
|
||||
Feeding it drake remains seems to empower a suit piece, though turns the remains back to lifeless ash."
|
||||
icon_state = "berserker"
|
||||
icon = 'icons/obj/clothing/head/helmet.dmi'
|
||||
worn_icon = 'icons/mob/clothing/head/helmet.dmi'
|
||||
armor_type = /datum/armor/hooded_berserker
|
||||
actions_types = list(/datum/action/item_action/berserk_mode)
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS|HIDESNOUT
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
clothing_flags = SNUG_FIT|THICKMATERIAL
|
||||
/// Current charge of berserk, goes from 0 to 100
|
||||
var/berserk_charge = 0
|
||||
/// Status of berserk
|
||||
var/berserk_active = FALSE
|
||||
|
||||
/obj/item/clothing/head/hooded/berserker/Initialize(mapload)
|
||||
. = ..()
|
||||
ADD_TRAIT(src, TRAIT_NODROP, LOCKED_HELMET_TRAIT)
|
||||
AddComponent(/datum/component/armor_plate, maxamount = 1, upgrade_item = /obj/item/drake_remains, armor_mod = /datum/armor/drake_empowerment, upgrade_prefix = "empowered")
|
||||
AddComponent(/datum/component/item_equipped_movement_rustle, SFX_PLATE_ARMOR_RUSTLE, 8)
|
||||
|
||||
/obj/item/clothing/head/hooded/berserker/examine()
|
||||
. = ..()
|
||||
. += span_notice("Berserk mode is [berserk_charge]% charged.")
|
||||
|
||||
/obj/item/clothing/head/hooded/berserker/process(seconds_per_tick)
|
||||
if(berserk_active)
|
||||
berserk_charge = clamp(berserk_charge - CHARGE_DRAINED_PER_SECOND * seconds_per_tick, 0, MAX_BERSERK_CHARGE)
|
||||
|
||||
if(!berserk_charge)
|
||||
if(ishuman(loc))
|
||||
end_berserk(loc)
|
||||
|
||||
/obj/item/clothing/head/hooded/berserker/dropped(mob/user)
|
||||
. = ..()
|
||||
end_berserk(user)
|
||||
|
||||
/obj/item/clothing/head/hooded/berserker/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
|
||||
if(berserk_active)
|
||||
return
|
||||
var/berserk_value = damage * DAMAGE_TO_CHARGE_SCALE
|
||||
if(attack_type == PROJECTILE_ATTACK)
|
||||
berserk_value *= PROJECTILE_HIT_MULTIPLIER
|
||||
berserk_charge = clamp(round(berserk_charge + berserk_value), 0, MAX_BERSERK_CHARGE)
|
||||
if(berserk_charge >= MAX_BERSERK_CHARGE)
|
||||
var/datum/action/item_action/berserk_mode/ragemode = locate() in actions
|
||||
to_chat(owner, span_cult_large("Berserk mode is fully charged."))
|
||||
balloon_alert(owner, "berserk charged")
|
||||
ragemode?.build_all_button_icons(UPDATE_BUTTON_STATUS)
|
||||
|
||||
/obj/item/clothing/head/hooded/berserker/IsReflect()
|
||||
return berserk_active
|
||||
|
||||
/// Starts berserk, reducing incoming brute by 50%, doubled attacking speed, NOGUNS trait, adding a color and giving them the berserk movespeed modifier
|
||||
/obj/item/clothing/head/hooded/berserker/proc/berserk_mode(mob/living/carbon/human/user)
|
||||
var/datum/action/item_action/berserk_mode/ragemode = locate() in actions
|
||||
to_chat(user, span_cult("You enter berserk mode."))
|
||||
playsound(user, 'sound/effects/magic/staff_healing.ogg', 50)
|
||||
user.add_movespeed_modifier(/datum/movespeed_modifier/berserk)
|
||||
user.physiology.brute_mod *= 0.5
|
||||
user.next_move_modifier *= BERSERK_ATTACK_SPEED_MODIFIER
|
||||
user.add_atom_colour(COLOR_BUBBLEGUM_RED, TEMPORARY_COLOUR_PRIORITY)
|
||||
user.add_traits(list(TRAIT_NOGUNS, TRAIT_TOSS_GUN_HARD), BERSERK_TRAIT)
|
||||
ADD_TRAIT(src, TRAIT_NODROP, BERSERK_TRAIT)
|
||||
berserk_active = TRUE
|
||||
START_PROCESSING(SSobj, src)
|
||||
ragemode?.build_all_button_icons(UPDATE_BUTTON_STATUS)
|
||||
|
||||
/// Ends berserk, reverting the changes from the proc [berserk_mode]
|
||||
/obj/item/clothing/head/hooded/berserker/proc/end_berserk(mob/living/carbon/human/user)
|
||||
if(!berserk_active)
|
||||
return
|
||||
berserk_active = FALSE
|
||||
if(QDELETED(user))
|
||||
return
|
||||
var/datum/action/item_action/berserk_mode/ragemode = locate() in actions
|
||||
ragemode?.build_all_button_icons(UPDATE_BUTTON_STATUS)
|
||||
to_chat(user, span_cult("You exit berserk mode."))
|
||||
playsound(user, 'sound/effects/magic/summonitems_generic.ogg', 50)
|
||||
user.remove_movespeed_modifier(/datum/movespeed_modifier/berserk)
|
||||
user.physiology.brute_mod *= 2
|
||||
user.next_move_modifier /= BERSERK_ATTACK_SPEED_MODIFIER
|
||||
user.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, COLOR_BUBBLEGUM_RED)
|
||||
user.remove_traits(list(TRAIT_NOGUNS, TRAIT_TOSS_GUN_HARD), BERSERK_TRAIT)
|
||||
REMOVE_TRAIT(src, TRAIT_NODROP, BERSERK_TRAIT)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
#undef MAX_BERSERK_CHARGE
|
||||
#undef PROJECTILE_HIT_MULTIPLIER
|
||||
#undef DAMAGE_TO_CHARGE_SCALE
|
||||
#undef CHARGE_DRAINED_PER_SECOND
|
||||
#undef BERSERK_ATTACK_SPEED_MODIFIER
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
// Memento Mori
|
||||
/obj/item/clothing/neck/necklace/memento_mori
|
||||
name = "Memento Mori"
|
||||
desc = "A mysterious pendant. An inscription on it says: \"Certain death tomorrow means certain life today.\""
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "memento_mori"
|
||||
worn_icon_state = "memento"
|
||||
actions_types = list(/datum/action/item_action/hands_free/memento_mori)
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
var/mob/living/carbon/human/active_owner
|
||||
|
||||
/obj/item/clothing/neck/necklace/memento_mori/item_action_slot_check(slot)
|
||||
return (slot & ITEM_SLOT_NECK)
|
||||
|
||||
/obj/item/clothing/neck/necklace/memento_mori/dropped(mob/user)
|
||||
. = ..()
|
||||
if(active_owner)
|
||||
mori()
|
||||
|
||||
// Just in case
|
||||
/obj/item/clothing/neck/necklace/memento_mori/Destroy()
|
||||
if(active_owner)
|
||||
mori()
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/neck/necklace/memento_mori/proc/memento(mob/living/carbon/human/user)
|
||||
to_chat(user, span_warning("You feel your life being drained by the pendant..."))
|
||||
if (!do_after(user, 4 SECONDS, target = user))
|
||||
return
|
||||
|
||||
to_chat(user, span_notice("Your lifeforce is now linked to the pendant! You feel like removing it would kill you, and yet you instinctively know that until then, you won't die."))
|
||||
user.add_traits(list(TRAIT_NODEATH, TRAIT_NOHARDCRIT, TRAIT_NOCRITDAMAGE), CLOTHING_TRAIT)
|
||||
RegisterSignal(user, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(check_health))
|
||||
icon_state = "memento_mori_active"
|
||||
active_owner = user
|
||||
|
||||
/obj/item/clothing/neck/necklace/memento_mori/proc/mori()
|
||||
icon_state = "memento_mori"
|
||||
if (!active_owner)
|
||||
return
|
||||
UnregisterSignal(active_owner, COMSIG_LIVING_HEALTH_UPDATE)
|
||||
var/mob/living/carbon/human/stored_owner = active_owner //to avoid infinite looping when dust unequips the pendant
|
||||
active_owner = null
|
||||
to_chat(stored_owner, span_userdanger("You feel your life rapidly slipping away from you!"))
|
||||
stored_owner.dust(TRUE, TRUE)
|
||||
|
||||
/obj/item/clothing/neck/necklace/memento_mori/proc/check_health(mob/living/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/list/guardians = source.get_all_linked_holoparasites()
|
||||
if (!length(guardians))
|
||||
return
|
||||
if (source.health <= HEALTH_THRESHOLD_DEAD)
|
||||
for (var/mob/guardian in guardians)
|
||||
if(guardian.loc == src)
|
||||
continue
|
||||
consume_guardian(guardian)
|
||||
else if (source.health > HEALTH_THRESHOLD_CRIT)
|
||||
for (var/mob/guardian in guardians)
|
||||
if(guardian.loc != src)
|
||||
continue
|
||||
regurgitate_guardian(guardian)
|
||||
|
||||
/obj/item/clothing/neck/necklace/memento_mori/proc/consume_guardian(mob/living/basic/guardian/guardian)
|
||||
new /obj/effect/temp_visual/guardian/phase/out(get_turf(guardian))
|
||||
guardian.locked = TRUE
|
||||
guardian.forceMove(src)
|
||||
to_chat(guardian, span_userdanger("You have been locked away in your summoner's pendant!"))
|
||||
guardian.playsound_local(get_turf(guardian), 'sound/effects/magic/summonitems_generic.ogg', 50, TRUE)
|
||||
|
||||
/obj/item/clothing/neck/necklace/memento_mori/proc/regurgitate_guardian(mob/living/basic/guardian/guardian)
|
||||
guardian.locked = FALSE
|
||||
guardian.recall(forced = TRUE)
|
||||
to_chat(guardian, span_notice("You have been returned back from your summoner's pendant!"))
|
||||
guardian.playsound_local(get_turf(guardian), 'sound/effects/magic/repulse.ogg', 50, TRUE)
|
||||
|
||||
/datum/action/item_action/hands_free/memento_mori
|
||||
check_flags = NONE
|
||||
name = "Memento Mori"
|
||||
desc = "Bind your life to the pendant."
|
||||
|
||||
/datum/action/item_action/hands_free/memento_mori/do_effect(trigger_flags)
|
||||
var/obj/item/clothing/neck/necklace/memento_mori/memento = target
|
||||
if(memento.active_owner || !ishuman(owner))
|
||||
return FALSE
|
||||
memento.memento(owner)
|
||||
Remove(memento.active_owner) //Remove the action button, since there's no real use in having it now.
|
||||
return TRUE
|
||||
|
||||
// Concussive Gauntlets
|
||||
|
||||
/obj/item/clothing/gloves/gauntlets
|
||||
name = "concussive gauntlets"
|
||||
desc = "Pickaxes... for your hands!"
|
||||
icon_state = "concussive_gauntlets"
|
||||
inhand_icon_state = null
|
||||
toolspeed = 0.1
|
||||
strip_delay = 40
|
||||
equip_delay_other = 20
|
||||
cold_protection = HANDS
|
||||
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
|
||||
heat_protection = HANDS
|
||||
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
|
||||
body_parts_covered = HANDS|ARMS
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF //they are from lavaland after all
|
||||
armor_type = /datum/armor/gloves_gauntlets
|
||||
|
||||
/datum/armor/gloves_gauntlets
|
||||
melee = 15
|
||||
bullet = 25
|
||||
laser = 15
|
||||
energy = 15
|
||||
bomb = 100
|
||||
fire = 100
|
||||
acid = 30
|
||||
|
||||
/obj/item/clothing/gloves/gauntlets/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(slot & ITEM_SLOT_GLOVES)
|
||||
tool_behaviour = TOOL_MINING
|
||||
RegisterSignal(user, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(rocksmash))
|
||||
RegisterSignal(user, COMSIG_MOVABLE_BUMP, PROC_REF(rocksmash))
|
||||
else
|
||||
stopmining(user)
|
||||
|
||||
/obj/item/clothing/gloves/gauntlets/dropped(mob/user)
|
||||
. = ..()
|
||||
stopmining(user)
|
||||
|
||||
/obj/item/clothing/gloves/gauntlets/proc/stopmining(mob/user)
|
||||
tool_behaviour = initial(tool_behaviour)
|
||||
UnregisterSignal(user, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_MOVABLE_BUMP))
|
||||
|
||||
/obj/item/clothing/gloves/gauntlets/proc/rocksmash(mob/living/carbon/human/user, atom/rocks, proximity)
|
||||
SIGNAL_HANDLER
|
||||
if(!proximity)
|
||||
return NONE
|
||||
if(!ismineralturf(rocks) && !isasteroidturf(rocks))
|
||||
return NONE
|
||||
rocks.attackby(src, user)
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
@@ -0,0 +1,244 @@
|
||||
// KA modkit design discs
|
||||
|
||||
/obj/item/disk/design_disk/modkit_disc
|
||||
name = "KA Mod Disk"
|
||||
desc = "A design disc containing the design for a unique kinetic accelerator modkit. It's compatible with a research console."
|
||||
icon_state = "datadisk1"
|
||||
var/modkit_design = /datum/design/unique_modkit
|
||||
|
||||
/obj/item/disk/design_disk/modkit_disc/Initialize(mapload)
|
||||
. = ..()
|
||||
blueprints += new modkit_design
|
||||
|
||||
/obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe
|
||||
name = "Offensive Mining Explosion Mod Disk"
|
||||
modkit_design = /datum/design/unique_modkit/offensive_turf_aoe
|
||||
|
||||
/obj/item/disk/design_disk/modkit_disc/rapid_repeater
|
||||
name = "Rapid Repeater Mod Disk"
|
||||
modkit_design = /datum/design/unique_modkit/rapid_repeater
|
||||
|
||||
/obj/item/disk/design_disk/modkit_disc/resonator_blast
|
||||
name = "Resonator Blast Mod Disk"
|
||||
modkit_design = /datum/design/unique_modkit/resonator_blast
|
||||
|
||||
/obj/item/disk/design_disk/modkit_disc/bounty
|
||||
name = "Death Syphon Mod Disk"
|
||||
modkit_design = /datum/design/unique_modkit/bounty
|
||||
|
||||
// Wisp Lantern
|
||||
/obj/item/wisp_lantern
|
||||
name = "spooky lantern"
|
||||
desc = "This lantern gives off no light, but is home to a friendly wisp."
|
||||
icon = 'icons/obj/lighting.dmi'
|
||||
icon_state = "lantern-blue-on"
|
||||
inhand_icon_state = "lantern-blue-on"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi'
|
||||
var/obj/effect/wisp/wisp
|
||||
|
||||
/obj/item/wisp_lantern/attack_self(mob/user)
|
||||
. = ..()
|
||||
|
||||
if(!wisp)
|
||||
to_chat(user, span_warning("The wisp has gone missing!"))
|
||||
icon_state = "lantern-blue"
|
||||
inhand_icon_state = "lantern-blue"
|
||||
return
|
||||
|
||||
if(wisp.loc == src)
|
||||
to_chat(user, span_notice("You release the wisp. It begins to bob around your head."))
|
||||
icon_state = "lantern-blue"
|
||||
inhand_icon_state = "lantern-blue"
|
||||
wisp.orbit(user, 20)
|
||||
SSblackbox.record_feedback("tally", "wisp_lantern", 1, "Freed")
|
||||
return
|
||||
|
||||
to_chat(user, span_notice("You return the wisp to the lantern."))
|
||||
icon_state = "lantern-blue-on"
|
||||
inhand_icon_state = "lantern-blue-on"
|
||||
wisp.forceMove(src)
|
||||
SSblackbox.record_feedback("tally", "wisp_lantern", 1, "Returned")
|
||||
|
||||
/obj/item/wisp_lantern/Initialize(mapload)
|
||||
. = ..()
|
||||
wisp = new(src)
|
||||
|
||||
/obj/item/wisp_lantern/Destroy()
|
||||
if(wisp)
|
||||
if(wisp.loc == src)
|
||||
qdel(wisp)
|
||||
else
|
||||
wisp.visible_message(span_notice("[wisp] has a sad feeling for a moment, then it passes."))
|
||||
return ..()
|
||||
|
||||
/obj/effect/wisp
|
||||
name = "friendly wisp"
|
||||
desc = "Happy to light your way."
|
||||
icon = 'icons/obj/lighting.dmi'
|
||||
icon_state = "orb"
|
||||
light_system = OVERLAY_LIGHT
|
||||
light_range = 6
|
||||
light_power = 1.2
|
||||
light_color = "#79f1ff"
|
||||
light_flags = LIGHT_ATTACHED
|
||||
layer = ABOVE_ALL_MOB_LAYER
|
||||
plane = ABOVE_GAME_PLANE
|
||||
var/static/list/color_cutoffs = list(10, 25, 25)
|
||||
|
||||
/obj/effect/wisp/orbit(atom/thing, radius, clockwise, rotation_speed, rotation_segments, pre_rotation, lockinorbit)
|
||||
. = ..()
|
||||
if(!ismob(thing))
|
||||
return
|
||||
var/mob/being = thing
|
||||
RegisterSignal(being, COMSIG_MOB_UPDATE_SIGHT, PROC_REF(update_user_sight))
|
||||
to_chat(being, span_notice("The wisp enhances your vision."))
|
||||
ADD_TRAIT(being, TRAIT_THERMAL_VISION, REF(src))
|
||||
being.update_sight()
|
||||
|
||||
/obj/effect/wisp/stop_orbit(datum/component/orbiter/orbits)
|
||||
if(!ismob(orbit_target))
|
||||
return ..()
|
||||
var/mob/being = orbit_target
|
||||
UnregisterSignal(being, COMSIG_MOB_UPDATE_SIGHT)
|
||||
to_chat(being, span_notice("Your vision returns to normal."))
|
||||
REMOVE_TRAIT(being, TRAIT_THERMAL_VISION, REF(src))
|
||||
being.update_sight()
|
||||
return ..()
|
||||
|
||||
/obj/effect/wisp/proc/update_user_sight(mob/user)
|
||||
SIGNAL_HANDLER
|
||||
if(!isnull(color_cutoffs))
|
||||
user.lighting_color_cutoffs = blend_cutoff_colors(user.lighting_color_cutoffs, color_cutoffs)
|
||||
|
||||
// Jacob's ladder
|
||||
|
||||
/obj/item/jacobs_ladder
|
||||
name = "jacob's ladder"
|
||||
desc = "A celestial ladder that violates the laws of physics."
|
||||
icon = 'icons/obj/structures.dmi'
|
||||
icon_state = "ladder00"
|
||||
|
||||
/obj/item/jacobs_ladder/attack_self(mob/user)
|
||||
var/turf/our_loc = get_turf(src)
|
||||
var/ladder_x = our_loc.x
|
||||
var/ladder_y = our_loc.y
|
||||
to_chat(user, span_notice("You unfold the ladder. It extends much farther than you were expecting."))
|
||||
var/last_ladder = null
|
||||
for(var/i in 1 to world.maxz)
|
||||
if(is_centcom_level(i) || is_reserved_level(i) || is_away_level(i))
|
||||
continue
|
||||
var/turf/new_loc = locate(ladder_x, ladder_y, i)
|
||||
last_ladder = new /obj/structure/ladder/unbreakable/jacob(new_loc, null, last_ladder)
|
||||
qdel(src)
|
||||
|
||||
// Inherit from unbreakable but don't set ID, to suppress the default Z linkage
|
||||
/obj/structure/ladder/unbreakable/jacob
|
||||
name = "jacob's ladder"
|
||||
desc = "An indestructible celestial ladder that violates the laws of physics."
|
||||
|
||||
// Book of Babel
|
||||
|
||||
/obj/item/book_of_babel
|
||||
name = "Book of Babel"
|
||||
desc = "An ancient tome written in countless tongues."
|
||||
icon = 'icons/obj/service/library.dmi'
|
||||
icon_state = "book1"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
|
||||
/obj/item/book_of_babel/attack_self(mob/user)
|
||||
if(user.is_blind())
|
||||
to_chat(user, span_warning("You are blind and can't read anything!"))
|
||||
return FALSE
|
||||
if(!user.can_read(src))
|
||||
return FALSE
|
||||
to_chat(user, span_notice("You flip through the pages of the book, quickly and conveniently learning every language in existence. Somewhat less conveniently, the aging book crumbles to dust in the process. Whoops."))
|
||||
cure_curse_of_babel(user) // removes tower of babel if we have it
|
||||
user.grant_all_languages(source = LANGUAGE_BABEL)
|
||||
user.remove_blocked_language(GLOB.all_languages, source = LANGUAGE_ALL)
|
||||
if(user.mind)
|
||||
ADD_TRAIT(user.mind, TRAIT_TOWER_OF_BABEL, MAGIC_TRAIT) // this makes you immune to babel effects
|
||||
new /obj/effect/decal/cleanable/ash(get_turf(user))
|
||||
qdel(src)
|
||||
|
||||
|
||||
// Potion of Flight
|
||||
|
||||
/obj/item/reagent_containers/cup/bottle/potion
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "potionflask"
|
||||
fill_icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
fill_icon_state = "potion_fill"
|
||||
fill_icon_thresholds = list(0, 1)
|
||||
|
||||
/obj/item/reagent_containers/cup/bottle/potion/update_overlays()
|
||||
. = ..()
|
||||
if(reagents?.total_volume)
|
||||
. += "potionflask_cap"
|
||||
|
||||
/obj/item/reagent_containers/cup/bottle/potion/flight
|
||||
name = "strange elixir"
|
||||
desc = "A flask with an almost-holy aura emitting from it. The label on the bottle says: 'erqo'hyy tvi'rf lbh jv'atf'."
|
||||
list_reagents = list(/datum/reagent/flightpotion = 5)
|
||||
|
||||
/datum/reagent/flightpotion
|
||||
name = "Flight Potion"
|
||||
description = "Strange mutagenic compound of unknown origins."
|
||||
color = "#976230"
|
||||
|
||||
/datum/reagent/flightpotion/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE)
|
||||
. = ..()
|
||||
if(!ishuman(exposed_mob) || exposed_mob.stat == DEAD)
|
||||
return
|
||||
if(!(methods & (INGEST | TOUCH)))
|
||||
return
|
||||
var/mob/living/carbon/human/exposed_human = exposed_mob
|
||||
var/obj/item/bodypart/chest/chest = exposed_human.get_bodypart(BODY_ZONE_CHEST)
|
||||
if(!chest.wing_types || reac_volume < 5 || !exposed_human.dna)
|
||||
if((methods & INGEST) && show_message)
|
||||
to_chat(exposed_human, span_notice("<i>You feel nothing but a terrible aftertaste.</i>"))
|
||||
return
|
||||
var/had_wings = exposed_human.get_organ_slot(ORGAN_SLOT_EXTERNAL_WINGS)
|
||||
var/wing_type = get_wing_choice(exposed_human, chest)
|
||||
if(!wing_type)
|
||||
return
|
||||
var/obj/item/organ/wings/functional/wings = new wing_type()
|
||||
wings.Insert(exposed_human)
|
||||
exposed_human.dna.species.handle_mutant_bodyparts(exposed_human) // BUBBER EDIT OR BEGIN
|
||||
if(had_wings)
|
||||
to_chat(exposed_human, span_userdanger("A terrible pain travels down your back as your wings change shape!"))
|
||||
else
|
||||
to_chat(exposed_human, span_userdanger("A terrible pain travels down your back as wings burst out!"))
|
||||
playsound(exposed_human.loc, 'sound/items/poster/poster_ripped.ogg', 50, TRUE, -1)
|
||||
exposed_human.apply_damage(20, def_zone = BODY_ZONE_CHEST, forced = TRUE, wound_bonus = CANT_WOUND)
|
||||
exposed_human.emote("scream")
|
||||
|
||||
/datum/reagent/flightpotion/proc/get_wing_choice(mob/living/carbon/human/needs_wings, obj/item/bodypart/chest/chest)
|
||||
var/list/wing_types = chest.wing_types.Copy()
|
||||
if (wing_types.len == 1 || !needs_wings.client)
|
||||
return wing_types[1]
|
||||
var/list/radial_wings = list()
|
||||
var/list/name2type = list()
|
||||
for(var/obj/item/organ/wings/functional/possible_type as anything in wing_types)
|
||||
var/datum/sprite_accessory/accessory = initial(possible_type.sprite_accessory_override) //get the type
|
||||
accessory = SSaccessories.sprite_accessories[initial(accessory.key)][initial(accessory.name)] //SKYRAT EDIT CHANGE - ORIGINAL: accessory = SSaccessories.wings_list[initial(accessory.name)] //get the singleton instance
|
||||
var/image/img = image(icon = accessory.icon, icon_state = "m_wingsopen_[accessory.icon_state]_BEHIND") //Process the HUD elements
|
||||
img.transform *= 0.5
|
||||
img.pixel_x = -32
|
||||
if(radial_wings[accessory.name])
|
||||
stack_trace("Different wing types with repeated names. Please fix as this may cause issues.")
|
||||
else
|
||||
radial_wings[accessory.name] = img
|
||||
name2type[accessory.name] = possible_type
|
||||
var/wing_name = show_radial_menu(needs_wings, needs_wings, radial_wings, tooltips = TRUE)
|
||||
var/wing_type = name2type[wing_name]
|
||||
// If our chest gets replaced in the meanwile this can end up breaking, so we need to re-fetch it just to make sure
|
||||
var/obj/item/bodypart/chest/new_chest = needs_wings.get_bodypart(BODY_ZONE_CHEST)
|
||||
if(new_chest != chest)
|
||||
chest = new_chest
|
||||
wing_type = null
|
||||
if(!wing_type)
|
||||
if(!length(chest.wing_types))
|
||||
return
|
||||
wing_type = pick(chest.wing_types)
|
||||
return wing_type
|
||||
@@ -0,0 +1,238 @@
|
||||
#define ATTACK_STRIKE "Hilt Strike"
|
||||
#define ATTACK_SLICE "Wide Slice"
|
||||
#define ATTACK_DASH "Dash Attack"
|
||||
#define ATTACK_CUT "Tendon Cut"
|
||||
#define ATTACK_CLOAK "Dark Cloak"
|
||||
#define ATTACK_SHATTER "Shatter"
|
||||
|
||||
/obj/item/organ/cyberimp/arm/shard
|
||||
name = "dark spoon shard"
|
||||
desc = "An eerie metal shard surrounded by dark energies...of soup drinking. You probably don't think you should have been able to find this."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "cursed_katana_organ"
|
||||
organ_flags = ORGAN_ORGANIC | ORGAN_FROZEN | ORGAN_UNREMOVABLE
|
||||
items_to_create = list(/obj/item/kitchen/spoon)
|
||||
extend_sound = 'sound/items/unsheath.ogg'
|
||||
retract_sound = 'sound/items/sheath.ogg'
|
||||
|
||||
/obj/item/organ/cyberimp/arm/shard/attack_self(mob/user, modifiers)
|
||||
. = ..()
|
||||
to_chat(user, span_userdanger("The mass goes up your arm and goes inside it!"))
|
||||
playsound(user, 'sound/effects/magic/demon_consume.ogg', 50, TRUE)
|
||||
var/index = user.get_held_index_of_item(src)
|
||||
zone = (index == LEFT_HANDS ? BODY_ZONE_L_ARM : BODY_ZONE_R_ARM)
|
||||
SetSlotFromZone()
|
||||
user.temporarilyRemoveItemFromInventory(src, TRUE)
|
||||
Insert(user)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/shard/screwdriver_act(mob/living/user, obj/item/screwtool)
|
||||
return
|
||||
|
||||
/obj/item/organ/cyberimp/arm/shard/katana
|
||||
name = "dark shard"
|
||||
desc = "An eerie metal shard surrounded by dark energies."
|
||||
items_to_create = list(/obj/item/cursed_katana)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/shard/katana/Retract()
|
||||
var/obj/item/cursed_katana/katana = active_item
|
||||
if(!katana || katana.shattered)
|
||||
return FALSE
|
||||
if(!katana.drew_blood)
|
||||
to_chat(owner, span_userdanger("[katana] lashes out at you in hunger!"))
|
||||
playsound(owner, 'sound/effects/magic/demon_attack1.ogg', 50, TRUE)
|
||||
owner.apply_damage(25, BRUTE, hand, wound_bonus = 10, sharpness = SHARP_EDGED)
|
||||
katana.drew_blood = FALSE
|
||||
katana.wash(CLEAN_TYPE_BLOOD)
|
||||
return ..()
|
||||
|
||||
/obj/item/cursed_katana
|
||||
name = "cursed katana"
|
||||
desc = "A katana used to seal something vile away long ago. \
|
||||
Even with the weapon destroyed, all the pieces containing the creature have coagulated back together to find a new host."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "cursed_katana"
|
||||
icon_angle = -45
|
||||
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
|
||||
force = 15
|
||||
armour_penetration = 30
|
||||
block_chance = 30
|
||||
block_sound = 'sound/items/weapons/parry.ogg'
|
||||
sharpness = SHARP_EDGED
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
|
||||
attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
|
||||
hitsound = 'sound/items/weapons/bladeslice.ogg'
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | FREEZE_PROOF
|
||||
var/shattered = FALSE
|
||||
var/drew_blood = FALSE
|
||||
var/static/list/combo_list = list(
|
||||
ATTACK_STRIKE = list(COMBO_STEPS = list(LEFT_ATTACK, LEFT_ATTACK, RIGHT_ATTACK), COMBO_PROC = PROC_REF(strike)),
|
||||
ATTACK_SLICE = list(COMBO_STEPS = list(RIGHT_ATTACK, LEFT_ATTACK, LEFT_ATTACK), COMBO_PROC = PROC_REF(slice)),
|
||||
ATTACK_DASH = list(COMBO_STEPS = list(LEFT_ATTACK, RIGHT_ATTACK, RIGHT_ATTACK), COMBO_PROC = PROC_REF(dash)),
|
||||
ATTACK_CUT = list(COMBO_STEPS = list(RIGHT_ATTACK, RIGHT_ATTACK, LEFT_ATTACK), COMBO_PROC = PROC_REF(cut)),
|
||||
ATTACK_CLOAK = list(COMBO_STEPS = list(LEFT_ATTACK, RIGHT_ATTACK, LEFT_ATTACK, RIGHT_ATTACK), COMBO_PROC = PROC_REF(cloak)),
|
||||
ATTACK_SHATTER = list(COMBO_STEPS = list(RIGHT_ATTACK, LEFT_ATTACK, RIGHT_ATTACK, LEFT_ATTACK), COMBO_PROC = PROC_REF(shatter)),
|
||||
)
|
||||
var/list/alt_continuous = list("stabs", "pierces", "impales")
|
||||
var/list/alt_simple = list("stab", "pierce", "impale")
|
||||
|
||||
/obj/item/cursed_katana/Initialize(mapload)
|
||||
. = ..()
|
||||
alt_continuous = string_list(alt_continuous)
|
||||
alt_simple = string_list(alt_simple)
|
||||
AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple)
|
||||
AddComponent( \
|
||||
/datum/component/combo_attacks, \
|
||||
combos = combo_list, \
|
||||
max_combo_length = 4, \
|
||||
examine_message = span_notice("<i>There seem to be inscriptions on it... you could examine them closer?</i>"), \
|
||||
reset_message = "you return to neutral stance", \
|
||||
can_attack_callback = CALLBACK(src, PROC_REF(can_combo_attack)) \
|
||||
)
|
||||
|
||||
/obj/item/cursed_katana/examine(mob/user)
|
||||
. = ..()
|
||||
. += drew_blood ? span_nicegreen("It's sated... for now.") : span_danger("It will not be sated until it tastes blood.")
|
||||
|
||||
/obj/item/cursed_katana/dropped(mob/user)
|
||||
. = ..()
|
||||
if(isturf(loc))
|
||||
qdel(src)
|
||||
|
||||
/obj/item/cursed_katana/attack(mob/living/target, mob/user, click_parameters)
|
||||
if(target.stat < DEAD && target != user)
|
||||
drew_blood = TRUE
|
||||
if(ismining(target))
|
||||
user.changeNext_move(CLICK_CD_RAPID)
|
||||
return ..()
|
||||
|
||||
/obj/item/cursed_katana/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
|
||||
if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK)
|
||||
final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword
|
||||
return ..()
|
||||
|
||||
/obj/item/cursed_katana/proc/can_combo_attack(mob/user, mob/living/target)
|
||||
return target.stat != DEAD && target != user
|
||||
|
||||
/obj/item/cursed_katana/proc/strike(mob/living/target, mob/user)
|
||||
user.visible_message(span_warning("[user] strikes [target] with [src]'s hilt!"),
|
||||
span_notice("You hilt strike [target]!"))
|
||||
to_chat(target, span_userdanger("You've been struck by [user]!"))
|
||||
playsound(src, 'sound/items/weapons/genhit3.ogg', 50, TRUE)
|
||||
RegisterSignal(target, COMSIG_MOVABLE_IMPACT, PROC_REF(strike_throw_impact))
|
||||
var/atom/throw_target = get_edge_target_turf(target, user.dir)
|
||||
target.throw_at(throw_target, 5, 3, user, FALSE, gentle = TRUE)
|
||||
target.apply_damage(damage = 17, bare_wound_bonus = 10)
|
||||
to_chat(target, span_userdanger("You've been struck by [user]!"))
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_PUNCH)
|
||||
|
||||
/obj/item/cursed_katana/proc/strike_throw_impact(mob/living/source, atom/hit_atom, datum/thrownthing/thrownthing)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
UnregisterSignal(source, COMSIG_MOVABLE_IMPACT)
|
||||
if(isclosedturf(hit_atom))
|
||||
source.apply_damage(damage = 5)
|
||||
if(ishostile(source))
|
||||
var/mob/living/simple_animal/hostile/target = source
|
||||
target.ranged_cooldown += 5 SECONDS
|
||||
else if(iscarbon(source))
|
||||
var/mob/living/carbon/target = source
|
||||
target.set_confusion_if_lower(8 SECONDS)
|
||||
return NONE
|
||||
|
||||
/obj/item/cursed_katana/proc/slice(mob/living/target, mob/user)
|
||||
user.visible_message(span_warning("[user] does a wide slice!"),
|
||||
span_notice("You do a wide slice!"))
|
||||
playsound(src, 'sound/items/weapons/bladeslice.ogg', 50, TRUE)
|
||||
user.do_item_attack_animation(target, used_item = src, animation_type = ATTACK_ANIMATION_SLASH)
|
||||
var/turf/user_turf = get_turf(user)
|
||||
var/dir_to_target = get_dir(user_turf, get_turf(target))
|
||||
var/static/list/cursed_katana_slice_angles = list(0, -45, 45, -90, 90) //so that the animation animates towards the target clicked and not towards a side target
|
||||
for(var/iteration in cursed_katana_slice_angles)
|
||||
var/turf/turf = get_step(user_turf, turn(dir_to_target, iteration))
|
||||
user.do_attack_animation(turf, ATTACK_EFFECT_SLASH)
|
||||
for(var/mob/living/additional_target in turf)
|
||||
if(user.Adjacent(additional_target) && additional_target.density)
|
||||
additional_target.apply_damage(damage = 15, sharpness = SHARP_EDGED, bare_wound_bonus = 10)
|
||||
to_chat(additional_target, span_userdanger("You've been sliced by [user]!"))
|
||||
target.apply_damage(damage = 5, sharpness = SHARP_EDGED, wound_bonus = 10)
|
||||
|
||||
/obj/item/cursed_katana/proc/cloak(mob/living/target, mob/user)
|
||||
user.alpha = 150
|
||||
user.SetInvisibility(INVISIBILITY_OBSERVER, id=type) // so hostile mobs cant see us or target us
|
||||
user.add_sight(SEE_SELF) // so we can see us
|
||||
user.visible_message(span_warning("[user] vanishes into thin air!"),
|
||||
span_notice("You enter the dark cloak."))
|
||||
new /obj/effect/temp_visual/mook_dust(get_turf(src))
|
||||
playsound(src, 'sound/effects/magic/smoke.ogg', 50, TRUE)
|
||||
if(ishostile(target))
|
||||
var/mob/living/simple_animal/hostile/hostile_target = target
|
||||
if(hostile_target.target == user)
|
||||
hostile_target.LoseTarget()
|
||||
addtimer(CALLBACK(src, PROC_REF(uncloak), user), 5 SECONDS, TIMER_UNIQUE)
|
||||
|
||||
/obj/item/cursed_katana/proc/uncloak(mob/user)
|
||||
user.alpha = 255
|
||||
user.RemoveInvisibility(type)
|
||||
user.clear_sight(SEE_SELF)
|
||||
user.visible_message(span_warning("[user] appears from thin air!"),
|
||||
span_notice("You exit the dark cloak."))
|
||||
playsound(src, 'sound/effects/magic/summonitems_generic.ogg', 50, TRUE)
|
||||
new /obj/effect/temp_visual/mook_dust(get_turf(src))
|
||||
|
||||
/obj/item/cursed_katana/proc/cut(mob/living/target, mob/user)
|
||||
user.visible_message(span_warning("[user] cuts [target]'s tendons!"),
|
||||
span_notice("You tendon cut [target]!"))
|
||||
to_chat(target, span_userdanger("Your tendons have been cut by [user]!"))
|
||||
user.do_item_attack_animation(target, used_item = src, animation_type = ATTACK_ANIMATION_SLASH)
|
||||
target.apply_damage(damage = 15, sharpness = SHARP_EDGED, wound_bonus = 15)
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
|
||||
playsound(src, 'sound/items/weapons/rapierhit.ogg', 50, TRUE)
|
||||
var/datum/status_effect/stacking/saw_bleed/bloodletting/status = target.has_status_effect(/datum/status_effect/stacking/saw_bleed/bloodletting)
|
||||
if(!status)
|
||||
target.apply_status_effect(/datum/status_effect/stacking/saw_bleed/bloodletting, 6)
|
||||
else
|
||||
status.add_stacks(6)
|
||||
|
||||
/obj/item/cursed_katana/proc/dash(mob/living/target, mob/user)
|
||||
user.visible_message(span_warning("[user] dashes through [target]!"),
|
||||
span_notice("You dash through [target]!"))
|
||||
to_chat(target, span_userdanger("[user] dashes through you!"))
|
||||
playsound(src, 'sound/effects/magic/blink.ogg', 50, TRUE)
|
||||
target.apply_damage(damage = 17, sharpness = SHARP_POINTY, bare_wound_bonus = 10)
|
||||
var/turf/dash_target = get_turf(target)
|
||||
for(var/distance in 0 to 8)
|
||||
var/turf/current_dash_target = dash_target
|
||||
current_dash_target = get_step(current_dash_target, user.dir)
|
||||
if(!current_dash_target.is_blocked_turf(TRUE))
|
||||
dash_target = current_dash_target
|
||||
else
|
||||
break
|
||||
new /obj/effect/temp_visual/guardian/phase/out(get_turf(user))
|
||||
new /obj/effect/temp_visual/guardian/phase(dash_target)
|
||||
do_teleport(user, dash_target, channel = TELEPORT_CHANNEL_MAGIC)
|
||||
|
||||
/obj/item/cursed_katana/proc/shatter(mob/living/target, mob/user)
|
||||
user.visible_message(span_warning("[user] shatters [src] over [target]!"),
|
||||
span_notice("You shatter [src] over [target]!"))
|
||||
to_chat(target, span_userdanger("[user] shatters [src] over you!"))
|
||||
target.apply_damage(damage = ishostile(target) ? 75 : 35, wound_bonus = 20)
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_SMASH)
|
||||
playsound(src, 'sound/effects/glass/glassbr3.ogg', 100, TRUE)
|
||||
shattered = TRUE
|
||||
moveToNullspace()
|
||||
balloon_alert(user, "katana shattered")
|
||||
addtimer(CALLBACK(src, PROC_REF(coagulate), user), 45 SECONDS)
|
||||
|
||||
/obj/item/cursed_katana/proc/coagulate(mob/user)
|
||||
balloon_alert(user, "katana coagulated")
|
||||
shattered = FALSE
|
||||
playsound(src, 'sound/effects/magic/demon_consume.ogg', 50, TRUE)
|
||||
|
||||
#undef ATTACK_STRIKE
|
||||
#undef ATTACK_SLICE
|
||||
#undef ATTACK_DASH
|
||||
#undef ATTACK_CUT
|
||||
#undef ATTACK_CLOAK
|
||||
#undef ATTACK_SHATTER
|
||||
@@ -0,0 +1,251 @@
|
||||
// Rod of Asclepius
|
||||
/obj/item/rod_of_asclepius
|
||||
name = "\improper Rod of Asclepius"
|
||||
desc = "A wooden rod about the size of your forearm with a snake carved around it, winding its way up the sides of the rod. Something about it seems to inspire in you the responsibilty and duty to help others."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
|
||||
icon_state = "asclepius_dormant"
|
||||
inhand_icon_state = "asclepius_dormant"
|
||||
icon_angle = -45
|
||||
var/activated = FALSE
|
||||
var/static/list/oath_lines = list(
|
||||
"I swear to fulfill, to the best of my ability and judgment, this covenant:",
|
||||
"I will apply, for the benefit of the sick, all measures that are required, avoiding those twin traps of overtreatment and therapeutic nihilism.",
|
||||
"I will remember that I remain a member of society, with special obligations to all my fellow human beings, those sound of mind and body as well as the infirm.",
|
||||
"If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter. May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help.",
|
||||
)
|
||||
|
||||
/obj/item/rod_of_asclepius/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/update_icon_updates_onmob)
|
||||
|
||||
/obj/item/rod_of_asclepius/update_desc(updates)
|
||||
. = ..()
|
||||
desc = activated ? "A short wooden rod with a mystical snake inseparably gripping itself and the rod to your forearm. It flows with a healing energy that disperses amongst yourself and those around you." : initial(desc)
|
||||
|
||||
/obj/item/rod_of_asclepius/update_icon_state()
|
||||
. = ..()
|
||||
icon_state = inhand_icon_state = "asclepius_[activated ? "active" : "dormant"]"
|
||||
|
||||
/obj/item/rod_of_asclepius/vv_edit_var(vname, vval)
|
||||
. = ..()
|
||||
if(vname == NAMEOF(src, activated) && activated)
|
||||
activated()
|
||||
|
||||
/obj/item/rod_of_asclepius/attack_self(mob/user)
|
||||
if(activated)
|
||||
return
|
||||
if(!iscarbon(user))
|
||||
to_chat(user, span_warning("The snake carving seems to come alive, if only for a moment, before returning to its dormant state, almost as if it finds you incapable of holding its oath."))
|
||||
return
|
||||
var/mob/living/carbon/itemUser = user
|
||||
var/usedHand = itemUser.get_held_index_of_item(src)
|
||||
if(itemUser.has_status_effect(/datum/status_effect/hippocratic_oath))
|
||||
to_chat(user, span_warning("You can't possibly handle the responsibility of more than one rod!"))
|
||||
return
|
||||
|
||||
var/static/failText = span_warning("The snake seems unsatisfied with your incomplete oath and returns to its previous place on the rod, returning to its dormant, wooden state. You must stand still while completing your oath!")
|
||||
to_chat(itemUser, span_notice("The wooden snake that was carved into the rod seems to suddenly come alive and begins to slither down your arm! The compulsion to help others grows abnormally strong..."))
|
||||
for(var/oath_line in oath_lines)
|
||||
if(do_after(itemUser, 3 SECONDS, target = itemUser))
|
||||
itemUser.say(oath_line, forced = "hippocratic oath")
|
||||
continue
|
||||
|
||||
balloon_alert(itemUser, "interrupted!")
|
||||
to_chat(itemUser, failText)
|
||||
return
|
||||
|
||||
to_chat(itemUser, span_notice("The snake, satisfied with your oath, attaches itself and the rod to your forearm with an inseparable grip. Your thoughts seem to only revolve around the core idea of helping others, and harm is nothing more than a distant, wicked memory..."))
|
||||
var/datum/status_effect/hippocratic_oath/effect = itemUser.apply_status_effect(/datum/status_effect/hippocratic_oath)
|
||||
effect.hand = usedHand
|
||||
activated()
|
||||
|
||||
/obj/item/rod_of_asclepius/proc/activated()
|
||||
item_flags = DROPDEL
|
||||
ADD_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT(type))
|
||||
activated = TRUE
|
||||
update_appearance()
|
||||
|
||||
// Red/Blue Cubes
|
||||
|
||||
/obj/item/warp_cube
|
||||
name = "blue cube"
|
||||
desc = "A mysterious blue cube."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "blue_cube"
|
||||
var/teleport_color = "#3FBAFD"
|
||||
var/obj/item/warp_cube/linked
|
||||
var/teleporting = FALSE
|
||||
|
||||
/obj/item/warp_cube/Destroy()
|
||||
if(!QDELETED(linked))
|
||||
qdel(linked)
|
||||
linked = null
|
||||
return ..()
|
||||
|
||||
/obj/item/warp_cube/attack_self(mob/user)
|
||||
var/turf/current_location = get_turf(user)
|
||||
if(!linked || isnull(get_turf(linked)) || !check_teleport_valid(src, current_location))
|
||||
to_chat(user, span_warning("[src] fizzles uselessly."))
|
||||
return
|
||||
if(teleporting)
|
||||
return
|
||||
teleporting = TRUE
|
||||
linked.teleporting = TRUE
|
||||
var/turf/T = get_turf(src)
|
||||
new /obj/effect/temp_visual/warp_cube(T, user, teleport_color, TRUE)
|
||||
SSblackbox.record_feedback("tally", "warp_cube", 1, type)
|
||||
new /obj/effect/temp_visual/warp_cube(get_turf(linked), user, linked.teleport_color, FALSE)
|
||||
var/obj/effect/warp_cube/link_holder = new /obj/effect/warp_cube(T)
|
||||
user.forceMove(link_holder) //mess around with loc so the user can't wander around
|
||||
sleep(0.25 SECONDS)
|
||||
if(QDELETED(user))
|
||||
qdel(link_holder)
|
||||
return
|
||||
if(QDELETED(linked) || isnull(get_turf(linked)))
|
||||
user.forceMove(get_turf(link_holder))
|
||||
qdel(link_holder)
|
||||
return
|
||||
link_holder.forceMove(get_turf(linked))
|
||||
sleep(0.25 SECONDS)
|
||||
if(QDELETED(user))
|
||||
qdel(link_holder)
|
||||
return
|
||||
teleporting = FALSE
|
||||
if(!QDELETED(linked))
|
||||
linked.teleporting = FALSE
|
||||
user.forceMove(get_turf(link_holder))
|
||||
qdel(link_holder)
|
||||
|
||||
/obj/item/warp_cube/red
|
||||
name = "red cube"
|
||||
desc = "A mysterious red cube."
|
||||
icon_state = "red_cube"
|
||||
teleport_color = "#FD3F48"
|
||||
|
||||
/obj/item/warp_cube/red/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!linked)
|
||||
var/obj/item/warp_cube/blue = new(src.loc)
|
||||
linked = blue
|
||||
blue.linked = src
|
||||
|
||||
/obj/effect/warp_cube
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
anchored = TRUE
|
||||
|
||||
// Immortality Talisman
|
||||
|
||||
/obj/item/immortality_talisman
|
||||
name = "\improper Immortality Talisman"
|
||||
desc = "A dread talisman that can render you completely invulnerable."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "talisman"
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
actions_types = list(/datum/action/item_action/immortality)
|
||||
var/cooldown = 0
|
||||
|
||||
/obj/item/immortality_talisman/Initialize(mapload)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/anti_magic, ALL)
|
||||
|
||||
/datum/action/item_action/immortality
|
||||
name = "Immortality"
|
||||
|
||||
/obj/item/immortality_talisman/attack_self(mob/user)
|
||||
if(cooldown < world.time)
|
||||
SSblackbox.record_feedback("amount", "immortality_talisman_uses", 1)
|
||||
cooldown = world.time + 600
|
||||
new /obj/effect/immortality_talisman(get_turf(user), user)
|
||||
else
|
||||
to_chat(user, span_warning("[src] is not ready yet!"))
|
||||
|
||||
/obj/effect/immortality_talisman
|
||||
name = "hole in reality"
|
||||
desc = "It's shaped an awful lot like a person."
|
||||
icon_state = "blank"
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
var/vanish_description = "vanishes from reality"
|
||||
// Weakref to the user who we're "acting" on
|
||||
var/datum/weakref/user_ref
|
||||
|
||||
/obj/effect/immortality_talisman/Initialize(mapload, mob/new_user)
|
||||
. = ..()
|
||||
if(new_user)
|
||||
vanish(new_user)
|
||||
|
||||
/obj/effect/immortality_talisman/Destroy()
|
||||
// If we have a mob, we need to free it before cleanup
|
||||
// This is a safety to prevent nuking a human, not so much a good pattern in general
|
||||
unvanish()
|
||||
return ..()
|
||||
|
||||
/obj/effect/immortality_talisman/proc/unvanish()
|
||||
var/mob/user = user_ref?.resolve()
|
||||
user_ref = null
|
||||
|
||||
if(!user)
|
||||
return
|
||||
|
||||
user.remove_traits(list(TRAIT_GODMODE, TRAIT_NO_TRANSFORM), REF(src))
|
||||
user.forceMove(get_turf(src))
|
||||
user.visible_message(span_danger("[user] pops back into reality!"))
|
||||
|
||||
/obj/effect/immortality_talisman/proc/vanish(mob/user)
|
||||
user.visible_message(span_danger("[user] [vanish_description], leaving a hole in [user.p_their()] place!"))
|
||||
|
||||
desc = "It's shaped an awful lot like [user.name]."
|
||||
setDir(user.dir)
|
||||
|
||||
user.forceMove(src)
|
||||
user.add_traits(list(TRAIT_GODMODE, TRAIT_NO_TRANSFORM), REF(src))
|
||||
|
||||
user_ref = WEAKREF(user)
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(dissipate)), 10 SECONDS)
|
||||
|
||||
/obj/effect/immortality_talisman/proc/dissipate()
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/immortality_talisman/attackby()
|
||||
return
|
||||
|
||||
/obj/effect/immortality_talisman/relaymove(mob/living/user, direction)
|
||||
// Won't really come into play since our mob has TRAIT_NO_TRANSFORM and cannot move,
|
||||
// but regardless block all relayed moves, because no, you cannot move in the void.
|
||||
return
|
||||
|
||||
/obj/effect/immortality_talisman/singularity_pull(atom/singularity, current_size)
|
||||
return
|
||||
|
||||
/obj/effect/immortality_talisman/void
|
||||
vanish_description = "is dragged into the void"
|
||||
|
||||
// Shared Bag
|
||||
|
||||
/obj/item/shared_storage
|
||||
name = "paradox bag"
|
||||
desc = "Somehow, it's in two places at once."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "paradox_bag"
|
||||
worn_icon_state = "paradoxbag"
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
|
||||
/obj/item/shared_storage/red
|
||||
|
||||
/obj/item/shared_storage/red/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
create_storage(max_total_storage = 15, max_slots = 21)
|
||||
|
||||
new /obj/item/shared_storage/blue(drop_location(), src)
|
||||
|
||||
/obj/item/shared_storage/blue/Initialize(mapload, atom/master)
|
||||
. = ..()
|
||||
if(!istype(master))
|
||||
return INITIALIZE_HINT_QDEL
|
||||
create_storage(max_total_storage = 15, max_slots = 21)
|
||||
|
||||
atom_storage.set_real_location(master)
|
||||
@@ -0,0 +1,113 @@
|
||||
/obj/item/clothing/glasses/godeye
|
||||
name = "eye of god"
|
||||
desc = "A strange eye, said to have been torn from an omniscient creature that used to roam the wastes."
|
||||
icon_state = "godeye"
|
||||
inhand_icon_state = null
|
||||
vision_flags = SEE_TURFS
|
||||
clothing_traits = list(TRAIT_MADNESS_IMMUNE)
|
||||
// Blue, light blue
|
||||
color_cutoffs = list(15, 30, 40)
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
custom_materials = null
|
||||
var/datum/action/cooldown/spell/pointed/scan/scan_ability
|
||||
|
||||
/obj/item/clothing/glasses/godeye/Initialize(mapload)
|
||||
. = ..()
|
||||
scan_ability = new(src)
|
||||
|
||||
/obj/item/clothing/glasses/godeye/Destroy()
|
||||
QDEL_NULL(scan_ability)
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/glasses/godeye/equipped(mob/living/user, slot)
|
||||
. = ..()
|
||||
if(ishuman(user) && (slot & ITEM_SLOT_EYES))
|
||||
ADD_TRAIT(src, TRAIT_NODROP, EYE_OF_GOD_TRAIT)
|
||||
pain(user)
|
||||
scan_ability.Grant(user)
|
||||
|
||||
/obj/item/clothing/glasses/godeye/dropped(mob/living/user)
|
||||
. = ..()
|
||||
// Behead someone, their "glasses" drop on the floor
|
||||
// and thus, the god eye should no longer be sticky
|
||||
REMOVE_TRAIT(src, TRAIT_NODROP, EYE_OF_GOD_TRAIT)
|
||||
// And remove the scan ability, note that if we're being called from Destroy
|
||||
// that this may already be nulled and removed
|
||||
scan_ability?.Remove(user)
|
||||
|
||||
/obj/item/clothing/glasses/godeye/proc/pain(mob/living/victim)
|
||||
to_chat(victim, span_userdanger("You experience blinding pain, as [src] burrows into your skull."))
|
||||
victim.emote("scream")
|
||||
victim.flash_act()
|
||||
|
||||
/datum/action/cooldown/spell/pointed/scan
|
||||
name = "Scan"
|
||||
desc = "Scan an enemy, to get their location and rebuke them, increasing their time between attacks."
|
||||
background_icon_state = "bg_clock"
|
||||
overlay_icon_state = "bg_clock_border"
|
||||
button_icon = 'icons/mob/actions/actions_items.dmi'
|
||||
button_icon_state = "scan"
|
||||
school = SCHOOL_HOLY
|
||||
cooldown_time = 35 SECONDS
|
||||
spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC
|
||||
antimagic_flags = MAGIC_RESISTANCE_MIND //Even god cannot penetrate the tin foil hat
|
||||
|
||||
ranged_mousepointer = 'icons/effects/mouse_pointers/scan_target.dmi'
|
||||
|
||||
/datum/action/cooldown/spell/pointed/scan/is_valid_target(atom/cast_on)
|
||||
if(!isliving(cast_on))
|
||||
owner.balloon_alert(owner, "not a valid target!")
|
||||
return FALSE
|
||||
var/mob/living/living_cast_on = cast_on
|
||||
if(living_cast_on.stat == DEAD)
|
||||
owner.balloon_alert(owner, "target is dead!")
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/action/cooldown/spell/pointed/scan/cast(mob/living/cast_on)
|
||||
. = ..()
|
||||
|
||||
if(cast_on.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0))
|
||||
to_chat(owner, span_warning("As we apply our dissecting vision, we are abruptly cut short. \
|
||||
They have some kind of enigmatic mental defense. It seems we've been foiled."))
|
||||
return
|
||||
|
||||
if(cast_on == owner)
|
||||
to_chat(owner, span_warning("The last time a god stared too closely into their own reflection, they became transfixed for all of time. Do not let us become like them."))
|
||||
return
|
||||
|
||||
var/mob/living/living_owner = owner
|
||||
var/mob/living/living_scanned = cast_on
|
||||
living_scanned.apply_status_effect(/datum/status_effect/rebuked)
|
||||
var/datum/status_effect/agent_pinpointer/scan_pinpointer = living_owner.apply_status_effect(/datum/status_effect/agent_pinpointer/scan)
|
||||
scan_pinpointer.scan_target = living_scanned
|
||||
|
||||
to_chat(living_scanned, span_warning("You briefly see a flash of [living_owner]'s face before being knocked off-balance by an unseen force!"))
|
||||
living_scanned.add_filter("scan", 2, list("type" = "outline", "color" = COLOR_RED, "size" = 1))
|
||||
addtimer(CALLBACK(living_scanned, TYPE_PROC_REF(/datum, remove_filter), "scan"), 30 SECONDS)
|
||||
|
||||
healthscan(living_owner, living_scanned, 1, TRUE)
|
||||
|
||||
owner.playsound_local(get_turf(owner), 'sound/effects/magic/smoke.ogg', 50, TRUE)
|
||||
owner.balloon_alert(owner, "[living_scanned] scanned")
|
||||
addtimer(CALLBACK(src, PROC_REF(send_cooldown_end_message), cooldown_time))
|
||||
|
||||
/datum/action/cooldown/spell/pointed/scan/proc/send_cooldown_end_message()
|
||||
owner?.balloon_alert(owner, "scan recharged")
|
||||
|
||||
/datum/status_effect/agent_pinpointer/scan
|
||||
duration = 15 SECONDS
|
||||
alert_type = /atom/movable/screen/alert/status_effect/agent_pinpointer/scan
|
||||
tick_interval = 2 SECONDS
|
||||
range_fuzz_factor = 0
|
||||
minimum_range = 1
|
||||
range_mid = 5
|
||||
range_far = 15
|
||||
|
||||
/datum/status_effect/agent_pinpointer/scan/scan_for_target()
|
||||
return
|
||||
|
||||
/atom/movable/screen/alert/status_effect/agent_pinpointer/scan
|
||||
name = "Scan Target"
|
||||
desc = "Contact may or may not be close."
|
||||
@@ -0,0 +1,77 @@
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer
|
||||
name = "godslayer armour"
|
||||
icon_state = "godslayer"
|
||||
desc = "A suit of armour fashioned from the remnants of a knight's armor, and parts of a wendigo."
|
||||
armor_type = /datum/armor/cloak_godslayer
|
||||
clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL
|
||||
hoodtype = /obj/item/clothing/head/hooded/cloakhood/godslayer
|
||||
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF | FREEZE_PROOF
|
||||
transparent_protection = HIDEGLOVES|HIDESUITSTORAGE|HIDEJUMPSUIT|HIDESHOES
|
||||
/// Amount to heal when the effect is triggered
|
||||
var/heal_amount = 500
|
||||
/// Time until the effect can take place again
|
||||
var/effect_cooldown_time = 10 MINUTES
|
||||
/// Current cooldown for the effect
|
||||
COOLDOWN_DECLARE(effect_cooldown)
|
||||
var/static/list/damage_heal_order = list(BRUTE, BURN, OXY)
|
||||
|
||||
/datum/armor/cloak_godslayer
|
||||
melee = 50
|
||||
bullet = 25
|
||||
laser = 25
|
||||
energy = 25
|
||||
bomb = 50
|
||||
bio = 50
|
||||
fire = 100
|
||||
acid = 100
|
||||
wound = 10
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer/Initialize(mapload)
|
||||
. = ..()
|
||||
allowed = GLOB.mining_suit_allowed
|
||||
AddComponent(/datum/component/item_equipped_movement_rustle, SFX_PLATE_ARMOR_RUSTLE, 8)
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer/examine(mob/user)
|
||||
. = ..()
|
||||
if(loc == user && !COOLDOWN_FINISHED(src, effect_cooldown))
|
||||
. += "You feel like the revival effect will be able to occur again in [COOLDOWN_TIMELEFT(src, effect_cooldown) / 10] seconds."
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(slot & ITEM_SLOT_OCLOTHING)
|
||||
RegisterSignal(user, COMSIG_MOB_STATCHANGE, PROC_REF(resurrect))
|
||||
return
|
||||
UnregisterSignal(user, COMSIG_MOB_STATCHANGE)
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer/dropped(mob/user)
|
||||
..()
|
||||
UnregisterSignal(user, COMSIG_MOB_STATCHANGE)
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/godslayer/proc/resurrect(mob/living/carbon/user, new_stat)
|
||||
SIGNAL_HANDLER
|
||||
if(new_stat > CONSCIOUS && new_stat < DEAD && COOLDOWN_FINISHED(src, effect_cooldown))
|
||||
COOLDOWN_START(src, effect_cooldown, effect_cooldown_time) //This needs to happen first, otherwise there's an infinite loop
|
||||
user.heal_ordered_damage(heal_amount, damage_heal_order)
|
||||
user.visible_message(span_notice("[user] suddenly revives, as their armor swirls with demonic energy!"), span_notice("You suddenly feel invigorated!"))
|
||||
playsound(user.loc, 'sound/effects/magic/clockwork/ratvar_attack.ogg', 50)
|
||||
|
||||
/obj/item/clothing/head/hooded/cloakhood/godslayer
|
||||
name = "godslayer helm"
|
||||
icon = 'icons/obj/clothing/head/helmet.dmi'
|
||||
worn_icon = 'icons/mob/clothing/head/helmet.dmi'
|
||||
icon_state = "godslayer"
|
||||
desc = "The horns and skull of a wendigo, held together by the remaining icy energy of a demonic miner."
|
||||
armor_type = /datum/armor/cloak_godslayer
|
||||
clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL | SNUG_FIT
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
flash_protect = FLASH_PROTECTION_WELDER
|
||||
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF | FREEZE_PROOF
|
||||
@@ -0,0 +1,294 @@
|
||||
// Ash drake suit
|
||||
|
||||
/obj/item/drake_remains
|
||||
name = "drake remains"
|
||||
desc = "The gathered remains of a drake. It still crackles with heat, and smells distinctly of brimstone."
|
||||
icon = 'icons/obj/clothing/head/helmet.dmi'
|
||||
icon_state = "dragon"
|
||||
|
||||
/obj/item/drake_remains/Initialize(mapload)
|
||||
. = ..()
|
||||
add_shared_particles(/particles/bonfire)
|
||||
|
||||
/obj/item/drake_remains/Destroy(force)
|
||||
remove_shared_particles(/particles/bonfire)
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/drake
|
||||
name = "drake armour"
|
||||
icon_state = "dragon"
|
||||
desc = "A suit of armour fashioned from the remains of an ash drake."
|
||||
armor_type = /datum/armor/cloak_drake
|
||||
hoodtype = /obj/item/clothing/head/hooded/cloakhood/drake
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
transparent_protection = HIDEGLOVES|HIDESUITSTORAGE|HIDEJUMPSUIT|HIDESHOES
|
||||
|
||||
/datum/armor/cloak_drake
|
||||
melee = 65
|
||||
bullet = 15
|
||||
laser = 40
|
||||
energy = 40
|
||||
bomb = 70
|
||||
bio = 60
|
||||
fire = 100
|
||||
acid = 100
|
||||
wound = 10
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/drake/Initialize(mapload)
|
||||
. = ..()
|
||||
allowed = GLOB.mining_suit_allowed
|
||||
|
||||
/obj/item/clothing/head/hooded/cloakhood/drake
|
||||
name = "drake helm"
|
||||
icon = 'icons/obj/clothing/head/helmet.dmi'
|
||||
worn_icon = 'icons/mob/clothing/head/helmet.dmi'
|
||||
icon_state = "dragon"
|
||||
desc = "The skull of a dragon."
|
||||
armor_type = /datum/armor/cloak_drake
|
||||
clothing_flags = SNUG_FIT
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
|
||||
// Spectral blade
|
||||
|
||||
/obj/item/melee/ghost_sword
|
||||
name = "\improper spectral blade"
|
||||
desc = "A rusted and dulled blade. It doesn't look like it'd do much damage. It glows weakly."
|
||||
icon = 'icons/obj/weapons/sword.dmi'
|
||||
icon_state = "spectral"
|
||||
inhand_icon_state = "spectral"
|
||||
icon_angle = -45
|
||||
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
|
||||
obj_flags = CONDUCTS_ELECTRICITY
|
||||
sharpness = SHARP_EDGED
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
force = 5 // Breakpoint past which something counts as a weapon, usually
|
||||
throwforce = 5
|
||||
hitsound = 'sound/effects/ghost2.ogg'
|
||||
block_sound = 'sound/items/weapons/parry.ogg'
|
||||
attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "rends")
|
||||
attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "rend")
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
var/list/mob/dead/observer/spirits
|
||||
var/list/alt_continuous = list("stabs", "pierces", "impales")
|
||||
var/list/alt_simple = list("stab", "pierce", "impale")
|
||||
COOLDOWN_DECLARE(summon_cooldown)
|
||||
|
||||
/obj/item/melee/ghost_sword/Initialize(mapload)
|
||||
. = ..()
|
||||
spirits = list()
|
||||
START_PROCESSING(SSobj, src)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
alt_continuous = string_list(alt_continuous)
|
||||
alt_simple = string_list(alt_simple)
|
||||
AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple)
|
||||
AddComponent(\
|
||||
/datum/component/butchering, \
|
||||
speed = 15 SECONDS, \
|
||||
effectiveness = 90, \
|
||||
)
|
||||
|
||||
/obj/item/melee/ghost_sword/Destroy()
|
||||
for(var/mob/dead/observer/ghost in spirits)
|
||||
ghost.RemoveInvisibility(type)
|
||||
spirits.Cut()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
. = ..()
|
||||
|
||||
/obj/item/melee/ghost_sword/attack_self(mob/user)
|
||||
if(!COOLDOWN_FINISHED(src, summon_cooldown))
|
||||
to_chat(user, span_warning("You just recently called out for aid. You don't want to annoy the spirits!"))
|
||||
return
|
||||
|
||||
COOLDOWN_START(src, summon_cooldown, 60 SECONDS)
|
||||
to_chat(user, span_notice("You call out for aid, attempting to summon spirits to your side."))
|
||||
notify_ghosts(
|
||||
"[user] is raising [user.p_their()] [name], calling for your help!",
|
||||
source = user,
|
||||
ignore_key = POLL_IGNORE_SPECTRAL_BLADE,
|
||||
header = "Spectral blade",
|
||||
)
|
||||
|
||||
/obj/item/melee/ghost_sword/process()
|
||||
ghost_check()
|
||||
|
||||
/obj/item/melee/ghost_sword/proc/ghost_check()
|
||||
var/turf/cur_turf = get_turf(src)
|
||||
var/list/contents = cur_turf.get_all_contents()
|
||||
var/mob/dead/observer/current_spirits = list()
|
||||
for(var/atom/random_thing in contents)
|
||||
random_thing.transfer_observers_to(src)
|
||||
|
||||
for(var/mob/dead/observer/ghost in orbiters?.orbiter_list)
|
||||
ghost.SetInvisibility(INVISIBILITY_NONE, id = type, priority = INVISIBILITY_PRIORITY_BASIC_ANTI_INVISIBILITY)
|
||||
current_spirits |= ghost
|
||||
|
||||
for(var/mob/dead/observer/ghost in spirits - current_spirits)
|
||||
ghost.RemoveInvisibility(type)
|
||||
|
||||
var/new_force = clamp((length(current_spirits) * 4), 0, 75)
|
||||
var/old_force = clamp((length(spirits) * 4), 0, 75)
|
||||
force += new_force - old_force
|
||||
spirits = current_spirits
|
||||
return length(spirits)
|
||||
|
||||
/obj/item/melee/ghost_sword/attack(mob/living/target, mob/living/carbon/human/user)
|
||||
var/ghost_counter = ghost_check()
|
||||
user.visible_message(span_danger("[user] strikes with the force of [ghost_counter] vengeful spirits!"))
|
||||
. = ..()
|
||||
|
||||
/obj/item/melee/ghost_sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
|
||||
var/ghost_counter = ghost_check()
|
||||
final_block_chance += clamp((ghost_counter * 5), 0, 75)
|
||||
owner.visible_message(span_danger("[owner] is protected by a ring of [ghost_counter] ghosts!"))
|
||||
return ..()
|
||||
|
||||
// Dragon's blood
|
||||
|
||||
/obj/item/dragons_blood
|
||||
name = "bottle of dragons blood"
|
||||
desc = "You're not actually going to drink this, are you?"
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "vial"
|
||||
|
||||
/obj/item/dragons_blood/attack_self(mob/living/carbon/human/user)
|
||||
if(!istype(user))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/consumer = user
|
||||
var/random = rand(2,4) //SKYRAT EDIT - Commenting out #1 because it makes people invisible.
|
||||
|
||||
switch(random)
|
||||
if(1)
|
||||
to_chat(user, span_danger("Your appearance morphs to that of a very small humanoid ash dragon! You get to look like a freak without the cool abilities."))
|
||||
consumer.dna.features = list(
|
||||
"mcolor" = "#A02720",
|
||||
"tail_lizard" = "Dark Tiger",
|
||||
"tail_human" = "None",
|
||||
"snout" = "Sharp",
|
||||
"horns" = "Curled",
|
||||
"ears" = "None",
|
||||
"wings" = "None",
|
||||
"frills" = "None",
|
||||
"spines" = "Long",
|
||||
"lizard_markings" = "Dark Tiger Body",
|
||||
"legs" = DIGITIGRADE_LEGS,
|
||||
)
|
||||
consumer.eye_color_left = "#FEE5A3"
|
||||
consumer.eye_color_right = "#FEE5A3"
|
||||
consumer.set_species(/datum/species/lizard)
|
||||
if(2)
|
||||
to_chat(user, span_danger("Your flesh begins to melt! Miraculously, you seem fine otherwise."))
|
||||
consumer.set_species(/datum/species/skeleton)
|
||||
if(3)
|
||||
to_chat(user, span_danger("Power courses through you! You can now shift your form at will."))
|
||||
var/datum/action/cooldown/spell/shapeshift/dragon/dragon_shapeshift = new(user.mind || user)
|
||||
dragon_shapeshift.Grant(user)
|
||||
if(4)
|
||||
to_chat(user, span_danger("You feel like you could walk straight through lava now."))
|
||||
ADD_TRAIT(user, TRAIT_LAVA_IMMUNE, type)
|
||||
|
||||
playsound(user,'sound/items/drink.ogg', 30, TRUE)
|
||||
qdel(src)
|
||||
|
||||
// Lava staff
|
||||
|
||||
/obj/item/lava_staff
|
||||
name = "staff of lava"
|
||||
desc = "The ability to fill the emergency shuttle with lava. What more could you want out of life?"
|
||||
icon_state = "lavastaff"
|
||||
inhand_icon_state = "lavastaff"
|
||||
icon_angle = -45
|
||||
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
|
||||
icon = 'icons/obj/weapons/guns/magic.dmi'
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
force = 18
|
||||
damtype = BURN
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
attack_verb_continuous = list("sears", "clubs", "burn")
|
||||
attack_verb_simple = list("sear", "club", "burn")
|
||||
hitsound = 'sound/items/weapons/sear.ogg'
|
||||
var/turf_type = /turf/open/lava/smooth/weak
|
||||
var/transform_string = "lava"
|
||||
var/reset_turf_type = /turf/open/misc/asteroid/basalt
|
||||
var/reset_string = "basalt"
|
||||
var/create_cooldown = 10 SECONDS
|
||||
var/create_delay = 3 SECONDS
|
||||
var/reset_cooldown = 5 SECONDS
|
||||
var/static/list/banned_turfs = typecacheof(list(/turf/open/space, /turf/closed))
|
||||
COOLDOWN_DECLARE(use_cooldown)
|
||||
|
||||
/obj/item/lava_staff/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
||||
if(interacting_with.atom_storage || SHOULD_SKIP_INTERACTION(interacting_with, src, user))
|
||||
return NONE
|
||||
|
||||
return ranged_interact_with_atom(interacting_with, user, modifiers)
|
||||
|
||||
/obj/item/lava_staff/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
||||
if(!COOLDOWN_FINISHED(src, use_cooldown))
|
||||
return NONE
|
||||
|
||||
if(is_type_in_typecache(interacting_with, banned_turfs))
|
||||
return NONE
|
||||
|
||||
if(!(interacting_with in view(user.client.view, get_turf(user))))
|
||||
return NONE
|
||||
|
||||
var/turf/open/target_turf = get_turf(interacting_with)
|
||||
if(!istype(target_turf))
|
||||
return NONE
|
||||
|
||||
if(islava(target_turf))
|
||||
var/old_name = target_turf.name
|
||||
if(target_turf.TerraformTurf(reset_turf_type, flags = CHANGETURF_INHERIT_AIR))
|
||||
return ITEM_INTERACT_FAILURE
|
||||
|
||||
COOLDOWN_START(src, use_cooldown, reset_cooldown)
|
||||
user.visible_message(span_danger("[user] turns \the [old_name] into [reset_string]!"))
|
||||
playsound(target_turf,'sound/effects/magic/fireball.ogg', 200, TRUE)
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
|
||||
var/obj/effect/temp_visual/lavastaff/lava_visual = new /obj/effect/temp_visual/lavastaff(target_turf)
|
||||
lava_visual.alpha = 0
|
||||
animate(lava_visual, alpha = 255, time = create_delay)
|
||||
user.visible_message(span_danger("[user] points [src] at [target_turf]!"))
|
||||
COOLDOWN_START(src, use_cooldown, create_delay + 1)
|
||||
|
||||
if(!do_after(user, create_delay, target_turf))
|
||||
balloon_alert(user, "interrupted!")
|
||||
COOLDOWN_RESET(src, use_cooldown)
|
||||
qdel(lava_visual)
|
||||
return ITEM_INTERACT_FAILURE
|
||||
|
||||
var/old_name = target_turf.name
|
||||
if(!target_turf.TerraformTurf(turf_type, flags = CHANGETURF_INHERIT_AIR))
|
||||
qdel(lava_visual)
|
||||
return ITEM_INTERACT_FAILURE
|
||||
|
||||
user.visible_message(span_danger("[user] turns \the [old_name] into [transform_string]!"))
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] fired the lava staff at [ADMIN_VERBOSEJMP(target_turf)]")
|
||||
user.log_message("fired the lava staff at [AREACOORD(target_turf)].", LOG_ATTACK)
|
||||
COOLDOWN_START(src, use_cooldown, create_cooldown)
|
||||
playsound(target_turf,'sound/effects/magic/fireball.ogg', 200, TRUE)
|
||||
qdel(lava_visual)
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
|
||||
/obj/effect/temp_visual/lavastaff
|
||||
icon_state = "lavastaff_warn"
|
||||
duration = 50
|
||||
|
||||
/turf/open/lava/smooth/weak
|
||||
lava_damage = 10
|
||||
lava_firestacks = 10
|
||||
temperature_damage = 2500
|
||||
@@ -0,0 +1,123 @@
|
||||
// Cleaving saw
|
||||
|
||||
/obj/item/melee/cleaving_saw
|
||||
name = "cleaving saw"
|
||||
desc = "This saw, effective at drawing the blood of beasts, transforms into a long cleaver that makes use of centrifugal force."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
|
||||
icon_state = "cleaving_saw"
|
||||
inhand_icon_state = "cleaving_saw"
|
||||
worn_icon_state = "cleaving_saw"
|
||||
attack_verb_continuous = list("attacks", "saws", "slices", "tears", "lacerates", "rips", "dices", "cuts")
|
||||
attack_verb_simple = list("attack", "saw", "slice", "tear", "lacerate", "rip", "dice", "cut")
|
||||
force = 12
|
||||
throwforce = 20
|
||||
inhand_x_dimension = 64
|
||||
inhand_y_dimension = 64
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
hitsound = 'sound/items/weapons/bladeslice.ogg'
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
sharpness = SHARP_EDGED
|
||||
/// List of factions we deal bonus damage to
|
||||
var/list/nemesis_factions = list(FACTION_MINING, FACTION_BOSS)
|
||||
/// Amount of damage we deal to the above factions
|
||||
var/faction_bonus_force = 30
|
||||
/// Whether the cleaver is actively AoE swiping something.
|
||||
var/swiping = FALSE
|
||||
/// Amount of bleed stacks gained per hit
|
||||
var/bleed_stacks_per_hit = 3
|
||||
/// Force when the saw is opened.
|
||||
var/open_force = 20
|
||||
/// Throwforce when the saw is opened.
|
||||
var/open_throwforce = 20
|
||||
|
||||
/obj/item/melee/cleaving_saw/Initialize(mapload)
|
||||
. = ..()
|
||||
AddComponent( \
|
||||
/datum/component/transforming, \
|
||||
transform_cooldown_time = (CLICK_CD_MELEE * 0.25), \
|
||||
force_on = open_force, \
|
||||
throwforce_on = open_throwforce, \
|
||||
sharpness_on = sharpness, \
|
||||
hitsound_on = hitsound, \
|
||||
w_class_on = w_class, \
|
||||
attack_verb_continuous_on = list("cleaves", "swipes", "slashes", "chops"), \
|
||||
attack_verb_simple_on = list("cleave", "swipe", "slash", "chop"), \
|
||||
)
|
||||
RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform))
|
||||
|
||||
/obj/item/melee/cleaving_saw/examine(mob/user)
|
||||
. = ..()
|
||||
. += span_notice("It is [HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE) ? "open, will cleave enemies in a wide arc and deal additional damage to fauna":"closed, and can be used for rapid consecutive attacks that cause fauna to bleed"].")
|
||||
. += span_notice("Both modes will build up existing bleed effects, doing a burst of high damage if the bleed is built up high enough.")
|
||||
. += span_notice("Transforming it immediately after an attack causes the next attack to come out faster.")
|
||||
|
||||
/obj/item/melee/cleaving_saw/suicide_act(mob/living/user)
|
||||
user.visible_message(span_suicide("[user] is [HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE) ? "closing [src] on [user.p_their()] neck" : "opening [src] into [user.p_their()] chest"]! It looks like [user.p_theyre()] trying to commit suicide!"))
|
||||
attack_self(user)
|
||||
return BRUTELOSS
|
||||
|
||||
/obj/item/melee/cleaving_saw/melee_attack_chain(mob/user, atom/target, params)
|
||||
. = ..()
|
||||
if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
|
||||
user.changeNext_move(CLICK_CD_MELEE * 0.5) //when closed, it attacks very rapidly
|
||||
|
||||
/obj/item/melee/cleaving_saw/attack(mob/living/target, mob/living/carbon/human/user)
|
||||
var/is_open = HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE)
|
||||
if(!is_open || swiping || !target.density || get_turf(target) == get_turf(user))
|
||||
if(!is_open)
|
||||
faction_bonus_force = 0
|
||||
var/is_nemesis_faction = FALSE
|
||||
for(var/found_faction in target.faction)
|
||||
if(found_faction in nemesis_factions)
|
||||
is_nemesis_faction = TRUE
|
||||
force += faction_bonus_force
|
||||
nemesis_effects(user, target)
|
||||
break
|
||||
. = ..()
|
||||
if(is_nemesis_faction)
|
||||
force -= faction_bonus_force
|
||||
if(!is_open)
|
||||
faction_bonus_force = initial(faction_bonus_force)
|
||||
return
|
||||
|
||||
var/turf/user_turf = get_turf(user)
|
||||
var/dir_to_target = get_dir(user_turf, get_turf(target))
|
||||
swiping = TRUE
|
||||
var/static/list/cleaving_saw_cleave_angles = list(0, -45, 45) //so that the animation animates towards the target clicked and not towards a side target
|
||||
for(var/i in cleaving_saw_cleave_angles)
|
||||
var/turf/turf = get_step(user_turf, turn(dir_to_target, i))
|
||||
for(var/mob/living/living_target in turf)
|
||||
if(user.Adjacent(living_target) && living_target.body_position != LYING_DOWN)
|
||||
melee_attack_chain(user, living_target)
|
||||
swiping = FALSE
|
||||
|
||||
/*
|
||||
* If we're attacking [target]s in our nemesis list, apply unique effects.
|
||||
*
|
||||
* user - the mob attacking with the saw
|
||||
* target - the mob being attacked
|
||||
*/
|
||||
/obj/item/melee/cleaving_saw/proc/nemesis_effects(mob/living/user, mob/living/target)
|
||||
if(istype(target, /mob/living/simple_animal/hostile/asteroid/elite))
|
||||
return
|
||||
var/datum/status_effect/stacking/saw_bleed/existing_bleed = target.has_status_effect(/datum/status_effect/stacking/saw_bleed)
|
||||
if(existing_bleed)
|
||||
existing_bleed.add_stacks(bleed_stacks_per_hit)
|
||||
else
|
||||
target.apply_status_effect(/datum/status_effect/stacking/saw_bleed, bleed_stacks_per_hit)
|
||||
|
||||
/*
|
||||
* Signal proc for [COMSIG_TRANSFORMING_ON_TRANSFORM].
|
||||
*
|
||||
* Gives feedback and makes the nextmove after transforming much quicker.
|
||||
*/
|
||||
/obj/item/melee/cleaving_saw/proc/on_transform(obj/item/source, mob/user, active)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
user.changeNext_move(CLICK_CD_MELEE * 0.25)
|
||||
if(user)
|
||||
balloon_alert(user, "[active ? "opened" : "closed"] [src]")
|
||||
playsound(src, 'sound/effects/magic/clockwork/fellowship_armory.ogg', 35, TRUE, frequency = 90000 - (active * 30000))
|
||||
return COMPONENT_NO_DEFAULT_MESSAGE
|
||||
@@ -0,0 +1,415 @@
|
||||
// Mayhem in a bottle
|
||||
|
||||
/obj/item/mayhem
|
||||
name = "mayhem in a bottle"
|
||||
desc = "A magically infused bottle of blood, the scent of which will drive anyone nearby into a murderous frenzy."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "vial"
|
||||
|
||||
/obj/item/mayhem/attack_self(mob/user)
|
||||
if(tgui_alert(user, "Breaking the bottle will cause nearby crewmembers to go into a murderous frenzy. Be sure you know what you are doing...", "Break the bottle?", list("Break it!", "DON'T")) != "Break it!")
|
||||
return
|
||||
|
||||
if(QDELETED(src) || !user.is_holding(src) || user.incapacitated)
|
||||
return
|
||||
|
||||
for(var/mob/living/carbon/human/target in range(7, user))
|
||||
target.apply_status_effect(/datum/status_effect/mayhem)
|
||||
|
||||
to_chat(user, span_notice("You shatter the bottle!"))
|
||||
playsound(user.loc, 'sound/effects/glass/glassbr1.ogg', 100, TRUE)
|
||||
message_admins(span_adminnotice("[ADMIN_LOOKUPFLW(user)] has activated a bottle of mayhem!"))
|
||||
user.log_message("activated a bottle of mayhem", LOG_ATTACK)
|
||||
qdel(src)
|
||||
|
||||
// H.E.C.K. Suit
|
||||
|
||||
/obj/item/clothing/suit/hooded/hostile_environment
|
||||
name = "H.E.C.K. suit"
|
||||
desc = "Hostile Environment Cross-Kinetic Suit: A suit designed to withstand the wide variety of hazards from Lavaland. It wasn't enough for its last owner."
|
||||
icon_state = "hostile_env"
|
||||
hoodtype = /obj/item/clothing/head/hooded/hostile_environment
|
||||
armor_type = /datum/armor/hooded_hostile_environment
|
||||
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
clothing_flags = THICKMATERIAL|HEADINTERNALS
|
||||
resistance_flags = FIRE_PROOF|LAVA_PROOF|ACID_PROOF
|
||||
transparent_protection = HIDESUITSTORAGE|HIDEJUMPSUIT
|
||||
allowed = null
|
||||
greyscale_colors = "#4d4d4d#808080"
|
||||
greyscale_config = /datum/greyscale_config/heck_suit
|
||||
greyscale_config_worn = /datum/greyscale_config/heck_suit/worn
|
||||
greyscale_config_worn_digi = /datum/greyscale_config/heck_suit/worn/digi //SKYRAT EDIT ADDITION - DigiGreyscale
|
||||
flags_1 = IS_PLAYER_COLORABLE_1
|
||||
|
||||
/datum/armor/hooded_hostile_environment
|
||||
melee = 70
|
||||
bullet = 40
|
||||
laser = 10
|
||||
energy = 20
|
||||
bomb = 50
|
||||
fire = 100
|
||||
acid = 100
|
||||
|
||||
/obj/item/clothing/suit/hooded/hostile_environment/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/radiation_protected_clothing)
|
||||
AddElement(/datum/element/gags_recolorable)
|
||||
allowed = GLOB.mining_suit_allowed
|
||||
|
||||
/obj/item/clothing/suit/hooded/hostile_environment/process(seconds_per_tick)
|
||||
var/mob/living/carbon/wearer = loc
|
||||
if(istype(wearer) && SPT_PROB(1, seconds_per_tick)) //cursed by bubblegum
|
||||
if(prob(7.5))
|
||||
wearer.cause_hallucination(/datum/hallucination/oh_yeah, "H.E.C.K suit", haunt_them = TRUE)
|
||||
else
|
||||
if(HAS_TRAIT(wearer, TRAIT_ANOSMIA)) //Anosmia quirk holder cannot fell any smell
|
||||
to_chat(wearer, span_warning("[pick("You hear faint whispers.","You feel hot.","You hear a roar in the distance.")]"))
|
||||
else
|
||||
to_chat(wearer, span_warning("[pick("You hear faint whispers.","You smell ash.","You feel hot.","You hear a roar in the distance.")]"))
|
||||
|
||||
/obj/item/clothing/head/hooded/hostile_environment
|
||||
name = "H.E.C.K. helmet"
|
||||
desc = "Hostile Environment Cross-Kinetic Helmet: A helmet designed to withstand the wide variety of hazards from Lavaland. It wasn't enough for its last owner."
|
||||
icon = 'icons/obj/clothing/head/helmet.dmi'
|
||||
worn_icon = 'icons/mob/clothing/head/helmet.dmi'
|
||||
icon_state = "hostile_env"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
armor_type = /datum/armor/hooded_hostile_environment
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
clothing_flags = SNUG_FIT|THICKMATERIAL
|
||||
resistance_flags = FIRE_PROOF|LAVA_PROOF|ACID_PROOF
|
||||
flags_inv = HIDEMASK|HIDEEARS|HIDEFACE|HIDEEYES|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
|
||||
flags_cover = HEADCOVERSMOUTH
|
||||
actions_types = list()
|
||||
greyscale_colors = "#4d4d4d#808080#ff3300"
|
||||
greyscale_config = /datum/greyscale_config/heck_helmet
|
||||
greyscale_config_worn = /datum/greyscale_config/heck_helmet/worn
|
||||
greyscale_config_worn_digi = /datum/greyscale_config/heck_helmet/worn/snouted //SKYRAT EDIT ADDITION - MuzzledGreyscale (Why does this use worn_digi)
|
||||
flags_1 = IS_PLAYER_COLORABLE_1
|
||||
|
||||
/obj/item/clothing/head/hooded/hostile_environment/Initialize(mapload)
|
||||
. = ..()
|
||||
update_appearance()
|
||||
AddComponent(/datum/component/butchering/wearable, \
|
||||
speed = 0.5 SECONDS, \
|
||||
effectiveness = 150, \
|
||||
bonus_modifier = 0, \
|
||||
butcher_sound = null, \
|
||||
disabled = null, \
|
||||
can_be_blunt = TRUE, \
|
||||
butcher_callback = CALLBACK(src, PROC_REF(consume)), \
|
||||
)
|
||||
AddElement(/datum/element/radiation_protected_clothing)
|
||||
AddElement(/datum/element/gags_recolorable)
|
||||
|
||||
/obj/item/clothing/head/hooded/hostile_environment/equipped(mob/user, slot, initial = FALSE)
|
||||
. = ..()
|
||||
to_chat(user, span_notice("You feel a bloodlust. You can now butcher corpses with your bare arms."))
|
||||
|
||||
/obj/item/clothing/head/hooded/hostile_environment/dropped(mob/user, silent = FALSE)
|
||||
. = ..()
|
||||
to_chat(user, span_notice("You lose your bloodlust."))
|
||||
|
||||
/obj/item/clothing/head/hooded/hostile_environment/proc/consume(mob/living/user, mob/living/butchered)
|
||||
if(butchered.mob_biotypes & (MOB_ROBOTIC | MOB_SPIRIT))
|
||||
return
|
||||
var/health_consumed = butchered.maxHealth * 0.1
|
||||
user.heal_ordered_damage(health_consumed, list(BRUTE, BURN, TOX))
|
||||
to_chat(user, span_notice("You heal from the corpse of [butchered]."))
|
||||
var/datum/client_colour/color_effect = user.add_client_colour(/datum/client_colour/bloodlust)
|
||||
QDEL_IN(color_effect, 1 SECONDS)
|
||||
|
||||
// Soulscythe
|
||||
|
||||
#define MAX_BLOOD_LEVEL 100
|
||||
|
||||
/obj/item/soulscythe
|
||||
name = "soulscythe"
|
||||
desc = "An old relic of hell created by devils to establish themselves as the leadership of hell over the demons. It grows stronger while it possesses a powerful soul."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "soulscythe"
|
||||
inhand_icon_state = "soulscythe"
|
||||
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
|
||||
attack_verb_continuous = list("chops", "slices", "cuts", "reaps")
|
||||
attack_verb_simple = list("chop", "slice", "cut", "reap")
|
||||
hitsound = 'sound/items/weapons/bladeslice.ogg'
|
||||
inhand_x_dimension = 64
|
||||
inhand_y_dimension = 64
|
||||
force = 20
|
||||
throwforce = 17
|
||||
armour_penetration = 50
|
||||
sharpness = SHARP_EDGED
|
||||
bare_wound_bonus = 10
|
||||
layer = MOB_LAYER
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
/// Soulscythe mob in the scythe
|
||||
var/mob/living/simple_animal/soulscythe/soul
|
||||
/// Are we grabbing a spirit?
|
||||
var/using = FALSE
|
||||
/// Currently charging?
|
||||
var/charging = FALSE
|
||||
/// Cooldown between moves
|
||||
COOLDOWN_DECLARE(move_cooldown)
|
||||
/// Cooldown between attacks
|
||||
COOLDOWN_DECLARE(attack_cooldown)
|
||||
|
||||
/obj/item/soulscythe/Initialize(mapload)
|
||||
. = ..()
|
||||
soul = new(src)
|
||||
RegisterSignal(soul, COMSIG_LIVING_RESIST, PROC_REF(on_resist))
|
||||
RegisterSignal(soul, COMSIG_MOB_ATTACK_RANGED, PROC_REF(on_attack))
|
||||
RegisterSignal(soul, COMSIG_MOB_ATTACK_RANGED_SECONDARY, PROC_REF(on_secondary_attack))
|
||||
RegisterSignal(src, COMSIG_ATOM_INTEGRITY_CHANGED, PROC_REF(on_integrity_change))
|
||||
AddElement(/datum/element/bane, mob_biotypes = MOB_PLANT, damage_multiplier = 0.5, requires_combat_mode = FALSE)
|
||||
|
||||
/obj/item/soulscythe/examine(mob/user)
|
||||
. = ..()
|
||||
. += soul.ckey ? span_nicegreen("There is a soul inhabiting it.") : span_danger("It's dormant.")
|
||||
|
||||
/obj/item/soulscythe/attack(mob/living/attacked, mob/living/user, params)
|
||||
. = ..()
|
||||
if(attacked.stat != DEAD)
|
||||
give_blood(10)
|
||||
|
||||
/obj/item/soulscythe/attack_hand(mob/user, list/modifiers)
|
||||
if(soul.ckey && !soul.faction_check_atom(user))
|
||||
to_chat(user, span_warning("You can't pick up [src]!"))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/item/soulscythe/pickup(mob/user)
|
||||
. = ..()
|
||||
if(soul.ckey)
|
||||
animate(src) //stop spinnage
|
||||
|
||||
/obj/item/soulscythe/dropped(mob/user, silent)
|
||||
. = ..()
|
||||
if(soul.ckey)
|
||||
reset_spin() //resume spinnage
|
||||
|
||||
/obj/item/soulscythe/attack_self(mob/user, modifiers)
|
||||
if(using || soul.ckey || soul.stat)
|
||||
return
|
||||
if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE))
|
||||
balloon_alert(user, "you can't awaken the scythe!")
|
||||
return
|
||||
using = TRUE
|
||||
balloon_alert(user, "you hold the scythe up...")
|
||||
ADD_TRAIT(src, TRAIT_NODROP, type)
|
||||
var/mob/chosen_one = SSpolling.poll_ghosts_for_target(
|
||||
check_jobban = ROLE_PAI,
|
||||
poll_time = 20 SECONDS,
|
||||
checked_target = src,
|
||||
ignore_category = POLL_IGNORE_POSSESSED_BLADE,
|
||||
alert_pic = src,
|
||||
role_name_text = "soulscythe soul",
|
||||
chat_text_border_icon = src,
|
||||
)
|
||||
on_poll_concluded(user, chosen_one)
|
||||
|
||||
/// Ghost poll has concluded and a candidate has been chosen.
|
||||
/obj/item/soulscythe/proc/on_poll_concluded(mob/living/master, mob/dead/observer/ghost)
|
||||
if(isnull(ghost))
|
||||
balloon_alert(master, "the scythe is dormant!")
|
||||
REMOVE_TRAIT(src, TRAIT_NODROP, type)
|
||||
using = FALSE
|
||||
return
|
||||
|
||||
soul.PossessByPlayer(ghost.ckey)
|
||||
soul.copy_languages(master, LANGUAGE_MASTER) //Make sure the sword can understand and communicate with the master.
|
||||
soul.faction = list("[REF(master)]")
|
||||
balloon_alert(master, "the scythe glows")
|
||||
add_overlay("soulscythe_gem")
|
||||
density = TRUE
|
||||
if(!ismob(loc))
|
||||
reset_spin()
|
||||
|
||||
REMOVE_TRAIT(src, TRAIT_NODROP, type)
|
||||
using = FALSE
|
||||
|
||||
/obj/item/soulscythe/relaymove(mob/living/user, direction)
|
||||
if(!COOLDOWN_FINISHED(src, move_cooldown) || charging)
|
||||
return
|
||||
if(!isturf(loc))
|
||||
balloon_alert(user, "resist out!")
|
||||
COOLDOWN_START(src, move_cooldown, 1 SECONDS)
|
||||
return
|
||||
if(!use_blood(1, FALSE))
|
||||
return
|
||||
if(pixel_x != base_pixel_x || pixel_y != base_pixel_y)
|
||||
animate(src, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y, flags = ANIMATION_PARALLEL)
|
||||
try_step_multiz(direction)
|
||||
COOLDOWN_START(src, move_cooldown, (direction in GLOB.cardinals) ? 0.1 SECONDS : 0.2 SECONDS)
|
||||
|
||||
/obj/item/soulscythe/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
||||
. = ..()
|
||||
if(!charging)
|
||||
return
|
||||
charging = FALSE
|
||||
throwforce *= 0.5
|
||||
reset_spin()
|
||||
if(ismineralturf(hit_atom))
|
||||
var/turf/closed/mineral/hit_rock = hit_atom
|
||||
hit_rock.gets_drilled()
|
||||
if(isliving(hit_atom))
|
||||
var/mob/living/hit_mob = hit_atom
|
||||
if(hit_mob.stat != DEAD)
|
||||
give_blood(15)
|
||||
|
||||
/obj/item/soulscythe/AllowClick()
|
||||
return TRUE
|
||||
|
||||
/obj/item/soulscythe/proc/use_blood(amount = 0, message = TRUE)
|
||||
if(amount > soul.blood_level)
|
||||
if(message)
|
||||
to_chat(soul, span_warning("Not enough blood!"))
|
||||
return FALSE
|
||||
soul.blood_level -= amount
|
||||
return TRUE
|
||||
|
||||
/obj/item/soulscythe/proc/give_blood(amount)
|
||||
soul.blood_level = min(MAX_BLOOD_LEVEL, soul.blood_level + amount)
|
||||
|
||||
/obj/item/soulscythe/proc/on_resist(mob/living/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(isturf(loc))
|
||||
return
|
||||
INVOKE_ASYNC(src, PROC_REF(break_out))
|
||||
|
||||
/obj/item/soulscythe/proc/break_out()
|
||||
if(!use_blood(10))
|
||||
return
|
||||
balloon_alert(soul, "you resist...")
|
||||
if(!do_after(soul, 5 SECONDS, target = src, timed_action_flags = IGNORE_TARGET_LOC_CHANGE))
|
||||
balloon_alert(soul, "interrupted!")
|
||||
return
|
||||
balloon_alert(soul, "you break out")
|
||||
if(ismob(loc))
|
||||
var/mob/holder = loc
|
||||
holder.temporarilyRemoveItemFromInventory(src)
|
||||
forceMove(drop_location())
|
||||
|
||||
/obj/item/soulscythe/proc/on_integrity_change(datum/source, old_value, new_value)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
soul.set_health(new_value)
|
||||
|
||||
/obj/item/soulscythe/proc/on_attack(mob/living/source, atom/attacked_atom, modifiers)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!COOLDOWN_FINISHED(src, attack_cooldown) || !isturf(loc))
|
||||
return
|
||||
if(get_dist(source, attacked_atom) > 1)
|
||||
INVOKE_ASYNC(src, PROC_REF(shoot_target), attacked_atom)
|
||||
else
|
||||
INVOKE_ASYNC(src, PROC_REF(slash_target), attacked_atom)
|
||||
|
||||
/obj/item/soulscythe/proc/on_secondary_attack(mob/living/source, atom/attacked_atom, modifiers)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!COOLDOWN_FINISHED(src, attack_cooldown) || !isturf(loc))
|
||||
return
|
||||
INVOKE_ASYNC(src, PROC_REF(charge_target), attacked_atom)
|
||||
|
||||
/obj/item/soulscythe/proc/shoot_target(atom/attacked_atom)
|
||||
if(!use_blood(15))
|
||||
return
|
||||
COOLDOWN_START(src, attack_cooldown, 3 SECONDS)
|
||||
var/obj/projectile/projectile = new /obj/projectile/soulscythe(get_turf(src))
|
||||
projectile.aim_projectile(attacked_atom, src)
|
||||
projectile.firer = src
|
||||
projectile.fire(null, attacked_atom)
|
||||
visible_message(span_danger("[src] fires at [attacked_atom]!"), span_notice("You fire at [attacked_atom]!"))
|
||||
playsound(src, 'sound/effects/magic/fireball.ogg', 50, TRUE)
|
||||
|
||||
/obj/item/soulscythe/proc/slash_target(atom/attacked_atom)
|
||||
if(isliving(attacked_atom) && use_blood(10))
|
||||
var/mob/living/attacked_mob = attacked_atom
|
||||
if(attacked_mob.stat != DEAD)
|
||||
give_blood(15)
|
||||
attacked_mob.apply_damage(damage = force * (ishostile(attacked_mob) ? 2 : 1), sharpness = SHARP_EDGED, bare_wound_bonus = 5)
|
||||
to_chat(attacked_mob, span_userdanger("You're slashed by [src]!"))
|
||||
else if((ismachinery(attacked_atom) || isstructure(attacked_atom)) && use_blood(5))
|
||||
var/obj/attacked_obj = attacked_atom
|
||||
attacked_obj.take_damage(force, BRUTE, MELEE, FALSE)
|
||||
else
|
||||
return
|
||||
COOLDOWN_START(src, attack_cooldown, 1 SECONDS)
|
||||
animate(src)
|
||||
SpinAnimation(5)
|
||||
addtimer(CALLBACK(src, PROC_REF(reset_spin)), 1 SECONDS)
|
||||
visible_message(span_danger("[src] slashes [attacked_atom]!"), span_notice("You slash [attacked_atom]!"))
|
||||
playsound(src, 'sound/items/weapons/bladeslice.ogg', 50, TRUE)
|
||||
do_attack_animation(attacked_atom, ATTACK_EFFECT_SLASH)
|
||||
|
||||
/obj/item/soulscythe/proc/charge_target(atom/attacked_atom)
|
||||
if(charging || !use_blood(30))
|
||||
return
|
||||
COOLDOWN_START(src, attack_cooldown, 5 SECONDS)
|
||||
animate(src)
|
||||
charging = TRUE
|
||||
visible_message(span_danger("[src] starts charging..."))
|
||||
balloon_alert(soul, "you start charging...")
|
||||
if(!do_after(soul, 2 SECONDS, target = src, timed_action_flags = IGNORE_TARGET_LOC_CHANGE))
|
||||
balloon_alert(soul, "interrupted!")
|
||||
return
|
||||
visible_message(span_danger("[src] charges at [attacked_atom]!"), span_notice("You charge at [attacked_atom]!"))
|
||||
new /obj/effect/temp_visual/mook_dust(get_turf(src))
|
||||
playsound(src, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE)
|
||||
SpinAnimation(1)
|
||||
throwforce *= 2
|
||||
throw_at(attacked_atom, 10, 3, soul, FALSE)
|
||||
|
||||
/obj/item/soulscythe/proc/reset_spin()
|
||||
animate(src)
|
||||
SpinAnimation(15)
|
||||
|
||||
/obj/item/soulscythe/Destroy(force)
|
||||
soul.ghostize()
|
||||
QDEL_NULL(soul)
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/soulscythe
|
||||
name = "mysterious spirit"
|
||||
maxHealth = 200
|
||||
health = 200
|
||||
gender = NEUTER
|
||||
mob_biotypes = MOB_SPIRIT
|
||||
faction = list()
|
||||
weather_immunities = list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE)
|
||||
/// Blood level, used for movement and abilities in a soulscythe
|
||||
var/blood_level = MAX_BLOOD_LEVEL
|
||||
|
||||
/mob/living/simple_animal/soulscythe/get_status_tab_items()
|
||||
. = ..()
|
||||
. += "Blood: [blood_level]/[MAX_BLOOD_LEVEL]"
|
||||
|
||||
/mob/living/simple_animal/soulscythe/Life(seconds_per_tick, times_fired)
|
||||
. = ..()
|
||||
if(!stat)
|
||||
blood_level = min(MAX_BLOOD_LEVEL, blood_level + round(1 * seconds_per_tick))
|
||||
|
||||
/obj/projectile/soulscythe
|
||||
name = "soulslash"
|
||||
icon_state = "soulslash"
|
||||
armor_flag = MELEE //jokair
|
||||
damage = 15
|
||||
light_range = 1
|
||||
light_power = 1
|
||||
light_color = LIGHT_COLOR_BLOOD_MAGIC
|
||||
|
||||
/obj/projectile/soulscythe/on_hit(atom/target, blocked = 0, pierce_hit)
|
||||
if(ishostile(target))
|
||||
damage *= 2
|
||||
return ..()
|
||||
|
||||
#undef MAX_BLOOD_LEVEL
|
||||
@@ -0,0 +1,441 @@
|
||||
// Divine vocal cords
|
||||
|
||||
/obj/item/organ/vocal_cords/colossus
|
||||
name = "divine vocal cords"
|
||||
desc = "They carry the voice of an ancient god."
|
||||
icon_state = "voice_of_god"
|
||||
actions_types = list(/datum/action/item_action/organ_action/colossus)
|
||||
var/next_command = 0
|
||||
var/cooldown_mod = 1
|
||||
var/base_multiplier = 1
|
||||
spans = list("colossus","yell")
|
||||
|
||||
/datum/action/item_action/organ_action/colossus
|
||||
name = "Voice of God"
|
||||
var/obj/item/organ/vocal_cords/colossus/cords = null
|
||||
|
||||
/datum/action/item_action/organ_action/colossus/New()
|
||||
..()
|
||||
cords = target
|
||||
|
||||
/datum/action/item_action/organ_action/colossus/IsAvailable(feedback = FALSE)
|
||||
if(!owner)
|
||||
return FALSE
|
||||
if(world.time < cords.next_command)
|
||||
if (feedback)
|
||||
owner.balloon_alert(owner, "wait [DisplayTimeText(cords.next_command - world.time)]!")
|
||||
return FALSE
|
||||
if(isliving(owner))
|
||||
var/mob/living/living = owner
|
||||
if(!living.can_speak())
|
||||
if (feedback)
|
||||
owner.balloon_alert(owner, "can't speak!")
|
||||
return FALSE
|
||||
if(check_flags & AB_CHECK_CONSCIOUS)
|
||||
if(owner.stat)
|
||||
if (feedback)
|
||||
owner.balloon_alert(owner, "unconscious!")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/action/item_action/organ_action/colossus/do_effect(trigger_flags)
|
||||
var/command = tgui_input_text(owner, "Speak with the Voice of God", "Command", max_length = MAX_MESSAGE_LEN)
|
||||
if(!command)
|
||||
return FALSE
|
||||
if(QDELETED(src) || QDELETED(owner))
|
||||
return FALSE
|
||||
owner.say(".x[command]")
|
||||
return TRUE
|
||||
|
||||
/obj/item/organ/vocal_cords/colossus/can_speak_with()
|
||||
if(!owner)
|
||||
return FALSE
|
||||
|
||||
if(world.time < next_command)
|
||||
to_chat(owner, span_notice("You must wait [DisplayTimeText(next_command - world.time)] before Speaking again."))
|
||||
return FALSE
|
||||
|
||||
return owner.can_speak()
|
||||
|
||||
/obj/item/organ/vocal_cords/colossus/handle_speech(message)
|
||||
playsound(get_turf(owner), 'sound/effects/magic/clockwork/invoke_general.ogg', 300, TRUE, 5)
|
||||
return //voice of god speaks for us
|
||||
|
||||
/obj/item/organ/vocal_cords/colossus/speak_with(message)
|
||||
var/cooldown = voice_of_god(uppertext(message), owner, spans, base_multiplier)
|
||||
next_command = world.time + (cooldown * cooldown_mod)
|
||||
|
||||
// Anomalous crystal
|
||||
|
||||
#define ACTIVATE_TOUCH "touch"
|
||||
#define ACTIVATE_SPEECH "speech"
|
||||
#define ACTIVATE_HEAT "heat"
|
||||
#define ACTIVATE_BULLET "bullet"
|
||||
#define ACTIVATE_ENERGY "energy"
|
||||
#define ACTIVATE_BOMB "bomb"
|
||||
#define ACTIVATE_MOB_BUMP "bumping"
|
||||
#define ACTIVATE_WEAPON "weapon"
|
||||
#define ACTIVATE_MAGIC "magic"
|
||||
|
||||
/obj/machinery/anomalous_crystal
|
||||
name = "anomalous crystal"
|
||||
desc = "A strange chunk of crystal, being in the presence of it fills you with equal parts excitement and dread."
|
||||
var/observer_desc = "Anomalous crystals have descriptions that only observers can see. But this one hasn't been changed from the default."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "anomaly_crystal"
|
||||
light_range = 8
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
use_power = NO_POWER_USE
|
||||
anchored = FALSE
|
||||
density = TRUE
|
||||
var/activation_method
|
||||
var/list/possible_methods = list(ACTIVATE_TOUCH, ACTIVATE_SPEECH, ACTIVATE_HEAT, ACTIVATE_BULLET, ACTIVATE_ENERGY, ACTIVATE_BOMB, ACTIVATE_MOB_BUMP, ACTIVATE_WEAPON, ACTIVATE_MAGIC)
|
||||
var/activation_damage_type = null
|
||||
/// Cooldown on this crystal
|
||||
var/cooldown_add = 3 SECONDS
|
||||
/// Time needed to use this crystal
|
||||
var/use_time = 0
|
||||
/// If we are being used
|
||||
var/active = FALSE
|
||||
var/activation_sound = 'sound/effects/break_stone.ogg'
|
||||
COOLDOWN_DECLARE(cooldown_timer)
|
||||
|
||||
/obj/machinery/anomalous_crystal/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!activation_method)
|
||||
activation_method = pick(possible_methods)
|
||||
become_hearing_sensitive(trait_source = ROUNDSTART_TRAIT)
|
||||
|
||||
/obj/machinery/anomalous_crystal/examine(mob/user)
|
||||
. = ..()
|
||||
if(isobserver(user))
|
||||
. += observer_desc
|
||||
. += "It is activated by [activation_method]."
|
||||
|
||||
/obj/machinery/anomalous_crystal/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, list/message_mods = list(), message_range)
|
||||
. = ..()
|
||||
if(isliving(speaker))
|
||||
ActivationReaction(speaker, ACTIVATE_SPEECH)
|
||||
|
||||
/obj/machinery/anomalous_crystal/attack_hand(mob/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
ActivationReaction(user, ACTIVATE_TOUCH)
|
||||
|
||||
/obj/machinery/anomalous_crystal/attackby(obj/item/I, mob/user, params)
|
||||
if(I.get_temperature())
|
||||
ActivationReaction(user, ACTIVATE_HEAT)
|
||||
else
|
||||
ActivationReaction(user, ACTIVATE_WEAPON)
|
||||
..()
|
||||
|
||||
/obj/machinery/anomalous_crystal/bullet_act(obj/projectile/proj, def_zone)
|
||||
. = ..()
|
||||
if(istype(proj, /obj/projectile/magic))
|
||||
ActivationReaction(proj.firer, ACTIVATE_MAGIC, proj.damage_type)
|
||||
return
|
||||
ActivationReaction(proj.firer, proj.armor_flag, proj.damage_type)
|
||||
|
||||
/obj/machinery/anomalous_crystal/proc/ActivationReaction(mob/user, method, damtype)
|
||||
if(!COOLDOWN_FINISHED(src, cooldown_timer))
|
||||
return FALSE
|
||||
if(activation_damage_type && activation_damage_type != damtype)
|
||||
return FALSE
|
||||
if(method != activation_method)
|
||||
return FALSE
|
||||
if(active)
|
||||
return FALSE
|
||||
if(use_time)
|
||||
charge_animation()
|
||||
COOLDOWN_START(src, cooldown_timer, cooldown_add)
|
||||
playsound(user, activation_sound, 100, TRUE)
|
||||
log_game("[src] activated by [key_name(user)] in [AREACOORD(src)]. The last fingerprints on the [src] was [fingerprintslast].")
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/anomalous_crystal/proc/charge_animation()
|
||||
icon_state = "anomaly_crystal_charging"
|
||||
active = TRUE
|
||||
set_anchored(TRUE)
|
||||
balloon_alert_to_viewers("charging...")
|
||||
playsound(src, 'sound/effects/magic/disable_tech.ogg', 50, TRUE)
|
||||
sleep(use_time)
|
||||
icon_state = initial(icon_state)
|
||||
active = FALSE
|
||||
set_anchored(FALSE)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/anomalous_crystal/Bumped(atom/movable/AM)
|
||||
..()
|
||||
if(ismob(AM))
|
||||
ActivationReaction(AM, ACTIVATE_MOB_BUMP)
|
||||
|
||||
/obj/machinery/anomalous_crystal/ex_act()
|
||||
ActivationReaction(null, ACTIVATE_BOMB)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/anomalous_crystal/honk //Revives the dead, but strips and equips them as a clown
|
||||
observer_desc = "This crystal revives targets around it as clowns. Oh, that's horrible...."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
activation_sound = 'sound/items/bikehorn.ogg'
|
||||
use_time = 3 SECONDS
|
||||
/// List of REFs to mobs that have been turned into a clown
|
||||
var/list/clowned_mob_refs = list()
|
||||
|
||||
/obj/machinery/anomalous_crystal/honk/ActivationReaction(mob/user)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
|
||||
for(var/atom/thing as anything in range(1, src))
|
||||
if(isturf(thing))
|
||||
new /obj/effect/decal/cleanable/confetti(thing)
|
||||
continue
|
||||
|
||||
if(!ishuman(thing))
|
||||
continue
|
||||
|
||||
var/mob/living/carbon/human/new_clown = thing
|
||||
|
||||
if(new_clown.stat != DEAD)
|
||||
continue
|
||||
|
||||
new_clown.revive(ADMIN_HEAL_ALL, force_grab_ghost = TRUE)
|
||||
|
||||
var/clown_ref = REF(new_clown)
|
||||
if(clown_ref in clowned_mob_refs) //one clowning per person
|
||||
continue
|
||||
|
||||
for(var/obj/item/to_strip in new_clown.get_equipped_items())
|
||||
new_clown.dropItemToGround(to_strip)
|
||||
new_clown.dress_up_as_job(SSjob.get_job_type(/datum/job/clown))
|
||||
clowned_mob_refs += clown_ref
|
||||
|
||||
return TRUE
|
||||
|
||||
/// Transforms the area to look like a new one
|
||||
/obj/machinery/anomalous_crystal/theme_warp
|
||||
observer_desc = "This crystal warps the area around it to a theme."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
cooldown_add = 20 SECONDS
|
||||
use_time = 5 SECONDS
|
||||
/// Theme which we turn areas into on activation
|
||||
var/datum/dimension_theme/terrain_theme
|
||||
/// List of all areas we've affected
|
||||
var/list/converted_areas = list()
|
||||
|
||||
/obj/machinery/anomalous_crystal/theme_warp/Initialize(mapload)
|
||||
. = ..()
|
||||
terrain_theme = SSmaterials.dimensional_themes[pick(subtypesof(/datum/dimension_theme))]
|
||||
observer_desc = "This crystal changes the area around it to match the theme of \"[terrain_theme.name]\"."
|
||||
|
||||
/obj/machinery/anomalous_crystal/theme_warp/ActivationReaction(mob/user, method)
|
||||
. = ..()
|
||||
if (!.)
|
||||
return FALSE
|
||||
var/area/current_area = get_area(src)
|
||||
if (current_area in converted_areas)
|
||||
return FALSE
|
||||
terrain_theme.apply_theme_to_list_of_turfs(current_area.get_turfs_from_all_zlevels())
|
||||
converted_areas += current_area
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/anomalous_crystal/theme_warp/Destroy()
|
||||
terrain_theme = null
|
||||
converted_areas.Cut()
|
||||
return ..()
|
||||
|
||||
/obj/machinery/anomalous_crystal/emitter //Generates a projectile when interacted with
|
||||
observer_desc = "This crystal generates a projectile when activated."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
cooldown_add = 5 SECONDS
|
||||
var/obj/projectile/generated_projectile = /obj/projectile/colossus
|
||||
|
||||
/obj/machinery/anomalous_crystal/emitter/Initialize(mapload)
|
||||
. = ..()
|
||||
observer_desc = "This crystal generates \a [initial(generated_projectile.name)] when activated."
|
||||
|
||||
/obj/machinery/anomalous_crystal/emitter/ActivationReaction(mob/user, method)
|
||||
if(..())
|
||||
var/obj/projectile/proj = new generated_projectile(get_turf(src))
|
||||
proj.firer = src
|
||||
proj.fire(dir2angle(dir))
|
||||
|
||||
/obj/machinery/anomalous_crystal/dark_reprise //Revives anyone nearby, but turns them into shadowpeople and renders them uncloneable, so the crystal is your only hope of getting up again if you go down.
|
||||
observer_desc = "When activated, this crystal revives anyone nearby, but turns them into Shadowpeople and makes them unclonable, making the crystal their only hope of getting up again."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
activation_sound = 'sound/effects/hallucinations/growl1.ogg'
|
||||
use_time = 3 SECONDS
|
||||
|
||||
/obj/machinery/anomalous_crystal/dark_reprise/ActivationReaction(mob/user, method)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
|
||||
for(var/atom/thing as anything in range(1, src))
|
||||
if(isturf(thing))
|
||||
new /obj/effect/temp_visual/cult/sparks(thing)
|
||||
continue
|
||||
|
||||
if(!ishuman(thing))
|
||||
continue
|
||||
|
||||
var/mob/living/carbon/human/to_revive = thing
|
||||
|
||||
if(to_revive.stat != DEAD)
|
||||
continue
|
||||
|
||||
to_revive.set_species(/datum/species/shadow, TRUE)
|
||||
to_revive.revive(ADMIN_HEAL_ALL, force_grab_ghost = TRUE)
|
||||
//Free revives, but significantly limits your options for reviving except via the crystal
|
||||
//except JK who cares about BADDNA anymore. this even heals suicides.
|
||||
ADD_TRAIT(to_revive, TRAIT_BADDNA, MAGIC_TRAIT)
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/anomalous_crystal/helpers //Lets ghost spawn as helpful creatures that can only heal people slightly. Incredibly fragile and they can't converse with humans
|
||||
observer_desc = "This crystal allows ghosts to turn into a fragile creature that can heal people."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
activation_sound = 'sound/effects/ghost2.ogg'
|
||||
use_time = 5 SECONDS
|
||||
var/ready_to_deploy = FALSE
|
||||
|
||||
/obj/machinery/anomalous_crystal/helpers/ActivationReaction(mob/user, method)
|
||||
if(..() && !ready_to_deploy)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
ready_to_deploy = TRUE
|
||||
notify_ghosts(
|
||||
"An anomalous crystal has been activated in [get_area(src)]! This crystal can always be used by ghosts hereafter.",
|
||||
source = src,
|
||||
header = "Anomalous crystal activated",
|
||||
click_interact = TRUE,
|
||||
ghost_sound = 'sound/effects/ghost2.ogg',
|
||||
)
|
||||
|
||||
/obj/machinery/anomalous_crystal/helpers/attack_ghost(mob/dead/observer/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(ready_to_deploy)
|
||||
var/be_helper = tgui_alert(usr, "Become a Lightgeist? (Warning, You can no longer be revived!)", "Lightgeist Deployment", list("Yes", "No"))
|
||||
if((be_helper == "Yes") && !QDELETED(src) && isobserver(user))
|
||||
var/mob/living/basic/lightgeist/deployable = new(get_turf(loc))
|
||||
deployable.PossessByPlayer(user.key)
|
||||
|
||||
/obj/machinery/anomalous_crystal/possessor //Allows you to bodyjack small animals, then exit them at your leisure, but you can only do this once per activation. Because they blow up. Also, if the bodyjacked animal dies, SO DO YOU.
|
||||
observer_desc = "When activated, this crystal allows you to take over small animals, and then exit them at the possessors leisure. Exiting the animal kills it, and if you die while possessing the animal, you die as well."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
use_time = 1 SECONDS
|
||||
|
||||
/obj/machinery/anomalous_crystal/possessor/ActivationReaction(mob/user, method)
|
||||
. = ..()
|
||||
if (!. || !ishuman(user))
|
||||
return FALSE
|
||||
|
||||
var/list/valid_mobs = list()
|
||||
var/list/nearby_things = range(1, src)
|
||||
for(var/mob/living/basic/possible_mob in nearby_things)
|
||||
if (!is_valid_animal(possible_mob))
|
||||
continue
|
||||
valid_mobs += possible_mob
|
||||
for(var/mob/living/simple_animal/possible_mob in nearby_things)
|
||||
if (!is_valid_animal(possible_mob))
|
||||
continue
|
||||
valid_mobs += possible_mob
|
||||
|
||||
if (!length(valid_mobs)) //Just in case there aren't any animals on the station, this will leave you with a terrible option to possess if you feel like it //i found it funny that in the file for a giant angel beast theres a cockroach
|
||||
new /mob/living/basic/cockroach(get_step(src,dir))
|
||||
return
|
||||
|
||||
var/mob/living/picked_mob = pick(valid_mobs)
|
||||
var/obj/structure/closet/stasis/possessor_container = new /obj/structure/closet/stasis(picked_mob)
|
||||
user.forceMove(possessor_container)
|
||||
|
||||
/// Returns true if this is a mob you're allowed to possess
|
||||
/obj/machinery/anomalous_crystal/possessor/proc/is_valid_animal(mob/living/check_mob)
|
||||
return check_mob.stat != DEAD && !check_mob.ckey && check_mob.mob_size < MOB_SIZE_LARGE && check_mob.melee_damage_upper <= 5
|
||||
|
||||
|
||||
/obj/structure/closet/stasis
|
||||
name = "quantum entanglement stasis warp field"
|
||||
desc = "You can hardly comprehend this thing... which is why you can't see it."
|
||||
icon_state = null //This shouldn't even be visible, so if it DOES show up, at least nobody will notice
|
||||
enable_door_overlay = FALSE //For obvious reasons
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE
|
||||
paint_jobs = null
|
||||
///The animal the closet (and the user's body) is inside of
|
||||
var/mob/living/holder_animal
|
||||
|
||||
/obj/structure/closet/stasis/Initialize(mapload)
|
||||
. = ..()
|
||||
if(isanimal_or_basicmob(loc))
|
||||
holder_animal = loc
|
||||
RegisterSignal(holder_animal, COMSIG_LIVING_DEATH, PROC_REF(on_holder_animal_death))
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
|
||||
|
||||
/obj/structure/closet/stasis/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
||||
. = ..()
|
||||
if(isliving(arrived) && holder_animal)
|
||||
var/mob/living/possessor = arrived
|
||||
possessor.add_traits(list(TRAIT_UNDENSE, TRAIT_NO_TRANSFORM, TRAIT_GODMODE), STASIS_MUTE)
|
||||
possessor.mind.transfer_to(holder_animal)
|
||||
var/datum/action/exit_possession/escape = new(holder_animal)
|
||||
escape.Grant(holder_animal)
|
||||
remove_verb(holder_animal, /mob/living/verb/pulled)
|
||||
|
||||
/obj/structure/closet/stasis/dump_contents(kill = TRUE)
|
||||
for(var/mob/living/possessor in src)
|
||||
possessor.remove_traits(list(TRAIT_UNDENSE, TRAIT_NO_TRANSFORM, TRAIT_GODMODE), STASIS_MUTE)
|
||||
if(kill || !isanimal_or_basicmob(loc))
|
||||
possessor.investigate_log("has died from [src].", INVESTIGATE_DEATHS)
|
||||
possessor.death(FALSE)
|
||||
if(holder_animal)
|
||||
holder_animal.mind.transfer_to(possessor)
|
||||
possessor.mind.grab_ghost(force = TRUE)
|
||||
possessor.forceMove(get_turf(holder_animal))
|
||||
holder_animal.investigate_log("has been gibbed by [src].", INVESTIGATE_DEATHS)
|
||||
holder_animal.gib(DROP_ALL_REMAINS)
|
||||
return ..()
|
||||
return ..()
|
||||
|
||||
/obj/structure/closet/stasis/ex_act()
|
||||
return FALSE
|
||||
|
||||
///When our host animal dies in any way, we empty the stasis closet out.
|
||||
/obj/structure/closet/stasis/proc/on_holder_animal_death()
|
||||
SIGNAL_HANDLER
|
||||
dump_contents()
|
||||
|
||||
/datum/action/exit_possession
|
||||
name = "Exit Possession"
|
||||
desc = "Exits the body you are possessing. They will explode violently when this occurs."
|
||||
button_icon = 'icons/mob/actions/actions_spells.dmi'
|
||||
button_icon_state = "exit_possession"
|
||||
|
||||
/datum/action/exit_possession/IsAvailable(feedback = FALSE)
|
||||
return ..() && isfloorturf(owner.loc)
|
||||
|
||||
/datum/action/exit_possession/Trigger(trigger_flags)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
|
||||
var/obj/structure/closet/stasis/stasis = locate() in owner
|
||||
if(!stasis)
|
||||
CRASH("[type] did not find a stasis closet thing in the owner.")
|
||||
|
||||
stasis.dump_contents(FALSE)
|
||||
qdel(stasis)
|
||||
qdel(src)
|
||||
|
||||
#undef ACTIVATE_BOMB
|
||||
#undef ACTIVATE_BULLET
|
||||
#undef ACTIVATE_ENERGY
|
||||
#undef ACTIVATE_HEAT
|
||||
#undef ACTIVATE_MAGIC
|
||||
#undef ACTIVATE_MOB_BUMP
|
||||
#undef ACTIVATE_SPEECH
|
||||
#undef ACTIVATE_TOUCH
|
||||
#undef ACTIVATE_WEAPON
|
||||
@@ -0,0 +1,104 @@
|
||||
// Resurrection crystal
|
||||
|
||||
/obj/item/resurrection_crystal
|
||||
name = "resurrection crystal"
|
||||
desc = "When used by anything holding it, this crystal gives them a second chance at life if they die."
|
||||
icon = 'icons/obj/mining.dmi'
|
||||
icon_state = "demonic_crystal"
|
||||
|
||||
/obj/item/resurrection_crystal/attack_self(mob/living/user)
|
||||
if(!iscarbon(user))
|
||||
to_chat(user, span_notice("A dark presence stops you from absorbing the crystal."))
|
||||
return
|
||||
forceMove(user)
|
||||
to_chat(user, span_notice("You feel a bit safer... but a demonic presence lurks in the back of your head..."))
|
||||
RegisterSignal(user, COMSIG_LIVING_DEATH, PROC_REF(resurrect))
|
||||
|
||||
/// Resurrects the target when they die by moving them and dusting a clone in their place, one life for another
|
||||
/obj/item/resurrection_crystal/proc/resurrect(mob/living/carbon/user, gibbed)
|
||||
SIGNAL_HANDLER
|
||||
if(gibbed)
|
||||
to_chat(user, span_notice("This power cannot be used if your entire mortal body is disintegrated..."))
|
||||
return
|
||||
user.visible_message(span_notice("You see [user]'s soul dragged out of their body!"), span_notice("You feel your soul dragged away to a fresh body!"))
|
||||
var/typepath = user.type
|
||||
var/mob/living/carbon/clone = new typepath(user.loc)
|
||||
clone.real_name = user.real_name
|
||||
INVOKE_ASYNC(user.dna, TYPE_PROC_REF(/datum/dna, transfer_identity), clone)
|
||||
clone.updateappearance(mutcolor_update=1)
|
||||
var/turf/T = find_maintenance_spawn(atmos_sensitive = TRUE, require_darkness = TRUE) || find_safe_turf()
|
||||
user.forceMove(T)
|
||||
user.revive(ADMIN_HEAL_ALL)
|
||||
INVOKE_ASYNC(user, TYPE_PROC_REF(/mob/living/carbon, set_species), /datum/species/shadow)
|
||||
to_chat(user, span_notice("You blink and find yourself in [get_area_name(T)]... feeling a bit darker."))
|
||||
clone.dust()
|
||||
qdel(src)
|
||||
|
||||
// Cursed boots
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail
|
||||
name = "cursed ice hiking boots"
|
||||
desc = "A pair of winter boots contractually made by a devil, they cannot be taken off once put on."
|
||||
actions_types = list(/datum/action/item_action/toggle)
|
||||
var/on = FALSE
|
||||
var/change_turf = /turf/open/misc/ice/icemoon/no_planet_atmos
|
||||
var/duration = 6 SECONDS
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/Initialize(mapload)
|
||||
. = ..()
|
||||
RegisterSignal(src, COMSIG_SHOES_STEP_ACTION, PROC_REF(on_step))
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(slot & ITEM_SLOT_FEET)
|
||||
ADD_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT(type))
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/dropped(mob/user)
|
||||
. = ..()
|
||||
// Could have been blown off in an explosion from the previous owner
|
||||
REMOVE_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT(type))
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/ui_action_click(mob/user)
|
||||
on = !on
|
||||
to_chat(user, span_notice("You [on ? "activate" : "deactivate"] [src]."))
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/examine(mob/user)
|
||||
. = ..()
|
||||
. += span_notice("The shoes are [on ? "enabled" : "disabled"].")
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/proc/on_step()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/turf/T = get_turf(loc)
|
||||
if(!on || isclosedturf(T) || istype(T, change_turf))
|
||||
return
|
||||
var/reset_turf = T.type
|
||||
T.ChangeTurf(change_turf, flags = CHANGETURF_INHERIT_AIR)
|
||||
addtimer(CALLBACK(T, TYPE_PROC_REF(/turf, ChangeTurf), reset_turf, null, CHANGETURF_INHERIT_AIR), duration, TIMER_OVERRIDE|TIMER_UNIQUE)
|
||||
|
||||
// Demonic jackhammer
|
||||
|
||||
/obj/item/pickaxe/drill/jackhammer/demonic
|
||||
name = "demonic jackhammer"
|
||||
desc = "Cracks rocks at an inhuman speed, as well as being enhanced for combat purposes."
|
||||
toolspeed = 0
|
||||
|
||||
/obj/item/pickaxe/drill/jackhammer/demonic/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/knockback, 4, TRUE, FALSE)
|
||||
AddElement(/datum/element/lifesteal, 5)
|
||||
|
||||
/obj/item/pickaxe/drill/jackhammer/demonic/use_tool(atom/target, mob/living/user, delay, amount=0, volume=0, datum/callback/extra_checks)
|
||||
var/turf/T = get_turf(target)
|
||||
mineral_scan_pulse(T, world.view + 1, src)
|
||||
. = ..()
|
||||
|
||||
// Ice energy crystal
|
||||
|
||||
/obj/item/ice_energy_crystal
|
||||
name = "ice energy crystal"
|
||||
desc = "Remnants of the demonic frost miners ice energy."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "ice_crystal"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
throwforce = 0
|
||||
@@ -0,0 +1,264 @@
|
||||
// Hierophant club
|
||||
|
||||
/obj/item/hierophant_club
|
||||
name = "hierophant club"
|
||||
desc = "The strange technology of this large club allows various nigh-magical teleportation feats. It used to beat you, but now you can set the beat."
|
||||
icon_state = "hierophant_club_ready_beacon"
|
||||
inhand_icon_state = "hierophant_club_ready_beacon"
|
||||
icon_angle = -135
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
|
||||
inhand_x_dimension = 64
|
||||
inhand_y_dimension = 64
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
force = 15
|
||||
attack_verb_continuous = list("clubs", "beats", "pummels")
|
||||
attack_verb_simple = list("club", "beat", "pummel")
|
||||
hitsound = 'sound/items/weapons/sonic_jackhammer.ogg'
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
actions_types = list(/datum/action/item_action/vortex_recall)
|
||||
action_slots = ALL
|
||||
/// Linked teleport beacon for the group teleport functionality.
|
||||
var/obj/effect/hierophant/beacon
|
||||
/// TRUE if currently doing a teleport to the beacon, FALSE otherwise.
|
||||
var/teleporting = FALSE
|
||||
/// Action enabling the blink-dash functionality.
|
||||
var/datum/action/innate/dash/hierophant/blink
|
||||
/// Whether the blink ability is activated. IF TRUE, left clicking a location will blink to it. If FALSE, this is disabled.
|
||||
var/blink_activated = TRUE
|
||||
|
||||
/obj/item/hierophant_club/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/update_icon_updates_onmob)
|
||||
blink = new(src)
|
||||
|
||||
/obj/item/hierophant_club/Destroy(force)
|
||||
QDEL_NULL(blink)
|
||||
return ..()
|
||||
|
||||
/obj/item/hierophant_club/examine(mob/user)
|
||||
. = ..()
|
||||
if (beacon)
|
||||
. += span_hierophant_warning("The beacon is currently detached.")
|
||||
else
|
||||
. += span_hierophant_warning("There is a beacon attached at the back end of the handle.")
|
||||
|
||||
/obj/item/hierophant_club/equipped(mob/user)
|
||||
. = ..()
|
||||
blink.Grant(user, src)
|
||||
user.update_icons()
|
||||
|
||||
/obj/item/hierophant_club/dropped(mob/user)
|
||||
. = ..()
|
||||
blink.Remove(user)
|
||||
user.update_icons()
|
||||
|
||||
/obj/item/hierophant_club/suicide_act(mob/living/user)
|
||||
say("Xverwpsgexmrk...", forced = "hierophant club suicide")
|
||||
user.visible_message(span_suicide("[user] holds [src] into the air! It looks like [user.p_theyre()] trying to commit suicide!"))
|
||||
new/obj/effect/temp_visual/hierophant/telegraph(get_turf(user))
|
||||
playsound(user,'sound/machines/airlock/airlockopen.ogg', 75, TRUE)
|
||||
user.visible_message(span_hierophant_warning("[user] fades out, leaving [user.p_their()] belongings behind!"))
|
||||
for (var/obj/item/user_item as anything in user.get_all_gear(FALSE, FALSE))
|
||||
user.dropItemToGround(user_item)
|
||||
for (var/turf/blast_turf as anything in RANGE_TURFS(1, user))
|
||||
new /obj/effect/temp_visual/hierophant/blast/visual(blast_turf, user, TRUE)
|
||||
user.dropItemToGround(src) //Drop us last, so it goes on top of their stuff
|
||||
qdel(user)
|
||||
|
||||
/obj/item/hierophant_club/attack_self(mob/user)
|
||||
. = ..()
|
||||
blink_activated = !blink_activated
|
||||
balloon_alert(user, "blinking [blink_activated ? "enabled" : "disabled"]")
|
||||
|
||||
/obj/item/hierophant_club/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
||||
// If our target is the beacon and the hierostaff is next to the beacon, we're trying to pick it up.
|
||||
if (interacting_with == beacon)
|
||||
return NONE
|
||||
|
||||
if (!blink_activated)
|
||||
return NONE
|
||||
|
||||
if (blink.teleport(user, interacting_with))
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
|
||||
/obj/item/hierophant_club/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
||||
if (blink_activated)
|
||||
blink.teleport(user, interacting_with)
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
return NONE
|
||||
|
||||
/obj/item/hierophant_club/update_icon_state()
|
||||
icon_state = inhand_icon_state = "hierophant_club[blink?.current_charges > 0 ? "_ready" : ""][!QDELETED(beacon) ? "" : "_beacon"]"
|
||||
return ..()
|
||||
|
||||
/obj/item/hierophant_club/ui_action_click(mob/user, action)
|
||||
if (teleporting)
|
||||
balloon_alert(user, "already in use!")
|
||||
return
|
||||
|
||||
if (!user.is_holding(src))
|
||||
to_chat(user, span_warning("You need to hold the club in your hands to [beacon ? "teleport with it" : "detach the beacon"]!"))
|
||||
return
|
||||
|
||||
if (!beacon)
|
||||
deploy_beacon(user)
|
||||
return
|
||||
|
||||
if (get_dist(user, beacon) <= 2)
|
||||
balloon_alert(user, "too close to the beacon!")
|
||||
return
|
||||
|
||||
var/turf/beacon_turf = get_turf(beacon)
|
||||
if (!beacon_turf || beacon_turf.is_blocked_turf(TRUE))
|
||||
balloon_alert(user, "the beacon is blocked!")
|
||||
return
|
||||
|
||||
if (!isturf(user.loc))
|
||||
balloon_alert(user, "not enough room to teleport!")
|
||||
return
|
||||
|
||||
var/turf/user_turf = get_turf(user)
|
||||
teleporting = TRUE
|
||||
user.update_mob_action_buttons()
|
||||
user.visible_message(span_hierophant_warning("[user] starts to glow faintly..."), span_hierophant_warning("You begin channeling [src]'s power..i."))
|
||||
beacon.icon_state = "hierophant_tele_on"
|
||||
var/obj/effect/temp_visual/hierophant/telegraph/edge/user_telegraph = new /obj/effect/temp_visual/hierophant/telegraph/edge(user_turf)
|
||||
var/obj/effect/temp_visual/hierophant/telegraph/edge/beacon_telegraph = new /obj/effect/temp_visual/hierophant/telegraph/edge(beacon_turf)
|
||||
if (!do_after(user, 4 SECONDS, user))
|
||||
if (user)
|
||||
balloon_alert(user, "interrupted!")
|
||||
stop_teleport(user)
|
||||
qdel(user_telegraph)
|
||||
qdel(beacon_telegraph)
|
||||
return
|
||||
|
||||
if (!beacon)
|
||||
balloon_alert(user, "interrupted!")
|
||||
stop_teleport(user)
|
||||
return
|
||||
|
||||
if (beacon_turf.is_blocked_turf(TRUE))
|
||||
balloon_alert(user, "the beacon is blocked!")
|
||||
stop_teleport(user)
|
||||
return
|
||||
|
||||
new /obj/effect/temp_visual/hierophant/telegraph(user_turf, user)
|
||||
new /obj/effect/temp_visual/hierophant/telegraph(beacon_turf, user)
|
||||
playsound(user_turf, 'sound/machines/airlock/airlockopen.ogg', 200, TRUE)
|
||||
playsound(beacon_turf, 'sound/effects/magic/wand_teleport.ogg', 200, TRUE)
|
||||
|
||||
new /obj/effect/temp_visual/hierophant/telegraph/teleport(user_turf, user)
|
||||
new /obj/effect/temp_visual/hierophant/telegraph/teleport(beacon_turf, user)
|
||||
|
||||
for(var/turf/turf_near_user_turf as anything in RANGE_TURFS(1, user_turf))
|
||||
new /obj/effect/temp_visual/hierophant/blast/visual(turf_near_user_turf, user, TRUE)
|
||||
|
||||
for(var/turf/turf_near_beacon as anything in RANGE_TURFS(1, beacon_turf))
|
||||
new /obj/effect/temp_visual/hierophant/blast/visual(turf_near_beacon, user, TRUE)
|
||||
|
||||
for(var/mob/living/victim in range(1, user_turf))
|
||||
INVOKE_ASYNC(src, PROC_REF(teleport_mob), user_turf, beacon_turf, victim, user)
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(stop_teleport), user), 0.6 SECONDS)
|
||||
|
||||
/// Just to cut down on copypasta
|
||||
/obj/item/hierophant_club/proc/stop_teleport(mob/user)
|
||||
teleporting = FALSE
|
||||
if (beacon)
|
||||
beacon.icon_state = "hierophant_tele_off"
|
||||
if (user)
|
||||
user.update_mob_action_buttons()
|
||||
|
||||
/// Teleports mobs after a short animation
|
||||
/obj/item/hierophant_club/proc/teleport_mob(turf/user_turf, turf/beacon_turf, mob/victim, mob/user)
|
||||
var/turf/target_turf = get_step(beacon_turf, get_dir(user_turf, victim))
|
||||
if (!target_turf || target_turf.is_blocked_turf(TRUE))
|
||||
return
|
||||
animate(victim, alpha = 0, time = 0.2 SECONDS, easing = SINE_EASING|EASE_OUT)
|
||||
sleep(0.2 SECONDS)
|
||||
victim.visible_message(span_hierophant_warning("[victim] fades out!"))
|
||||
var/success = do_teleport(victim, target_turf, no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC)
|
||||
animate(victim, alpha = 255, time = 0.2 SECONDS, SINE_EASING|EASE_OUT)
|
||||
victim.visible_message(span_hierophant_warning("[victim] fades in!"))
|
||||
if (user != victim && success)
|
||||
log_combat(user, victim, "teleported", null, "from [AREACOORD(user_turf)]")
|
||||
|
||||
/// Attempts to place a return beacon at user's feet
|
||||
/obj/item/hierophant_club/proc/deploy_beacon(mob/user)
|
||||
if (!isopenturf(user.loc) && !isopenspaceturf(user.loc))
|
||||
to_chat(user, span_warning("You need to be on solid ground to detach the beacon!"))
|
||||
return
|
||||
|
||||
user.visible_message(span_hierophant_warning("[user] starts fiddling with [src]'s pommel..."), span_notice("You start detaching the hierophant beacon..."))
|
||||
balloon_alert(user, "detaching the beacon...")
|
||||
if (!do_after(user, 5 SECONDS, user))
|
||||
balloon_alert(user, "interrupted!")
|
||||
return
|
||||
|
||||
// Already dropped one
|
||||
if (beacon)
|
||||
return
|
||||
|
||||
var/turf/user_turf = get_turf(user)
|
||||
new /obj/effect/temp_visual/hierophant/telegraph/teleport(user_turf, user)
|
||||
beacon = new /obj/effect/hierophant(user_turf)
|
||||
playsound(beacon, 'sound/effects/magic/blind.ogg', 200, TRUE, -4)
|
||||
RegisterSignal(beacon, COMSIG_QDELETING, PROC_REF(beacon_destroyed))
|
||||
|
||||
user.update_mob_action_buttons()
|
||||
user.visible_message(span_hierophant_warning("[user] places a strange machine beneath [user.p_their()] feet!"), span_hierophant("You detach the hierophant beacon, allowing you to teleport yourself and any allies to it at any time!"))
|
||||
to_chat(user, span_hierophant("You can remove the beacon to place it again by striking it with the club."))
|
||||
update_appearance(UPDATE_ICON_STATE)
|
||||
|
||||
/obj/item/hierophant_club/proc/beacon_destroyed(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
beacon = null
|
||||
if (ismob(loc))
|
||||
to_chat(loc, span_hierophant("With a loud snap, a new beacon appears at [src]'s pommel."))
|
||||
else
|
||||
visible_message(span_hierophant("With a loud snap, a new beacon appears at [src]'s pommel."))
|
||||
playsound(src, 'sound/effects/magic/blind.ogg', 50, TRUE, -4)
|
||||
update_appearance(UPDATE_ICON_STATE)
|
||||
|
||||
#define HIEROPHANT_BLINK_RANGE 5
|
||||
#define HIEROPHANT_BLINK_COOLDOWN (15 SECONDS)
|
||||
|
||||
/datum/action/innate/dash/hierophant
|
||||
current_charges = 1
|
||||
max_charges = 1
|
||||
charge_rate = HIEROPHANT_BLINK_COOLDOWN
|
||||
recharge_sound = null
|
||||
phasein = /obj/effect/temp_visual/hierophant/blast/visual
|
||||
phaseout = /obj/effect/temp_visual/hierophant/blast/visual
|
||||
// It's a simple purple beam, works well enough for the purple hiero effects.
|
||||
beam_effect = "plasmabeam"
|
||||
|
||||
/datum/action/innate/dash/hierophant/teleport(mob/user, atom/target)
|
||||
var/dist = get_dist(user, target)
|
||||
if(dist > HIEROPHANT_BLINK_RANGE)
|
||||
user.balloon_alert(user, "destination out of range!")
|
||||
return FALSE
|
||||
|
||||
var/turf/target_turf = get_turf(target)
|
||||
if(target_turf.is_blocked_turf_ignore_climbable())
|
||||
user.balloon_alert(user, "destination blocked!")
|
||||
return FALSE
|
||||
|
||||
. = ..()
|
||||
|
||||
var/obj/item/hierophant_club/club = target
|
||||
if(!istype(club))
|
||||
club.update_appearance(UPDATE_ICON_STATE)
|
||||
|
||||
/datum/action/innate/dash/hierophant/charge()
|
||||
. = ..()
|
||||
var/obj/item/hierophant_club/club = target
|
||||
if(istype(club))
|
||||
club.update_appearance(UPDATE_ICON_STATE)
|
||||
|
||||
#undef HIEROPHANT_BLINK_RANGE
|
||||
#undef HIEROPHANT_BLINK_COOLDOWN
|
||||
@@ -0,0 +1,129 @@
|
||||
// Staff of storms
|
||||
|
||||
/obj/item/storm_staff
|
||||
name = "staff of storms"
|
||||
desc = "An ancient staff retrieved from the remains of Legion. The wind stirs as you move it."
|
||||
icon_state = "staffofstorms"
|
||||
inhand_icon_state = "staffofstorms"
|
||||
icon_angle = -45
|
||||
icon = 'icons/obj/weapons/guns/magic.dmi'
|
||||
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
force = 20
|
||||
damtype = BURN
|
||||
hitsound = 'sound/items/weapons/taserhit.ogg'
|
||||
wound_bonus = -30
|
||||
bare_wound_bonus = 20
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
var/max_thunder_charges = 3
|
||||
var/thunder_charges = 3
|
||||
var/thunder_charge_time = 15 SECONDS
|
||||
var/static/list/excluded_areas = list(/area/space)
|
||||
var/list/targeted_turfs = list()
|
||||
|
||||
/obj/item/storm_staff/examine(mob/user)
|
||||
. = ..()
|
||||
. += span_notice("It has [thunder_charges] charges remaining.")
|
||||
. += span_notice("Use it in hand to dispel storms.")
|
||||
. += span_notice("Use it on targets to summon thunderbolts from the sky.")
|
||||
. += span_notice("The thunderbolts are boosted if in an area with weather effects.")
|
||||
|
||||
/obj/item/storm_staff/attack_self(mob/user)
|
||||
var/area/user_area = get_area(user)
|
||||
var/turf/user_turf = get_turf(user)
|
||||
if(!user_area || !user_turf || (is_type_in_list(user_area, excluded_areas)))
|
||||
to_chat(user, span_warning("Something is preventing you from using the staff here."))
|
||||
return
|
||||
var/datum/weather/affected_weather
|
||||
for(var/datum/weather/weather as anything in SSweather.processing)
|
||||
if((user_turf.z in weather.impacted_z_levels) && ispath(user_area.type, weather.area_type))
|
||||
affected_weather = weather
|
||||
break
|
||||
if(!affected_weather)
|
||||
return
|
||||
if(affected_weather.stage == END_STAGE)
|
||||
balloon_alert(user, "already ended!")
|
||||
return
|
||||
if(affected_weather.stage == WIND_DOWN_STAGE)
|
||||
balloon_alert(user, "already ending!")
|
||||
return
|
||||
balloon_alert(user, "you hold the staff up...")
|
||||
if(!do_after(user, 3 SECONDS, target = src))
|
||||
balloon_alert(user, "interrupted!")
|
||||
return
|
||||
user.visible_message(span_warning("[user] holds [src] skywards as an orange beam travels into the sky!"), \
|
||||
span_notice("You hold [src] skyward, dispelling the storm!"))
|
||||
playsound(user, 'sound/effects/magic/staff_change.ogg', 200, FALSE)
|
||||
var/old_color = user.color
|
||||
user.color = list(340/255, 240/255, 0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
|
||||
var/old_transform = user.transform
|
||||
user.transform *= 1.2
|
||||
animate(user, color = old_color, transform = old_transform, time = 1 SECONDS)
|
||||
affected_weather.wind_down()
|
||||
user.log_message("has dispelled a storm at [AREACOORD(user_turf)].", LOG_GAME)
|
||||
|
||||
/obj/item/storm_staff/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
||||
return thunder_blast(interacting_with, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
|
||||
|
||||
/obj/item/storm_staff/afterattack(atom/target, mob/user, click_parameters)
|
||||
thunder_blast(target, user)
|
||||
|
||||
/obj/item/storm_staff/proc/thunder_blast(atom/target, mob/user)
|
||||
if(!thunder_charges)
|
||||
balloon_alert(user, "needs to charge!")
|
||||
return FALSE
|
||||
var/turf/target_turf = get_turf(target)
|
||||
var/area/target_area = get_area(target)
|
||||
if(!target_turf || !target_area || (is_type_in_list(target_area, excluded_areas)))
|
||||
balloon_alert(user, "can't bolt here!")
|
||||
return FALSE
|
||||
if(target_turf in targeted_turfs)
|
||||
balloon_alert(user, "already targeted!")
|
||||
return FALSE
|
||||
if(HAS_TRAIT(user, TRAIT_PACIFISM))
|
||||
balloon_alert(user, "you don't want to harm!")
|
||||
return FALSE
|
||||
var/power_boosted = FALSE
|
||||
for(var/datum/weather/weather as anything in SSweather.processing)
|
||||
if(weather.stage != MAIN_STAGE)
|
||||
continue
|
||||
if((target_turf.z in weather.impacted_z_levels) && ispath(target_area.type, weather.area_type))
|
||||
power_boosted = TRUE
|
||||
break
|
||||
playsound(src, 'sound/effects/magic/lightningshock.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
|
||||
targeted_turfs += target_turf
|
||||
balloon_alert(user, "you aim at [target_turf]...")
|
||||
new /obj/effect/temp_visual/telegraphing/thunderbolt(target_turf)
|
||||
addtimer(CALLBACK(src, PROC_REF(throw_thunderbolt), target_turf, power_boosted), 1.5 SECONDS)
|
||||
thunder_charges--
|
||||
addtimer(CALLBACK(src, PROC_REF(recharge)), thunder_charge_time)
|
||||
user.log_message("fired the staff of storms at [AREACOORD(target_turf)].", LOG_ATTACK)
|
||||
return TRUE
|
||||
|
||||
/obj/item/storm_staff/proc/recharge(mob/user)
|
||||
thunder_charges = min(thunder_charges + 1, max_thunder_charges)
|
||||
playsound(src, 'sound/effects/magic/charge.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
|
||||
|
||||
/obj/item/storm_staff/proc/throw_thunderbolt(turf/target, boosted)
|
||||
targeted_turfs -= target
|
||||
new /obj/effect/temp_visual/thunderbolt(target)
|
||||
var/list/affected_turfs = list(target)
|
||||
if(boosted)
|
||||
for(var/direction in GLOB.alldirs)
|
||||
var/turf_to_add = get_step(target, direction)
|
||||
if(!turf_to_add)
|
||||
continue
|
||||
affected_turfs += turf_to_add
|
||||
for(var/turf/turf as anything in affected_turfs)
|
||||
new /obj/effect/temp_visual/electricity(turf)
|
||||
for(var/mob/living/hit_mob in turf)
|
||||
to_chat(hit_mob, span_userdanger("You've been struck by lightning!"))
|
||||
hit_mob.electrocute_act(15 * (isanimal_or_basicmob(hit_mob) ? 3 : 1) * (turf == target ? 2 : 1) * (boosted ? 2 : 1), src, flags = SHOCK_TESLA|SHOCK_NOSTUN)
|
||||
|
||||
for(var/obj/hit_thing in turf)
|
||||
hit_thing.take_damage(20, BURN, ENERGY, FALSE)
|
||||
playsound(target, 'sound/effects/magic/lightningbolt.ogg', 100, TRUE)
|
||||
target.visible_message(span_danger("A thunderbolt strikes [target]!"))
|
||||
explosion(target, light_impact_range = (boosted ? 1 : 0), flame_range = (boosted ? 2 : 1), silent = TRUE)
|
||||
@@ -0,0 +1,170 @@
|
||||
/datum/action/innate/brain_undeployment
|
||||
name = "Disconnect from shell"
|
||||
desc = "Stop controlling your shell and resume normal core operations."
|
||||
button_icon = 'icons/mob/actions/actions_AI.dmi'
|
||||
button_icon_state = "ai_core"
|
||||
|
||||
/datum/action/innate/brain_undeployment/Trigger(trigger_flags)
|
||||
if(!..())
|
||||
return FALSE
|
||||
var/obj/item/organ/brain/cybernetic/ai/shell_to_disconnect = owner.get_organ_by_type(/obj/item/organ/brain/cybernetic/ai)
|
||||
|
||||
shell_to_disconnect.undeploy()
|
||||
return TRUE
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai
|
||||
name = "AI-uplink brain"
|
||||
desc = "Can be inserted into a body with NO ORGANIC INTERNAL ORGANS (robotic organs only) to allow AIs to control it. Comes with its own health sensors beacon. MUST be a humanoid or bad things happen to the consciousness."
|
||||
can_smoothen_out = FALSE
|
||||
/// if connected, our AI
|
||||
var/mob/living/silicon/ai/mainframe
|
||||
/// action for undeployment
|
||||
var/datum/action/innate/brain_undeployment/undeployment_action = new
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/noticable_organ, "%PRONOUN_Their eyes move with machine precision, their expression completely blank.")
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/Destroy()
|
||||
. = ..()
|
||||
undeploy()
|
||||
mainframe = null
|
||||
QDEL_NULL(undeployment_action)
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/on_mob_insert(mob/living/carbon/brain_owner, special, movement_flags)
|
||||
. = ..()
|
||||
brain_owner.add_traits(list(HUMAN_SENSORS_VISIBLE_WITHOUT_SUIT, TRAIT_NO_MINDSWAP, TRAIT_CORPSELOCKED), REF(src))
|
||||
update_med_hud_status(brain_owner)
|
||||
RegisterSignal(brain_owner, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(update_med_hud_status))
|
||||
RegisterSignal(brain_owner, COMSIG_CLICK, PROC_REF(owner_clicked))
|
||||
RegisterSignal(brain_owner, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(get_status_tab_item))
|
||||
RegisterSignal(brain_owner, COMSIG_MOB_MIND_BEFORE_MIDROUND_ROLL, PROC_REF(cancel_rolls))
|
||||
RegisterSignals(brain_owner, list(COMSIG_QDELETING, COMSIG_LIVING_PRE_WABBAJACKED), PROC_REF(undeploy))
|
||||
RegisterSignal(brain_owner, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(on_organ_gain))
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
|
||||
undeploy()
|
||||
. = ..()
|
||||
organ_owner.remove_traits(list(HUMAN_SENSORS_VISIBLE_WITHOUT_SUIT, TRAIT_NO_MINDSWAP, TRAIT_CORPSELOCKED), REF(src))
|
||||
UnregisterSignal(organ_owner, list(COMSIG_LIVING_HEALTH_UPDATE, COMSIG_CLICK, COMSIG_MOB_GET_STATUS_TAB_ITEMS, COMSIG_MOB_MIND_BEFORE_MIDROUND_ROLL, COMSIG_QDELETING, COMSIG_LIVING_PRE_WABBAJACKED))
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/proc/cancel_rolls(mob/living/source, datum/mind/mind, datum/antagonist/antagonist)
|
||||
SIGNAL_HANDLER
|
||||
if(ispath(antagonist, /datum/antagonist/malf_ai))
|
||||
return
|
||||
return CANCEL_ROLL
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/proc/get_status_tab_item(mob/living/source, list/items)
|
||||
SIGNAL_HANDLER
|
||||
if(!mainframe)
|
||||
return
|
||||
items += mainframe.get_status_tab_items()
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/proc/update_med_hud_status(mob/living/mob_parent)
|
||||
SIGNAL_HANDLER
|
||||
var/image/holder = mob_parent.hud_list?[STATUS_HUD]
|
||||
if(isnull(holder))
|
||||
return
|
||||
var/icon/size_check = icon(mob_parent.icon, mob_parent.icon_state, mob_parent.dir)
|
||||
holder.pixel_y = size_check.Height() - ICON_SIZE_Y
|
||||
if(mob_parent.stat == DEAD || HAS_TRAIT(mob_parent, TRAIT_FAKEDEATH) || isnull(mainframe))
|
||||
holder.icon_state = "huddead2"
|
||||
holder.pixel_x = -8 // new icon states? nuh uh
|
||||
else
|
||||
holder.icon_state = "hudtrackingai"
|
||||
holder.pixel_x = -16
|
||||
|
||||
// no thoughts only wifi
|
||||
/obj/item/organ/brain/cybernetic/ai/can_gain_trauma(datum/brain_trauma/trauma, resilience, natural_gain = FALSE)
|
||||
return FALSE
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/proc/owner_clicked(datum/source, atom/location, control, params, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
if(!isAI(user))
|
||||
return
|
||||
var/list/lines = list()
|
||||
lines += span_bold("[owner]")
|
||||
lines += "Target is currently [!HAS_TRAIT(owner, TRAIT_INCAPACITATED) ? "functional" : "incapacitated"]"
|
||||
lines += "Estimated organic/inorganic integrity: [owner.health]"
|
||||
if(mainframe)
|
||||
lines += span_warning("Already occupied by another digital entity.")
|
||||
else if(!is_sufficiently_augmented())
|
||||
lines += span_warning("Organic organs detected. Robotic organs only, cannot take over.")
|
||||
else
|
||||
lines += "<a href='byond://?src=[REF(src)];ai_take_control=[REF(user)]'>[span_boldnotice("Take control?")]</a><br>"
|
||||
|
||||
to_chat(user, boxed_message(jointext(lines, "\n")), type = MESSAGE_TYPE_INFO)
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/Topic(href, href_list)
|
||||
..()
|
||||
if(!href_list["ai_take_control"] || !is_sufficiently_augmented() || mainframe)
|
||||
return
|
||||
var/mob/living/silicon/ai/AI = locate(href_list["ai_take_control"]) in GLOB.silicon_mobs
|
||||
if(isnull(AI))
|
||||
return
|
||||
if(AI.controlled_equipment)
|
||||
to_chat(AI, span_warning("You are already loaded into an onboard computer!"))
|
||||
return
|
||||
if(!GLOB.cameranet.checkCameraVis(owner))
|
||||
to_chat(AI, span_warning("Target is no longer near active cameras."))
|
||||
return
|
||||
if(!isturf(AI.loc))
|
||||
to_chat(AI, span_warning("You aren't in your core!"))
|
||||
return
|
||||
|
||||
RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(undeploy))
|
||||
AI.deployed_shell = owner
|
||||
deploy_init(AI)
|
||||
ADD_TRAIT(AI.mind, TRAIT_UNCONVERTABLE, REF(src))
|
||||
ADD_TRAIT(AI, TRAIT_MIND_TEMPORARILY_GONE, REF(src))
|
||||
AI.mind.transfer_to(owner)
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/proc/deploy_init(mob/living/silicon/ai/AI)
|
||||
//todo camera maybe
|
||||
mainframe = AI
|
||||
RegisterSignal(AI, COMSIG_QDELETING, PROC_REF(ai_deleted))
|
||||
undeployment_action.Grant(owner)
|
||||
update_med_hud_status(owner)
|
||||
to_chat(owner, span_boldbig("You are still considered a silicon/cyborg/AI. Follow your laws."))
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/proc/undeploy(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
if(!owner?.mind || !mainframe)
|
||||
return
|
||||
UnregisterSignal(owner, list(COMSIG_LIVING_DEATH, COMSIG_QDELETING))
|
||||
UnregisterSignal(mainframe, COMSIG_QDELETING)
|
||||
mainframe.redeploy_action.Remove(mainframe)
|
||||
mainframe.redeploy_action.last_used_shell = null
|
||||
owner.mind.transfer_to(mainframe)
|
||||
mainframe.deployed_shell = null
|
||||
undeployment_action.Remove(owner)
|
||||
if(mainframe.laws)
|
||||
mainframe.laws.show_laws(mainframe)
|
||||
if(mainframe.eyeobj)
|
||||
mainframe.eyeobj.setLoc(loc)
|
||||
REMOVE_TRAIT(mainframe.mind, TRAIT_UNCONVERTABLE, REF(src))
|
||||
REMOVE_TRAIT(mainframe, TRAIT_MIND_TEMPORARILY_GONE, REF(src))
|
||||
mainframe = null
|
||||
update_med_hud_status(owner)
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/proc/is_sufficiently_augmented()
|
||||
var/mob/living/carbon/carb_owner = owner
|
||||
. = TRUE
|
||||
if(!istype(carb_owner))
|
||||
return
|
||||
for(var/obj/item/organ/organ as anything in carb_owner.organs)
|
||||
if(organ.organ_flags & ORGAN_EXTERNAL)
|
||||
continue
|
||||
if(!IS_ROBOTIC_ORGAN(organ) && !istype(organ, /obj/item/organ/tongue)) //tongues are not in the exosuit fab and nobody is going to bother to find them so
|
||||
return FALSE
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/proc/on_organ_gain(datum/source, obj/item/organ/new_organ, special)
|
||||
SIGNAL_HANDLER
|
||||
if(!is_sufficiently_augmented())
|
||||
to_chat(owner, span_danger("Connection failure. Organics detected."))
|
||||
undeploy()
|
||||
|
||||
/obj/item/organ/brain/cybernetic/ai/proc/ai_deleted(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
to_chat(owner, span_danger("Your core has been rendered inoperable..."))
|
||||
undeploy()
|
||||
@@ -0,0 +1,29 @@
|
||||
// Wendigo blood
|
||||
|
||||
/obj/item/wendigo_blood
|
||||
name = "bottle of wendigo blood"
|
||||
desc = "A bottle of viscous red liquid... You're not actually going to drink this, are you?"
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "vial"
|
||||
|
||||
/obj/item/wendigo_blood/attack_self(mob/living/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
if(!human_user.mind)
|
||||
return
|
||||
to_chat(human_user, span_danger("Power courses through you! You can now shift your form at will."))
|
||||
var/datum/action/cooldown/spell/shapeshift/polar_bear/transformation_spell = new(user.mind || user)
|
||||
transformation_spell.Grant(user)
|
||||
playsound(human_user.loc, 'sound/items/drink.ogg', rand(10,50), TRUE)
|
||||
qdel(src)
|
||||
|
||||
// Wendigo skull
|
||||
|
||||
/obj/item/wendigo_skull
|
||||
name = "wendigo skull"
|
||||
desc = "A bloody skull torn from a murderous beast, the soulless eye sockets seem to constantly track your movement."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "wendigo_skull"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
throwforce = 0
|
||||
File diff suppressed because it is too large
Load Diff
@@ -224,378 +224,4 @@
|
||||
else
|
||||
SSexplosions.medturf += target
|
||||
|
||||
///Anomolous Crystal///
|
||||
|
||||
#define ACTIVATE_TOUCH "touch"
|
||||
#define ACTIVATE_SPEECH "speech"
|
||||
#define ACTIVATE_HEAT "heat"
|
||||
#define ACTIVATE_BULLET "bullet"
|
||||
#define ACTIVATE_ENERGY "energy"
|
||||
#define ACTIVATE_BOMB "bomb"
|
||||
#define ACTIVATE_MOB_BUMP "bumping"
|
||||
#define ACTIVATE_WEAPON "weapon"
|
||||
#define ACTIVATE_MAGIC "magic"
|
||||
|
||||
/obj/machinery/anomalous_crystal
|
||||
name = "anomalous crystal"
|
||||
desc = "A strange chunk of crystal, being in the presence of it fills you with equal parts excitement and dread."
|
||||
var/observer_desc = "Anomalous crystals have descriptions that only observers can see. But this one hasn't been changed from the default."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "anomaly_crystal"
|
||||
light_range = 8
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
use_power = NO_POWER_USE
|
||||
anchored = FALSE
|
||||
density = TRUE
|
||||
var/activation_method
|
||||
var/list/possible_methods = list(ACTIVATE_TOUCH, ACTIVATE_SPEECH, ACTIVATE_HEAT, ACTIVATE_BULLET, ACTIVATE_ENERGY, ACTIVATE_BOMB, ACTIVATE_MOB_BUMP, ACTIVATE_WEAPON, ACTIVATE_MAGIC)
|
||||
var/activation_damage_type = null
|
||||
/// Cooldown on this crystal
|
||||
var/cooldown_add = 3 SECONDS
|
||||
/// Time needed to use this crystal
|
||||
var/use_time = 0
|
||||
/// If we are being used
|
||||
var/active = FALSE
|
||||
var/activation_sound = 'sound/effects/break_stone.ogg'
|
||||
COOLDOWN_DECLARE(cooldown_timer)
|
||||
|
||||
/obj/machinery/anomalous_crystal/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!activation_method)
|
||||
activation_method = pick(possible_methods)
|
||||
become_hearing_sensitive(trait_source = ROUNDSTART_TRAIT)
|
||||
|
||||
/obj/machinery/anomalous_crystal/examine(mob/user)
|
||||
. = ..()
|
||||
if(isobserver(user))
|
||||
. += observer_desc
|
||||
. += "It is activated by [activation_method]."
|
||||
|
||||
/obj/machinery/anomalous_crystal/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, list/message_mods = list(), message_range)
|
||||
. = ..()
|
||||
if(isliving(speaker))
|
||||
ActivationReaction(speaker, ACTIVATE_SPEECH)
|
||||
|
||||
/obj/machinery/anomalous_crystal/attack_hand(mob/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
ActivationReaction(user, ACTIVATE_TOUCH)
|
||||
|
||||
/obj/machinery/anomalous_crystal/attackby(obj/item/I, mob/user, params)
|
||||
if(I.get_temperature())
|
||||
ActivationReaction(user, ACTIVATE_HEAT)
|
||||
else
|
||||
ActivationReaction(user, ACTIVATE_WEAPON)
|
||||
..()
|
||||
|
||||
/obj/machinery/anomalous_crystal/bullet_act(obj/projectile/proj, def_zone)
|
||||
. = ..()
|
||||
if(istype(proj, /obj/projectile/magic))
|
||||
ActivationReaction(proj.firer, ACTIVATE_MAGIC, proj.damage_type)
|
||||
return
|
||||
ActivationReaction(proj.firer, proj.armor_flag, proj.damage_type)
|
||||
|
||||
/obj/machinery/anomalous_crystal/proc/ActivationReaction(mob/user, method, damtype)
|
||||
if(!COOLDOWN_FINISHED(src, cooldown_timer))
|
||||
return FALSE
|
||||
if(activation_damage_type && activation_damage_type != damtype)
|
||||
return FALSE
|
||||
if(method != activation_method)
|
||||
return FALSE
|
||||
if(active)
|
||||
return FALSE
|
||||
if(use_time)
|
||||
charge_animation()
|
||||
COOLDOWN_START(src, cooldown_timer, cooldown_add)
|
||||
playsound(user, activation_sound, 100, TRUE)
|
||||
log_game("[src] activated by [key_name(user)] in [AREACOORD(src)]. The last fingerprints on the [src] was [fingerprintslast].")
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/anomalous_crystal/proc/charge_animation()
|
||||
icon_state = "anomaly_crystal_charging"
|
||||
active = TRUE
|
||||
set_anchored(TRUE)
|
||||
balloon_alert_to_viewers("charging...")
|
||||
playsound(src, 'sound/effects/magic/disable_tech.ogg', 50, TRUE)
|
||||
sleep(use_time)
|
||||
icon_state = initial(icon_state)
|
||||
active = FALSE
|
||||
set_anchored(FALSE)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/anomalous_crystal/Bumped(atom/movable/AM)
|
||||
..()
|
||||
if(ismob(AM))
|
||||
ActivationReaction(AM, ACTIVATE_MOB_BUMP)
|
||||
|
||||
/obj/machinery/anomalous_crystal/ex_act()
|
||||
ActivationReaction(null, ACTIVATE_BOMB)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/anomalous_crystal/honk //Revives the dead, but strips and equips them as a clown
|
||||
observer_desc = "This crystal revives targets around it as clowns. Oh, that's horrible...."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
activation_sound = 'sound/items/bikehorn.ogg'
|
||||
use_time = 3 SECONDS
|
||||
/// List of REFs to mobs that have been turned into a clown
|
||||
var/list/clowned_mob_refs = list()
|
||||
|
||||
/obj/machinery/anomalous_crystal/honk/ActivationReaction(mob/user)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
|
||||
for(var/atom/thing as anything in range(1, src))
|
||||
if(isturf(thing))
|
||||
new /obj/effect/decal/cleanable/confetti(thing)
|
||||
continue
|
||||
|
||||
if(!ishuman(thing))
|
||||
continue
|
||||
|
||||
var/mob/living/carbon/human/new_clown = thing
|
||||
|
||||
if(new_clown.stat != DEAD)
|
||||
continue
|
||||
|
||||
new_clown.revive(ADMIN_HEAL_ALL, force_grab_ghost = TRUE)
|
||||
|
||||
var/clown_ref = REF(new_clown)
|
||||
if(clown_ref in clowned_mob_refs) //one clowning per person
|
||||
continue
|
||||
|
||||
for(var/obj/item/to_strip in new_clown.get_equipped_items())
|
||||
new_clown.dropItemToGround(to_strip)
|
||||
new_clown.dress_up_as_job(SSjob.get_job_type(/datum/job/clown))
|
||||
clowned_mob_refs += clown_ref
|
||||
|
||||
return TRUE
|
||||
|
||||
/// Transforms the area to look like a new one
|
||||
/obj/machinery/anomalous_crystal/theme_warp
|
||||
observer_desc = "This crystal warps the area around it to a theme."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
cooldown_add = 20 SECONDS
|
||||
use_time = 5 SECONDS
|
||||
/// Theme which we turn areas into on activation
|
||||
var/datum/dimension_theme/terrain_theme
|
||||
/// List of all areas we've affected
|
||||
var/list/converted_areas = list()
|
||||
|
||||
/obj/machinery/anomalous_crystal/theme_warp/Initialize(mapload)
|
||||
. = ..()
|
||||
terrain_theme = SSmaterials.dimensional_themes[pick(subtypesof(/datum/dimension_theme))]
|
||||
observer_desc = "This crystal changes the area around it to match the theme of \"[terrain_theme.name]\"."
|
||||
|
||||
/obj/machinery/anomalous_crystal/theme_warp/ActivationReaction(mob/user, method)
|
||||
. = ..()
|
||||
if (!.)
|
||||
return FALSE
|
||||
var/area/current_area = get_area(src)
|
||||
if (current_area in converted_areas)
|
||||
return FALSE
|
||||
terrain_theme.apply_theme_to_list_of_turfs(current_area.get_turfs_from_all_zlevels())
|
||||
converted_areas += current_area
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/anomalous_crystal/theme_warp/Destroy()
|
||||
terrain_theme = null
|
||||
converted_areas.Cut()
|
||||
return ..()
|
||||
|
||||
/obj/machinery/anomalous_crystal/emitter //Generates a projectile when interacted with
|
||||
observer_desc = "This crystal generates a projectile when activated."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
cooldown_add = 5 SECONDS
|
||||
var/obj/projectile/generated_projectile = /obj/projectile/colossus
|
||||
|
||||
/obj/machinery/anomalous_crystal/emitter/Initialize(mapload)
|
||||
. = ..()
|
||||
observer_desc = "This crystal generates \a [initial(generated_projectile.name)] when activated."
|
||||
|
||||
/obj/machinery/anomalous_crystal/emitter/ActivationReaction(mob/user, method)
|
||||
if(..())
|
||||
var/obj/projectile/proj = new generated_projectile(get_turf(src))
|
||||
proj.firer = src
|
||||
proj.fire(dir2angle(dir))
|
||||
|
||||
/obj/machinery/anomalous_crystal/dark_reprise //Revives anyone nearby, but turns them into shadowpeople and renders them uncloneable, so the crystal is your only hope of getting up again if you go down.
|
||||
observer_desc = "When activated, this crystal revives anyone nearby, but turns them into Shadowpeople and makes them unclonable, making the crystal their only hope of getting up again."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
activation_sound = 'sound/effects/hallucinations/growl1.ogg'
|
||||
use_time = 3 SECONDS
|
||||
|
||||
/obj/machinery/anomalous_crystal/dark_reprise/ActivationReaction(mob/user, method)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
|
||||
for(var/atom/thing as anything in range(1, src))
|
||||
if(isturf(thing))
|
||||
new /obj/effect/temp_visual/cult/sparks(thing)
|
||||
continue
|
||||
|
||||
if(!ishuman(thing))
|
||||
continue
|
||||
|
||||
var/mob/living/carbon/human/to_revive = thing
|
||||
|
||||
if(to_revive.stat != DEAD)
|
||||
continue
|
||||
|
||||
to_revive.set_species(/datum/species/shadow, TRUE)
|
||||
to_revive.revive(ADMIN_HEAL_ALL, force_grab_ghost = TRUE)
|
||||
//Free revives, but significantly limits your options for reviving except via the crystal
|
||||
//except JK who cares about BADDNA anymore. this even heals suicides.
|
||||
ADD_TRAIT(to_revive, TRAIT_BADDNA, MAGIC_TRAIT)
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/anomalous_crystal/helpers //Lets ghost spawn as helpful creatures that can only heal people slightly. Incredibly fragile and they can't converse with humans
|
||||
observer_desc = "This crystal allows ghosts to turn into a fragile creature that can heal people."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
activation_sound = 'sound/effects/ghost2.ogg'
|
||||
use_time = 5 SECONDS
|
||||
var/ready_to_deploy = FALSE
|
||||
|
||||
/obj/machinery/anomalous_crystal/helpers/ActivationReaction(mob/user, method)
|
||||
if(..() && !ready_to_deploy)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
ready_to_deploy = TRUE
|
||||
notify_ghosts(
|
||||
"An anomalous crystal has been activated in [get_area(src)]! This crystal can always be used by ghosts hereafter.",
|
||||
source = src,
|
||||
header = "Anomalous crystal activated",
|
||||
click_interact = TRUE,
|
||||
ghost_sound = 'sound/effects/ghost2.ogg',
|
||||
)
|
||||
|
||||
/obj/machinery/anomalous_crystal/helpers/attack_ghost(mob/dead/observer/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(ready_to_deploy)
|
||||
var/be_helper = tgui_alert(usr, "Become a Lightgeist? (Warning, You can no longer be revived!)", "Lightgeist Deployment", list("Yes", "No"))
|
||||
if((be_helper == "Yes") && !QDELETED(src) && isobserver(user))
|
||||
var/mob/living/basic/lightgeist/deployable = new(get_turf(loc))
|
||||
deployable.PossessByPlayer(user.key)
|
||||
|
||||
/obj/machinery/anomalous_crystal/possessor //Allows you to bodyjack small animals, then exit them at your leisure, but you can only do this once per activation. Because they blow up. Also, if the bodyjacked animal dies, SO DO YOU.
|
||||
observer_desc = "When activated, this crystal allows you to take over small animals, and then exit them at the possessors leisure. Exiting the animal kills it, and if you die while possessing the animal, you die as well."
|
||||
activation_method = ACTIVATE_TOUCH
|
||||
use_time = 1 SECONDS
|
||||
|
||||
/obj/machinery/anomalous_crystal/possessor/ActivationReaction(mob/user, method)
|
||||
. = ..()
|
||||
if (!. || !ishuman(user))
|
||||
return FALSE
|
||||
|
||||
var/list/valid_mobs = list()
|
||||
var/list/nearby_things = range(1, src)
|
||||
for(var/mob/living/basic/possible_mob in nearby_things)
|
||||
if (!is_valid_animal(possible_mob))
|
||||
continue
|
||||
valid_mobs += possible_mob
|
||||
for(var/mob/living/simple_animal/possible_mob in nearby_things)
|
||||
if (!is_valid_animal(possible_mob))
|
||||
continue
|
||||
valid_mobs += possible_mob
|
||||
|
||||
if (!length(valid_mobs)) //Just in case there aren't any animals on the station, this will leave you with a terrible option to possess if you feel like it //i found it funny that in the file for a giant angel beast theres a cockroach
|
||||
new /mob/living/basic/cockroach(get_step(src,dir))
|
||||
return
|
||||
|
||||
var/mob/living/picked_mob = pick(valid_mobs)
|
||||
var/obj/structure/closet/stasis/possessor_container = new /obj/structure/closet/stasis(picked_mob)
|
||||
user.forceMove(possessor_container)
|
||||
|
||||
/// Returns true if this is a mob you're allowed to possess
|
||||
/obj/machinery/anomalous_crystal/possessor/proc/is_valid_animal(mob/living/check_mob)
|
||||
return check_mob.stat != DEAD && !check_mob.ckey && check_mob.mob_size < MOB_SIZE_LARGE && check_mob.melee_damage_upper <= 5
|
||||
|
||||
|
||||
/obj/structure/closet/stasis
|
||||
name = "quantum entanglement stasis warp field"
|
||||
desc = "You can hardly comprehend this thing... which is why you can't see it."
|
||||
icon_state = null //This shouldn't even be visible, so if it DOES show up, at least nobody will notice
|
||||
enable_door_overlay = FALSE //For obvious reasons
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE
|
||||
paint_jobs = null
|
||||
///The animal the closet (and the user's body) is inside of
|
||||
var/mob/living/holder_animal
|
||||
|
||||
/obj/structure/closet/stasis/Initialize(mapload)
|
||||
. = ..()
|
||||
if(isanimal_or_basicmob(loc))
|
||||
holder_animal = loc
|
||||
RegisterSignal(holder_animal, COMSIG_LIVING_DEATH, PROC_REF(on_holder_animal_death))
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
|
||||
|
||||
/obj/structure/closet/stasis/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
||||
. = ..()
|
||||
if(isliving(arrived) && holder_animal)
|
||||
var/mob/living/possessor = arrived
|
||||
possessor.add_traits(list(TRAIT_UNDENSE, TRAIT_NO_TRANSFORM, TRAIT_GODMODE), STASIS_MUTE)
|
||||
possessor.mind.transfer_to(holder_animal)
|
||||
var/datum/action/exit_possession/escape = new(holder_animal)
|
||||
escape.Grant(holder_animal)
|
||||
remove_verb(holder_animal, /mob/living/verb/pulled)
|
||||
|
||||
/obj/structure/closet/stasis/dump_contents(kill = TRUE)
|
||||
for(var/mob/living/possessor in src)
|
||||
possessor.remove_traits(list(TRAIT_UNDENSE, TRAIT_NO_TRANSFORM, TRAIT_GODMODE), STASIS_MUTE)
|
||||
if(kill || !isanimal_or_basicmob(loc))
|
||||
possessor.investigate_log("has died from [src].", INVESTIGATE_DEATHS)
|
||||
possessor.death(FALSE)
|
||||
if(holder_animal)
|
||||
holder_animal.mind.transfer_to(possessor)
|
||||
possessor.mind.grab_ghost(force = TRUE)
|
||||
possessor.forceMove(get_turf(holder_animal))
|
||||
holder_animal.investigate_log("has been gibbed by [src].", INVESTIGATE_DEATHS)
|
||||
holder_animal.gib(DROP_ALL_REMAINS)
|
||||
return ..()
|
||||
return ..()
|
||||
|
||||
/obj/structure/closet/stasis/ex_act()
|
||||
return FALSE
|
||||
|
||||
///When our host animal dies in any way, we empty the stasis closet out.
|
||||
/obj/structure/closet/stasis/proc/on_holder_animal_death()
|
||||
SIGNAL_HANDLER
|
||||
dump_contents()
|
||||
|
||||
/datum/action/exit_possession
|
||||
name = "Exit Possession"
|
||||
desc = "Exits the body you are possessing. They will explode violently when this occurs."
|
||||
button_icon = 'icons/mob/actions/actions_spells.dmi'
|
||||
button_icon_state = "exit_possession"
|
||||
|
||||
/datum/action/exit_possession/IsAvailable(feedback = FALSE)
|
||||
return ..() && isfloorturf(owner.loc)
|
||||
|
||||
/datum/action/exit_possession/Trigger(trigger_flags)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
|
||||
var/obj/structure/closet/stasis/stasis = locate() in owner
|
||||
if(!stasis)
|
||||
CRASH("[type] did not find a stasis closet thing in the owner.")
|
||||
|
||||
stasis.dump_contents(FALSE)
|
||||
qdel(stasis)
|
||||
qdel(src)
|
||||
|
||||
#undef ACTIVATE_BOMB
|
||||
#undef ACTIVATE_BULLET
|
||||
#undef ACTIVATE_ENERGY
|
||||
#undef ACTIVATE_HEAT
|
||||
#undef ACTIVATE_MAGIC
|
||||
#undef ACTIVATE_MOB_BUMP
|
||||
#undef ACTIVATE_SPEECH
|
||||
#undef ACTIVATE_TOUCH
|
||||
#undef ACTIVATE_WEAPON
|
||||
#undef COLOSSUS_ENRAGED
|
||||
|
||||
@@ -232,95 +232,6 @@ Difficulty: Extremely Hard
|
||||
if(isturf(target) || isobj(target))
|
||||
EX_ACT(target, EXPLODE_HEAVY)
|
||||
|
||||
/obj/item/resurrection_crystal
|
||||
name = "resurrection crystal"
|
||||
desc = "When used by anything holding it, this crystal gives them a second chance at life if they die."
|
||||
icon = 'icons/obj/mining.dmi'
|
||||
icon_state = "demonic_crystal"
|
||||
|
||||
/obj/item/resurrection_crystal/attack_self(mob/living/user)
|
||||
if(!iscarbon(user))
|
||||
to_chat(user, span_notice("A dark presence stops you from absorbing the crystal."))
|
||||
return
|
||||
forceMove(user)
|
||||
to_chat(user, span_notice("You feel a bit safer... but a demonic presence lurks in the back of your head..."))
|
||||
RegisterSignal(user, COMSIG_LIVING_DEATH, PROC_REF(resurrect))
|
||||
|
||||
/// Resurrects the target when they die by moving them and dusting a clone in their place, one life for another
|
||||
/obj/item/resurrection_crystal/proc/resurrect(mob/living/carbon/user, gibbed)
|
||||
SIGNAL_HANDLER
|
||||
if(gibbed)
|
||||
to_chat(user, span_notice("This power cannot be used if your entire mortal body is disintegrated..."))
|
||||
return
|
||||
user.visible_message(span_notice("You see [user]'s soul dragged out of their body!"), span_notice("You feel your soul dragged away to a fresh body!"))
|
||||
var/typepath = user.type
|
||||
var/mob/living/carbon/clone = new typepath(user.loc)
|
||||
clone.real_name = user.real_name
|
||||
INVOKE_ASYNC(user.dna, TYPE_PROC_REF(/datum/dna, transfer_identity), clone)
|
||||
clone.updateappearance(mutcolor_update=1)
|
||||
var/turf/T = find_maintenance_spawn(atmos_sensitive = TRUE, require_darkness = TRUE) || find_safe_turf()
|
||||
user.forceMove(T)
|
||||
user.revive(ADMIN_HEAL_ALL)
|
||||
INVOKE_ASYNC(user, TYPE_PROC_REF(/mob/living/carbon, set_species), /datum/species/shadow)
|
||||
to_chat(user, span_notice("You blink and find yourself in [get_area_name(T)]... feeling a bit darker."))
|
||||
clone.dust()
|
||||
qdel(src)
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail
|
||||
name = "cursed ice hiking boots"
|
||||
desc = "A pair of winter boots contractually made by a devil, they cannot be taken off once put on."
|
||||
actions_types = list(/datum/action/item_action/toggle)
|
||||
var/on = FALSE
|
||||
var/change_turf = /turf/open/misc/ice/icemoon/no_planet_atmos
|
||||
var/duration = 6 SECONDS
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/Initialize(mapload)
|
||||
. = ..()
|
||||
RegisterSignal(src, COMSIG_SHOES_STEP_ACTION, PROC_REF(on_step))
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(slot & ITEM_SLOT_FEET)
|
||||
ADD_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT(type))
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/dropped(mob/user)
|
||||
. = ..()
|
||||
// Could have been blown off in an explosion from the previous owner
|
||||
REMOVE_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT(type))
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/ui_action_click(mob/user)
|
||||
on = !on
|
||||
to_chat(user, span_notice("You [on ? "activate" : "deactivate"] [src]."))
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/examine(mob/user)
|
||||
. = ..()
|
||||
. += span_notice("The shoes are [on ? "enabled" : "disabled"].")
|
||||
|
||||
/obj/item/clothing/shoes/winterboots/ice_boots/ice_trail/proc/on_step()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/turf/T = get_turf(loc)
|
||||
if(!on || isclosedturf(T) || istype(T, change_turf))
|
||||
return
|
||||
var/reset_turf = T.type
|
||||
T.ChangeTurf(change_turf, flags = CHANGETURF_INHERIT_AIR)
|
||||
addtimer(CALLBACK(T, TYPE_PROC_REF(/turf, ChangeTurf), reset_turf, null, CHANGETURF_INHERIT_AIR), duration, TIMER_OVERRIDE|TIMER_UNIQUE)
|
||||
|
||||
/obj/item/pickaxe/drill/jackhammer/demonic
|
||||
name = "demonic jackhammer"
|
||||
desc = "Cracks rocks at an inhuman speed, as well as being enhanced for combat purposes."
|
||||
toolspeed = 0
|
||||
|
||||
/obj/item/pickaxe/drill/jackhammer/demonic/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/knockback, 4, TRUE, FALSE)
|
||||
AddElement(/datum/element/lifesteal, 5)
|
||||
|
||||
/obj/item/pickaxe/drill/jackhammer/demonic/use_tool(atom/target, mob/living/user, delay, amount=0, volume=0, datum/callback/extra_checks)
|
||||
var/turf/T = get_turf(target)
|
||||
mineral_scan_pulse(T, world.view + 1, src)
|
||||
. = ..()
|
||||
|
||||
/datum/status_effect/ice_block_talisman
|
||||
id = "ice_block_talisman"
|
||||
duration = 4 SECONDS
|
||||
@@ -365,14 +276,6 @@ Difficulty: Extremely Hard
|
||||
owner.cut_overlay(cube)
|
||||
UnregisterSignal(owner, COMSIG_MOVABLE_PRE_MOVE)
|
||||
|
||||
/obj/item/ice_energy_crystal
|
||||
name = "ice energy crystal"
|
||||
desc = "Remnants of the demonic frost miners ice energy."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "ice_crystal"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
throwforce = 0
|
||||
|
||||
/obj/structure/frost_miner_prism
|
||||
name = "frost miner light prism"
|
||||
desc = "A magical crystal enhanced by a demonic presence."
|
||||
|
||||
@@ -222,32 +222,6 @@ Difficulty: Hard
|
||||
pixel_moves += .
|
||||
set_angle(original_angle + pixel_moves * wave_speed)
|
||||
|
||||
/obj/item/wendigo_blood
|
||||
name = "bottle of wendigo blood"
|
||||
desc = "A bottle of viscous red liquid... You're not actually going to drink this, are you?"
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "vial"
|
||||
|
||||
/obj/item/wendigo_blood/attack_self(mob/living/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
if(!human_user.mind)
|
||||
return
|
||||
to_chat(human_user, span_danger("Power courses through you! You can now shift your form at will."))
|
||||
var/datum/action/cooldown/spell/shapeshift/polar_bear/transformation_spell = new(user.mind || user)
|
||||
transformation_spell.Grant(user)
|
||||
playsound(human_user.loc, 'sound/items/drink.ogg', rand(10,50), TRUE)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/wendigo_skull
|
||||
name = "wendigo skull"
|
||||
desc = "A bloody skull torn from a murderous beast, the soulless eye sockets seem to constantly track your movement."
|
||||
icon = 'icons/obj/mining_zones/artefacts.dmi'
|
||||
icon_state = "wendigo_skull"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
throwforce = 0
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/wendigo/noportal/create_portal()
|
||||
return
|
||||
|
||||
|
||||
@@ -255,3 +255,37 @@
|
||||
)
|
||||
departmental_flags = DEPARTMENT_BITFLAG_CARGO
|
||||
|
||||
/datum/design/unique_modkit
|
||||
category = list(
|
||||
RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_PKA_MODS,
|
||||
)
|
||||
build_type = PROTOLATHE
|
||||
departmental_flags = DEPARTMENT_BITFLAG_CARGO
|
||||
|
||||
/datum/design/unique_modkit/offensive_turf_aoe
|
||||
name = "Kinetic Accelerator Offensive Mining Explosion Mod"
|
||||
desc = "A device which causes kinetic accelerators to fire AoE blasts that destroy rock and damage creatures."
|
||||
id = "hyperaoemod"
|
||||
materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*3.5, /datum/material/glass = SHEET_MATERIAL_AMOUNT*1.5, /datum/material/silver =SHEET_MATERIAL_AMOUNT*1.5, /datum/material/gold =SHEET_MATERIAL_AMOUNT*1.5, /datum/material/diamond = SHEET_MATERIAL_AMOUNT*2)
|
||||
build_path = /obj/item/borg/upgrade/modkit/aoe/turfs/andmobs
|
||||
|
||||
/datum/design/unique_modkit/rapid_repeater
|
||||
name = "Kinetic Accelerator Rapid Repeater Mod"
|
||||
desc = "A device which greatly reduces a kinetic accelerator's cooldown on striking a living target or rock, but greatly increases its base cooldown."
|
||||
id = "repeatermod"
|
||||
materials = list(/datum/material/iron =HALF_SHEET_MATERIAL_AMOUNT * 5, /datum/material/glass =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/uranium = SHEET_MATERIAL_AMOUNT*4, /datum/material/bluespace =SHEET_MATERIAL_AMOUNT)
|
||||
build_path = /obj/item/borg/upgrade/modkit/cooldown/repeater
|
||||
|
||||
/datum/design/unique_modkit/resonator_blast
|
||||
name = "Kinetic Accelerator Resonator Blast Mod"
|
||||
desc = "A device which causes kinetic accelerators to fire shots that leave and detonate resonator blasts."
|
||||
id = "resonatormod"
|
||||
materials = list(/datum/material/iron =HALF_SHEET_MATERIAL_AMOUNT*5, /datum/material/glass =HALF_SHEET_MATERIAL_AMOUNT*5, /datum/material/silver =HALF_SHEET_MATERIAL_AMOUNT*5, /datum/material/uranium =SHEET_MATERIAL_AMOUNT * 2.5)
|
||||
build_path = /obj/item/borg/upgrade/modkit/resonator_blasts
|
||||
|
||||
/datum/design/unique_modkit/bounty
|
||||
name = "Kinetic Accelerator Death Syphon Mod"
|
||||
desc = "A device which causes kinetic accelerators to permanently gain damage against creature types killed with it."
|
||||
id = "bountymod"
|
||||
materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*2, /datum/material/silver = SHEET_MATERIAL_AMOUNT*2, /datum/material/gold = SHEET_MATERIAL_AMOUNT*2, /datum/material/bluespace = SHEET_MATERIAL_AMOUNT*2)
|
||||
build_path = /obj/item/borg/upgrade/modkit/bounty
|
||||
|
||||
@@ -17,72 +17,6 @@
|
||||
/obj/item/organ/vocal_cords/proc/handle_speech(message) //actually say the message
|
||||
owner.say(message, spans = spans, sanitize = FALSE)
|
||||
|
||||
//Colossus drop, forces the listeners to obey certain commands
|
||||
/obj/item/organ/vocal_cords/colossus
|
||||
name = "divine vocal cords"
|
||||
desc = "They carry the voice of an ancient god."
|
||||
icon_state = "voice_of_god"
|
||||
actions_types = list(/datum/action/item_action/organ_action/colossus)
|
||||
var/next_command = 0
|
||||
var/cooldown_mod = 1
|
||||
var/base_multiplier = 1
|
||||
spans = list("colossus","yell")
|
||||
|
||||
/datum/action/item_action/organ_action/colossus
|
||||
name = "Voice of God"
|
||||
var/obj/item/organ/vocal_cords/colossus/cords = null
|
||||
|
||||
/datum/action/item_action/organ_action/colossus/New()
|
||||
..()
|
||||
cords = target
|
||||
|
||||
/datum/action/item_action/organ_action/colossus/IsAvailable(feedback = FALSE)
|
||||
if(!owner)
|
||||
return FALSE
|
||||
if(world.time < cords.next_command)
|
||||
if (feedback)
|
||||
owner.balloon_alert(owner, "wait [DisplayTimeText(cords.next_command - world.time)]!")
|
||||
return FALSE
|
||||
if(isliving(owner))
|
||||
var/mob/living/living = owner
|
||||
if(!living.can_speak())
|
||||
if (feedback)
|
||||
owner.balloon_alert(owner, "can't speak!")
|
||||
return FALSE
|
||||
if(check_flags & AB_CHECK_CONSCIOUS)
|
||||
if(owner.stat)
|
||||
if (feedback)
|
||||
owner.balloon_alert(owner, "unconscious!")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/action/item_action/organ_action/colossus/do_effect(trigger_flags)
|
||||
var/command = tgui_input_text(owner, "Speak with the Voice of God", "Command", max_length = MAX_MESSAGE_LEN)
|
||||
if(!command)
|
||||
return FALSE
|
||||
if(QDELETED(src) || QDELETED(owner))
|
||||
return FALSE
|
||||
owner.say(".x[command]")
|
||||
return TRUE
|
||||
|
||||
/obj/item/organ/vocal_cords/colossus/can_speak_with()
|
||||
if(!owner)
|
||||
return FALSE
|
||||
|
||||
if(world.time < next_command)
|
||||
to_chat(owner, span_notice("You must wait [DisplayTimeText(next_command - world.time)] before Speaking again."))
|
||||
return FALSE
|
||||
|
||||
return owner.can_speak()
|
||||
|
||||
/obj/item/organ/vocal_cords/colossus/handle_speech(message)
|
||||
playsound(get_turf(owner), 'sound/effects/magic/clockwork/invoke_general.ogg', 300, TRUE, 5)
|
||||
return //voice of god speaks for us
|
||||
|
||||
/obj/item/organ/vocal_cords/colossus/speak_with(message)
|
||||
var/cooldown = voice_of_god(uppertext(message), owner, spans, base_multiplier)
|
||||
next_command = world.time + (cooldown * cooldown_mod)
|
||||
|
||||
/obj/item/organ/adamantine_resonator
|
||||
name = "adamantine resonator"
|
||||
desc = "Fragments of adamantine exist in all golems, stemming from their origins as purely magical constructs. These are used to \"hear\" messages from their leaders."
|
||||
|
||||
+16
-2
@@ -4922,9 +4922,23 @@
|
||||
#include "code\modules\mining\laborcamp\laborshuttle.dm"
|
||||
#include "code\modules\mining\laborcamp\laborstacker.dm"
|
||||
#include "code\modules\mining\lavaland\ash_flora.dm"
|
||||
#include "code\modules\mining\lavaland\megafauna_loot.dm"
|
||||
#include "code\modules\mining\lavaland\necropolis_chests.dm"
|
||||
#include "code\modules\mining\lavaland\tendril_loot.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\berserker.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\clothing.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\consumables.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\cursed_katana.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\equipment.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\eye_of_god.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\godslayer.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\megafauna\ash_drake.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\megafauna\blood_drunk.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\megafauna\bubblegum.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\megafauna\colossus.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\megafauna\demonic_frost_miner.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\megafauna\hierophant.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\megafauna\legion.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\megafauna\the_thing.dm"
|
||||
#include "code\modules\mining\lavaland\mining_loot\megafauna\wendigo.dm"
|
||||
#include "code\modules\mob\emote.dm"
|
||||
#include "code\modules\mob\inventory.dm"
|
||||
#include "code\modules\mob\login.dm"
|
||||
|
||||
Reference in New Issue
Block a user