diff --git a/code/__DEFINES/anomalies.dm b/code/__DEFINES/anomalies.dm index 1be5237d898..b8b38f777aa 100644 --- a/code/__DEFINES/anomalies.dm +++ b/code/__DEFINES/anomalies.dm @@ -12,12 +12,24 @@ #define iscoret2(A) (istype((A), /obj/item/assembly/signaler/anomaly/tier2)) #define iscoret3(A) (istype((A), /obj/item/assembly/signaler/anomaly/tier3)) -#define iscoreempty(A) (type in list(/obj/item/assembly/signaler/anomaly/tier1, /obj/item/assembly/signaler/anomaly/tier2, /obj/item/assembly/signaler/anomaly/tier3)) -#define iscoreatmos(A) (type in list(/obj/item/assembly/signaler/anomaly/tier1/pyro, /obj/item/assembly/signaler/anomaly/tier2/pyro, /obj/item/assembly/signaler/anomaly/tier3/pyro)) -#define iscorebluespace(A) (type in list(/obj/item/assembly/signaler/anomaly/tier1/bluespace, /obj/item/assembly/signaler/anomaly/tier2/bluespace, /obj/item/assembly/signaler/anomaly/tier3/bluespace)) -#define iscoregrav(A) (type in list(/obj/item/assembly/signaler/anomaly/tier1/grav, /obj/item/assembly/signaler/anomaly/tier2/grav, /obj/item/assembly/signaler/anomaly/tier3/grav)) -#define iscorevortex(A) (type in list(/obj/item/assembly/signaler/anomaly/tier1/vortex, /obj/item/assembly/signaler/anomaly/tier2/vortex, /obj/item/assembly/signaler/anomaly/tier3/vortex)) -#define iscoreflux(A) (type in list(/obj/item/assembly/signaler/anomaly/tier1/flux, /obj/item/assembly/signaler/anomaly/tier2/flux, /obj/item/assembly/signaler/anomaly/tier3/flux)) +#define iscoreempty(A) ((A.type == /obj/item/assembly/signaler/anomaly/tier1) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier2) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier3)) +#define iscoreatmos(A) ((A.type == /obj/item/assembly/signaler/anomaly/tier1/pyro) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier2/pyro) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier3/pyro)) +#define iscorebluespace(A) ((A.type == /obj/item/assembly/signaler/anomaly/tier1/bluespace) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier2/bluespace) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier3/bluespace)) +#define iscoregrav(A) ((A.type == /obj/item/assembly/signaler/anomaly/tier1/grav) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier2/grav) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier3/grav)) +#define iscorevortex(A) ((A.type == /obj/item/assembly/signaler/anomaly/tier1/vortex) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier2/vortex) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier3/vortex)) +#define iscoreflux(A) ((A.type == /obj/item/assembly/signaler/anomaly/tier1/flux) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier2/flux) || \ + (A.type == /obj/item/assembly/signaler/anomaly/tier3/flux)) GLOBAL_LIST_INIT(anomaly_types, list( "1" = list( @@ -42,3 +54,14 @@ GLOBAL_LIST_INIT(anomaly_types, list( ANOMALY_TYPE_FLUX = /obj/item/assembly/signaler/anomaly/tier3/flux, ), )) + +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 diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index f42f919f917..b98f503681c 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1167,7 +1167,7 @@ update_icon() return TRUE -/atom/movable/proc/random_throw(range_low, range_high, speed) +/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) diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm index 895384ac923..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/tier2/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/items/weapons/tuned_anomalous_teleporter.dm b/code/game/objects/items/weapons/tuned_anomalous_teleporter.dm index e12eb1b7624..c0ea20e2280 100644 --- a/code/game/objects/items/weapons/tuned_anomalous_teleporter.dm +++ b/code/game/objects/items/weapons/tuned_anomalous_teleporter.dm @@ -1,6 +1,13 @@ -/obj/item/tuned_anomalous_teleporter +/obj/item/assembly/tuned_anomalous_teleporter name = "tuned anomalous teleporter" - desc = "A portable item using blue-space technology." + 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' @@ -14,58 +21,164 @@ 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/anomaly/core = null /// 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 + /// 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 - var/tp_range = 5 // range of teleportations - origin_tech = "bluespace=5" + /// 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/activate(mob/user) + if(max_tp_range < 5) + if(user) + to_chat(user, span_warning("[declent_ru(NOMINATIVE)] не может вас телепортировать, \ + из-за отсутствия достаточно сильного ядра.")) + return -/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(user) + to_chat(user, span_warning("[declent_ru(NOMINATIVE)] не может вас телепортировать, \ + из-за того, что он временно выведен из строя.")) + + return + if(!COOLDOWN_FINISHED(src, tuned_anomalous_teleporter_cooldown)) - to_chat(user, span_warning("[src] is still recharging.")) - return FALSE + if(user) + to_chat(user, span_warning("[declent_ru(NOMINATIVE)] все еще перезаряжается.")) + + 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(user.dir)) % 360) - var/turf/T1 = get_turf(user) + 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, user) - s2.set_up(5, FALSE, user) - TP.start(user, T1, FALSE, TRUE, s1, s2, 'sound/effects/phasein.ogg', ) + 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/tuned_anomalous_teleporter/emp_act(severity) +/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/tuned_anomalous_teleporter/proc/make_inactive(severity) +/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/tuned_anomalous_teleporter/examine(mob/user) +/obj/item/assembly/tuned_anomalous_teleporter/examine(mob/user) . = ..() if(emp_timer > world.time) - . += span_warning("It looks inactive.") + . += 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)) + user.balloon_alert(user, "не отпустить") + return + + core = I + user.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 /datum/crafting_recipe/tuned_anomalous_teleporter name = "Tuned anomalous teleporter" - result = /obj/item/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/assembly/signaler/anomaly/tier2/bluespace = 1, /obj/item/gps = 1, /obj/item/stack/ore/bluespace_crystal, /obj/item/stack/sheet/metal = 2, diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm index be133477b63..11b4de839c6 100644 --- a/code/game/objects/items/weapons/twohanded.dm +++ b/code/game/objects/items/weapons/twohanded.dm @@ -922,7 +922,14 @@ //pyro claws /obj/item/twohanded/required/pyro_claws name = "hardplasma energy claws" - desc = "The power of the sun, in the claws of your hand." + ru_names = list(NOMINATIVE = "энергокогти", \ + GENITIVE = "энергокогтей", \ + DATIVE = "энергокогтям", \ + ACCUSATIVE = "энергокогти", \ + INSTRUMENTAL = "энергокогтями", \ + PREPOSITIONAL = "энергокогтях") + desc = "Сила солнца в ваших когтях." + gender = PLURAL icon_state = "pyro_claws" item_flags = ABSTRACT|DROPDEL force = 25 @@ -950,8 +957,10 @@ /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 @@ -959,20 +968,33 @@ return if(A.locked) - to_chat(user, "The airlock's bolts prevent it from being forced.") + to_chat(user, span_notice("Болты шлюза не позволяют взломать его силой.")) 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.") + 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("[user] forces the airlock open with [user.p_their()] [name]!", "You force open the airlock.", "You hear a metal screeching sound.") + + user.visible_message(span_warning("[user] силой открыл шлюз при помощи [declent_ru(GENITIVE)]!"), \ + span_warning("Вы силой открыли шлюз."), \ + span_warning("Вы слышите металлический скрежет.")) 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." + ru_names = list(NOMINATIVE = "плавящие перчатки", \ + GENITIVE = "плавящих перчаток", \ + DATIVE = "плавящим перчаткам", \ + ACCUSATIVE = "плавящие перчатки", \ + INSTRUMENTAL = "плавящими перчатками", \ + PREPOSITIONAL = "плавящих перчатках") + desc = "Перчатки разработаенные Cybersun Industries после того, как один из солдат прикрепил атмосферное ядро ​​к \ + энергетическому мечу, и нашел результат весьма эффективными." + gender = PLURAL item_state = "pyro" item_color = "pyro" icon_state = "pyro" @@ -980,7 +1002,7 @@ actions_types = list(/datum/action/item_action/toggle) var/on_cooldown = FALSE var/used = FALSE - var/obj/item/assembly/signaler/anomaly/tier2/pyro/core + var/obj/item/assembly/signaler/anomaly/core /obj/item/clothing/gloves/color/black/pyro_claws/Destroy() QDEL_NULL(core) @@ -989,59 +1011,94 @@ /obj/item/clothing/gloves/color/black/pyro_claws/examine(mob/user) . = ..() if(core) - . += "[src] are fully operational!" + . += span_notice("[declent_ru(NOMINATIVE)] полностью работоспособны.") else - . += "It is missing a pyroclastic anomaly core." + . += span_warning("[declent_ru(NOMINATIVE)] требуют атмосферное ядро для работы!") /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 + return slot == ITEM_SLOT_GLOVES /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!") + to_chat(user, span_notice("В [declent_ru(PREPOSITIONAL)] не хватает ядра!")) return + if(on_cooldown) - to_chat(user, "[src] is on cooldown!") + to_chat(user, span_notice("[declent_ru(NOMINATIVE)] перезаряжаются.")) do_sparks(rand(1,6), 1, loc) return + if(used) - visible_message("Energy claws slides back into the depths of [loc]'s wrists.") + 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, "[src] are unable to deploy the blades with the items in your hands!") + to_chat(user, span_notice("[declent_ru(NOMINATIVE)] не могут выпустить клинки, пока у вас в руках есть предметы!")) 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) + + 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(istype(I, /obj/item/assembly/signaler/anomaly/tier2/pyro)) + if(iscoreatmos(I)) + var/obj/item/assembly/signaler/anomaly/I_core = I + if(I_core.get_strenght() < 100) + user.balloon_alert(user, "ядро слишком слабо") + return + add_fingerprint(user) + var/msg = "ядро вставлено" if(core) - to_chat(user, span_warning("The [core.name] is already installed.")) - return ATTACK_CHAIN_PROCEED + user.put_in_hands(core) + msg = "ядро заменено" + 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.")) + user.balloon_alert(user, "не отпустить") + return + + 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("Internal plasma canisters recharged. Gloves sufficiently cooled") + atom_say("Внутренние плазменные баллоны перезаряжены. Перчатки достаточно охлаждены.") /obj/item/twohanded/fishingrod name = "ol' reliable" diff --git a/code/modules/anomalies/anomalies/anomaly.dm b/code/modules/anomalies/anomalies/anomaly.dm index 698c9083571..c8fdd8011e5 100644 --- a/code/modules/anomalies/anomalies/anomaly.dm +++ b/code/modules/anomalies/anomalies/anomaly.dm @@ -4,6 +4,7 @@ icon_state = "bhole3" gender = FEMALE anchored = TRUE + density = TRUE alpha = 0 light_range = 3 layer = ABOVE_ALL_MOB_LAYER @@ -46,6 +47,7 @@ sleep(1 SECONDS) /obj/effect/anomaly/Initialize(spawnloc, spawn_strenght = rand(30, 70), spawn_stability = rand(10, 29)) + GLOB.created_anomalies[anomaly_type]++ . = ..() if(!get_area(src)) return INITIALIZE_HINT_QDEL @@ -72,7 +74,7 @@ // It is in function because the size will change depending on the strength of the anomaly. /obj/effect/anomaly/proc/set_strenght(new_strenght) - strenght = new_strenght + strenght = clamp(new_strenght, 0, 100) var/matrix/M = matrix() var/mult = (strenght + 100) / 200 M.Scale(mult, mult) @@ -120,26 +122,56 @@ new stronger_anomaly_type(loc, rand(20, 50), clamp(stability - rand(10, 20), 0, 100)) qdel(src) -/obj/effect/anomaly/CanAllowThrough(atom/movable/mover, border_dir) - . = ..() - if(istype(mover, /obj/item/projectile/beam/anomaly)) - return FALSE +/obj/effect/anomaly/proc/mob_touch_effect(mob/living/M) + return TRUE - if(ismob(mover)) - return mob_touch_effect(mover) +/obj/effect/anomaly/proc/core_touch_effect(obj/item/assembly/signaler/anomaly/core) + var/mult = 1 + if(core.tier <= tier) + mult *= 1 << (core.tier - tier) + else + mult /= 1 << (core.tier - tier) - if(isitem(mover)) - return item_touch_effect(mover) + if(!iscoreempty(core)) + core.visible_message(span_warning("[core.declent_ru(NOMINATIVE)] распадается передавая свой заряд [declent_ru(DATIVE)].")) + set_strenght(strenght + core.strenght / mult) + qdel(core) + do_sparks(5, FALSE, src) + return + var/charge_delta = min(100, round(strenght / 3 * mult)) + var/new_charge = core.strenght + charge_delta -/obj/effect/anomaly/proc/mob_touch_effect(mob/living/M) - return TRUE + do_sparks(5, FALSE, src) + set_strenght(strenght - round(charge_delta / mult)) + + if(new_charge <= 50) + core.strenght = new_charge + core.random_throw(3, 6, 5) + core.visible_message(span_warning("[core.declent_ru(NOMINATIVE)] заряжается от [declent_ru(GENITIVE)], \ + но остается пустым из-за низкого заряда.")) + return + + var/obj/item/assembly/signaler/anomaly/new_core = new core_type(core.loc, new_charge) + new_core.visible_message(span_warning("[core.declent_ru(NOMINATIVE)] заряжается от [declent_ru(GENITIVE)], \ + и становится [new_core.declent_ru(INSTRUMENTAL)].")) + 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(iscore(I)) + var/obj/item/assembly/signaler/anomaly/core = I + if(core.born_moment + 1 SECONDS >= world.time) + return TRUE + + core_touch_effect(core) + return FALSE + if(!I.origin_tech) return @@ -171,7 +203,11 @@ /obj/effect/anomaly/Bumped(atom/movable/moving_atom) . = ..() - item_touch_effect(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)) @@ -195,10 +231,10 @@ return max(min(strenght, 10), strenght - weaken) /obj/effect/anomaly/process() - if(stability < 30) + if(stability < ANOMALY_GROW_STABILITY) set_strenght(strenght + 1) - if(stability > 70) + if(stability > ANOMALY_DECREASE_STABILITY) set_strenght(strenght - 1) if(strenght == 100) @@ -217,7 +253,7 @@ level_down() return - if(!prob(get_strenght())) + if(!prob(get_strenght()) || stability > 60) return if(normal_move()) diff --git a/code/modules/anomalies/anomalies/atmospheric.dm b/code/modules/anomalies/anomalies/atmospheric.dm index b26e7f05a59..85f203d32cc 100644 --- a/code/modules/anomalies/anomalies/atmospheric.dm +++ b/code/modules/anomalies/anomalies/atmospheric.dm @@ -41,7 +41,7 @@ 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 = 0 to rand(collapse_slimes_low, collapse_slimes_high)) + for(var/i = 1 to rand(collapse_slimes_low, collapse_slimes_high)) INVOKE_ASYNC(src, PROC_REF(make_slime)) . = ..() diff --git a/code/modules/anomalies/anomalies/gravitational.dm b/code/modules/anomalies/anomalies/gravitational.dm index d8099f03ee1..a1fcad8dc43 100644 --- a/code/modules/anomalies/anomalies/gravitational.dm +++ b/code/modules/anomalies/anomalies/gravitational.dm @@ -14,6 +14,7 @@ for(var/atom/movable/A in view(tier * 2, src)) if(!iseffect(A)) A.random_throw(tier, tier * 3, 5) + A.update_icon() . = ..() @@ -30,7 +31,10 @@ /obj/effect/anomaly/grav/item_touch_effect(obj/item/I) . = ..() - random_gravity_change(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/grav/tier1 name = "малая гравитационная аномалия" diff --git a/code/modules/anomalies/anomaly_analyzer.dm b/code/modules/anomalies/anomaly_analyzer.dm new file mode 100644 index 00000000000..ed51159f8ae --- /dev/null +++ b/code/modules/anomalies/anomaly_analyzer.dm @@ -0,0 +1,55 @@ +/obj/item/anomaly_analyzer + name = "сканер аномалий" + ru_names = list( + NOMINATIVE = "сканер аномалий", \ + GENITIVE = "сканера аномалий", \ + DATIVE = "сканеру аномалий", \ + ACCUSATIVE = "сканер аномалий", \ + INSTRUMENTAL = "сканером аномалий", \ + PREPOSITIONAL = "сканере аномалий" + ) + desc = "Продвинутое устройство предназначенное для сканирования аномалий. \ + Выводит достаточно полную информацию о сканируемой аномалии. \ + Может сканировать аномалии на расстоянии." + icon = 'icons/obj/device.dmi' + icon_state = "atmos" + item_state = "analyzer" + 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() + . += "Сила аномалии: [target.strenght]" + . += "Стабильность аномалии: [target.strenght]" + if(target.stability < ANOMALY_GROW_STABILITY) + . += "Состояние аномалии: " + span_warning("Рост") + else if(target.stability > ANOMALY_DECREASE_STABILITY) + . += "Состояние аномалии: Уменьшение" + else + . += "Состояние аномалии: Стабильное" + + . += "Импульсы:\n" + for(var/datum/anomaly_impulse/impulse in target.impulses) + . += " [impulse.name]" + . += "  Описание: [impulse.desc]" + . += "  Время между импульсами: [impulse.scale_by_strenght(impulse.period_low, impulse.period_high) / 10]" + . += "  Блокируящая стабильность: [impulse.stability_high]" + +/obj/item/anomaly_analyzer/proc/show(mob/user) + var/datum/browser/popup = new(user, "anomalyscanner", scan_title, 400, 600) + 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 index 505e2845b9f..bad7a3b5fd5 100644 --- a/code/modules/anomalies/anomaly_generator.dm +++ b/code/modules/anomalies/anomaly_generator.dm @@ -19,6 +19,7 @@ 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 @@ -32,7 +33,7 @@ /// Last charge per second. var/last_charge = 0 /// List of items placed inside. - var/list/obj/item/containment = list() + var/list/obj/item/assembly/signaler/anomaly/containment = list() /// The maximum number of items that can be in the anomaly generator. var/containment_limit = 2 @@ -108,6 +109,9 @@ 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] в [src]."), \ @@ -211,9 +215,11 @@ containment.Remove(I) /obj/machinery/power/anomaly_generator/proc/get_req_energy() - var/mult = 1 + var/mult if(selected_type == ANOMALY_TYPE_RANDOM) - mult = 3 + mult = selected_tier == 1 ? 0.3 : 3 + else + mult = 1 + GLOB.created_anomalies[selected_type] / 4 switch(selected_tier) if("1") @@ -258,13 +264,11 @@ /obj/machinery/power/anomaly_generator/process() if((stat & BROKEN) || !cur_anomaly) STOP_PROCESSING(SSprocessing, src) - return - if(charge == get_req_energy()) - STOP_PROCESSING(SSprocessing, src) + if(charge >= get_req_energy()) finish_generation() cur_anomaly = null - return + STOP_PROCESSING(SSprocessing, src) if(stat & NOPOWER) return diff --git a/code/modules/anomalies/anomaly_stabilizer.dm b/code/modules/anomalies/anomaly_stabilizer.dm index 93b9867c9d2..4a85b7bbe2a 100644 --- a/code/modules/anomalies/anomaly_stabilizer.dm +++ b/code/modules/anomalies/anomaly_stabilizer.dm @@ -10,10 +10,14 @@ PREPOSITIONAL = "стабилизаторе аномалий" ) desc = "Продвинутое устройство предназначенное для стабилизации аномалий. \ - Можно вставить до двух ядер аномалий, для улучшения." + Можно вставить до двух любых не пустых ядер аномалий, для улучшения, \ + в зависимости от типа и уровня ядер." icon = 'icons/obj/weapons/energy.dmi' + 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 /// Cores inserted into this anomaly stabilizer. var/list/obj/item/assembly/signaler/anomaly/cores = list() /// Range of allowed stability deltas. If val - X, range is [-x; x]. @@ -30,21 +34,19 @@ 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 moment at which the reduction in the effects of the anomaly will be reset. + /// 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() /obj/item/gun/energy/anomaly_stabilizer/attack_self(mob/living/user) + add_fingerprint(user) ui_interact(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, "Anomaly_Stabilizer", "Стабилизатор аномалий") - ui.set_autoupdate(TRUE) - ui.open() - - /obj/item/gun/energy/anomaly_stabilizer/newshot() if(!ammo_type || !cell) return @@ -53,12 +55,12 @@ if(cell.charge < shot.e_cost) return - chambered = shot //...prepare a new shot based on the current ammo type selected + chambered = new shot if(!chambered.BB) chambered.newshot() var/obj/item/ammo_casing/energy/anomaly/en_chambered = chambered - en_chambered.e_cost *= stability_delta * stability_delta + en_chambered.e_cost *= max(1, stability_delta * stability_delta) var/obj/item/projectile/beam/anomaly/BB = chambered.BB BB.stability_delta = stability_delta @@ -68,7 +70,7 @@ BB.anom_weaken = weaken_val BB.weaken_time = weaken_time -/obj/item/gun/energy/anomaly_stabilizer/proc/update_stability_delta(mob/user, new_val) +/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 @@ -80,10 +82,10 @@ ammo_type = list(/obj/item/ammo_casing/energy/anomaly) -/obj/item/gun/energy/anomaly_stabilizer/proc/eject_core(index) - if(ishuman(loc)) - var/mob/living/carbon/human/user = loc +/obj/item/gun/energy/anomaly_stabilizer/proc/eject_core(index, mob/user) + if(user) user.put_in_hands(cores[index]) + user.balloon_alert(user, "ядро извлечено") else cores[index].forceMove(get_turf(src)) @@ -92,21 +94,26 @@ /obj/item/gun/energy/anomaly_stabilizer/proc/insert_core(obj/item/assembly/signaler/anomaly/core, mob/user) + add_fingerprint(user) if(iscoreempty(core)) - user.balloon_alert("ядро пусто") + user.balloon_alert(user, "ядро пусто") return ATTACK_CHAIN_PROCEED if(!user.drop_transfer_item_to_loc(core, src)) - user.balloon_alert("не отпустить") + user.balloon_alert(user, "не отпустить") return ATTACK_CHAIN_PROCEED if(cores.len >= 2) - balloon_alert(user, "нет места") + user.balloon_alert(user, "нет места") + return ATTACK_CHAIN_PROCEED + + if(!user.drop_transfer_item_to_loc(core, src)) + user.balloon_alert(user, "не отпустить") return ATTACK_CHAIN_PROCEED cores.Add(core) update_cores() - balloon_alert(user, "ядро вставлено") + user.balloon_alert(user, "ядро вставлено") return ATTACK_CHAIN_PROCEED /obj/item/gun/energy/anomaly_stabilizer/proc/update_cores() @@ -133,17 +140,17 @@ strenght_gravitation += strenght stability_range = 1 + round(strenght_energetic / 50) - stability_delta = clamp(stability_delta, -stability_range, stability_range) + update_stability_delta(stability_delta) pull_range = strenght_gravitation / 50 choosen_pull_dist = clamp(choosen_pull_dist, -pull_range, pull_range) - block_move_time = strenght_vortex / 100 + block_move_time = (strenght_vortex / 100) SECONDS - block_move_impulses_time = strenght_bluespace / 100 + block_move_impulses_time = (strenght_bluespace / 100) SECONDS weaken_val = strenght_atmospheric / 3 - weaken_time = strenght_atmospheric / 50 + weaken_time = (strenght_atmospheric / 50) SECONDS newshot() @@ -151,8 +158,82 @@ 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)) + user.balloon_alert(user, "не отпустить") + return ATTACK_CHAIN_PROCEED + + user.put_in_hands(cell) + cell = I + cell_type = I.type + user.balloon_alert(user, "батарейка заменена") + return ATTACK_CHAIN_PROCEED + if(!iscore(I)) return ..() - insert_core(I, user) - return ATTACK_CHAIN_PROCEED + 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", name) + 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) + newshot() + + if("change_pull_dist") + var/new_val = text2num(params["new_val"]) + pull_range = 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] выстрелов и изменение стабильности на [shots * stability_delta] при текущих настройках.") diff --git a/code/modules/anomalies/cores.dm b/code/modules/anomalies/cores.dm index 12a76510b6a..fa3f439469a 100644 --- a/code/modules/anomalies/cores.dm +++ b/code/modules/anomalies/cores.dm @@ -12,15 +12,26 @@ var/strenght = 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/anomaly/tier2/New(spawnloc, strenght = rand(40, 60)) +/obj/item/assembly/signaler/anomaly/examine(mob/user) + . = ..() + . += span_info("Текущий заряд: [strenght].") + . += span_info("Текущая сила: [get_strenght()].") + +/obj/item/assembly/signaler/anomaly/New(spawnloc, strenght = rand(51, 60)) . = ..() src.strenght = strenght + born_moment = world.time -/obj/item/assembly/signaler/anomaly/tier2/receive_signal(datum/signal/signal) - if(..()) - for(var/obj/effect/old_anomaly/A in get_turf(src)) - A.anomalyNeutralize() +// Used in old anomalies. +/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal) + if(!..()) + return + + for(var/obj/effect/old_anomaly/A in get_turf(src)) + A.anomalyNeutralize() /obj/item/assembly/signaler/anomaly/attack_self() return @@ -41,7 +52,8 @@ ACCUSATIVE = "пустое ядро малой аномалии", \ INSTRUMENTAL = "пустым ядром малой аномалии", \ PREPOSITIONAL = "пустом ядре малой аномалии") - desc = "Вероятно, его можно как-то зарядить." + desc = "Не похоже что силы аномалии на момент стабилизации хватило, чтобы придать ядру какие-то свойства. \ + Вероятно, его можно как-то зарядить." icon_state = "pyro_core" anomaly_type = null origin_tech = "materials=3" // clonable by experimentor @@ -122,7 +134,8 @@ ACCUSATIVE = "пустое ядро аномалии", \ INSTRUMENTAL = "пустым ядром аномалии", \ PREPOSITIONAL = "пустом ядре аномалии") - desc = "Вероятно, его можно как-то зарядить." + desc = "Не похоже что силы аномалии на момент стабилизации хватило, чтобы придать ядру какие-то свойства. \ + Вероятно, его можно как-то зарядить." icon_state = "pyro_core" anomaly_type = null origin_tech = "materials=5" // not clonable by experimentor @@ -173,7 +186,8 @@ ACCUSATIVE = "пустое ядро большой аномалии", \ INSTRUMENTAL = "пустым ядром большой аномалии", \ PREPOSITIONAL = "пустом ядре большой аномалии") - desc = "Вероятно, его можно как-то зарядить." + desc = "Не похоже что силы аномалии на момент стабилизации хватило, чтобы придать ядру какие-то свойства. \ + Вероятно, его можно как-то зарядить." icon_state = "pyro_core" anomaly_type = null origin_tech = "materials=7" // Sorry, not clonable by experimentor @@ -192,6 +206,8 @@ anomaly_type = /obj/effect/anomaly/pyro/tier3 origin_tech = "plasmatech=8" +/obj/item/assembly/signaler/anomaly/tier3/pyro/ + /obj/item/assembly/signaler/anomaly/tier3/grav name = "ядро большой гравитационной аномалии" ru_names = list(NOMINATIVE = "ядро большой гравитационной аномалии", \ diff --git a/code/modules/anomalies/gen_datums.dm b/code/modules/anomalies/gen_datums.dm index 72eaf0e28e1..d17eb9eff92 100644 --- a/code/modules/anomalies/gen_datums.dm +++ b/code/modules/anomalies/gen_datums.dm @@ -37,7 +37,6 @@ if(!T.is_safe()) return FALSE - // Не забыть добавить проверку на наличие других аномалий. return TRUE /datum/anomaly_gen_datum/proc/is_possible_turf(turf/T) @@ -47,7 +46,7 @@ var/list/used = list() if(use_items) used = get_used(containment) - if(!used.len) + if(!used.len && !istype(src, /datum/anomaly_gen_datum/tier1)) return FALSE var/turf/choosen @@ -85,47 +84,27 @@ /datum/anomaly_gen_datum/tier1/pyroclastic anomaly_type = "малая атмосферная" anomaly = /obj/effect/anomaly/pyro/tier1 - req_item = "Балон" - -/datum/anomaly_gen_datum/tier1/pyroclastic/is_req_item(obj/item/I) - return istype(I, /obj/item/tank/internals) - + req_item = "-" /datum/anomaly_gen_datum/tier1/bluespace anomaly_type = "малая блюспейс" anomaly = /obj/effect/anomaly/bluespace/tier1 - req_item = "Блюспейс кристалл" - -/datum/anomaly_gen_datum/tier1/bluespace/is_req_item(obj/item/I) - return istype(I, /obj/item/stack/sheet/bluespace_crystal) - + req_item = "-" /datum/anomaly_gen_datum/tier1/vortex anomaly_type = "малая вихревая" anomaly = /obj/effect/anomaly/vortex/tier1 - req_item = "Контейнер с жидкой темной материей" - -/datum/anomaly_gen_datum/tier1/vortex/is_req_item(obj/item/I) - return istype(I, /obj/item/reagent_containers/glass) - + req_item = "-" /datum/anomaly_gen_datum/tier1/grav anomaly_type = "малая гравитационная" anomaly = /obj/effect/anomaly/grav/tier1 - req_item = "Лист урана" - -/datum/anomaly_gen_datum/tier1/grav/is_req_item(obj/item/I) - return istype(I, /obj/item/stack/sheet/mineral/uranium) - + req_item = "-" /datum/anomaly_gen_datum/tier1/flux anomaly_type = "малая энергетическая" anomaly = /obj/effect/anomaly/flux/tier1 - req_item = "Что угодно обладающее энергией" - -/datum/anomaly_gen_datum/tier1/flux/is_req_item(obj/item/I) - return istype(I, /obj/item/stock_parts/cell) || get_cell_from(I) - + req_item = "-" //==================================== TIER 2 =========================================== 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/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm index 99a4f082c13..1bec33f04fe 100644 --- a/code/modules/clothing/shoes/magboots.dm +++ b/code/modules/clothing/shoes/magboots.dm @@ -174,7 +174,14 @@ /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." + ru_names = list(NOMINATIVE = "настраеваемый аномальный телепортер", \ + GENITIVE = "настраеваемого аномального телепортера", \ + DATIVE = "настраеваемому аномальному телепортеру", \ + ACCUSATIVE = "настраеваемый аномальный телепортер", \ + INSTRUMENTAL = "настраеваемым аномальным телепортером", \ + PREPOSITIONAL = "настраеваемом аномальном телепортере") + desc = "Эти экспериментальные магбутсы обходят замедление обычных, за счет миниатюрных гравитационных в подошвах. \ + К сожалению, для работы им необходимо ядро гравитационной аномалии." 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 diff --git a/code/modules/events/anomaly.dm b/code/modules/events/anomaly.dm index d2076e96ca1..7b430153c51 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/old_anomaly/anomaly_path = /obj/effect/old_anomaly/flux + var/obj/effect/anomaly/anomaly_path = /obj/effect/anomaly/flux/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 74f68185381..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/old_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 026c59f7bd4..9eaea672ab0 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/old_anomaly/flux + anomaly_path = /obj/effect/anomaly/flux/tier2 prefix_message = "На сканерах дальнего действия обнаружена поточная гиперэнергетическая аномалия." diff --git a/code/modules/events/anomaly_grav.dm b/code/modules/events/anomaly_grav.dm index 17cb94aa8e1..6de7c3d9a98 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/old_anomaly/grav + anomaly_path = /obj/effect/anomaly/grav/tier2 prefix_message = "На сканерах дальнего действия обнаружена гравитационная аномалия." diff --git a/code/modules/events/anomaly_pyro.dm b/code/modules/events/anomaly_pyro.dm index 324dac1915f..9c3de203663 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/old_anomaly/pyro + anomaly_path = /obj/effect/anomaly/pyro/tier2 prefix_message = "На сканерах дальнего действия обнаружена атмосферная аномалия." diff --git a/code/modules/events/anomaly_vortex.dm b/code/modules/events/anomaly_vortex.dm index f7abda80474..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/old_anomaly/bhole + anomaly_path = /obj/effect/anomaly/vortex/tier2 prefix_message = "На сканерах дальнего действия обнаружена вихревая аномалия высокой интенсивности." diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index dfa0a6a6b39..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.no_gravity()) && no_gravity()) + if(!forced && (!old_loc || old_loc.no_gravity()) && !no_gravity()) thunk() diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm index 6895e78f951..870e67f0f50 100644 --- a/code/modules/projectiles/ammunition/energy.dm +++ b/code/modules/projectiles/ammunition/energy.dm @@ -279,7 +279,14 @@ 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 +295,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 +317,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 @@ -506,7 +519,7 @@ fire_sound = 'sound/weapons/emitter.ogg' select_name = "emitter" delay = 0.4 - e_cost = 500 + e_cost = 100 projectile_type = /obj/item/projectile/beam/anomaly muzzle_flash_color = LIGHT_COLOR_GREEN diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index ede64b936bb..9870fe45a68 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -409,8 +409,8 @@ modifystate = TRUE /obj/item/gun/energy/bsg - name = "\improper Б.С.П" - desc = "Большая С*** Пушка. Использует ядро аномалии потока и кристалл блюспейса для производства разрушительных взрывов энергии, вдохновленный дивизионом БСА Нанотрейзен." + 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. @@ -421,20 +421,22 @@ slot_flags = ITEM_SLOT_BACK cell_type = /obj/item/stock_parts/cell/bsg shaded_charge = TRUE - var/has_core = FALSE + gender = FEMALE + /// Inserted flux anomaly core. + var/obj/item/assembly/signaler/anomaly/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(has_core && has_bluespace_crystal) - . += "[src] полностью рабочая!" - else if(has_core) - . += "Аномалия потока вставлена, но не хватает БС кристалла." + 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) @@ -444,46 +446,73 @@ 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/tier2/flux)) + if(iscoreflux(I)) add_fingerprint(user) - if(has_core) + if(core) balloon_alert(user, "уже установлено!") return ATTACK_CHAIN_PROCEED + + var/obj/item/assembly/signaler/anomaly/Icore = I + if(Icore.get_strenght() < 140) + balloon_alert(user, "ядро слишком слабо") + return + if(!user.drop_transfer_item_to_loc(I, src)) return ..() + balloon_alert(user, "установлено") - has_core = TRUE - qdel(I) + 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, "отсутствует блюспейс кристалл!") + balloon_alert(user, "нужен блюспейс кристалл") return - if(!has_core) - balloon_alert(user, "отсутствует ядро аномалии!") + + 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(has_core) + 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 @@ -499,7 +528,8 @@ /obj/item/gun/energy/bsg/proc/shatter() if(admin_model) return - visible_message("БС кристалл [src] треснул!") + + visible_message(span_warning("БС кристалл [src] треснул!")) playsound(src, 'sound/effects/pylon_shatter.ogg', 50, TRUE) has_bluespace_crystal = FALSE update_icon(UPDATE_ICON_STATE) @@ -510,7 +540,7 @@ /obj/item/gun/energy/bsg/prebuilt/Initialize(mapload) . = ..() - has_core = TRUE + core = TRUE update_icon(UPDATE_ICON_STATE) /obj/item/gun/energy/bsg/prebuilt/admin diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 4c62697555b..d7de08f14f9 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -300,10 +300,12 @@ /obj/item/projectile/beam/anomaly/stabilizer name = "стабилизирующий луч" + icon_state = "blue_laser" 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 = "red_laser" impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser light_color = LIGHT_COLOR_RED 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/paradise.dme b/paradise.dme index 55153d7bfa2..591aa803920 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1579,6 +1579,7 @@ #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\cores.dm" diff --git a/tgui/packages/tgui/interfaces/AnomalyStabilizer.js b/tgui/packages/tgui/interfaces/AnomalyStabilizer.js new file mode 100644 index 00000000000..63f62aae5b5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AnomalyStabilizer.js @@ -0,0 +1,116 @@ +import { classes } from 'common/react'; +import { useBackend } from '../backend'; +import { + Box, + Button, + Section, + Stack, + Table, + AnimatedNumber, + Icon, + LabeledList, + ProgressBar, + NumberInput, +} from '../components'; +import { Window } from '../layouts'; + +export const AnomalyStabilizer = (props, context) => { + const { act, data } = useBackend(context); + const { + full_info, + core1_name, + core2_name, + possible_stability, + stability_delta, + pull_range, + choosen_pull_dist, + block_move_time, + block_move_impulses_time, + weaken_val, + weaken_time, + } = data; + return ( + + +
+ {InsertedCores(core1_name, core2_name)} +
+ + Выбор уровня стабилизации: + act('change_stability', { new_val: value })} + /> + + {full_info ? ( + + Чтобы расширить область допустимых значений изменения стабильности, + вы можете вставить ядро энергетической аномалии. Затраты энергии при + изменении стабильности на Х повышены в Х*Х раз при Х не равном 0. + + ) : null} + + + Выбор силы притяжения: + act('change_pull_dist', { new_val: value })} + /> + + {full_info ? ( + + + Чтобы добавить снарядам возможность притягивать аномалию, + необходимо вставить ядро гравитационной аномалии. При + отрицательной силе притяжения, аномалию будет отталкивать на + выбранное количество шагов. + + + Снаряды будут блокировать естественное передвижение аномалии на + время равное {block_move_time / 10} в секундах. Для увеличения + этого значения добавьте ядро вихревой аномалии. + + + Снаряды будут блокировать импульсы перемещающие аномалию на время + равное {block_move_impulses_time / 10} в секундах. Для увеличения + этого значения добавьте ядро блюспейс аномалии. + + + Снаряды будут ослаблять эффекты аномалии на время равное + {weaken_time / 10} в секундах. Ослабление будет понижать эффекты + до уровня аномалии с силой на + {weaken_val} меньше текущей, но не ниже 10. Для увеличения этих + значений добавьте ядро атмосферной аномалии. + + + ) : null} +