mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 13:10:02 +01:00
Merge branch 'master' of https://github.com/tgstation/tgstation into upstream-15-10-2025
This commit is contained in:
@@ -1,261 +0,0 @@
|
||||
//Originally coded by ISaidNo, later modified by Kelenius. Ported from Baystation12.
|
||||
|
||||
/obj/structure/closet/crate/secure/loot
|
||||
name = "abandoned crate"
|
||||
desc = "What could be inside?"
|
||||
icon_state = "securecrate"
|
||||
base_icon_state = "securecrate"
|
||||
integrity_failure = 0 //no breaking open the crate
|
||||
var/code = null
|
||||
var/lastattempt = null
|
||||
var/attempts = 10
|
||||
var/codelen = 4
|
||||
var/qdel_on_open = FALSE
|
||||
var/spawned_loot = FALSE
|
||||
tamperproof = 90
|
||||
|
||||
// Stop people from "diving into" the crate accidentally, and then detonating it.
|
||||
divable = FALSE
|
||||
|
||||
/obj/structure/closet/crate/secure/loot/Initialize(mapload)
|
||||
. = ..()
|
||||
var/list/digits = list("1", "2", "3", "4", "5", "6", "7", "8", "9", "0")
|
||||
code = ""
|
||||
for(var/i in 1 to codelen)
|
||||
var/dig = pick(digits)
|
||||
code += dig
|
||||
digits -= dig //there are never matching digits in the answer
|
||||
|
||||
//ATTACK HAND IGNORING PARENT RETURN VALUE
|
||||
/obj/structure/closet/crate/secure/loot/attack_hand(mob/user, list/modifiers)
|
||||
if(locked)
|
||||
to_chat(user, span_notice("The crate is locked with a Deca-code lock."))
|
||||
var/input = input(usr, "Enter [codelen] digits. All digits must be unique.", "Deca-Code Lock", "") as text|null
|
||||
if(user.can_perform_action(src) && locked)
|
||||
var/list/sanitised = list()
|
||||
var/sanitycheck = TRUE
|
||||
var/char = ""
|
||||
var/length_input = length(input)
|
||||
for(var/i = 1, i <= length_input, i += length(char)) //put the guess into a list
|
||||
char = input[i]
|
||||
if(!(char >= "0" && char <= "9"))
|
||||
sanitycheck = FALSE //if a non-digit is found, reject the input
|
||||
sanitised += text2num(char)
|
||||
for(var/i in 1 to length(sanitised) - 1) //compare each digit in the guess to all those following it
|
||||
for(var/j in i + 1 to length(sanitised))
|
||||
if(sanitised[i] == sanitised[j])
|
||||
sanitycheck = FALSE //if a digit is repeated, reject the input
|
||||
if(input == code)
|
||||
if(!spawned_loot)
|
||||
spawn_loot()
|
||||
tamperproof = 0 // set explosion chance to zero, so we dont accidently hit it with a multitool and instantly die
|
||||
togglelock(user)
|
||||
else if(!input || !sanitycheck || length(sanitised) != codelen)
|
||||
to_chat(user, span_notice("You leave the crate alone."))
|
||||
else
|
||||
to_chat(user, span_warning("A red light flashes."))
|
||||
lastattempt = input
|
||||
attempts--
|
||||
if(attempts == 0)
|
||||
boom(user)
|
||||
return
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/structure/closet/crate/secure/loot/click_alt(mob/living/user)
|
||||
attack_hand(user) //this helps you not blow up so easily by overriding unlocking which results in an immediate boom.
|
||||
return CLICK_ACTION_SUCCESS
|
||||
|
||||
/obj/structure/closet/crate/secure/loot/attackby(obj/item/W, mob/user)
|
||||
if(locked)
|
||||
if(W.tool_behaviour == TOOL_MULTITOOL)
|
||||
to_chat(user, span_notice("DECA-CODE LOCK REPORT:"))
|
||||
if(attempts == 1)
|
||||
to_chat(user, span_warning("* Anti-Tamper Bomb will activate on next failed access attempt."))
|
||||
else
|
||||
to_chat(user, span_notice("* Anti-Tamper Bomb will activate after [attempts] failed access attempts."))
|
||||
if(lastattempt != null)
|
||||
var/bulls = 0 //right position, right number
|
||||
var/cows = 0 //wrong position but in the puzzle
|
||||
|
||||
var/lastattempt_char = ""
|
||||
var/length_lastattempt = length(lastattempt)
|
||||
var/lastattempt_it = 1
|
||||
|
||||
var/code_char = ""
|
||||
var/length_code = length(code)
|
||||
var/code_it = 1
|
||||
|
||||
while(lastattempt_it <= length_lastattempt && code_it <= length_code) // Go through list and count matches
|
||||
lastattempt_char = lastattempt[lastattempt_it]
|
||||
code_char = code[code_it]
|
||||
if(lastattempt_char == code_char)
|
||||
++bulls
|
||||
else if(findtext(code, lastattempt_char))
|
||||
++cows
|
||||
|
||||
lastattempt_it += length(lastattempt_char)
|
||||
code_it += length(code_char)
|
||||
|
||||
to_chat(user, span_notice("Last code attempt, [lastattempt], had [bulls] correct digits at correct positions and [cows] correct digits at incorrect positions."))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/structure/closet/crate/secure/loot/emag_act(mob/user, obj/item/card/emag/emag_card)
|
||||
. = ..()
|
||||
|
||||
if(locked)
|
||||
boom(user) // no feedback since it just explodes, thats its own feedback
|
||||
return TRUE
|
||||
return
|
||||
|
||||
/obj/structure/closet/crate/secure/loot/togglelock(mob/user, silent = FALSE)
|
||||
if(!locked)
|
||||
. = ..() //Run the normal code.
|
||||
if(locked) //Double check if the crate actually locked itself when the normal code ran.
|
||||
//reset the anti-tampering, number of attempts and last attempt when the lock is re-enabled.
|
||||
tamperproof = initial(tamperproof)
|
||||
attempts = initial(attempts)
|
||||
lastattempt = null
|
||||
return
|
||||
if(tamperproof)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/structure/closet/crate/secure/loot/atom_deconstruct(disassembled = TRUE)
|
||||
if(locked)
|
||||
boom()
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/structure/closet/crate/secure/loot/after_open(mob/living/user, force)
|
||||
. = ..()
|
||||
if(qdel_on_open)
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/closet/crate/secure/loot/proc/spawn_loot()
|
||||
var/loot = rand(1,100) //100 different crates with varying chances of spawning
|
||||
switch(loot)
|
||||
if(1 to 5) //5% chance
|
||||
new /obj/item/reagent_containers/cup/glass/bottle/rum(src)
|
||||
new /obj/item/reagent_containers/cup/glass/bottle/whiskey(src)
|
||||
new /obj/item/reagent_containers/cup/glass/bottle/whiskey(src)
|
||||
new /obj/item/lighter(src)
|
||||
new /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium(src)
|
||||
for(var/i in 1 to 3)
|
||||
new /obj/item/cigarette/rollie(src)
|
||||
if(6 to 10)
|
||||
new /obj/item/melee/skateboard/pro(src)
|
||||
if(11 to 15)
|
||||
new /mob/living/basic/bot/honkbot(src)
|
||||
if(16 to 20)
|
||||
new /obj/item/stack/ore/diamond(src, 10)
|
||||
if(21 to 25)
|
||||
for(var/i in 1 to 5)
|
||||
new /obj/item/poster/random_contraband(src)
|
||||
if(26 to 30)
|
||||
new /obj/item/vending_refill/sovietsoda(src)
|
||||
var/obj/item/circuitboard/machine/vendor/board = new (src)
|
||||
board.set_type(/obj/machinery/vending/sovietsoda)
|
||||
if(31 to 35)
|
||||
new /obj/item/seeds/firelemon(src)
|
||||
if(36 to 40)
|
||||
for(var/i in 1 to 5)
|
||||
new /obj/item/toy/snappop/phoenix(src)
|
||||
if(41 to 45)
|
||||
new /obj/item/modular_computer/pda/clear(src)
|
||||
if(46 to 50)
|
||||
new /obj/item/storage/box/syndie_kit/chameleon/broken
|
||||
if(51 to 52) // 2% chance
|
||||
new /obj/item/melee/baton(src)
|
||||
if(53 to 54)
|
||||
new /obj/item/toy/balloon/corgi(src)
|
||||
if(55 to 56)
|
||||
var/newitem = pick(subtypesof(/obj/item/toy/mecha))
|
||||
new newitem(src)
|
||||
if(57 to 58)
|
||||
new /obj/item/toy/balloon/syndicate(src)
|
||||
if(59 to 60)
|
||||
new /obj/item/borg/upgrade/modkit/aoe/mobs(src)
|
||||
new /obj/item/clothing/suit/space(src)
|
||||
new /obj/item/clothing/head/helmet/space(src)
|
||||
if(61 to 62)
|
||||
for(var/i in 1 to 5)
|
||||
new /obj/item/clothing/head/costume/kitty(src)
|
||||
new /obj/item/clothing/neck/petcollar(src)
|
||||
if(63 to 64)
|
||||
new /obj/item/clothing/shoes/kindle_kicks(src)
|
||||
if(65 to 66)
|
||||
new /obj/item/clothing/suit/costume/wellworn_shirt/graphic/ian(src)
|
||||
new /obj/item/clothing/suit/hooded/ian_costume(src)
|
||||
if(67 to 68)
|
||||
var/obj/item/gibtonite/free_bomb = new /obj/item/gibtonite(src)
|
||||
free_bomb.quality = rand(GIBTONITE_QUALITY_LOW, GIBTONITE_QUALITY_HIGH)
|
||||
free_bomb.GibtoniteReaction(null, "A secure loot closet has spawned a live")
|
||||
if(69 to 70)
|
||||
new /obj/item/stack/ore/bluespace_crystal(src, 5)
|
||||
if(71 to 72)
|
||||
new /obj/item/toy/plush/snakeplushie(src)
|
||||
if(73 to 74)
|
||||
new /mob/living/basic/pet/gondola(src)
|
||||
if(75 to 76)
|
||||
new /obj/item/bikehorn/airhorn(src)
|
||||
if(77 to 78)
|
||||
new /obj/item/toy/plush/lizard_plushie(src)
|
||||
if(79 to 80)
|
||||
new /obj/item/stack/sheet/mineral/bananium(src, 10)
|
||||
if(81 to 82)
|
||||
new /obj/item/bikehorn/airhorn(src)
|
||||
if(83 to 84)
|
||||
new /obj/item/toy/plush/beeplushie(src)
|
||||
if(85 to 86)
|
||||
new /obj/item/defibrillator/compact(src)
|
||||
if(87) //1% chance
|
||||
var/list/cannabis_seeds = typesof(/obj/item/seeds/cannabis)
|
||||
var/list/cannabis_plants = typesof(/obj/item/food/grown/cannabis)
|
||||
for(var/i in 1 to rand(2, 4))
|
||||
var/seed_type = pick(cannabis_seeds)
|
||||
new seed_type(src)
|
||||
for(var/i in 1 to rand(2, 4))
|
||||
var/cannabis_type = pick(cannabis_plants)
|
||||
new cannabis_type(src)
|
||||
if(88)
|
||||
new /obj/item/reagent_containers/cup/glass/bottle/lizardwine(src)
|
||||
if(89)
|
||||
new /obj/item/melee/energy/sword/bananium(src)
|
||||
if(90)
|
||||
new /obj/item/dnainjector/wackymut(src)
|
||||
if(91)
|
||||
for(var/i in 1 to 30)
|
||||
new /mob/living/basic/cockroach/bloodroach(src)
|
||||
if(92)
|
||||
new /obj/item/katana(src)
|
||||
if(93)
|
||||
new /obj/item/dnainjector/xraymut(src)
|
||||
if(94)
|
||||
new /mob/living/basic/mimic/crate(src)
|
||||
qdel_on_open = TRUE
|
||||
if(95)
|
||||
new /obj/item/toy/plush/nukeplushie(src)
|
||||
if(96)
|
||||
new /obj/item/banhammer(src)
|
||||
for(var/i in 1 to 3)
|
||||
var/obj/effect/mine/sound/bwoink/mine = new (src)
|
||||
mine.set_anchored(FALSE)
|
||||
mine.move_resist = MOVE_RESIST_DEFAULT
|
||||
if(97)
|
||||
for(var/i in 1 to 4)
|
||||
new /obj/item/clothing/mask/balaclava(src)
|
||||
new /obj/item/gun/ballistic/shotgun/toy(src)
|
||||
new /obj/item/gun/ballistic/automatic/pistol/toy(src)
|
||||
new /obj/item/gun/ballistic/automatic/toy(src)
|
||||
new /obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted(src)
|
||||
new /obj/item/ammo_box/foambox(src)
|
||||
if(98)
|
||||
for(var/i in 1 to 3)
|
||||
new /mob/living/basic/bee/toxin(src)
|
||||
if(99)
|
||||
new /obj/item/implanter/sad_trombone(src)
|
||||
if(100)
|
||||
new /obj/item/melee/skateboard/hoverboard(src)
|
||||
spawned_loot = TRUE
|
||||
@@ -61,7 +61,7 @@
|
||||
attacked_atom = singular_turf
|
||||
break
|
||||
|
||||
if(user.CanReach(attacked_atom))
|
||||
if(attacked_atom.IsReachableBy(user))
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
|
||||
var/atom/bullet = fire_projectile(/obj/projectile/grapple_hook, attacked_atom, 'sound/items/weapons/zipline_fire.ogg')
|
||||
|
||||
@@ -208,6 +208,14 @@
|
||||
SEND_SIGNAL(user, COMSIG_LIVING_CRUSHER_DETONATE, target, src, backstabbed)
|
||||
target.apply_damage(combined_damage, BRUTE, blocked = def_check)
|
||||
|
||||
/obj/item/kinetic_crusher/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
||||
if(!istype(interacting_with, /obj/item/crusher_trophy))
|
||||
return NONE
|
||||
var/obj/item/crusher_trophy/new_trophy = interacting_with
|
||||
if(new_trophy.add_to(src, user))
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
|
||||
/obj/item/kinetic_crusher/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
|
||||
if(!HAS_TRAIT(src, TRAIT_WIELDED))
|
||||
balloon_alert(user, "wield it first!")
|
||||
|
||||
@@ -25,11 +25,6 @@
|
||||
SHOULD_CALL_PARENT(FALSE)
|
||||
return "errors"
|
||||
|
||||
/obj/item/crusher_trophy/attackby(obj/item/attacking_item, mob/living/user)
|
||||
if(!istype(attacking_item, /obj/item/kinetic_crusher))
|
||||
return ..()
|
||||
add_to(attacking_item, user)
|
||||
|
||||
/// Tries to add the trophy to our crusher
|
||||
/obj/item/crusher_trophy/proc/add_to(obj/item/kinetic_crusher/crusher, mob/living/user)
|
||||
for(var/obj/item/crusher_trophy/trophy as anything in crusher.trophies)
|
||||
|
||||
@@ -60,9 +60,7 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sort_list(list(
|
||||
|
||||
/obj/item/stack/marker_beacon/click_alt(mob/living/user)
|
||||
var/input_color = tgui_input_list(user, "Choose a color", "Beacon Color", GLOB.marker_beacon_colors)
|
||||
if(isnull(input_color))
|
||||
return CLICK_ACTION_BLOCKING
|
||||
if(!user.can_perform_action(src))
|
||||
if(isnull(input_color) || !user.can_perform_action(src))
|
||||
return CLICK_ACTION_BLOCKING
|
||||
picked_color = input_color
|
||||
update_appearance()
|
||||
@@ -154,15 +152,12 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sort_list(list(
|
||||
|
||||
/obj/structure/marker_beacon/click_alt(mob/living/user)
|
||||
var/input_color = tgui_input_list(user, "Choose a color", "Beacon Color", GLOB.marker_beacon_colors)
|
||||
if(isnull(input_color))
|
||||
if(isnull(input_color) || !user.can_perform_action(src))
|
||||
return CLICK_ACTION_BLOCKING
|
||||
if(!user.can_perform_action(src))
|
||||
return NONE
|
||||
picked_color = input_color
|
||||
update_appearance()
|
||||
return CLICK_ACTION_SUCCESS
|
||||
|
||||
|
||||
/* Preset marker beacon types, for mapping */
|
||||
// Set the icon_state here to make it clear for mappers.
|
||||
|
||||
|
||||
@@ -73,6 +73,10 @@
|
||||
hitsound = 'sound/items/weapons/drill.ogg'
|
||||
desc = "An electric mining drill for the especially scrawny."
|
||||
|
||||
/obj/item/pickaxe/drill/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item) //closed handle
|
||||
|
||||
/obj/item/pickaxe/drill/diamonddrill
|
||||
name = "diamond-tipped mining drill"
|
||||
icon_state = "diamonddrill"
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#define BRIMDUST_STACKS_ON_LIFE 1
|
||||
/// Number of stacks to add if you activate the item in hand
|
||||
#define BRIMDUST_STACKS_ON_USE 3
|
||||
/// Overlay alpha per effect stack
|
||||
#define BRIMDUST_ALPHA_PER_STACK 75
|
||||
|
||||
/**
|
||||
* Gives you three stacks of Brimdust Coating, when you get hit by anything it will make a short ranged explosion.
|
||||
@@ -89,7 +91,8 @@
|
||||
/atom/movable/screen/alert/status_effect/brimdust_coating
|
||||
name = "Brimdust Coating"
|
||||
desc = "You %STACKS% explosive dust, kinetic impacts will cause it to detonate! \
|
||||
The explosion will not harm you as long as you're not under atmospheric pressure."
|
||||
The explosion will not harm you as long as you're not under atmospheric pressure. \
|
||||
Click this alert to shake off the dust."
|
||||
|
||||
/atom/movable/screen/alert/status_effect/brimdust_coating/MouseEntered(location,control,params)
|
||||
desc = initial(desc)
|
||||
@@ -106,6 +109,21 @@
|
||||
desc = replacetext(desc, "%STACKS%", dust_amount_string)
|
||||
return ..()
|
||||
|
||||
/atom/movable/screen/alert/status_effect/brimdust_coating/Click(location, control, params)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!owner.can_resist())
|
||||
return
|
||||
owner.balloon_alert(owner, "shaking off the dust...")
|
||||
var/datum/status_effect/stacking/brimdust_coating/dust = attached_effect
|
||||
if (!do_after(owner, dust.stacks * 1.5 SECONDS, owner))
|
||||
return
|
||||
dust.explode()
|
||||
// The effect can get deleted from exploding and reducing stacks
|
||||
if (!QDELETED(dust))
|
||||
qdel(dust)
|
||||
|
||||
/datum/status_effect/stacking/brimdust_coating/refresh(effect, stacks_to_add)
|
||||
. = ..()
|
||||
add_stacks(stacks_to_add)
|
||||
@@ -115,6 +133,10 @@
|
||||
if (stacks == 0)
|
||||
return
|
||||
linked_alert.icon_state = "brimdemon_[stacks]"
|
||||
if (dust_overlay)
|
||||
owner.cut_overlay(dust_overlay)
|
||||
dust_overlay.alpha = stacks * BRIMDUST_ALPHA_PER_STACK
|
||||
owner.add_overlay(dust_overlay)
|
||||
|
||||
/datum/status_effect/stacking/brimdust_coating/on_creation(mob/living/new_owner, stacks_to_apply)
|
||||
. = ..()
|
||||
@@ -122,16 +144,19 @@
|
||||
|
||||
/datum/status_effect/stacking/brimdust_coating/on_apply()
|
||||
. = ..()
|
||||
|
||||
dust_overlay = mutable_appearance('icons/effects/weather_effects.dmi', "ash_storm")
|
||||
dust_overlay.alpha = stacks * BRIMDUST_ALPHA_PER_STACK
|
||||
dust_overlay.color = COLOR_RED_LIGHT
|
||||
dust_overlay.blend_mode = BLEND_INSET_OVERLAY
|
||||
owner.add_overlay(dust_overlay)
|
||||
owner.add_shared_particles(/particles/brimdust)
|
||||
RegisterSignal(owner, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_cleaned))
|
||||
RegisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(on_take_damage))
|
||||
|
||||
/datum/status_effect/stacking/brimdust_coating/on_remove()
|
||||
. = ..()
|
||||
owner.cut_overlay(dust_overlay)
|
||||
owner.remove_shared_particles(/particles/brimdust)
|
||||
UnregisterSignal(owner, list(COMSIG_MOB_APPLY_DAMAGE, COMSIG_COMPONENT_CLEAN_ACT))
|
||||
|
||||
/// When you are cleaned, wash off the buff
|
||||
@@ -162,14 +187,14 @@
|
||||
new /obj/effect/temp_visual/explosion/fast(origin_turf)
|
||||
|
||||
var/damage_dealt = blast_damage
|
||||
var/list/possible_targets = range(1, origin_turf)
|
||||
if(lavaland_equipment_pressure_check(origin_turf))
|
||||
possible_targets -= owner
|
||||
else
|
||||
var/safe_explosion = lavaland_equipment_pressure_check(origin_turf)
|
||||
if(!safe_explosion)
|
||||
damage_dealt *= pressure_modifier
|
||||
owner.apply_status_effect(/datum/status_effect/brimdust_concussion)
|
||||
|
||||
for(var/mob/living/target in possible_targets)
|
||||
for(var/mob/living/target in range(1, origin_turf) - (safe_explosion ? list(owner, owner.buckled) : null))
|
||||
if (safe_explosion && owner.faction_check_atom(target))
|
||||
continue
|
||||
var/armor = target.run_armor_check(attack_flag = BOMB)
|
||||
target.apply_damage(damage_dealt, damagetype = BURN, blocked = armor, spread_damage = TRUE)
|
||||
|
||||
@@ -202,3 +227,4 @@
|
||||
#undef BRIMDUST_LIFE_APPLY_COOLDOWN
|
||||
#undef BRIMDUST_STACKS_ON_LIFE
|
||||
#undef BRIMDUST_STACKS_ON_USE
|
||||
#undef BRIMDUST_ALPHA_PER_STACK
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define HEALTH_DANGER_ZONE 30
|
||||
|
||||
/**
|
||||
* On use in hand, makes you run really fast for 5 seconds and ignore injury movement decrease.
|
||||
* On use in hand, makes you run faster for a bit and ignore injury movement decrease.
|
||||
* On use when implanted, run for longer and ignore all negative movement. Automatically triggers if health is low (to escape).
|
||||
*/
|
||||
/obj/item/organ/monster_core/rush_gland
|
||||
@@ -37,13 +37,13 @@
|
||||
INVOKE_ASYNC(src, PROC_REF(trigger_organ_action))
|
||||
|
||||
/**
|
||||
* Status effect: Makes you run really fast and ignore speed penalties for a short duration.
|
||||
* Status effect: Makes you run faster and ignore damage speed penalties for a short duration.
|
||||
* If you run into a wall indoors you will fall over and lose the buff.
|
||||
* If you run into someone you both fall over.
|
||||
*/
|
||||
/datum/status_effect/lobster_rush
|
||||
id = "lobster_rush"
|
||||
duration = 3 SECONDS
|
||||
duration = 30 SECONDS
|
||||
alert_type = /atom/movable/screen/alert/status_effect/lobster_rush
|
||||
show_duration = TRUE
|
||||
var/spawned_last_move = FALSE
|
||||
@@ -55,50 +55,58 @@
|
||||
|
||||
/datum/status_effect/lobster_rush/on_apply()
|
||||
. = ..()
|
||||
RegisterSignal(owner, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_move))
|
||||
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
|
||||
RegisterSignal(owner, COMSIG_MOVABLE_BUMP, PROC_REF(on_bump))
|
||||
owner.add_traits(list(TRAIT_IGNORESLOWDOWN, TRAIT_TENTACLE_IMMUNE), TRAIT_STATUS_EFFECT(id))
|
||||
ADD_TRAIT(owner, TRAIT_TENTACLE_IMMUNE, TRAIT_STATUS_EFFECT(id))
|
||||
owner.add_movespeed_mod_immunities(id, /datum/movespeed_modifier/damage_slowdown)
|
||||
owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/lobster_rush)
|
||||
to_chat(owner, span_notice("You feel your blood pumping!"))
|
||||
|
||||
/datum/status_effect/lobster_rush/on_remove()
|
||||
. = ..()
|
||||
UnregisterSignal(owner, list(COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOVABLE_BUMP))
|
||||
owner.remove_traits(list(TRAIT_IGNORESLOWDOWN, TRAIT_TENTACLE_IMMUNE), TRAIT_STATUS_EFFECT(id))
|
||||
UnregisterSignal(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_BUMP))
|
||||
REMOVE_TRAIT(owner, TRAIT_TENTACLE_IMMUNE, TRAIT_STATUS_EFFECT(id))
|
||||
owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/lobster_rush)
|
||||
owner.remove_movespeed_mod_immunities(id, /datum/movespeed_modifier/damage_slowdown)
|
||||
to_chat(owner, span_notice("Your pulse returns to normal."))
|
||||
|
||||
/// Spawn an afterimage every other step, because every step was too many
|
||||
/datum/status_effect/lobster_rush/proc/on_move()
|
||||
/datum/status_effect/lobster_rush/proc/on_move(datum/source, atom/old_loc, dir)
|
||||
SIGNAL_HANDLER
|
||||
if (!isturf(old_loc) || !isturf(owner.loc))
|
||||
return
|
||||
if (!spawned_last_move)
|
||||
new /obj/effect/temp_visual/decoy/fading(owner.loc, owner)
|
||||
new /obj/effect/temp_visual/decoy/fading(old_loc, owner, 150)
|
||||
spawned_last_move = !spawned_last_move
|
||||
|
||||
/datum/status_effect/lobster_rush/proc/on_bump(mob/living/source, atom/target)
|
||||
SIGNAL_HANDLER
|
||||
if (!target.density)
|
||||
return
|
||||
if (isliving(target))
|
||||
source.visible_message(span_warning("[source] crashes into [target]!"))
|
||||
smack_into(source)
|
||||
smack_into(target)
|
||||
qdel(src)
|
||||
return
|
||||
if (lavaland_equipment_pressure_check(get_turf(source)))
|
||||
return
|
||||
smack_into(source)
|
||||
source.visible_message(span_warning("[source] crashes into [target]!"))
|
||||
if (isliving(target))
|
||||
smack_into(target)
|
||||
qdel(src)
|
||||
|
||||
/datum/status_effect/lobster_rush/proc/smack_into(mob/living/target)
|
||||
target.Knockdown(5 SECONDS)
|
||||
target.apply_damage(40, STAMINA)
|
||||
target.apply_damage(20, BRUTE, spread_damage = TRUE)
|
||||
target.Knockdown(2 SECONDS)
|
||||
target.apply_damage(30, STAMINA)
|
||||
target.apply_damage(10, BRUTE, spread_damage = TRUE)
|
||||
|
||||
/// You get a longer buff if you take the time to implant it in yourself
|
||||
/datum/status_effect/lobster_rush/extended
|
||||
duration = 5 SECONDS
|
||||
duration = 60 SECONDS
|
||||
|
||||
/datum/status_effect/lobster_rush/extended/on_apply()
|
||||
. = ..()
|
||||
ADD_TRAIT(owner, TRAIT_IGNORESLOWDOWN, TRAIT_STATUS_EFFECT(id))
|
||||
|
||||
/datum/status_effect/lobster_rush/extended/on_remove()
|
||||
. = ..()
|
||||
REMOVE_TRAIT(owner, TRAIT_IGNORESLOWDOWN, TRAIT_STATUS_EFFECT(id))
|
||||
|
||||
/// Action used by the rush gland
|
||||
/datum/action/cooldown/monster_core_action/adrenal_boost
|
||||
@@ -106,6 +114,6 @@
|
||||
desc = "Pump your rush gland to give yourself a boost of speed. \
|
||||
Impacts with objects can be dangerous under atmospheric pressure."
|
||||
button_icon_state = "lobster_gland_stable"
|
||||
cooldown_time = 90 SECONDS
|
||||
cooldown_time = 180 SECONDS
|
||||
|
||||
#undef HEALTH_DANGER_ZONE
|
||||
|
||||
@@ -24,39 +24,40 @@
|
||||
reach = 2
|
||||
attack_icon = 'icons/effects/effects.dmi'
|
||||
attack_icon_state = "cain_abel_attack"
|
||||
///our current combo count
|
||||
/// Our current combo count
|
||||
var/combo_count = 0
|
||||
///the maximum combo we can reach
|
||||
/// The maximum combo we can reach
|
||||
var/max_combo = 6
|
||||
///percentage boost we get on every combo
|
||||
/// Percentage boost we get on every combo
|
||||
var/damage_boost = 1.15
|
||||
///pixel offsets of our wisps
|
||||
var/static/list/wisp_offsets = list(
|
||||
list(9, 12),
|
||||
list(14, 0),
|
||||
list(9, -12),
|
||||
list(-9, 12),
|
||||
list(-14, 0),
|
||||
list(-9, -12),
|
||||
)
|
||||
///what throw mode we're using
|
||||
/// Animation positions used by wisps
|
||||
var/static/list/animation_steps
|
||||
/// What throw mode we're using
|
||||
var/throw_mode = THROW_MODE_CRYSTALS
|
||||
///flames we have up!
|
||||
/// Flames we have up!
|
||||
var/list/current_wisps = list()
|
||||
///cooldown till we can throw blades again
|
||||
/// Cooldown till we can throw blades again
|
||||
COOLDOWN_DECLARE(throw_cooldown)
|
||||
|
||||
/obj/item/cain_and_abel/Initialize(mapload)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/two_handed, require_twohands = TRUE, force_unwielded = force, force_wielded = force)
|
||||
if (length(animation_steps))
|
||||
return
|
||||
|
||||
animation_steps = list(
|
||||
new /datum/abel_wisp_frame(-18, -2, MOB_LAYER, EASE_OUT, EASE_IN),
|
||||
new /datum/abel_wisp_frame(-6, -6, ABOVE_MOB_LAYER, EASE_IN, EASE_OUT),
|
||||
new /datum/abel_wisp_frame(6, -6, ABOVE_MOB_LAYER, EASE_IN, EASE_OUT),
|
||||
new /datum/abel_wisp_frame(18, -2, MOB_LAYER, EASE_OUT, EASE_IN),
|
||||
new /datum/abel_wisp_frame(6, 2, BELOW_MOB_LAYER, EASE_IN, EASE_OUT),
|
||||
new /datum/abel_wisp_frame(-6, 2, BELOW_MOB_LAYER, EASE_IN, EASE_OUT),
|
||||
)
|
||||
|
||||
/obj/item/cain_and_abel/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
|
||||
. = ..()
|
||||
if(!isliving(old_loc))
|
||||
return
|
||||
|
||||
unset_user(old_loc)
|
||||
if(isliving(old_loc))
|
||||
unset_user(old_loc)
|
||||
|
||||
/obj/item/cain_and_abel/proc/unset_user(mob/living/source)
|
||||
if(HAS_TRAIT(source, TRAIT_RELAYING_ATTACKER))
|
||||
@@ -65,7 +66,6 @@
|
||||
set_combo(new_value = 0, user = source)
|
||||
UnregisterSignal(source, list(COMSIG_ATOM_WAS_ATTACKED))
|
||||
|
||||
|
||||
/obj/item/cain_and_abel/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
|
||||
@@ -92,8 +92,6 @@
|
||||
|
||||
for(var/index in 0 to (length(current_wisps) - 1))
|
||||
addtimer(CALLBACK(src, PROC_REF(fire_wisp), user, interacting_with), index * 0.15 SECONDS)
|
||||
|
||||
set_combo(new_value = 0, user = user)
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
|
||||
/obj/item/cain_and_abel/attack(mob/living/target, mob/living/carbon/human/user)
|
||||
@@ -117,29 +115,55 @@
|
||||
user.balloon_alert(user, "crystals [throw_mode == THROW_MODE_CRYSTALS ? "activated" : "deactivated"]")
|
||||
return TRUE
|
||||
|
||||
/obj/item/cain_and_abel/proc/set_combo(new_value, mob/living/user)
|
||||
combo_count = (new_value <= max_combo) ? new_value : 0
|
||||
handle_wisps(user)
|
||||
/obj/item/cain_and_abel/proc/set_combo(new_value, mob/living/user, instant = FALSE)
|
||||
new_value = clamp(new_value, 0, max_combo)
|
||||
if (new_value == combo_count)
|
||||
return
|
||||
|
||||
/obj/item/cain_and_abel/proc/handle_wisps(mob/living/user)
|
||||
var/should_remove = length(current_wisps) > combo_count
|
||||
var/wisps_to_alter = abs(combo_count - length(current_wisps))
|
||||
|
||||
for(var/i = 1, i <= wisps_to_alter, i++)
|
||||
if(!should_remove)
|
||||
if (new_value > combo_count)
|
||||
for (var/i in 1 to new_value - combo_count)
|
||||
add_wisp(user)
|
||||
continue
|
||||
var/obj/my_wisp = current_wisps[i]
|
||||
remove_wisp(my_wisp)
|
||||
else
|
||||
for (var/i in 1 to combo_count - new_value)
|
||||
remove_wisp(current_wisps[i], instant)
|
||||
|
||||
combo_count = new_value
|
||||
|
||||
/obj/item/cain_and_abel/proc/add_wisp(mob/living/user)
|
||||
var/obj/effect/overlay/blood_wisp/new_wisp = new(src)
|
||||
current_wisps += new_wisp
|
||||
var/list/position = wisp_offsets[length(current_wisps)]
|
||||
user.vis_contents += new_wisp
|
||||
new_wisp.pixel_x = position[1]
|
||||
new_wisp.pixel_y = position[2]
|
||||
RegisterSignal(new_wisp, COMSIG_QDELETING, PROC_REF(on_wisp_delete))
|
||||
for (var/wisp_index in 1 to length(current_wisps))
|
||||
var/obj/effect/overlay/blood_wisp/wisp = current_wisps[wisp_index]
|
||||
var/spawn_index = floor(length(animation_steps) / length(current_wisps) * wisp_index)
|
||||
var/datum/abel_wisp_frame/spawn_position = animation_steps[spawn_index]
|
||||
|
||||
// Latest added wisp is teleported to its spawn position, others animate from their current position
|
||||
if (wisp_index == length(current_wisps))
|
||||
wisp.pixel_w = spawn_position.x
|
||||
wisp.pixel_z = spawn_position.y
|
||||
wisp.layer = spawn_position.layer
|
||||
else
|
||||
animate(wisp, tag = "wisp_anim_x")
|
||||
animate(wisp, tag = "wisp_anim_y")
|
||||
|
||||
var/start_index = spawn_index % length(animation_steps) + 1
|
||||
var/datum/abel_wisp_frame/position = animation_steps[start_index]
|
||||
animate(wisp, pixel_w = position.x, layer = position.layer, time = 0.6 SECONDS, loop = -1, tag = "wisp_anim_x")
|
||||
// We need to animate x and y coordinates separately as they have different easing steps
|
||||
for (var/frame_index in 1 to length(animation_steps) - 1)
|
||||
// Get actual index starting from our *next* animation step, not initial position
|
||||
var/anim_index = (start_index + frame_index - 1) % length(animation_steps) + 1
|
||||
position = animation_steps[anim_index]
|
||||
animate(time = 0.6 SECONDS, pixel_w = position.x, layer = position.layer, easing = SINE_EASING | position.x_easing)
|
||||
|
||||
animate(wisp, time = 0.6 SECONDS, pixel_z = position.y, loop = -1, tag = "wisp_anim_y")
|
||||
for (var/frame_index in 1 to length(animation_steps) - 1)
|
||||
// Get actual index starting from our *next* animation step, not initial position
|
||||
var/anim_index = (start_index + frame_index - 1) % length(animation_steps) + 1
|
||||
position = animation_steps[anim_index]
|
||||
animate(time = 0.6 SECONDS, pixel_z = position.y, easing = SINE_EASING | position.y_easing)
|
||||
|
||||
/obj/item/cain_and_abel/proc/on_wisp_delete(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
@@ -148,7 +172,27 @@
|
||||
|
||||
/obj/item/cain_and_abel/proc/fire_wisp(atom/user, atom/target)
|
||||
user.fire_projectile(/obj/projectile/dagger_wisp, target)
|
||||
set_combo(combo_count - 1, user, TRUE)
|
||||
|
||||
/obj/item/cain_and_abel/proc/remove_wisp(obj/wisp_to_remove)
|
||||
/obj/item/cain_and_abel/proc/remove_wisp(obj/wisp_to_remove, instant = FALSE)
|
||||
if (instant)
|
||||
qdel(wisp_to_remove)
|
||||
return
|
||||
animate(wisp_to_remove, alpha = 0, time = 0.2 SECONDS)
|
||||
QDEL_IN(wisp_to_remove, 0.2 SECONDS)
|
||||
|
||||
/// Frame data for cain & abel wisps so we don't have to make list monstrocities
|
||||
/datum/abel_wisp_frame
|
||||
var/x = 0
|
||||
var/y = 0
|
||||
var/layer = MOB_LAYER
|
||||
var/x_easing = NONE
|
||||
var/y_easing = NONE
|
||||
|
||||
/datum/abel_wisp_frame/New(x, y, layer, x_easing, y_easing)
|
||||
. = ..()
|
||||
src.x = x
|
||||
src.y = y
|
||||
src.layer = layer
|
||||
src.x_easing = x_easing
|
||||
src.y_easing = y_easing
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//effect when we're swinging wildly around
|
||||
/obj/effect/temp_visual/dagger_slash
|
||||
name = "Blood Wisp"
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
anchored = TRUE
|
||||
vis_flags = VIS_INHERIT_DIR | VIS_INHERIT_PLANE
|
||||
@@ -19,10 +18,10 @@
|
||||
|
||||
//flames we collect around our body
|
||||
/obj/effect/overlay/blood_wisp
|
||||
name = "Blood Wisp"
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
anchored = TRUE
|
||||
vis_flags = VIS_INHERIT_DIR | VIS_INHERIT_PLANE
|
||||
layer = ABOVE_HUD_PLANE
|
||||
vis_flags = VIS_INHERIT_PLANE
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "blood_wisp"
|
||||
light_power = 2
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
// consumer.dna.features = list(
|
||||
// FEATURE_MUTANT_COLOR = "#A02720",
|
||||
// FEATURE_TAIL_LIZARD = "Dark Tiger",
|
||||
// FEATURE_TAIL = "None",
|
||||
// FEATURE_TAIL_CAT = "None",
|
||||
// FEATURE_SNOUT = "Sharp",
|
||||
// FEATURE_HORNS = "Curled",
|
||||
// FEATURE_EARS = "None",
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
/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)
|
||||
if (interacting_with == beacon || !isturf(interacting_with.loc))
|
||||
return NONE
|
||||
|
||||
if (!blink_activated)
|
||||
|
||||
@@ -96,8 +96,8 @@
|
||||
/obj/structure/closet/crate/necropolis/colossus
|
||||
name = "colossus chest"
|
||||
|
||||
/obj/structure/closet/crate/necropolis/colossus/bullet_act(obj/projectile/proj)
|
||||
if(istype(proj, /obj/projectile/colossus))
|
||||
/obj/structure/closet/crate/necropolis/colossus/projectile_hit(obj/projectile/hitting_projectile, def_zone, piercing_hit, blocked)
|
||||
if(istype(hitting_projectile, /obj/projectile/colossus))
|
||||
return BULLET_ACT_FORCE_PIERCE
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -648,7 +648,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
|
||||
name = "eldritch coin"
|
||||
desc = "A surprisingly heavy, ornate coin. Its sides seem to depict a different image each time you look."
|
||||
icon_state = "coin_heretic"
|
||||
custom_materials = list(/datum/material/diamond =HALF_SHEET_MATERIAL_AMOUNT, /datum/material/plasma =HALF_SHEET_MATERIAL_AMOUNT)
|
||||
custom_materials = list(/datum/material/plasma = HALF_SHEET_MATERIAL_AMOUNT)
|
||||
sideslist = list("heretic", "blade")
|
||||
heads_name = "heretic"
|
||||
has_action = TRUE
|
||||
@@ -679,17 +679,4 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
|
||||
continue
|
||||
target_airlock.lock()
|
||||
|
||||
/obj/item/coin/eldritch/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
||||
if(!istype(interacting_with, /obj/machinery/door/airlock))
|
||||
return NONE
|
||||
if(!IS_HERETIC(user))
|
||||
user.adjustBruteLoss(5)
|
||||
user.adjustFireLoss(5)
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
var/obj/machinery/door/airlock/target_airlock = interacting_with
|
||||
to_chat(user, span_warning("You insert [src] into the airlock."))
|
||||
target_airlock.emag_act(user, src)
|
||||
qdel(src)
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
|
||||
#undef ORESTACK_OVERLAYS_MAX
|
||||
|
||||
Reference in New Issue
Block a user