mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-06-04 05:34:50 +01:00
f501b1e49e
Doubles the range of the MOD Pathfinder AI Fixes modules rendering below the suit. Adds the ability for modules to be used when inactive. Documents/cleans up some code. Updates some old descriptions and explains some concepts better. Armor Booster and Ash Accretion can no longer boost your speed over no slowdown at all. Makes flashlight module start with 4 instead of 3 range, so it's better for people that don't know about configuration. Doubles t-ray module range, from 2 to 4 (t-ray scanner is 3). Puts the noslip module lower in progression, lowers its' price to 2. New sprites for the magnetic harness module by Onule. Brings back the holster module, it can now be used when the suit is inactive, can be printed with security suit research. Adds the power kick module for the ERT Commander. It's a powerful kick.
615 lines
23 KiB
Plaintext
615 lines
23 KiB
Plaintext
//Supply modules for MODsuits
|
|
|
|
///Internal GPS - Extends a GPS you can use.
|
|
/obj/item/mod/module/gps
|
|
name = "MOD internal GPS module"
|
|
desc = "This module uses common Nanotrasen technology to calculate the user's position anywhere in space, \
|
|
down to the exact coordinates. This information is fed to a central database viewable from the device itself, \
|
|
though using it to help people is up to you."
|
|
icon_state = "gps"
|
|
module_type = MODULE_USABLE
|
|
complexity = 1
|
|
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
|
|
incompatible_modules = list(/obj/item/mod/module/gps)
|
|
cooldown_time = 0.5 SECONDS
|
|
|
|
/obj/item/mod/module/gps/Initialize(mapload)
|
|
. = ..()
|
|
AddComponent(/datum/component/gps/item, "MOD0", state = GLOB.deep_inventory_state, overlay_state = FALSE)
|
|
|
|
/obj/item/mod/module/gps/on_use()
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
attack_self(mod.wearer)
|
|
|
|
///Hydraulic Clamp - Lets you pick up and drop crates.
|
|
/obj/item/mod/module/clamp
|
|
name = "MOD hydraulic clamp module"
|
|
desc = "A series of actuators installed into both arms of the suit, boasting a lifting capacity of almost a ton. \
|
|
However, this design has been locked by Nanotrasen to be primarily utilized for lifting various crates. \
|
|
A lot of people would say that loading cargo is a dull job, but you could not disagree more."
|
|
icon_state = "clamp"
|
|
module_type = MODULE_ACTIVE
|
|
complexity = 3
|
|
use_power_cost = DEFAULT_CHARGE_DRAIN
|
|
incompatible_modules = list(/obj/item/mod/module/clamp)
|
|
cooldown_time = 0.5 SECONDS
|
|
overlay_state_inactive = "module_clamp"
|
|
overlay_state_active = "module_clamp_on"
|
|
/// Time it takes to load a crate.
|
|
var/load_time = 3 SECONDS
|
|
/// The max amount of crates you can carry.
|
|
var/max_crates = 3
|
|
/// The crates stored in the module.
|
|
var/list/stored_crates = list()
|
|
|
|
/obj/item/mod/module/clamp/on_select_use(atom/target)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
if(!mod.wearer.Adjacent(target))
|
|
return
|
|
if(istype(target, /obj/structure/closet/crate) || istype(target, /obj/item/delivery/big))
|
|
var/atom/movable/picked_crate = target
|
|
if(!check_crate_pickup(picked_crate))
|
|
return
|
|
playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
|
|
if(!do_after(mod.wearer, load_time, target = target))
|
|
balloon_alert(mod.wearer, "interrupted!")
|
|
return
|
|
if(!check_crate_pickup(picked_crate))
|
|
return
|
|
stored_crates += picked_crate
|
|
picked_crate.forceMove(src)
|
|
balloon_alert(mod.wearer, "picked up [picked_crate]")
|
|
drain_power(use_power_cost)
|
|
else if(length(stored_crates))
|
|
var/turf/target_turf = get_turf(target)
|
|
if(target_turf.is_blocked_turf())
|
|
return
|
|
playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
|
|
if(!do_after(mod.wearer, load_time, target = target))
|
|
balloon_alert(mod.wearer, "interrupted!")
|
|
return
|
|
if(target_turf.is_blocked_turf())
|
|
return
|
|
var/atom/movable/dropped_crate = pop(stored_crates)
|
|
dropped_crate.forceMove(target_turf)
|
|
balloon_alert(mod.wearer, "dropped [dropped_crate]")
|
|
drain_power(use_power_cost)
|
|
else
|
|
balloon_alert(mod.wearer, "invalid target!")
|
|
|
|
/obj/item/mod/module/clamp/on_suit_deactivation(deleting = FALSE)
|
|
if(deleting)
|
|
return
|
|
for(var/atom/movable/crate as anything in stored_crates)
|
|
crate.forceMove(drop_location())
|
|
stored_crates -= crate
|
|
|
|
/obj/item/mod/module/clamp/proc/check_crate_pickup(atom/movable/target)
|
|
if(length(stored_crates) >= max_crates)
|
|
balloon_alert(mod.wearer, "too many crates!")
|
|
return FALSE
|
|
for(var/mob/living/mob in target.get_all_contents())
|
|
if(mob.mob_size < MOB_SIZE_HUMAN)
|
|
continue
|
|
balloon_alert(mod.wearer, "crate too heavy!")
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/item/mod/module/clamp/loader
|
|
name = "MOD loader hydraulic clamp module"
|
|
icon_state = "clamp_loader"
|
|
complexity = 0
|
|
removable = FALSE
|
|
overlay_state_inactive = null
|
|
overlay_state_active = "module_clamp_loader"
|
|
load_time = 1 SECONDS
|
|
max_crates = 5
|
|
use_mod_colors = TRUE
|
|
|
|
///Drill - Lets you dig through rock and basalt.
|
|
/obj/item/mod/module/drill
|
|
name = "MOD drill module"
|
|
desc = "An integrated drill, typically extending over the user's hand. While useful for drilling through rock, \
|
|
your drill is surely the one that both pierces and creates the heavens."
|
|
icon_state = "drill"
|
|
module_type = MODULE_ACTIVE
|
|
complexity = 2
|
|
use_power_cost = DEFAULT_CHARGE_DRAIN
|
|
incompatible_modules = list(/obj/item/mod/module/drill)
|
|
cooldown_time = 0.5 SECONDS
|
|
overlay_state_active = "module_drill"
|
|
|
|
/obj/item/mod/module/drill/on_activation()
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
RegisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP, .proc/bump_mine)
|
|
|
|
/obj/item/mod/module/drill/on_deactivation(display_message = TRUE, deleting = FALSE)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP)
|
|
|
|
/obj/item/mod/module/drill/on_select_use(atom/target)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
if(!mod.wearer.Adjacent(target))
|
|
return
|
|
if(istype(target, /turf/closed/mineral))
|
|
var/turf/closed/mineral/mineral_turf = target
|
|
mineral_turf.gets_drilled(mod.wearer)
|
|
drain_power(use_power_cost)
|
|
else if(istype(target, /turf/open/misc/asteroid))
|
|
var/turf/open/misc/asteroid/sand_turf = target
|
|
if(!sand_turf.can_dig(mod.wearer))
|
|
return
|
|
sand_turf.getDug()
|
|
drain_power(use_power_cost)
|
|
|
|
/obj/item/mod/module/drill/proc/bump_mine(mob/living/carbon/human/bumper, atom/bumped_into, proximity)
|
|
SIGNAL_HANDLER
|
|
if(!istype(bumped_into, /turf/closed/mineral) || !drain_power(use_power_cost))
|
|
return
|
|
var/turf/closed/mineral/mineral_turf = bumped_into
|
|
mineral_turf.gets_drilled(mod.wearer)
|
|
return COMPONENT_CANCEL_ATTACK_CHAIN
|
|
|
|
///Ore Bag - Lets you pick up ores and drop them from the suit.
|
|
/obj/item/mod/module/orebag
|
|
name = "MOD ore bag module"
|
|
desc = "An integrated ore storage system installed into the suit, \
|
|
this utilizes precise electromagnets and storage compartments to automatically collect and deposit ore. \
|
|
It's recommended by Nakamura Engineering to actually deposit that ore at local refineries."
|
|
icon_state = "ore"
|
|
module_type = MODULE_USABLE
|
|
complexity = 1
|
|
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
|
|
incompatible_modules = list(/obj/item/mod/module/orebag)
|
|
cooldown_time = 0.5 SECONDS
|
|
/// The ores stored in the bag.
|
|
var/list/ores = list()
|
|
|
|
/obj/item/mod/module/orebag/on_equip()
|
|
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/ore_pickup)
|
|
|
|
/obj/item/mod/module/orebag/on_unequip()
|
|
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
|
|
|
|
/obj/item/mod/module/orebag/proc/ore_pickup(atom/movable/source, atom/old_loc, dir, forced)
|
|
SIGNAL_HANDLER
|
|
|
|
for(var/obj/item/stack/ore/ore in get_turf(mod.wearer))
|
|
INVOKE_ASYNC(src, .proc/move_ore, ore)
|
|
playsound(src, SFX_RUSTLE, 50, TRUE)
|
|
|
|
/obj/item/mod/module/orebag/proc/move_ore(obj/item/stack/ore)
|
|
for(var/obj/item/stack/stored_ore as anything in ores)
|
|
if(!ore.can_merge(stored_ore))
|
|
continue
|
|
ore.merge(stored_ore)
|
|
if(QDELETED(ore))
|
|
return
|
|
break
|
|
ore.forceMove(src)
|
|
ores += ore
|
|
|
|
/obj/item/mod/module/orebag/on_use()
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
for(var/obj/item/ore as anything in ores)
|
|
ore.forceMove(drop_location())
|
|
ores -= ore
|
|
drain_power(use_power_cost)
|
|
|
|
/obj/item/mod/module/hydraulic
|
|
name = "MOD loader hydraulic arms module"
|
|
desc = "A pair of powerful hydraulic arms installed in a MODsuit."
|
|
icon_state = "launch_loader"
|
|
module_type = MODULE_ACTIVE
|
|
removable = FALSE
|
|
use_power_cost = DEFAULT_CHARGE_DRAIN*10
|
|
incompatible_modules = list(/obj/item/mod/module/hydraulic)
|
|
cooldown_time = 4 SECONDS
|
|
overlay_state_inactive = "module_hydraulic"
|
|
overlay_state_active = "module_hydraulic_active"
|
|
use_mod_colors = TRUE
|
|
/// Time it takes to launch
|
|
var/launch_time = 2 SECONDS
|
|
/// User overlay
|
|
var/mutable_appearance/lightning
|
|
|
|
/obj/item/mod/module/hydraulic/on_select_use(atom/target)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
var/atom/game_renderer = mod.wearer.hud_used.plane_masters["[RENDER_PLANE_GAME]"]
|
|
var/matrix/render_matrix = matrix(game_renderer.transform)
|
|
render_matrix.Scale(1.25, 1.25)
|
|
animate(game_renderer, launch_time, flags = SINE_EASING|EASE_IN, transform = render_matrix)
|
|
var/current_time = world.time
|
|
mod.wearer.visible_message(span_warning("[mod.wearer] starts whirring!"), \
|
|
blind_message = span_hear("You hear a whirring sound."))
|
|
playsound(src, 'sound/items/modsuit/loader_charge.ogg', 75, TRUE)
|
|
lightning = mutable_appearance('icons/effects/effects.dmi', "electricity3", plane = GAME_PLANE_FOV_HIDDEN)
|
|
mod.wearer.add_overlay(lightning)
|
|
balloon_alert(mod.wearer, "you start charging...")
|
|
var/power = launch_time
|
|
if(!do_after(mod.wearer, launch_time, target = mod))
|
|
power = world.time - current_time
|
|
animate(game_renderer)
|
|
drain_power(use_power_cost)
|
|
new /obj/effect/temp_visual/mook_dust(get_turf(src))
|
|
playsound(src, 'sound/items/modsuit/loader_launch.ogg', 75, TRUE)
|
|
game_renderer.transform = game_renderer.transform.Scale(0.8, 0.8)
|
|
mod.wearer.cut_overlay(lightning)
|
|
var/angle = get_angle(mod.wearer, target)
|
|
mod.wearer.transform = mod.wearer.transform.Turn(angle)
|
|
mod.wearer.throw_at(get_ranged_target_turf_direct(mod.wearer, target, power), \
|
|
range = power, speed = max(round(0.2*power), 1), thrower = mod.wearer, spin = FALSE, \
|
|
callback = CALLBACK(src, .proc/on_throw_end, mod.wearer, -angle))
|
|
|
|
/obj/item/mod/module/hydraulic/proc/on_throw_end(mob/user, angle)
|
|
if(!user)
|
|
return
|
|
user.transform = user.transform.Turn(angle)
|
|
|
|
/obj/item/mod/module/disposal_connector
|
|
name = "MOD disposal selector module"
|
|
desc = "A module that connects to the disposal pipeline, causing the user to go into their config selected disposal. \
|
|
Only seems to work when the suit is on."
|
|
icon_state = "disposal"
|
|
complexity = 2
|
|
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
|
|
incompatible_modules = list(/obj/item/mod/module/disposal_connector)
|
|
var/disposal_tag = NONE
|
|
|
|
/obj/item/mod/module/disposal_connector/Initialize(mapload)
|
|
. = ..()
|
|
disposal_tag = pick(GLOB.TAGGERLOCATIONS)
|
|
|
|
/obj/item/mod/module/disposal_connector/on_suit_activation()
|
|
RegisterSignal(mod.wearer, COMSIG_MOVABLE_DISPOSING, .proc/disposal_handling)
|
|
|
|
/obj/item/mod/module/disposal_connector/on_suit_deactivation(deleting = FALSE)
|
|
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_DISPOSING)
|
|
|
|
/obj/item/mod/module/disposal_connector/get_configuration()
|
|
. = ..()
|
|
.["disposal_tag"] = add_ui_configuration("Disposal Tag", "list", GLOB.TAGGERLOCATIONS[disposal_tag], GLOB.TAGGERLOCATIONS)
|
|
|
|
/obj/item/mod/module/disposal_connector/configure_edit(key, value)
|
|
switch(key)
|
|
if("disposal_tag")
|
|
for(var/tag in 1 to length(GLOB.TAGGERLOCATIONS))
|
|
if(GLOB.TAGGERLOCATIONS[tag] == value)
|
|
disposal_tag = tag
|
|
break
|
|
|
|
/obj/item/mod/module/disposal_connector/proc/disposal_handling(datum/disposal_source, obj/structure/disposalholder/disposal_holder, obj/machinery/disposal/disposal_machine, hasmob)
|
|
SIGNAL_HANDLER
|
|
|
|
disposal_holder.destinationTag = disposal_tag
|
|
|
|
/obj/item/mod/module/magnet
|
|
name = "MOD loader hydraulic magnet module"
|
|
desc = "A powerful hydraulic electromagnet able to launch crates and lockers towards the user, and keep 'em attached."
|
|
icon_state = "magnet_loader"
|
|
module_type = MODULE_ACTIVE
|
|
removable = FALSE
|
|
use_power_cost = DEFAULT_CHARGE_DRAIN*3
|
|
incompatible_modules = list(/obj/item/mod/module/magnet)
|
|
cooldown_time = 1.5 SECONDS
|
|
overlay_state_active = "module_magnet"
|
|
use_mod_colors = TRUE
|
|
|
|
/obj/item/mod/module/magnet/on_select_use(atom/target)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
if(istype(mod.wearer.pulling, /obj/structure/closet))
|
|
var/obj/structure/closet/locker = mod.wearer.pulling
|
|
playsound(locker, 'sound/effects/gravhit.ogg', 75, TRUE)
|
|
locker.forceMove(mod.wearer.loc)
|
|
locker.throw_at(target, range = 7, speed = 4, thrower = mod.wearer)
|
|
return
|
|
if(!istype(target, /obj/structure/closet) || !(target in view(mod.wearer)))
|
|
balloon_alert(mod.wearer, "invalid target!")
|
|
return
|
|
var/obj/structure/closet/locker = target
|
|
if(locker.anchored || locker.move_resist >= MOVE_FORCE_OVERPOWERING)
|
|
balloon_alert(mod.wearer, "target anchored!")
|
|
return
|
|
new /obj/effect/temp_visual/mook_dust(get_turf(locker))
|
|
playsound(locker, 'sound/effects/gravhit.ogg', 75, TRUE)
|
|
locker.throw_at(mod.wearer, range = 7, speed = 3, force = MOVE_FORCE_WEAK, \
|
|
callback = CALLBACK(src, .proc/check_locker, locker))
|
|
|
|
/obj/item/mod/module/magnet/on_deactivation(display_message = TRUE, deleting = FALSE)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
if(istype(mod.wearer.pulling, /obj/structure/closet))
|
|
mod.wearer.stop_pulling()
|
|
|
|
/obj/item/mod/module/magnet/proc/check_locker(obj/structure/closet/locker)
|
|
if(!mod?.wearer)
|
|
return
|
|
if(!locker.Adjacent(mod.wearer) || !isturf(locker.loc) || !isturf(mod.wearer.loc))
|
|
return
|
|
mod.wearer.start_pulling(locker)
|
|
locker.strong_grab = TRUE
|
|
RegisterSignal(locker, COMSIG_ATOM_NO_LONGER_PULLED, .proc/on_stop_pull)
|
|
|
|
/obj/item/mod/module/magnet/proc/on_stop_pull(obj/structure/closet/locker, atom/movable/last_puller)
|
|
SIGNAL_HANDLER
|
|
|
|
locker.strong_grab = FALSE
|
|
UnregisterSignal(locker, COMSIG_ATOM_NO_LONGER_PULLED)
|
|
|
|
/obj/item/mod/module/ash_accretion
|
|
name = "MOD ash accretion module"
|
|
desc = "A module that collects ash from the terrain, covering the suit in a protective layer, this layer is \
|
|
lost when moving across standard terrain."
|
|
icon_state = "ash_accretion"
|
|
removable = FALSE
|
|
incompatible_modules = list(/obj/item/mod/module/ash_accretion)
|
|
overlay_state_inactive = "module_ash"
|
|
use_mod_colors = TRUE
|
|
/// How many tiles we can travel to max out the armor.
|
|
var/max_traveled_tiles = 10
|
|
/// How many tiles we traveled through.
|
|
var/traveled_tiles = 0
|
|
/// Armor values per tile.
|
|
var/list/armor_values = list(MELEE = 4, BULLET = 1, LASER = 2, ENERGY = 2, BOMB = 4)
|
|
/// Speed added when you're fully covered in ash.
|
|
var/speed_added = 0.5
|
|
/// Speed that we actually added.
|
|
var/actual_speed_added = 0
|
|
/// Turfs that let us accrete ash.
|
|
var/static/list/accretion_turfs
|
|
/// Turfs that let us keep ash.
|
|
var/static/list/keep_turfs
|
|
|
|
/obj/item/mod/module/ash_accretion/Initialize(mapload)
|
|
. = ..()
|
|
if(!accretion_turfs)
|
|
accretion_turfs = typecacheof(list(
|
|
/turf/open/misc/asteroid,
|
|
/turf/open/misc/ashplanet,
|
|
/turf/open/misc/dirt,
|
|
))
|
|
if(!keep_turfs)
|
|
keep_turfs = typecacheof(list(
|
|
/turf/open/misc/grass,
|
|
/turf/open/floor/plating/snowed,
|
|
/turf/open/misc/sandy_dirt,
|
|
/turf/open/misc/ironsand,
|
|
/turf/open/misc/ice,
|
|
/turf/open/indestructible/hierophant,
|
|
/turf/open/indestructible/boss,
|
|
/turf/open/indestructible/necropolis,
|
|
/turf/open/lava,
|
|
/turf/open/water,
|
|
))
|
|
|
|
/obj/item/mod/module/ash_accretion/on_suit_activation()
|
|
ADD_TRAIT(mod.wearer, TRAIT_ASHSTORM_IMMUNE, MOD_TRAIT)
|
|
ADD_TRAIT(mod.wearer, TRAIT_SNOWSTORM_IMMUNE, MOD_TRAIT)
|
|
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/on_move)
|
|
|
|
/obj/item/mod/module/ash_accretion/on_suit_deactivation(deleting = FALSE)
|
|
REMOVE_TRAIT(mod.wearer, TRAIT_ASHSTORM_IMMUNE, MOD_TRAIT)
|
|
REMOVE_TRAIT(mod.wearer, TRAIT_SNOWSTORM_IMMUNE, MOD_TRAIT)
|
|
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
|
|
if(!traveled_tiles)
|
|
return
|
|
var/list/parts = mod.mod_parts + mod
|
|
var/list/removed_armor = armor_values.Copy()
|
|
for(var/armor_type in removed_armor)
|
|
removed_armor[armor_type] = -removed_armor[armor_type] * traveled_tiles
|
|
for(var/obj/item/part as anything in parts)
|
|
part.armor = part.armor.modifyRating(arglist(removed_armor))
|
|
if(traveled_tiles == max_traveled_tiles)
|
|
mod.slowdown += speed_added
|
|
mod.wearer.update_equipment_speed_mods()
|
|
traveled_tiles = 0
|
|
|
|
/obj/item/mod/module/ash_accretion/generate_worn_overlay(mutable_appearance/standing)
|
|
overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
|
|
return ..()
|
|
|
|
/obj/item/mod/module/ash_accretion/proc/on_move(atom/source, atom/oldloc, dir, forced)
|
|
if(!isturf(mod.wearer.loc)) //dont lose ash from going in a locker
|
|
return
|
|
if(traveled_tiles) //leave ash every tile
|
|
new /obj/effect/temp_visual/light_ash(get_turf(src))
|
|
if(is_type_in_typecache(mod.wearer.loc, accretion_turfs))
|
|
if(traveled_tiles >= max_traveled_tiles)
|
|
return
|
|
traveled_tiles++
|
|
var/list/parts = mod.mod_parts + mod
|
|
for(var/obj/item/part as anything in parts)
|
|
part.armor = part.armor.modifyRating(arglist(armor_values))
|
|
if(traveled_tiles >= max_traveled_tiles)
|
|
balloon_alert(mod.wearer, "fully ash covered")
|
|
mod.wearer.color = list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,3) //make them super light
|
|
animate(mod.wearer, 1 SECONDS, color = null, flags = ANIMATION_PARALLEL)
|
|
playsound(src, 'sound/effects/sparks1.ogg', 100, TRUE)
|
|
actual_speed_added = max(0, min(mod.slowdown_active, speed_added))
|
|
mod.slowdown -= actual_speed_added
|
|
mod.wearer.update_equipment_speed_mods()
|
|
else if(is_type_in_typecache(mod.wearer.loc, keep_turfs))
|
|
return
|
|
else
|
|
if(traveled_tiles <= 0)
|
|
return
|
|
if(traveled_tiles == max_traveled_tiles)
|
|
mod.slowdown += actual_speed_added
|
|
mod.wearer.update_equipment_speed_mods()
|
|
traveled_tiles--
|
|
var/list/parts = mod.mod_parts + mod
|
|
var/list/removed_armor = armor_values.Copy()
|
|
for(var/armor_type in removed_armor)
|
|
removed_armor[armor_type] = -removed_armor[armor_type]
|
|
for(var/obj/item/part as anything in parts)
|
|
part.armor = part.armor.modifyRating(arglist(removed_armor))
|
|
if(traveled_tiles <= 0)
|
|
balloon_alert(mod.wearer, "ran out of ash!")
|
|
|
|
/obj/item/mod/module/sphere_transform
|
|
name = "MOD sphere transform module"
|
|
desc = "A module able to move the suit's parts around, turning it and the user into a sphere. \
|
|
The sphere can move quickly, even through lava, and launch mining bombs to decimate terrain."
|
|
icon_state = "sphere"
|
|
module_type = MODULE_ACTIVE
|
|
removable = FALSE
|
|
active_power_cost = DEFAULT_CHARGE_DRAIN*0.5
|
|
use_power_cost = DEFAULT_CHARGE_DRAIN*3
|
|
incompatible_modules = list(/obj/item/mod/module/sphere_transform)
|
|
cooldown_time = 1.25 SECONDS
|
|
/// Time it takes us to complete the animation.
|
|
var/animate_time = 0.25 SECONDS
|
|
|
|
/obj/item/mod/module/sphere_transform/on_activation()
|
|
if(!mod.wearer.has_gravity())
|
|
balloon_alert(mod.wearer, "no gravity!")
|
|
return FALSE
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
playsound(src, 'sound/items/modsuit/ballin.ogg', 100, TRUE)
|
|
mod.wearer.add_filter("mod_ball", 1, alpha_mask_filter(icon = icon('icons/mob/clothing/modsuit/mod_modules.dmi', "ball_mask"), flags = MASK_INVERSE))
|
|
mod.wearer.add_filter("mod_blur", 2, angular_blur_filter(size = 15))
|
|
mod.wearer.add_filter("mod_outline", 3, outline_filter(color = "#000000AA"))
|
|
mod.wearer.base_pixel_y -= 4
|
|
animate(mod.wearer, animate_time, pixel_y = mod.wearer.base_pixel_y, flags = ANIMATION_PARALLEL)
|
|
mod.wearer.SpinAnimation(1.5)
|
|
ADD_TRAIT(mod.wearer, TRAIT_LAVA_IMMUNE, MOD_TRAIT)
|
|
ADD_TRAIT(mod.wearer, TRAIT_HANDS_BLOCKED, MOD_TRAIT)
|
|
ADD_TRAIT(mod.wearer, TRAIT_FORCED_STANDING, MOD_TRAIT)
|
|
ADD_TRAIT(mod.wearer, TRAIT_NOSLIPALL, MOD_TRAIT)
|
|
mod.wearer.add_movespeed_mod_immunities(MOD_TRAIT, /datum/movespeed_modifier/turf_slowdown)
|
|
mod.wearer.RemoveElement(/datum/element/footstep, FOOTSTEP_MOB_HUMAN, 1, -6)
|
|
mod.wearer.AddElement(/datum/element/footstep, FOOTSTEP_OBJ_ROBOT, 1, -6, sound_vary = TRUE)
|
|
mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/sphere)
|
|
RegisterSignal(mod.wearer, COMSIG_MOB_STATCHANGE, .proc/on_statchange)
|
|
|
|
/obj/item/mod/module/sphere_transform/on_deactivation(display_message = TRUE, deleting = FALSE)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
if(!deleting)
|
|
playsound(src, 'sound/items/modsuit/ballin.ogg', 100, TRUE, frequency = -1)
|
|
mod.wearer.base_pixel_y = 0
|
|
animate(mod.wearer, animate_time, pixel_y = mod.wearer.base_pixel_y)
|
|
addtimer(CALLBACK(mod.wearer, /atom.proc/remove_filter, list("mod_ball", "mod_blur", "mod_outline")), animate_time)
|
|
REMOVE_TRAIT(mod.wearer, TRAIT_LAVA_IMMUNE, MOD_TRAIT)
|
|
REMOVE_TRAIT(mod.wearer, TRAIT_HANDS_BLOCKED, MOD_TRAIT)
|
|
REMOVE_TRAIT(mod.wearer, TRAIT_FORCED_STANDING, MOD_TRAIT)
|
|
REMOVE_TRAIT(mod.wearer, TRAIT_NOSLIPALL, MOD_TRAIT)
|
|
mod.wearer.remove_movespeed_mod_immunities(MOD_TRAIT, /datum/movespeed_modifier/damage_slowdown)
|
|
mod.wearer.RemoveElement(/datum/element/footstep, FOOTSTEP_OBJ_ROBOT, 1, -6, sound_vary = TRUE)
|
|
mod.wearer.AddElement(/datum/element/footstep, FOOTSTEP_MOB_HUMAN, 1, -6)
|
|
mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/sphere)
|
|
UnregisterSignal(mod.wearer, COMSIG_MOB_STATCHANGE)
|
|
|
|
/obj/item/mod/module/sphere_transform/on_use()
|
|
if(!lavaland_equipment_pressure_check(get_turf(src)))
|
|
balloon_alert(mod.wearer, "too much pressure!")
|
|
playsound(src, 'sound/weapons/gun/general/dry_fire.ogg', 25, TRUE)
|
|
return FALSE
|
|
return ..()
|
|
|
|
/obj/item/mod/module/sphere_transform/on_select_use(atom/target)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
var/obj/projectile/bomb = new /obj/projectile/bullet/reusable/mining_bomb(mod.wearer.loc)
|
|
bomb.preparePixelProjectile(target, mod.wearer)
|
|
bomb.firer = mod.wearer
|
|
playsound(src, 'sound/weapons/gun/general/grenade_launch.ogg', 75, TRUE)
|
|
INVOKE_ASYNC(bomb, /obj/projectile.proc/fire)
|
|
drain_power(use_power_cost)
|
|
|
|
/obj/item/mod/module/sphere_transform/on_active_process(delta_time)
|
|
animate(mod.wearer) //stop the animation
|
|
mod.wearer.SpinAnimation(1.5) //start it back again
|
|
if(!mod.wearer.has_gravity())
|
|
on_deactivation() //deactivate in no grav
|
|
|
|
/obj/item/mod/module/sphere_transform/proc/on_statchange(datum/source)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!mod.wearer.stat)
|
|
return
|
|
on_deactivation()
|
|
|
|
/obj/projectile/bullet/reusable/mining_bomb
|
|
name = "mining bomb"
|
|
desc = "A bomb. Why are you examining this?"
|
|
icon_state = "mine_bomb"
|
|
icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
|
|
damage = 0
|
|
nodamage = TRUE
|
|
range = 6
|
|
suppressed = SUPPRESSED_VERY
|
|
armor_flag = BOMB
|
|
light_system = MOVABLE_LIGHT
|
|
light_range = 1
|
|
light_power = 1
|
|
light_color = COLOR_LIGHT_ORANGE
|
|
ammo_type = /obj/structure/mining_bomb
|
|
|
|
/obj/structure/mining_bomb
|
|
name = "mining bomb"
|
|
desc = "A bomb. Why are you examining this?"
|
|
icon_state = "mine_bomb"
|
|
icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
|
|
anchored = TRUE
|
|
resistance_flags = FIRE_PROOF|LAVA_PROOF
|
|
light_system = MOVABLE_LIGHT
|
|
light_range = 1
|
|
light_power = 1
|
|
light_color = COLOR_LIGHT_ORANGE
|
|
/// Time to prime the explosion
|
|
var/prime_time = 0.5 SECONDS
|
|
/// Time to explode from the priming
|
|
var/explosion_time = 1 SECONDS
|
|
/// Damage done on explosion.
|
|
var/damage = 15
|
|
/// Damage multiplier on hostile fauna.
|
|
var/fauna_boost = 4
|
|
/// Image overlaid on explosion.
|
|
var/static/image/explosion_image
|
|
|
|
/obj/structure/mining_bomb/Initialize(mapload)
|
|
. = ..()
|
|
if(!explosion_image)
|
|
explosion_image = image('icons/effects/96x96.dmi', "judicial_explosion")
|
|
explosion_image.pixel_x = -32
|
|
explosion_image.pixel_y = -32
|
|
explosion_image.plane = ABOVE_GAME_PLANE
|
|
addtimer(CALLBACK(src, .proc/prime), prime_time)
|
|
|
|
/obj/structure/mining_bomb/proc/prime()
|
|
add_overlay(explosion_image)
|
|
addtimer(CALLBACK(src, .proc/boom), explosion_time)
|
|
|
|
/obj/structure/mining_bomb/proc/boom()
|
|
visible_message(span_danger("[src] explodes!"))
|
|
playsound(src, 'sound/magic/magic_missile.ogg', 200, vary = TRUE)
|
|
for(var/turf/closed/mineral/rock in circle_range_turfs(src, 2))
|
|
rock.gets_drilled()
|
|
for(var/mob/living/mob in range(1, src))
|
|
mob.apply_damage(12 * (ishostile(mob) ? fauna_boost : 1), BRUTE, spread_damage = TRUE)
|
|
for(var/obj/object in range(1, src))
|
|
object.take_damage(damage, BRUTE, BOMB)
|
|
qdel(src)
|