diff --git a/_maps/__MAP_DEFINES.dm b/_maps/__MAP_DEFINES.dm index 416d116e379..ee8e3b620f3 100644 --- a/_maps/__MAP_DEFINES.dm +++ b/_maps/__MAP_DEFINES.dm @@ -57,7 +57,7 @@ #define ZTRAIT_UP "Up" #define ZTRAIT_DOWN "Down" -#define ZTRAIT_GRAVITY "Gravity" // overrides Z-level gravity making it always on. Unless it's space turf or openspace in a space area. See atom/proc/has_gravity() +#define ZTRAIT_GRAVITY "Gravity" // overrides Z-level gravity making it always on. Unless it's space turf or openspace in a space area. See atom/proc/get_gravity() #define ZTRAIT_BASETURF "Baseturf" // overrides Z-level baseturf. set type path by ZTRAIT_BASETURF = "/turf/..." // 3 Is already big one hella station. diff --git a/_maps/map_files/templates/piratbase.dmm b/_maps/map_files/templates/piratbase.dmm index b2937e83da7..467f4083e56 100644 --- a/_maps/map_files/templates/piratbase.dmm +++ b/_maps/map_files/templates/piratbase.dmm @@ -6397,7 +6397,7 @@ /area/ruin/space/pirate_base/mining) "sq" = ( /obj/structure/table/glass, -/obj/item/assembly/signaler/anomaly/bluespace, +/obj/item/assembly/signaler/core/bluespace/tier2, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "dark" diff --git a/code/__DEFINES/anomalies.dm b/code/__DEFINES/anomalies.dm new file mode 100644 index 00000000000..3dd76fdfa7e --- /dev/null +++ b/code/__DEFINES/anomalies.dm @@ -0,0 +1,59 @@ +#define ANOMALY_TYPE_RANDOM "random" +#define ANOMALY_TYPE_ATMOS "atmospheric" +#define ANOMALY_TYPE_BLUESPACE "bluespace" +#define ANOMALY_TYPE_GRAV "gravitational" +#define ANOMALY_TYPE_VORTEX "vortex" +#define ANOMALY_TYPE_FLUX "energetic" +#define TIER1 "1" +#define TIER2 "2" +#define TIER3 "3" + +#define isanomaly(A) (istype((A), /obj/effect/anomaly)) + +#define iscore(A) (istype((A), /obj/item/assembly/signaler/core)) + +#define iscoreempty(A) ((A.type == /obj/item/assembly/signaler/core/tier1) || \ + (A.type == /obj/item/assembly/signaler/core/tier2) || \ + (A.type == /obj/item/assembly/signaler/core/tier3)) + +#define iscoreatmos(A) (istype((A), /obj/item/assembly/signaler/core/atmospheric)) +#define iscorebluespace(A) (istype((A), /obj/item/assembly/signaler/core/bluespace)) +#define iscoregrav(A) (istype((A), /obj/item/assembly/signaler/core/gravitational)) +#define iscorevortex(A) (istype((A), /obj/item/assembly/signaler/core/vortex)) +#define iscoreflux(A) (istype((A), /obj/item/assembly/signaler/core/energetic)) + +GLOBAL_LIST_INIT(anomaly_types, list( + TIER1 = list( + ANOMALY_TYPE_ATMOS = /datum/anomaly_gen_datum/tier1/pyroclastic, + ANOMALY_TYPE_BLUESPACE = /datum/anomaly_gen_datum/tier1/bluespace, + ANOMALY_TYPE_GRAV = /datum/anomaly_gen_datum/tier1/gravitational, + ANOMALY_TYPE_VORTEX = /datum/anomaly_gen_datum/tier1/vortex, + ANOMALY_TYPE_FLUX = /datum/anomaly_gen_datum/tier1/energetic, + ), + TIER2 = list( + ANOMALY_TYPE_ATMOS = /datum/anomaly_gen_datum/tier2/pyroclastic, + ANOMALY_TYPE_BLUESPACE = /datum/anomaly_gen_datum/tier2/bluespace, + ANOMALY_TYPE_GRAV = /datum/anomaly_gen_datum/tier2/gravitational, + ANOMALY_TYPE_VORTEX = /datum/anomaly_gen_datum/tier2/vortex, + ANOMALY_TYPE_FLUX = /datum/anomaly_gen_datum/tier2/energetic, + ), + TIER3 = list( + ANOMALY_TYPE_ATMOS = /datum/anomaly_gen_datum/tier3/pyroclastic, + ANOMALY_TYPE_BLUESPACE = /datum/anomaly_gen_datum/tier3/bluespace, + ANOMALY_TYPE_GRAV = /datum/anomaly_gen_datum/tier3/gravitational, + ANOMALY_TYPE_VORTEX = /datum/anomaly_gen_datum/tier3/vortex, + ANOMALY_TYPE_FLUX = /datum/anomaly_gen_datum/tier3/energetic, + ), +)) + +GLOBAL_LIST_INIT(created_anomalies, list( + ANOMALY_TYPE_ATMOS = 0, + ANOMALY_TYPE_BLUESPACE = 0, + ANOMALY_TYPE_GRAV = 0, + ANOMALY_TYPE_VORTEX = 0, + ANOMALY_TYPE_FLUX = 0, +)) + +#define ANOMALY_GROW_STABILITY 30 +#define ANOMALY_DECREASE_STABILITY 70 +#define ANOMALY_MOVE_MAX_STABILITY 59 diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index aa521e47314..6662ac0b652 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -169,7 +169,7 @@ ///from base of atom/handle_atom_del(): (atom/deleted) #define COMSIG_ATOM_CONTENTS_DEL "atom_contents_del" -///from base of atom/has_gravity(): (turf/location, list/forced_gravities) +///from base of atom/get_gravity(): (turf/location, list/forced_gravities) #define COMSIG_ATOM_HAS_GRAVITY "atom_has_gravity" ///from proc/get_rad_contents(): () #define COMSIG_ATOM_RAD_PROBE "atom_rad_probe" @@ -287,7 +287,7 @@ ///from base of turf/ChangeTurf(): (path, list/new_baseturf, flags, list/transferring_comps) #define COMSIG_TURF_CHANGE "turf_change" -///from base of atom/has_gravity(): (atom/asker, list/forced_gravities) +///from base of atom/get_gravity(): (atom/asker, list/forced_gravities) #define COMSIG_TURF_HAS_GRAVITY "turf_has_gravity" ///from base of turf/multiz_turf_del(): (turf/source, direction) #define COMSIG_TURF_MULTIZ_DEL "turf_multiz_del" diff --git a/code/__DEFINES/gravity.dm b/code/__DEFINES/gravity.dm index a824ba45e42..b0a52d8aa6f 100644 --- a/code/__DEFINES/gravity.dm +++ b/code/__DEFINES/gravity.dm @@ -6,7 +6,13 @@ */ #define NEGATIVE_GRAVITY -1 +#define NO_GRAVITY 0.3 + #define STANDARD_GRAVITY 1 //Anything above this is high gravity, anything below no grav until negative gravity +/// The gravity strength threshold for slownown. +#define HIGH_GRAVITY_SLOWDOWN 1.5 +/// The gravity strength threshold for disability of staying. +#define GRAVITY_CANT_STAY 2 /// The gravity strength threshold for high gravity damage. #define GRAVITY_DAMAGE_THRESHOLD 3 /// The scaling factor for high gravity damage. @@ -14,3 +20,6 @@ /// The maximum [BRUTE] damage a mob can take from high gravity per second. #define GRAVITY_DAMAGE_MAXIMUM 1.5 + +#define GRAVITY_SOURCE_GRAVGEN "gravgen" +#define GRAVITY_SOURCE_ANOMALY "anomaly" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 089ea970b09..4d56edd5a50 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -382,6 +382,8 @@ ///How much a mob's sprite should be moved when they're lying down #define PIXEL_Y_OFFSET_LYING -6 +///How much a mob's sprite should be moved when they're lying up (on the ceiling) +#define PIXEL_Y_OFFSET_LYING_REVERSED 6 // Slip flags, also known as lube flags /// The mob will not slip if they're walking intent diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index 92565ab1354..80a700de392 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -157,3 +157,5 @@ #define BLOB_INFECTED_TRAIT "blob_infected" #define VENDOR_FLATTENING_TRAIT "vendor_flattening" + +#define GRAVITATION_TRAIT "gravitation" diff --git a/code/__HELPERS/AnimationLibrary.dm b/code/__HELPERS/AnimationLibrary.dm index 2c44c3b8d1d..b8f67e5e079 100644 --- a/code/__HELPERS/AnimationLibrary.dm +++ b/code/__HELPERS/AnimationLibrary.dm @@ -31,7 +31,7 @@ if(random_side) side = pick(-1, 1) - spawn(rand(1,10)) + spawn(rand(1, 10)) animate(A, pixel_y = 32, transform = matrix(floatdegrees * (side == 1 ? 1:-1), MATRIX_ROTATE), time = floatspeed, loop = loopnum, easing = SINE_EASING) animate(pixel_y = 0, transform = matrix(floatdegrees * (side == 1 ? -1:1), MATRIX_ROTATE), time = floatspeed, loop = loopnum, easing = SINE_EASING) @@ -43,7 +43,7 @@ if(random_side) side = pick(-1, 1) - spawn(rand(1,10)) + spawn(rand(1, 10)) animate(A, pixel_y = 8, transform = matrix(floatdegrees * (side == 1 ? 1:-1), MATRIX_ROTATE), time = floatspeed, loop = loopnum, easing = SINE_EASING) animate(pixel_y = 0, transform = null, time = floatspeed, loop = loopnum, easing = SINE_EASING) @@ -55,7 +55,7 @@ if(random_side) side = pick(-1, 1) - spawn(rand(1,10)) + spawn(rand(1, 10)) animate(A, pixel_y = 8, transform = matrix(floatdegrees * (side == 1 ? 1:-1), MATRIX_ROTATE), time = floatspeed, loop = loopnum, easing = SINE_EASING) animate(pixel_y = 0, transform = matrix(floatdegrees * (side == 1 ? -1:1), MATRIX_ROTATE), time = floatspeed, loop = loopnum, easing = SINE_EASING) @@ -67,7 +67,7 @@ while(do_loops > 0) do_loops-- animate(A, transform = M, pixel_z = A.pixel_z + 12, alpha = A.alpha - 17, time = 1, loop = 1, easing = LINEAR_EASING) - M.Scale(1.2,1.2) + M.Scale(1.2, 1.2) sleep(1) A.alpha = 0 @@ -76,37 +76,37 @@ return var/matrix/M = matrix() var/do_loops = 15 - M.Scale(18,18) + M.Scale(18, 18) while(do_loops > 0) do_loops-- animate(A, transform = M, pixel_z = A.pixel_z - 12, alpha = A.alpha + 17, time = 1, loop = 1, easing = LINEAR_EASING) - M.Scale(0.8,0.8) + M.Scale(0.8, 0.8) sleep(1) animate(A, transform = M, pixel_z = 0, alpha = 255, time = 1, loop = 1, easing = LINEAR_EASING) -/proc/animate_shake(var/atom/A, var/amount = 5, var/x_severity = 2, var/y_severity = 2) +/proc/animate_shake(atom/A, amount = 5, x_severity = 2, y_severity = 2) // Wiggles the sprite around on its tile then returns it to normal if(!istype(A)) return if(!isnum(amount) || !isnum(x_severity) || !isnum(y_severity)) return - amount = max(1,min(amount,50)) - x_severity = max(-32,min(x_severity,32)) - y_severity = max(-32,min(y_severity,32)) + amount = max(1, min(amount, 50)) + x_severity = max(-32, min(x_severity, 32)) + y_severity = max(-32, min(y_severity, 32)) var/x_severity_inverse = 0 - x_severity var/y_severity_inverse = 0 - y_severity - animate(A, transform = null, pixel_y = rand(y_severity_inverse,y_severity), pixel_x = rand(x_severity_inverse,x_severity),time = 1,loop = amount, easing = ELASTIC_EASING) + animate(A, transform = null, pixel_y = rand(y_severity_inverse, y_severity), pixel_x = rand(x_severity_inverse, x_severity), time = 1, loop = amount, easing = ELASTIC_EASING, flags = ANIMATION_PARALLEL) spawn(amount) - animate(A, transform = null, pixel_y = 0, pixel_x = 0,time = 1,loop = 1, easing = LINEAR_EASING) + animate(A, transform = null, pixel_y = 0, pixel_x = 0, time = 1, loop = 1, easing = LINEAR_EASING, flags = ANIMATION_PARALLEL) /proc/animate_teleport(var/atom/A) if(!istype(A)) return var/matrix/M = matrix(1, 3, MATRIX_SCALE) animate(A, transform = M, pixel_y = 32, time = 10, alpha = 50, easing = CIRCULAR_EASING) - M.Scale(0,4) + M.Scale(0, 4) animate(transform = M, time = 5, color = "#1111ff", alpha = 0, easing = CIRCULAR_EASING) animate(transform = null, time = 5, color = "#ffffff", alpha = 255, pixel_y = 0, easing = ELASTIC_EASING) @@ -122,19 +122,19 @@ /proc/animate_rainbow_glow_old(var/atom/A) if(!istype(A)) return - animate(A, color = "#FF0000", time = rand(5,10), loop = -1, easing = LINEAR_EASING) - animate(color = "#00FF00", time = rand(5,10), loop = -1, easing = LINEAR_EASING) - animate(color = "#0000FF", time = rand(5,10), loop = -1, easing = LINEAR_EASING) + animate(A, color = "#FF0000", time = rand(5, 10), loop = -1, easing = LINEAR_EASING) + animate(color = "#00FF00", time = rand(5, 10), loop = -1, easing = LINEAR_EASING) + animate(color = "#0000FF", time = rand(5, 10), loop = -1, easing = LINEAR_EASING) /proc/animate_rainbow_glow(var/atom/A) if(!istype(A)) return - animate(A, color = "#FF0000", time = rand(5,10), loop = -1, easing = LINEAR_EASING) - animate(color = "#FFFF00", time = rand(5,10), loop = -1, easing = LINEAR_EASING) - animate(color = "#00FF00", time = rand(5,10), loop = -1, easing = LINEAR_EASING) - animate(color = "#00FFFF", time = rand(5,10), loop = -1, easing = LINEAR_EASING) - animate(color = "#0000FF", time = rand(5,10), loop = -1, easing = LINEAR_EASING) - animate(color = "#FF00FF", time = rand(5,10), loop = -1, easing = LINEAR_EASING) + animate(A, color = "#FF0000", time = rand(5, 10), loop = -1, easing = LINEAR_EASING) + animate(color = "#FFFF00", time = rand(5, 10), loop = -1, easing = LINEAR_EASING) + animate(color = "#00FF00", time = rand(5, 10), loop = -1, easing = LINEAR_EASING) + animate(color = "#00FFFF", time = rand(5, 10), loop = -1, easing = LINEAR_EASING) + animate(color = "#0000FF", time = rand(5, 10), loop = -1, easing = LINEAR_EASING) + animate(color = "#FF00FF", time = rand(5, 10), loop = -1, easing = LINEAR_EASING) /proc/animate_fade_to_color_fill(var/atom/A, var/the_color, var/time) if(!istype(A) || !the_color || !time) @@ -163,8 +163,8 @@ /proc/animate_wiggle_then_reset(var/atom/A, var/loops = 5, var/speed = 5, var/x_var = 3, var/y_var = 3) if(!istype(A) || !loops || !speed) return - animate(A, pixel_x = rand(-x_var, x_var), pixel_y = rand(-y_var, y_var), time = speed * 2,loop = loops, easing = rand(2,7)) - animate(pixel_x = 0, pixel_y = 0, time = speed, easing = rand(2,7)) + animate(A, pixel_x = rand(-x_var, x_var), pixel_y = rand(-y_var, y_var), time = speed * 2, loop = loops, easing = rand(2, 7)) + animate(pixel_x = 0, pixel_y = 0, time = speed, easing = rand(2, 7)) /proc/animate_spin(var/atom/A, var/dir = "L", var/T = 1, var/looping = -1) if(!istype(A)) @@ -180,9 +180,10 @@ animate(transform = matrix(M, turn, MATRIX_ROTATE | MATRIX_MODIFY), time = T, loop = looping) animate(transform = matrix(M, turn, MATRIX_ROTATE | MATRIX_MODIFY), time = T, loop = looping) -/proc/animate_shockwave(var/atom/A) +/proc/animate_shockwave(atom/A) if(!istype(A)) return + var/punchstr = rand(10, 20) var/original_y = A.pixel_y animate(A, transform = matrix(punchstr, MATRIX_ROTATE), pixel_y = 16, time = 2, color = "#eeeeee", easing = BOUNCE_EASING) diff --git a/code/__HELPERS/paths/path.dm b/code/__HELPERS/paths/path.dm index e01bea57f7c..c005d323df4 100644 --- a/code/__HELPERS/paths/path.dm +++ b/code/__HELPERS/paths/path.dm @@ -345,7 +345,7 @@ src.movement_type = construct_from.movement_type src.thrown = !!construct_from.throwing src.anchored = construct_from.anchored - src.has_gravity = construct_from.has_gravity() + src.has_gravity = construct_from.get_gravity() if(ismob(construct_from)) var/mob/mob_construct = construct_from src.faction = mob_construct.faction?.Copy() diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index 4a0132a6645..59a6bd18743 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -340,29 +340,27 @@ or something covering your eyes." /atom/movable/screen/alert/negative - name = "Negative Gravity" - desc = "You're getting pulled upwards. While you won't have to worry about falling down anymore, you may accidentally fall upwards!" + name = "Обратная гравитация" + desc = "Вас тянет вверх. Хоть падение вниз вам больше не грозит, вы всё ещё можете упасть вверх!" icon_state = "negative" /atom/movable/screen/alert/weightless - name = "Weightless" - desc = "Gravity has ceased affecting you, and you're floating around aimlessly. You'll need something large and heavy, like a \ -wall or lattice, to push yourself off if you want to move. A jetpack would enable free range of motion. A pair of \ -magboots would let you walk around normally on the floor. Barring those, you can throw things, use a fire extinguisher, \ -or shoot a gun to move around via Newton's 3rd Law of Motion." + name = "Невесомость" + desc = "Гравитация перестала на вас влиять, и вы парите в пространстве. Чтобы двигаться, вы можете оттолкнуться от ближайших объектов, \ +кинуть что-то от себя или выстрелить в противоположную сторону. Для более комфортного перемещения используйте специальное оборудование." icon_state = "weightless" /atom/movable/screen/alert/highgravity - name = "High Gravity" - desc = "You're getting crushed by high gravity, picking up items and movement will be slowed." + name = "Повышенная гравитация" + desc = "На вас давит высокая гравитация. Двигаться в таком состоянии непросто." icon_state = "paralysis" /atom/movable/screen/alert/veryhighgravity - name = "Crushing Gravity" - desc = "You're getting crushed by high gravity, picking up items and movement will be slowed. You'll also accumulate brute damage!" + name = "Сокрушительная гравитация" + desc = "На вас давит невероятно высокая гравитация. Вы чувствуете, будто вас буквально разрывает на части!" icon_state = "paralysis" diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm index b5d5c8bd01d..fc4eb4a740f 100644 --- a/code/controllers/subsystem/throwing.dm +++ b/code/controllers/subsystem/throwing.dm @@ -157,7 +157,7 @@ SUBSYSTEM_DEF(throwing) //calculate how many tiles to move, making up for any missed ticks. var/tilestomove = CEILING(min(((((world.time + world.tick_lag) - start_time + delayed_time) * speed) - (dist_travelled ? dist_travelled : -1)), speed * MAX_TICKS_TO_MAKE_UP) * (world.tick_lag * SSthrowing.wait), 1) while(tilestomove-- > 0) - if((dist_travelled >= maxrange || AM.loc == target_turf) && AM.has_gravity(AM.loc)) + if((dist_travelled >= maxrange || AM.loc == target_turf) && !AM.no_gravity(AM.loc)) if(!hitcheck()) finalize() return @@ -181,7 +181,15 @@ SUBSYSTEM_DEF(throwing) finalize() return - dist_travelled++ + /* + A - Acceleration of gravity. + H - The height of the object's fall. + T - Past tense of falling. + H(T) = A * T * T / 2 + If A will become X times bigger, T will become sqrt(X) times lower. + */ + if(!AM.no_gravity()) // If no gravity, it causes some problems. I think, it will work normally this way. + dist_travelled += 1 * sqrt(abs(AM.get_gravity())) if(dist_travelled > MAX_THROWING_DIST) finalize() diff --git a/code/datums/action.dm b/code/datums/action.dm index ce7e9830f6e..46c9971cc55 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -558,7 +558,7 @@ /datum/action/item_action/gravity_jump name = "Gravity jump" - desc = "Directs a pulse of gravity in front of the user, pulling them forward rapidly." + desc = "Направляет импульс гравитации перед пользователем, придавая ему ускорение." attack_self = FALSE /datum/action/item_action/gravity_jump/Trigger(left_click = TRUE) diff --git a/code/datums/components/conveyor_movement.dm b/code/datums/components/conveyor_movement.dm index 7eb80972bb4..1e24f184032 100644 --- a/code/datums/components/conveyor_movement.dm +++ b/code/datums/components/conveyor_movement.dm @@ -30,7 +30,7 @@ if((moving_mob.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) && !moving_mob.stat) return MOVELOOP_SKIP_STEP var/atom/movable/moving_parent = parent - if(moving_parent.anchored || !moving_parent.has_gravity()) + if(moving_parent.anchored || moving_parent.get_gravity() > NO_GRAVITY) // No moving on convey with negative gravity. return MOVELOOP_SKIP_STEP diff --git a/code/datums/components/riding/riding.dm b/code/datums/components/riding/riding.dm index 3764199cc46..0ec660095a6 100644 --- a/code/datums/components/riding/riding.dm +++ b/code/datums/components/riding/riding.dm @@ -288,7 +288,7 @@ /datum/component/riding/proc/Process_Spacemove(direction, continuous_move) var/atom/movable/AM = parent - return override_allow_spacemove || AM.has_gravity() + return override_allow_spacemove || !AM.no_gravity() /// currently replicated from ridable because we need this behavior here too, see if we can deal with that /datum/component/riding/proc/unequip_buckle_inhands(mob/living/carbon/user) diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm index fb26d0dc21d..a4ea888e5ba 100644 --- a/code/datums/components/squeak.dm +++ b/code/datums/components/squeak.dm @@ -116,7 +116,7 @@ if(I.item_flags & ABSTRACT) return - if((arrived.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || !arrived.has_gravity()) + if((arrived.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || arrived.no_gravity()) return if(ismob(arrived) && !arrived.density) // Prevents 10 overlapping mice from making an unholy sound while moving diff --git a/code/datums/elements/footstep.dm b/code/datums/elements/footstep.dm index 782af5a7f13..0537891e1d1 100644 --- a/code/datums/elements/footstep.dm +++ b/code/datums/elements/footstep.dm @@ -90,7 +90,7 @@ if(steps % 2) return - if(steps != 0 && !source.has_gravity()) // don't need to step as often when you hop around + if(steps != 0 && source.no_gravity()) // don't need to step as often when you hop around return . = list(FOOTSTEP_MOB_SHOE = turf.footstep, FOOTSTEP_MOB_BAREFOOT = turf.barefootstep, FOOTSTEP_MOB_HEAVY = turf.heavyfootstep, FOOTSTEP_MOB_CLAW = turf.clawfootstep) diff --git a/code/datums/gravity.dm b/code/datums/gravity.dm new file mode 100644 index 00000000000..5c526c9511c --- /dev/null +++ b/code/datums/gravity.dm @@ -0,0 +1,33 @@ +/atom + var/list/gravity_sources = list() + var/list/ignored_gravity_sources = list() + +/atom/proc/add_gravity(id, gravity_delta) + if(id in gravity_sources) + gravity_sources[id] = 0 + + gravity_sources[id] += gravity_delta + + if(!gravity_sources[id]) + gravity_sources.Remove(id) + + if(isliving(src)) + var/mob/living/M = src + M.refresh_gravity() + +/atom/proc/remove_gravity_source(id) + gravity_sources.Remove(id) + if(isliving(src)) + var/mob/living/M = src + M.refresh_gravity() + +/atom/proc/add_ignored_gravity_source(id) + if(!(id in ignored_gravity_sources)) + ignored_gravity_sources[id] = 1 + else + ignored_gravity_sources[id]++ + +/atom/proc/remove_ignored_gravity_source(id) + ignored_gravity_sources[id]-- + if(!ignored_gravity_sources[id]) + ignored_gravity_sources.Remove(id) diff --git a/code/datums/helper_datums/construction_datum.dm b/code/datums/helper_datums/construction_datum.dm index aa709e1568a..acc3c7ad1ae 100644 --- a/code/datums/helper_datums/construction_datum.dm +++ b/code/datums/helper_datums/construction_datum.dm @@ -87,11 +87,14 @@ if(istype(task)) task.unit_completed() - new result(get_turf(holder)) + after_spawn_result(new result(get_turf(holder))) spawn() qdel(holder) return +/datum/construction/proc/after_spawn_result(atom/A) + return + /datum/construction/proc/set_desc(index as num) var/list/step = steps[index] holder.desc = step["desc"] diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 0d116755c7c..fc843dcf6aa 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -51,7 +51,7 @@ //must succeed in most cases /datum/teleport/proc/setTeleatom(atom/movable/ateleatom) - if(iseffect(ateleatom) && !istype(ateleatom, /obj/effect/dummy/chameleon)) + if(iseffect(ateleatom) && !istype(ateleatom, /obj/effect/dummy/chameleon) && !istype(ateleatom, /obj/effect/anomaly)) qdel(ateleatom) return 0 if(istype(ateleatom)) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 755a19232bb..1015adb5e95 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -731,9 +731,10 @@ /atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(density && !AM.has_gravity()) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). + if(density && AM.no_gravity()) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). addtimer(CALLBACK(src, PROC_REF(hitby_react), AM), 2) + SEND_SIGNAL(src, COMSIG_ATOM_HITBY, AM, skipcatch, hitpush, blocked, throwingdatum) /** * Called when living mob clicks on this atom with pulled movable. @@ -1592,36 +1593,44 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) * * Gravity if there's a gravity generator on the z level * * otherwise no gravity */ -/atom/proc/has_gravity(turf/gravity_turf) +/atom/proc/get_gravity(turf/gravity_turf) if(!isnull(GLOB.gravity_is_on)) // global admin override return GLOB.gravity_is_on if(!isturf(gravity_turf)) gravity_turf = get_turf(src) - if(!gravity_turf)//no gravity in nullspace + if(!gravity_turf)//no gravity in nullspace 1984 return FALSE if(check_level_trait(gravity_turf.z, ZTRAIT_GRAVITY)) return TRUE - var/list/forced_gravity = list() - SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, gravity_turf, forced_gravity) - SEND_SIGNAL(gravity_turf, COMSIG_TURF_HAS_GRAVITY, src, forced_gravity) - if(length(forced_gravity)) - var/positive_grav = max(forced_gravity) - var/negative_grav = min(min(forced_gravity), 0) //negative grav needs to be below or equal to 0 - - //our gravity is sum of the most massive positive and negative numbers returned by the signal - //so that adding two forced_gravity elements with an effect size of 1 each doesnt add to 2 gravity - //but negative force gravity effects can cancel out positive ones + if(gravity_turf.force_no_gravity) + return FALSE - return (positive_grav + negative_grav) + var/result_gravity = 0 + var/list/gravity_deltas = list() + SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, gravity_turf, gravity_deltas) + SEND_SIGNAL(gravity_turf, COMSIG_TURF_HAS_GRAVITY, src, gravity_deltas) var/area/turf_area = gravity_turf.loc + if(turf_area.has_gravity) + gravity_deltas.Add(1) + else if(!turf_area.ignore_gravgen && length(GLOB.gravity_generators["[gravity_turf.z]"]) && !(GRAVITY_SOURCE_GRAVGEN in ignored_gravity_sources)) + gravity_deltas.Add(1) + + for(var/source in gravity_sources) + if(!(source in ignored_gravity_sources)) + gravity_deltas.Add(gravity_sources[source]) - return !gravity_turf.force_no_gravity && (turf_area.has_gravity || (!turf_area.ignore_gravgen && length(GLOB.gravity_generators["[gravity_turf.z]"]))) + for(var/delta in gravity_deltas) + result_gravity += delta + return result_gravity + +/atom/proc/no_gravity(turf/gravity_turf) + return abs(get_gravity(gravity_turf)) <= NO_GRAVITY ///Setter for the `density` variable to append behavior related to its changing. /atom/proc/set_density(new_density) @@ -1720,3 +1729,10 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) */ /atom/proc/relaydrive(mob/living/user, direction) return !(SEND_SIGNAL(src, COMSIG_RIDDEN_DRIVER_MOVE, user, direction) & COMPONENT_DRIVER_BLOCK_MOVE) + +/atom/proc/get_external_loc() + var/atom/ext_loc = src + while(!isturf(ext_loc.loc)) + ext_loc = ext_loc.loc + + return ext_loc diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index d290628326d..b98f503681c 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -981,9 +981,9 @@ if(z_move_flags & ZMOVE_FEEDBACK) to_chat(rider || src, span_warning("There's nowhere to go in that direction!")) return FALSE - if(z_move_flags & ZMOVE_FALL_CHECKS && (throwing || (movement_type & (FLYING|FLOATING)) || !has_gravity(start))) + if(z_move_flags & ZMOVE_FALL_CHECKS && (throwing || (movement_type & (FLYING|FLOATING)) || no_gravity(start))) return FALSE - if(z_move_flags & ZMOVE_CAN_FLY_CHECKS && !(movement_type & (FLYING|FLOATING)) && has_gravity(start)) + if(z_move_flags & ZMOVE_CAN_FLY_CHECKS && !(movement_type & (FLYING|FLOATING)) && !no_gravity(start)) if(z_move_flags & ZMOVE_FEEDBACK) if(rider) to_chat(rider, span_notice("[src] is not capable of flight.")) @@ -1049,7 +1049,7 @@ * * continuous_move - If this check is coming from something in the context of already drifting */ /atom/movable/proc/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) - if(has_gravity()) + if(!no_gravity()) return TRUE if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir, continuous_move) & COMSIG_MOVABLE_STOP_SPACEMOVE) @@ -1164,8 +1164,17 @@ SSthrowing.processing[src] = thrown_thing thrown_thing.tick() + update_icon() return TRUE +/atom/movable/proc/random_throw(range_low = 0, range_high = 5, speed = 4) + var/list/turf/targets = list() + for(var/turf/T in range(range_high, src)) + if(get_dist(T, src) >= range_low && get_dist(T, src) <= range_high) + targets.Add(T) + + var/turf/target = pick(targets) + return throw_at(target, get_dist(src, target), speed) //Overlays /atom/movable/overlay diff --git a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm index e7a464d7d32..53b0ea0783c 100644 --- a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm +++ b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm @@ -850,6 +850,7 @@ return FALSE /mob/living/simple_animal/demon/pulse_demon/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + SEND_SIGNAL(src, COMSIG_ATOM_HITBY, AM, skipcatch, hitpush, blocked, throwingdatum) return /mob/living/simple_animal/demon/pulse_demon/experience_pressure_difference() diff --git a/code/game/machinery/computer/HolodeckControl.dm b/code/game/machinery/computer/HolodeckControl.dm index e45b8a0599b..75fbb3eded4 100644 --- a/code/game/machinery/computer/HolodeckControl.dm +++ b/code/game/machinery/computer/HolodeckControl.dm @@ -564,6 +564,30 @@ return FALSE +/obj/structure/holohoop/CanAllowThrough(atom/movable/mover, border_dir) + . = ..() + if((isitem(mover) && !isprojectile(mover)) && mover.throwing && mover.pass_flags != PASSEVERYTHING) + if(prob(50)) + mover.forceMove(loc) + visible_message(span_notice("Swish! [mover] lands in [src].")) + else + visible_message(span_alert("[mover] bounces off of [src]'s rim!")) + return FALSE + + +/obj/structure/holohoop/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + if(isitem(AM) && !isprojectile(AM)) + if(prob(50) || (throwingdatum && throwingdatum.thrower && HAS_TRAIT(throwingdatum.thrower, TRAIT_BADASS))) + AM.forceMove(get_turf(src)) + visible_message(span_warning("Swish! [AM] lands in [src].")) + SEND_SIGNAL(src, COMSIG_ATOM_HITBY, AM, skipcatch, hitpush, blocked, throwingdatum) + return + else + visible_message(span_danger("[AM] bounces off of [src]'s rim!")) + return ..() + else + return ..() + /obj/machinery/readybutton name = "Ready Declaration Device" desc = "This device is used to declare ready. If all devices in an area are ready, the event will begin!" diff --git a/code/game/machinery/computer/power.dm b/code/game/machinery/computer/power.dm index 10a05df63fa..56780f6da4d 100644 --- a/code/game/machinery/computer/power.dm +++ b/code/game/machinery/computer/power.dm @@ -62,7 +62,7 @@ else GLOB.powermonitor_repository.remove_from_cache(src) -/obj/machinery/computer/monitor/proc/find_powernet() +/obj/machinery/proc/find_powernet() var/obj/structure/cable/attached = null var/turf/T = loc if(isturf(T)) diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index 01e0baaca4b..0dd708cd6fe 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -1174,3 +1174,13 @@ to destroy them and players will be able to make replacements. /obj/item/stock_parts/micro_laser = 1, /obj/item/stack/cable_coil = 3, /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/anomaly_generator + board_name = "Генератор аномалий" + build_path = /obj/machinery/power/anomaly_generator + board_type = "machine" + origin_tech = "programming=1;bluespace=3" + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/capacitor = 2) diff --git a/code/game/machinery/customat.dm b/code/game/machinery/customat.dm index 15fd069fb6a..938b064d684 100644 --- a/code/game/machinery/customat.dm +++ b/code/game/machinery/customat.dm @@ -18,7 +18,7 @@ ///The key by which the object is pushed into the machine's row var/key = "generic_0" ///List of items in row - var/list/obj/item/containtment = list() + var/list/obj/item/containment = list() /// Price to buy one var/price = 0 ///Icon in tgui @@ -27,7 +27,7 @@ /datum/data/customat_product/New(obj/item/I) name = I.name amount = 0 - containtment = list() + containment = list() price = 0 icon = icon2base64(icon(initial(I.icon), initial(I.icon_state), SOUTH, 1, FALSE)) @@ -198,11 +198,11 @@ /obj/machinery/customat/proc/eject_all() for (var/key in products) var/datum/data/customat_product/product = products[key] - for (var/obj/item/I in product.containtment) + for (var/obj/item/I in product.containment) I.forceMove(get_turf(src)) product.amount = 0 - inserted_items_count -= product.containtment.len - product.containtment = list() + inserted_items_count -= product.containment.len + product.containment = list() /obj/machinery/customat/Destroy() eject_all() @@ -224,10 +224,6 @@ found_trunk.set_linked(src) trunk = found_trunk -/obj/machinery/customat/update_icon(updates = ALL) - return ..() - - /obj/machinery/customat/update_overlays() . = ..() @@ -382,7 +378,7 @@ products[key] = product product = products[key] - product.containtment += I + product.containment += I product.amount++ inserted_items_count++ @@ -704,7 +700,7 @@ */ /obj/machinery/customat/proc/do_vend(datum/data/customat_product/product, mob/user) var/put_on_turf = TRUE - var/obj/item/vended = product.containtment[1] + var/obj/item/vended = product.containment[1] if(istype(vended) && user && iscarbon(user) && user.Adjacent(src)) if(user.put_in_hands(vended, ignore_anim = FALSE)) put_on_turf = FALSE @@ -713,7 +709,7 @@ var/turf/T = get_turf(src) vended.forceMove(T) - product.containtment.Remove(product.containtment[1]) + product.containment.Remove(product.containment[1]) inserted_items_count-- return TRUE diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index 1d44e121709..f16288a592f 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -195,7 +195,7 @@ underlays += emissive_appearance(icon, "[icon_state]_lightmask", src) -/obj/machinery/recharger/proc/get_cell_from(obj/item/I) +/proc/get_cell_from(obj/item/I) if(istype(I, /obj/item/gun/energy)) var/obj/item/gun/energy/E = I return E.cell diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index a7af66b69be..6acb8d94127 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -1189,7 +1189,7 @@ */ /obj/machinery/vending/proc/tilt(atom/target_atom, crit = FALSE, from_combat = FALSE) - if(QDELETED(src) || !has_gravity(src) || !tiltable || tilted) + if(QDELETED(src) || no_gravity(src) || !tiltable || tilted) return tilted = TRUE diff --git a/code/game/mecha/combat/phazon.dm b/code/game/mecha/combat/phazon.dm index 846b55d0c6c..3584cc58981 100644 --- a/code/game/mecha/combat/phazon.dm +++ b/code/game/mecha/combat/phazon.dm @@ -19,7 +19,6 @@ force = 15 phase_state = "phazon-phase" max_equip = 3 - mech_type = MECH_TYPE_PHAZON /obj/mecha/combat/phazon/GrantActions(mob/living/user, human_occupant = 0) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 716c499ae69..fa35df0aba5 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -124,6 +124,9 @@ ///Mech subtype. Currently used in paintkits. var/mech_type = MECH_TYPE_NONE + /// Modifier of some phasing effects. + var/phase_modifier = 1 + hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) /obj/mecha/Initialize() @@ -511,16 +514,16 @@ can_move = world.time + step_in_final if(turnsound) playsound(src, turnsound, 40, 1) - if(phasing && get_charge() >= phasing_energy_drain) + if(phasing && get_charge() >= phasing_energy_drain / phase_modifier) if(strafe) //No strafe while phase mode is active toggle_strafe(silent = TRUE) if(can_move < world.time) . = FALSE // We lie to mech code and say we didn't get to move, because we want to handle power usage + cooldown ourself flick("[initial_icon]-phase", src) forceMove(get_step(src, direction)) - use_power(phasing_energy_drain) + use_power(phasing_energy_drain / phase_modifier) playsound(src, stepsound, 40, 1) - can_move = world.time + (step_in * 3) + can_move = world.time + (step_in * 3 / phase_modifier) else if(stepsound) playsound(src, stepsound, 40, 1) diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm index f71553b6061..79079fb72de 100644 --- a/code/game/mecha/mecha_construction_paths.dm +++ b/code/game/mecha/mecha_construction_paths.dm @@ -1191,8 +1191,8 @@ result = "/obj/mecha/combat/phazon" steps = list( //1 - list("key" = /obj/item/assembly/signaler/anomaly/bluespace, - "backkey"=null, //Cannot remove the anomaly core once it's in + list("key" = /obj/item/assembly/signaler/core/bluespace, + "backkey"=TOOL_CROWBAR, "desc"="Anomaly core socket is open and awaiting connection."), //2 list("key" = TOOL_WELDER, @@ -1287,10 +1287,16 @@ "desc"="The hydraulic systems are disconnected.") ) + /// Inserted bluespace anomaly core. + var/obj/item/assembly/signaler/core/bluespace/core = null /datum/construction/reversible/mecha/phazon/action(atom/used_atom,mob/user as mob) return check_step(used_atom,user) +/datum/construction/reversible/mecha/phazon/after_spawn_result(atom/A) + var/obj/mecha/phazon = A + phazon.phase_modifier = core.get_strenght() / 150 + /datum/construction/reversible/mecha/phazon/custom_action(index, diff, atom/used_atom, mob/user) if(!..()) return 0 @@ -1470,8 +1476,13 @@ holder.icon_state = "phazon21" if(1) if(diff==FORWARD) + var/obj/item/assembly/signaler/core/bluespace/core = used_atom + if(core.get_strenght() < 100) + to_chat(user, span_warning("Ядро слишком слабо!")) + return FALSE + user.visible_message("[user] carefully inserts the anomaly core into \the [holder] and secures it.", "You slowly place the anomaly core into its socket and close its chamber.") - qdel(used_atom) + src.core = used_atom return 1 //ODYSSEUS diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm index 57550545f21..d050a155e83 100644 --- a/code/game/mecha/mecha_parts.dm +++ b/code/game/mecha/mecha_parts.dm @@ -401,7 +401,7 @@ /obj/item/mecha_parts/chassis/phazon/attackby(obj/item/I, mob/user, params) . = ..() - if(istype(I, /obj/item/assembly/signaler/anomaly) && !istype(I, /obj/item/assembly/signaler/anomaly/bluespace)) + if(iscore(I) && !iscorebluespace(I)) to_chat(user, span_warning("The anomaly core socket only accepts bluespace anomaly cores!")) diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index 60b9191a03f..3b7a8f7a984 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -101,6 +101,7 @@ target.set_glide_size(glide_size) target.setDir(dir) + REMOVE_TRAIT(target, TRAIT_FLOORED, GRAVITATION_TRAIT) post_buckle_mob(target) SEND_SIGNAL(src, COMSIG_MOVABLE_BUCKLE, target, force) @@ -157,6 +158,10 @@ if(istype(location) && !buckled_mob.currently_z_moving) location.zFall(buckled_mob) + if(isliving(src)) + var/mob/living/M = src + M.refresh_gravity() + post_unbuckle_mob(.) if(!QDELETED(buckled_mob) && !buckled_mob.currently_z_moving && isturf(buckled_mob.loc)) // In the case they unbuckled to a flying movable midflight. diff --git a/code/game/objects/effects/effect_system/effects_other.dm b/code/game/objects/effects/effect_system/effects_other.dm index 9e0f6c3eb40..21130411a73 100644 --- a/code/game/objects/effects/effect_system/effects_other.dm +++ b/code/game/objects/effects/effect_system/effects_other.dm @@ -51,7 +51,7 @@ /datum/effect_system/trail_follow/generate_effect() if(!check_conditions()) return stop() - if(oldposition && !(oldposition == get_turf(holder)) && (!oldposition.has_gravity() || !nograv_required)) + if(oldposition && !(oldposition == get_turf(holder)) && (oldposition.no_gravity() || !nograv_required)) var/obj/effect/new_effect = new effect_type(oldposition) set_dir(new_effect) if(fade && fadetype) @@ -96,7 +96,7 @@ /datum/effect_system/trail_follow/spacepod/generate_effect() if(!check_conditions()) return stop() - if(oldposition && !(oldposition == get_turf(holder)) && (!oldposition.has_gravity() || !nograv_required)) + if(oldposition && !(oldposition == get_turf(holder)) && (oldposition.no_gravity() || !nograv_required)) // spacepod loc is always southwest corner of 4x4 space var/turf/our_turf = holder.loc var/loc1 diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm index 86b498b54c4..1f9e588e5cb 100644 --- a/code/game/objects/effects/effects.dm +++ b/code/game/objects/effects/effects.dm @@ -82,7 +82,7 @@ /obj/effect/abstract/singularity_act() return -/obj/effect/abstract/has_gravity() +/obj/effect/abstract/get_gravity() return /obj/effect/abstract/narsie_act() @@ -103,7 +103,7 @@ /obj/effect/abstract/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) return -/obj/effect/abstract/has_gravity(turf/gravity_turf) +/obj/effect/abstract/get_gravity(turf/gravity_turf) return FALSE /obj/effect/decal diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index 080ac73019f..619f14d71f6 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -285,7 +285,7 @@ lootcount = 8 loot = list( /obj/item/mmi/robotic_brain = 50, - /obj/item/assembly/signaler/anomaly = 50, + /obj/item/assembly/signaler/core/tier2 = 50, /obj/item/mecha_parts/mecha_equipment/weapon/energy/xray = 50, /obj/item/mecha_parts/mecha_equipment/teleporter/precise = 50, /obj/item/autoimplanter/old = 50, @@ -302,11 +302,11 @@ /obj/item/slime_extract/adamantine = 50, /obj/item/slime_extract/rainbow = 50, /obj/item/slime_extract/sepia = 50, - /obj/item/assembly/signaler/anomaly/vortex = 50, - /obj/item/assembly/signaler/anomaly/bluespace = 50, - /obj/item/assembly/signaler/anomaly/flux = 50, - /obj/item/assembly/signaler/anomaly/grav = 50, - /obj/item/assembly/signaler/anomaly/pyro = 50, + /obj/item/assembly/signaler/core/vortex/tier2 = 50, + /obj/item/assembly/signaler/core/bluespace/tier2 = 50, + /obj/item/assembly/signaler/core/energetic/tier2 = 50, + /obj/item/assembly/signaler/core/gravitational/tier2 = 50, + /obj/item/assembly/signaler/core/atmospheric/tier2 = 50, /obj/item/t_scanner/science = 50, /obj/item/t_scanner/experimental = 5) diff --git a/code/game/objects/items/anomaly_beacon.dm b/code/game/objects/items/anomaly_beacon.dm deleted file mode 100644 index 113136a524a..00000000000 --- a/code/game/objects/items/anomaly_beacon.dm +++ /dev/null @@ -1,30 +0,0 @@ -/obj/item/assembly/anomaly_beacon - icon = 'icons/obj/weapons/techrelic.dmi' - icon_state = "beacon" - item_state = "beacon" - lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' - righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' - name = "anomaly beacon" - desc = "A device that draws power from bluespace and creates a permanent tracking beacon." - origin_tech = "bluespace=6" - -/obj/item/assembly/anomaly_beacon/activate() - var/obj/effect/anomaly/anomaly_path = pick(subtypesof(/obj/effect/anomaly/)) - var/newAnomaly = new anomaly_path(get_turf(src)) - notify_ghosts("[name] has an object of interest: [newAnomaly]!", title = "Something's Interesting!", source = newAnomaly, action = NOTIFY_FOLLOW) - qdel(src) - -/obj/item/assembly/anomaly_beacon/attack_self(mob/user) - activate() - -/datum/crafting_recipe/anomaly_beacon - name = "Anomaly beacon" - result = /obj/item/assembly/anomaly_beacon - tools = list(TOOL_SCREWDRIVER) - reqs = list(/obj/item/assembly/signaler/anomaly = 1, - /obj/item/relict_production/rapid_dupe = 1, - /obj/item/radio/beacon = 1, - /obj/item/stack/cable_coil = 5) - time = 300 - category = CAT_WEAPONRY - subcategory = CAT_WEAPON diff --git a/code/game/objects/items/weapons/experimental_syringe_gun.dm b/code/game/objects/items/weapons/experimental_syringe_gun.dm deleted file mode 100644 index e6095af7b03..00000000000 --- a/code/game/objects/items/weapons/experimental_syringe_gun.dm +++ /dev/null @@ -1,68 +0,0 @@ -/obj/item/gun/syringe/rapidsyringe/experimental - name = "experimental syringe gun" - desc = "Эксперементальный шприцемет с 6 слотами для шприцев, встроенным, самовосполняющимся хранилищем химикатов и новейшей системой автозаправки шприцев." - origin_tech = "combat=3;biotech=4;bluespace=5" - icon = 'icons/obj/weapons/techrelic.dmi' - item_state = "strynggun" - lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' - righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' - icon_state = "strynggun" - materials = list(MAT_METAL=2000, MAT_GLASS=2000, MAT_BLUESPACE=400) - var/obj/item/reagent_containers/glass/beaker/large/ready_reagents = new - var/obj/item/reagent_containers/glass/beaker/large/processed_reagents = new - var/synth_speed = 5 - var/bank_size = 100 - origin_tech = "bluespace=4;biotech=5" - -/obj/item/gun/syringe/rapidsyringe/experimental/Initialize() - . = ..() - START_PROCESSING(SSobj, src) - -/obj/item/gun/syringe/rapidsyringe/experimental/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/gun/syringe/rapidsyringe/experimental/attackby(obj/item/A, mob/user) - if(istype(A, /obj/item/reagent_containers/syringe)) - var/in_clip = length(syringes) + (chambered.BB ? 1 : 0) - if(in_clip < max_syringes) - if(!user.drop_transfer_item_to_loc(A, src)) - return ..() - balloon_alert(user, "заряжено!") - syringes.Add(A) - process_chamber() // Chamber the syringe if none is already - return ATTACK_CHAIN_BLOCKED_ALL - else - balloon_alert(user, "недостаточно места!") - return ATTACK_CHAIN_PROCEED - else if(istype(A, /obj/item/reagent_containers/glass)) - var/obj/item/reagent_containers/glass/RC = A - if (!RC.reagents.reagent_list) - return ..() - ready_reagents.reagents.clear_reagents() - processed_reagents.reagents.clear_reagents() - RC.reagents.trans_to(ready_reagents, bank_size) - ready_reagents.reagents.trans_to(processed_reagents, synth_speed) - balloon_alert(user, "синтезируемый набор веществ изменен!") - return ATTACK_CHAIN_BLOCKED_ALL - else - return ..() - -/obj/item/gun/syringe/rapidsyringe/experimental/process() - for (var/obj/item/reagent_containers/syringe/S in syringes) - ready_reagents.reagents.trans_to(S, ready_reagents.reagents.total_volume) - for (var/datum/reagent/R in processed_reagents.reagents.reagent_list) - if (R.can_synth) - ready_reagents.reagents.add_reagent(R.id, R.volume) - -/datum/crafting_recipe/rapidsyringe_experimental - name = "Experemintal syringe gun" - result = /obj/item/gun/syringe/rapidsyringe/experimental - tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH) - reqs = list(/obj/item/relict_production/perfect_mix = 1, - /obj/item/assembly/signaler/anomaly/vortex = 1, - /obj/item/gun/syringe/rapidsyringe = 1, - /obj/item/stock_parts/matter_bin = 1) - time = 300 - category = CAT_WEAPONRY - subcategory = CAT_WEAPON diff --git a/code/game/objects/items/weapons/grenades/fauna_bomb.dm b/code/game/objects/items/weapons/grenades/fauna_bomb.dm deleted file mode 100644 index 6ca2c4314be..00000000000 --- a/code/game/objects/items/weapons/grenades/fauna_bomb.dm +++ /dev/null @@ -1,64 +0,0 @@ -/obj/item/grenade/fauna_bomb - name = "fauna bomb" - desc = "Эксперементальная, многоразовая граната, создающая фауну агрессивную ко всем, кроме активировавшего гранату." - w_class = WEIGHT_CLASS_SMALL - icon = 'icons/obj/weapons/techrelic.dmi' - icon_state = "bomb" - item_state = "bomb" - lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' - righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' - var/deliveryamt = 8 - var/amount = 3 - COOLDOWN_DECLARE(fauna_bomb_cooldown) - var/mob/activator - origin_tech = "bluespace=4;biotech=5" - -/obj/item/grenade/fauna_bomb/attack_self(mob/user) - if(!COOLDOWN_FINISHED(src, fauna_bomb_cooldown)) - to_chat(user, span_warning("[src] is still recharging!")) - return - - COOLDOWN_START(src, fauna_bomb_cooldown, 60 SECONDS) - activator = user - return ..(user, FALSE) - -/obj/item/grenade/fauna_bomb/prime() - active = FALSE - playsound(get_turf(src), 'sound/items/rawr.ogg', 100, TRUE) - var/faction = activator.name + "_fauna_bomb" - activator.faction |= faction - var/list/mob/living/simple_animal/mobs = list() - - var/mob/living/simple_animal/spawn_mob_type = pick(/mob/living/simple_animal/hostile/asteroid/hivelord/legion, /mob/living/simple_animal/hostile/asteroid/goliath, /mob/living/simple_animal/hostile/asteroid/marrowweaver) - - for(var/i in 1 to amount) - var/mob/living/simple_animal/new_mob = new spawn_mob_type(get_turf(src)) - mobs.Add(new_mob) - new_mob.set_leash(activator, 10) - new_mob.faction |= faction - if(prob(50)) - for(var/j = 1, j <= rand(1, 3), j++) - step(new_mob, pick(NORTH, SOUTH, EAST, WEST)) - - if(prob(40)) - to_chat(activator, span_warning("[src] falls apart!")) - qdel(src) - - sleep(600) - for (var/mob/mob in mobs) - mob.dust() - -/obj/item/grenade/fauna_bomb/update_icon_state() - return - -/datum/crafting_recipe/fauna_bomb - name = "Fauna bomb" - result = /obj/item/grenade/fauna_bomb - tools = list(TOOL_SCREWDRIVER) - reqs = list(/obj/item/relict_production/pet_spray = 1, - /obj/item/assembly/signaler/anomaly/pyro = 1, - /obj/item/grenade/chem_grenade/adv_release = 1, - /obj/item/stack/cable_coil = 5) - time = 300 - category = CAT_WEAPONRY - subcategory = CAT_WEAPON diff --git a/code/game/objects/items/weapons/laser_eyes_injector.dm b/code/game/objects/items/weapons/laser_eyes_injector.dm index a31969c55f4..32e9a666906 100644 --- a/code/game/objects/items/weapons/laser_eyes_injector.dm +++ b/code/game/objects/items/weapons/laser_eyes_injector.dm @@ -56,8 +56,8 @@ /obj/effect/proc_holder/spell/lasereyes/cast(list/targets, mob/user = usr) if(HAS_TRAIT_FROM(user, TRAIT_LASEREYES, UNIQUE_TRAIT_SOURCE(src))) REMOVE_TRAIT(user, TRAIT_LASEREYES, UNIQUE_TRAIT_SOURCE(src)) - to_chat(user, span_warning("Легкое жжение в области ваших глаз прошло.")) + to_chat(user, span_warning("Лёгкое жжение в области ваших глаз прошло.")) else ADD_TRAIT(user, TRAIT_LASEREYES, UNIQUE_TRAIT_SOURCE(src)) - to_chat(user, span_warning("Вы чувствуете легкое жжение в области ваших глаз.")) + to_chat(user, span_warning("Вы чувствуете лёгкое жжение в области ваших глаз.")) diff --git a/code/game/objects/items/weapons/tuned_anomalous_teleporter.dm b/code/game/objects/items/weapons/tuned_anomalous_teleporter.dm deleted file mode 100644 index 620ea5f9222..00000000000 --- a/code/game/objects/items/weapons/tuned_anomalous_teleporter.dm +++ /dev/null @@ -1,74 +0,0 @@ -/obj/item/tuned_anomalous_teleporter - name = "tuned anomalous teleporter" - desc = "A portable item using blue-space technology." - icon = 'icons/obj/weapons/techrelic.dmi' - icon_state = "teleport" - lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' - righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' - item_state = "teleport" - throwforce = 0 - w_class = WEIGHT_CLASS_SMALL - throw_speed = 3 - throw_range = 5 - materials = list(MAT_METAL=10000) - origin_tech = "magnets=3;bluespace=4" - armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 30, BIO = 0, RAD = 0, FIRE = 100, ACID = 100) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - /// Variable contains next time hand tele can be used to make it not EMP proof - var/emp_timer = 0 - COOLDOWN_DECLARE(tuned_anomalous_teleporter_cooldown) // declare cooldown for teleportations - COOLDOWN_DECLARE(emp_cooldown) // declare cooldown for EMP - var/base_cooldown = 20 SECONDS // cooldown for teleportations - var/emp_cooldown_min = 10 SECONDS // min cooldown for emp - var/emp_cooldown_max = 15 SECONDS // max cooldown for emp - var/tp_range = 5 // range of teleportations - origin_tech = "bluespace=5" - -/obj/item/tuned_anomalous_teleporter/attack_self(mob/user) - if(!COOLDOWN_FINISHED(src, emp_cooldown)) - do_sparks(5, FALSE, loc) - to_chat(user, span_warning("[src] attempts to teleport you, but abruptly shuts off.")) - return FALSE - if(!COOLDOWN_FINISHED(src, tuned_anomalous_teleporter_cooldown)) - to_chat(user, span_warning("[src] is still recharging.")) - return FALSE - - COOLDOWN_START(src, tuned_anomalous_teleporter_cooldown, base_cooldown) - - var/datum/teleport/TP = new /datum/teleport() - var/crossdir = angle2dir((dir2angle(user.dir)) % 360) - var/turf/T1 = get_turf(user) - for(var/i in 1 to tp_range) - T1 = get_step(T1, crossdir) - var/datum/effect_system/smoke_spread/s1 = new - var/datum/effect_system/smoke_spread/s2 = new - s1.set_up(5, FALSE, user) - s2.set_up(5, FALSE, user) - TP.start(user, T1, FALSE, TRUE, s1, s2, 'sound/effects/phasein.ogg', ) - TP.doTeleport() - -/obj/item/tuned_anomalous_teleporter/emp_act(severity) - make_inactive(severity) - return ..() - -/obj/item/tuned_anomalous_teleporter/proc/make_inactive(severity) - var/time = rand(emp_cooldown_min, emp_cooldown_max) * (severity == EMP_HEAVY ? 2 : 1) - COOLDOWN_START(src, emp_cooldown, time) - -/obj/item/tuned_anomalous_teleporter/examine(mob/user) - . = ..() - if(emp_timer > world.time) - . += span_warning("It looks inactive.") - -/datum/crafting_recipe/tuned_anomalous_teleporter - name = "Tuned anomalous teleporter" - result = /obj/item/tuned_anomalous_teleporter - tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) - reqs = list(/obj/item/relict_production/strange_teleporter = 1, - /obj/item/assembly/signaler/anomaly/bluespace = 1, - /obj/item/gps = 1, - /obj/item/stack/ore/bluespace_crystal, - /obj/item/stack/sheet/metal = 2, - /obj/item/stack/cable_coil = 5) - time = 300 - category = CAT_MISC diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm index 85a4c2e9fa4..7185b9fa88b 100644 --- a/code/game/objects/items/weapons/twohanded.dm +++ b/code/game/objects/items/weapons/twohanded.dm @@ -9,7 +9,6 @@ * Singularity hammer * Mjolnnir * Knighthammer - * Pyro Claws */ /*################################################################## @@ -919,130 +918,6 @@ /obj/item/twohanded/bamboospear/update_icon_state() icon_state = "bamboo_spear[HAS_TRAIT(src, TRAIT_WIELDED)]" -//pyro claws -/obj/item/twohanded/required/pyro_claws - name = "hardplasma energy claws" - desc = "The power of the sun, in the claws of your hand." - icon_state = "pyro_claws" - item_flags = ABSTRACT|DROPDEL - force = 25 - force_wielded = 25 - damtype = BURN - armour_penetration = 40 - block_chance = 50 - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut", "savaged", "clawed") - toolspeed = 0.5 - -/obj/item/twohanded/required/pyro_claws/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT) - START_PROCESSING(SSobj, src) - -/obj/item/twohanded/required/pyro_claws/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/twohanded/required/pyro_claws/process() - if(prob(15)) - do_sparks(rand(1,6), 1, loc) - -/obj/item/twohanded/required/pyro_claws/afterattack(atom/target, mob/user, proximity, params) - if(!proximity) - return - if(prob(60)) - do_sparks(rand(1,6), 1, loc) - if(istype(target, /obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/A = target - - if(!A.requiresID() || A.allowed(user)) - return - - if(A.locked) - to_chat(user, "The airlock's bolts prevent it from being forced.") - return - - if(A.arePowerSystemsOn()) - user.visible_message("[user] jams [user.p_their()] [name] into the airlock and starts prying it open!", "You start forcing the airlock open.", "You hear a metal screeching sound.") - playsound(A, 'sound/machines/airlock_alien_prying.ogg', 150, 1) - if(!do_after(user, 2.5 SECONDS, A)) - return - user.visible_message("[user] forces the airlock open with [user.p_their()] [name]!", "You force open the airlock.", "You hear a metal screeching sound.") - A.open(2) - -/obj/item/clothing/gloves/color/black/pyro_claws - name = "Fusion gauntlets" - desc = "Cybersun Industries developed these gloves after a grifter fought one of their soldiers, who attached a pyro core to an energy sword, and found it mostly effective." - item_state = "pyro" - item_color = "pyro" - icon_state = "pyro" - can_be_cut = FALSE - actions_types = list(/datum/action/item_action/toggle) - var/on_cooldown = FALSE - var/used = FALSE - var/obj/item/assembly/signaler/anomaly/pyro/core - -/obj/item/clothing/gloves/color/black/pyro_claws/Destroy() - QDEL_NULL(core) - return ..() - -/obj/item/clothing/gloves/color/black/pyro_claws/examine(mob/user) - . = ..() - if(core) - . += "[src] are fully operational!" - else - . += "It is missing a pyroclastic anomaly core." - -/obj/item/clothing/gloves/color/black/pyro_claws/item_action_slot_check(slot, mob/user, datum/action/action) - if(slot == ITEM_SLOT_GLOVES) - return TRUE - -/obj/item/clothing/gloves/color/black/pyro_claws/ui_action_click(mob/user, datum/action/action, leftclick) - if(!core) - to_chat(user, "[src] has no core to power it!") - return - if(on_cooldown) - to_chat(user, "[src] is on cooldown!") - do_sparks(rand(1,6), 1, loc) - return - if(used) - visible_message("Energy claws slides back into the depths of [loc]'s wrists.") - user.drop_from_active_hand(force = TRUE)//dropdel stuff. only ui act, without hotkeys - do_sparks(rand(1,6), 1, loc) - on_cooldown = TRUE - addtimer(CALLBACK(src, PROC_REF(reboot)), 1 MINUTES) - return - if(user.get_active_hand() && !user.drop_from_active_hand()) - to_chat(user, "[src] are unable to deploy the blades with the items in your hands!") - return - var/obj/item/W = new /obj/item/twohanded/required/pyro_claws - user.visible_message("[user] deploys [W] from [user.p_their()] wrists in a shower of sparks!", "You deploy [W] from your wrists!", "You hear the shower of sparks!") - user.put_in_hands(W) - ADD_TRAIT(src, TRAIT_NODROP, PYRO_CLAWS_TRAIT) - used = TRUE - do_sparks(rand(1,6), 1, loc) - - -/obj/item/clothing/gloves/color/black/pyro_claws/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/assembly/signaler/anomaly/pyro)) - add_fingerprint(user) - if(core) - to_chat(user, span_warning("The [core.name] is already installed.")) - return ATTACK_CHAIN_PROCEED - if(!user.drop_transfer_item_to_loc(I, src)) - return ..() - to_chat(user, span_notice("You insert [I] into [src], and it starts to warm up.")) - core = I - return ATTACK_CHAIN_BLOCKED_ALL - return ..() - - -/obj/item/clothing/gloves/color/black/pyro_claws/proc/reboot() - on_cooldown = FALSE - used = FALSE - REMOVE_TRAIT(src, TRAIT_NODROP, PYRO_CLAWS_TRAIT) - atom_say("Internal plasma canisters recharged. Gloves sufficiently cooled") - /obj/item/twohanded/fishingrod name = "ol' reliable" desc = "Hey! I caught a miner!" diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm index e21051d2128..72542aaade5 100644 --- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm +++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm @@ -27,7 +27,7 @@ /obj/structure/closet/cardboard/relaymove(mob/living/user, direction) - if(!COOLDOWN_FINISHED(src, recently_moved_cd) || !istype(user) || opened || user.incapacitated() || !isturf(loc) || !has_gravity()) + if(!COOLDOWN_FINISHED(src, recently_moved_cd) || !istype(user) || opened || user.incapacitated() || !isturf(loc) || no_gravity()) return var/turf/next_step = get_step(src, direction) if(!next_step) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm index 274d00974e5..bfd491deceb 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm @@ -62,6 +62,10 @@ new /obj/item/megaphone(src) //added here deleted on maps new /obj/item/storage/garmentbag/RD(src) new /obj/item/t_scanner/experimental(src) + new /obj/item/anomaly_analyzer(src) + new /obj/item/anomaly_analyzer(src) + new /obj/item/gun/energy/anomaly_stabilizer(src) + new /obj/item/gun/energy/anomaly_stabilizer(src) /obj/structure/closet/secure_closet/research_reagents name = "research chemical storage closet" diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm index f10084aa8af..7f0c6c67f35 100644 --- a/code/game/objects/structures/false_walls.dm +++ b/code/game/objects/structures/false_walls.dm @@ -214,9 +214,11 @@ // Copy of `/atom/proc/hitby()`. Falsewalls must use this `hitby` as do regular walls. /obj/structure/falsewall/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(density && !AM.has_gravity()) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). + if(density && AM.no_gravity()) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, hitby_react), AM), 0.2 SECONDS) + SEND_SIGNAL(src, COMSIG_ATOM_HITBY, AM, skipcatch, hitpush, blocked, throwingdatum) + /* * False R-Walls diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm index 976b13ae40e..12dddcab10e 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm @@ -66,8 +66,9 @@ /obj/structure/chair/wheelchair/relaymove(mob/user, direction) if(!COOLDOWN_FINISHED(src, wheelchair_move_delay)) return FALSE + var/turf/next_step = get_step(src, direction) - if(!next_step || propelled || !Process_Spacemove(direction) || !has_gravity(loc) || !isturf(loc) || !has_buckled_mobs() || user != buckled_mobs[1]) + if(!next_step || propelled || !Process_Spacemove(direction) || no_gravity(loc) || !isturf(loc) || !has_buckled_mobs() || user != buckled_mobs[1]) COOLDOWN_START(src, wheelchair_move_delay, 0.5 SECONDS) return FALSE diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 52c4c242a19..567e7f2f8ad 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -519,7 +519,9 @@ /obj/structure/table/glass/proc/check_break(mob/living/M) if(M.incorporeal_move || (M.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)) return - if(M.has_gravity() && M.mob_size > MOB_SIZE_SMALL) + + // It won't break with neative gravity. + if(M.get_gravity() > NO_GRAVITY && M.mob_size > MOB_SIZE_SMALL) table_shatter(M) diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 811044f0adf..8f93b2c0a9d 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -144,7 +144,7 @@ /turf/simulated/handle_slip(mob/living/carbon/slipper, weaken_amount, obj/slippable, lube_flags, tilesSlipped) if(slipper.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) return FALSE - if(!slipper.has_gravity(src)) + if(slipper.no_gravity(src)) return FALSE var/slide_distance = isnull(tilesSlipped) ? 4 : tilesSlipped diff --git a/code/game/turfs/simulated/floor/lava.dm b/code/game/turfs/simulated/floor/lava.dm index 0f98f4f00bf..b7ffa55ce14 100644 --- a/code/game/turfs/simulated/floor/lava.dm +++ b/code/game/turfs/simulated/floor/lava.dm @@ -46,6 +46,7 @@ START_PROCESSING(SSprocessing, src) /turf/simulated/floor/lava/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + SEND_SIGNAL(src, COMSIG_ATOM_HITBY, AM, skipcatch, hitpush, blocked, throwingdatum) if(burn_stuff(AM)) START_PROCESSING(SSprocessing, src) @@ -104,7 +105,8 @@ /turf/simulated/floor/lava/proc/can_burn_stuff(atom/movable/burn_target) if(QDELETED(burn_target)) return LAVA_BE_IGNORING - if(burn_target.movement_type & MOVETYPES_NOT_TOUCHING_GROUND || !burn_target.has_gravity()) //you're flying over it. + + if(burn_target.movement_type & MOVETYPES_NOT_TOUCHING_GROUND || burn_target.no_gravity()) //you're flying over it. return LAVA_BE_IGNORING if(isobj(burn_target)) @@ -127,7 +129,7 @@ var/atom/movable/burn_buckled = burn_living.buckled if(burn_buckled) - if(burn_buckled.movement_type & MOVETYPES_NOT_TOUCHING_GROUND || !burn_buckled.has_gravity()) + if(burn_buckled.movement_type & MOVETYPES_NOT_TOUCHING_GROUND || burn_buckled.no_gravity()) return LAVA_BE_PROCESSING if(isobj(burn_buckled)) var/obj/burn_buckled_obj = burn_buckled diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 337e11f4264..d1dc22e35f7 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -204,6 +204,23 @@ return FALSE +// Enter, but hypothetical. +/turf/proc/can_enter(atom/movable/mover) + var/atom/mover_loc = mover.loc + var/border_dir = get_dir(src, mover) + var/can_pass_self = CanPass(mover, border_dir) + if(can_pass_self) + for(var/atom/movable/obstacle as anything in contents) + // Multi tile objects and moving out of other objects. + if(obstacle == mover || obstacle == mover_loc) + continue + + if(!obstacle.CanPass(mover, border_dir)) + return FALSE + + return TRUE + + /turf/Enter(atom/movable/mover) // Do not call ..() // Byond's default turf/Enter() doesn't have the behaviour we want with Bump() @@ -524,8 +541,9 @@ /turf/handle_fall(mob/living/carbon/faller) - if(has_gravity(src)) + if(!no_gravity(src)) playsound(src, "bodyfall", 50, TRUE) + faller.drop_from_hands() @@ -679,12 +697,17 @@ /// Precipitates a movable (plus whatever buckled to it) to lower z levels if possible and then calls zImpact() /turf/proc/zFall(atom/movable/falling, levels = 1, force = FALSE, falling_from_move = FALSE) - var/turf/target = get_step_multiz(src, DOWN) + // Yes, you can fall up. + var/fall_dir = get_gravity() > 0 ? DOWN : UP + + var/turf/target = get_step_multiz(src, fall_dir) if(!target) return FALSE + var/isliving = isliving(falling) if(!isliving && !isobj(falling)) return FALSE + var/atom/movable/living_buckled if(isliving) var/mob/living/falling_living = falling @@ -692,9 +715,11 @@ if(falling_living.buckled) living_buckled = falling falling = falling_living.buckled + if(!falling_from_move && falling.currently_z_moving) return FALSE - if(!force && !falling.can_z_move(DOWN, src, target, ZMOVE_FALL_FLAGS)) + + if(!force && !falling.can_z_move(fall_dir, src, target, ZMOVE_FALL_FLAGS)) falling.set_currently_z_moving(FALSE, TRUE) living_buckled?.set_currently_z_moving(FALSE, TRUE) return FALSE diff --git a/code/modules/anomalies/anomalies/anomaly.dm b/code/modules/anomalies/anomalies/anomaly.dm new file mode 100644 index 00000000000..14ac0066132 --- /dev/null +++ b/code/modules/anomalies/anomalies/anomaly.dm @@ -0,0 +1,314 @@ +/obj/effect/anomaly + name = "аномалия" + desc = "Загадочная аномалия. Обычно такую можно наблюдать только в станционном секторе." + ru_names = list( + NOMINATIVE = "аномалия", \ + GENITIVE = "аномалии", \ + DATIVE = "аномалии", \ + ACCUSATIVE = "аномалию", \ + INSTRUMENTAL = "аномалией", \ + PREPOSITIONAL = "аномалии" + ) + icon_state = "bhole3" + gender = FEMALE + anchored = TRUE + density = TRUE + alpha = 0 + light_range = 3 + layer = ABOVE_ALL_MOB_LAYER + /// Type of core that will be dropped after stabilisation. + var/core_type = /obj/item/toy/plushie/blahaj/twohanded + /// Type of anomaly of the next tier. + var/stronger_anomaly_type = null + /// Type of anomaly of the prew tier. + var/weaker_anomaly_type = null + /// Name of the type of anomaly. + var/anomaly_type = ANOMALY_TYPE_RANDOM + /// Tier of anomaly. + var/tier = 0 + /// Level of strenght. Affects the effects of anomaly. + var/strenght = 100 + /// Anomaly stability. Affects speed and strenght change. + var/stability = 50 + /// List of impulses types. + var/list/impulses_types = list() + /// List of impulses datums. + var/list/datum/anomaly_impulse/impulses = list() + + /// The moment from which the anomaly will be able to move. + var/move_moment = 0 + /// The moment from which the anomaly will be able to move by impulse. + var/move_impulse_moment = 0 + /// The amount by which the strength of the anomaly's effects is temporarily reduced. + var/weaken = 0 + /// The moment at which the reduction in the effects of the anomaly will be reset. + var/weaken_moment = 0 + /// Matrix used for anomaly animations. + var/matrix/matr = matrix() + +/obj/effect/anomaly/proc/size_by_strenght(cur_strenght) + if(!cur_strenght) + cur_strenght = strenght + + return (tier * 50 + cur_strenght / 2) / 100 + +/obj/effect/anomaly/proc/init_animation() + matr.Scale(0.1, 0.1) + animate(src, transform = matr, time = 0, flags = ANIMATION_PARALLEL) + var/mult = size_by_strenght() * 10 + matr.Scale(mult, mult) + animate(src, transform = matr, time = 1 SECONDS, alpha = 255, flags = ANIMATION_PARALLEL) + + +/obj/effect/anomaly/Initialize(spawnloc, spawn_strenght = rand(20, 40), spawn_stability = rand(10, 29)) + GLOB.created_anomalies[anomaly_type]++ + . = ..() + if(!get_area(src)) + return INITIALIZE_HINT_QDEL + + set_strenght(spawn_strenght, FALSE) + INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/effect/anomaly, init_animation)) + stability = spawn_stability + + GLOB.poi_list |= src + START_PROCESSING(SSobj, src) + + for(var/imp_type in impulses_types) + impulses.Add(new imp_type(src)) + + for(var/datum/anomaly_impulse/imp in impulses) + addtimer(CALLBACK(imp, TYPE_PROC_REF(/datum/anomaly_impulse, impulse_cycle)), rand(0, imp.scale_by_strenght(imp.period_low, imp.period_high))) + +/obj/effect/anomaly/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/anomaly/proc/get_move_dir() + return pick(GLOB.alldirs) + +// It is in function because the size will change depending on the strength of the anomaly. +/obj/effect/anomaly/proc/set_strenght(new_strenght, do_anim = TRUE) + if(do_anim) + var/mult = size_by_strenght(new_strenght) / size_by_strenght(strenght) + matr.Scale(mult, mult) + animate(src, transform = matr, time = 0.1 SECONDS, flags = ANIMATION_PARALLEL) + + strenght = clamp(new_strenght, 0, 100) + check_size_change() + +/obj/effect/anomaly/proc/collapse() + visible_message(span_warning("[capitalize(declent_ru(NOMINATIVE))] достигает критической массы и разрушается!")) + add_filter("collapse", 1, gauss_blur_filter(1)) + matr.Scale(3, 3) + animate(src, transform = matr, time = 1 SECONDS, alpha = 0, flags = ANIMATION_PARALLEL) + sleep(1 SECONDS) + qdel(src) + +/obj/effect/anomaly/proc/stabilyse() + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(tier * 3, FALSE, loc) + smoke.start() + + if(strenght < 50) + core_type = text2path("/obj/item/assembly/signaler/core/tier[tier]") + + new core_type(loc, strenght) + GLOB.poi_list.Remove(src) + qdel(src) + +/obj/effect/anomaly/proc/level_down() + if(!weaker_anomaly_type) + matr.Scale(0, 0) + animate(src, transform = matr, time = 1 SECONDS, flags = ANIMATION_PARALLEL) + visible_message(span_warning("[capitalize(declent_ru(NOMINATIVE))] теряет свою энергию и растворяется в пространстве!")) + sleep(1 SECONDS) + qdel(src) + else + visible_message(span_warning("[capitalize(declent_ru(NOMINATIVE))] ослабевает!")) + new weaker_anomaly_type(loc, rand(50, 80), clamp(stability + rand(10, 20), 0, 100)) + qdel(src) + +/obj/effect/anomaly/proc/level_up() + if(!stronger_anomaly_type) + collapse() + else + visible_message(span_warning("[capitalize(declent_ru(NOMINATIVE))] становится мощнее!")) + new stronger_anomaly_type(loc, rand(20, 50), clamp(stability - rand(10, 20), 0, 100)) + qdel(src) + +/obj/effect/anomaly/proc/mob_touch_effect(mob/living/matr) + return TRUE + +/obj/effect/anomaly/proc/check_size_change() + if(strenght == 100) + if(stability >= 50) + level_up() + else + collapse() + + return + + if(!strenght) + level_down() + return + +/obj/effect/anomaly/proc/core_touch_effect(obj/item/assembly/signaler/core/core) + var/mult + if(core.tier <= tier) + mult = 1 << (tier - core.tier) + else + mult = 1 / (1 << (core.tier - tier)) + + if(!iscoreempty(core)) + core.visible_message(span_warning("[capitalize(core.declent_ru(NOMINATIVE))] распадается, передавая свой заряд [declent_ru(DATIVE)].")) + set_strenght(strenght + core.charge / mult) + qdel(core) + do_sparks(5, FALSE, src) + return + + var/charge_delta = min(100, round(strenght / 3 * mult)) + var/new_charge = core.charge + charge_delta + + do_sparks(5, FALSE, src) + set_strenght(strenght - round(charge_delta / mult)) + + if(new_charge <= 50) + core.charge = new_charge + core.random_throw(3, 6, 5) + core.visible_message(span_warning("[capitalize(core.declent_ru(NOMINATIVE))] заряжается от [declent_ru(GENITIVE)], \ + но остаётся пустым из-за слишком низкого заряда.")) + return + + var/path = text2path("/obj/item/assembly/signaler/core/tier[core.tier]/[anomaly_type]") + var/obj/item/assembly/signaler/core/new_core = new path(core.loc, new_charge) + new_core.visible_message(span_warning("[capitalize(core.declent_ru(NOMINATIVE))] заряжается от [declent_ru(GENITIVE)], \ + превращаясь в [new_core.declent_ru(ACCUSATIVE)].")) + qdel(core) + new_core.random_throw(3, 6, 5) + return + +/obj/effect/anomaly/proc/item_touch_effect(obj/item/I) + . = TRUE + if(!istype(I)) + return + + if(tier == 3 && istype(I, /obj/item/anomaly_upgrader)) + visible_message(span_danger("[capitalize(I.declent_ru(NOMINATIVE))] попадает в [declent_ru(ACCUSATIVE)], прикрепляется к ней и активируется!")) + var/type = text2path("/obj/effect/anomaly/[anomaly_type]/tier4") + new type(loc, rand(20, 50), clamp(stability - rand(10, 20), 0, 100)) + qdel(I) + qdel(src) + return FALSE + + if(iscore(I)) + var/obj/item/assembly/signaler/core/core = I + if(core.born_moment + 1 SECONDS >= world.time) + return TRUE + + core_touch_effect(core) + return FALSE + + if(!I.origin_tech) + return + + if (prob(2)) + do_sparks(5, TRUE, src) + new /obj/item/relic(get_turf(I)) + qdel(I) + return + + if (!istype(I, /obj/item/relict_production/rapid_dupe)) + return + + var/amount = rand(1, 3) + for (var/i; i <= amount; i++) + new /obj/item/relic(get_turf(I)) + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(5, get_turf(I)) + smoke.start() + + qdel(I) + +/obj/effect/anomaly/attackby(obj/item/I, mob/living/user, params) + . = ..() + mob_touch_effect(user) + +/obj/effect/anomaly/attack_hand(mob/living/user, list/modifiers) + . = ..() + mob_touch_effect(user) + +/obj/effect/anomaly/Bumped(atom/movable/moving_atom) + . = ..() + if(isitem(moving_atom)) + item_touch_effect(moving_atom) + + if(isliving(moving_atom)) + mob_touch_effect(moving_atom) + +/obj/effect/anomaly/proc/after_move() + for(var/obj/item/I in get_turf(src)) + item_touch_effect(I) + + for(var/mob/living/matr in get_turf(src)) + mob_touch_effect(matr) + +/obj/effect/anomaly/proc/normal_move() + if(world.time > move_moment) + return do_move(get_move_dir()) + +/obj/effect/anomaly/proc/do_move(dir) + step(src, dir) + return TRUE + +/obj/effect/anomaly/proc/get_strenght() + if(world.time > weaken_moment) + weaken = 0 + + return max(min(strenght, 10), strenght - weaken) + +/obj/effect/anomaly/process() + if(stability < ANOMALY_GROW_STABILITY) + set_strenght(strenght + 1) + + if(stability > ANOMALY_DECREASE_STABILITY) + set_strenght(strenght - 1) + + if(stability == 100) + stabilyse() + return + + if(stability > ANOMALY_MOVE_MAX_STABILITY || !prob(get_strenght())) + return + + if(normal_move()) + after_move() + +/obj/effect/anomaly/proc/go_to(target, steps) + var/reversed = steps < 0 + if(reversed) + steps = -steps + + for(var/i = 1 to steps) + var/move_dir + if(reversed) + move_dir = get_dir(target, src) + else + move_dir = get_dir(src, target) + + do_move(move_dir) + sleep(2) + +/obj/effect/anomaly/singularity_act() + collapse() + +/obj/effect/anomaly/tesla_act() + collapse() + +/obj/effect/anomaly/ratvar_act() + collapse() + +/obj/effect/anomaly/narsie_act() + collapse() + +/obj/effect/anomaly/ex_act(severity) + return diff --git a/code/modules/anomalies/anomalies/atmospheric.dm b/code/modules/anomalies/anomalies/atmospheric.dm new file mode 100644 index 00000000000..16efd87b8dd --- /dev/null +++ b/code/modules/anomalies/anomalies/atmospheric.dm @@ -0,0 +1,213 @@ +/obj/effect/anomaly/atmospheric + anomaly_type = ANOMALY_TYPE_ATMOS + icon_state = "mustard" + /// Range of collapse effects. + var/collapse_range = 0 + /// Amount of gases spawned when anomaly collapses. + var/collapse_gas_amount = 0 + /// Minimum amount of slimes spawned when anomaly collapses. + var/collapse_slimes_low = 0 + /// Maximum amount of slimes spawned when anomaly collapses. + var/collapse_slimes_high = 0 + +/obj/effect/anomaly/atmospheric/collapse() + for(var/turf/simulated/T in view(collapse_range * 2, src)) + if(T.air) + T.air.temperature = rand(0, 50) + + for(var/turf/simulated/floor/T in view(collapse_range, src)) + var/near_ice = 0 // Generation will be more beautiful. + for(var/turf/simulated/checked in view(1, T)) + if(checked.GetComponent(/datum/component/wet_floor)) + near_ice++ + + if(prob(80 - near_ice * 20)) + new /obj/effect/snow(T) + else + T.MakeSlippery(TURF_WET_ICE, 120 SECONDS) + + for(var/mob/living/M in view(collapse_range, src)) + M.adjust_bodytemperature(-100) + M.apply_status_effect(/datum/status_effect/freon) + if(ishuman(M)) + M.reagents.add_reagent("frostoil", 5) + + var/turf/simulated/T = get_turf(src) + if(istype(T)) + T.atmos_spawn_air(LINDA_SPAWN_OXYGEN, collapse_gas_amount * 2/7) + T.atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, collapse_gas_amount * 5/7) + + for(var/i = 1 to rand(collapse_slimes_low, collapse_slimes_high)) + INVOKE_ASYNC(src, PROC_REF(make_slime)) + + . = ..() + +/obj/effect/anomaly/atmospheric/mob_touch_effect(mob/living/M) + . = ..() + var/new_temp = rand(0, 500) + M.adjust_bodytemperature(new_temp - M.bodytemperature) + if(new_temp >= T0C + 100 && prob(70)) + M.adjust_fire_stacks(new_temp / 50) + M.IgniteMob() + else + M.ExtinguishMob() + +/obj/effect/anomaly/atmospheric/item_touch_effect(obj/item/I) + . = ..() + I.fire_act(null, rand(0, 1000), rand(20, 200)) + +/obj/effect/anomaly/atmospheric/proc/make_slime() + var/turf/simulated/T = get_turf(src) + var/new_colour = pick("red", "orange", "blue", "dark blue") + var/mob/living/simple_animal/slime/random/S = new(T, new_colour) + S.rabid = TRUE + S.set_nutrition(S.get_max_nutrition()) + + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Хотите сыграть за слайма из атмосферной аномалии?", ROLE_SENTIENT, FALSE, 100, source = S, role_cleanname = "pyroclastic anomaly slime") + if(LAZYLEN(candidates)) + var/mob/dead/observer/chosen = pick(candidates) + S.key = chosen.key + S.mind.special_role = SPECIAL_ROLE_PYROCLASTIC_SLIME + add_game_logs("was made into a slime by pyroclastic anomaly at [AREACOORD(T)].", S) + +/obj/effect/anomaly/atmospheric/tier1 + name = "малая атмосферная аномалия" + ru_names = list( + NOMINATIVE = "малая атмосферная аномалия", \ + GENITIVE = "малой атмосферной аномалии", \ + DATIVE = "малой атмосферной аномалии", \ + ACCUSATIVE = "малую ​​атмосферную аномалию", \ + INSTRUMENTAL = "малой ​атмосферной аномалией", \ + PREPOSITIONAL = "малой ​​атмосферной аномалии" + ) + core_type = /obj/item/assembly/signaler/core/atmospheric/tier1 + stronger_anomaly_type = /obj/effect/anomaly/atmospheric/tier2 + tier = 1 + impulses_types = list( + /datum/anomaly_impulse/random_temp/tier1, + /datum/anomaly_impulse/freese/tier1, + /datum/anomaly_impulse/fire/tier1, + ) + + collapse_range = 2 + collapse_gas_amount = 150 + +/obj/effect/anomaly/atmospheric/tier2 + name = "атмосферная аномалия" + ru_names = list( + NOMINATIVE = "атмосферная аномалия", \ + GENITIVE = "атмосферной аномалии", \ + DATIVE = "атмосферной аномалии", \ + ACCUSATIVE = "​​атмосферную аномалию", \ + INSTRUMENTAL = "​атмосферной аномалией", \ + PREPOSITIONAL = "​​атмосферной аномалии" + ) + core_type = /obj/item/assembly/signaler/core/atmospheric/tier2 + weaker_anomaly_type = /obj/effect/anomaly/atmospheric/tier1 + stronger_anomaly_type = /obj/effect/anomaly/atmospheric/tier3 + tier = 2 + impulses_types = list( + /datum/anomaly_impulse/random_temp/tier2, + /datum/anomaly_impulse/freese/tier2, + /datum/anomaly_impulse/fire/tier2, + ) + + collapse_range = 5 + collapse_gas_amount = 350 + collapse_slimes_low = 0 + collapse_slimes_high = 2 + +/obj/effect/anomaly/atmospheric/tier3 + name = "большая атмосферная аномалия" + ru_names = list( + NOMINATIVE = "большая атмосферная аномалия", \ + GENITIVE = "большой атмосферной аномалии", \ + DATIVE = "большой атмосферной аномалии", \ + ACCUSATIVE = "большую ​​атмосферную аномалию", \ + INSTRUMENTAL = "большой ​атмосферной аномалией", \ + PREPOSITIONAL = "большой ​​атмосферной аномалии" + ) + core_type = /obj/item/assembly/signaler/core/atmospheric/tier3 + weaker_anomaly_type = /obj/effect/anomaly/atmospheric/tier2 + tier = 3 + impulses_types = list( + /datum/anomaly_impulse/random_temp/tier3, + /datum/anomaly_impulse/freese/tier3, + /datum/anomaly_impulse/fire/tier3, + ) + + collapse_range = 7 + collapse_gas_amount = 700 + collapse_slimes_low = 0 + collapse_slimes_high = 3 + +/obj/effect/anomaly/atmospheric/tier3/New() + . = ..() + + for(var/mob/living/M in GLOB.player_list) + if(M.stat) + continue + + if(get_dist(src, M) > 20 || z != M.z) + return + + M.playsound_local(null, 'sound/effects/comfyfire.ogg', 15, TRUE) + to_chat(M, "Вас накрывает волна жара! Воздух вокруг дрожит.") // It used in one place. + +/obj/effect/anomaly/atmospheric/tier3/collapse() + for(var/obj/item/paper in range(30)) // Just for fan. + paper.fire_act(null, 1000, 1000) + + . = ..() + + +// TIER 4 ANOMALY | ADMIN SPAWN ONLY! + +/obj/effect/anomaly/atmospheric/tier4 + name = "колосальная атмосферная аномалия" + ru_names = list( + NOMINATIVE = "колосальная атмосферная аномалия", \ + GENITIVE = "колоссальной атмосферной аномалии", \ + DATIVE = "колоссальной атмосферной аномалии", \ + ACCUSATIVE = "колосальную ​​атмосферную аномалию", \ + INSTRUMENTAL = "колоссальной ​атмосферной аномалией", \ + PREPOSITIONAL = "колоссальной ​атмосферной аномалии" + ) + core_type = /obj/item/assembly/signaler/core/atmospheric/tier3/tier4 + weaker_anomaly_type = /obj/effect/anomaly/atmospheric/tier4 + tier = 4 + impulses_types = list( + /datum/anomaly_impulse/random_temp/tier4, + /datum/anomaly_impulse/freese/tier4, + /datum/anomaly_impulse/fire/tier4, + /datum/anomaly_impulse/dist_fire, + /datum/anomaly_impulse/atmosfastmove, + ) + + collapse_range = 15 + collapse_gas_amount = 5000 + collapse_slimes_low = 3 + collapse_slimes_high = 6 + +/obj/effect/anomaly/atmospheric/tier4/do_move(dir) + . = ..() + for(var/turf/simulated/wall/wall in range(3, src)) + wall.take_damage(700) + + for(var/mob/living/M in range(3, src)) + to_chat(M, span_danger("Вы были испепелены [declent_ru(INSTRUMENTAL)]!")) + M.dust() + + for(var/obj/O in range(3, src)) + if(!isanomaly(O)) + qdel(O) + +/obj/effect/anomaly/atmospheric/tier4/New() + . = ..() + + for(var/mob/living/M in GLOB.player_list) + if(M.stat) + continue + + M.playsound_local(null, 'sound/effects/comfyfire.ogg', 15, TRUE) + to_chat(M, "Нечто колоссальной мощи явилось в наш мир.") diff --git a/code/modules/anomalies/anomalies/bluespace.dm b/code/modules/anomalies/anomalies/bluespace.dm new file mode 100644 index 00000000000..269b44a6d81 --- /dev/null +++ b/code/modules/anomalies/anomalies/bluespace.dm @@ -0,0 +1,262 @@ +/obj/effect/anomaly/bluespace + anomaly_type = ANOMALY_TYPE_BLUESPACE + icon = 'icons/effects/anomalies.dmi' + icon_state = "bluespace1" + + /// Minimum bump teleportation radius. + var/bump_tp_min = 0 + /// Maximum bump teleportation radius. + var/bump_tp_max = 0 + + /// The radius at which items are randomly teleported when anomaly collapses. + var/collapse_radius = 0 + /// The radius on which items are randomly teleported when anomaly collapses. + var/collapse_tp_radius = 0 + +/obj/effect/anomaly/bluespace/proc/teleport(atom/movable/target, radius) + if(target.anchored && target != src || isobserver(target)) + return + + var/turf/start = get_turf(src) + var/try_x = start.x + rand(-radius, radius) + var/try_y = start.y + rand(-radius, radius) + try_x = clamp(try_x, 1, world.maxx) + try_y = clamp(try_y, 1, world.maxy) + var/turf/tp_pos = get_turf(locate(try_x, try_y, start.z)) + do_teleport(target, tp_pos, asoundin = 'sound/effects/phasein.ogg') + if(isliving(target)) + investigate_log("teleported [key_name_log(target)] to [COORD(target)]", INVESTIGATE_TELEPORTATION) + +/obj/effect/anomaly/bluespace/mob_touch_effect(mob/living/M) + ..() + var/radius = bump_tp_min + round((bump_tp_max - bump_tp_min) * get_strenght() / 100) + teleport(M, radius) + return FALSE + +/obj/effect/anomaly/bluespace/item_touch_effect(obj/item/I) + ..() + var/radius = bump_tp_min + round((bump_tp_max - bump_tp_min) * get_strenght() / 100) + teleport(I, radius) + return FALSE + +/obj/effect/anomaly/bluespace/attackby(obj/item/I, mob/living/user, params) + . = ..() + var/radius = bump_tp_min + round((bump_tp_max - bump_tp_min) * get_strenght() / 100) + teleport(user, radius) + +/obj/effect/anomaly/bluespace/collapse() + for(var/atom/movable/atom in range(collapse_radius)) + teleport(atom, collapse_tp_radius) + + . = ..() + +/obj/effect/anomaly/bluespace/tier1 + name = "малая блюспейс аномалия" + ru_names = list( + NOMINATIVE = "малая ​​блюспейс аномалия", \ + GENITIVE = "малой ​​блюспейс аномалии", \ + DATIVE = "малой ​​блюспейс аномалии", \ + ACCUSATIVE = "малую ​​блюспейс аномалию", \ + INSTRUMENTAL = "малой ​​блюспейс аномалией", \ + PREPOSITIONAL = "малой ​​блюспейс аномалии" + ) + icon_state = "bluespace1" + core_type = /obj/item/assembly/signaler/core/bluespace/tier1 + stronger_anomaly_type = /obj/effect/anomaly/bluespace/tier2 + tier = 1 + impulses_types = list( + /datum/anomaly_impulse/move/bs_selftp/tier1, + ) + + bump_tp_min = 1 + bump_tp_max = 2 + collapse_radius = 3 + collapse_tp_radius = 5 + +// Moves only by /datum/anomaly_impulse/move/bs_selftp +/obj/effect/anomaly/bluespace/tier1/normal_move() + return FALSE + +/obj/effect/anomaly/bluespace/tier2 + name = "блюспейс аномалия" + ru_names = list( + NOMINATIVE = "​​блюспейс аномалия", \ + GENITIVE = "​​блюспейс аномалии", \ + DATIVE = "​​блюспейс аномалии", \ + ACCUSATIVE = "​​блюспейс аномалию", \ + INSTRUMENTAL = "​​блюспейс аномалией", \ + PREPOSITIONAL = "​​блюспейс аномалии" + ) + icon_state = "bluespace2" + core_type = /obj/item/assembly/signaler/core/bluespace/tier2 + weaker_anomaly_type = /obj/effect/anomaly/bluespace/tier1 + stronger_anomaly_type = /obj/effect/anomaly/bluespace/tier3 + tier = 2 + impulses_types = list( + /datum/anomaly_impulse/move/bs_selftp/tier2, + /datum/anomaly_impulse/bs_tp_other/tier2, + /datum/anomaly_impulse/wormholes/tier2, + ) + + bump_tp_min = 2 + bump_tp_max = 7 + collapse_radius = 5 + collapse_tp_radius = 50 + +/obj/effect/anomaly/bluespace/tier3 + name = "большая блюспейс аномалия" + ru_names = list( + NOMINATIVE = "большая ​​блюспейс аномалия", \ + GENITIVE = "большой ​​блюспейс аномалии", \ + DATIVE = "большой ​​блюспейс аномалии", \ + ACCUSATIVE = "большую ​​блюспейс аномалию", \ + INSTRUMENTAL = "большой ​​блюспейс аномалией", \ + PREPOSITIONAL = "большой ​​блюспейс аномалии" + ) + icon_state = "bluespace3" + core_type = /obj/item/assembly/signaler/core/bluespace/tier3 + weaker_anomaly_type = /obj/effect/anomaly/bluespace/tier2 + tier = 3 + impulses_types = list( + /datum/anomaly_impulse/move/bs_selftp/tier3, + /datum/anomaly_impulse/bs_tp_other/tier3, + /datum/anomaly_impulse/wormholes/tier3, + ) + + bump_tp_min = 4 + bump_tp_max = 10 + collapse_radius = 7 + collapse_tp_radius = 50 + +/obj/effect/anomaly/bluespace/tier3/New() + . = ..() + for(var/mob/living/M in GLOB.player_list) + if(M.stat) + continue + + if(get_dist(src, M) > 20 || z != M.z) + return + + M.playsound_local(null,'sound/effects/explosionfar.ogg', 15, TRUE) + to_chat(M, "Вы слышите страшный треск! Это что... трещит пространство?") // It used in one place. + +/obj/effect/anomaly/bluespace/tier3/collapse() + new /datum/event/wormholes/anomaly() + for(var/i = 1 to rand(0, 5)) + new /obj/effect/anomaly/bluespace/tier1(get_turf(locate(rand(1, world.maxx), rand(1, world.maxy), z))) + + . = ..() + + +// TIER 4 ANOMALY | ADMIN SPAWN ONLY! + +/obj/effect/anomaly/bluespace/tier4 + name = "колоссальная блюспейс аномалия" + ru_names = list( + NOMINATIVE = "колоссальная ​​блюспейс аномалия", \ + GENITIVE = "колоссальной ​​блюспейс аномалии", \ + DATIVE = "колоссальной ​​блюспейс аномалии", \ + ACCUSATIVE = "колоссальную ​​блюспейс аномалию", \ + INSTRUMENTAL = "колоссальной ​​блюспейс аномалией", \ + PREPOSITIONAL = "колоссальной ​​блюспейс аномалии" + ) + icon_state = "bluespace3" + core_type = /obj/item/assembly/signaler/core/bluespace/tier3 + weaker_anomaly_type = /obj/effect/anomaly/bluespace/tier3 + tier = 4 + impulses_types = list( + /datum/anomaly_impulse/move/bs_selftp/tier4, + /datum/anomaly_impulse/bs_tp_other_t4, + /datum/anomaly_impulse/wormholes/tier4, + ) + + bump_tp_min = 30 + bump_tp_max = 70 + collapse_radius = 7 + collapse_tp_radius = 50 + +/obj/effect/anomaly/bluespace/tier4/New() + . = ..() + for(var/mob/living/M in GLOB.player_list) + if(M.stat) + continue + + M.playsound_local(null,'sound/effects/explosionfar.ogg', 15, TRUE) + to_chat(M, "Пространство пало...") + +/obj/effect/anomaly/bluespace/tier4/collapse() + new /datum/event/wormholes/anomaly() + for(var/i = 1 to rand(3, 7)) + new /obj/effect/anomaly/bluespace/tier2(get_turf(locate(rand(1, world.maxx), rand(1, world.maxy), z))) + + var/list/turf/turfs = list() + for(var/turf/simulated/T in range(10, src)) + turfs.Add(T) + + // swaps + for(var/i = 1; i <= rand(40, 50); ++i) + var/turf/T1 = pick(turfs) + var/turf/T2 = pick(turfs) + + var/dir1 = T1.dir + var/icon_state1 = T1.icon_state + var/icon1 = T1.icon + T2.dir = dir1 + T2.icon = icon1 + T2.icon_state = icon_state1 + + var/list/C1 = list() + for(var/atom/movable/A in T1) + C1.Add(A) + + var/list/C2 = list() + for(var/atom/movable/A in T2) + C2.Add(A) + + for(var/atom/movable/A in C1) + A.forceMove(T2) + + for(var/atom/movable/A in C2) + A.forceMove(T2) + + C1 = list() + C2 = list() + for(var/V in T1.vars) + if(!(V in list("type", "loc", "locs", "vars", "parent", "parent_type", "verbs", "ckey", "key", "x", "y", "z", "destination_z", "destination_x", "destination_y", "contents", "luminosity", "group"))) + C1[V] = T1.vars[V] + + for(var/V in T2.vars) + if(!(V in list("type", "loc", "locs", "vars", "parent", "parent_type", "verbs", "ckey", "key", "x", "y", "z", "destination_z", "destination_x", "destination_y", "contents", "luminosity", "group"))) + C2[V] = T2.vars[V] + + var/type1 = T1.type + var/type2 = T2.type + T2.ChangeTurf(type1) + T1.ChangeTurf(type2) + + . = ..() + +/obj/effect/anomaly/bluespace/tier4/teleport(atom/movable/target, radius) + if(target.anchored && target != src || isobserver(target)) + return + + var/turf/start = get_turf(src) + var/try_x = start.x + rand(-radius, radius) + var/try_y = start.y + rand(-radius, radius) + try_x = clamp(try_x, 1, world.maxx) + try_y = clamp(try_y, 1, world.maxy) + var/z = start.z + if(prob(10) && !isanomaly(target)) + var/list/possible_z = list() + for(var/i in 1 to world.maxz) + if(is_admin_level(i) || is_away_level(i) || is_taipan(i) || !is_teleport_allowed(i)) + continue + + possible_z.Add(i) + + z = pick(possible_z) + + var/turf/tp_pos = get_turf(locate(try_x, try_y, z)) + do_teleport(target, tp_pos, asoundin = 'sound/effects/phasein.ogg') + if(isliving(target)) + investigate_log("teleported [key_name_log(target)] to [COORD(target)]", INVESTIGATE_TELEPORTATION) diff --git a/code/modules/anomalies/anomalies/energetic.dm b/code/modules/anomalies/anomalies/energetic.dm new file mode 100644 index 00000000000..1324bcd5100 --- /dev/null +++ b/code/modules/anomalies/anomalies/energetic.dm @@ -0,0 +1,341 @@ +/obj/effect/anomaly/energetic + anomaly_type = ANOMALY_TYPE_FLUX + icon_state = "electricity2" + icon = 'icons/effects/anomalies.dmi' + icon_state = "energetic1" + + /// The voltage that this anomaly supplies to nearby powernets. + var/voltage = 0 + /// Minimum number of jumps when collapsed. + var/collapse_jumps_low = 0 + /// Maximum number of jumps when collapsed. + var/collapse_jumps_high = 0 + /// Range of do_shock_ex when collapse. + var/collapse_shock_range = 0 + /// Damage of do_shock_ex when collapses. + var/collapse_shock_damage = 0 + /// Minimum number of generated energy balls. + var/eballs_num_low = 0 + /// Maximum number of generated energy balls. + var/eballs_num_high = 0 + /// List of energy balls connected to rhis anomaly. + var/list/obj/effect/energy_ball/eballs = list() + /// List of types of energy balls that can appear. + var/list/eballs_types = list() + /// Desired distance from the eball. + var/eball_dist = 2 + +/obj/effect/anomaly/energetic/New() + . = ..() + for(var/i = 1 to rand(eballs_num_low, eballs_num_high)) + var/type = pick_weight_classic(eballs_types) + eballs.Add(new type(loc, src)) + +/obj/effect/anomaly/energetic/collapse() + for(var/i = 1 to rand(collapse_jumps_low, collapse_jumps_high)) + jump_to_machinery(collapse_shock_damage * 2) + do_shock_ex(collapse_shock_range, collapse_shock_damage, TRUE) + sleep(0.2 SECONDS) + + if(tier < 3) + QDEL_LIST(eballs) + return ..() + + for(var/obj/effect/energy_ball/eball in eballs) + if(prob(50)) + var/spawn_type = eball.spawn_type + new spawn_type(eball.loc) + + QDEL_LIST(eballs) + return ..() + +/obj/effect/anomaly/energetic/process() + . = ..() + var/list/powernets = list() + for(var/turf/T in view(3, src)) + var/obj/structure/cable/C = null + if(isturf(T)) + C = locate() in T + + if(!C?.powernet) + continue + + if(!(C.powernet in powernets)) + powernets.Add(C.powernet) + + var/cur_voltage = voltage * strenght / 100 + for(var/datum/powernet/P in powernets) + P.newavail += cur_voltage / powernets.len + +/obj/effect/anomaly/energetic/mob_touch_effect(mob/living/M) + . = ..() + M.electrocute_act(collapse_shock_damage, "энергетической аномалии", flags = SHOCK_NOGLOVES) + +/obj/effect/anomaly/energetic/item_touch_effect(obj/item/I) + . = ..() + do_shock_ex(collapse_shock_range / 2, collapse_shock_damage / 2, TRUE) + +/obj/effect/anomaly/energetic/proc/jump_to_machinery(damage) + var/list/possible_targets = list() + for(var/obj/machinery/mach in view(5, src)) + if(!(mach.stat & BROKEN)) + possible_targets += mach + + var/obj/target = pick(possible_targets) + target.take_damage(damage, BURN, ENERGY, TRUE, get_dir(src, target)) + jump(target) + after_move() + +/obj/effect/anomaly/energetic/do_move(dir) + var/turf/target = get_step(src, dir) + if(target && target.Enter(src)) + jump(target) + + return TRUE + +// A jump accompanied by an electric shock. +/obj/effect/anomaly/energetic/proc/jump(target) + var/turf/target_turf = get_turf(target) + if(!target_turf) + return + + Beam(target_turf, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 0.5 SECONDS) + forceMove(target_turf) + +/obj/effect/anomaly/energetic/tier1 + name = "малая энергетическая аномалия" + ru_names = list( + NOMINATIVE = "малая энергетическая аномалия", \ + GENITIVE = "малой энергетической аномалии", \ + DATIVE = "малой энергетической аномалии", \ + ACCUSATIVE = "малую энергетическую аномалию", \ + INSTRUMENTAL = "малой энергетической аномалией", \ + PREPOSITIONAL = "малой энергетической аномалии" + ) + icon_state = "energetic1" + core_type = /obj/item/assembly/signaler/core/energetic/tier1 + stronger_anomaly_type = /obj/effect/anomaly/energetic/tier2 + tier = 1 + light_range = 5 + impulses_types = list( + /datum/anomaly_impulse/move/energ_fastmove/tier1, + /datum/anomaly_impulse/energ_shock_ex/tier1, + /datum/anomaly_impulse/move/machinery_jump/tier1, + ) + + voltage = 75000 + collapse_jumps_low = 3 + collapse_jumps_high = 7 + collapse_shock_range = 3 + collapse_shock_damage = 10 + +/obj/effect/anomaly/energetic/tier2 + name = "энергетическая аномалия" + ru_names = list( + NOMINATIVE = "энергетическая аномалия", \ + GENITIVE = "энергетической аномалии", \ + DATIVE = "энергетической аномалии", \ + ACCUSATIVE = "энергетическую аномалию", \ + INSTRUMENTAL = "энергетической аномалией", \ + PREPOSITIONAL = "энергетической аномалии" + ) + icon_state = "energetic2" + core_type = /obj/item/assembly/signaler/core/energetic/tier2 + weaker_anomaly_type = /obj/effect/anomaly/energetic/tier1 + stronger_anomaly_type = /obj/effect/anomaly/energetic/tier3 + tier = 2 + light_range = 6 + impulses_types = list( + /datum/anomaly_impulse/move/energ_fastmove/tier2, + /datum/anomaly_impulse/energ_shock_ex/tier2, + /datum/anomaly_impulse/move/machinery_jump/tier2, + ) + + voltage = 250000 + collapse_jumps_low = 5 + collapse_jumps_high = 10 + collapse_shock_range = 3 + collapse_shock_damage = 30 + eballs_num_low = 2 + eballs_num_high = 3 + eballs_types = list(/obj/effect/energy_ball = 1) + +/obj/effect/anomaly/energetic/tier3 + name = "большая энергетическая аномалия" + ru_names = list( + NOMINATIVE = "большая энергетическая аномалия", \ + GENITIVE = "большой энергетической аномалии", \ + DATIVE = "большой энергетической аномалии", \ + ACCUSATIVE = "большую энергетическую аномалию", \ + INSTRUMENTAL = "большой энергетической аномалией", \ + PREPOSITIONAL = "большой энергетической аномалии" + ) + icon_state = "energetic3" + core_type = /obj/item/assembly/signaler/core/energetic/tier3 + weaker_anomaly_type = /obj/effect/anomaly/energetic/tier2 + tier = 3 + light_range = 7 + impulses_types = list( + /datum/anomaly_impulse/move/energ_fastmove/tier3, + /datum/anomaly_impulse/energ_shock_ex/tier3, + /datum/anomaly_impulse/move/machinery_jump/tier3, + ) + + voltage = 1000000 // A stabilized flux anomaly can be a useful source of energy. + collapse_jumps_low = 10 + collapse_jumps_high = 15 + collapse_shock_range = 4 + collapse_shock_damage = 70 + eballs_num_low = 3 + eballs_num_high = 5 + eballs_types = list(/obj/effect/energy_ball = 3, /obj/effect/energy_ball/big = 1) + +/obj/effect/anomaly/energetic/tier3/New() + . = ..() + for(var/mob/living/M in GLOB.player_list) + if(M.stat) + continue + + if(get_dist(src, M) > 20 || z != M.z) + return + + M.playsound_local(null, 'sound/magic/lightningbolt.ogg', 15, TRUE) + to_chat(M, "Вы слышите тихое потрескивание в воздухе. Подозрительно похоже на статическое электричество.") // It used in one place. + + +/obj/effect/energy_ball + name = "энергетический шар" + ru_names = list( + NOMINATIVE = "энергетический шар", \ + GENITIVE = "энергетического шара", \ + DATIVE = "энергетическому шару", \ + ACCUSATIVE = "энергетический шар", \ + INSTRUMENTAL = "энергетическим шаром", \ + PREPOSITIONAL = "энергетическом шаре" + ) + desc = "Миниатюрная, отностилельно стабильная шаровая молния. Обычно появляется вместе с энергетическими аномалиями." + icon = 'icons/effects/anomalies.dmi' + icon_state = "energetic1" + gender = MALE + alpha = 0 + light = 5 + /// Anomaly that src conected with. + var/obj/effect/anomaly/energetic/owner + /// The proportion of the size relative to the default size. + var/size = 0.5 + /// Type of anomaly that spawns instead of this eball when owner colapses. + var/spawn_type = /obj/effect/anomaly/energetic/tier1 + +/obj/effect/energy_ball/New(loc, owner) + . = ..() + src.owner = owner + + var/matrix/M = matrix() + M.Scale(0.1, 0.1) + animate(src, transform = M, time = 0, flags = ANIMATION_PARALLEL) + M.Scale(10 * size, 10 * size) + animate(src, transform = M, time = 1 SECONDS, alpha = 255, flags = ANIMATION_PARALLEL) + + START_PROCESSING(SSobj, src) + +/obj/effect/energy_ball/Destroy() + . = ..() + STOP_PROCESSING(SSobj, src) + +/obj/effect/energy_ball/process() + if(!owner) + qdel(src) + return + + if(get_dist(src, owner) <= 2) + jump(get_step(src, owner.get_move_dir())) + return + + if(z != owner.z || get_dist(src, owner) > 10) + jump(get_turf(owner)) + return + + while(get_dist(src, owner) > owner.eball_dist) + jump(get_step(src, get_dir(src, owner))) + sleep(2) + +/obj/effect/energy_ball/proc/jump(target) + var/turf/target_turf = get_turf(target) + if(!target_turf) + return + + Beam(target_turf, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 0.5 SECONDS) + forceMove(target_turf) + if(!prob(20)) + return + + var/list/obj/connected = list(owner) + owner.eballs + Beam(pick(connected), icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 0.5 SECONDS) + +/obj/effect/energy_ball/ex_act(severity) + return + +/obj/effect/energy_ball/CanAllowThrough(atom/movable/mover, border_dir) + . = ..() + if(isliving(mover)) + var/mob/living/M = mover + M.electrocute_act(rand(20, 30), "энергетического шара", flags = SHOCK_NOGLOVES) + +/obj/effect/energy_ball/big + size = 1 + +/obj/effect/energy_ball/verybig + size = 1.5 + spawn_type = /obj/effect/anomaly/energetic/tier2 + + +// TIER 4 ADMIN SPAWN ONLY + +/obj/effect/anomaly/energetic/tier4 + name = "колоссальная энергетическая аномалия" + ru_names = list( + NOMINATIVE = "колоссальная энергетическая аномалия", \ + GENITIVE = "колоссальной энергетической аномалии", \ + DATIVE = "колоссальной энергетической аномалии", \ + ACCUSATIVE = "колоссальную энергетическую аномалию", \ + INSTRUMENTAL = "колоссальной энергетической аномалией", \ + PREPOSITIONAL = "колоссальной энергетической аномалии" + ) + icon_state = "energetic3" + core_type = /obj/item/assembly/signaler/core/energetic/tier3/tier4 + weaker_anomaly_type = /obj/effect/anomaly/energetic/tier3 + tier = 4 + light_range = 15 + impulses_types = list( + /datum/anomaly_impulse/move/energ_fastmove/tier4, + /datum/anomaly_impulse/energ_shock_ex/tier4, + /datum/anomaly_impulse/move/machinery_jump/tier4, + /datum/anomaly_impulse/move/machinery_destroy, + ) + + voltage = 5000000 // A stabilized flux anomaly can be a useful source of energy. + collapse_jumps_low = 20 + collapse_jumps_high = 35 + collapse_shock_range = 6 + collapse_shock_damage = 120 + eballs_num_low = 10 + eballs_num_high = 12 + eballs_types = list(/obj/effect/energy_ball = 3, /obj/effect/energy_ball/big = 2, /obj/effect/energy_ball/verybig = 1) + eball_dist = 5 + +/obj/effect/anomaly/energetic/tier4/New() + . = ..() + for(var/mob/living/M in GLOB.player_list) + M.electrocute_act(rand(5, 15), "[declent_ru(GENITIVE)]") + if(M.stat) + continue + + if(is_admin_level(M)) + continue + + M.playsound_local(null, 'sound/magic/lightningbolt.ogg', 25, TRUE) + to_chat(M, "Вы слышите черезвычайно громкий электрический треск!") + +/obj/effect/anomaly/energetic/tier4/do_move(dir) + . = ..() + explosion(get_turf(src), -1, 1, 2, cause = "tier4 energetic anomaly move", adminlog = FALSE) diff --git a/code/modules/anomalies/anomalies/gravitational.dm b/code/modules/anomalies/anomalies/gravitational.dm new file mode 100644 index 00000000000..a6a70bdd51e --- /dev/null +++ b/code/modules/anomalies/anomalies/gravitational.dm @@ -0,0 +1,196 @@ +/obj/effect/anomaly/gravitational + anomaly_type = ANOMALY_TYPE_GRAV + icon_state = "shield2" + /// Maximum level of changing gravity on touch. + var/grav_change_level = 0 + /// Minimum time of changing gravity on touch. + var/grav_change_time_low = 0 + /// Maximum time of changing gravity on touch. + var/grav_change_time_high = 0 + +/obj/effect/anomaly/gravitational/collapse() + for(var/i = 1 to max(2, rand(tier, tier * 2))) + sleep(2) + for(var/atom/movable/A in view(tier * 2, src)) + if(isobserver(A)) + continue + + if(!iseffect(A)) + A.random_throw(tier, tier * 3, 5) + A.update_icon() + + . = ..() + +/obj/effect/anomaly/gravitational/proc/random_gravity_change(atom/A) + var/grav_delta = rand(-grav_change_level * 100, grav_change_level * 100) / 100 + var/id = GRAVITY_SOURCE_ANOMALY + "[rand(1, 1000000)]" + + A.add_gravity(id, grav_delta) + addtimer(CALLBACK(A, TYPE_PROC_REF(/atom, remove_gravity_source), id), rand(grav_change_time_low, grav_change_time_high)) + +/obj/effect/anomaly/gravitational/mob_touch_effect(mob/living/M) + . = ..() + random_gravity_change(M) + +/obj/effect/anomaly/gravitational/item_touch_effect(obj/item/I) + . = ..() + var/grav_delta = -I.get_gravity() + var/id = GRAVITY_SOURCE_ANOMALY + "[rand(1, 1000000)]" + I.add_gravity(id, grav_delta) + addtimer(CALLBACK(I, TYPE_PROC_REF(/atom, remove_gravity_source), id), rand(grav_change_time_low, grav_change_time_high)) + +/obj/effect/anomaly/gravitational/process() + . = ..() + for(var/obj/O in oview(max(2, tier * 2 - 1), src)) + if(!O.anchored) + step_towards(O,src) + +/obj/effect/anomaly/gravitational/tier1 + name = "малая гравитационная аномалия" + ru_names = list( + NOMINATIVE = "малая гравитационная аномалия", \ + GENITIVE = "малой гравитационной аномалии", \ + DATIVE = "малой гравитационной аномалии", \ + ACCUSATIVE = "малую гравитационную аномалию", \ + INSTRUMENTAL = "малой гравитационной аномалией", \ + PREPOSITIONAL = "малой гравитационной аномалии" + ) + core_type = /obj/item/assembly/signaler/core/gravitational/tier1 + stronger_anomaly_type = /obj/effect/anomaly/gravitational/tier2 + tier = 1 + impulses_types = list( + /datum/anomaly_impulse/change_grav/tier1, + /datum/anomaly_impulse/random_throws/tier1, + ) + + grav_change_level = 1 + grav_change_time_low = 3 SECONDS + grav_change_time_high = 5 SECONDS + +/obj/effect/anomaly/gravitational/tier2 + name = "гравитационная аномалия" + ru_names = list( + NOMINATIVE = "гравитационная аномалия", \ + GENITIVE = "гравитационной аномалии", \ + DATIVE = "гравитационной аномалии", \ + ACCUSATIVE = "гравитационную аномалию", \ + INSTRUMENTAL = "гравитационной аномалией", \ + PREPOSITIONAL = "гравитационной аномалии" + ) + core_type = /obj/item/assembly/signaler/core/gravitational/tier2 + weaker_anomaly_type = /obj/effect/anomaly/gravitational/tier1 + stronger_anomaly_type = /obj/effect/anomaly/gravitational/tier3 + tier = 2 + impulses_types = list( + /datum/anomaly_impulse/change_grav/tier2, + /datum/anomaly_impulse/random_throws/tier2, + ) + + grav_change_level = 2 + grav_change_time_low = 20 SECONDS + grav_change_time_high = 60 SECONDS + +/obj/effect/anomaly/gravitational/tier3 + name = "большая гравитационная аномалия" + ru_names = list( + NOMINATIVE = "большая гравитационная аномалия", \ + GENITIVE = "большой гравитационной аномалии", \ + DATIVE = "большой гравитационной аномалии", \ + ACCUSATIVE = "большую гравитационную аномалию", \ + INSTRUMENTAL = "большой гравитационной аномалией", \ + PREPOSITIONAL = "большой гравитационной аномалии" + ) + core_type = /obj/item/assembly/signaler/core/gravitational/tier3 + weaker_anomaly_type = /obj/effect/anomaly/gravitational/tier2 + tier = 3 + impulses_types = list( + /datum/anomaly_impulse/change_grav/tier3, + /datum/anomaly_impulse/random_throws/tier3, + ) + + grav_change_level = 3 + grav_change_time_low = 5 SECONDS + grav_change_time_high = 20 SECONDS + +/obj/effect/anomaly/gravitational/tier3/New() + . = ..() + + for(var/mob/living/M in GLOB.player_list) + if(M.stat) + continue + + if(get_dist(src, M) > 20 || z != M.z) + return + + M.playsound_local(null, 'sound/effects/empulse.ogg', 15, TRUE) + to_chat(M, "Ваше тело становится необычайно лёгким... Или тяжёлым... Всё вокруг неестественно подрагивает.") // It used in one place. + +/obj/effect/anomaly/gravitational/tier3/collapse() + for(var/i = 1 to rand(30, 60)) + var/mob/living/M = pick(GLOB.mob_living_list) + random_gravity_change(M) + + . = ..() + + +// TIER 4 ADMIN SPAWN ONLY + +/obj/effect/anomaly/gravitational/tier4 + name = "колоссальная гравитационная аномалия" + ru_names = list( + NOMINATIVE = "колоссальная гравитационная аномалия", \ + GENITIVE = "колоссальной гравитационной аномалии", \ + DATIVE = "колоссальной гравитационной аномалии", \ + ACCUSATIVE = "колоссальную гравитационную аномалию", \ + INSTRUMENTAL = "колоссальной гравитационной аномалией", \ + PREPOSITIONAL = "колоссальной гравитационной аномалии" + ) + core_type = /obj/item/assembly/signaler/core/gravitational/tier3/tier4 + weaker_anomaly_type = /obj/effect/anomaly/gravitational/tier3 + tier = 4 + impulses_types = list( + /datum/anomaly_impulse/change_grav/tier4, + /datum/anomaly_impulse/random_throws/tier4, + /datum/anomaly_impulse/grav_fastmove, + ) + + grav_change_level = 10 + grav_change_time_low = 60 SECONDS + grav_change_time_high = 360 SECONDS + +/obj/effect/anomaly/gravitational/tier4/New() + . = ..() + + for(var/mob/living/M in GLOB.player_list) + if(M.stat) + continue + + M.playsound_local(null, 'sound/effects/empulse.ogg', 15, TRUE) + to_chat(M, "Вы чувствуете, что кто-то решил поиграть в Бога...") // It used in one place. + +/obj/effect/anomaly/gravitational/tier4/collapse() + for(var/i = 1 to rand(100, 200)) + var/mob/living/M = pick(GLOB.mob_living_list) + random_gravity_change(M) + + . = ..() + +/obj/effect/anomaly/gravitational/tier4/do_move(dir) + . = ..() + for(var/turf/simulated/wall/wall in range(3, src)) + wall.take_damage(700) + + for(var/obj/structure/struct in range(3, src)) + struct.take_damage(700) + + for(var/obj/item/I in range(3, src)) + I.random_throw(tier, tier * 3, 5) + + for(var/mob/living/M in range(3, src)) + M.random_throw(tier, tier * 3, 5) + +/obj/effect/anomaly/gravitational/process() + . = ..() + for(var/obj/O in oview(max(2, tier * 2 - 1), src)) + step_towards(O, src) + step_towards(O, src) diff --git a/code/modules/anomalies/anomalies/vortex.dm b/code/modules/anomalies/anomalies/vortex.dm new file mode 100644 index 00000000000..b3a359f0391 --- /dev/null +++ b/code/modules/anomalies/anomalies/vortex.dm @@ -0,0 +1,236 @@ +/obj/effect/anomaly/vortex + anomaly_type = ANOMALY_TYPE_VORTEX + icon_state = "bhole3" + /// Minimum radius at which surrounding objects are attracted. + var/grav_pull_range_low = 0 + /// Maximum radius at which surrounding objects are attracted. + var/grav_pull_range_high = 0 + /// The level of singularity that corresponds to the force of attraction. + var/grav_pull_strenght = 0 + /// The radius at which collapse effects are applied. + var/collapse_range = 0 + +/obj/effect/anomaly/vortex/collapse() + var/list/affected = list() + for(var/turf/T in range(collapse_range, src)) + var/key = "[get_dist(src, T)]" + if(!(key in affected)) + affected[key] = list() + + var/list/list = affected[key] + list.Add(T) + + for(var/key in affected) + matr = matrix() + var/mult = text2num(key) + matr.Scale(mult, mult) + animate(src, transform = matr, time = 0.2 SECONDS, flags = ANIMATION_PARALLEL) + var/list/list = affected[key] + for(var/turf/T in list) + if(prob(100 - mult * 10)) + T.singularity_act(grav_pull_strenght) + + sleep(2) + + . = ..() + +/obj/effect/anomaly/vortex/proc/pull(atom/movable/A) + // a - vector A->src + var/ax = x - A.x + var/ay = y - A.y + var/a_len = sqrt(ax * ax + ay * ay) + + // a1 - notmalised (len = 1) vector a + var/a1x = ax * a_len + var/a1y = ay * a_len + + // b - vector perpendicular to vector a1. + var/bx = -a1y + var/by = a1x + + var/radius = round(grav_pull_range_low + (grav_pull_range_high - grav_pull_range_low) * get_strenght() / 100) + + // c - vector of moving. Always move 1 + var/cx = ax * radius + bx * (a_len - 1) + var/cy = ay * radius + by * (a_len - 1) + + var/turf/target = get_turf(locate(A.x + cx, A.y + cy, z)) + A.singularity_pull(target, grav_pull_strenght) + A.update_icon() + +/obj/effect/anomaly/vortex/proc/do_pulls() + var/radius = round(grav_pull_range_low + (grav_pull_range_high - grav_pull_range_low) * get_strenght() / 100) + for(var/atom/movable/A in view(radius, src)) + if(isobserver(A)) + continue + + if(!A.anchored || ismachinery(A)) + pull(A) + +/obj/effect/anomaly/vortex/process() + var/list/obj/was_near = list() + for(var/obj/O in range(1, src)) + was_near.Add(O) + + do_pulls() + + // If something movable was near and can not be pulled inside, it will be throwen. + for(var/obj/O in range(1, src)) + if(!(O in was_near)) + continue + + if(O.anchored && !ismachinery(O)) + continue + + O.random_throw(tier * 2, tier * 3, 4) + + . = ..() + +/obj/effect/anomaly/vortex/mob_touch_effect(mob/living/M) + . = ..() + M.random_throw(tier * 2, tier * 3, 4) + +/obj/effect/anomaly/vortex/item_touch_effect(obj/item/I) + . = ..() + I.random_throw(tier * 2, tier * 3, 4) + +/obj/effect/anomaly/vortex/process() + . = ..() + + for(var/atom/movable/A in loc.contents) + if(!A.anchored) + A.random_throw(tier * 2, tier * 3, 5) + +/obj/effect/anomaly/vortex/tier1 + name = "малая вихревая аномалия" + ru_names = list( + NOMINATIVE = "малая вихревая аномалия", \ + GENITIVE = "малой вихревой аномалии", \ + DATIVE = "малой вихревой аномалии", \ + ACCUSATIVE = "малую вихревую аномалию", \ + INSTRUMENTAL = "малой вихревой аномалией", \ + PREPOSITIONAL = "малой вихревой аномалии" + ) + core_type = /obj/item/assembly/signaler/core/vortex/tier1 + stronger_anomaly_type = /obj/effect/anomaly/vortex/tier2 + tier = 1 + impulses_types = list( + /datum/anomaly_impulse/emp/tier1, + /datum/anomaly_impulse/superpull/tier1, + ) + + grav_pull_range_low = 1 + grav_pull_range_high = 2 + grav_pull_strenght = STAGE_THREE + collapse_range = 0 + +/obj/effect/anomaly/vortex/tier2 + name = "вихревая аномалия" + ru_names = list( + NOMINATIVE = "вихревая аномалия", \ + GENITIVE = "вихревой аномалии", \ + DATIVE = "вихревой аномалии", \ + ACCUSATIVE = "вихревую аномалию", \ + INSTRUMENTAL = "вихревой аномалией", \ + PREPOSITIONAL = "вихревой аномалии" + ) + core_type = /obj/item/assembly/signaler/core/vortex/tier2 + weaker_anomaly_type = /obj/effect/anomaly/vortex/tier1 + stronger_anomaly_type = /obj/effect/anomaly/vortex/tier3 + tier = 2 + impulses_types = list( + /datum/anomaly_impulse/emp/tier2, + /datum/anomaly_impulse/superpull/tier2, + ) + + grav_pull_range_low = 2 + grav_pull_range_high = 3 + grav_pull_strenght = STAGE_FOUR + collapse_range = 1 + +/obj/effect/anomaly/vortex/tier3 + name = "большая вихревая аномалия" + ru_names = list( + NOMINATIVE = "большая вихревая аномалия", \ + GENITIVE = "большой вихревой аномалии", \ + DATIVE = "большой вихревой аномалии", \ + ACCUSATIVE = "большую вихревую аномалию", \ + INSTRUMENTAL = "большой вихревой аномалией", \ + PREPOSITIONAL = "большой вихревой аномалии" + ) + core_type = /obj/item/assembly/signaler/core/vortex/tier3 + weaker_anomaly_type = /obj/effect/anomaly/vortex/tier2 + tier = 3 + impulses_types = list( + /datum/anomaly_impulse/emp/tier3, + /datum/anomaly_impulse/superpull/tier3, + ) + + grav_pull_range_low = 2 + grav_pull_range_high = 4 + grav_pull_strenght = STAGE_FIVE + collapse_range = 3 + +/obj/effect/anomaly/vortex/tier3/New() + . = ..() + + for(var/mob/living/M in GLOB.player_list) + if(M.stat) + continue + + if(get_dist(src, M) > 20 || z != M.z) + return + + to_chat(M, "Сильный ветер дует вам прямо в лицо. Стоп, откуда на космической станции ветер?") // It used in one place. + +// TIER 4 ADMIN SPAWN ONLY + +/obj/effect/anomaly/vortex/tier4 + name = "колоссальная вихревая аномалия" + ru_names = list( + NOMINATIVE = "колоссальная вихревая аномалия", \ + GENITIVE = "колоссальной вихревой аномалии", \ + DATIVE = "колоссальной вихревой аномалии", \ + ACCUSATIVE = "колоссальную вихревую аномалию", \ + INSTRUMENTAL = "колоссальной вихревой аномалией", \ + PREPOSITIONAL = "колоссальной вихревой аномалии" + ) + core_type = /obj/item/assembly/signaler/core/vortex/tier3/tier4 + weaker_anomaly_type = /obj/effect/anomaly/vortex/tier3 + tier = 4 + impulses_types = list( + /datum/anomaly_impulse/emp/tier4, + /datum/anomaly_impulse/superpull/tier4, + /datum/anomaly_impulse/vortex_fastmove, + ) + + grav_pull_range_low = 8 + grav_pull_range_high = 16 + grav_pull_strenght = STAGE_SIX + collapse_range = 15 + +/obj/effect/anomaly/vortex/tier4/New() + . = ..() + + for(var/mob/living/M in GLOB.player_list) + if(M.stat) + continue + + to_chat(M, "Ураганный поток ветра чуть не сбивает вас с ног. Это точно не сулит для вас ничего хорошего.") + +/obj/effect/anomaly/vortex/tier4/item_touch_effect(obj/item/I) + . = ..() + if(!iscore(I)) + I.singularity_act() + +/obj/effect/anomaly/vortex/tier4/mob_touch_effect(mob/living/M) + M.singularity_act() + +/obj/effect/anomaly/vortex/tier4/do_move(dir) + . = ..() + pull() + for(var/atom/A in range(2, src)) + A.singularity_act() + +/obj/effect/anomaly/vortex/singularity_act() + return diff --git a/code/modules/anomalies/anomaly_analyzer.dm b/code/modules/anomalies/anomaly_analyzer.dm new file mode 100644 index 00000000000..8225923bcda --- /dev/null +++ b/code/modules/anomalies/anomaly_analyzer.dm @@ -0,0 +1,66 @@ +/obj/item/anomaly_analyzer + name = "сканер аномалий" + ru_names = list( + NOMINATIVE = "сканер аномалий", \ + GENITIVE = "сканера аномалий", \ + DATIVE = "сканеру аномалий", \ + ACCUSATIVE = "сканер аномалий", \ + INSTRUMENTAL = "сканером аномалий", \ + PREPOSITIONAL = "сканере аномалий" + ) + desc = "Продвинутое устройство предназначенное для сканирования аномалий. \ + Выводит достаточно полную информацию о сканируемой аномалии. \ + Может сканировать аномалии на расстоянии." + icon = 'icons/obj/anomaly/anomaly_stuff.dmi' + lefthand_file = 'icons/obj/anomaly/anomaly_inhand_l.dmi' + righthand_file = 'icons/obj/anomaly/anomaly_inhand_r.dmi' + icon_state = "scanner_item" + item_state = "scanner" + gender = MALE + origin_tech = "programming=3;magnets=1" + /// Title of scan window. + var/scan_title + /// Anomaly info in scan window. + var/scan_data + +/obj/item/anomaly_analyzer/proc/scan(obj/effect/anomaly/target) + scan_title = "Сканирование [target.declent_ru(GENITIVE)]" + scan_data = list() + var/stre = target.strenght + var/stab = target.stability + scan_data += "Сила аномалии: [stre > 70 ? span_warning("[stre]") : stre]" + scan_data += "Стабильность аномалии: [stab < 30 ? span_warning("[stab]") : stab]" + var/state + if(target.stability < ANOMALY_GROW_STABILITY) + state = span_warning("Рост") + else if(target.stability > ANOMALY_DECREASE_STABILITY) + state = "Уменьшение" + else + state = "Стабильное" + + scan_data += "Состояние аномалии: [state]" + if(target.stability > ANOMALY_MOVE_MAX_STABILITY || world.time > target.move_moment) + scan_data += span_info("Движение прекращено.") + + scan_data += "
Импульсы:\n" + for(var/datum/anomaly_impulse/impulse in target.impulses) + var/blocked = world.time < target.move_impulse_moment && istype(impulse, /datum/anomaly_impulse/move) || target.stability > impulse.stability_high + scan_data += " [impulse.name]" + (blocked ? " ([span_green("заблокирован")]" : "") + scan_data += "  Описание: [impulse.desc]" + scan_data += "  Время между импульсами: [impulse.scale_by_strenght(impulse.period_low, impulse.period_high) / 10]" + scan_data += "  Блокируящая стабильность: [impulse.stability_high]" + +/obj/item/anomaly_analyzer/proc/show(mob/user) + var/datum/browser/popup = new(user, "anomalyscanner", scan_title, 500, 600) + popup.set_content(span_highlight("[jointext(scan_data, "
")]")) + popup.open(no_focus = 1) + +/obj/item/anomaly_analyzer/attack_self(mob/user) + show(user) + +/obj/item/anomaly_analyzer/afterattack(atom/target, mob/user, proximity, params, status) + if(target == user || !isanomaly(target) || !iscarbon(user) || user.incapacitated() || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) + return + + scan(target) + show(user) diff --git a/code/modules/anomalies/anomaly_generator.dm b/code/modules/anomalies/anomaly_generator.dm new file mode 100644 index 00000000000..34f703e41be --- /dev/null +++ b/code/modules/anomalies/anomaly_generator.dm @@ -0,0 +1,368 @@ +/obj/machinery/power/anomaly_generator + name = "Генератор аномалий" + ru_names = list( + NOMINATIVE = "Генератор аномалий", \ + GENITIVE = "Генератора аномалий", \ + DATIVE = "Генератору аномалий", \ + ACCUSATIVE = "Генератор аномалий", \ + INSTRUMENTAL = "Генератором аномалий", \ + PREPOSITIONAL = "Генераторе аномалий" + ) + desc = "Необычного вида машина, разработанная на основе эксперементальной технологии, предназначенная для \ + генерации аномалий." + gender = MALE + density = TRUE + anchored = TRUE + icon = 'icons/obj/anomaly/anomaly_stuff.dmi' + icon_state = "generator_on" + use_power = IDLE_POWER_USE + idle_power_usage = 300 + active_power_usage = 300 + max_integrity = 200 + integrity_failure = 100 + resistance_flags = FIRE_PROOF | ACID_PROOF + processing_flags = START_PROCESSING_MANUALLY + + /// Usage of energy from powernet. + var/powernet_usage = 0 + /// Selected anomaly tier. + var/selected_tier = TIER1 + /// Selected anomaly type. + var/selected_type = ANOMALY_TYPE_RANDOM + + /// Current generator charge. + var/charge = 0 + /// Last charge per second. + var/last_charge = 0 + /// List of items placed inside. + var/list/obj/item/assembly/signaler/core/containment = list() + + /// The maximum number of items that can be in the anomaly generator. + var/containment_limit = 2 + /// The radius at which anomalies will be generated. + var/creating_range = 15 + /// The speed with which energy will be collected. + var/speed = 1e4 + + /// A beacon located inside the anomaly generator. + var/obj/item/radio/beacon/beacon + /// The beacon selected as a place for generating anomalies. + var/obj/item/radio/beacon/selected_beacon + /// Current anomaly generator datum. + var/datum/anomaly_gen_datum/cur_anomaly + + /// If true, generator will collect charge from connected wire node. + var/use_powernet = TRUE + /// If true, generator will collect charge from SMESes in 3*3. + var/use_smeses = TRUE + /// If true, generator will collect charge from apcs in this area. + var/use_apcs = TRUE + +/obj/machinery/power/anomaly_generator/New() + ..() + beacon = new(src) + selected_beacon = beacon + component_parts = list() + component_parts += new /obj/item/circuitboard/anomaly_generator + component_parts += new /obj/item/stock_parts/matter_bin + component_parts += new /obj/item/stock_parts/matter_bin + component_parts += new /obj/item/stock_parts/manipulator + component_parts += new /obj/item/stock_parts/capacitor + component_parts += new /obj/item/stock_parts/capacitor + RefreshParts() + +/obj/machinery/power/anomaly_generator/upgraded/New() + ..() + LAZYCLEARLIST(component_parts) + component_parts = list() + component_parts += new /obj/item/circuitboard/anomaly_generator + component_parts += new /obj/item/stock_parts/matter_bin/bluespace + component_parts += new /obj/item/stock_parts/matter_bin/bluespace + component_parts += new /obj/item/stock_parts/manipulator/femto + component_parts += new /obj/item/stock_parts/capacitor/quadratic + component_parts += new /obj/item/stock_parts/capacitor/quadratic + RefreshParts() + +/obj/machinery/power/anomaly_generator/Initialize(mapload) + . = ..() + powernet = find_powernet() + +/obj/machinery/power/anomaly_generator/Destroy() + . = ..() + qdel(beacon) + STOP_PROCESSING(SSprocessing, src) + +/obj/machinery/power/anomaly_generator/RefreshParts() + containment_limit = 0 + for(var/obj/item/stock_parts/matter_bin/matter_bin in component_parts) + containment_limit += matter_bin.rating + + while(containment.len > containment_limit) + eject(pick(containment)) + + creating_range = 25 + for(var/obj/item/stock_parts/manipulator/manipulator in component_parts) + creating_range /= manipulator.rating + + // 2 tier 1 = 10000 (100 sec, ~17 min, ~83 min); 2 tier 4 = 1280000 (1 sec, 8 sec, 39 sec). + speed = 1e4 + for(var/obj/item/stock_parts/capacitor/capacitor in component_parts) + speed *= capacitor.rating * capacitor.rating + +/obj/machinery/power/anomaly_generator/update_icon(updates = ALL) + if(stat & NOPOWER) + icon_state = "generator_off" + else + icon_state = "generator_on" + + return ..() + +/obj/machinery/power/anomaly_generator/attackby(obj/item/I, mob/user, params) + if(user.a_intent == INTENT_HARM) + return ..() + + if(exchange_parts(user, I)) + return ATTACK_CHAIN_PROCEED_SUCCESS + + if(!iscore(I)) + return ..() + + if(user.drop_transfer_item_to_loc(I, src)) + add_fingerprint(user) + user.visible_message(span_warning("[user] поместил[genderize_ru(user.gender, "", "а", "о", "и")] [I.declent_ru(ACCUSATIVE)] в [declent_ru(ACCUSATIVE)]."), \ + span_warning("Вы поместили [I.declent_ru(ACCUSATIVE)] в [declent_ru(ACCUSATIVE)].")) + containment.Add(I) + return ATTACK_CHAIN_PROCEED_SUCCESS + + return ..() + +/obj/machinery/power/anomaly_generator/ui_act(action, params, datum/tgui/ui, datum/ui_state/state) + if(..()) + return + + if(stat & (NOPOWER|BROKEN)) + return + + add_fingerprint(usr) + + . = TRUE + switch(action) + if("choose_type") + var/type = params["type"] + selected_type = type + + if("choose_tier") + var/tier = params["tier"] + selected_tier = tier + + if("generate") + generate() + + if("eject_all") + while(containment.len) + eject(containment[1]) + + if("stop") + cur_anomaly = null + atom_say("Создание аномалии прекращено.") + playsound(src, 'sound/machines/buzz-sigh.ogg', 40) + + if("toggle_apcs") + use_apcs = !use_apcs + + if("toggle_smeses") + use_smeses = !use_smeses + + if("toggle_powernet") + use_powernet = !use_powernet + + if("beakon") + var/list/options = list() + for(var/obj/item/radio/beacon/R in GLOB.beacons) + var/turf/T = get_turf(R) + if(!T) + continue + + if(!is_teleport_allowed(T.z) && !R.cc_beacon) + continue + + if(R.syndicate) + continue + + options["[T.loc.name]"] = R + + var/choice = tgui_input_list(ui.user, "Выберите маячок для создания аномалии.", "Выбор маячка", options) + selected_beacon = choice + + else + . = FALSE + +/obj/machinery/power/anomaly_generator/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/power/anomaly_generator/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/power/anomaly_generator/attack_hand(mob/user) + if(..()) + return TRUE + + ui_interact(user) + +/obj/machinery/power/anomaly_generator/ui_state(mob/user) + return GLOB.default_state + +/obj/machinery/power/anomaly_generator/ui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AnomalyGenerator", "Генератор аномалий") + ui.open() + +/obj/machinery/power/anomaly_generator/ui_data(mob/user) + var/list/data = list() + data["type"] = selected_type + data["tier"] = selected_tier + data["req_energy"] = get_req_energy() + data["req_item"] = get_req_items() + data["anomaly_type"] = get_anomaly_type() + data["charge"] = charge + data["generating"] = cur_anomaly != null + data["use_acps"] = use_apcs + data["use_smeses"] = use_smeses + data["use_powernet"] = use_powernet + data["has_powernet"] = powernet != null + data["last_charge"] = last_charge + return data + +/obj/machinery/power/anomaly_generator/ui_static_data(mob/user) + var/list/data = list() + return data + +/obj/machinery/power/anomaly_generator/proc/eject(obj/item/I, mob/living/carbon/human/user) + if(!(I in containment)) + return + + if(!user?.put_in_hands(I, ignore_anim = FALSE)) + I.forceMove(get_turf(src)) + + containment.Remove(I) + +/obj/machinery/power/anomaly_generator/proc/get_req_energy() + var/mult + if(selected_type == ANOMALY_TYPE_RANDOM) + mult = selected_tier == 1 ? 0.3 : 3 + else + mult = 1 + GLOB.created_anomalies[selected_type] / 2 + + switch(selected_tier) + if("1") + return (/datum/anomaly_gen_datum/tier1::req_energy) * mult + if("2") + return (/datum/anomaly_gen_datum/tier2::req_energy) * mult + if("3") + return (/datum/anomaly_gen_datum/tier3::req_energy) * mult + +/obj/machinery/power/anomaly_generator/proc/get_req_items() + if(selected_type == ANOMALY_TYPE_RANDOM) + return "-" + + var/datum/anomaly_gen_datum/anomaly = GLOB.anomaly_types["[selected_tier]"][selected_type] + return anomaly::req_item + +/obj/machinery/power/anomaly_generator/proc/get_anomaly_type() + if(selected_type == ANOMALY_TYPE_RANDOM) + return "случайная" + + var/datum/anomaly_gen_datum/anomaly = GLOB.anomaly_types["[selected_tier]"][selected_type] + return anomaly::anomaly_type + +/obj/machinery/power/anomaly_generator/proc/generate() + var/datum/anomaly_gen_datum/anomaly + if(selected_type == ANOMALY_TYPE_RANDOM) + var/anomaly_datum_type = GLOB.anomaly_types[selected_tier][pick(GLOB.anomaly_types[selected_tier])] + anomaly = new anomaly_datum_type + else + var/anomaly_datum_type = GLOB.anomaly_types["[selected_tier]"][selected_type] + anomaly = new anomaly_datum_type + var/list/possible_used = anomaly.get_used(containment) + if(!possible_used.len && anomaly.req_item != "-") + playsound(src, 'sound/machines/buzz-sigh.ogg', 40) + atom_say("Недостаточно ресурсов!") + return + + atom_say("Сбор энергии начался. Текущая цель: [anomaly.anomaly_type].") + cur_anomaly = anomaly + START_PROCESSING(SSprocessing, src) + +/obj/machinery/power/anomaly_generator/process() + if((stat & BROKEN) || !cur_anomaly) + STOP_PROCESSING(SSprocessing, src) + + if(charge >= get_req_energy()) + finish_generation() + cur_anomaly = null + STOP_PROCESSING(SSprocessing, src) + + if(stat & NOPOWER) + return + + var/going_to_use = min(speed, get_req_energy() - charge) + var/was_charge = charge + + // POWERNET + if(use_powernet && powernet) + add_load(-powernet_usage) + var/used_charge = min(going_to_use, surplus()) + powernet_usage = used_charge + add_load(powernet_usage) + charge += used_charge + going_to_use -= used_charge + + // SMES + if(use_smeses) + for(var/obj/machinery/power/smes/smes in range(2, src)) + var/used_charge = max(0, min(going_to_use, min(smes.charge * 0.05, smes.output_level))) + smes.output_used += used_charge + smes.charge -= used_charge + charge += used_charge + going_to_use -= used_charge + + // APC + var/area/A = get_area(src) + if(use_apcs && A) + // It won't use more power than was prepared for equipment. + for(var/obj/machinery/power/apc/apc in A?.apc) + if(!apc.cell) + continue + + if(!apc.is_channel_on(EQUIP)) + continue + + var/min_charge_border = apc.is_channel_force_on(EQUIP) ? 0 : 1250 + var/used_charge = min(apc.cell.charge - min_charge_border, going_to_use) + apc.cell.charge -= used_charge + charge += used_charge + going_to_use -= used_charge + + last_charge = charge - was_charge + +/obj/machinery/power/anomaly_generator/proc/finish_generation() + if(!cur_anomaly.generate(containment, selected_beacon, creating_range, selected_type != ANOMALY_TYPE_RANDOM)) + atom_say("Создание аномалии провалилось.") + playsound(src, 'sound/machines/buzz-sigh.ogg', 40) + return + + atom_say("Была создана [cur_anomaly.anomaly_type] аномалия.") + playsound(src, 'sound/machines/ping.ogg', 50, 1, -1) + +/obj/machinery/power/anomaly_generator/upgraded/admin + desc = "Необычного вида машина, разработанная на основе эксперементальной технологии, предназначенная для \ + генерации аномалий. В данной модели были использованы секретные разработки NanoTrasen." + +/obj/machinery/power/anomaly_generator/wrench_act(mob/living/user, obj/item/I) + default_unfasten_wrench(user, I) + powernet = find_powernet() + return TRUE + +/obj/machinery/power/anomaly_generator/upgraded/admin/get_req_energy() + return 0 diff --git a/code/modules/anomalies/anomaly_stabilizer.dm b/code/modules/anomalies/anomaly_stabilizer.dm new file mode 100644 index 00000000000..f233fcb1331 --- /dev/null +++ b/code/modules/anomalies/anomaly_stabilizer.dm @@ -0,0 +1,247 @@ +/obj/item/gun/energy/anomaly_stabilizer + name = "стабилизатор аномалий" + ru_names = list( + NOMINATIVE = "стабилизатор аномалий", \ + GENITIVE = "стабилизатора аномалий", \ + DATIVE = "стабилизатору аномалий", \ + ACCUSATIVE = "стабилизатор аномалий", \ + INSTRUMENTAL = "стабилизатором аномалий", \ + PREPOSITIONAL = "стабилизаторе аномалий" + ) + desc = "Продвинутое устройство, предназначенное для стабилизации аномалий. \ + Имеет два слота для ядер аномалий." + icon = 'icons/obj/anomaly/anomaly_stuff.dmi' + icon_state = "pistol_base_item" + lefthand_file = 'icons/obj/anomaly/anomaly_inhand_l.dmi' + righthand_file = 'icons/obj/anomaly/anomaly_inhand_r.dmi' + item_state = "pistol_base" + gender = MALE + gun_light_overlay = "flight" + can_add_sibyl_system = FALSE + origin_tech = "programming=3;magnets=3" + cell_type = /obj/item/stock_parts/cell/high + var/cur_ammo_type = /obj/item/ammo_casing/energy/anomaly/stabilizer + /// Cores inserted into this anomaly stabilizer. + var/list/obj/item/assembly/signaler/core/cores = list() + /// Range of allowed stability deltas. If val - X, range is [-x; x]. + var/stability_range = 1 + /// The current value of the anomaly's stability change upon impact. + var/stability_delta = 1 + /// Max allowed anomaly pull range. + var/pull_range = 0 + /// Choosen anomaly pull distance. + var/choosen_pull_dist = 0 + /// Choosen time on which beams block anomaly's normal movements. + var/block_move_time = 0 + /// Choosen time on which beams block anomaly's impulsive movements. + var/block_move_impulses_time = 0 + /// The amount by which the strength of the anomaly's effects is temporarily reduced. + var/weaken_val = 0 + /// The time after hit at which the reduction in the effects of the anomaly will be reset. + var/weaken_time = 0 + /// If true, tgui will show more info about this anomaly_stabilizer. + var/full_info = FALSE + +/obj/item/gun/energy/anomaly_stabilizer/Initialize(mapload, ...) + . = ..() + update_stability_delta(1) + +/obj/item/gun/energy/anomaly_stabilizer/attack_self(mob/living/user) + add_fingerprint(user) + ui_interact(user) + +/obj/item/gun/energy/anomaly_stabilizer/newshot() + if(!cell) + return + + chambered = new cur_ammo_type + if(!chambered.BB) + chambered.newshot() + + var/obj/item/ammo_casing/energy/anomaly/en_chambered = chambered + en_chambered.e_cost *= max(1, stability_delta * stability_delta) + + var/obj/item/projectile/beam/anomaly/BB = chambered.BB + BB.stability_delta = stability_delta + BB.pull_strenght = choosen_pull_dist + BB.move_block = block_move_time + BB.move_impulces_block = block_move_impulses_time + BB.anom_weaken = weaken_val + BB.weaken_time = weaken_time + +/obj/item/gun/energy/anomaly_stabilizer/proc/update_stability_delta(new_val) + new_val = clamp(new_val, -stability_range, stability_range) + stability_delta = new_val + + if(new_val > 0) + cur_ammo_type = /obj/item/ammo_casing/energy/anomaly/stabilizer + else if(new_val < 0) + cur_ammo_type = /obj/item/ammo_casing/energy/anomaly/destabilizer + else + cur_ammo_type = /obj/item/ammo_casing/energy/anomaly + + +/obj/item/gun/energy/anomaly_stabilizer/proc/eject_core(index, mob/user) + if(user) + user.put_in_hands(cores[index]) + else + cores[index].forceMove(get_turf(src)) + + cores.Remove(cores[index]) + update_cores() + + +/obj/item/gun/energy/anomaly_stabilizer/proc/insert_core(obj/item/assembly/signaler/core/core, mob/user) + add_fingerprint(user) + if(iscoreempty(core)) + balloon_alert(user, "ядро пустое!") + return ATTACK_CHAIN_PROCEED + + if(!user.drop_transfer_item_to_loc(core, src)) + balloon_alert(user, "отпустить невозможно!") + return ATTACK_CHAIN_PROCEED + + if(cores.len >= 2) + balloon_alert(user, "слоты для ядер заняты!") + return ATTACK_CHAIN_PROCEED + + + cores.Add(core) + update_cores() + balloon_alert(user, "ядро вставлено") + return ATTACK_CHAIN_PROCEED + +/obj/item/gun/energy/anomaly_stabilizer/proc/update_cores() + var/strenght_energetic = 0 + var/strenght_atmospheric = 0 + var/strenght_bluespace = 0 + var/strenght_vortex = 0 + var/strenght_gravitation = 0 + for(var/obj/item/assembly/signaler/core/core in cores) + var/strenght = core.get_strenght() + if(iscoreflux(core)) + strenght_energetic += strenght + + if(iscoreatmos(core)) + strenght_atmospheric += strenght + + if(iscorebluespace(core)) + strenght_bluespace += strenght + + if(iscorevortex(core)) + strenght_vortex += strenght + + if(iscoregrav(core)) + strenght_gravitation += strenght + + stability_range = 1 + round(strenght_energetic / 50 + 0.5) + update_stability_delta(stability_delta) + + pull_range = round(strenght_gravitation / 50 + 0.5) + choosen_pull_dist = clamp(choosen_pull_dist, -pull_range, pull_range) + + block_move_time = (strenght_vortex / 100) SECONDS + + block_move_impulses_time = (strenght_bluespace / 100) SECONDS + + weaken_val = strenght_atmospheric / 3 + weaken_time = (strenght_atmospheric / 50) SECONDS + + newshot() + +/obj/item/gun/energy/anomaly_stabilizer/attackby(obj/item/I, mob/user, params) + if(user.intent == INTENT_HARM) + return ..() + + add_fingerprint(user) + if(istype(I, /obj/item/stock_parts/cell)) + if(!user.drop_transfer_item_to_loc(I, src)) + balloon_alert(user, "отпустить невозможно!") + return ATTACK_CHAIN_PROCEED + + user.put_in_hands(cell) + cell = I + cell_type = I.type + balloon_alert(user, "батарейка заменена") + return ATTACK_CHAIN_PROCEED + + if(!iscore(I)) + return ..() + + return insert_core(I, user) + + +/obj/item/gun/energy/anomaly_stabilizer/ui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AnomalyStabilizer", "Стабилизатор аномалий") + ui.set_autoupdate(TRUE) + ui.open() + +/obj/item/gun/energy/anomaly_stabilizer/ui_data(mob/user) + var/list/data = list() + data["full_info"] = full_info + data["core1_name"] = null + data["core2_name"] = null + if(cores.len > 0) + data["core1_name"] = cores[1].name + + if(cores.len > 1) + data["core2_name"] = cores[2].name + + data["possible_stability"] = stability_range + data["stability_delta"] = stability_delta + data["pull_range"] = pull_range + data["choosen_pull_dist"] = choosen_pull_dist + data["block_move_time"] = block_move_time + data["block_move_impulses_time"] = block_move_impulses_time + data["weaken_val"] = weaken_val + data["weaken_time"] = weaken_time + return data + +/obj/item/gun/energy/anomaly_stabilizer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + if(..()) + return + + . = TRUE + switch(action) + if("eject1") + eject_core(1, ui.user) + + if("eject2") + eject_core(2, ui.user) + + if("change_stability") + var/new_val = text2num(params["new_val"]) + update_stability_delta(new_val) + update_icon(UPDATE_OVERLAYS) + newshot() + + if("change_pull_dist") + var/new_val = text2num(params["new_val"]) + choosen_pull_dist = new_val + newshot() + + if("toggle_full_info") + full_info = !full_info + + else + return FALSE + +/obj/item/gun/energy/anomaly_stabilizer/examine(mob/user) + . = ..() + var/shots = round(cell.charge / (/obj/item/ammo_casing/energy/anomaly::e_cost) / stability_delta / stability_delta) + . += span_notice("Индикатор заряда сообщает: [cell.charge]\\[cell.maxcharge].") + . += span_notice("Этого хватит на [shots] [declension_ru(shots, "выстрел", "выстрела", "выстрелов")] и изменение стабильности аномалии на [shots * stability_delta] [declension_ru(shots * stability_delta, "единицу", "единицы", "единиц")] при текущих настройках.") + +/obj/item/gun/energy/anomaly_stabilizer/update_overlays() + . = list() + if(cell.charge < /obj/item/ammo_casing/energy/anomaly::e_cost) + return + else if(stability_delta < 0) + . += image(icon = icon, icon_state = "pistol_destab_overlay") + else if(stability_delta > 0) + . += image(icon = icon, icon_state = "pistol_stabil_overlay") + else + . += image(icon = icon, icon_state = "pistol_zero_overlay") + diff --git a/code/modules/anomalies/anomaly_upgrader.dm b/code/modules/anomalies/anomaly_upgrader.dm new file mode 100644 index 00000000000..910e27ac72c --- /dev/null +++ b/code/modules/anomalies/anomaly_upgrader.dm @@ -0,0 +1,19 @@ +// Adminspawn only. Transforms tier3 anomaly to tier4 anomaly. + +/obj/item/anomaly_upgrader + name = "усилитель аномалий" + ru_names = list( + NOMINATIVE = "усилитель аномалий", \ + GENITIVE = "усилителя аномалий", \ + DATIVE = "усилителю аномалий", \ + ACCUSATIVE = "усилитель аномалий", \ + INSTRUMENTAL = "усилителем аномалий", \ + PREPOSITIONAL = "усилителе аномалий" + ) + desc = "Черезвычайно дорогой и высокотехнологичный прибор, способный значительно усилять большие аномалии, попадая в них. \ + Такие приборы невозможно достать без помощи кого-то с ЦК. Если вы решите его использовать, убедитесь что готовы, \ + иначе вы уже ни в чем не сможете убедиться." + gender = MALE + icon = 'icons/obj/stock_parts.dmi' + icon_state = "capacitor" + origin_tech = "magnets=11" diff --git a/code/modules/anomalies/cores.dm b/code/modules/anomalies/cores.dm new file mode 100644 index 00000000000..faa3dc1d24b --- /dev/null +++ b/code/modules/anomalies/cores.dm @@ -0,0 +1,583 @@ +// Embedded signaller used in anomalies. +/obj/item/assembly/signaler/core + name = "anomaly core" + ru_names = list( + NOMINATIVE = "ядро аномалии", \ + GENITIVE = "ядра аномалии", \ + DATIVE = "ядру аномалии", \ + ACCUSATIVE = "ядро аномалии", \ + INSTRUMENTAL = "ядром аномалии", \ + PREPOSITIONAL = "ядре аномалии" + ) + desc = "The neutralized core of an anomaly. It'd probably be valuable for research." + gender = NEUTER + icon_state = "core_bluespace_t2" + item_state = "electronic" + resistance_flags = FIRE_PROOF + receiving = TRUE + /// The type of anomaly that leaves nuclei of this type. + var/anomaly_type = /obj/effect/old_anomaly + /// The strength of the anomaly at the moment of stabilization. Used to scale some effects of items using anomaly cores. + var/charge = 50 + /// The level of the anomaly from which the core was collected. + var/tier = 0 + /// Moment whet this core was created. Used to prevent the core from instantly disintegrating when charging. + var/born_moment = 0 + +/obj/item/assembly/signaler/core/suicide_act(mob/user) + user.visible_message(span_suicide("[user] засовывает [declent_ru(ACCUSATIVE)] себе в рот. Похоже [genderize_ru(user.gender, "он", "она", "оно", "они")] пыта[genderize_ru(user.gender, "е", "е", "е", "ю")]тся убить себя!")) + return OXYLOSS | BRUTELOSS + +/obj/item/assembly/signaler/core/examine(mob/user) + . = ..() + . += span_info("Текущий заряд: [charge].") + . += span_info("Текущая сила: [get_strenght()].") + +/obj/item/assembly/signaler/core/New(spawnloc, charge) + . = ..() + if(!charge) + charge = iscoreempty(src) ? 0 : rand(51, 60) + + src.charge = charge + born_moment = world.time + +// Used in old anomalies. +/obj/item/assembly/signaler/core/receive_signal(datum/signal/signal) + if(!..()) + return + + for(var/obj/effect/old_anomaly/A in get_turf(src)) + A.anomalyNeutralize() + +/obj/item/assembly/signaler/core/attack_self() + return + +/proc/iscoret1(obj/item/assembly/signaler/core/A) + return istype(A) && A.tier == 1 + +/proc/iscoret2(obj/item/assembly/signaler/core/A) + return istype(A) && A.tier == 2 + +/proc/iscoret3(obj/item/assembly/signaler/core/A) + return istype(A) && A.tier == 3 + +/* +100 of tier 1 == 50 of tier 2 == 25 of tier 3 +100 of tier 3 == 200 of tier 2 == 400 of tier 1 +*/ +/obj/item/assembly/signaler/core/proc/get_strenght() + return round(charge * (1 << (tier - 1)) * (tier != 4 ? 1 : 1.5)) + +// ============================ Tier 1 =================================== +/obj/item/assembly/signaler/core/tier1 + name = "пустое ядро малой аномалии" + ru_names = list( + NOMINATIVE = "пустое ядро малой аномалии", \ + GENITIVE = "пустого ядра малой аномалии", \ + DATIVE = "пустому ядру малой аномалии", \ + ACCUSATIVE = "пустое ядро малой аномалии", \ + INSTRUMENTAL = "пустым ядром малой аномалии", \ + PREPOSITIONAL = "пустом ядре малой аномалии" + ) + desc = "Не похоже, что силы аномалии на момент стабилизации хватило, чтобы придать этому ядру какие-то свойства. \ + Вероятно, его можно как-то зарядить." + icon_state = "core_empty_t1" + anomaly_type = null + origin_tech = "materials=3" + tier = 1 + +/obj/item/assembly/signaler/core/atmospheric/tier1 + name = "ядро малой атмосферной аномалии" + ru_names = list( + NOMINATIVE = "ядро малой атмосферной аномалии", \ + GENITIVE = "ядра малой атмосферной аномалии", \ + DATIVE = "ядру малой атмосферной аномалии", \ + ACCUSATIVE = "ядро малой атмосферной аномалии", \ + INSTRUMENTAL = "ядром малой атмосферной аномалии", \ + PREPOSITIONAL = "ядре малой атмосферной аномалии" + ) + desc = "Стабилизированное ядро ​малой атмосферной аномалии. На ощупь прохладное. Вероятно, оно пригодится для исследований." + icon_state = "core_atmos_t1" + anomaly_type = /obj/effect/anomaly/atmospheric/tier1 + origin_tech = "plasmatech=5" + tier = 1 + +/obj/item/assembly/signaler/core/gravitational/tier1 + name = "ядро малой гравитационной аномалии" + ru_names = list( + NOMINATIVE = "ядро малой гравитационной аномалии", \ + GENITIVE = "ядра малой гравитационной аномалии", \ + DATIVE = "ядру малой гравитационной аномалии", \ + ACCUSATIVE = "ядро малой гравитационной аномалии", \ + INSTRUMENTAL = "ядром малой гравитационной аномалии", \ + PREPOSITIONAL = "ядре малой гравитационной аномалии" + ) + desc = "Нейтрализованное ядро малой ​​гравитационной аномалии. Слегка легче, чем выглядит. Вероятно, оно пригодится для исследований." + icon_state = "core_grav_t1" + anomaly_type = /obj/effect/anomaly/gravitational/tier1 + origin_tech = "magnets=5" + tier = 1 + +/obj/item/assembly/signaler/core/energetic/tier1 + name = "ядро малой ​​энергетической аномалии" + ru_names = list( + NOMINATIVE = "ядро малой ​​энергетической аномалии", \ + GENITIVE = "ядра малой ​​энергетической аномалии", \ + DATIVE = "ядру малой ​​энергетической аномалии", \ + ACCUSATIVE = "ядро малой ​​энергетической аномалии", \ + INSTRUMENTAL = "ядром малой ​​энергетической аномалии", \ + PREPOSITIONAL = "ядре малой ​​энергетической аномалии" + ) + desc = "Стабилизированное ядро малой ​​энергетической аномалии. Прикосновение к нему вызывает лёгкое покалывание. Вероятно, оно пригодится для исследований." + icon_state = "core_energ_t1" + anomaly_type = /obj/effect/anomaly/energetic/tier1 + origin_tech = "powerstorage=5" + tier = 1 + +/obj/item/assembly/signaler/core/bluespace/tier1 + name = "ядро малой ​​блюспейс аномалии" + ru_names = list( + NOMINATIVE = "ядро малой ​​блюспейс аномалии", \ + GENITIVE = "ядра малой ​​блюспейс аномалии", \ + DATIVE = "ядру малой ​​блюспейс аномалии", \ + ACCUSATIVE = "ядро малой ​​блюспейс аномалии", \ + INSTRUMENTAL = "ядром малой ​​блюспейс аномалии", \ + PREPOSITIONAL = "ядре малой ​​блюспейс аномалии" + ) + desc = "Стабилизированное ядро ​малой ​блюспейс аномалии. Оно изредка, на долю секунды, исчезает из виду. Вероятно, оно пригодится для исследований." + icon_state = "core_bluespace_t1" + anomaly_type = /obj/effect/anomaly/bluespace/tier1 + origin_tech = "bluespace=5" + tier = 1 + +/obj/item/assembly/signaler/core/vortex/tier1 + name = "ядро малой вихревой аномалии" + ru_names = list( + NOMINATIVE = "ядро малой вихревой аномалии", \ + GENITIVE = "ядра малой вихревой аномалии", \ + DATIVE = "ядру малой вихревой аномалии", \ + ACCUSATIVE = "ядро малой вихревой аномалии", \ + INSTRUMENTAL = "ядром малой вихревой аномалии", \ + PREPOSITIONAL = "ядре малой вихревой аномалии" + ) + desc = "Стабилизированное ядро малой ​​вихревой аномалии. Оно изредка подёргивается. Вероятно, оно пригодится для исследований." + icon_state = "core_vortex_t1" + anomaly_type = /obj/effect/anomaly/vortex/tier1 + origin_tech = "engineering=5" + tier = 1 + + +// ============================ Tier 2 =================================== +/obj/item/assembly/signaler/core/tier2 + name = "пустое ядро аномалии" + ru_names = list( + NOMINATIVE = "пустое ядро аномалии", \ + GENITIVE = "пустого ядра аномалии", \ + DATIVE = "пустому ядру аномалии", \ + ACCUSATIVE = "пустое ядро аномалии", \ + INSTRUMENTAL = "пустым ядром аномалии", \ + PREPOSITIONAL = "пустом ядре аномалии" + ) + desc = "Не похоже, что силы аномалии на момент стабилизации хватило, чтобы придать ядру какие-то свойства. \ + Вероятно, его можно как-то зарядить." + icon_state = "core_empty_t2" + anomaly_type = null + origin_tech = "materials=5" // not clonable by experimentor + tier = 2 + +/obj/item/assembly/signaler/core/atmospheric/tier2 + name = "\improper pyroclastic anomaly core" + desc = "Стабилизированное ядро ​атмосферной аномалии. На ощупь теплое. Вероятно, оно пригодится для исследований." + icon_state = "core_atmos_t2" + anomaly_type = /obj/effect/anomaly/atmospheric/tier2 + origin_tech = "plasmatech=7" + tier = 2 + +/obj/item/assembly/signaler/core/gravitational/tier2 + name = "\improper gravitational anomaly core" + ru_names = list( + NOMINATIVE = "ядро гравитационной аномалии", \ + GENITIVE = "ядра гравитационной аномалии", \ + DATIVE = "ядру гравитационной аномалии", \ + ACCUSATIVE = "ядро гравитационной аномалии", \ + INSTRUMENTAL = "ядром гравитационной аномалии", \ + PREPOSITIONAL = "ядре гравитационной аномалии" + ) + desc = "Стабилизированное ядро ​​гравитационной аномалии. Гораздо тяжелее, чем кажется. Вероятно, оно пригодится для исследований." + icon_state = "core_grav_t2" + anomaly_type = /obj/effect/anomaly/gravitational/tier2 + origin_tech = "magnets=7" + tier = 2 + +/obj/item/assembly/signaler/core/energetic/tier2 + name = "\improper flux anomaly core" + ru_names = list( + NOMINATIVE = "ядро ​​энергетической аномалии", \ + GENITIVE = "ядра ​​энергетической аномалии", \ + DATIVE = "ядру ​​энергетической аномалии", \ + ACCUSATIVE = "ядро ​​энергетической аномалии", \ + INSTRUMENTAL = "ядром ​​энергетической аномалии", \ + PREPOSITIONAL = "ядре ​​энергетической аномалии" + ) + desc = "Стабилизированное ядро ​​энергетической аномалии. Прикосновение к нему вызывает лёгкое покалывание. Вероятно, оно пригодится для исследований." + icon_state = "core_energ_t2" + anomaly_type = /obj/effect/anomaly/energetic/tier2 + origin_tech = "powerstorage=7" + tier = 2 + +/obj/item/assembly/signaler/core/bluespace/tier2 + name = "\improper bluespace anomaly core" + ru_names = list( + NOMINATIVE = "ядро ​​блюспейс аномалии", \ + GENITIVE = "ядра ​​блюспейс аномалии", \ + DATIVE = "ядру ​​блюспейс аномалии", \ + ACCUSATIVE = "ядро ​​блюспейс аномалии", \ + INSTRUMENTAL = "ядром ​​блюспейс аномалии", \ + PREPOSITIONAL = "ядре ​​блюспейс аномалии" + ) + desc = "Стабилизированное ядро ​​блюспейс аномалии. Оно то появляется, то исчезает из виду. Вероятно, оно пригодится для исследований." + icon_state = "core_bluespace_t2" + anomaly_type = /obj/effect/anomaly/bluespace/tier2 + origin_tech = "bluespace=7" + tier = 2 + +/obj/item/assembly/signaler/core/vortex/tier2 + name = "\improper vortex anomaly core" + ru_names = list( + NOMINATIVE = "ядро ​​вихревой аномалии", \ + GENITIVE = "ядра ​​вихревой аномалии", \ + DATIVE = "ядру ​​вихревой аномалии", \ + ACCUSATIVE = "ядро ​​вихревой аномалии", \ + INSTRUMENTAL = "ядром ​​вихревой аномалии", \ + PREPOSITIONAL = "ядре ​​вихревой аномалии" + ) + desc = "Стабилизированное ядро ​​вихревой аномалии. Оно слегка трясётся, как будто на него воздействует некая невидимая сила. Вероятно, оно пригодится для исследований." + icon_state = "core_vortex_t2" + anomaly_type = /obj/effect/anomaly/vortex/tier2 + origin_tech = "engineering=7" + tier = 2 + + +// ============================ Tier 3 =================================== +/obj/item/assembly/signaler/core/tier3 + name = "пустое ядро большой аномалии" + ru_names = list( + NOMINATIVE = "пустое ядро большой аномалии", \ + GENITIVE = "пустого ядра большой аномалии", \ + DATIVE = "пустому ядру большой аномалии", \ + ACCUSATIVE = "пустое ядро большой аномалии", \ + INSTRUMENTAL = "пустым ядром большой аномалии", \ + PREPOSITIONAL = "пустом ядре большой аномалии" + ) + desc = "Не похоже, что силы аномалии на момент стабилизации хватило, чтобы придать ядру какие-то свойства. \ + Вероятно, его можно как-то зарядить." + icon_state = "core_empty_t3" + anomaly_type = null + origin_tech = "materials=7" // Sorry, not clonable by experimentor + tier = 3 + +/obj/item/assembly/signaler/core/atmospheric/tier3 + name = "ядро большой атмосферной аномалии" + ru_names = list( + NOMINATIVE = "ядро большой атмосферной аномалии", \ + GENITIVE = "ядра большой атмосферной аномалии", \ + DATIVE = "ядру большой атмосферной аномалии", \ + ACCUSATIVE = "ядро большой атмосферной аномалии", \ + INSTRUMENTAL = "ядром большой атмосферной аномалии", \ + PREPOSITIONAL = "ядре большой атмосферной аномалии" + ) + desc = "Стабилизированное ядро большой атмосферной аномалии. От одного его вида вас бросает то в жар, то в холод, причём буквально." + icon_state = "core_atmos_t3" + anomaly_type = /obj/effect/anomaly/atmospheric/tier3 + origin_tech = "plasmatech=8" + tier = 3 + +/obj/item/assembly/signaler/core/atmospheric/tier3/suicide_act(mob/living/user) + ..() + user.adjust_fire_stacks(30) + user.IgniteMob() + return FIRELOSS + +/obj/item/assembly/signaler/core/atmospheric/tier3/forceMove(atom/destination) + if(ishuman(destination)) + START_PROCESSING(SSobj, src) + if(prob(1)) + var/mob/living/carbon/human/H = destination + H.adjust_fire_stacks(charge/10) + H.IgniteMob() + else + STOP_PROCESSING(SSobj, src) + + return ..() + +/obj/item/assembly/signaler/core/atmospheric/tier3/process() + var/mob/living/carbon/human/H = loc + if(!istype(H)) + return + + if(prob(90)) + return + + if(H.bodytemperature < T0C - 50) + visible_message("[capitalize(declent_ru(NOMINATIVE))] реагирует на контакт с холодным объектом, испуская языки пламени!") + H.adjust_fire_stacks(round(get_strenght() / 30 + 0.5)) + H.IgniteMob() + else if(H.bodytemperature > T0C + 100) + visible_message("[capitalize(declent_ru(NOMINATIVE))] реагирует на контакт с горячим объектом, значительно охлаждая окружающую среду!") + H.apply_status_effect(/datum/status_effect/freon) + H.ExtinguishMob() + H.adjust_bodytemperature(-get_strenght()) + + +/obj/item/assembly/signaler/core/gravitational/tier3 + name = "ядро большой гравитационной аномалии" + ru_names = list( + NOMINATIVE = "ядро большой гравитационной аномалии", \ + GENITIVE = "ядра большой гравитационной аномалии", \ + DATIVE = "ядру большой гравитационной аномалии", \ + ACCUSATIVE = "ядро большой гравитационной аномалии", \ + INSTRUMENTAL = "ядром большой гравитационной аномалии", \ + PREPOSITIONAL = "ядре большой гравитационной аномалии" + ) + desc = "Нейтрализованное ядро большой ​​гравитационной аномалии. Вы чувствуете сильное несоответствие веса многих окружающих предметов с их внешним видом." + icon_state = "core_grav_t3" + anomaly_type = /obj/effect/anomaly/gravitational/tier3 + origin_tech = "magnets=8" + var/atom/old_owner = null + tier = 3 + +/obj/item/assembly/signaler/core/gravitational/tier3/suicide_act(mob/user) + ..() + user.visible_message(span_suicide("[user] взрывается из-за возникшего гравитационного колодца!"), \ + span_suicide("Вы взрываетесь из-за возникшего гравитационного колодца!"), + span_suicide("Вы слышите громкий, гулкий хлопок!")) + user.gib() + return OBLITERATION + +/obj/item/assembly/signaler/core/gravitational/tier3/Initialize() + . = ..() + old_owner = get_external_loc() + update_gravity(TRUE) + +// Mobs will be in reversed gravity. Items will be without gravity. +/obj/item/assembly/signaler/core/gravitational/tier3/proc/update_gravity(restart = FALSE) + if(restart) + addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/item/assembly/signaler/core/gravitational/tier3, update_gravity)), 5 SECONDS) + + var/atom/new_owner = get_external_loc() + if(old_owner == new_owner && old_owner.get_gravity() == -1) + return + + old_owner.remove_gravity_source("core_grav") + if(ismob(new_owner)) + new_owner.add_gravity("core_grav", -(new_owner.get_gravity() + 2)) + + if(isitem(new_owner)) + new_owner.add_gravity("core_grav", -(new_owner.get_gravity() + 1)) + + old_owner = new_owner + +/obj/item/assembly/signaler/core/gravitational/tier3/forceMove(atom/destination) + . = ..() + update_gravity() + +/obj/item/assembly/signaler/core/energetic/tier3 + name = "ядро большой ​​энергетической аномалии" + ru_names = list( + NOMINATIVE = "ядро большой ​​энергетической аномалии", \ + GENITIVE = "ядра большой ​​энергетической аномалии", \ + DATIVE = "ядру большой ​​энергетической аномалии", \ + ACCUSATIVE = "ядро большой ​​энергетической аномалии", \ + INSTRUMENTAL = "ядром большой ​​энергетической аномалии", \ + PREPOSITIONAL = "ядре большой ​​энергетической аномалии" + ) + desc = "Стабилизированное ядро большой ​​энергетической аномалии. Вокруг ядра периодически возникают электрические разряды. Окружающая электроника напряженно гудит." + icon_state = "core_energ_t3" + anomaly_type = /obj/effect/anomaly/energetic/tier3 + origin_tech = "powerstorage=8" + tier = 3 + +/obj/item/assembly/signaler/core/energetic/tier3/Bump(atom/bumped_atom) + . = ..() + try_shock(bumped_atom) + +/obj/item/assembly/signaler/core/energetic/tier3/suicide_act(mob/living/user) + ..() + user.electrocute_act(600, "[declent_ru(GENITIVE)]") + return FIRELOSS + +/obj/item/assembly/signaler/core/energetic/tier3/proc/try_shock(atom/target) + if(!iscarbon(target)) + return FALSE + + visible_message(span_warning("[declent_ru(NOMINATIVE)] внезапно испустил[genderize_ru(gender, "", "а", "о", "и")] электрический разряд!")) + var/mob/living/carbon/human/H = target + if(H.electrocute_act(charge, "[declent_ru(GENITIVE)]")) + do_sparks(max(1, charge / 20)) + return TRUE + + return FALSE + +/obj/item/assembly/signaler/core/energetic/tier3/forceMove(atom/destination) + if(!try_shock(destination)) + return ..() + else + return FALSE + +/obj/item/assembly/signaler/core/bluespace/tier3 + name = "ядро большой ​​блюспейс аномалии" + ru_names = list( + NOMINATIVE = "ядро большой ​​блюспейс аномалии", \ + GENITIVE = "ядра большой ​​блюспейс аномалии", \ + DATIVE = "ядру большой ​​блюспейс аномалии", \ + ACCUSATIVE = "ядро большой ​​блюспейс аномалии", \ + INSTRUMENTAL = "ядром большой ​​блюспейс аномалии", \ + PREPOSITIONAL = "ядре большой ​​блюспейс аномалии" + ) + desc = "Стабилизированное ядро ​большой ​блюспейс аномалии. Пространство вокруг него постоянно искревляется." + icon_state = "core_bluespace_t3" + anomaly_type = /obj/effect/anomaly/bluespace/tier3 + origin_tech = "bluespace=8" + tier = 3 + +/obj/item/assembly/signaler/core/bluespace/tier3/suicide_act(mob/user) + ..() + user.gib() + for(var/obj/item/organ/internal/O in range(2)) + if(isturf(O.loc)) + do_teleport(O, O, 2, asoundin = 'sound/effects/phasein.ogg') + + return OBLITERATION + +/obj/item/assembly/signaler/core/bluespace/tier3/Bump(atom/bumped_atom) + . = ..() + try_teleport() + +/obj/item/assembly/signaler/core/bluespace/tier3/proc/try_teleport() + if(prob(80) || !ismob(loc)) + return FALSE + + visible_message(span_warning("[declent_ru(NOMINATIVE)] внезапно телепортируется!")) + return do_teleport(src, src, 2, asoundin = 'sound/effects/phasein.ogg') + +/obj/item/assembly/signaler/core/bluespace/tier3/forceMove(atom/destination) + if(!try_teleport()) + return ..() + else + return FALSE + + +/obj/item/assembly/signaler/core/vortex/tier3 + name = "ядро большой вихревой аномалии" + ru_names = list( + NOMINATIVE = "ядро большой вихревой аномалии", \ + GENITIVE = "ядра большой вихревой аномалии", \ + DATIVE = "ядру большой вихревой аномалии", \ + ACCUSATIVE = "ядро большой вихревой аномалии", \ + INSTRUMENTAL = "ядром большой вихревой аномалии", \ + PREPOSITIONAL = "ядре большой вихревой аномалии" + ) + desc = "Стабилизированное ядро большой ​​вихревой аномалии. Предметы вокруг ядра опасно подрагивают." + icon_state = "core_vortex_t3" + anomaly_type = /obj/effect/anomaly/vortex/tier3 + origin_tech = "engineering=8" + tier = 3 + + +// ============================ Tier4 (admin spawn only) =================================== +/obj/item/assembly/signaler/core/tier3/tier4 + name = "пустое ядро колоссальной аномалии" + ru_names = list( + NOMINATIVE = "пустое ядро колоссальной аномалии", \ + GENITIVE = "пустого ядра колоссальной аномалии", \ + DATIVE = "пустому ядру колоссальной аномалии", \ + ACCUSATIVE = "пустое ядро колоссальной аномалии", \ + INSTRUMENTAL = "пустым ядром колоссальной аномалии", \ + PREPOSITIONAL = "пустом ядре колоссальной аномалии" + ) + desc = "Не похоже что силы аномалии на момент стабилизации хватило, чтобы придать ядру какие-то свойства. \ + Вероятно, его можно как-то зарядить. У вас стойкое чувство, что его не должно здесь находиться." + icon_state = "core_empty_t3" + anomaly_type = null + origin_tech = "materials=10" // Sorry, not clonable by experimentor + tier = 4 + +/obj/item/assembly/signaler/core/atmospheric/tier3/tier4 + name = "ядро колоссальной атмосферной аномалии" + ru_names = list( + NOMINATIVE = "ядро колоссальной атмосферной аномалии", \ + GENITIVE = "ядра колоссальной атмосферной аномалии", \ + DATIVE = "ядру колоссальной атмосферной аномалии", \ + ACCUSATIVE = "ядро колоссальной атмосферной аномалии", \ + INSTRUMENTAL = "ядром колоссальной атмосферной аномалии", \ + PREPOSITIONAL = "ядре колоссальной атмосферной аномалии" + ) + desc = "Стабилизированное ядро колоссальной атмосферной аномалии. У вас стойкое чувство, что его не должно здесь находиться." + icon_state = "core_atmos_t3" + anomaly_type = /obj/effect/anomaly/atmospheric/tier4 + origin_tech = "plasmatech=11" + tier = 4 + +/obj/item/assembly/signaler/core/gravitational/tier3/tier4 + name = "ядро колоссальной гравитационной аномалии" + ru_names = list( + NOMINATIVE = "ядро колоссальной гравитационной аномалии", \ + GENITIVE = "ядра колоссальной гравитационной аномалии", \ + DATIVE = "ядру колоссальной гравитационной аномалии", \ + ACCUSATIVE = "ядро колоссальной гравитационной аномалии", \ + INSTRUMENTAL = "ядром колоссальной гравитационной аномалии", \ + PREPOSITIONAL = "ядре колоссальной гравитационной аномалии" + ) + desc = "Нейтрализованное ядро колоссальной ​​гравитационной аномалии. У вас стойкое чувство, что его не должно здесь находиться." + icon_state = "core_grav_t3" + anomaly_type = /obj/effect/anomaly/gravitational/tier4 + origin_tech = "magnets=11" + tier = 4 + +/obj/item/assembly/signaler/core/energetic/tier3/tier4 + name = "ядро колоссальной ​​энергетической аномалии" + ru_names = list( + NOMINATIVE = "ядро колоссальной ​​энергетической аномалии", \ + GENITIVE = "ядра колоссальной ​​энергетической аномалии", \ + DATIVE = "ядру колоссальной ​​энергетической аномалии", \ + ACCUSATIVE = "ядро колоссальной ​​энергетической аномалии", \ + INSTRUMENTAL = "ядром колоссальной ​​энергетической аномалии", \ + PREPOSITIONAL = "ядре колоссальной ​​энергетической аномалии" + ) + desc = "Стабилизированное ядро колоссальной ​​энергетической аномалии. У вас стойкое чувство, что его не должно здесь находиться." + icon_state = "core_energ_t3" + anomaly_type = /obj/effect/anomaly/energetic/tier4 + origin_tech = "powerstorage=11" + tier = 4 + +/obj/item/assembly/signaler/core/bluespace/tier3/tier4 + name = "ядро колоссальной ​​блюспейс аномалии" + ru_names = list( + NOMINATIVE = "ядро колоссальной ​​блюспейс аномалии", \ + GENITIVE = "ядра колоссальной ​​блюспейс аномалии", \ + DATIVE = "ядру колоссальной ​​блюспейс аномалии", \ + ACCUSATIVE = "ядро колоссальной ​​блюспейс аномалии", \ + INSTRUMENTAL = "ядром колоссальной ​​блюспейс аномалии", \ + PREPOSITIONAL = "ядре колоссальной ​​блюспейс аномалии" + ) + desc = "Стабилизированное ядро ​большой ​блюспейс аномалии. У вас стойкое чувство, что его не должно здесь находиться." + icon_state = "core_bluespace_t3" + anomaly_type = /obj/effect/anomaly/bluespace/tier4 + origin_tech = "bluespace=11" + tier = 4 + +/obj/item/assembly/signaler/core/vortex/tier3/tier4 + name = "ядро колоссальной вихревой аномалии" + ru_names = list( + NOMINATIVE = "ядро колоссальной вихревой аномалии", \ + GENITIVE = "ядра колоссальной вихревой аномалии", \ + DATIVE = "ядру колоссальной вихревой аномалии", \ + ACCUSATIVE = "ядро колоссальной вихревой аномалии", \ + INSTRUMENTAL = "ядром колоссальной вихревой аномалии", \ + PREPOSITIONAL = "ядре колоссальной вихревой аномалии" + ) + desc = "Стабилизированное ядро колоссальной ​​вихревой аномалии. У вас стойкое чувство, что его не должно здесь находиться." + icon_state = "core_vortex_t3" + anomaly_type = /obj/effect/anomaly/vortex/tier4 + origin_tech = "engineering=11" + tier = 4 diff --git a/code/modules/anomalies/gen_datums.dm b/code/modules/anomalies/gen_datums.dm new file mode 100644 index 00000000000..5e0ba53f0d7 --- /dev/null +++ b/code/modules/anomalies/gen_datums.dm @@ -0,0 +1,213 @@ +/datum/anomaly_gen_datum + /// The name of the anomaly visible during generation. + var/anomaly_type = "Вы не должны это видеть. Пишите баг-репорт." + /// Object that create anomaly in the place of spawning. + var/anomaly = /obj/effect/old_anomaly + /// The charge required to create this anomaly. + var/req_energy + /// The type of raw material required to generate the anomaly. + var/req_item = "Плюшевая игрушка акулы" + +/datum/anomaly_gen_datum/proc/is_req_item(obj/item/I) + return FALSE + +/datum/anomaly_gen_datum/proc/get_useful(list/obj/item/containment) + var/list/useful = list() + for(var/I in containment) + if(!is_req_item(I)) + continue + + useful.Add(I) + + return useful + +// If not enough, always return empty list. +/datum/anomaly_gen_datum/proc/get_used(list/obj/item/containment) + var/list/useful = get_useful(containment) + if(!useful.len) + return list() + + return list(useful[1]) + +/datum/anomaly_gen_datum/proc/is_ok_in_range(turf/center, range) + if(!center) + return + + for(var/turf/T in range(center, range)) + if(!T.is_safe()) + return FALSE + + return TRUE + +/datum/anomaly_gen_datum/proc/is_possible_turf(turf/T) + return !is_ok_in_range(T, 2) + +/datum/anomaly_gen_datum/proc/generate(list/containment, obj/item/radio/beacon/beacon, range = 100, use_items = TRUE) + var/list/used = list() + if(use_items) + used = get_used(containment) + if(!used.len && !istype(src, /datum/anomaly_gen_datum/tier1)) + return FALSE + + var/turf/choosen + for(var/i = 0; i < 100; ++i) + var/turf/bturf = get_turf(beacon) + var/try_x = bturf.x + rand(-range, range) + var/try_y = bturf.y + rand(-range, range) + try_x = clamp(try_x, 1, world.maxx) + try_y = clamp(try_y, 1, world.maxy) + + var/turf/option = get_turf(locate(try_x, try_y, bturf.z)) + if(is_possible_turf(option)) + choosen = option + break + + if(!choosen) + return FALSE + + return spawn_anomaly(choosen, used, containment) + +/datum/anomaly_gen_datum/proc/spawn_anomaly(turf/T, list/used, list/containment) + for(var/I in used) + containment.Remove(I) + qdel(used[I]) + + new anomaly(T) + return TRUE + + +//==================================== TIER 1 =========================================== + +/datum/anomaly_gen_datum/tier1 + req_energy = 1e6 + +/datum/anomaly_gen_datum/tier1/pyroclastic + anomaly_type = "малая атмосферная" + anomaly = /obj/effect/anomaly/atmospheric/tier1 + req_item = "-" + +/datum/anomaly_gen_datum/tier1/bluespace + anomaly_type = "малая блюспейс" + anomaly = /obj/effect/anomaly/bluespace/tier1 + req_item = "-" + +/datum/anomaly_gen_datum/tier1/vortex + anomaly_type = "малая вихревая" + anomaly = /obj/effect/anomaly/vortex/tier1 + req_item = "-" + +/datum/anomaly_gen_datum/tier1/gravitational + anomaly_type = "малая гравитационная" + anomaly = /obj/effect/anomaly/gravitational/tier1 + req_item = "-" + +/datum/anomaly_gen_datum/tier1/energetic + anomaly_type = "малая энергетическая" + anomaly = /obj/effect/anomaly/energetic/tier1 + req_item = "-" + +//==================================== TIER 2 =========================================== + +/datum/anomaly_gen_datum/tier2 + req_energy = 1e7 + +/datum/anomaly_gen_datum/tier2/pyroclastic + anomaly_type = "атмосферная" + anomaly = /obj/effect/anomaly/atmospheric/tier2 + req_item = "Ядро малой атмосферной аноамлии" + +/datum/anomaly_gen_datum/tier2/pyroclastic/is_req_item(obj/item/I) + return istype(I, /obj/item/assembly/signaler/core/atmospheric/tier1) + + +/datum/anomaly_gen_datum/tier2/bluespace + anomaly_type = "блюспейс" + anomaly = /obj/effect/anomaly/bluespace/tier2 + req_item = "Ядро малой блюспейс аномалии" + +/datum/anomaly_gen_datum/tier2/bluespace/is_req_item(obj/item/I) + return istype(I, /obj/item/assembly/signaler/core/bluespace/tier1) + + +/datum/anomaly_gen_datum/tier2/vortex + anomaly_type = "вихревая" + anomaly = /obj/effect/anomaly/vortex/tier2 + req_item = "Ядро малой вихревой аномалии" + +/datum/anomaly_gen_datum/tier2/vortex/is_req_item(obj/item/I) + return istype(I, /obj/item/assembly/signaler/core/vortex/tier1) + + +/datum/anomaly_gen_datum/tier2/gravitational + anomaly_type = "гравитационная" + anomaly = /obj/effect/anomaly/gravitational/tier2 + req_item = "Ядро малой гравитационной аномалии" + +/datum/anomaly_gen_datum/tier2/gravitational/is_req_item(obj/item/I) + return istype(I, /obj/item/assembly/signaler/core/gravitational/tier1) + + +/datum/anomaly_gen_datum/tier2/energetic + anomaly_type = "энергетическая" + anomaly = /obj/effect/anomaly/energetic/tier2 + req_item = "Ядро малой энергетической аномалии" + +/datum/anomaly_gen_datum/tier2/energetic/is_req_item(obj/item/I) + return istype(I, /obj/item/assembly/signaler/core/energetic/tier1) + + +//==================================== TIER 3 =========================================== + +/datum/anomaly_gen_datum/tier3 + req_energy = 5e7 + +/datum/anomaly_gen_datum/tier3/get_used(list/obj/item/containment) + var/list/useful = get_useful(containment) + if(useful.len < 2) + return list() + + return list(useful[1], useful[2]) + +/datum/anomaly_gen_datum/tier3/pyroclastic + anomaly_type = "большая атмосферная" + anomaly = /obj/effect/anomaly/atmospheric/tier3 + req_item = "Два ядра атмосферных аномалий" + +/datum/anomaly_gen_datum/tier3/pyroclastic/is_req_item(obj/item/I) + return istype(I, /obj/item/assembly/signaler/core/atmospheric/tier2) + + +/datum/anomaly_gen_datum/tier3/bluespace + anomaly_type = "большая блюспейс" + anomaly = /obj/effect/anomaly/bluespace/tier3 + req_item = "Два ядра блюспейс аномалий" + +/datum/anomaly_gen_datum/tier3/bluespace/is_req_item(obj/item/I) + return istype(I, /obj/item/assembly/signaler/core/bluespace/tier2) + + +/datum/anomaly_gen_datum/tier3/vortex + anomaly_type = "большая вихревая" + anomaly = /obj/effect/anomaly/vortex/tier3 + req_item = "Два ядра вихревых аномалий" + +/datum/anomaly_gen_datum/tier3/vortex/is_req_item(obj/item/I) + return istype(I, /obj/item/assembly/signaler/core/vortex/tier2) + + +/datum/anomaly_gen_datum/tier3/gravitational + anomaly_type = "большая гравитационная" + anomaly = /obj/effect/anomaly/gravitational/tier3 + req_item = "Два ядра гравитационных аномалий" + +/datum/anomaly_gen_datum/tier3/gravitational/is_req_item(obj/item/I) + return istype(I, /obj/item/assembly/signaler/core/gravitational/tier2) + + +/datum/anomaly_gen_datum/tier3/energetic + anomaly_type = "большая энергетическая" + anomaly = /obj/effect/anomaly/energetic/tier3 + req_item = "Два ядра энергетических аномалий" + +/datum/anomaly_gen_datum/tier3/energetic/is_req_item(obj/item/I) + return istype(I, /obj/item/assembly/signaler/core/energetic/tier2) diff --git a/code/modules/anomalies/impulses/atmosferics.dm b/code/modules/anomalies/impulses/atmosferics.dm new file mode 100644 index 00000000000..77d1e3686ad --- /dev/null +++ b/code/modules/anomalies/impulses/atmosferics.dm @@ -0,0 +1,213 @@ +/datum/anomaly_impulse/random_temp + name = "Температурная дестабилизация" + desc = "Аномалия случайно меняет температуру окружающих ее газов, вызывая перепады давления." + /// Minimum delta of temperature + var/temp_delta_low = 0 + /// Maximum delta of temperature. + var/temp_delta_high = 0 + /// Minimum range of effect. + var/range_low = 0 + /// Maximum range of effect. + var/range_high = 0 + +/datum/anomaly_impulse/random_temp/impulse() + . = ..() + for(var/turf/simulated/T in view(scale_by_strenght(range_low, range_high), owner)) + T.air.temperature += max(0, rand(temp_delta_low, temp_delta_high)) + +/datum/anomaly_impulse/random_temp/tier1 + period_low = 15 SECONDS + period_high = 45 SECONDS + temp_delta_low = -100 + temp_delta_high = 100 + range_low = 1 + range_high = 2 + +/datum/anomaly_impulse/random_temp/tier2 + period_low = 15 SECONDS + period_high = 45 SECONDS + temp_delta_low = -150 + temp_delta_high = 150 + range_low = 1 + range_high = 3 + +/datum/anomaly_impulse/random_temp/tier3 + period_low = 15 SECONDS + period_high = 45 SECONDS + temp_delta_low = -200 + temp_delta_high = 200 + range_low = 2 + range_high = 4 + +/datum/anomaly_impulse/random_temp/tier4 + period_low = 5 SECONDS + period_high = 10 SECONDS + temp_delta_low = -200 + temp_delta_high = 1000 + range_low = 4 + range_high = 7 + +/datum/anomaly_impulse/random_temp/tier4/impulse() + . = ..() + for(var/mob/living/M in view(scale_by_strenght(range_low, range_high), owner)) + M.IgniteMob() + +/datum/anomaly_impulse/freese + name = "Заморозка" + desc = "Аномалия выпускает водяной пар понижает температуру окружающей среды, что приводит к образованию льда на полу." + /// Minimum range of effect. + var/range_low = 0 + /// Maximum range of effect. + var/range_high = 0 + +/datum/anomaly_impulse/freese/impulse() + . = ..() + for(var/turf/simulated/T in view(scale_by_strenght(range_low, range_high) * 2, owner)) + if(T.air) + T.air.temperature = rand(0, 50) + + for(var/turf/simulated/floor/T in spiral_range_turfs(scale_by_strenght(range_low, range_high), owner)) + if(prob(100 - get_dist(T, owner) * 5)) + T.MakeSlippery(TURF_WET_ICE, 120 SECONDS) + +/datum/anomaly_impulse/freese/tier1 + period_low = 15 SECONDS + period_low = 45 SECONDS + range_low = 1 + range_high = 2 + +/datum/anomaly_impulse/freese/tier2 + period_low = 15 SECONDS + period_low = 45 SECONDS + range_low = 2 + range_high = 3 + +/datum/anomaly_impulse/freese/tier3 + period_low = 15 SECONDS + period_low = 45 SECONDS + range_low = 2 + range_high = 4 + +/datum/anomaly_impulse/freese/tier4 + period_low = 5 SECONDS + period_low = 15 SECONDS + range_low = 4 + range_high = 7 + +/datum/anomaly_impulse/freese/tier4/impulse() + . = ..() + for(var/mob/living/M in view(7, owner)) + M.adjust_bodytemperature(-100) + M.apply_status_effect(/datum/status_effect/freon) + if(ishuman(M)) + M.reagents.add_reagent("frostoil", 15) + +/datum/anomaly_impulse/fire + name = "Пожар" + desc = "Аномалия создает вокруг себя нагретую горючую смесь плазмы и кислорода." + /// Minimum range of effect. + var/range_low = 0 + /// Maximum range of effect. + var/range_high = 0 + /// Minimum generated amount of gases. + var/gases_low = 0 + /// Maximum generated amount of gases. + var/gases_high = 0 + +/datum/anomaly_impulse/fire/impulse() + . = ..() + for(var/turf/simulated/T in view(scale_by_strenght(range_low, range_high), owner)) + var/gases_amount = scale_by_strenght(gases_low, gases_high) + T.atmos_spawn_air(LINDA_SPAWN_OXYGEN, gases_amount * 2/7) + T.atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, gases_amount * 5/7) + +/datum/anomaly_impulse/fire/tier1 + period_low = 15 SECONDS + period_high = 45 SECONDS + range_low = 1 + range_high = 2 + gases_low = 0 + gases_high = 5 + +/datum/anomaly_impulse/fire/tier2 + period_low = 10 SECONDS + period_high = 35 SECONDS + range_low = 1 + range_high = 2 + gases_low = 0 + gases_high = 7 + +/datum/anomaly_impulse/fire/tier3 + period_low = 5 SECONDS + period_high = 20 SECONDS + range_low = 1 + range_high = 3 + gases_low = 0 + gases_high = 7 + +/datum/anomaly_impulse/fire/tier4 + period_low = 3 SECONDS + period_high = 5 SECONDS + range_low = 3 + range_high = 7 + gases_low = 7 + gases_high = 14 + +// TIER 4 ONLY + +/datum/anomaly_impulse/dist_fire + name = "Пожар на расстоянии" + desc = "Аномалия создает в нескольких точках вокруг себя нагретую горючую смесь плазмы и кислорода." + /// Minimum range of effect. + var/range_low = 7 + /// Maximum range of effect. + var/range_high = 15 + /// Minimum generated amount of gases. + var/gases_low = 300 + /// Maximum generated amount of gases. + var/gases_high = 600 + /// Minimum number of fire spawns. + var/count_low = 3 + /// Maximum number of fire spawns. + var/count_high = 10 + +/datum/anomaly_impulse/dist_fire/impulse() + . = ..() + var/radius = scale_by_strenght(range_low, range_high) + var/turf/start = get_turf(owner) + var/gases_amount = scale_by_strenght(gases_low, gases_high) + for(var/i = 0 to scale_by_strenght(count_low, count_high)) + var/try_x = start.x + rand(-radius, radius) + var/try_y = start.y + rand(-radius, radius) + try_x = clamp(try_x, 1, world.maxx) + try_y = clamp(try_y, 1, world.maxy) + var/turf/simulated/spawn_pos = get_turf(locate(try_x, try_y, start.z)) + spawn_pos.atmos_spawn_air(LINDA_SPAWN_OXYGEN, gases_amount * 2/7) + spawn_pos.atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, gases_amount * 5/7) + + +/datum/anomaly_impulse/atmosfastmove + name = "Рывок" + desc = "Аномалия быстро двигается в определенном направлении сжигая все на своем пути." + stability_high = 60 + period_low = 10 SECONDS + period_high = 20 SECONDS + /// Minimum range of effect. + var/range_low = 7 + /// Maximum range of effect. + var/range_high = 21 + /// Minimum generated amount of gases. + var/gases_low = 50 + /// Maximum generated amount of gases. + var/gases_high = 150 + +/datum/anomaly_impulse/atmosfastmove/impulse() + . = ..() + var/dir = pick(GLOB.alldirs) + var/gases_amount = scale_by_strenght(gases_low, gases_high) + for(var/i = 0 to scale_by_strenght(range_low, range_high)) + owner.do_move(dir) + var/turf/simulated/spawn_pos = get_turf(owner) + spawn_pos.atmos_spawn_air(LINDA_SPAWN_OXYGEN, gases_amount * 2/7) + spawn_pos.atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, gases_amount * 5/7) + sleep(2) diff --git a/code/modules/anomalies/impulses/bluespace.dm b/code/modules/anomalies/impulses/bluespace.dm new file mode 100644 index 00000000000..c7e89b45f45 --- /dev/null +++ b/code/modules/anomalies/impulses/bluespace.dm @@ -0,0 +1,199 @@ +/datum/anomaly_impulse/move/bs_selftp + name = "Пространственный сдвиг" + desc = "Аномалия перемещается из одной точки в другую без пересечения физического пространства между ними. \ + Уровень стабильности аномалии выше 60 полностью убирает данный вид импульсов." + stability_high = 60 + do_shake = FALSE + /// Minimum range of teleportation. + var/tp_range_low = -1 + /// Maximum range of teleportation. + var/tp_range_high = -1 + +/datum/anomaly_impulse/move/bs_selftp/impulse() + owner.matr.Scale(0.1, 0.1) + animate(owner, transform = owner.matr, time = 0.5 SECONDS, alpha = 0, flags = ANIMATION_PARALLEL) + + sleep(0.5 SECONDS) + var/obj/effect/anomaly/bluespace/anomaly = owner + anomaly.teleport(owner, scale_by_strenght(tp_range_low, tp_range_high)) + + owner.matr.Scale(10, 10) + animate(owner, transform = owner.matr, time = 0.5 SECONDS, alpha = 255) + +/datum/anomaly_impulse/move/bs_selftp/tier1 + period_low = 3 SECONDS + period_high = 5 SECONDS + tp_range_low = 1 + tp_range_high = 2 + +/datum/anomaly_impulse/move/bs_selftp/tier2 + period_low = 5 SECONDS + period_high = 10 SECONDS + tp_range_low = 1 + tp_range_high = 4 + +/datum/anomaly_impulse/move/bs_selftp/tier3 + period_low = 5 SECONDS + period_high = 10 SECONDS + tp_range_low = 2 + tp_range_high = 6 + +/datum/anomaly_impulse/move/bs_selftp/tier4 + period_low = 2 SECONDS + period_high = 4 SECONDS + tp_range_low = 5 + tp_range_high = 11 + + +/datum/anomaly_impulse/bs_tp_other + name = "Всплеск телепортаций" + desc = "Аномалия мгновенно меняет местоположение окружающих объектов не прикладывая к ним силу в процессе." + /// Minimum range of teleportation. + var/tp_range_low = -1 + /// Maximum range of teleportation. + var/tp_range_high = -1 + +/datum/anomaly_impulse/bs_tp_other/impulse() + var/obj/effect/anomaly/bluespace/anomaly = owner + var/tp_range = scale_by_strenght(tp_range_low, tp_range_high) + for(var/atom/movable/atom in view(tp_range, owner)) + if(atom != owner) + anomaly.teleport(atom, tp_range) + +// Not for tier 1 + +/datum/anomaly_impulse/bs_tp_other/tier2 + period_low = 5 SECONDS + period_high = 10 SECONDS + tp_range_low = 1 + tp_range_high = 3 + +/datum/anomaly_impulse/bs_tp_other/tier3 + period_low = 3 SECONDS + period_high = 10 SECONDS + tp_range_low = 2 + tp_range_high = 6 + + +/datum/anomaly_impulse/wormholes + name = "Генерация червоточин" + desc = "Аномалия временно дестабилизирует окружающее пространство, создавая несколько червоточин." + /// Minimum range of teleportation. + var/effect_range_low = -1 + /// Maximum range of teleportation. + var/effect_range_high = -1 + /// Minimum number of wormholes created. + var/wormholes_num_low = 0 + /// Maximum number of wormholes created. + var/wormholes_num_high = 0 + /// Minimum lifetime of wormholes. + var/wormholes_time_low = 0 + /// Maximum lifetime of wormholes. + var/wormholes_time_high = 0 + /// List of currently existing wormholes. Needed for simultaneous deletion. + var/list/wormholes = list() + +/datum/anomaly_impulse/wormholes/impulse() + var/radius = scale_by_strenght(effect_range_low, effect_range_high) + var/list/possible_turfs = list() + for(var/turf/T in range(radius, owner)) + possible_turfs.Add(T) + + var/number_of_wormholes = scale_by_strenght(wormholes_num_low, wormholes_num_high) + for(var/i in 1 to number_of_wormholes) + var/turf/anomaly_turf = pick_n_take(possible_turfs) + if(anomaly_turf) + wormholes.Add(new /obj/effect/portal/wormhole/anomaly(anomaly_turf, null, null, -1, null, TRUE, wormholes)) + + addtimer(CALLBACK(src, PROC_REF(end)), scale_by_strenght(wormholes_time_low, wormholes_time_high)) + +/datum/anomaly_impulse/wormholes/proc/end() + QDEL_LIST(wormholes) + +// Not for tier 1 + +/datum/anomaly_impulse/wormholes/tier2 + period_low = 10 SECONDS + period_high = 30 SECONDS + effect_range_low = 2 + effect_range_high = 3 + wormholes_num_low = 2 + wormholes_num_high = 5 + wormholes_time_low = 3 SECONDS + wormholes_time_high = 7 SECONDS + +/datum/anomaly_impulse/wormholes/tier3 + period_low = 5 SECONDS + period_high = 20 SECONDS + effect_range_low = 3 + effect_range_high = 4 + wormholes_num_low = 5 + wormholes_num_high = 10 + wormholes_time_low = 3 SECONDS + wormholes_time_high = 5 SECONDS + +/datum/anomaly_impulse/wormholes/tier4 + period_low = 5 SECONDS + period_high = 10 SECONDS + effect_range_low = 7 + effect_range_high = 15 + wormholes_num_low = 10 + wormholes_num_high = 20 + wormholes_time_low = 10 SECONDS + wormholes_time_high = 15 SECONDS + + +// Tier 4 only + +/datum/anomaly_impulse/bs_tp_other_t4 + name = "Всплеск телепортаций" + desc = "Аномалия мгновенно меняет местоположение окружающих объектов не прикладывая к ним силу в процессе." + period_low = 3 SECONDS + period_high = 5 SECONDS + +/datum/anomaly_impulse/bs_tp_other_t4/impulse() + var/list/turf/turfs = list() + var/tp_range = scale_by_strenght(5, 10) + for(var/turf/simulated/T in range(tp_range, owner)) + turfs.Add(T) + + // swaps + for(var/i = 1; i <= rand(20, 30); ++i) + var/turf/T1 = pick(turfs) + var/turf/T2 = pick(turfs) + + var/dir1 = T1.dir + var/icon_state1 = T1.icon_state + var/icon1 = T1.icon + T2.dir = dir1 + T2.icon = icon1 + T2.icon_state = icon_state1 + + var/list/C1 = list() + for(var/atom/movable/A in T1) + C1.Add(A) + + var/list/C2 = list() + for(var/atom/movable/A in T2) + C2.Add(A) + + for(var/atom/movable/A in C1) + A.forceMove(T2) + + for(var/atom/movable/A in C2) + A.forceMove(T2) + + C1 = list() + C2 = list() + for(var/V in T1.vars) + if(!(V in list("type", "loc", "locs", "vars", "parent", "parent_type", "verbs", "ckey", "key", "x", "y", "z", "destination_z", "destination_x", "destination_y", "contents", "luminosity", "group"))) + C1[V] = T1.vars[V] + + for(var/V in T2.vars) + if(!(V in list("type", "loc", "locs", "vars", "parent", "parent_type", "verbs", "ckey", "key", "x", "y", "z", "destination_z", "destination_x", "destination_y", "contents", "luminosity", "group"))) + C2[V] = T2.vars[V] + + var/type1 = T1.type + var/type2 = T2.type + T2.ChangeTurf(type1) + T1.ChangeTurf(type2) diff --git a/code/modules/anomalies/impulses/energetic.dm b/code/modules/anomalies/impulses/energetic.dm new file mode 100644 index 00000000000..d1b6af8acf2 --- /dev/null +++ b/code/modules/anomalies/impulses/energetic.dm @@ -0,0 +1,161 @@ +/datum/anomaly_impulse/move/energ_fastmove + name = "Рывок" + desc = "Аномалия совершает несколько коротких прыжков в случайном направлении. \ + В процессе прыжков аномалия игнорирует любые перпятствия. \ + Уровень стабильности аномалии выше 60 полностью убирает данный вид импульсов." + stability_high = 60 + do_shake = FALSE + /// Minimum number of jumps. + var/jumps_low = 0 + /// Maximum number of jumps. + var/jumps_high = 0 + +/datum/anomaly_impulse/move/energ_fastmove/impulse() + var/obj/effect/anomaly/energetic/anomaly = owner + + var/list/turf/possible_targets = list() + var/jumps = scale_by_strenght(jumps_low, jumps_high) + for(var/turf/T in orange(jumps, owner)) + if(get_dist(owner, T) == jumps) + possible_targets.Add(T) + + var/turf/target = pick(possible_targets) + if(!target) + return + + for(var/i = 1; i <= jumps; ++i) + var/cur_dir = get_dir(anomaly, target) + anomaly.jump(get_step(owner, cur_dir)) + anomaly.after_move() + sleep(2) + +/datum/anomaly_impulse/move/energ_fastmove/tier1 + period_low = 5 SECONDS + period_high = 20 SECONDS + jumps_low = 3 + jumps_high = 5 + +/datum/anomaly_impulse/move/energ_fastmove/tier2 + period_low = 10 SECONDS + period_high = 30 SECONDS + jumps_low = 3 + jumps_high = 7 + +/datum/anomaly_impulse/move/energ_fastmove/tier3 + period_low = 20 SECONDS + period_high = 40 SECONDS + jumps_low = 3 + jumps_high = 11 + +/datum/anomaly_impulse/move/energ_fastmove/tier4 + period_low = 10 SECONDS + period_high = 15 SECONDS + jumps_low = 10 + jumps_high = 20 + +/datum/anomaly_impulse/energ_shock_ex + name = "Удар током" + desc = "Аномалия бьет окружающих живых существ током." + /// Minimum range of shock. + var/effect_range_low = 0 + /// Maximum range of shock. + var/effect_range_high = 0 + /// Minimum damage of shock. + var/shock_damage_low = 0 + /// Maximum damage of shock. + var/shock_damage_high = 0 + +/datum/anomaly_impulse/energ_shock_ex/impulse() + var/radius = scale_by_strenght(effect_range_low, effect_range_high) + var/damage = scale_by_strenght(shock_damage_low, shock_damage_high) + owner.do_shock_ex(radius, damage, TRUE) + +/datum/anomaly_impulse/energ_shock_ex/tier1 + period_low = 15 SECONDS + period_high = 45 SECONDS + effect_range_low = 1 + effect_range_high = 3 + shock_damage_low = 5 + shock_damage_high = 10 + +/datum/anomaly_impulse/energ_shock_ex/tier2 + period_low = 10 SECONDS + period_high = 20 SECONDS + effect_range_low = 1 + effect_range_high = 4 + shock_damage_low = 10 + shock_damage_high = 30 + +/datum/anomaly_impulse/energ_shock_ex/tier3 + period_low = 5 SECONDS + period_high = 10 SECONDS + effect_range_low = 3 + effect_range_high = 7 + shock_damage_low = 30 + shock_damage_high = 70 + +/datum/anomaly_impulse/energ_shock_ex/tier4 + period_low = 2 SECONDS + period_high = 5 SECONDS + effect_range_low = 5 + effect_range_high = 13 + shock_damage_low = 120 + shock_damage_high = 150 + + +/datum/anomaly_impulse/move/machinery_jump + name = "Перемещение по машинерии" + desc = "Аномалия прыгает по энергосети к ближайшей машинерии. \ + Уровень стабильности аномалии выше 60 полностью убирает данный вид импульсов." + do_shake = FALSE + stability_high = 60 + /// Minimum damage that machinery takes when teleports. + var/damage_low = 0 + /// Maximum damage that machinery takes when teleports. + var/damage_high = 0 + +/datum/anomaly_impulse/move/machinery_jump/impulse() + var/obj/effect/anomaly/energetic/anomaly = owner + anomaly.jump_to_machinery(scale_by_strenght(damage_low, damage_high)) + +/datum/anomaly_impulse/move/machinery_jump/tier1 + period_low = 15 SECONDS + period_high = 45 SECONDS + damage_low = 20 + damage_high = 40 + +/datum/anomaly_impulse/move/machinery_jump/tier2 + period_low = 10 SECONDS + period_high = 25 SECONDS + damage_low = 40 + damage_high = 60 + +/datum/anomaly_impulse/move/machinery_jump/tier3 + period_low = 5 SECONDS + period_high = 15 SECONDS + damage_low = 60 + damage_high = 80 + +/datum/anomaly_impulse/move/machinery_jump/tier4 + period_low = 3 SECONDS + period_high = 5 SECONDS + damage_low = 300 + damage_high = 500 + +/// Tier 4 only + +/datum/anomaly_impulse/move/machinery_destroy + name = "Репродукция" + desc = "Аномалия собирает большой объем энергии в случайной машинерии неподалеку. \ + Машинерия разрушается и из нее появляется новая малая энергетическая аномалия." + period_low = 5 SECONDS + period_high = 15 SECONDS + +/datum/anomaly_impulse/move/machinery_destroy/impulse() + . = ..() + for(var/obj/machinery/M in range(10, owner)) + explosion(get_turf(M), -1, 1, 2, cause = "machinery_destroy impulse") + new /obj/effect/anomaly/energetic/tier1(get_turf(M)) + qdel(M) + if(prob(30)) + break diff --git a/code/modules/anomalies/impulses/gravitational.dm b/code/modules/anomalies/impulses/gravitational.dm new file mode 100644 index 00000000000..f9b4e609971 --- /dev/null +++ b/code/modules/anomalies/impulses/gravitational.dm @@ -0,0 +1,135 @@ +/datum/anomaly_impulse/change_grav + name = "Гравитационная дестабилизация" + desc = "Аномалия временно дестабилизирует работу гравитации относительно нескольких окружающих ее объектов." + /// Minimum radius of effect. + var/effect_radius_low = 0 + /// Maximum radius of effect. + var/effect_radius_high = 0 + +/datum/anomaly_impulse/change_grav/impulse() + var/obj/effect/anomaly/gravitational/anomaly = owner + for(var/atom/movable/A in view(scale_by_strenght(effect_radius_low, effect_radius_high), owner)) + if(!iseffect(A)) + anomaly.random_gravity_change(A) + +/datum/anomaly_impulse/change_grav/tier1 + period_low = 5 SECONDS + period_high = 10 SECONDS + effect_radius_low = 1 + effect_radius_high = 2 + +/datum/anomaly_impulse/change_grav/tier2 + period_low = 5 SECONDS + period_high = 10 SECONDS + effect_radius_low = 1 + effect_radius_high = 3 + +/datum/anomaly_impulse/change_grav/tier3 + period_low = 5 SECONDS + period_high = 20 SECONDS + effect_radius_low = 2 + effect_radius_high = 4 + +/datum/anomaly_impulse/change_grav/tier4 + period_low = 5 SECONDS + period_high = 10 SECONDS + effect_radius_low = 6 + effect_radius_high = 10 + +/datum/anomaly_impulse/random_throws + name = "Гравитационный всплеск" + desc = "Аномалия случайно раскидывает окружающие ее объекты." + /// Minimum radius of effect. + var/effect_radius_low = 0 + /// Maximum radius of effect. + var/effect_radius_high = 0 + /// Minimum radius of throwing. + var/throw_range_low = 0 + /// Maximum radius of throwing. + var/throw_range_high = 0 + /// Minimum speed of throwing. + var/throw_speed_low = 0 + /// Maximum speed of throwing. + var/throw_speed_high = 0 + +/datum/anomaly_impulse/random_throws/impulse() + var/ost_atoms = 100 + for(var/atom/movable/A in view(scale_by_strenght(effect_radius_low, effect_radius_high), owner)) + if(isobserver(A)) + continue + + if(!A.anchored) + A.random_throw(throw_range_low, throw_range_high, scale_by_strenght(throw_speed_low, throw_speed_high)) + ost_atoms-- + + if(!ost_atoms) + break + +/datum/anomaly_impulse/random_throws/tier1 + period_low = 10 SECONDS + period_high = 20 SECONDS + effect_radius_low = 1 + effect_radius_high = 2 + throw_range_low = 2 + throw_range_high = 3 + throw_speed_low = 3 + throw_speed_high = 4 + +/datum/anomaly_impulse/random_throws/tier2 + period_low = 10 SECONDS + period_high = 15 SECONDS + effect_radius_low = 1 + effect_radius_high = 3 + throw_range_low = 2 + throw_range_high = 5 + throw_speed_low = 3 + throw_speed_high = 5 + +/datum/anomaly_impulse/random_throws/tier3 + period_low = 5 SECONDS + period_high = 10 SECONDS + effect_radius_low = 2 + effect_radius_high = 4 + throw_range_low = 3 + throw_range_high = 8 + throw_speed_low = 4 + throw_speed_high = 7 + +/datum/anomaly_impulse/random_throws/tier4 + period_low = 3 SECONDS + period_high = 5 SECONDS + effect_radius_low = 5 + effect_radius_high = 13 + throw_range_low = 5 + throw_range_high = 20 + throw_speed_low = 4 + throw_speed_high = 7 + + +// Tier 4 only + +/datum/anomaly_impulse/grav_fastmove + name = "Рывок" + desc = "Аномалия быстро двигается в случайном направлении сметая все на своем пути." + period_low = 15 SECONDS + period_high = 20 SECONDS + /// Minimum radius of effect. + var/range_low = 10 + /// Maximum radius of effect. + var/range_high = 20 + +/datum/anomaly_impulse/grav_fastmove/impulse() + var/dir = pick(GLOB.alldirs) + for(var/i = 1 to scale_by_strenght(range_low, range_high)) + owner.do_move(dir) + var/ost_atoms = 100 + for(var/atom/movable/A in view(3, owner)) + if(isobserver(A)) + continue + + A.random_throw(15, 20, 6) + ost_atoms-- + if(!ost_atoms) + break + + sleep(2) diff --git a/code/modules/anomalies/impulses/impulse.dm b/code/modules/anomalies/impulses/impulse.dm new file mode 100644 index 00000000000..d30c755d900 --- /dev/null +++ b/code/modules/anomalies/impulses/impulse.dm @@ -0,0 +1,37 @@ +/datum/anomaly_impulse + /// Name of this type of impulse. Used for scanning anomalies. + var/name = "" + /// Description of the effects of this type of pulses. Used when scanning anomalies. + var/desc = "" + /// Mimimum time between bursts of this type of impulse. + var/period_low = -1 + /// Maximum time between bursts of this type of impulse. + var/period_high = -1 + /// Maximum lewel of stability when impulse can activate. + var/stability_high = 100 + /// If TRUE, anomaly will shake when this impulse activates. + var/do_shake = TRUE + /// The object emitting the pulse. If it does not exist, the pulse should be removed. + var/obj/effect/anomaly/owner = null + +/datum/anomaly_impulse/New(var/owner) + . = ..() + src.owner = owner + +/datum/anomaly_impulse/proc/impulse_cycle() + if(QDELETED(owner)) + return FALSE + + addtimer(CALLBACK(src, PROC_REF(impulse_cycle)), scale_by_strenght(period_high, period_low)) + if(world.time < owner.move_impulse_moment && istype(src, /datum/anomaly_impulse/move)) + return + + if(owner.stability < stability_high) + impulse() + +/datum/anomaly_impulse/proc/impulse() + if(do_shake) + animate_shake(src) + +/datum/anomaly_impulse/proc/scale_by_strenght(l_val, r_val) + return round(l_val + (r_val - l_val) * owner.get_strenght() / 100) diff --git a/code/modules/anomalies/impulses/vortex.dm b/code/modules/anomalies/impulses/vortex.dm new file mode 100644 index 00000000000..c6419c58c79 --- /dev/null +++ b/code/modules/anomalies/impulses/vortex.dm @@ -0,0 +1,103 @@ +/datum/anomaly_impulse/emp + name = "ЭМИ" + desc = "Аномалия выпускает электромагнитный импульс с эпицентром в аномалии." + /// Minimum radius of the emitted light EMP. + var/light_range_low = 0 + /// Maximum radius of the emitted light EMP. + var/light_range_high = 0 + /// Minimum radius of the emitted heavy EMP. + var/heavy_range_low = 0 + /// Maximum radius of the emitted heavy EMP. + var/heavy_range_high = 0 + +/datum/anomaly_impulse/emp/impulse() + empulse(owner, scale_by_strenght(heavy_range_low, heavy_range_high), scale_by_strenght(light_range_low, light_range_high)) + +/datum/anomaly_impulse/emp/tier1 + period_low = 10 SECONDS + period_high = 20 SECONDS + light_range_low = 0 + light_range_high = 2 + heavy_range_low = 0 + heavy_range_high = 0 + +/datum/anomaly_impulse/emp/tier2 + period_low = 10 SECONDS + period_high = 15 SECONDS + light_range_low = 2 + light_range_high = 4 + heavy_range_low = 0 + heavy_range_high = 1 + +/datum/anomaly_impulse/emp/tier3 + period_low = 5 SECONDS + period_high = 10 SECONDS + light_range_low = 3 + light_range_high = 5 + heavy_range_low = 1 + heavy_range_high = 3 + +/datum/anomaly_impulse/emp/tier4 + period_low = 3 SECONDS + period_high = 5 SECONDS + light_range_low = 10 + light_range_high = 20 + heavy_range_low = 5 + heavy_range_high = 10 + + +/datum/anomaly_impulse/superpull + name = "Всплеск притяжения" + desc = "Способность аномалии притягивать предметы временно усиливается." + /// Minimum amount of pulls. + var/pulls_low = 0 + /// Maximum amount of pulls. + var/pulls_high = 0 + +/datum/anomaly_impulse/superpull/impulse() + var/obj/effect/anomaly/vortex/anomaly = owner + for(var/i = 1 to scale_by_strenght(pulls_low, pulls_high)) + anomaly.do_pulls() + sleep(2) + +/datum/anomaly_impulse/superpull/tier1 + period_low = 10 SECONDS + period_high = 20 SECONDS + pulls_low = 0 + pulls_high = 3 + +/datum/anomaly_impulse/superpull/tier2 + period_low = 10 SECONDS + period_high = 15 SECONDS + pulls_low = 2 + pulls_high = 5 + +/datum/anomaly_impulse/superpull/tier3 + period_low = 5 SECONDS + period_high = 10 SECONDS + pulls_low = 3 + pulls_high = 7 + +/datum/anomaly_impulse/superpull/tier4 + period_low = 5 SECONDS + period_high = 10 SECONDS + pulls_low = 10 + pulls_high = 20 + +// Tier 4 only + +/datum/anomaly_impulse/vortex_fastmove + name = "Рывок" + desc = "Аномалия быстро двигается в случайном направлении сметая все на своем пути." + period_low = 15 SECONDS + period_high = 20 SECONDS + /// Minimum radius of effect. + var/range_low = 10 + /// Maximum radius of effect. + var/range_high = 20 + +/datum/anomaly_impulse/vortex_fastmove/impulse() + var/dir = pick(GLOB.alldirs) + for(var/i = 1 to scale_by_strenght(range_low, range_high)) + owner.do_move(dir) + sleep(2) diff --git a/code/modules/anomalies/items/anomaly_beacon.dm b/code/modules/anomalies/items/anomaly_beacon.dm new file mode 100644 index 00000000000..e3fb38426f2 --- /dev/null +++ b/code/modules/anomalies/items/anomaly_beacon.dm @@ -0,0 +1,62 @@ +/obj/item/assembly/anomaly_beacon + icon = 'icons/obj/weapons/techrelic.dmi' + icon_state = "beacon" + item_state = "beacon" + lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' + righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' + name = "anomaly beacon" + desc = "A device that draws power from bluespace and creates a permanent tracking beacon." + origin_tech = "bluespace=6" + /// Inserted core of anomaly. + var/obj/item/assembly/signaler/core/core = null + +/obj/item/assembly/anomaly_beacon/activate() + if(!core) + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0) + return + + var/datum/anomaly_gen_datum/gen_datum = GLOB.anomaly_types["[core.tier - 1]"][pick(GLOB.anomaly_types["[core.tier - 1]"])] + var/obj/effect/anomaly/anomaly_path = gen_datum.anomaly + var/newAnomaly = new anomaly_path(get_turf(src)) + notify_ghosts("[name] has an object of interest: [newAnomaly]!", title = "Аномалия!", source = newAnomaly, action = NOTIFY_FOLLOW) + qdel(src) + +/obj/item/assembly/anomaly_beacon/attack_self(mob/user) + activate() + +/obj/item/assembly/anomaly_beacon/attackby(obj/item/I, mob/user, params) + if(!(iscore(I) && !iscoreempty(I) && !iscoret1(I))) + return ..() + + if(!user.drop_transfer_item_to_loc(I, src)) + balloon_alert(user, "отпустить невозможно!") + return ATTACK_CHAIN_PROCEED + + var/msg = "ядро вставлено" + if(core) + msg = "ядро заменено" + user.put_in_hands(core) + + core = I + user.balloon_alert(user, msg) + return ATTACK_CHAIN_BLOCKED + +/obj/item/assembly/anomaly_beacon/AltClick(mob/user) + if(!core) + user.balloon_alert(user, "нет ядра") + return + + user.put_in_hands(core) + core = null + user.balloon_alert(user, "ядро извлечено") + +/datum/crafting_recipe/anomaly_beacon + name = "Anomaly beacon" + result = /obj/item/assembly/anomaly_beacon + tools = list(TOOL_SCREWDRIVER) + reqs = list(/obj/item/relict_production/rapid_dupe = 1, + /obj/item/radio/beacon = 1, + /obj/item/stack/cable_coil = 5) + time = 10 SECONDS + category = CAT_WEAPONRY + subcategory = CAT_WEAPON diff --git a/code/modules/anomalies/items/bsg.dm b/code/modules/anomalies/items/bsg.dm new file mode 100644 index 00000000000..e26ac9eca2e --- /dev/null +++ b/code/modules/anomalies/items/bsg.dm @@ -0,0 +1,138 @@ +/obj/item/gun/energy/bsg + name = "Б.С.П" // No \improper because it's russian name. "The Б.С.П." is worse than just "Б.С.П.". + desc = "Большая С*** Пушка. Использует ядро энергетической аномалии и блюспейс кристалл для производства разрушительных взрывов энергии, вдохновленный дивизионом БСА Нанотрейзен." + icon_state = "bsg" + item_state = "bsg" + origin_tech = "combat=6;materials=6;powerstorage=6;bluespace=6;magnets=6" //cutting edge technology, be my guest if you want to deconstruct one instead of use it. + ammo_type = list(/obj/item/ammo_casing/energy/bsg) + weapon_weight = WEAPON_HEAVY + w_class = WEIGHT_CLASS_BULKY + can_holster = FALSE + slot_flags = ITEM_SLOT_BACK + cell_type = /obj/item/stock_parts/cell/bsg + shaded_charge = TRUE + gender = FEMALE + /// Inserted flux anomaly core. + var/obj/item/assembly/signaler/core/energetic/core = null + var/has_bluespace_crystal = FALSE + var/admin_model = FALSE //For the admin gun, prevents crystal shattering, so anyone can use it, and you dont need to carry backup crystals. + +/obj/item/gun/energy/bsg/examine(mob/user) + . = ..() + if(core && has_bluespace_crystal) + . += span_notice("[src] полностью рабочая!") + else if(core) + . += span_warning("Ядро энергетической аномалии присутствует, но не хватает БС кристалла.") + else if(has_bluespace_crystal) + . += span_warning("БС кристалл присутствует, но не хватает ядра энергетической аномалии.") + else + . += span_warning("Не хватает ядра энергетической аномалии и БС кристалла для работы.") + + +/obj/item/gun/energy/bsg/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stack/ore/bluespace_crystal)) + add_fingerprint(user) + var/obj/item/stack/ore/bluespace_crystal/crystal = I + if(has_bluespace_crystal) + balloon_alert(user, "уже установлено!") + return ATTACK_CHAIN_PROCEED + + if(!crystal.use(1)) + balloon_alert(user, "недостаточно кристаллов!") + return ATTACK_CHAIN_PROCEED + + balloon_alert(user, "установлено") + has_bluespace_crystal = TRUE + update_icon(UPDATE_ICON_STATE) + return ATTACK_CHAIN_PROCEED_SUCCESS + + if(iscoreflux(I)) + add_fingerprint(user) + if(core) + balloon_alert(user, "уже установлено!") + return ATTACK_CHAIN_PROCEED + + var/obj/item/assembly/signaler/core/Icore = I + if(Icore.get_strenght() < 140) + balloon_alert(user, "ядро слишком слабо") + return + + if(!user.drop_transfer_item_to_loc(I, src)) + return ..() + + balloon_alert(user, "установлено") + core = I + update_icon(UPDATE_ICON_STATE) + return ATTACK_CHAIN_BLOCKED_ALL + + return ..() + +/obj/item/gun/energy/bsg/AltClick(mob/user) + if(!core) + user.balloon_alert(user, "нет ядра") + return + + user.put_in_active_hand(core) + core = null + user.balloon_alert(user, "ядро извлечено") + +/obj/item/gun/energy/bsg/process_fire(atom/target, mob/living/user, message = TRUE, params, zone_override, bonus_spread = 0) + if(!has_bluespace_crystal) + balloon_alert(user, "нужен блюспейс кристалл") + return + + if(!core) + balloon_alert(user, "нужно ядро аномалии") + return + + if(!chambered && can_shoot(user)) + process_chamber() + + if(!chambered?.BB) + return ..() + + var/obj/item/projectile/energy/bsg/bsg_BB = chambered.BB + bsg_BB.core_strenght = core.get_strenght() + return ..() + + +/obj/item/gun/energy/bsg/update_icon_state() + if(core) + if(has_bluespace_crystal) + icon_state = "bsg_finished" + else + icon_state = "bsg_core" + + else if(has_bluespace_crystal) + icon_state = "bsg_crystal" + else + icon_state = "bsg" + + +/obj/item/gun/energy/bsg/emp_act(severity) + ..() + if(prob(75 / severity)) + if(has_bluespace_crystal) + shatter() + +/obj/item/gun/energy/bsg/proc/shatter() + if(admin_model) + return + + visible_message(span_warning("БС кристалл [src] треснул!")) + playsound(src, 'sound/effects/pylon_shatter.ogg', 50, TRUE) + has_bluespace_crystal = FALSE + update_icon(UPDATE_ICON_STATE) + +/obj/item/gun/energy/bsg/prebuilt + icon_state = "bsg_finished" + has_bluespace_crystal = TRUE + +/obj/item/gun/energy/bsg/prebuilt/Initialize(mapload) + . = ..() + core = TRUE + update_icon(UPDATE_ICON_STATE) + +/obj/item/gun/energy/bsg/prebuilt/admin + desc = "Большая С*** Пушка. Лучшим людям - лучшее творение. У этой версии БС кристалл никогда не треснет, и уже загружено ядро аномалии потока." + admin_model = TRUE diff --git a/code/modules/anomalies/items/experimental_syringe_gun.dm b/code/modules/anomalies/items/experimental_syringe_gun.dm new file mode 100644 index 00000000000..698570c3eb7 --- /dev/null +++ b/code/modules/anomalies/items/experimental_syringe_gun.dm @@ -0,0 +1,179 @@ +/obj/item/gun/syringe/rapidsyringe/experimental + name = "experimental syringe gun" + ru_names = list( + NOMINATIVE = "экспериментальный шприцемет", \ + GENITIVE = "экспериментального шприцемета", \ + DATIVE = "экспериментальному шприцемету", \ + ACCUSATIVE = "экспериментальный шприцемет", \ + INSTRUMENTAL = "экспериментальным шприцеметом", \ + PREPOSITIONAL = "экспериментальном шприцемете" + ) + desc = "Эксперементальный шприцемет с 6 слотами для шприцев, встроенным, самовосполняющимся хранилищем \ + химикатов и новейшей системой автозаправки шприцев. Для смены синтезируемых химикатов залейте новую \ + смесь внутрь. Не может синтезировать некоторые, особенно сложные вещества." + gender = MALE + origin_tech = "combat=3;biotech=4;bluespace=5" + icon = 'icons/obj/weapons/techrelic.dmi' + item_state = "strynggun" + lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' + righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' + icon_state = "strynggun" + materials = list(MAT_METAL=2000, MAT_GLASS=2000, MAT_BLUESPACE=400) + origin_tech = "bluespace=4;biotech=5" + /// Tank with ready reagents. + var/obj/item/reagent_containers/glass/beaker/large/ready_reagents = new + /// A list synthesized reagents. + var/list/synth_reagents = list() + /// The amount of substance synthesized in a cycle. + var/synth_speed = 5 + /// The size of the internal tank with ready-made reagents. + var/bank_size = 100 + /// Inserted vortex anomaly core. + var/obj/item/assembly/signaler/core/vortex/core = null + +/obj/item/gun/syringe/rapidsyringe/experimental/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/item/gun/syringe/rapidsyringe/experimental/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/gun/syringe/rapidsyringe/experimental/proc/update_core() + if(!core) + synth_speed = 0 + + synth_speed = core.get_strenght() / 30 + +/obj/item/gun/syringe/rapidsyringe/experimental/attackby(obj/item/I, mob/user) + if(iscorevortex(I)) + add_fingerprint(user) + var/msg = "ядро вставлено" + if(core) + user.put_in_hands(core) + msg = "ядро заменено" + + if(!user.drop_transfer_item_to_loc(I, src)) + balloon_alert(user, "отпустить невозможно!") + return ATTACK_CHAIN_PROCEED + + core = I + user.balloon_alert(user, msg) + update_core() + return ATTACK_CHAIN_PROCEED + + if(istype(I, /obj/item/reagent_containers/syringe)) + var/in_clip = length(syringes) + (chambered.BB ? 1 : 0) + if(in_clip >= max_syringes) + user.balloon_alert(user, "недостаточно места") + return ATTACK_CHAIN_PROCEED + + if(!user.drop_transfer_item_to_loc(I, src)) + return ..() + + user.balloon_alert(user, "заряжено") + syringes.Add(I) + process_chamber() // Chamber the syringe if none is already + return ATTACK_CHAIN_BLOCKED_ALL + + if(istype(I, /obj/item/reagent_containers/glass)) + if(!core) + user.balloon_alert(user, "нет ядра") + return ..() + + var/obj/item/reagent_containers/glass/RC = I + if (!RC.reagents.reagent_list) + return ..() + + ready_reagents.reagents.clear_reagents() + synth_reagents = list() + RC.reagents.trans_to(ready_reagents, bank_size) + var/synch_reagent_volume = 0 + for(var/datum/reagent/reagent in ready_reagents.reagents.reagent_list) + if(reagent.can_synth) + synch_reagent_volume += reagent.volume + + for(var/datum/reagent/reagent in ready_reagents.reagents.reagent_list) + if(reagent.can_synth) + synth_reagents[reagent.id] = reagent.volume / synch_reagent_volume + + user.balloon_alert(user, "смесь изменена") + return ATTACK_CHAIN_BLOCKED_ALL + + return ..() + +/obj/item/gun/syringe/rapidsyringe/experimental/AltClick(mob/user) + if(!user.contains(src)) + return + + user.put_in_active_hand(core) + core = null + user.balloon_alert(user, "ядро извлечено") + update_core() + +/obj/item/gun/syringe/rapidsyringe/experimental/process() + if(!core) + return + + if(prob(core.get_strenght() / 4)) + syringes.Add(new /obj/item/reagent_containers/syringe) + process_chamber() + + var/synth_volume = min(synth_speed, ready_reagents.reagents.maximum_volume - ready_reagents.reagents.total_volume) + for(var/id in synth_reagents) + ready_reagents.reagents.add_reagent(id, synth_reagents[id] * synth_volume) + + if(chambered?.BB) + ready_reagents.reagents.trans_to(chambered.BB, ready_reagents.reagents.total_volume) + + for (var/obj/item/reagent_containers/syringe/S in syringes) + ready_reagents.reagents.trans_to(S, ready_reagents.reagents.total_volume) + +/obj/item/gun/syringe/rapidsyringe/experimental/afterattack(atom/target, mob/living/user, flag, params) + if(!istype(target, /obj/item/reagent_containers/glass)) + return ..() + + var/obj/item/reagent_containers/glass/G = target + ready_reagents.reagents.trans_to(G, ready_reagents.reagents.total_volume) + +/obj/item/gun/syringe/rapidsyringe/experimental/examine(mob/user) + . = ..() + if(!core) + . += span_warning("В [declent_ru(PREPOSITIONAL)] нет ядра!") + else + . += span_info("В [declent_ru(PREPOSITIONAL)] есть ядро.") + + . += span_info("Синтезируемые реагенты:") + for(var/id in synth_reagents) + var/datum/reagent/reagent = GLOB.chemical_reagents_list[id] + . += span_info(" [reagent.name]: [synth_reagents[id] * synth_speed]") + + . += span_info("Готовые реагенты:") + for(var/datum/reagent/reagent in ready_reagents.reagents.reagent_list) + . += span_info(" [reagent.name]: [reagent.volume]") + +/obj/item/gun/syringe/rapidsyringe/experimental/suicide_act(mob/living/carbon/human/user) + if(!core || HAS_TRAIT(user, TRAIT_NO_BLOOD) || !istype(user)) + return ..() + + var/gend_letter = genderize_ru(gender, "е", "е", "е", "ю") + user.visible_message(span_suicide("[user] разреза[gend_letter]т свою руку и подключают систему автозаправки к \ + кровеносной системе! Выглядит будто он[genderize_ru(gender, "", "а", "о", "и")] \ + птыа[genderize_ru(gender, "е", "е", "е", "ю")]тся убить себя!")) + ready_reagents.reagents.trans_to(user, ready_reagents.reagents.total_volume) + user.bleed(user.blood_volume) + return OXYLOSS | BRUTELOSS + +/obj/item/gun/syringe/rapidsyringe/experimental/attack_self(mob/living/user) + return + +/datum/crafting_recipe/rapidsyringe_experimental + name = "Experemintal syringe gun" + result = /obj/item/gun/syringe/rapidsyringe/experimental + tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH) + reqs = list(/obj/item/relict_production/perfect_mix = 1, + /obj/item/gun/syringe/rapidsyringe = 1, + /obj/item/stock_parts/matter_bin = 1) + time = 300 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON diff --git a/code/modules/anomalies/items/fauna_bomb/air_mob.dm b/code/modules/anomalies/items/fauna_bomb/air_mob.dm new file mode 100644 index 00000000000..b8a6e70c738 --- /dev/null +++ b/code/modules/anomalies/items/fauna_bomb/air_mob.dm @@ -0,0 +1,111 @@ +/mob/living/simple_animal/hostile/airmob + gender = FEMALE + healable = FALSE // It would be strange if styptic powder treated compressed air. + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + unsuitable_atmos_damage = 0 + sentience_type = SENTIENCE_OTHER + deathmessage = "распадается." + pressure_resistance = 500 + weather_immunities = list("air_mob") + robust_searching = TRUE + search_objects = TRUE + /// Armor of this mob. Req if mob was made of human with armor. + var/list/armor + /// Fauna bomb that spawned this mob. + var/obj/item/fauna_bomb/spawner + /// Amount of charge that req for this mob. + var/req_charge = 0 + /// Number of scan data. Used to find mob of some type. + var/scan_num = 0 + +/mob/living/simple_animal/hostile/airmob/Initialize(mapload, obj/item/fauna_bomb/spawner, datum/airmob_data/data) + . = ..() + scan_num = data.scan_num + name = data.name + ru_names = data.ru_names + desc = data.desc + maxHealth = data.maxHealth + armor = data.armor + health = data.maxHealth + response_help = data.response_help + response_disarm = data.response_disarm + response_harm = data.response_harm + harm_intent_damage = data.harm_intent_damage + melee_damage_lower = max(5, data.melee_damage_lower) + melee_damage_upper = max(5, data.melee_damage_upper) + obj_damage = data.obj_damage + armour_penetration = data.armour_penetration + friendly = data.friendly + environment_smash = data.environment_smash + speed = data.speed + leash = spawner + leash_radius = round(spawner.core.get_strenght() / 15 + 0.5) + mob_size = data.mob_size + rapid_melee = data.rapid_melee + if(data.appearance) + appearance = data.appearance + + alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + var/turf/simulated/T = get_turf(spawner) + if(istype(T) && T.air.toxins > MOLES_PLASMA_VISIBLE) + can_be_on_fire = TRUE + fire_damage = 20 // Fire kills plasma creatures. + add_atom_colour(COLOR_PINK, FIXED_COLOUR_PRIORITY) + else + color = MATRIX_GREYSCALE + + src.spawner = spawner + req_charge = data.req_charge + +/mob/living/simple_animal/hostile/airmob/getarmor(def_zone, attack_flag = MELEE) + return armor[attack_flag] + +/mob/living/simple_animal/hostile/airmob/death(gibbed) + spawner.use_charge(-req_charge) + spawner.used_charge -= req_charge + spawner.max_charge += req_charge + . = ..() + spawner.created_mobs.Remove(src) + qdel(src) + +/mob/living/simple_animal/hostile/airmob/MeleeAction(patience) + . = ..() + sidestep_per_cycle = rand(1, 2) + +/mob/living/simple_animal/hostile/airmob/PickTarget() + if(spawner.last_command == "attack") + return spawner.current_target + + // We won't attack in go and stop mods. + return + +/mob/living/simple_animal/hostile/airmob/Move(atom/newloc, direct, glide_size_override, update_dir) + if(get_dist(spawner, newloc) + 3 <= leash_radius) // We don't want to risk. + return ..() + +/mob/living/simple_animal/hostile/airmob/proc/do_commands() + if(!spawner.current_target) + return + + if(spawner.last_command == "attack") + FindTarget() + return + + if(spawner.last_command == "go") + Goto(get_turf(spawner?.current_target), move_to_delay, get_dist(src, spawner.current_target) - 1) + return + + if(!spawner.last_command || spawner.last_command == "stop") + LoseTarget() + return + + if(get_dist(src, spawner.current_target) > 7) + Goto(get_turf(spawner.current_target), move_to_delay, get_dist(src, spawner.current_target) - 3) + +/mob/living/simple_animal/hostile/airmob/Life(seconds, times_fired) + . = ..() + do_commands() + if(get_dist(src, spawner) + 4 > leash_radius) + Goto(get_turf(spawner), move_to_delay, 3) + return + diff --git a/code/modules/anomalies/items/fauna_bomb/fauna_bomb.dm b/code/modules/anomalies/items/fauna_bomb/fauna_bomb.dm new file mode 100644 index 00000000000..1aaa459ab9c --- /dev/null +++ b/code/modules/anomalies/items/fauna_bomb/fauna_bomb.dm @@ -0,0 +1,345 @@ +#define MAX_CREATED_MOBS 12 +#define MAX_REMEMBERED_MOBS 12 + +/obj/item/fauna_bomb + name = "fauna bomb" + ru_names = list( + NOMINATIVE = "фаунная бомба", \ + GENITIVE = "фаунной бомбы", \ + DATIVE = "фаунной бомбе", \ + ACCUSATIVE = "фаунную бомбу", \ + INSTRUMENTAL = "фаунной бомбой", \ + PREPOSITIONAL = "фаунной бомбе" + ) + desc = "Эксперементальный прибор, способный создавать и поддерживать плотные копии отсканированных существ, \ + сделанные из окружающих газов. Для работы требует ядро атмосферной аномалии." + gender = FEMALE + w_class = WEIGHT_CLASS_SMALL + icon = 'icons/obj/weapons/techrelic.dmi' + icon_state = "bomb" + item_state = "bomb" + lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' + righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' + origin_tech = "bluespace=4;engineering=5" + /// Inserted atmospheric cnomaly core. + var/obj/item/assembly/signaler/core/atmospheric/core + /// List of saved mob datas. + var/list/datum/airmob_data/datas = list() + /// List of already created and still alive mobs. + var/list/mob/living/simple_animal/hostile/airmob/created_mobs = list() + /// Used level of max_charge. + var/used_charge = 0 + /// Max level of charge, that can be stored. Depends of inserted core and created mobs. + var/max_charge = 0 + /// Current level of charge. + var/charge = 0 + /// Amount of charge that regenerates every SSobj process. + var/charge_speed = 0 + /// Number of all scans. Used for generating keys for datas. + var/scan_num = 1 + /// A mob that currently carries src with it. + var/mob/living/owner = null + /// If true, owner is choosing someone right now. + var/in_choose_mode = FALSE + /// Timer of choosing target. + var/choose_target_timer + /// Current choosen target. + var/atom/current_target = null + /// Last command that was given. (attack/go/stop) null == stop + var/last_command = null + /// Client of somebody, who needs to choose target. + var/client/chooser = null + /// Number of current target choosing. Used to not stop choosing because of multiclicks. + var/cur_choosing = 0 + +/obj/item/fauna_bomb/Destroy() + for(var/mob/living/M in created_mobs) + M.death() + + . = ..() + +/obj/item/fauna_bomb/proc/use_charge(amount) + if(amount > charge) + return FALSE + + var/delta = clamp(amount, -(max_charge - charge), max_charge - charge) + charge -= delta + if(charge == max_charge) + STOP_PROCESSING(SSobj, src) + else + START_PROCESSING(SSobj, src) + + return TRUE + +/obj/item/fauna_bomb/attackby(obj/item/I, mob/user, params) + if(iscoreatmos(I)) + if(!user.drop_transfer_item_to_loc(I, src)) + balloon_alert(user, "отпустить невозможно!") + return ATTACK_CHAIN_PROCEED + + var/msg = "ядро вставлено" + if(core) + msg = "ядро заменено" + user.put_in_hands(core) + + core = I + user.balloon_alert(user, msg) + update_core() + return + + return ..() + +/obj/item/fauna_bomb/process(seconds_per_tick) + use_charge(-charge_speed) + +/obj/item/fauna_bomb/AltClick(mob/user) + if(!core) + user.balloon_alert(user, "нет ядра") + return + + user.put_in_hands(core) + core = null + user.balloon_alert(user, "ядро извлечено") + update_core() + +/obj/item/fauna_bomb/proc/update_core() + charge = 0 + if(!core) + max_charge = 0 + charge_speed = 0 + used_charge = 0 + for(var/mob/living/simple_animal/SA in created_mobs) + SA.death() + + use_charge(0) // Stop charging. + return + + max_charge = core.get_strenght() + charge_speed = max_charge / 75 + var/req_charge = 0 + for(var/mob/living/simple_animal/hostile/airmob/SA in created_mobs) + SA.leash_radius = round(core.get_strenght() / 15 + 0.5) + if (get_dist(src, SA) > SA.leash_radius) + SA.dust() + else + req_charge += SA.req_charge + + while(used_charge > max_charge) + var/mob/living/simple_animal/hostile/airmob/cheapest_mob = null + for(var/mob/living/simple_animal/hostile/airmob/SA in created_mobs) + if(!cheapest_mob || cheapest_mob.req_charge > SA.req_charge) + cheapest_mob = SA + + cheapest_mob.death() + + max_charge -= used_charge + charge = min(charge, max_charge) + use_charge(0) // Start charging. + +/obj/item/fauna_bomb/examine(mob/user) + . = ..() + if(!core) + . += span_warning("В [declent_ru(PREPOSITIONAL)] нет ядра атмосферной аномалии!") + return + + . += span_info("Текущий заряд: [charge != max_charge ? charge : span_boldnotice("[charge]")]/[max_charge + used_charge]") + . += span_info("Свободный заряд: [max_charge != max_charge + used_charge ? max_charge : span_boldnotice("[max_charge]")]/[max_charge + used_charge]") + . += span_info("Скорость восстановления заряда: [charge_speed]") + . += span_info("Проецируется существ: [created_mobs.len != MAX_CREATED_MOBS ? created_mobs.len : span_boldnotice("[created_mobs.len]")]/[MAX_CREATED_MOBS]") + +/obj/item/fauna_bomb/suicide_act(mob/user) + user.visible_message(span_suicide("[user] направляет проецирующую систему [declent_ru(GENITIVE)] себе в рот, \ + выкручивает на максимальную мощность и активирует."), \ + span_suicide("Вы направляете проецирующую систему [declent_ru(GENITIVE)] себе в рот, \ + выкручиваете на максимальную мощность и активируете."), + span_warning("Вы слышите громкий хлопок!")) + user.gib() + return OBLITERATION + +/obj/item/fauna_bomb/afterattack(atom/target, mob/user, proximity, params, status) + if(user.incapacitated() || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) + return ..() + + if(choose_target_timer && user.client == chooser) + choosing_target_off(cur_choosing) + current_target = target + do_commands() + return + + if(!isliving(target) || \ + istype(target, /mob/living/simple_animal/hostile/megafauna/ancient_robot) || \ + istype(target, /mob/living/simple_animal/hostile/airmob)) + return ..() + + if(!proximity) + return ..() + + if(datas.len >= MAX_REMEMBERED_MOBS) + user.balloon_alert(user, "мало памяти") + return + + if(!do_after(user, 0.5 SECONDS, target)) + return + + var/datum/airmob_data/data = new(target, src) + datas["[scan_num]"] = data + data.scan_num = scan_num + scan_num++ + user.balloon_alert(user, "особь просканированна") + +/obj/item/fauna_bomb/attack_self(mob/user) + ui_interact(user) + if(owner == user) + return + + if(owner) + owner.faction -= "fauna_bomb[UID()]" + + user.faction += "fauna_bomb[UID()]" + owner = user + +/obj/item/fauna_bomb/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "FaunaBomb", "Управление проецированием") + ui.open() + +/obj/item/fauna_bomb/ui_data(mob/user) + var/list/data = list() + var/list/scans = list() + for(var/key in datas) + var/datum/airmob_data/scan = datas[key] + scans += list(list( + "name" = scan.normal_name + " ([key])", + "cost" = scan.req_charge, + "health" = scan.maxHealth, + "dmg_low" = scan.melee_damage_lower, + "dmg_high" = scan.melee_damage_upper, + "dmg_obj" = scan.obj_damage, + "icon" = icon2base64(scan.icon), + "index" = scan.scan_num, + )) + + data["scans"] = scans + data["charge"] = charge + data["max_charge"] = max_charge + data["charge_speed"] = charge_speed + data["created_len"] = created_mobs.len + return data + +/obj/item/fauna_bomb/proc/do_commands() + for(var/mob/living/simple_animal/hostile/airmob/M in created_mobs) + M.do_commands() + +/obj/item/fauna_bomb/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + if(..()) + return + + . = TRUE + + switch(action) + if("forget") + var/index = params["index"] + for(var/mob/living/simple_animal/hostile/airmob/SA in created_mobs) + if(SA.scan_num == datas["[index]"].scan_num) + SA.death() + + datas.Remove("[index]") + + if("kill") + var/index = params["index"] + var/mob/living/simple_animal/hostile/airmob/weakest_mob = null + for(var/mob/living/simple_animal/hostile/airmob/SA in created_mobs) + if(SA.scan_num == datas["[index]"].scan_num && (!weakest_mob || weakest_mob.health > SA.health)) + weakest_mob = SA + + if(weakest_mob) + weakest_mob.death() + ui.user.balloon_alert(ui.user, "проекция развеяна") + else + ui.user.balloon_alert(ui.user, "нет таких проекций") + + if("create") + if(created_mobs.len >= MAX_CREATED_MOBS) + ui.user.balloon_alert(ui.user, "превышение нагрузки") + return + + var/index = params["index"] + if(!use_charge(datas["[index]"].req_charge)) + ui.user.balloon_alert(ui.user, "недостаточно энергии") + return + + used_charge += datas["[index]"].req_charge + max_charge -= datas["[index]"].req_charge + var/mob/mob = new /mob/living/simple_animal/hostile/airmob(get_turf(src), src, datas["[index]"]) + created_mobs.Add(mob) + mob.faction = list("fauna_bomb[UID()]") + for(var/j = 1, j <= rand(1, 3), j++) + step(mob, GLOB.cardinal) + + ui.user.balloon_alert(ui.user, "проекция создана") + + if("attack") + last_command = action + choose_target(ui.user.client) + + if("go") + last_command = action + choose_target(ui.user.client) + + if("stop") + current_target = src + last_command = action + do_commands() + + +#define CHOOSING_ICON 'icons/effects/cult_target.dmi' + +/obj/item/fauna_bomb/proc/choose_target(client/client) + cur_choosing++ + choose_target_timer = addtimer(CALLBACK(src, PROC_REF(choosing_target_off), cur_choosing), 3 SECONDS) + chooser = client + in_choose_mode = TRUE + if(chooser?.mouse_pointer_icon == initial(chooser.mouse_pointer_icon)) + chooser.mouse_pointer_icon = CHOOSING_ICON + +/obj/item/fauna_bomb/proc/choosing_target_off(choosing_num) + if(choosing_num != cur_choosing) + return + + choose_target_timer = null + in_choose_mode = FALSE + if(chooser?.mouse_pointer_icon == CHOOSING_ICON) + chooser.mouse_pointer_icon = initial(chooser.mouse_pointer_icon) + +#undef CHOOSING_ICON + +/obj/item/fauna_bomb/attack(mob/living/target, mob/living/user, params, def_zone, skip_attack_anim) + return ATTACK_CHAIN_PROCEED + +/obj/item/fauna_bomb/preloaded/Initialize(mapload) + . = ..() + update_core() + +/obj/item/fauna_bomb/preloaded/t1 + core = new /obj/item/assembly/signaler/core/atmospheric/tier1 + +/obj/item/fauna_bomb/preloaded/t2 + core = new /obj/item/assembly/signaler/core/atmospheric/tier2 + +/obj/item/fauna_bomb/preloaded/t3 + core = new /obj/item/assembly/signaler/core/atmospheric/tier3 + +/datum/crafting_recipe/fauna_bomb + name = "Fauna bomb" + result = /obj/item/fauna_bomb + tools = list(TOOL_SCREWDRIVER) + reqs = list(/obj/item/relict_production/pet_spray = 1, + /obj/item/grenade/chem_grenade/adv_release = 1, + /obj/item/stack/cable_coil = 5) + time = 300 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON + +#undef MAX_CREATED_MOBS +#undef MAX_REMEMBERED_MOBS diff --git a/code/modules/anomalies/items/fauna_bomb/mob_data.dm b/code/modules/anomalies/items/fauna_bomb/mob_data.dm new file mode 100644 index 00000000000..75e08640ad3 --- /dev/null +++ b/code/modules/anomalies/items/fauna_bomb/mob_data.dm @@ -0,0 +1,117 @@ +/datum/airmob_data + var/normal_name // Used only for names in list of choosing. + var/icon/icon + var/name + var/ru_names + var/desc + var/maxHealth + var/armor + var/response_help = "гладит" + var/response_disarm = "толкает" + var/response_harm = "бьет" + var/harm_intent_damage = 10 + var/melee_damage_lower = 10 + var/melee_damage_upper = 10 + var/obj_damage = 10 + var/armour_penetration = 0 + var/friendly = "касается" + var/environment_smash = ENVIRONMENT_SMASH_STRUCTURES + var/speed = 1 + var/mob_size = MOB_SIZE_HUMAN + var/appearance + var/req_charge + var/scan_num = 0 + var/rapid_melee = 1 + +/datum/airmob_data/New(mob/living/target, obj/item/fauna_bomb/spawner) + . = ..() + + mob_size = target.mob_size + normal_name = target.declent_ru(NOMINATIVE) + icon = icon(initial(target.icon), initial(target.icon_state), SOUTH, 1, FALSE) + name = "воздушная проекция [target.declent_ru(GENITIVE)]" + ru_names = list(NOMINATIVE = "воздушная проекция [target.declent_ru(GENITIVE)]", \ + GENITIVE = "воздушной проекции [target.declent_ru(GENITIVE)]", \ + DATIVE = "воздушной проекции [target.declent_ru(GENITIVE)]", \ + ACCUSATIVE = "воздушную проекцию [target.declent_ru(GENITIVE)]", \ + INSTRUMENTAL = "воздушной проекцей [target.declent_ru(GENITIVE)]", \ + PREPOSITIONAL = "воздушной проекции [target.declent_ru(GENITIVE)]") + desc = "Уплотненная смесь газов, принявшая форму [target.declent_ru(GENITIVE)]." + appearance = target.appearance + maxHealth = target.maxHealth + armor = list(MELEE = target.getarmor(attack_flag = MELEE), BULLET = target.getarmor(attack_flag = BULLET), \ + LASER = target.getarmor(attack_flag = LASER), ENERGY = target.getarmor(attack_flag = ENERGY), \ + BOMB = target.getarmor(attack_flag = BOMB), BIO = target.getarmor(attack_flag = BIO), \ + RAD = target.getarmor(attack_flag = RAD), FIRE = target.getarmor(attack_flag = FIRE), \ + ACID = target.getarmor(attack_flag = ACID)) + speed = target.cached_multiplicative_slowdown + + if(istype(target, /mob/living/simple_animal)) + var/mob/living/simple_animal/SA = target + response_help = SA.response_help + response_disarm = SA.response_disarm + response_harm = SA.response_harm + harm_intent_damage = SA.harm_intent_damage + if(SA.melee_damage_type == BRUTE) + melee_damage_lower = SA.melee_damage_lower + melee_damage_upper = SA.melee_damage_upper + obj_damage = SA.obj_damage + + req_charge = sqrt(max(20, target.maxHealth) * max(5, (melee_damage_lower + melee_damage_upper) / 2)) * 1.2 + armour_penetration = SA.armour_penetration + friendly = SA.friendly + environment_smash = SA.environment_smash + return + + if(isalien(target)) + var/mob/living/carbon/alien/A = target + rapid_melee = 1.25 + armour_penetration = A.armour_penetration + melee_damage_lower = A.attack_damage + melee_damage_upper = A.attack_damage + obj_damage = A.obj_damage + environment_smash = A.environment_smash + req_charge = sqrt(max(20, target.maxHealth) * max(5, (melee_damage_lower + melee_damage_upper) / 2)) * 1.2 + return + + if(ishuman(target)) + var/mob/living/carbon/human/H = target + rapid_melee = 1.25 + if(H.has_left_hand()) + var/obj/item/I = H.get_item_by_slot(ITEM_SLOT_HAND_LEFT) + if(I) + armour_penetration = I.armour_penetration + if(!istype(I, /obj/item/twohanded)) + melee_damage_lower = I.force + melee_damage_upper = I.force + else + var/obj/item/twohanded/ITH = I + if(ITH.wielded) + melee_damage_lower = ITH.force_wielded + melee_damage_upper = ITH.force_wielded + else + melee_damage_lower = ITH.force_unwielded + melee_damage_upper = ITH.force_unwielded + else + melee_damage_lower = H.dna.species.punchdamagelow + melee_damage_upper = H.dna.species.punchdamagehigh + else + var/obj/item/I = H.get_item_by_slot(ITEM_SLOT_HAND_RIGHT) + if(I) + armour_penetration = max(armour_penetration, I.armour_penetration) + if(!istype(I, /obj/item/twohanded)) + melee_damage_lower = max(melee_damage_lower, I.force) + melee_damage_upper = max(melee_damage_upper, I.force) + else + var/obj/item/twohanded/ITH = I + if(ITH.wielded) + melee_damage_lower = max(melee_damage_lower, ITH.force_wielded) + melee_damage_upper = max(melee_damage_upper, ITH.force_wielded) + else + melee_damage_lower = max(melee_damage_lower, ITH.force_unwielded) + melee_damage_upper = max(melee_damage_upper, ITH.force_unwielded) + else + melee_damage_lower = max(melee_damage_lower, H.dna.species.punchdamagelow) + melee_damage_upper = max(melee_damage_upper, H.dna.species.punchdamagehigh) + + req_charge = sqrt(max(20, target.maxHealth) * max(5, (melee_damage_lower + melee_damage_upper) / 2)) * 1.2 diff --git a/code/modules/anomalies/items/gravitational_boots.dm b/code/modules/anomalies/items/gravitational_boots.dm new file mode 100644 index 00000000000..72992c311be --- /dev/null +++ b/code/modules/anomalies/items/gravitational_boots.dm @@ -0,0 +1,241 @@ +/obj/item/clothing/shoes/magboots/gravity + name = "gravitational boots" + ru_names = list( + NOMINATIVE = "гравитационные ботинки", \ + GENITIVE = "гравитационных ботинок", \ + DATIVE = "гравитационным ботинкам", \ + ACCUSATIVE = "гравитационные ботинки", \ + INSTRUMENTAL = "гравитационными ботинками", \ + PREPOSITIONAL = "гравитационных ботинках" + ) + desc = "Эти экспериментальные магбутсы обходят замедление обычных, за счет миниатюрных гравитационных в подошвах. \ + К сожалению, для работы им необходимо ядро гравитационной аномалии." + gender = PLURAL + icon_state = "gravboots0" + actions_types = list(/datum/action/item_action/toggle, /datum/action/item_action/gravity_jump) //combination of magboots and jumpboots + strip_delay = 10 SECONDS + put_on_delay = 10 SECONDS + slowdown_active = 0 + base_icon_state = "gravboots" + magpulse_name = "micro gravitational traction system" + var/datum/martial_art/grav_stomp/style + var/jumpdistance = 5 + var/jumpspeed = 3 + var/recharging_rate = 6 SECONDS + var/recharging_time = 0 // Time until next dash + var/dash_cost = 1000 // Cost to dash. + var/power_consumption_rate = 30 // How much power is used by the boots each cycle when magboots are active + var/obj/item/assembly/signaler/core/gravitational/core = null + var/obj/item/stock_parts/cell/cell = null + + +/obj/item/clothing/shoes/magboots/gravity/Initialize() + . = ..() + style = new() + + +/obj/item/clothing/shoes/magboots/gravity/Destroy() + QDEL_NULL(style) + QDEL_NULL(cell) + QDEL_NULL(core) + return ..() + +/obj/item/clothing/shoes/magboots/gravity/examine(mob/user) + . = ..() + if(core && cell) + . += span_notice("[declent_ru(NOMINATIVE)] полностью функциональны!") + . += span_notice("Ботинки заряжены на [round(cell.percent())]%.") + else if(core) + . += span_warning("В них установлено ядро ​​гравитационной аномалии, но не установлена батарейка.") + else if(cell) + . += span_warning("В них установлена батарейка, но не установлено ядро гравитационной аномалии.") + else + . += span_warning("В них не хватает ядра гравитационной аномалии и батарейки.") + + +/obj/item/clothing/shoes/magboots/gravity/toggle_magpulse(mob/user, silent = FALSE) + if(silent && (!cell || !core || cell.charge <= power_consumption_rate && !magpulse)) + return + + if(!cell) + user.balloon_alert(user, "нет батарейки") + return + + if(cell.charge <= power_consumption_rate && !magpulse) + user.balloon_alert(user, "недостаточно заряда") + return + + if(!core) + user.balloon_alert(user, "нет ядра") + return + + return ..() + + +/obj/item/clothing/shoes/magboots/gravity/process() + if(!cell) //There should be a cell here, but safety first + return + + if(cell.charge <= power_consumption_rate * 2) + if(ishuman(loc)) + var/mob/living/carbon/human/user = loc + to_chat(user, span_warning("[declent_ru(NOMINATIVE)] израсходовали весь заряд и отключились!")) + toggle_magpulse(user, silent = TRUE) + else + cell.use(power_consumption_rate) + +/obj/item/clothing/shoes/magboots/gravity/screwdriver_act(mob/living/user, obj/item/I) + if(!cell) + to_chat(user, span_warning("Внутри нет батарейки!")) + return + + if(magpulse) + to_chat(user, span_warning("Сначала выключите [declent_ru(ACCUSATIVE)]!")) + return + + if(!I.use_tool(src, user, volume = I.tool_volume)) + return + + cell.forceMove_turf() + user.put_in_hands(cell, ignore_anim = FALSE) + to_chat(user, span_notice("Вы достали [cell.declent_ru(ACCUSATIVE)] из [declent_ru(GENITIVE)].")) + cell.update_icon() + cell = null + update_icon() + + +/obj/item/clothing/shoes/magboots/gravity/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stock_parts/cell)) + add_fingerprint(user) + if(cell) + to_chat(user, span_warning("В [declent_ru(PREPOSITIONAL)] уже есть батарейка.")) + return ATTACK_CHAIN_PROCEED + + if(!user.drop_transfer_item_to_loc(I, src)) + return ..() + + to_chat(user, span_notice("Вы установили [I.declent_ru(ACCUSATIVE)] в [declent_ru(NOMINATIVE)].")) + cell = I + update_icon() + return ATTACK_CHAIN_BLOCKED_ALL + + if(iscoregrav(I)) + add_fingerprint(user) + if(core) + to_chat(user, span_warning("В [declent_ru(PREPOSITIONAL)] уже есть [core.declent_ru(NOMINATIVE)].")) + return ATTACK_CHAIN_PROCEED + + if(!user.drop_transfer_item_to_loc(I, src)) + return ..() + + to_chat(user, span_notice("Вы установили [I.declent_ru(ACCUSATIVE)] в [declent_ru(NOMINATIVE)]. Они немного потеплели.")) + core = I + return ATTACK_CHAIN_BLOCKED_ALL + + return ..() + + +/obj/item/clothing/shoes/magboots/gravity/AltClick(mob/user) + if(!user.contains(src)) + return ..() + + if(!core) + user.balloon_alert(user, "нет ядра") + return + + user.put_in_active_hand(core) + core = null + user.balloon_alert(user, "ядро извлечено") + +/obj/item/clothing/shoes/magboots/gravity/equipped(mob/user, slot, initial) + . = ..() + + if(!ishuman(user)) + return + + if(slot == ITEM_SLOT_FEET && cell && core) + style.bonus_damage = 10 * core.get_strenght() / 150 + style.teach(user, TRUE) + + +/obj/item/clothing/shoes/magboots/gravity/dropped(mob/living/carbon/human/user, slot, silent = FALSE) + . = ..() + if(!ishuman(user) || slot != ITEM_SLOT_FEET) + return . + + style.remove(user) + if(!magpulse) + return + + if(!silent) + to_chat(user, span_notice("Как только вы сняли [declent_ru(NOMINATIVE)] они автоматически деактивировались.")) + + toggle_magpulse(user, silent = TRUE) + + +/obj/item/clothing/shoes/magboots/gravity/item_action_slot_check(slot, mob/user, datum/action/action) + if(slot == ITEM_SLOT_FEET) + return TRUE + +/obj/item/clothing/shoes/magboots/gravity/proc/dash(mob/user, action) + if(!isliving(user)) + return + + if(!cell) + user.balloon_alert(user, "нет батарейки") + return + + if(cell.charge <= dash_cost) + user.balloon_alert(user, "недостаточно заряда") + return + + if(!core) + user.balloon_alert(user, "нет ядра") + return + + if(recharging_time > world.time) + user.balloon_alert(user, "идет перезарядка") + return + + if(user.throwing) + user.balloon_alert(user, "нет опоры") + return + + var/jump_mult = core.get_strenght() / 150 + var/cur_jumpdistance = jumpdistance * jump_mult + var/cur_jumpjumpspeed = jumpspeed * jump_mult + var/turf/T = get_step(get_turf(user), user.dir) + for(var/i = 1 to cur_jumpdistance) + if(!T.can_enter(user)) + cur_jumpjumpspeed = max(3, cur_jumpjumpspeed * ((i - 1) / cur_jumpdistance)) + cur_jumpdistance = i - 1 + break + + T = get_step(T, user.dir) + + var/atom/target = get_edge_target_turf(user, user.dir) //gets the user's direction + ADD_TRAIT(user, TRAIT_MOVE_FLYING, ITEM_GRAV_BOOTS_TRAIT) + var/after_jump_callback = CALLBACK(src, PROC_REF(after_jump), user) + if(user.throw_at(target, cur_jumpdistance, cur_jumpjumpspeed, spin = FALSE, diagonals_first = TRUE, callback = after_jump_callback)) + playsound(src, 'sound/effects/stealthoff.ogg', 50, 1, 1) + user.visible_message(span_warning("[user] прыгает вперед!")) + recharging_time = world.time + recharging_rate + cell.use(dash_cost) + return + + after_jump(user) + to_chat(user, span_warning("Что-то помешало вам прыгнуть!")) + +/obj/item/clothing/shoes/magboots/gravity/proc/after_jump(mob/user) + REMOVE_TRAIT(user, TRAIT_MOVE_FLYING, ITEM_GRAV_BOOTS_TRAIT) + +/obj/item/clothing/shoes/magboots/gravity/suicide_act(mob/user) + if(!cell || !core) + return ..() + + user.visible_message(span_suicide("[user] прижимает подошвы [declent_ru(GENITIVE)] к своему торсу с двух сторон и активирует. Похоже [genderize_ru(user.gender, "он", "она", "оно", "они")] пыта[genderize_ru(user.gender, "е", "е", "е", "ю")]тся убить себя!")) + user.visible_message(span_suicide("[user] взрывается из-за возникшего гравитационного колодца!"), \ + span_suicide("Вы взрываетесь из-за возникшего гравитационного колодца!"), + span_suicide("Вы слышите громкий, гулкий хлопок!")) + user.gib() + return OBLITERATION diff --git a/code/modules/anomalies/items/pyro_claws.dm b/code/modules/anomalies/items/pyro_claws.dm new file mode 100644 index 00000000000..83fbfffc1cf --- /dev/null +++ b/code/modules/anomalies/items/pyro_claws.dm @@ -0,0 +1,191 @@ +//pyro claws +/obj/item/twohanded/required/pyro_claws + name = "hardplasma energy claws" + ru_names = list( + NOMINATIVE = "энергокогти", \ + GENITIVE = "энергокогтей", \ + DATIVE = "энергокогтям", \ + ACCUSATIVE = "энергокогти", \ + INSTRUMENTAL = "энергокогтями", \ + PREPOSITIONAL = "энергокогтях" + ) + desc = "Сила солнца в ваших когтях." + gender = PLURAL + icon_state = "pyro_claws" + item_flags = ABSTRACT|DROPDEL + force = 25 + force_wielded = 25 + damtype = BURN + armour_penetration = 40 + block_chance = 50 + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut", "savaged", "clawed") + toolspeed = 0.5 + +/obj/item/twohanded/required/pyro_claws/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT) + START_PROCESSING(SSobj, src) + +/obj/item/twohanded/required/pyro_claws/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/twohanded/required/pyro_claws/process() + if(prob(15)) + do_sparks(rand(1,6), 1, loc) + +/obj/item/twohanded/required/pyro_claws/afterattack(atom/target, mob/user, proximity, params) + if(!proximity) + return + + if(prob(60)) + do_sparks(rand(1,6), 1, loc) + + if(istype(target, /obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/A = target + + if(!A.requiresID() || A.allowed(user)) + return + + if(A.locked) + to_chat(user, span_notice("Болты шлюза не позволяют взломать его силой.")) + return + + if(A.arePowerSystemsOn()) + user.visible_message(span_warning("[user] вставляет [declent_ru(NOMINATIVE)] в шлюз и начинает открывать его!"), \ + span_warning("Вы начинаете силой открывать шлюз."), \ + span_warning("Вы слышите металлический скрежет.")) + playsound(A, 'sound/machines/airlock_alien_prying.ogg', 150, 1) + if(!do_after(user, 2.5 SECONDS, A)) + return + + user.visible_message(span_warning("[user] силой открыл шлюз при помощи [declent_ru(GENITIVE)]!"), \ + span_warning("Вы силой открыли шлюз."), \ + span_warning("Вы слышите металлический скрежет.")) + A.open(2) + +/obj/item/twohanded/required/pyro_claws/suicide_act(mob/living/user) + user.visible_message(span_suicide("[user] начинает пилить [declent_ru(NOMINATIVE)] друг об друга! \ + Похоже [genderize_ru(user.gender, "он", "она", "оно", "они")] пыта[genderize_ru(user.gender, "е", "е", "е", "ю")]тся убить себя!")) + user.adjust_fire_stacks(10) + user.IgniteMob() + return FIRELOSS + +/obj/item/clothing/gloves/color/black/pyro_claws + name = "Fusion gauntlets" + ru_names = list( + NOMINATIVE = "плавящие перчатки", \ + GENITIVE = "плавящих перчаток", \ + DATIVE = "плавящим перчаткам", \ + ACCUSATIVE = "плавящие перчатки", \ + INSTRUMENTAL = "плавящими перчатками", \ + PREPOSITIONAL = "плавящих перчатках" + ) + desc = "Перчатки разработаенные Cybersun Industries после того, как один из солдат прикрепил атмосферное ядро ​​к \ + энергетическому мечу, и нашел результат весьма эффективными." + gender = PLURAL + item_state = "pyro" + item_color = "pyro" + icon_state = "pyro" + can_be_cut = FALSE + actions_types = list(/datum/action/item_action/toggle) + var/on_cooldown = FALSE + var/used = FALSE + var/obj/item/assembly/signaler/core/atmospheric/core + +/obj/item/clothing/gloves/color/black/pyro_claws/Destroy() + QDEL_NULL(core) + return ..() + +/obj/item/clothing/gloves/color/black/pyro_claws/examine(mob/user) + . = ..() + if(core) + . += span_notice("[declent_ru(NOMINATIVE)] полностью работоспособны.") + else + . += span_warning("[declent_ru(NOMINATIVE)] требуют атмосферное ядро для работы!") + +/obj/item/clothing/gloves/color/black/pyro_claws/item_action_slot_check(slot, mob/user, datum/action/action) + return slot == ITEM_SLOT_GLOVES + +/obj/item/clothing/gloves/color/black/pyro_claws/ui_action_click(mob/user, datum/action/action, leftclick) + if(!core) + user.balloon_alert(user, "нет ядра") + return + + if(on_cooldown) + user.balloon_alert(user, "идет перезарядка") + do_sparks(rand(1,6), 1, loc) + return + + if(used) + visible_message(span_warning("Энергетические когти скользят обратно в [declent_ru(ACCUSATIVE)].")) + user.drop_from_active_hand(force = TRUE)//dropdel stuff. only ui act, without hotkeys + do_sparks(rand(1,6), 1, loc) + on_cooldown = TRUE + addtimer(CALLBACK(src, PROC_REF(reboot)), 1 MINUTES) + return + + if(user.get_active_hand() && !user.drop_from_active_hand()) + to_chat(user, span_notice("[declent_ru(NOMINATIVE)] не могут выпустить клинки, пока у вас в руках есть предметы!")) + return + + var/obj/item/twohanded/required/pyro_claws/claws = new /obj/item/twohanded/required/pyro_claws + var/strenght_mult = core.get_strenght() / 150 + claws.force = 25 * strenght_mult + claws.force_wielded = 25 * strenght_mult + claws.armour_penetration = 100 * (1 - 0.6 / strenght_mult) + claws.block_chance = 100 * (1 - 0.5 / strenght_mult) + claws.toolspeed = 0.5 / strenght_mult + + user.visible_message(span_warning("[user] со снопом искр выпуска[genderize_ru(user.gender, "е", "е", "е", "ю")]т [claws.declent_ru(NOMINATIVE)] из запястий!"), \ + span_notice("Вы выпускаете [claws.declent_ru(NOMINATIVE)] из [declent_ru(GENITIVE)]!"), \ + span_warning("Вы слышите сноп искр!")) + user.put_in_hands(claws) + ADD_TRAIT(src, TRAIT_NODROP, PYRO_CLAWS_TRAIT) + used = TRUE + do_sparks(rand(1,6), 1, loc) + + +/obj/item/clothing/gloves/color/black/pyro_claws/attackby(obj/item/I, mob/user, params) + if(iscoreatmos(I)) + var/obj/item/assembly/signaler/core/I_core = I + if(I_core.get_strenght() < 100) + user.balloon_alert(user, "ядро слишком слабо") + return + + add_fingerprint(user) + var/msg = "ядро вставлено" + if(core) + user.put_in_hands(core) + msg = "ядро заменено" + + if(!user.drop_transfer_item_to_loc(I, src)) + balloon_alert(user, "отпустить невозможно!") + return ATTACK_CHAIN_PROCEED + + user.balloon_alert(user, msg) + to_chat(user, span_notice("Вы вставили [I.declent_ru(NOMINATIVE)] в [declent_ru(ACCUSATIVE)]. \ + От [declent_ru(GENITIVE)] начал исходить жар.")) + core = I + return ATTACK_CHAIN_BLOCKED_ALL + + return ..() + +/obj/item/clothing/gloves/color/black/pyro_claws/AltClick(mob/user) + if(!user.contains(src)) + return + + if(!core) + user.balloon_alert(user, "нет ядра") + return + + user.put_in_active_hand(core) + core = null + user.balloon_alert(user, "ядро извлечено") + +/obj/item/clothing/gloves/color/black/pyro_claws/proc/reboot() + on_cooldown = FALSE + used = FALSE + REMOVE_TRAIT(src, TRAIT_NODROP, PYRO_CLAWS_TRAIT) + atom_say("Внутренние плазменные баллоны перезаряжены. Перчатки достаточно охлаждены.") diff --git a/code/modules/anomalies/items/tuned_anomalous_teleporter.dm b/code/modules/anomalies/items/tuned_anomalous_teleporter.dm new file mode 100644 index 00000000000..b91c3e37290 --- /dev/null +++ b/code/modules/anomalies/items/tuned_anomalous_teleporter.dm @@ -0,0 +1,221 @@ +/obj/item/assembly/tuned_anomalous_teleporter + name = "tuned anomalous teleporter" + ru_names = list( + NOMINATIVE = "настраеваемый аномальный телепортер", \ + GENITIVE = "настраеваемого аномального телепортера", \ + DATIVE = "настраеваемому аномальному телепортеру", \ + ACCUSATIVE = "настраеваемый аномальный телепортер", \ + INSTRUMENTAL = "настраеваемым аномальным телепортером", \ + PREPOSITIONAL = "настраеваемом аномальном телепортере" + ) + desc = "Протативный настраиваемый телепортер использующий ядро блюспейс аномалии для телепортации пользователя в \ + выбранном направлении." + icon = 'icons/obj/weapons/techrelic.dmi' + icon_state = "teleport" + lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' + righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' + item_state = "teleport" + throwforce = 0 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + throw_range = 5 + materials = list(MAT_METAL=10000) + origin_tech = "magnets=3;bluespace=4" + armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 30, BIO = 0, RAD = 0, FIRE = 100, ACID = 100) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + origin_tech = "bluespace=5" + gender = MALE + + /// Inserted bluespace anomaly core. + var/obj/item/assembly/signaler/core/bluespace/core = null + /// Variable contains next time hand tele can be used to make it not EMP proof + var/emp_timer = 0 + /// Cooldown for teleportations. + var/base_cooldown = 20 SECONDS + /// Minimum waiting time after EMP. + var/emp_cooldown_min = 10 SECONDS // min cooldown for emp + /// Maximum waiting time after EMP. + var/emp_cooldown_max = 15 SECONDS // max cooldown for emp + /// Selected range of teleportations. + var/tp_range = 0 + /// Max allowed range of teleportations. + var/max_tp_range = 0 + + COOLDOWN_DECLARE(tuned_anomalous_teleporter_cooldown) // declare cooldown for teleportations + COOLDOWN_DECLARE(emp_cooldown) // declare cooldown for EMP + +/obj/item/assembly/tuned_anomalous_teleporter/Initialize(mapload) + . = ..() + update_core() + +/obj/item/assembly/tuned_anomalous_teleporter/proc/can_teleport(mob/user) + if(max_tp_range < 5) + if(user) + to_chat(user, span_warning("[declent_ru(NOMINATIVE)] не может вас телепортировать, \ + из-за отсутствия достаточно сильного ядра.")) + return FALSE + + if(!COOLDOWN_FINISHED(src, emp_cooldown)) + do_sparks(5, FALSE, loc) + if(user) + to_chat(user, span_warning("[declent_ru(NOMINATIVE)] не может вас телепортировать, \ + из-за того, что он временно выведен из строя.")) + + return FALSE + + if(!COOLDOWN_FINISHED(src, tuned_anomalous_teleporter_cooldown)) + if(user) + to_chat(user, span_warning("[declent_ru(NOMINATIVE)] все еще перезаряжается.")) + + return FALSE + + return TRUE + +/obj/item/assembly/tuned_anomalous_teleporter/activate(mob/user) + if(!can_teleport(user)) + return + + var/atom/tp_target = src + if(user) + tp_target = user + else + while(!isturf(tp_target.loc)) + tp_target = tp_target.loc + + COOLDOWN_START(src, tuned_anomalous_teleporter_cooldown, base_cooldown) + + var/datum/teleport/TP = new /datum/teleport() + var/crossdir = angle2dir((dir2angle(tp_target.dir)) % 360) + var/turf/T1 = get_turf(tp_target) + for(var/i in 1 to tp_range) + T1 = get_step(T1, crossdir) + + var/datum/effect_system/smoke_spread/s1 = new + var/datum/effect_system/smoke_spread/s2 = new + s1.set_up(5, FALSE, tp_target) + s2.set_up(5, FALSE, tp_target) + TP.start(tp_target, T1, FALSE, TRUE, s1, s2, 'sound/effects/phasein.ogg', ) + TP.doTeleport() + +/obj/item/assembly/tuned_anomalous_teleporter/attack_self(mob/user) + activate(user) + +/obj/item/assembly/tuned_anomalous_teleporter/emp_act(severity) + make_inactive(severity) + return ..() + +/obj/item/assembly/tuned_anomalous_teleporter/proc/make_inactive(severity) + var/time = rand(emp_cooldown_min, emp_cooldown_max) * (severity == EMP_HEAVY ? 2 : 1) + COOLDOWN_START(src, emp_cooldown, time) + +/obj/item/assembly/tuned_anomalous_teleporter/examine(mob/user) + . = ..() + if(!core) + . += span_warning("В [declent_ru(PREPOSITIONAL)] нет ядра!") + else + . += span_info("В [declent_ru(PREPOSITIONAL)] есть ядро.") + . += span_info("Для настройки кликните пустой рукой.") + + if(emp_timer > world.time) + . += span_warning("[declent_ru(NOMINATIVE)] выглядит неработающим.") + +/obj/item/assembly/tuned_anomalous_teleporter/AltClick(mob/user) + if(!user.contains(src)) + return + + if(!core) + user.balloon_alert(user, "нет ядра") + return + + user.put_in_active_hand(core) + core = null + user.balloon_alert(user, "ядро извлечено") + update_core() + +/obj/item/assembly/tuned_anomalous_teleporter/attackby(obj/item/I, mob/user, params) + if(!iscorebluespace(I)) + return ..() + + add_fingerprint(user) + var/msg = "ядро вставлено" + if(core) + user.put_in_hands(core) + msg = "ядро заменено" + + if(!user.drop_transfer_item_to_loc(I, src)) + balloon_alert(user, "отпустить невозможно!") + return ATTACK_CHAIN_PROCEED + + core = I + balloon_alert(user, msg) + update_core() + return ATTACK_CHAIN_PROCEED + +/obj/item/assembly/tuned_anomalous_teleporter/attack_hand(mob/user, pickupfireoverride) + if(!core || !user.is_in_hands(src)) + return ..() + + add_fingerprint(user) + if(max_tp_range < 5) + to_chat(user, span_warning("В [declent_ru(PREPOSITIONAL)] нет ядра, достаточно сильного, для телепортации.")) + return + + var/new_range = tgui_input_number(user, "Выберите дистанцию телепортации", "Настройка телепортера", tp_range, max_tp_range, 1) + if(!new_range) + return + + if(get_dist(user, src) > 1) + user.balloon_alert(user, "слишком далеко") + return + + tp_range = clamp(new_range, 1, max_tp_range) + user.balloon_alert(user, "выбрана дистанция [tp_range]") + +/* +Ranges with core charge 50-100: + tier1 - 1-3 (to weak for any) + tier2 - 3-7 + tier3 - 7-10 +*/ +/obj/item/assembly/tuned_anomalous_teleporter/proc/update_core() + if(!core) + max_tp_range = 0 + tp_range = 0 + return + + var/old_max_tp_range = max_tp_range + max_tp_range = max(1, round((core.get_strenght() + 10) / 30)) + if(tp_range != old_max_tp_range) // If was max, set max, else leave old. + tp_range = max_tp_range + +/obj/item/assembly/tuned_anomalous_teleporter/suicide_act(mob/user) + if(!can_teleport()) // Without massages. + return ..() + + var/gend_letter = genderize_ru(gender, "е", "е", "е", "ю") + user.visible_message(span_suicide("[user] перенастраива[gend_letter]т [declent_ru(NOMINATIVE)] случайным образом и пыта[gend_letter]тся телепортироваться! Выглядит, будто он[genderize_ru(gender, "", "а", "о", "и")] хо[genderize_ru(gender, "чет", "чет", "чет", "тят")] убить себя!")) + if(!do_after(user, 1 SECONDS, user)) + return ..() + + tp_range = rand(1, max_tp_range) + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(10, FALSE, user) + smoke.start() + user.gib() + return OBLITERATION + +/obj/item/assembly/tuned_anomalous_teleporter/bullet_act(obj/item/projectile/P) + . = ..() + activate() + +/datum/crafting_recipe/tuned_anomalous_teleporter + name = "Tuned anomalous teleporter" + result = /obj/item/assembly/tuned_anomalous_teleporter + tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) + reqs = list(/obj/item/relict_production/strange_teleporter = 1, + /obj/item/gps = 1, + /obj/item/stack/ore/bluespace_crystal, + /obj/item/stack/sheet/metal = 2, + /obj/item/stack/cable_coil = 5) + time = 300 + category = CAT_MISC diff --git a/code/game/objects/effects/anomalies.dm b/code/modules/anomalies/old.dm similarity index 78% rename from code/game/objects/effects/anomalies.dm rename to code/modules/anomalies/old.dm index 5f15b415f03..0f7b8022df8 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/modules/anomalies/old.dm @@ -3,7 +3,7 @@ /// Chance of taking a step per second #define ANOMALY_MOVECHANCE 70 -/obj/effect/anomaly +/obj/effect/old_anomaly name = "anomaly" desc = "A mysterious anomaly, seen commonly only in the region of space that the station orbits..." icon_state = "bhole3" @@ -11,7 +11,7 @@ anchored = TRUE light_range = 3 var/movechance = ANOMALY_MOVECHANCE - var/obj/item/assembly/signaler/anomaly/aSignal = /obj/item/assembly/signaler/anomaly + var/obj/item/assembly/signaler/core/aSignal = /obj/item/assembly/signaler/core var/area/impact_area /// Time in deciseconds before the anomaly triggers var/lifespan = 990 @@ -23,7 +23,7 @@ /// Do we drop a core when we're neutralized? var/drops_core = TRUE -/obj/effect/anomaly/Initialize(mapload, new_lifespan, _drops_core = TRUE) +/obj/effect/old_anomaly/Initialize(mapload, new_lifespan, _drops_core = TRUE) . = ..() GLOB.poi_list |= src START_PROCESSING(SSobj, src) @@ -51,14 +51,14 @@ countdown.color = countdown_colour countdown.start() -/obj/effect/anomaly/Destroy() +/obj/effect/old_anomaly/Destroy() GLOB.poi_list.Remove(src) STOP_PROCESSING(SSobj, src) QDEL_NULL(countdown) QDEL_NULL(aSignal) return ..() -/obj/effect/anomaly/process() +/obj/effect/old_anomaly/process() for(var/obj/item/I in get_turf(src)) if(!I.origin_tech) continue @@ -81,18 +81,18 @@ detonate() qdel(src) -/obj/effect/anomaly/proc/anomalyEffect() +/obj/effect/old_anomaly/proc/anomalyEffect() if(prob(movechance)) step(src, pick(GLOB.alldirs)) -/obj/effect/anomaly/proc/detonate() +/obj/effect/old_anomaly/proc/detonate() return -/obj/effect/anomaly/ex_act(severity) +/obj/effect/old_anomaly/ex_act(severity) if(severity == EXPLODE_DEVASTATE) qdel(src) -/obj/effect/anomaly/proc/anomalyNeutralize() +/obj/effect/old_anomaly/proc/anomalyNeutralize() new /obj/effect/particle_effect/smoke/bad(loc) if(drops_core) @@ -103,7 +103,7 @@ qdel(src) -/obj/effect/anomaly/attackby(obj/item/I, mob/user, params) +/obj/effect/old_anomaly/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/analyzer)) to_chat(user, span_notice("Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(aSignal.frequency)], code [aSignal.code].")) return ATTACK_CHAIN_PROCEED_SUCCESS @@ -111,16 +111,16 @@ /////////////////////// -/obj/effect/anomaly/grav +/obj/effect/old_anomaly/gravitational name = "gravitational anomaly" icon_state = "shield2" density = FALSE var/boing = FALSE var/knockdown = FALSE - aSignal = /obj/item/assembly/signaler/anomaly/grav + aSignal = /obj/item/assembly/signaler/core/gravitational/tier2 -/obj/effect/anomaly/grav/Initialize(mapload, new_lifespan, _drops_core) +/obj/effect/old_anomaly/gravitational/Initialize(mapload, new_lifespan, _drops_core) . = ..() var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_entered), @@ -128,7 +128,7 @@ AddElement(/datum/element/connect_loc, loc_connections) -/obj/effect/anomaly/grav/anomalyEffect() +/obj/effect/old_anomaly/gravitational/anomalyEffect() ..() boing = TRUE for(var/obj/O in orange(4, src)) @@ -146,25 +146,25 @@ O.throw_at(target, 5, 10) -/obj/effect/anomaly/grav/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) +/obj/effect/old_anomaly/gravitational/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) SIGNAL_HANDLER gravShock(arrived) -/obj/effect/anomaly/grav/Bump(atom/bumped_atom) +/obj/effect/old_anomaly/gravitational/Bump(atom/bumped_atom) . = ..() if(.) return . gravShock(bumped_atom) -/obj/effect/anomaly/grav/Bumped(atom/movable/moving_atom) +/obj/effect/old_anomaly/gravitational/Bumped(atom/movable/moving_atom) . = ..() gravShock(moving_atom) -/obj/effect/anomaly/grav/proc/gravShock(mob/living/A) +/obj/effect/old_anomaly/gravitational/proc/gravShock(mob/living/A) if(boing && isliving(A) && !A.stat) if(!knockdown) // no hardstuns with megafauna A.Weaken(4 SECONDS) @@ -174,17 +174,17 @@ ///////////////////// -/obj/effect/anomaly/flux +/obj/effect/old_anomaly/energetic name = "flux wave anomaly" icon_state = "electricity2" density = TRUE - aSignal = /obj/item/assembly/signaler/anomaly/flux + aSignal = /obj/item/assembly/signaler/core/energetic/tier2 var/canshock = FALSE var/shockdamage = 20 var/explosive = TRUE -/obj/effect/anomaly/flux/Initialize(mapload, new_lifespan, drops_core = TRUE, _explosive = TRUE) +/obj/effect/old_anomaly/energetic/Initialize(mapload, new_lifespan, drops_core = TRUE, _explosive = TRUE) . = ..() explosive = _explosive var/static/list/loc_connections = list( @@ -193,35 +193,35 @@ AddElement(/datum/element/connect_loc, loc_connections) -/obj/effect/anomaly/flux/anomalyEffect() +/obj/effect/old_anomaly/energetic/anomalyEffect() ..() canshock = TRUE for(var/mob/living/M in get_turf(src)) mobShock(M) -/obj/effect/anomaly/flux/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) +/obj/effect/old_anomaly/energetic/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) SIGNAL_HANDLER mobShock(arrived) -/obj/effect/anomaly/flux/Bump(atom/bumped_atom) +/obj/effect/old_anomaly/energetic/Bump(atom/bumped_atom) . = ..() if(.) return . mobShock(bumped_atom) -/obj/effect/anomaly/flux/Bumped(atom/movable/moving_atom) +/obj/effect/old_anomaly/energetic/Bumped(atom/movable/moving_atom) . = ..() mobShock(moving_atom) -/obj/effect/anomaly/flux/proc/mobShock(mob/living/M) +/obj/effect/old_anomaly/energetic/proc/mobShock(mob/living/M) if(canshock && istype(M)) canshock = FALSE //Just so you don't instakill yourself if you slam into the anomaly five times in a second. M.electrocute_act(shockdamage, "потоковой аномалии", flags = SHOCK_NOGLOVES) -/obj/effect/anomaly/flux/detonate() +/obj/effect/old_anomaly/energetic/detonate() if(explosive) explosion(src, 1, 4, 16, 18, cause = src) //Low devastation, but hits a lot of stuff. else @@ -229,31 +229,31 @@ ///////////////////// -/obj/effect/anomaly/bluespace +/obj/effect/old_anomaly/bluespace name = "bluespace anomaly" icon = 'icons/obj/weapons/projectiles.dmi' icon_state = "bluespace" density = TRUE var/mass_teleporting = TRUE - aSignal = /obj/item/assembly/signaler/anomaly/bluespace + aSignal = /obj/item/assembly/signaler/core/bluespace/tier2 -/obj/effect/anomaly/bluespace/Initialize(mapload, new_lifespan, drops_core = TRUE, _mass_teleporting = TRUE) +/obj/effect/old_anomaly/bluespace/Initialize(mapload, new_lifespan, drops_core = TRUE, _mass_teleporting = TRUE) . = ..() mass_teleporting = _mass_teleporting -/obj/effect/anomaly/bluespace/anomalyEffect() +/obj/effect/old_anomaly/bluespace/anomalyEffect() ..() for(var/mob/living/M in range(1, src)) do_teleport(M, M, 4) investigate_log("teleported [key_name_log(M)] to [COORD(M)]", INVESTIGATE_TELEPORTATION) -/obj/effect/anomaly/bluespace/Bumped(atom/movable/moving_atom) +/obj/effect/old_anomaly/bluespace/Bumped(atom/movable/moving_atom) . = ..() if(isliving(moving_atom)) do_teleport(moving_atom, moving_atom, 8) investigate_log("teleported [key_name_log(moving_atom)] to [COORD(moving_atom)]", INVESTIGATE_TELEPORTATION) -/obj/effect/anomaly/bluespace/detonate() +/obj/effect/old_anomaly/bluespace/detonate() if(!mass_teleporting) return var/turf/T = pick(get_area_turfs(impact_area)) @@ -299,7 +299,7 @@ if(M.client) INVOKE_ASYNC(src, PROC_REF(blue_effect), M) -/obj/effect/anomaly/bluespace/proc/blue_effect(mob/M) +/obj/effect/old_anomaly/bluespace/proc/blue_effect(mob/M) var/obj/blueeffect = new /obj(src) blueeffect.screen_loc = "WEST,SOUTH to EAST,NORTH" blueeffect.icon = 'icons/effects/effects.dmi' @@ -315,18 +315,18 @@ ///////////////////// -/obj/effect/anomaly/pyro +/obj/effect/old_anomaly/atmospheric name = "pyroclastic anomaly" icon_state = "mustard" var/ticks = 0 var/produces_slime = TRUE - aSignal = /obj/item/assembly/signaler/anomaly/pyro + aSignal = /obj/item/assembly/signaler/core/atmospheric/tier2 -/obj/effect/anomaly/pyro/Initialize(mapload, new_lifespan, drops_core = TRUE, _produces_slime = TRUE) +/obj/effect/old_anomaly/atmospheric/Initialize(mapload, new_lifespan, drops_core = TRUE, _produces_slime = TRUE) . = ..() produces_slime = _produces_slime -/obj/effect/anomaly/pyro/anomalyEffect() +/obj/effect/old_anomaly/atmospheric/anomalyEffect() ..() ticks++ if(ticks < 5) @@ -337,11 +337,11 @@ if(istype(T)) T.atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS | LINDA_SPAWN_OXYGEN, 5) -/obj/effect/anomaly/pyro/detonate() +/obj/effect/old_anomaly/atmospheric/detonate() if(produces_slime) INVOKE_ASYNC(src, PROC_REF(makepyroslime)) -/obj/effect/anomaly/pyro/proc/makepyroslime() +/obj/effect/old_anomaly/atmospheric/proc/makepyroslime() var/turf/simulated/T = get_turf(src) if(istype(T)) T.atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS | LINDA_SPAWN_OXYGEN, 500) //Make it hot and burny for the new slime @@ -359,13 +359,13 @@ ///////////////////// -/obj/effect/anomaly/bhole +/obj/effect/old_anomaly/bhole name = "vortex anomaly" icon_state = "bhole3" desc = "That's a nice station you have there. It'd be a shame if something happened to it." - aSignal = /obj/item/assembly/signaler/anomaly/vortex + aSignal = /obj/item/assembly/signaler/core/vortex/tier2 -/obj/effect/anomaly/bhole/anomalyEffect() +/obj/effect/old_anomaly/bhole/anomalyEffect() ..() if(!isturf(loc)) //blackhole cannot be contained inside anything. Weird stuff might happen qdel(src) @@ -384,14 +384,14 @@ else O.ex_act(EXPLODE_HEAVY) -/obj/effect/anomaly/bhole/proc/grav(r, ex_act_force, pull_chance, turf_removal_chance) +/obj/effect/old_anomaly/bhole/proc/grav(r, ex_act_force, pull_chance, turf_removal_chance) for(var/t = -r, t < r, t++) affect_coord(x + t, y - r, ex_act_force, pull_chance, turf_removal_chance) affect_coord(x - t, y + r, ex_act_force, pull_chance, turf_removal_chance) affect_coord(x + r, y + t, ex_act_force, pull_chance, turf_removal_chance) affect_coord(x - r, y - t, ex_act_force, pull_chance, turf_removal_chance) -/obj/effect/anomaly/bhole/proc/affect_coord(x, y, ex_act_force, pull_chance, turf_removal_chance) +/obj/effect/old_anomaly/bhole/proc/affect_coord(x, y, ex_act_force, pull_chance, turf_removal_chance) //Get turf at coordinate var/turf/T = locate(x, y, z) if(isnull(T)) diff --git a/code/modules/antagonists/space_ninja/components/ninja_states_breaker.dm b/code/modules/antagonists/space_ninja/components/ninja_states_breaker.dm index 2f4dff815d6..18330e27507 100644 --- a/code/modules/antagonists/space_ninja/components/ninja_states_breaker.dm +++ b/code/modules/antagonists/space_ninja/components/ninja_states_breaker.dm @@ -28,7 +28,7 @@ COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_APPLY_DAMAGE, COMSIG_SIMPLE_ANIMAL_ATTACKEDBY, - COMSIG_CARBON_HITBY), PROC_REF(cancel_states)) + COMSIG_ATOM_HITBY), PROC_REF(cancel_states)) /datum/component/ninja_states_breaker/UnregisterFromParent() @@ -43,7 +43,7 @@ COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_APPLY_DAMAGE, COMSIG_SIMPLE_ANIMAL_ATTACKEDBY, - COMSIG_CARBON_HITBY)) + COMSIG_ATOM_HITBY)) /// Собственно код выключающий сам инвиз и хамелион режимы костюма /datum/component/ninja_states_breaker/proc/cancel_states() diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index cafb0666791..905654e204f 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -128,9 +128,11 @@ if(assembly.secured) to_chat(user, span_warning("The [assembly.name] should not be secured.")) return ATTACK_CHAIN_PROCEED + if(secured) to_chat(user, span_warning("The [name] should not be secured.")) return ATTACK_CHAIN_PROCEED + attach_assembly(assembly, user) return ATTACK_CHAIN_BLOCKED_ALL diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 644e9216730..6785d2b85f3 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -13,8 +13,7 @@ var/obj/item/assembly/a_left = null var/obj/item/assembly/a_right = null - -/obj/item/assembly_holder/Initialize(mapload) +/obj/item/assembly_holder/Initialize(mapload, left_icon, left_iconstate, right_icon, right_iconstate) . = ..() var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_entered), @@ -86,14 +85,32 @@ /obj/item/assembly_holder/update_overlays() . = ..() + var/icon/icon_obj = new /icon(icon) // :) + var/list/states = icon_obj.IconStates() + var/list/add_overlays = list() if(a_left) - . += "[a_left.icon_state]_left" - for(var/O in a_left.attached_overlays) - . += "[O]_l" + var/state = "[a_left.icon_state]_left" + if(state in states) + . += state + for(var/O in a_left.attached_overlays) + . += "[O]_l" + + else + add_overlays += image(a_left.icon, a_left.icon_state) + if(a_right) - . += "[a_right.icon_state]_right" - for(var/O in a_right.attached_overlays) - . += "[O]_r" + var/state = "[a_right.icon_state]_right" + if(state in states) + . += state + for(var/O in a_right.attached_overlays) + . += "[O]_r" + + else + add_overlays += image(a_right.icon, a_right.icon_state) + + for(var/overlay in add_overlays) + add_overlay(overlay) + master?.update_icon() @@ -229,6 +246,6 @@ if(a_right) a_right.holder = null a_right.forceMove(T) - user.put_in_hands(a_left, ignore_anim = FALSE) + user.put_in_hands(a_right, ignore_anim = FALSE) qdel(src) diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm index 4041b6944b4..18e90a90d56 100644 --- a/code/modules/clothing/shoes/magboots.dm +++ b/code/modules/clothing/shoes/magboots.dm @@ -171,187 +171,3 @@ if(!silent) to_chat(user, span_notice("You poke the gem on [src]. Nothing happens.")) - -/obj/item/clothing/shoes/magboots/gravity - name = "gravitational boots" - desc = "These experimental boots try to get around the restrictions of magboots by installing miniture gravitational generators in the soles. Sadly, power hungry, and needs a gravitational anomaly core." - icon_state = "gravboots0" - actions_types = list(/datum/action/item_action/toggle, /datum/action/item_action/gravity_jump) //combination of magboots and jumpboots - strip_delay = 10 SECONDS - put_on_delay = 10 SECONDS - slowdown_active = 0 - base_icon_state = "gravboots" - magpulse_name = "micro gravitational traction system" - var/datum/martial_art/grav_stomp/style - var/jumpdistance = 5 - var/jumpspeed = 3 - var/recharging_rate = 6 SECONDS - var/recharging_time = 0 // Time until next dash - var/dash_cost = 1000 // Cost to dash. - var/power_consumption_rate = 30 // How much power is used by the boots each cycle when magboots are active - var/obj/item/assembly/signaler/anomaly/grav/core = null - var/obj/item/stock_parts/cell/cell = null - - -/obj/item/clothing/shoes/magboots/gravity/Initialize() - . = ..() - style = new() - - -/obj/item/clothing/shoes/magboots/gravity/Destroy() - QDEL_NULL(style) - QDEL_NULL(cell) - QDEL_NULL(core) - return ..() - -/obj/item/clothing/shoes/magboots/gravity/examine(mob/user) - . = ..() - if(core && cell) - . += "[src] are fully operational!" - . += "The boots are [round(cell.percent())]% charged." - else if(core) - . += "It has a gravitational anomaly core installed, but no power cell installed." - else if(cell) - . += "It has a power installed, but no gravitational anomaly core installed." - else - . += "It is missing a gravitational anomaly core and a power cell." - - -/obj/item/clothing/shoes/magboots/gravity/toggle_magpulse(mob/user, silent = FALSE) - if(!cell) - if(!silent) - to_chat(user, "Your boots do not have a power cell!") - return - else if(cell.charge <= power_consumption_rate && !magpulse) - if(!silent) - to_chat(user, "Your boots do not have enough charge!") - return - if(!core) - if(!silent) - to_chat(user, "There's no core installed!") - return - return ..() - - -/obj/item/clothing/shoes/magboots/gravity/process() - if(!cell) //There should be a cell here, but safety first - return - if(cell.charge <= power_consumption_rate * 2) - if(ishuman(loc)) - var/mob/living/carbon/human/user = loc - to_chat(user, "[src] has ran out of charge, and turned off!") - toggle_magpulse(user, silent = TRUE) - else - cell.use(power_consumption_rate) - -/obj/item/clothing/shoes/magboots/gravity/screwdriver_act(mob/living/user, obj/item/I) - if(!cell) - to_chat(user, "There's no cell installed!") - return - - if(magpulse) - to_chat(user, "Turn off the boots first!") - return - - if(!I.use_tool(src, user, volume = I.tool_volume)) - return - cell.forceMove_turf() - user.put_in_hands(cell, ignore_anim = FALSE) - to_chat(user, "You remove [cell] from [src].") - cell.update_icon() - cell = null - update_icon() - - -/obj/item/clothing/shoes/magboots/gravity/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stock_parts/cell)) - add_fingerprint(user) - if(cell) - to_chat(user, span_warning("The [name] already has a cell.")) - return ATTACK_CHAIN_PROCEED - if(!user.drop_transfer_item_to_loc(I, src)) - return ..() - to_chat(user, span_notice("You install [I] into [src].")) - cell = I - update_icon() - return ATTACK_CHAIN_BLOCKED_ALL - - if(istype(I, /obj/item/assembly/signaler/anomaly/grav)) - add_fingerprint(user) - if(core) - to_chat(user, span_warning("The [name] already has [core].")) - return ATTACK_CHAIN_PROCEED - if(!user.drop_transfer_item_to_loc(I, src)) - return ..() - to_chat(user, span_notice("You insert [I] into [src], and it starts to warm up.")) - core = I - return ATTACK_CHAIN_BLOCKED_ALL - - return ..() - - -/obj/item/clothing/shoes/magboots/gravity/equipped(mob/user, slot, initial) - . = ..() - - if(!ishuman(user)) - return - if(slot == ITEM_SLOT_FEET && cell && core) - style.teach(user, TRUE) - - -/obj/item/clothing/shoes/magboots/gravity/dropped(mob/living/carbon/human/user, slot, silent = FALSE) - . = ..() - if(!ishuman(user) || slot != ITEM_SLOT_FEET) - return . - - style.remove(user) - if(magpulse) - if(!silent) - to_chat(user, "As [src] are removed, they deactivate.") - toggle_magpulse(user, silent = TRUE) - - -/obj/item/clothing/shoes/magboots/gravity/item_action_slot_check(slot, mob/user, datum/action/action) - if(slot == ITEM_SLOT_FEET) - return TRUE - -/obj/item/clothing/shoes/magboots/gravity/proc/dash(mob/user, action) - if(!isliving(user)) - return - - if(cell) - if(cell.charge <= dash_cost) - to_chat(user, span_warning("Your boots do not have enough charge to dash!")) - return - else - to_chat(user, span_warning("Your boots do not have a power cell!")) - return - - if(!core) - to_chat(user, span_warning("There's no core installed!")) - return - - if(recharging_time > world.time) - to_chat(user, span_warning("The boot's gravitational pulse needs to recharge still!")) - return - - if(user.throwing) - to_chat(user, span_warning("You can't jump in the middle of another jump!")) - return - - var/atom/target = get_edge_target_turf(user, user.dir) //gets the user's direction - ADD_TRAIT(user, TRAIT_MOVE_FLYING, ITEM_GRAV_BOOTS_TRAIT) - var/after_jump_callback = CALLBACK(src, PROC_REF(after_jump), user) - if(user.throw_at(target, jumpdistance, jumpspeed, spin = FALSE, diagonals_first = TRUE, callback = after_jump_callback)) - playsound(src, 'sound/effects/stealthoff.ogg', 50, 1, 1) - user.visible_message(span_warning("[user] dashes forward into the air!")) - recharging_time = world.time + recharging_rate - cell.use(dash_cost) - else - after_jump(user) - to_chat(user, span_warning("Something prevents you from dashing forward!")) - - -/obj/item/clothing/shoes/magboots/gravity/proc/after_jump(mob/user) - REMOVE_TRAIT(user, TRAIT_MOVE_FLYING, ITEM_GRAV_BOOTS_TRAIT) - diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index e17f94aa6a6..4555650228a 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -465,7 +465,7 @@ if(user.throwing) to_chat(user, span_warning("You can't jump in the middle of another jump!")) return - if(!jumper.has_gravity()) + if(jumper.no_gravity()) to_chat(user, span_warning("You can't jump without gravity!")) return diff --git a/code/modules/countdown/countdown.dm b/code/modules/countdown/countdown.dm index a48a294e765..cbc7265e4dc 100644 --- a/code/modules/countdown/countdown.dm +++ b/code/modules/countdown/countdown.dm @@ -95,7 +95,7 @@ name = "anomaly countdown" /obj/effect/countdown/anomaly/get_value() - var/obj/effect/anomaly/A = attached_to + var/obj/effect/old_anomaly/A = attached_to if(!istype(A)) return var/time_left = max(0, (A.death_time - world.time) / 10) diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm index 4dcd10981ba..fc4f63b88f2 100644 --- a/code/modules/crafting/recipes.dm +++ b/code/modules/crafting/recipes.dm @@ -1551,7 +1551,7 @@ /obj/item/stack/sheet/mineral/diamond = 5 ) result = list(/obj/item/pickaxe/diamond) - + /datum/crafting_recipe/drone name = "Inactive Drone" result = list(/obj/item/inactive_drone) diff --git a/code/modules/economy/ATM.dm b/code/modules/economy/ATM.dm index da677f8c89b..b59352db2cb 100644 --- a/code/modules/economy/ATM.dm +++ b/code/modules/economy/ATM.dm @@ -241,10 +241,10 @@ log transactions req_money = INSURANCE_DELUXE_COST if(authenticated_account.charge(req_money)) - usr.balloon_alert("Тип страховки изменен") + usr.balloon_alert(usr, "тип страховки изменен") authenticated_account.insurance_type = new_insurance_type else - usr.balloon_alert("Недостаточно средств") + usr.balloon_alert(usr, "недостаточно средств") if("attempt_auth") if(linked_db) diff --git a/code/modules/economy/robotic_quests/robo_quests.dm b/code/modules/economy/robotic_quests/robo_quests.dm index d6499498d92..26ae7ce6d59 100644 --- a/code/modules/economy/robotic_quests/robo_quests.dm +++ b/code/modules/economy/robotic_quests/robo_quests.dm @@ -158,7 +158,7 @@ /datum/roboshop_item/bluespace_core name = "bluespace anomaly core" desc = "The neutralized core of a bluespace anomaly. It keeps phasing in and out of view. It'd probably be valuable for research." - visual_item = /obj/item/assembly/signaler/anomaly/bluespace + visual_item = /obj/item/assembly/signaler/core/bluespace/tier2 cost = list("working" = 0, "medical" = 0, "security" = 0, "robo" = 15) /datum/roboshop_item/advanced_roboquest_pad diff --git a/code/modules/events/anomaly.dm b/code/modules/events/anomaly.dm index 2cf45febddd..f175538c7de 100644 --- a/code/modules/events/anomaly.dm +++ b/code/modules/events/anomaly.dm @@ -2,7 +2,7 @@ /datum/event/anomaly name = "Anomaly: Energetic Flux" - var/obj/effect/anomaly/anomaly_path = /obj/effect/anomaly/flux + var/obj/effect/anomaly/anomaly_path = /obj/effect/anomaly/energetic/tier2 var/turf/target_turf announceWhen = 1 /// The prefix message for the anomaly annoucement. diff --git a/code/modules/events/anomaly_bluespace.dm b/code/modules/events/anomaly_bluespace.dm index 8c574deb8fd..c7f7953ce1e 100644 --- a/code/modules/events/anomaly_bluespace.dm +++ b/code/modules/events/anomaly_bluespace.dm @@ -2,5 +2,5 @@ name = "Anomaly: Bluespace" startWhen = 3 announceWhen = 10 - anomaly_path = /obj/effect/anomaly/bluespace + anomaly_path = /obj/effect/anomaly/bluespace/tier2 prefix_message = "На сканерах дальнего действия обнаружена нестабильная блюспейс-аномалия." diff --git a/code/modules/events/anomaly_flux.dm b/code/modules/events/anomaly_flux.dm index e55821ef0c1..2455eca05b4 100644 --- a/code/modules/events/anomaly_flux.dm +++ b/code/modules/events/anomaly_flux.dm @@ -2,5 +2,5 @@ name = "Anomaly: Hyper-Energetic Flux" startWhen = 10 announceWhen = 3 - anomaly_path = /obj/effect/anomaly/flux + anomaly_path = /obj/effect/anomaly/energetic/tier2 prefix_message = "На сканерах дальнего действия обнаружена поточная гиперэнергетическая аномалия." diff --git a/code/modules/events/anomaly_grav.dm b/code/modules/events/anomaly_grav.dm index 31532c3b24e..de340d2b9a7 100644 --- a/code/modules/events/anomaly_grav.dm +++ b/code/modules/events/anomaly_grav.dm @@ -2,5 +2,5 @@ name = "Anomaly: Gravitational" startWhen = 3 announceWhen = 20 - anomaly_path = /obj/effect/anomaly/grav + anomaly_path = /obj/effect/anomaly/gravitational/tier2 prefix_message = "На сканерах дальнего действия обнаружена гравитационная аномалия." diff --git a/code/modules/events/anomaly_pyro.dm b/code/modules/events/anomaly_pyro.dm index ee96502c2d1..ed953680cfb 100644 --- a/code/modules/events/anomaly_pyro.dm +++ b/code/modules/events/anomaly_pyro.dm @@ -2,5 +2,5 @@ name = "Anomaly: Pyroclastic" startWhen = 3 announceWhen = 10 - anomaly_path = /obj/effect/anomaly/pyro + anomaly_path = /obj/effect/anomaly/atmospheric/tier2 prefix_message = "На сканерах дальнего действия обнаружена атмосферная аномалия." diff --git a/code/modules/events/anomaly_vortex.dm b/code/modules/events/anomaly_vortex.dm index 6156e879477..c2a3a4b37b1 100644 --- a/code/modules/events/anomaly_vortex.dm +++ b/code/modules/events/anomaly_vortex.dm @@ -2,5 +2,5 @@ name = "Anomaly: Vortex" startWhen = 10 announceWhen = 3 - anomaly_path = /obj/effect/anomaly/bhole + anomaly_path = /obj/effect/anomaly/vortex/tier2 prefix_message = "На сканерах дальнего действия обнаружена вихревая аномалия высокой интенсивности." diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm index 4e712da6002..c8fd2fbbd49 100644 --- a/code/modules/events/wormholes.dm +++ b/code/modules/events/wormholes.dm @@ -88,3 +88,17 @@ return FALSE return TRUE + +// Wormholes generated by collapse of tier3 bluespace anomaly. +/datum/event/wormholes/anomaly + number_of_wormholes = 800 + +/obj/effect/portal/wormhole/anomaly/teleport(atom/movable/M) + var/try_x = rand(1, world.maxx) + var/try_y = rand(1, world.maxy) + var/turf/tp_pos = get_turf(locate(try_x, try_y, z)) + do_teleport(M, tp_pos, asoundin = 'sound/effects/phasein.ogg') + return TRUE + +/datum/event/wormholes/anomaly/announce() + GLOB.event_announcement.Announce("Дистабилизация крупной блюспейс аномалии вызвала пространственно-временные аномалии на борту станции. Дополнительная информация отсутствует.", "ВНИМАНИЕ: ОБНАРУЖЕНА АНОМАЛИЯ.", new_sound = 'sound/AI/spanomalies.ogg') diff --git a/code/modules/martial_arts/grav_stomp.dm b/code/modules/martial_arts/grav_stomp.dm index 918f67ce9ae..a919d421440 100644 --- a/code/modules/martial_arts/grav_stomp.dm +++ b/code/modules/martial_arts/grav_stomp.dm @@ -1,19 +1,20 @@ /datum/martial_art/grav_stomp name = "Gravitational Boots" weight = 4 + var/bonus_damage = 10 /datum/martial_art/grav_stomp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) MARTIAL_ARTS_ACT_CHECK add_attack_logs(A, D, "Melee attacked with [src]") - var/picked_hit_type = "kicks" - var/bonus_damage = 10 + var/picked_hit_type = "пинает" if(D.body_position == LYING_DOWN) - bonus_damage = 15 - picked_hit_type = "stomps on" + bonus_damage = bonus_damage * 1.5 + picked_hit_type = "топчет" + A.do_attack_animation(D, ATTACK_EFFECT_KICK) playsound(get_turf(D), 'sound/effects/hit_kick.ogg', 50, 1, -1) D.apply_damage(bonus_damage, BRUTE) objective_damage(A, D, bonus_damage, BRUTE) - D.visible_message("[A] [picked_hit_type] [D]!", \ - "[A] [picked_hit_type] you!") + D.visible_message(span_danger("[A] [picked_hit_type] [D]!"), \ + span_userdanger("[A] [picked_hit_type] вас!")) return TRUE diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 7ec37837804..01a07645470 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -469,6 +469,7 @@ I.do_pickup_animation(newloc) I.forceMove(newloc) + I.dir = dir /** @@ -506,6 +507,7 @@ I.pixel_x = shift_x I.pixel_y = shift_y I.do_drop_animation(src) + I.dir = dir /** @@ -520,6 +522,7 @@ /mob/proc/transfer_item_to_loc(obj/item/I, atom/newloc, force = FALSE, invdrop = TRUE, silent = FALSE) . = do_unEquip(I, force, newloc, FALSE, invdrop, silent) I.do_drop_animation(src) + I.dir = dir /** diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm index 16dcafe0634..e2ba4fdd52d 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm @@ -64,7 +64,7 @@ body_position_pixel_y_offset = -32 update_icons() ADD_TRAIT(src, TRAIT_MOVE_FLOATING, LEAPING_TRAIT) //Throwing itself doesn't protect mobs against lava (because gulag). - var/updated_speed = (!has_gravity() || !target.has_gravity()) ? LEAP_SPEED_NO_GRAVITY : LEAP_SPEED_DEFAULT + var/updated_speed = (no_gravity() || target.no_gravity()) ? LEAP_SPEED_NO_GRAVITY : LEAP_SPEED_DEFAULT throw_at(target, MAX_ALIEN_LEAP_DIST, updated_speed, src, FALSE, TRUE, callback = CALLBACK(src, PROC_REF(leap_end))) #undef MAX_ALIEN_LEAP_DIST diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 6152cecfe17..78004d87ec5 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -426,6 +426,12 @@ pose = addtext(pose,".") //Makes sure all emotes end with a period. msg += "\n[p_they(TRUE)] [p_are()] [pose]" + if(get_gravity(src) < -NO_GRAVITY && !buckled) + msg += "[genderize_ru(user.gender, "Он", "Она", "Оно", "Они")] наход[genderize_ru(user.gender, "и", "и", "и", "я")]тся на потолке." + + if(user.no_gravity() && !buckled) + msg += "[p_they(TRUE)] не подвержен[genderize_ru(user.gender, "", "а", "о", "ы")] действию гравитации." + if(client && mind && !mind.offstation_role && user.mind?.special_role) // No ashwalkers, monkeys etc var/permission_granted = client.prefs.toggles2 & PREFTOGGLE_2_GIB_WITHOUT_OBJECTIVE msg += "\n
[span_info("Вы[permission_granted ? "" : " [span_warning("НЕ")]"] можете вывести этого игрока из игры не имея соответствующей цели.")]
" diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 7f463abfa0c..ea9c8ad94eb 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -632,7 +632,7 @@ emp_act if(locateUID(I.thrownby) == src) //No throwing stuff at yourself to trigger reactions return ..() - SEND_SIGNAL(src, COMSIG_CARBON_HITBY) + SEND_SIGNAL(src, COMSIG_ATOM_HITBY, AM, skipcatch, hitpush, blocked, throwingdatum) if(check_shields(AM, throwpower, "\the [AM.name]", THROWN_PROJECTILE_ATTACK, armour_penetration, shields_penetration)) hitpush = FALSE diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 62a0c337ef5..347aa305bf5 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -1,6 +1,6 @@ /mob/living/carbon/human/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) . = ..() - if(!forced && (!old_loc || !old_loc.has_gravity()) && has_gravity()) + if(!forced && (!old_loc || old_loc.no_gravity()) && !no_gravity()) thunk() @@ -42,7 +42,7 @@ else if(prob(30)) playsound(src, "bonebreak", 10, TRUE) - if(!has_gravity()) + if(no_gravity()) return . if(nutrition && stat != DEAD && !isvampire(src)) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 765ff34e0f8..fd0833d693d 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -243,24 +243,42 @@ /mob/living/proc/handle_gravity(seconds_per_tick, times_fired) - if(gravity_state > STANDARD_GRAVITY) + if(abs(gravity_state) > STANDARD_GRAVITY) handle_high_gravity(gravity_state, seconds_per_tick, times_fired) +/mob/living/carbon/handle_gravity(seconds_per_tick, times_fired) + . = ..() + if(gravity_state < HIGH_GRAVITY_SLOWDOWN) + remove_movespeed_modifier(/datum/movespeed_modifier/high_gravity) + + if(gravity_state < GRAVITY_CANT_STAY) + REMOVE_TRAIT(src, TRAIT_FLOORED, GRAVITATION_TRAIT) + return + + if(!buckled) + ADD_TRAIT(src, TRAIT_FLOORED, GRAVITATION_TRAIT) + + /mob/living/proc/gravity_animate() if(!get_filter("gravity")) add_filter("gravity",1,list("type"="motion_blur", "x"=0, "y"=0)) + animate(get_filter("gravity"), y = 1, time = 10, loop = -1) animate(y = 0, time = 10) /mob/living/proc/handle_high_gravity(gravity, seconds_per_tick, times_fired) - if(gravity < GRAVITY_DAMAGE_THRESHOLD) //Aka gravity values of 3 or more + if(abs(gravity) < HIGH_GRAVITY_SLOWDOWN) return - var/grav_strength = gravity - GRAVITY_DAMAGE_THRESHOLD - adjustBruteLoss(min(GRAVITY_DAMAGE_SCALING * grav_strength, GRAVITY_DAMAGE_MAXIMUM) * seconds_per_tick) + add_movespeed_modifier(/datum/movespeed_modifier/high_gravity) + if(abs(gravity) < GRAVITY_DAMAGE_THRESHOLD) //Aka gravity values of 3 or more + return + + var/grav_strength = abs(gravity) - GRAVITY_DAMAGE_THRESHOLD + adjustBruteLoss(min(GRAVITY_DAMAGE_SCALING * grav_strength, GRAVITY_DAMAGE_MAXIMUM) * seconds_per_tick) /// Updates grabbed victim status effects. /mob/living/proc/pull_on_life() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f966df04963..a2cc2a8c3f5 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -422,23 +422,32 @@ // all this repeated spaghetti code is used to properly register projectiles if(mover.pass_flags == PASSEVERYTHING) return TRUE + if(mover.pass_flags & pass_flags_self) return TRUE - if(mover.throwing && (pass_flags_self & LETPASSTHROW)) + + // We can pass through low buildings if we walk on the ceiling. + if((mover.throwing || get_gravity(mover) < -NO_GRAVITY) && (pass_flags_self & LETPASSTHROW)) return TRUE + if(mover in buckled_mobs) return TRUE + var/is_projectile = isprojectile(mover) if(!density || is_projectile) if(is_projectile) return projectile_allow_through(mover, border_dir) return TRUE + if(mover.throwing) return body_position == LYING_DOWN || mover.throwing.thrower == src + if(pulling && pulling == mover && grab_state >= GRAB_NECK) // pulled mob can step through us return TRUE + if(buckled == mover) return TRUE + return !mover.density || body_position == LYING_DOWN @@ -871,12 +880,13 @@ /mob/living/proc/makeTrail(turf/T) - if(!has_gravity()) + if(no_gravity()) return - var/blood_exists = 0 + var/blood_exists = 0 for(var/obj/effect/decal/cleanable/trail_holder/C in loc) //checks for blood splatter already on the floor blood_exists = 1 + if(isturf(loc)) var/trail_type = getTrail() if(trail_type) @@ -2013,8 +2023,10 @@ add_traits(list(TRAIT_UI_BLOCKED, TRAIT_PULL_BLOCKED, TRAIT_UNDENSE), LYING_DOWN_TRAIT) if(HAS_TRAIT(src, TRAIT_FLOORED) && !(dir & (NORTH|SOUTH))) setDir(pick(NORTH, SOUTH)) // We are and look helpless. + if(rotate_on_lying) - body_position_pixel_y_offset = PIXEL_Y_OFFSET_LYING + body_position_pixel_y_offset = gravity_state >= 0 ? PIXEL_Y_OFFSET_LYING : PIXEL_Y_OFFSET_LYING_REVERSED + if(!buckled || buckled.buckle_lying == NO_BUCKLE_LYING) lying_angle_on_lying_down(new_lying_angle) diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index be43860cfae..5f7916ffb4a 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -150,7 +150,7 @@ /// Handles gravity effects. Call if something about our gravity has potentially changed! /mob/living/proc/refresh_gravity() var/old_grav_state = gravity_state - gravity_state = has_gravity() + gravity_state = get_gravity() if(gravity_state == old_grav_state) return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm index 3e54b3d2e93..c2cc15916d5 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm @@ -151,15 +151,15 @@ Difficulty: Very Hard var/core_type = null switch(mode) if(BLUESPACE) - core_type = /obj/item/assembly/signaler/anomaly/bluespace + core_type = /obj/item/assembly/signaler/core/bluespace/tier2 if(GRAV) - core_type = /obj/item/assembly/signaler/anomaly/grav + core_type = /obj/item/assembly/signaler/core/gravitational/tier2 if(PYRO) - core_type = /obj/item/assembly/signaler/anomaly/pyro + core_type = /obj/item/assembly/signaler/core/atmospheric/tier2 if(FLUX) - core_type = /obj/item/assembly/signaler/anomaly/flux + core_type = /obj/item/assembly/signaler/core/energetic/tier2 if(VORTEX) - core_type = /obj/item/assembly/signaler/anomaly/vortex + core_type = /obj/item/assembly/signaler/core/vortex/tier2 var/crate_type = pick(loot) var/obj/structure/closet/crate/C = new crate_type(loc) @@ -430,19 +430,19 @@ Difficulty: Very Hard var/time_to_use = enraged ? 25 SECONDS : 15 SECONDS switch(mode) if(BLUESPACE) - var/obj/effect/anomaly/bluespace/A = new(spot, time_to_use, FALSE) + var/obj/effect/old_anomaly/bluespace/A = new(spot, time_to_use, FALSE) A.mass_teleporting = FALSE if(GRAV) - var/obj/effect/anomaly/grav/A = new(spot, time_to_use, FALSE, FALSE) + var/obj/effect/old_anomaly/gravitational/A = new(spot, time_to_use, FALSE, FALSE) A.knockdown = TRUE if(PYRO) - var/obj/effect/anomaly/pyro/A = new(spot, time_to_use, FALSE) + var/obj/effect/old_anomaly/atmospheric/A = new(spot, time_to_use, FALSE) A.produces_slime = FALSE if(FLUX) - var/obj/effect/anomaly/flux/A = new(spot, time_to_use, FALSE) + var/obj/effect/old_anomaly/energetic/A = new(spot, time_to_use, FALSE) A.explosive = FALSE if(VORTEX) - new /obj/effect/anomaly/bhole(spot, time_to_use, FALSE) + new /obj/effect/old_anomaly/bhole(spot, time_to_use, FALSE) anomalies++ return diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 5eaa8ad55a0..b9e4bd21e46 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -320,7 +320,7 @@ return rebound -/mob/has_gravity(turf/gravity_turf) +/mob/get_gravity(turf/gravity_turf) if(!isnull(GLOB.gravity_is_on)) // global admin override. return GLOB.gravity_is_on return mob_negates_gravity() || ..() diff --git a/code/modules/movespeed/modifiers/misc.dm b/code/modules/movespeed/modifiers/misc.dm index f170b23c816..0aa4f642edd 100644 --- a/code/modules/movespeed/modifiers/misc.dm +++ b/code/modules/movespeed/modifiers/misc.dm @@ -7,3 +7,6 @@ multiplicative_slowdown = -1 blacklisted_movetypes = (FLYING|FLOATING) +/datum/movespeed_modifier/high_gravity + multiplicative_slowdown = 1.5 + blacklisted_movetypes = (FLYING|FLOATING) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 29e7c8c8995..13977bf8540 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -1700,6 +1700,33 @@ aidisabled = FALSE updateDialog() +/obj/machinery/power/apc/proc/is_channel_on(chan = EQUIP) + var/channel_type + switch(chan) + if(EQUIP) + channel_type = equipment_channel + + if(LIGHT) + channel_type = lighting_channel + + if(ENVIRON) + channel_type = environment_channel + + return channel_type == CHANNEL_SETTING_ON || channel_type == CHANNEL_SETTING_AUTO_ON + +/obj/machinery/power/apc/proc/is_channel_force_on(chan = EQUIP) + var/channel_type + switch(chan) + if(EQUIP) + channel_type = equipment_channel + + if(LIGHT) + channel_type = lighting_channel + + if(ENVIRON) + channel_type = environment_channel + + return channel_type == CHANNEL_SETTING_ON #undef UPSTATE_CELL_IN #undef UPSTATE_OPENED1 diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm index 4f961b593bf..dfb75de6d00 100644 --- a/code/modules/projectiles/ammunition/energy.dm +++ b/code/modules/projectiles/ammunition/energy.dm @@ -279,7 +279,16 @@ select_name = "heavy bolt" /obj/item/projectile/energy/bsg - name = "Сфера чистой БС энергии" + name = "сфера чистой БС энергии" + ru_names = list( + NOMINATIVE = "сфера чистой БС энергии", \ + GENITIVE = "сферы чистой БС энергии", \ + DATIVE = "сферу чистой БС энергии", \ + ACCUSATIVE = "сферу чистой БС энергии", \ + INSTRUMENTAL = "сферой чистой БС энергии", \ + PREPOSITIONAL = "сфере чистой БС энергии" + ) + gender = FEMALE icon_state = "bluespace" impact_effect_type = /obj/effect/temp_visual/bsg_kaboom damage = 60 @@ -288,6 +297,8 @@ weaken = 8 SECONDS //This is going to knock you off your feet eyeblur = 20 SECONDS speed = 2 + /// The strenght of the core of the fired Б.С.Г. + var/core_strenght = 0 /obj/item/ammo_casing/energy/bsg/ready_proj(atom/target, mob/living/user, quiet, zone_override = "") ..() @@ -308,30 +319,34 @@ ..() /obj/item/projectile/energy/bsg/proc/kaboom() - playsound(src, 'sound/weapons/bsg_explode.ogg', 75, TRUE) - for(var/mob/living/M in hearers(7, src)) //No stuning people with thermals through a wall. + var/effects_mult = core_strenght / 170 + playsound(src, 'sound/weapons/bsg_explode.ogg', 75 * effects_mult, TRUE) + for(var/mob/living/M in hearers(7 * effects_mult, src)) //No stuning people with thermals through a wall. var/floored = FALSE if(ishuman(M)) var/mob/living/carbon/human/H = M var/obj/item/gun/energy/bsg/N = locate() in H if(N) - to_chat(H, "[N] deploys an energy shield to project you from [src]'s explosion.") + to_chat(H, span_notice("[N] развертывает энергетический щит, чтобы защитить вас от взрыва [declent_ru(GENITIVE)].")) continue + var/distance = (1 + get_dist(M, src)) - if(prob(min(400 / distance, 100))) //100% chance to hit with the blast up to 3 tiles, after that chance to hit is 80% at 4 tiles, 66.6% at 5, 57% at 6, and 50% at 7 - if(prob(min(150 / distance, 100)))//100% chance to upgraded to a stun as well at a direct hit, 75% at 1 tile, 50% at 2, 37.5% at 3, 30% at 4, 25% at 5, 21% at 6, and finaly 19% at 7. This is calculated after the first hit however. + if(prob(min(400 * effects_mult / distance, 100))) //100% chance to hit with the blast up to 3 tiles, after that chance to hit is 80% at 4 tiles, 66.6% at 5, 57% at 6, and 50% at 7 + if(prob(min(150 * effects_mult / distance, 100)))//100% chance to upgraded to a stun as well at a direct hit, 75% at 1 tile, 50% at 2, 37.5% at 3, 30% at 4, 25% at 5, 21% at 6, and finaly 19% at 7. This is calculated after the first hit however. floored = TRUE - M.apply_damage((rand(15, 30) * (1.1 - distance / 10)), BURN) //reduced by 10% per tile + + M.apply_damage((rand(15, 30) * (1.1 - distance / 10)) * effects_mult, BURN) //reduced by 10% per tile add_attack_logs(src, M, "Hit heavily by [src]") if(floored) - to_chat(M, "You see a flash of briliant blue light as [src] explodes, knocking you to the ground and burning you!") - M.Weaken(8 SECONDS) + to_chat(M, span_userdanger("Вы видите яркую вспышку синего света, когда [declent_ru(NOMINATIVE)] взрывается, сбивая вас с ног и обжигая!")) + M.Weaken(8 * effects_mult SECONDS) else - to_chat(M, "You see a flash of briliant blue light as [src] explodes, burning you!") + to_chat(M, span_userdanger("Вы видите яркую вспышку синего света, когда [declent_ru(NOMINATIVE)] взрывается, обжигая вас!")) + else - to_chat(M, "You feel the heat of the explosion of [src], but the blast mostly misses you.") + to_chat(M, span_userdanger("Вы чувствуете жар от взрыва [declent_ru(GENITIVE)], но он почти не задевает вас.")) add_attack_logs(src, M, "Hit lightly by [src]") - M.apply_damage(rand(1, 5), BURN) + M.apply_damage(rand(1, 5) * effects_mult, BURN) /obj/item/ammo_casing/energy/dart projectile_type = /obj/item/projectile/energy/dart @@ -500,3 +515,20 @@ muzzle_flash_color = LIGHT_COLOR_GREEN select_name = "emitter" e_cost = 750 + + +/obj/item/ammo_casing/energy/anomaly + fire_sound = 'sound/weapons/emitter.ogg' + select_name = "emitter" + delay = 0.4 + e_cost = 100 + projectile_type = /obj/item/projectile/beam/anomaly + muzzle_flash_color = LIGHT_COLOR_GREEN + +/obj/item/ammo_casing/energy/anomaly/stabilizer + projectile_type = /obj/item/projectile/beam/anomaly/stabilizer + muzzle_flash_color = LIGHT_COLOR_BLUE + +/obj/item/ammo_casing/energy/anomaly/destabilizer + projectile_type = /obj/item/projectile/beam/anomaly/destabilizer + muzzle_flash_color = LIGHT_COLOR_RED diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index b3bc18b5fb7..e07e8fbdb08 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -41,9 +41,11 @@ if(!can_add_sibyl_system) to_chat(user, span_warning("The [name] is incompatible with the sibyl systems module.")) return ATTACK_CHAIN_PROCEED + if(sibyl_mod) to_chat(user, span_warning("The [name] is already has a sibyl systems module installed.")) return ATTACK_CHAIN_PROCEED + new_sibyl.install(src, user) return ATTACK_CHAIN_BLOCKED_ALL diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 1e1cb7e47c7..332037664e0 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -417,115 +417,6 @@ shaded_charge = TRUE modifystate = TRUE -/obj/item/gun/energy/bsg - name = "\improper Б.С.П" - desc = "Большая С*** Пушка. Использует ядро аномалии потока и кристалл блюспейса для производства разрушительных взрывов энергии, вдохновленный дивизионом БСА Нанотрейзен." - icon_state = "bsg" - item_state = "bsg" - origin_tech = "combat=6;materials=6;powerstorage=6;bluespace=6;magnets=6" //cutting edge technology, be my guest if you want to deconstruct one instead of use it. - ammo_type = list(/obj/item/ammo_casing/energy/bsg) - weapon_weight = WEAPON_HEAVY - w_class = WEIGHT_CLASS_BULKY - can_holster = FALSE - slot_flags = ITEM_SLOT_BACK - cell_type = /obj/item/stock_parts/cell/bsg - shaded_charge = TRUE - var/has_core = FALSE - var/has_bluespace_crystal = FALSE - var/admin_model = FALSE //For the admin gun, prevents crystal shattering, so anyone can use it, and you dont need to carry backup crystals. - -/obj/item/gun/energy/bsg/examine(mob/user) - . = ..() - if(has_core && has_bluespace_crystal) - . += "[src] полностью рабочая!" - else if(has_core) - . += "Аномалия потока вставлена, но не хватает БС кристалла." - else if(has_bluespace_crystal) - . += "Имеет инкрустированный БС кристалл, но нет установленного ядра аномалии потока." - else - . += "Не хватает ядра аномалии потока и БС кристалла для работы." - - -/obj/item/gun/energy/bsg/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stack/ore/bluespace_crystal)) - add_fingerprint(user) - var/obj/item/stack/ore/bluespace_crystal/crystal = I - if(has_bluespace_crystal) - balloon_alert(user, "уже установлено!") - return ATTACK_CHAIN_PROCEED - if(!crystal.use(1)) - balloon_alert(user, "недостаточно кристаллов!") - return ATTACK_CHAIN_PROCEED - balloon_alert(user, "установлено") - has_bluespace_crystal = TRUE - update_icon(UPDATE_ICON_STATE) - return ATTACK_CHAIN_PROCEED_SUCCESS - - if(istype(I, /obj/item/assembly/signaler/anomaly/flux)) - add_fingerprint(user) - if(has_core) - balloon_alert(user, "уже установлено!") - return ATTACK_CHAIN_PROCEED - if(!user.drop_transfer_item_to_loc(I, src)) - return ..() - balloon_alert(user, "установлено") - has_core = TRUE - qdel(I) - update_icon(UPDATE_ICON_STATE) - return ATTACK_CHAIN_BLOCKED_ALL - - return ..() - - -/obj/item/gun/energy/bsg/process_fire(atom/target, mob/living/user, message = TRUE, params, zone_override, bonus_spread = 0) - if(!has_bluespace_crystal) - balloon_alert(user, "отсутствует блюспейс кристалл!") - return - if(!has_core) - balloon_alert(user, "отсутствует ядро аномалии!") - return - return ..() - - -/obj/item/gun/energy/bsg/update_icon_state() - if(has_core) - if(has_bluespace_crystal) - icon_state = "bsg_finished" - else - icon_state = "bsg_core" - else if(has_bluespace_crystal) - icon_state = "bsg_crystal" - else - icon_state = "bsg" - - -/obj/item/gun/energy/bsg/emp_act(severity) - ..() - if(prob(75 / severity)) - if(has_bluespace_crystal) - shatter() - -/obj/item/gun/energy/bsg/proc/shatter() - if(admin_model) - return - visible_message("БС кристалл [src] треснул!") - playsound(src, 'sound/effects/pylon_shatter.ogg', 50, TRUE) - has_bluespace_crystal = FALSE - update_icon(UPDATE_ICON_STATE) - -/obj/item/gun/energy/bsg/prebuilt - icon_state = "bsg_finished" - has_bluespace_crystal = TRUE - -/obj/item/gun/energy/bsg/prebuilt/Initialize(mapload) - . = ..() - has_core = TRUE - update_icon(UPDATE_ICON_STATE) - -/obj/item/gun/energy/bsg/prebuilt/admin - desc = "Большая С*** Пушка. Лучшим людям - лучшее творение. У этой версии БС кристалл никогда не треснет, и уже загружено ядро аномалии потока." - admin_model = TRUE - // Temperature Gun // /obj/item/gun/energy/temperature name = "temperature gun" diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index ad9ddc0a321..b0800c9e54c 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -262,3 +262,53 @@ impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser light_color = LIGHT_COLOR_LIGHT_CYAN + +/obj/item/projectile/beam/anomaly + name = "луч стабилизатора аномалий" + icon_state = "xray" // Looks mostly like "blue/red_laser" in green colour. + damage = 0 + hitsound = 'sound/weapons/resonator_blast.ogg' + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + light_color = LIGHT_COLOR_GREEN + /// The amount by which the stability of the anomaly changes upon impact. + var/stability_delta = 0 + /// The distance the anomaly is pulled towards the shooter upon impact. + var/pull_strenght = 0 + /// The amount of time that beam increase the blocking of the anomaly's normal movement. + var/move_block = 0 + /// The amount of time that beam increase the blocking of the anomaly's impulsive movement. + var/move_impulces_block = 0 + /// The amount by which the strength of the anomaly's effects is temporarily reduced. + var/anom_weaken = 0 + /// The moment at which the reduction in the effects of the anomaly will be reset. + var/weaken_time = 0 + +/obj/item/projectile/beam/anomaly/on_hit(atom/target, blocked, hit_zone) + if(!isanomaly(target)) + return ..() + + do_sparks(clamp(abs(stability_delta) * 2, 3, 10)) + var/obj/effect/anomaly/anomaly = target + if(anomaly.tier != 4 || prob(50)) + anomaly.stability = clamp(anomaly.stability + stability_delta, 0, 100) + + anomaly.move_moment = max(world.time + move_block, anomaly.move_moment) + anomaly.move_impulse_moment = max(world.time + move_impulces_block, anomaly.move_impulse_moment) + if(anom_weaken) + anomaly.weaken = anom_weaken + anomaly.weaken_moment = world.time + weaken_time + + INVOKE_ASYNC(anomaly, TYPE_PROC_REF(/obj/effect/anomaly, go_to), get_turf(firer_source_atom), pull_strenght) + return TRUE + +/obj/item/projectile/beam/anomaly/stabilizer + name = "стабилизирующий луч" + icon_state = "bluelaser" + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_BLUE + +/obj/item/projectile/beam/anomaly/destabilizer + name = "дестабилизирующий луч" + icon_state = "laser" + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser + light_color = LIGHT_COLOR_RED diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index c791df0687a..ef690bf7323 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -313,11 +313,12 @@ name = "dart" icon_state = "cbbolt" damage = 6 + var/volume = 50 var/piercing = FALSE /obj/item/projectile/bullet/dart/New() ..() - create_reagents(50) + create_reagents(volume) reagents.set_reacting(FALSE) /obj/item/projectile/bullet/dart/on_hit(atom/target, blocked = 0, hit_zone) @@ -351,6 +352,7 @@ name = "syringe" icon = 'icons/obj/chemical.dmi' icon_state = "syringeproj" + volume = 15 /obj/item/projectile/bullet/dart/syringe/tranquilizer diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm index b28c80245de..0834115ce1f 100644 --- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm +++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm @@ -404,11 +404,15 @@ mix_message = "The reaction releases an electrical blast!" mix_sound = 'sound/magic/lightningbolt.ogg' +/atom/proc/do_shock_ex(radius, damage = 3.5, animate = FALSE) + var/turf/epicenter = get_turf(src) + for(var/mob/living/L in view(radius, src)) + L.Beam(epicenter, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5) //What? Why are we beaming from the mob to the turf? Turf to mob generates really odd results. + L.electrocute_act(damage, "взрыва электричества") + /datum/chemical_reaction/shock_explosion/on_reaction(datum/reagents/holder, created_volume) var/turf/T = get_turf(holder.my_atom) - for(var/mob/living/L in view(min(8, round(created_volume * 2)), T)) - L.Beam(T, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5) //What? Why are we beaming from the mob to the turf? Turf to mob generates really odd results. - L.electrocute_act(3.5, "взрыва электричества") + T.do_shock_ex(min(8, round(created_volume * 2))) holder.del_reagent("teslium") //Clear all remaining Teslium and Uranium, but leave all other reagents untouched. holder.del_reagent("uranium") diff --git a/code/modules/research/anomaly/anomaly.dm b/code/modules/research/anomaly/anomaly.dm deleted file mode 100644 index c8401b6ed99..00000000000 --- a/code/modules/research/anomaly/anomaly.dm +++ /dev/null @@ -1,53 +0,0 @@ -// Embedded signaller used in anomalies. -/obj/item/assembly/signaler/anomaly - name = "anomaly core" - desc = "The neutralized core of an anomaly. It'd probably be valuable for research." - icon_state = "anomaly_core" - item_state = "electronic" - resistance_flags = FIRE_PROOF - receiving = TRUE - var/anomaly_type = /obj/effect/anomaly - -/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal) - if(..()) - for(var/obj/effect/anomaly/A in get_turf(src)) - A.anomalyNeutralize() - -/obj/item/assembly/signaler/anomaly/attack_self() - return - -//Anomaly cores -/obj/item/assembly/signaler/anomaly/pyro - name = "\improper pyroclastic anomaly core" - desc = "The neutralized core of a pyroclastic anomaly. It feels warm to the touch. It'd probably be valuable for research." - icon_state = "pyro_core" - anomaly_type = /obj/effect/anomaly/pyro - origin_tech = "plasmatech=7" - -/obj/item/assembly/signaler/anomaly/grav - name = "\improper gravitational anomaly core" - desc = "The neutralized core of a gravitational anomaly. It feels much heavier than it looks. It'd probably be valuable for research." - icon_state = "grav_core" - anomaly_type = /obj/effect/anomaly/grav - origin_tech = "magnets=7" - -/obj/item/assembly/signaler/anomaly/flux - name = "\improper flux anomaly core" - desc = "The neutralized core of a flux anomaly. Touching it makes your skin tingle. It'd probably be valuable for research." - icon_state = "flux_core" - anomaly_type = /obj/effect/anomaly/flux - origin_tech = "powerstorage=7" - -/obj/item/assembly/signaler/anomaly/bluespace - name = "\improper bluespace anomaly core" - desc = "The neutralized core of a bluespace anomaly. It keeps phasing in and out of view. It'd probably be valuable for research." - icon_state = "anomaly_core" - anomaly_type = /obj/effect/anomaly/bluespace - origin_tech = "bluespace=7" - -/obj/item/assembly/signaler/anomaly/vortex - name = "\improper vortex anomaly core" - desc = "The neutralized core of a vortex anomaly. It won't sit still, as if some invisible force is acting on it. It'd probably be valuable for research." - icon_state = "vortex_core" - anomaly_type = /obj/effect/anomaly/bhole - origin_tech = "engineering=7" diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index 734787af43b..d03f60f4c46 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -601,3 +601,13 @@ materials = list(MAT_GLASS = 1000, MAT_BLUESPACE = 500) build_path = /obj/item/circuitboard/brs_stationary_scanner category = list ("Research Machinery") + +/datum/design/anomaly_generator + name = "Machine Design (Генератор аномалий)" + desc = "Плата прибора, предназначенного для генерации аномалий." + id = "anomaly_generator" + req_tech = list("programming" = 3) + build_type = IMPRINTER + materials = list(MAT_GLASS = 1000) + build_path = /obj/item/circuitboard/anomaly_generator + category = list ("Research Machinery") diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm index 089cb6de111..530bd37a22e 100644 --- a/code/modules/research/designs/misc_designs.dm +++ b/code/modules/research/designs/misc_designs.dm @@ -151,3 +151,23 @@ materials = list(MAT_METAL = 800, MAT_GLASS = 600) build_path = /obj/item/vending_refill/custom category = list("Miscellaneous") + +/datum/design/anomaly_stabilizer + name = "anomaly stabilizer" + desc = "Продвинутое устройство предназначенное для стабилизации аномалий." + id = "anomaly_stabilizer" + req_tech = list("powerstorage" = 2, "programming" = 4, "magnets" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL=3000, MAT_GLASS=2000) + build_path = /obj/item/gun/energy/anomaly_stabilizer + category = list("Miscellaneous") + +/datum/design/anomaly_analyzer + name = "anomaly analyzer" + desc = "Продвинутое устройство предназначенное для сканирования аномалий." + id = "anomaly_analyzer" + req_tech = list("programming" = 4, "magnets" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL=1000, MAT_GLASS=500) + build_path = /obj/item/anomaly_analyzer + category = list("Miscellaneous") diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index 2ad6c281052..2649298936a 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -76,7 +76,7 @@ Note: Must be placed within 3 tiles of the R&D Console return ATTACK_CHAIN_PROCEED // anomaly cores are only disassembed in the upgraded machine. // 3x4(femto-manipulator,quad-ultra micro-laser,triphasic scanning module) - if(istype(I, /obj/item/assembly/signaler/anomaly) && (decon_mod < 12)) + if(istype(I, /obj/item/assembly/signaler/core) && (decon_mod < 12)) to_chat(user, span_warning("Машина не в состоянии обработать такой сложный образец.")) return ATTACK_CHAIN_PROCEED if(!I.origin_tech) diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index df0112bc7ed..aba1d6eed43 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -195,16 +195,24 @@ /obj/item/slimepotion/sentience/afterattack(mob/living/M, mob/user, proximity_flag, params) if(!proximity_flag || user.incapacitated() || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) return + if(being_used || !ismob(M)) return + if(!isanimal(M) && !is_monkeybasic(M)) - to_chat(user, "[M] is not animal nor lesser life form!") + to_chat(user, span_warning("[M] is not animal nor lesser life form!")) return ..() + if(istype(M, /mob/living/simple_animal/hostile/poison/giant_spider/nurse)) - to_chat(user, "unknown power prevents you from using sentience potion on [M]") + to_chat(user, span_warning("unknown power prevents you from using sentience potion on [M])")) return ..() + + if(istype(M, /mob/living/simple_animal/hostile/airmob)) + to_chat(user, span_warning("[M.declent_ru(NOMINATIVE)] не является формой жизни и не может обрести разум.")) + return ..() + if(M.stat) - to_chat(user, "[M] is dead!") + to_chat(user, span_warning("[M] is dead!")) return ..() if(M.ckey && isanimal(M)) //giving sentience to simple mobs under player control diff --git a/code/modules/ruins/lavalandruin_code/ash_walker_den.dm b/code/modules/ruins/lavalandruin_code/ash_walker_den.dm index 671c24fc971..6ecb96b0486 100644 --- a/code/modules/ruins/lavalandruin_code/ash_walker_den.dm +++ b/code/modules/ruins/lavalandruin_code/ash_walker_den.dm @@ -38,7 +38,7 @@ STOP_PROCESSING(SSprocessing, src) /obj/structure/lavaland/ash_walker/deconstruct(disassembled) - var/core_to_drop = pick(subtypesof(/obj/item/assembly/signaler/anomaly)) + var/core_to_drop = pick(subtypesof(/obj/item/assembly/signaler/core)) new core_to_drop (get_step(loc, pick(GLOB.alldirs))) new /obj/effect/collapse(loc) return ..() diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 756b2100a86..ac30f1f492a 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -35,7 +35,7 @@ return QDEL_HINT_LETMELIVE -/obj/docking_port/has_gravity(turf/T) +/obj/docking_port/get_gravity(turf/T) return FALSE /obj/docking_port/take_damage() diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index 9e5855e8de9..398a1535096 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -1213,7 +1213,7 @@ if(!next_step) COOLDOWN_START(src, spacepod_move_cooldown, 0.5 SECONDS) return FALSE - var/calculated_move_delay = has_gravity(loc) ? GRAVITY_SPEED : NO_GRAVITY_SPEED + var/calculated_move_delay = !no_gravity(loc) ? GRAVITY_SPEED : NO_GRAVITY_SPEED . = Move(next_step, direction) if(ISDIAGONALDIR(direction) && loc == next_step) calculated_move_delay *= sqrt(2) diff --git a/code/modules/station_goals/bfl.dm b/code/modules/station_goals/bfl.dm index fbeaa26bd21..f9c6cf0a39d 100644 --- a/code/modules/station_goals/bfl.dm +++ b/code/modules/station_goals/bfl.dm @@ -607,6 +607,7 @@ burn_stuff(arrived) /obj/effect/bfl_laser/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + SEND_SIGNAL(src, COMSIG_ATOM_HITBY, AM, skipcatch, hitpush, blocked, throwingdatum) burn_stuff(AM) /obj/effect/bfl_laser/process() diff --git a/code/modules/surgery/organs/augments_legs.dm b/code/modules/surgery/organs/augments_legs.dm index 07aba6d05f4..c674ff2d991 100644 --- a/code/modules/surgery/organs/augments_legs.dm +++ b/code/modules/surgery/organs/augments_legs.dm @@ -136,7 +136,7 @@ to_chat(owner, span_warning("The boot's internal propulsion needs to recharge still!")) return - if(!owner.has_gravity()) + if(owner.no_gravity()) to_chat(owner, span_warning("You can't jump without gravity!")) return diff --git a/icons/effects/anomalies.dmi b/icons/effects/anomalies.dmi new file mode 100644 index 00000000000..056f373dca9 Binary files /dev/null and b/icons/effects/anomalies.dmi differ diff --git a/icons/obj/anomaly/anomaly_inhand_l.dmi b/icons/obj/anomaly/anomaly_inhand_l.dmi new file mode 100644 index 00000000000..b781fcb1938 Binary files /dev/null and b/icons/obj/anomaly/anomaly_inhand_l.dmi differ diff --git a/icons/obj/anomaly/anomaly_inhand_r.dmi b/icons/obj/anomaly/anomaly_inhand_r.dmi new file mode 100644 index 00000000000..27e6cb346b9 Binary files /dev/null and b/icons/obj/anomaly/anomaly_inhand_r.dmi differ diff --git a/icons/obj/anomaly/anomaly_stuff.dmi b/icons/obj/anomaly/anomaly_stuff.dmi new file mode 100644 index 00000000000..97be612df98 Binary files /dev/null and b/icons/obj/anomaly/anomaly_stuff.dmi differ diff --git a/icons/obj/assemblies/new_assemblies.dmi b/icons/obj/assemblies/new_assemblies.dmi index b96b2fc6ad1..e990f4cd3ec 100644 Binary files a/icons/obj/assemblies/new_assemblies.dmi and b/icons/obj/assemblies/new_assemblies.dmi differ diff --git a/icons/obj/weapons/techrelic.dmi b/icons/obj/weapons/techrelic.dmi index 06cf9fc1f3e..6c23042cad7 100644 Binary files a/icons/obj/weapons/techrelic.dmi and b/icons/obj/weapons/techrelic.dmi differ diff --git a/paradise.dme b/paradise.dme index d0af00ebdfe..6e4ba86b959 100644 --- a/paradise.dme +++ b/paradise.dme @@ -32,6 +32,7 @@ #include "code\__DEFINES\actionspeed_modification.dm" #include "code\__DEFINES\admin.dm" #include "code\__DEFINES\alerts.dm" +#include "code\__DEFINES\anomalies.dm" #include "code\__DEFINES\antagonists.dm" #include "code\__DEFINES\asset_defines.dm" #include "code\__DEFINES\atmospherics.dm" @@ -385,6 +386,7 @@ #include "code\datums\discord.dm" #include "code\datums\dog_fashion.dm" #include "code\datums\filter.dm" +#include "code\datums\gravity.dm" #include "code\datums\holocall.dm" #include "code\datums\http.dm" #include "code\datums\hud.dm" @@ -1072,7 +1074,6 @@ #include "code\game\objects\objs.dm" #include "code\game\objects\structures.dm" #include "code\game\objects\effects\alien_acid.dm" -#include "code\game\objects\effects\anomalies.dm" #include "code\game\objects\effects\bump_teleporter.dm" #include "code\game\objects\effects\effects.dm" #include "code\game\objects\effects\forcefields.dm" @@ -1127,7 +1128,6 @@ #include "code\game\objects\effects\temporary_visuals\miscellaneous.dm" #include "code\game\objects\effects\temporary_visuals\muzzle_flashes.dm" #include "code\game\objects\effects\temporary_visuals\temporary_visual.dm" -#include "code\game\objects\items\anomaly_beacon.dm" #include "code\game\objects\items\ashtray.dm" #include "code\game\objects\items\blueprints.dm" #include "code\game\objects\items\bodybag.dm" @@ -1249,7 +1249,6 @@ #include "code\game\objects\items\weapons\dna_injector.dm" #include "code\game\objects\items\weapons\dna_upgrader.dm" #include "code\game\objects\items\weapons\dnascrambler.dm" -#include "code\game\objects\items\weapons\experimental_syringe_gun.dm" #include "code\game\objects\items\weapons\explosives.dm" #include "code\game\objects\items\weapons\extinguisher.dm" #include "code\game\objects\items\weapons\flamethrower.dm" @@ -1289,7 +1288,6 @@ #include "code\game\objects\items\weapons\syndie_RCD.dm" #include "code\game\objects\items\weapons\tape.dm" #include "code\game\objects\items\weapons\teleportation.dm" -#include "code\game\objects\items\weapons\tuned_anomalous_teleporter.dm" #include "code\game\objects\items\weapons\twohanded.dm" #include "code\game\objects\items\weapons\vending_items.dm" #include "code\game\objects\items\weapons\weaponry.dm" @@ -1302,7 +1300,6 @@ #include "code\game\objects\items\weapons\grenades\confetti.dm" #include "code\game\objects\items\weapons\grenades\custom_grenades.dm" #include "code\game\objects\items\weapons\grenades\emgrenade.dm" -#include "code\game\objects\items\weapons\grenades\fauna_bomb.dm" #include "code\game\objects\items\weapons\grenades\flashbang.dm" #include "code\game\objects\items\weapons\grenades\frag.dm" #include "code\game\objects\items\weapons\grenades\ghettobomb.dm" @@ -1579,6 +1576,34 @@ #include "code\modules\admin\verbs\SDQL2\SDQL_2.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm" #include "code\modules\admin\verbs\SDQL2\useful_procs.dm" +#include "code\modules\anomalies\anomaly_analyzer.dm" +#include "code\modules\anomalies\anomaly_generator.dm" +#include "code\modules\anomalies\anomaly_stabilizer.dm" +#include "code\modules\anomalies\anomaly_upgrader.dm" +#include "code\modules\anomalies\cores.dm" +#include "code\modules\anomalies\gen_datums.dm" +#include "code\modules\anomalies\old.dm" +#include "code\modules\anomalies\anomalies\anomaly.dm" +#include "code\modules\anomalies\anomalies\atmospheric.dm" +#include "code\modules\anomalies\anomalies\bluespace.dm" +#include "code\modules\anomalies\anomalies\energetic.dm" +#include "code\modules\anomalies\anomalies\gravitational.dm" +#include "code\modules\anomalies\anomalies\vortex.dm" +#include "code\modules\anomalies\impulses\atmosferics.dm" +#include "code\modules\anomalies\impulses\bluespace.dm" +#include "code\modules\anomalies\impulses\energetic.dm" +#include "code\modules\anomalies\impulses\gravitational.dm" +#include "code\modules\anomalies\impulses\impulse.dm" +#include "code\modules\anomalies\impulses\vortex.dm" +#include "code\modules\anomalies\items\anomaly_beacon.dm" +#include "code\modules\anomalies\items\bsg.dm" +#include "code\modules\anomalies\items\experimental_syringe_gun.dm" +#include "code\modules\anomalies\items\gravitational_boots.dm" +#include "code\modules\anomalies\items\pyro_claws.dm" +#include "code\modules\anomalies\items\tuned_anomalous_teleporter.dm" +#include "code\modules\anomalies\items\fauna_bomb\air_mob.dm" +#include "code\modules\anomalies\items\fauna_bomb\fauna_bomb.dm" +#include "code\modules\anomalies\items\fauna_bomb\mob_data.dm" #include "code\modules\antagonists\_common\antag_datum.dm" #include "code\modules\antagonists\_common\antag_helpers.dm" #include "code\modules\antagonists\_common\antag_hud.dm" @@ -2962,7 +2987,6 @@ #include "code\modules\research\rdmachines.dm" #include "code\modules\research\research.dm" #include "code\modules\research\server.dm" -#include "code\modules\research\anomaly\anomaly.dm" #include "code\modules\research\designs\AI_module_designs.dm" #include "code\modules\research\designs\autolathe_designs.dm" #include "code\modules\research\designs\biogenerator_designs.dm" diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss index 820b57fb212..eb4ee20d38e 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss @@ -1281,3 +1281,33 @@ span.body .coderesponses { .proradio { color: #e3027a; } + +.bluespace_anomaly { + color: #2cb5ff; + font-weight: bold; + font-size: 120%; +} + +.energetic_anomaly { + color: #ffe925; + font-weight: bold; + font-size: 120%; +} + +.atmospferic_anomaly { + color: #ff0707; + font-weight: bold; + font-size: 120%; +} + +.gravitational_anomaly { + color: #00a7bd; + font-weight: bold; + font-size: 120%; +} + +.vortex_anomaly { + color: #486374; + font-weight: bold; + font-size: 120%; +} diff --git a/tgui/packages/tgui/interfaces/AnomalyGenerator.js b/tgui/packages/tgui/interfaces/AnomalyGenerator.js new file mode 100644 index 00000000000..8c032f91a94 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AnomalyGenerator.js @@ -0,0 +1,166 @@ +import { classes } from 'common/react'; +import { useBackend } from '../backend'; +import { + Box, + Button, + Section, + Stack, + Table, + AnimatedNumber, + Icon, + LabeledList, + ProgressBar, + ImageButton, +} from '../components'; +import { Window } from '../layouts'; + +export const AnomalyGenerator = (props, context) => { + const { act, data } = useBackend(context); + const { + type, + tier, + creating, + req_energy, + req_item, + anomaly_type, + charge, + generating, + use_acps, + use_smeses, + use_powernet, + has_powernet, + last_charge, + } = data; + + if (generating) { + return ( + + + + +