diff --git a/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm b/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm index 4ea337fddc7..3f019e5842c 100644 --- a/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm +++ b/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm @@ -3967,6 +3967,10 @@ /obj/structure/lattice/catwalk, /turf/open/openspace, /area/station/hallway/primary/fore) +"bbC" = ( +/obj/effect/spawner/random/gizmo, +/turf/open/floor/engine, +/area/station/science/auxlab/firing_range) "bbV" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/power/apc/auto_name/directional/west, @@ -7043,13 +7047,13 @@ /turf/open/floor/iron/white, /area/station/medical/medbay/central) "bPN" = ( -/obj/structure/table, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/disposalpipe/sorting/mail{ dir = 1 }, /obj/effect/mapping_helpers/mail_sorting/science/xenobiology, +/obj/machinery/gizmo/toggle/tucker_tubes, /turf/open/floor/iron/dark/smooth_large, /area/station/science/explab) "bPO" = ( @@ -193993,7 +193997,7 @@ qSk amt pON soU -sOn +bbC pOR ezP gfz diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index e7f959668cd..2e82aba768d 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -48017,6 +48017,7 @@ dir = 5 }, /obj/machinery/status_display/ai/directional/north, +/obj/effect/spawner/random/gizmo, /turf/open/floor/engine, /area/station/science/explab) "lLy" = ( @@ -54144,6 +54145,7 @@ name = "Experimentor Blast Door" }, /obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/gizmo/toggle/tucker_tubes, /turf/open/floor/iron/dark, /area/station/science/explab) "nqD" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 06e0e702a95..d66954b8980 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -10743,6 +10743,10 @@ /obj/machinery/light/floor, /turf/open/floor/wood, /area/station/commons/lounge) +"cXQ" = ( +/obj/effect/spawner/random/gizmo, +/turf/open/floor/engine, +/area/station/science/explab) "cXR" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/sign/warning/cold_temp/directional/north, @@ -54276,9 +54280,9 @@ /turf/open/floor/iron/smooth, /area/station/engineering/lobby) "pnK" = ( -/obj/structure/filingcabinet/chestdrawer, /obj/effect/decal/cleanable/dirt, /obj/machinery/light_switch/directional/north, +/obj/machinery/gizmo/toggle/tucker_tubes, /turf/open/floor/iron, /area/station/science/explab) "pnR" = ( @@ -266768,7 +266772,7 @@ xLq nsZ rDZ rDZ -rDZ +cXQ swa rDZ rDZ diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 0afd221b765..61344388592 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -6346,6 +6346,7 @@ /area/station/engineering/break_room) "cjD" = ( /obj/machinery/airalarm/directional/north, +/obj/machinery/gizmo/toggle/tucker_tubes, /turf/open/floor/iron/white, /area/station/science/explab) "cky" = ( @@ -40660,6 +40661,10 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/security/prison/garden) +"nQg" = ( +/obj/effect/spawner/random/gizmo, +/turf/open/floor/engine, +/area/station/science/explab) "nQA" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -105987,7 +105992,7 @@ jgK tUn dQT wXF -jSk +nQg ukF sIW jSk diff --git a/_maps/map_files/NebulaStation/NebulaStation.dmm b/_maps/map_files/NebulaStation/NebulaStation.dmm index 7e574ce2849..10b138897a7 100644 --- a/_maps/map_files/NebulaStation/NebulaStation.dmm +++ b/_maps/map_files/NebulaStation/NebulaStation.dmm @@ -45113,7 +45113,6 @@ /turf/open/floor/grass, /area/station/security/checkpoint/customs) "gFS" = ( -/obj/structure/hedge, /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 1 }, @@ -45122,6 +45121,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/sign/poster/random/directional/north, +/obj/machinery/gizmo/toggle/tucker_tubes, /turf/open/floor/iron/dark, /area/station/science/explab) "gFU" = ( @@ -160106,6 +160106,7 @@ dir = 10 }, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/spawner/random/gizmo, /turf/open/floor/engine, /area/station/science/explab) "xIE" = ( diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index 978bfa94cb6..eaebc55e319 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -59881,6 +59881,7 @@ /obj/effect/turf_decal/siding/thinplating_new{ dir = 4 }, +/obj/machinery/gizmo/toggle/tucker_tubes, /turf/open/floor/iron, /area/station/science/explab) "tWb" = ( @@ -61776,6 +61777,10 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/secondary/entry) +"uAs" = ( +/obj/effect/spawner/random/gizmo, +/turf/open/floor/engine, +/area/station/science/explab) "uAx" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 4 @@ -119971,7 +119976,7 @@ sQZ owO owO owO -owO +uAs cJS rKD udg diff --git a/_maps/map_files/wawastation/wawastation.dmm b/_maps/map_files/wawastation/wawastation.dmm index 6e7f04f5f94..eff6c09bd75 100644 --- a/_maps/map_files/wawastation/wawastation.dmm +++ b/_maps/map_files/wawastation/wawastation.dmm @@ -5536,6 +5536,10 @@ }, /turf/open/floor/wood/parquet, /area/station/medical/psychology) +"bWe" = ( +/obj/effect/spawner/random/gizmo, +/turf/open/floor/engine, +/area/station/science/explab) "bWi" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -64611,6 +64615,10 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/security/prison/safe) +"wid" = ( +/obj/machinery/gizmo/toggle/tucker_tubes, +/turf/open/floor/iron/white, +/area/station/science/explab) "wie" = ( /obj/structure/lattice/catwalk, /obj/structure/railing, @@ -176316,7 +176324,7 @@ hOa ukx nas gKt -jim +wid rHF mHf xfK @@ -177598,7 +177606,7 @@ raz occ fMz obA -oQW +bWe mSY ddZ nXL diff --git a/code/__DEFINES/_flags.dm b/code/__DEFINES/_flags.dm index a0019a66cba..0254bfebff0 100644 --- a/code/__DEFINES/_flags.dm +++ b/code/__DEFINES/_flags.dm @@ -236,6 +236,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define ZAP_DEFAULT_FLAGS ZAP_MOB_STUN | ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE #define ZAP_FUSION_FLAGS ZAP_OBJ_DAMAGE | ZAP_MOB_DAMAGE | ZAP_MOB_STUN #define ZAP_SUPERMATTER_FLAGS ZAP_GENERATES_POWER +#define ZAP_GIZMO_FLAGS ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE ///Object will protect itself. #define EMP_PROTECT_SELF (1<<0) diff --git a/code/__DEFINES/gizmo.dm b/code/__DEFINES/gizmo.dm new file mode 100644 index 00000000000..e48b14e21c2 --- /dev/null +++ b/code/__DEFINES/gizmo.dm @@ -0,0 +1,40 @@ +// Cryptic gizmo pulses for Interface Science +#define GIZMO_PULSE_1 "Gizmo Pulse 1" +#define GIZMO_PULSE_2 "Gizmo Pulse 2" +#define GIZMO_PULSE_3 "Gizmo Pulse 3" +#define GIZMO_PULSE_4 "Gizmo Pulse 4" +#define GIZMO_PULSE_5 "Gizmo Pulse 5" +#define GIZMO_PULSE_6 "Gizmo Pulse 6" +#define GIZMO_PULSE_7 "Gizmo Pulse 7" +#define GIZMO_PULSE_8 "Gizmo Pulse 8" + +/// From base of /datum/gizactive/start_moving/activate(): () +#define COMSIG_GIZMO_START_MOVING "gizmo_start_moving" +/// From base of /datum/gizactive/stop_moving/activate(): () +#define COMSIG_GIZMO_STOP_MOVING "gizmo_stop_moving" + +/// From base of /datum/gizactive/lights_on/activate(): () +#define COMSIG_GIZMO_ON_STATE "gizmo_on_state" +/// From base of /datum/gizactive/lights_off/activate(): () +#define COMSIG_GIZMO_OFF_STATE "gizmo_off_state" + +#define GIZMO_PUZZLE_WRONG 1 +#define GIZMO_PUZZLE_CORRECT 2 +#define GIZMO_PUZZLE_SOLVED 3 + +#define GIZMO_INTERFACE_WIRES "gizmo_interface_wires" +#define GIZMO_INTERFACE_VOICE "gizmo_interface_voices" + +#define GIZMO_PICK_ONE "gizmo_pick_one" + +/// Common gizmo modes to pick from. They are also weighted so you have even more probability control isnt that cool +#define GIZMO_COMMON_MODES list(\ + /datum/gizmodes/mood_pulser = 1,\ + /datum/gizmodes/mopper = 1,\ + /datum/gizmodes/teleporter = 1,\ + /datum/gizmodes/electric = 1,\ + /datum/gizmodes/dispenser/food = 1,\ + /datum/gizmodes/sputter = 1,\ + /datum/gizmodes/copier = 1,\ + /datum/gizmodes/bad = 1,\ +) diff --git a/code/__DEFINES/wires.dm b/code/__DEFINES/wires.dm index 51990b007b5..9b0883173a9 100644 --- a/code/__DEFINES/wires.dm +++ b/code/__DEFINES/wires.dm @@ -108,3 +108,13 @@ #define AI_WIRE_DISABLED_HACKED -1 #define MAX_WIRE_COUNT 17 + +// Cryptic wires for Interface Science +#define CRYPTIC_WIRE_1 "Cryptic Wire 1" +#define CRYPTIC_WIRE_2 "Cryptic Wire 2" +#define CRYPTIC_WIRE_3 "Cryptic Wire 3" +#define CRYPTIC_WIRE_4 "Cryptic Wire 4" +#define CRYPTIC_WIRE_5 "Cryptic Wire 5" +#define CRYPTIC_WIRE_6 "Cryptic Wire 6" +#define CRYPTIC_WIRE_7 "Cryptic Wire 7" +#define CRYPTIC_WIRE_8 "Cryptic Wire 8" diff --git a/code/datums/elements/moving_randomly.dm b/code/datums/elements/moving_randomly.dm new file mode 100644 index 00000000000..5677ab2c7b9 --- /dev/null +++ b/code/datums/elements/moving_randomly.dm @@ -0,0 +1,26 @@ +/// Just move something around in the simplest way possible +/datum/element/moving_randomly + element_flags = ELEMENT_DETACH_ON_HOST_DESTROY + + /// Movables that we are moving around + var/list/movers = list() + +/datum/element/moving_randomly/Attach(datum/target) + . = ..() + + if(!ismovable(target)) + return ELEMENT_INCOMPATIBLE + + movers += target + +/datum/element/moving_randomly/Detach(datum/source, ...) + . = ..() + + movers -= source + +/datum/element/moving_randomly/New() + START_PROCESSING(SSdcs, src) + +/datum/element/moving_randomly/process(seconds_per_tick) + for(var/atom/movable/mover as anything in movers) + mover.Move(get_step(mover, pick(GLOB.alldirs))) diff --git a/code/datums/mood_events/generic_negative_events.dm b/code/datums/mood_events/generic_negative_events.dm index 665857195f1..60f6b09aed1 100644 --- a/code/datums/mood_events/generic_negative_events.dm +++ b/code/datums/mood_events/generic_negative_events.dm @@ -655,3 +655,8 @@ description = "Man, getting jabbed with that thing really sucked." mood_change = -4 timeout = 5 MINUTES + +/datum/mood_event/gizmo_negative + description = "I hear a voice whispering, and I don't like what it says." + mood_change = -3 + timeout = 30 SECONDS diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm index ab581040bf9..7364493f853 100644 --- a/code/datums/mood_events/generic_positive_events.dm +++ b/code/datums/mood_events/generic_positive_events.dm @@ -701,3 +701,8 @@ description = "What a night! I can't wait to do it all again!" mood_change = 2 timeout = 10 MINUTES + +/datum/mood_event/gizmo_positive + description = "I hear a voice whispering kind words in my ear!" + mood_change = 3 + timeout = 30 SECONDS diff --git a/code/game/objects/effects/spawners/random/structure.dm b/code/game/objects/effects/spawners/random/structure.dm index bfebd92e01f..38f49d611cb 100644 --- a/code/game/objects/effects/spawners/random/structure.dm +++ b/code/game/objects/effects/spawners/random/structure.dm @@ -22,6 +22,7 @@ /obj/structure/closet/mini_fridge = 35, /obj/effect/spawner/random/trash/mess = 30, /obj/item/kirbyplants/fern = 20, + /obj/effect/spawner/random/gizmo = 20, /obj/structure/closet/crate/decorations = 15, /obj/effect/decal/remains/human/smokey/maintenance = 7, /obj/structure/destructible/cult/pants_altar = 1, diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index ec8c398f335..f181ca6b57b 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -781,17 +781,17 @@ pixel_x = -16 pixel_y = -16 duration = 0.5 SECONDS - color = COLOR_LIME var/max_alpha = 255 ///How far the effect would scale in size var/amount_to_scale = 2 -/obj/effect/temp_visual/circle_wave/Initialize(mapload) +/obj/effect/temp_visual/circle_wave/Initialize(mapload, color) transform = matrix().Scale(0.1) animate(src, transform = matrix().Scale(amount_to_scale), time = duration, flags = ANIMATION_PARALLEL) animate(src, alpha = max_alpha, time = duration * 0.6, flags = ANIMATION_PARALLEL) animate(alpha = 0, time = duration * 0.4) apply_wibbly_filters(src) + src.color ||= color return ..() /obj/effect/temp_visual/circle_wave/bioscrambler diff --git a/code/modules/cargo/packs/science.dm b/code/modules/cargo/packs/science.dm index 870645ab7d7..c556a7878ef 100644 --- a/code/modules/cargo/packs/science.dm +++ b/code/modules/cargo/packs/science.dm @@ -121,3 +121,15 @@ contains = list(/obj/item/mod/core/standard = 3) crate_name = "\improper MOD core crate" crate_type = /obj/structure/closet/crate/nakamura + +/datum/supply_pack/science/gizmo + name = "Gizmo research crate" + desc = "Three weird science gizmo thinga-majiggers? We don't know what they do." + cost = CARGO_CRATE_VALUE * 5 + access = ACCESS_SCIENCE + access_view = ACCESS_SCIENCE + contains = list( + /obj/machinery/gizmo = 1, + /obj/effect/spawner/random/gizmo = 2, + ) + crate_name = "\improper Gizmo research crate" diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 5043e61f2e4..728d0e70e80 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -1358,3 +1358,13 @@ taste_description = "metallic salt" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED randomized_spawns = REAGENT_SPAWN_ALL_RANDOM_SPAWNS + +/datum/reagent/consumable/gizmo_goop + name = "Gizmo Goop" + description = "A thick, grey goup that is supposedly 'nutritious'." + color = "#707070" + taste_description = "goop" + chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + nutriment_factor = 0.5 + randomized_spawns = REAGENT_SPAWN_ALL_RANDOM_SPAWNS + diff --git a/code/modules/research/gizmo/gizmo_controller.dm b/code/modules/research/gizmo/gizmo_controller.dm new file mode 100644 index 00000000000..90b5f7bef87 --- /dev/null +++ b/code/modules/research/gizmo/gizmo_controller.dm @@ -0,0 +1,42 @@ +/// The master datum that handles every bit of gizmo code +/// Just so you will understand the full insanity of this: +/// object > gizmo_controller > gizmo_puzzle + gizmo_interface > gizmode > gizpulse > callbacks +/datum/gizmo_controller + /// Can hold different interacting modes (wires, voice) and connected interfaces + var/list/interfaces = list(GIZMO_INTERFACE_WIRES = /datum/gizmo_interface) + /// Instanted interfaces. really just here so I can check this shit in vv + var/list/instances = list() + +/// Generate interfaces for interacting with the gizmo +/datum/gizmo_controller/proc/generate_interfaces(atom/movable/holder) + for(var/interface_define, interface_type in interfaces) + + var/list/callbacks = list() + var/datum/gizmo_interface/interface_instance = new interface_type (src) + interface_instance.generate_interface(holder, callbacks) + instances += interface_instance + + switch(interface_define) + if(GIZMO_INTERFACE_WIRES) + holder.set_wires(new /datum/wires/gizmo(holder, interface_instance.puzzle)) + if(GIZMO_INTERFACE_VOICE) + holder.AddComponent(/datum/component/gizmo_voice, interface_instance.puzzle) + +/// Wired with an interface that moves +/datum/gizmo_controller/beyblade + interfaces = list(GIZMO_INTERFACE_WIRES = /datum/gizmo_interface/beyblade) + +/// Wired with an interface that toggles the icon_state and/or glows +/datum/gizmo_controller/toggle + interfaces = list(GIZMO_INTERFACE_WIRES = /datum/gizmo_interface/toggle) + +/// Voice controller with a voice puzzle and interface. Comes with a wire interface that gives you the hint to use the voice interface +/datum/gizmo_controller/voice + interfaces = list(GIZMO_INTERFACE_WIRES = /datum/gizmo_interface/voice_unlock, GIZMO_INTERFACE_VOICE = /datum/gizmo_interface/voice) + +/// For held gizmo's +/datum/gizmo_controller/item + +/// Always horrible agony! +/datum/gizmo_controller/cursed + interfaces = list(GIZMO_INTERFACE_WIRES = /datum/gizmo_interface/cursed) diff --git a/code/modules/research/gizmo/gizmo_interface.dm b/code/modules/research/gizmo/gizmo_interface.dm new file mode 100644 index 00000000000..7e46a47fcc6 --- /dev/null +++ b/code/modules/research/gizmo/gizmo_interface.dm @@ -0,0 +1,69 @@ +/// Essentially a small wrapper that holds the puzzle datum and connects it with the operating modes of the machine +/datum/gizmo_interface + /// The gizmo master ultra mega controller + var/datum/gizmo_controller/controller + /// The puzzle that connects the interface to the activation callbacks and checks if you pulsed the right sequences + var/datum/gizmo_puzzle/puzzle = /datum/gizmo_puzzle + + /// List (or list define) for the gizmodes to pick + var/list/possible_active_modes = GIZMO_COMMON_MODES + /// Guaranteed active pulses + var/list/guaranteed_active_gizmodes = list() + /// The gizmode instances + var/list/active_gizmodes = list() + + /// Min modes to select from possible_active_modes + var/min_modes = 1 + /// Max modes to select from possible_active_modes + var/max_modes = 2 + +/datum/gizmo_interface/New(datum/gizmo_controller/controller) + . = ..() + + src.controller = controller + +/// Instantiate the active modes, tell them to instantiate and pass their callbacks to the puzzle maker +/datum/gizmo_interface/proc/generate_interface(atom/movable/holder, datum/callback/pulse_callback) + var/list/trigger_callbacks = list() + var/list/modes_to_spawn = list() + guaranteed_active_gizmodes + + for(var/i in 1 to rand(min_modes, max_modes)) + var/path = pick_weight_take(possible_active_modes) + if(!path) + break + modes_to_spawn += path + + for(var/path in modes_to_spawn) + var/datum/gizmodes/mode = new path () + mode.generate_modes(trigger_callbacks, src) + active_gizmodes += mode + + puzzle = new puzzle () + puzzle.generate_code_sequences(trigger_callbacks) + +/// Moves around. Guaranteed to have a mode that controles the movement +/datum/gizmo_interface/beyblade + guaranteed_active_gizmodes = list(/datum/gizmodes/mover) + min_modes = 0 + max_modes = 1 + +/// Guaranteed to have a lights mode +/datum/gizmo_interface/toggle + guaranteed_active_gizmodes = list(/datum/gizmodes/lights) + +/// Guaranteed to have a mode that will give a voice components secret keywords. Assumes there's a voice interface added by the gizmo_controller +/datum/gizmo_interface/voice_unlock + guaranteed_active_gizmodes = list(/datum/gizmodes/voice) + min_modes = 0 + max_modes = 0 + +/// For the actual voice operated puzzle. Is a bit more forgiving +/datum/gizmo_interface/voice + puzzle = /datum/gizmo_puzzle/voice + +/// Always bad! +/datum/gizmo_interface/cursed + possible_active_modes = list( + /datum/gizmodes/bad = 1, + ) + max_modes = 1 diff --git a/code/modules/research/gizmo/gizmo_items.dm b/code/modules/research/gizmo/gizmo_items.dm new file mode 100644 index 00000000000..bc6d237681a --- /dev/null +++ b/code/modules/research/gizmo/gizmo_items.dm @@ -0,0 +1,22 @@ +/// A handheld gizmo, with some different activation modes +/obj/item/gizmo + name = "gizmo" + desc = "Fliggoes the giggoe when its oven in hot the device." + icon = 'icons/obj/science/gizmos.dmi' + + resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF + + /// Possible icon states + var/list/icon_states = list("gizmo_item_1") + /// Reference to the gizmo master controller that handles all the other gizmo stuff + var/datum/gizmo_controller/controller = /datum/gizmo_controller/item + +/obj/item/gizmo/Initialize(mapload) + . = ..() + + if(icon_states) + base_icon_state = pick(icon_states) + icon_state = base_icon_state + + controller = new controller(src) + controller.generate_interfaces(src) diff --git a/code/modules/research/gizmo/gizmo_machines.dm b/code/modules/research/gizmo/gizmo_machines.dm new file mode 100644 index 00000000000..3d50d797d5d --- /dev/null +++ b/code/modules/research/gizmo/gizmo_machines.dm @@ -0,0 +1,136 @@ +/// Science object that behaves similairly to to strange objects/relics, but is activated by cracking wire sequences and other functions +/obj/machinery/gizmo + name = "gizmo" + desc = "Does a function when you put the jigger at the other ends thing." + icon_state = "gizmo_1" + icon = 'icons/obj/science/gizmos.dmi' + + resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF + + panel_open = TRUE + density = TRUE + anchored = FALSE + + /// Possible icon states to pick from + var/list/icon_states = list("gizmo_0", "gizmo_1", "gizmo_2", "gizmo_3", "gizmo_4") + /// Reference to the gizmo. We dont actually need to track this for anything but ease of vv + var/datum/gizmo_controller/controller = /datum/gizmo_controller + /// Possible names to pick from to keep things confusing + var/list/possible_names = list( + "gizmo", "jigger", "doohickey", "particle inverter", "polarity superplexer", "flitcher poster", "natty gonk", "quantum quantum", + "entropy nilum", "tachyon streamer", "doing device", "task operator", "interface responder", "kinetic observer", "turbo encabulator", + "statistic responder", "possibility matrix", "toety aaier", "phase anchor", + ) + +/obj/machinery/gizmo/Initialize(mapload) + . = ..() + + name = pick(possible_names) + + if(icon_states) + base_icon_state = pick(icon_states) + icon_state = base_icon_state + + controller = new controller(src) + controller.generate_interfaces(src) + + RegisterSignal(src, COMSIG_GIZMO_START_MOVING, PROC_REF(start_moving)) + RegisterSignal(src, COMSIG_GIZMO_STOP_MOVING, PROC_REF(stop_moving)) + +/obj/machinery/gizmo/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + add_fingerprint(user) + + if(is_wire_tool(tool)) + attempt_wire_interaction(user) + return ITEM_INTERACT_SUCCESS + return NONE + +/obj/machinery/gizmo/wrench_act(mob/living/user, obj/item/tool) + . = ITEM_INTERACT_BLOCKING + if(default_unfasten_wrench(user, tool) == SUCCESSFUL_UNFASTEN) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/gizmo/proc/start_moving(datum/source, datum/gizpulse/pulse) + SIGNAL_HANDLER + + on_start_moving(pulse) + +/obj/machinery/gizmo/proc/stop_moving(datum/source, datum/gizpulse/pulse) + SIGNAL_HANDLER + + on_stop_moving(pulse) + +/obj/machinery/gizmo/proc/on_start_moving(datum/gizpulse/pulse) + return + +/obj/machinery/gizmo/proc/on_stop_moving(datum/gizpulse/pulse) + return + +/// Gizmo that comes with a gizmo interface to start moving +/obj/machinery/gizmo/beyblade + icon_states = list("beyblade") + + controller = /datum/gizmo_controller/beyblade + + /// Are we currently moving? + var/moving = FALSE + +/obj/machinery/gizmo/beyblade/update_icon(updates) + . = ..() + + icon_state = base_icon_state + (moving ? "_spinning" : "") + +/obj/machinery/gizmo/beyblade/on_start_moving(datum/gizpulse/pulse) + density = TRUE + + moving = TRUE + update_appearance(UPDATE_ICON) + +/obj/machinery/gizmo/beyblade/on_stop_moving(datum/gizpulse/pulse) + density = FALSE + + moving = FALSE + update_appearance(UPDATE_ICON) + +/// A gizmo with some sort of "on" state. Really only for visuals +/obj/machinery/gizmo/toggle + controller = /datum/gizmo_controller/toggle + + icon_states = list("gizmo_active_0", "gizmo_active_1", "gizmo_active_2", "gizmo_active_3", "gizmo_active_4", "gizmo_active_5") + + var/on_state = FALSE + +/obj/machinery/gizmo/toggle/Initialize(mapload) + . = ..() + + RegisterSignal(src, COMSIG_GIZMO_ON_STATE, PROC_REF(on_state)) + RegisterSignal(src, COMSIG_GIZMO_OFF_STATE, PROC_REF(off_state)) + +/obj/machinery/gizmo/toggle/update_icon(updates) + . = ..() + + icon_state = base_icon_state + (on_state ? "_on" : "") + +/obj/machinery/gizmo/toggle/tucker_tubes + icon_states = list("gizmo_active_0") + icon_state = "gizmo_active_0" //for the mapping icon + anchored = TRUE + +/obj/machinery/gizmo/toggle/proc/on_state(datum/source) + SIGNAL_HANDLER + + on_state = TRUE + update_icon() + +/obj/machinery/gizmo/toggle/proc/off_state(datum/source) + SIGNAL_HANDLER + + on_state = FALSE + update_icon() + +/// A gizmo with a voice activated interface +/obj/machinery/gizmo/voice + controller = /datum/gizmo_controller/voice + +/obj/machinery/gizmo/cursed + controller = /datum/gizmo_controller/cursed diff --git a/code/modules/research/gizmo/gizmo_spawner.dm b/code/modules/research/gizmo/gizmo_spawner.dm new file mode 100644 index 00000000000..c7c33ff68a3 --- /dev/null +++ b/code/modules/research/gizmo/gizmo_spawner.dm @@ -0,0 +1,9 @@ +/obj/effect/spawner/random/gizmo + name = "gizmo spawner" + icon_state = "gizmo" + loot = list( + /obj/machinery/gizmo = 6, + /obj/machinery/gizmo/toggle = 4, + /obj/machinery/gizmo/voice = 2, + /obj/machinery/gizmo/beyblade = 1, + ) diff --git a/code/modules/research/gizmo/gizmo_voice.dm b/code/modules/research/gizmo/gizmo_voice.dm new file mode 100644 index 00000000000..0c69e44074a --- /dev/null +++ b/code/modules/research/gizmo/gizmo_voice.dm @@ -0,0 +1,62 @@ +/// The words (or tones really) the gizmo voice interface can listen for. Limit to 2 characters (or it breaks because im cringe) +GLOBAL_LIST_INIT(gizmo_words, world.file2list("strings/gizmo_words.txt")) + +/// Listen to the tones and send the sequence to the puzzle datum +/datum/component/gizmo_voice + var/datum/gizmo_puzzle/puzzle + /// they're not really words but you get it + var/static/list/active_words = list() + +/datum/component/gizmo_voice/Initialize(datum/gizmo_puzzle/puzzle) + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + + var/atom/movable/movable = parent + movable.become_hearing_sensitive(type) + RegisterSignal(movable, COMSIG_MOVABLE_HEAR, PROC_REF(on_hear)) + + src.puzzle = puzzle + + if(!active_words.len) + generate_active_words() + +/// Pick the activate keywords +/datum/component/gizmo_voice/proc/generate_active_words() + var/list/possible_words = GLOB.gizmo_words.Copy() + for(var/i in 1 to puzzle.cryptic_pulse.len) + active_words += pick_n_take(possible_words) + +/// Listen to a message, and pick out the puzzle words +/datum/component/gizmo_voice/proc/on_hear(datum/source, list/hearing_args) + SIGNAL_HANDLER + + if(hearing_args[HEARING_SPEAKER] == parent) + return + + var/haystack = hearing_args[HEARING_RAW_MESSAGE] + var/list/text_position_index = list() + + for(var/needle in active_words) + var/position = 1 + // we can have multiple of the same keyword in one sequence + for(var/i in 1 to puzzle.code_length) + position = findtext(haystack, needle, position) + + if(!position) + break + + /// needle + position because assocs need to be unique, we splice it away later when sending it + text_position_index[needle + "[position]"] = position + // So for the next loop we dont find the exact same word again + position++ + + if(!text_position_index.len) + return + + text_position_index = sortTim(text_position_index, associative = TRUE) + for(var/thing, position in text_position_index) + // Only send feedback for the last speech packet + var/no_feedback = text_position_index.Find(thing) != text_position_index.len + // When solved, accept no further packets from this + if(puzzle.on_pulse(active_words.Find(copytext(thing, 1, 3)), hearing_args[HEARING_SPEAKER], parent, no_feedback = no_feedback) == GIZMO_PUZZLE_SOLVED) + return diff --git a/code/modules/research/gizmo/gizmo_wires.dm b/code/modules/research/gizmo/gizmo_wires.dm new file mode 100644 index 00000000000..01ce0c5eb3b --- /dev/null +++ b/code/modules/research/gizmo/gizmo_wires.dm @@ -0,0 +1,30 @@ +/// Wires that send pulses to a gizmo puzzle datum +/datum/wires/gizmo + /// It's already randomized on the puzzle component + randomize = FALSE + + /// Might as well keep it broad, it's all signals anyway + holder_type = /obj + + /// The wires we need to pulse for cracking the code + var/list/cryptic_wires = list( + CRYPTIC_WIRE_1, + CRYPTIC_WIRE_2, + CRYPTIC_WIRE_3, + CRYPTIC_WIRE_4, + CRYPTIC_WIRE_5, + CRYPTIC_WIRE_6, + CRYPTIC_WIRE_7, + CRYPTIC_WIRE_8, + ) + + var/datum/gizmo_puzzle/puzzle + +/datum/wires/gizmo/New(atom/holder, datum/gizmo_puzzle/puzzle) + wires = cryptic_wires + src.puzzle = puzzle + + ..() + +/datum/wires/gizmo/on_pulse(wire, mob/living/user) + puzzle.on_pulse(cryptic_wires.Find(wire), user, holder) diff --git a/code/modules/research/gizmo/gizmodes/giz_filler.dm b/code/modules/research/gizmo/gizmodes/giz_filler.dm new file mode 100644 index 00000000000..15adbc58023 --- /dev/null +++ b/code/modules/research/gizmo/gizmodes/giz_filler.dm @@ -0,0 +1,101 @@ +/// Shake around some, make some oil or just fly away +/datum/gizmodes/sputter + possible_active_modes = list( + /datum/gizpulse/sputter = 1, + /datum/gizpulse/throw_self = 1, + ) + + min_modes = 2 + cooldown_time = 5 SECONDS + +/// Shake around some and spill oil +/datum/gizpulse/sputter + /// Range in which we can oilerize + var/oil_range = 3 + /// Chance for a tile to get oiled + var/oil_chance = 8 + +/datum/gizpulse/sputter/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + playsound(holder, 'sound/effects/splat.ogg', 30) + for(var/turf/open/tile in oview(oil_range, holder)) + if(prob(oil_chance)) + new /obj/effect/decal/cleanable/blood/oil(tile) + + holder.Shake() + +/datum/gizpulse/throw_self/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + holder.throw_at(get_edge_target_turf(holder, pick(GLOB.alldirs)), 50, 1) + +/// Spawn some item +/datum/gizmodes/dispenser + possible_active_modes = list( + /datum/gizpulse/dispense = 1, + /datum/gizpulse/dispense = 1, + /datum/gizpulse/dispense = 1, + /datum/gizpulse/dispense = 1, + /datum/gizpulse/dispense = 1, + /datum/gizpulse/dispense = 1, + ) + min_modes = 4 + max_modes = 6 + +/datum/gizpulse/dispense + /// Weighted list of objects we could spawn + var/list/possible_objects = list( + /obj/item/crowbar = 1, + /obj/item/wrench = 1, + /obj/item/screwdriver = 1, + /obj/item/multitool = 1, + /obj/item/wirecutters = 1, + /obj/item/weldingtool = 1, + ) + /// Typepath of object to spawn + var/object_to_spawn + + /// List of softrefs of the objects we spawned. Exists only to prevent game-crashing object spam + var/list/spawned_objects_weakrefs = list() + /// Max objects that can exist at once + var/max_objects = 50 + /// The position of the next object to spawn in spawned_objects_weakrefs + var/next_object_position = 0 + +/datum/gizpulse/dispense/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + if(!object_to_spawn) + object_to_spawn = pick_weight(possible_objects) + + // We don't technically track to make sure we dont make more than [max_objects] objects, we just delete whatever was made [max_objects] ago + if(spawned_objects_weakrefs.len > (next_object_position % max_objects)) + qdel(spawned_objects_weakrefs["[(next_object_position % max_objects) + 1]"]) + + var/new_object = new object_to_spawn (get_turf(holder)) + modify(new_object) + spawned_objects_weakrefs["[(next_object_position % max_objects) + 1]"] = WEAKREF(new_object) + next_object_position++ + +/datum/gizpulse/dispense/proc/modify(atom/movable/new_object) + return + +/// Spawn fake goop food +/datum/gizmodes/dispenser/food + possible_active_modes = list( + /datum/gizpulse/dispense/food = 1, + ) + + mode_pulses = list( + /datum/gizpulse/mode_controle/direct_activate, + ) + + min_modes = 1 + max_modes = 1 + +/// Food made with goop +/datum/gizpulse/dispense/food + possible_objects = list( + /obj/item/food/donut/plain = 1, + /obj/item/food/burger = 1, + ) + +/datum/gizpulse/dispense/food/modify(atom/movable/new_object) + new_object.reagents.clear_reagents() + // its goop all the way down + new_object.reagents.add_reagent(/datum/reagent/consumable/gizmo_goop, 5) diff --git a/code/modules/research/gizmo/gizmodes/gizactives.dm b/code/modules/research/gizmo/gizmodes/gizactives.dm new file mode 100644 index 00000000000..632ce7815f4 --- /dev/null +++ b/code/modules/research/gizmo/gizmodes/gizactives.dm @@ -0,0 +1,124 @@ +/// You can imagine a /datum/gizmodes as remote and a TV +/// The mode_controles is the remote. Maybe there's 9 buttons for 1-9, and pressing the buttons just goes to that channel! +/// Maybe there's 10 buttons, 1-9 for selecting a channel and an extra for going to the selected channel +/// Or maybe there's two buttons, one that cycles to the next number and one that then goes to that channel] +/// The giz_pulse is essentually the TV. Generally the functioning of the TV is themed, happy image :) or sad image :( +/datum/gizmodes + /// Our paarent gizmo interface + var/datum/gizmo_interface/interface + /// The currently selected gizpulse + var/datum/gizpulse/current_active + + /// Instantiated active operating modes/gizpulses + var/list/active_gizmodes = list() + /// Guaranted operating modes/gizpulses types. use GIZMO_PICK_ONE with an associated list for more bespoke guaranteed picking + var/list/guaranteed_active_gizmodes = list() + + /// Random gizpulses we can have. PICKWEIGHTED SO GIVE IT A VALUE (/datum/gizpulse/milk_person = 1) + var/list/possible_active_modes = list() + /// Min modes from possible_active_modes + var/min_modes = 1 + /// Max modes from possible_active_modes + var/max_modes = 2 + + /// Mode controles add signals that decide how gizpulses are activated + /// Such as cycle to the next gizpulse, directly activate a gizpulse, or select a specific gizpulse + /// Select mode, for example, adds a callback for every gizpulse + 1 for activating that + /// Cycle mode activate adds only one callback, which cycles to the next one and then activates it + var/list/mode_pulses = list( + /datum/gizpulse/mode_controle/select_mode, + /datum/gizpulse/mode_controle/cycle_mode, + /datum/gizpulse/mode_controle/direct_activate, + /datum/gizpulse/mode_controle/cycle_mode/activate, + ) + /// The selected mode controle + var/datum/gizpulse/mode_controle/mode_pulse + /// Time between pulses + var/cooldown_time = 1 SECONDS + COOLDOWN_DECLARE(cooldown_timer) + +/// Pick the paths to generate and instantiate them +/datum/gizmodes/proc/generate_modes(list/trigger_callbacks, datum/gizmo_interface/interface) + src.interface = interface + + var/list/modes_to_spawn = list() + + for(var/path in guaranteed_active_gizmodes) + if(path == GIZMO_PICK_ONE) + var/list/nested_list = guaranteed_active_gizmodes[path] + modes_to_spawn += pick_weight(nested_list) + else + modes_to_spawn += path + + for(var/i in 1 to rand(min_modes, max_modes)) + var/path = pick_weight_take(possible_active_modes) + if(!path) + break + modes_to_spawn += path + + for(var/path in modes_to_spawn) + active_gizmodes += new path () + + current_active = pick(active_gizmodes) + + var/mode_path = pick(mode_pulses) + mode_pulse = new mode_path() + mode_pulse.setup_mode_controle(src, active_gizmodes, trigger_callbacks) + +/// Activate this gizmode which in turn activates the active gizpulse (you following me here?) +/datum/gizmodes/proc/activate(atom/movable/holder) + if(current_active.affect_timer) + if(!COOLDOWN_FINISHED(src, cooldown_timer)) + return + + COOLDOWN_START(src, cooldown_timer, cooldown_time) + current_active.activate(holder, src, interface) + +/// Holds some functionaly that is activated and selected by the /gizmodes +/datum/gizpulse + /// If TRUE, put the gizmode into cooldown + var/affect_timer = TRUE + +/// Activate it do so stuff +/datum/gizpulse/proc/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + return + +/// Changes the currently activate gizpulse and adds a way to activate gizpulses +/datum/gizpulse/mode_controle + affect_timer = FALSE + +/// Select how activating the pulzes interacts with the gizpulses +/datum/gizpulse/mode_controle/proc/setup_mode_controle(datum/gizmodes/master, list/active_gizmodes, list/trigger_callbacks) + return + +/// Adds a puzzle for every possible made to select it, and a single wire to activate the selected mode +/datum/gizpulse/mode_controle/select_mode/setup_mode_controle(datum/gizmodes/master, list/active_gizmodes, list/trigger_callbacks) + for(var/active in active_gizmodes) + trigger_callbacks += VARSET_CALLBACK(master, current_active, active) + trigger_callbacks += CALLBACK(master, PROC_REF(activate)) + +/// Adds a puzzle to cycle to the next gizpulse, and a puzzle to activate the currently active mode +/datum/gizpulse/mode_controle/cycle_mode/setup_mode_controle(datum/gizmodes/master, list/active_gizmodes, list/trigger_callbacks) + trigger_callbacks += CALLBACK(src, PROC_REF(cycle_mode), master) + trigger_callbacks += CALLBACK(master, PROC_REF(activate)) + +/datum/gizpulse/mode_controle/cycle_mode/proc/cycle_mode(datum/gizmodes/master, atom/movable/holder) + // Move to the next mode in the list (and loop back to 1 if needed) + master.current_active = master.active_gizmodes[((master.active_gizmodes.Find(master.current_active)) % (master.active_gizmodes.len)) + 1] + +/// Adds a puzzle for every gizpulse that just immediately activates that gizpulse +/datum/gizpulse/mode_controle/direct_activate/setup_mode_controle(datum/gizmodes/master, list/active_gizmodes, list/trigger_callbacks) + for(var/active in active_gizmodes) + trigger_callbacks += CALLBACK(src, PROC_REF(switch_and_activate), master, active) + +/datum/gizpulse/mode_controle/direct_activate/proc/switch_and_activate(datum/gizmodes/master, datum/gizpulse/active, atom/movable/holder) + master.current_active = active + master.activate(holder) + +/// Adds a single wire, that cycles and then activates +/datum/gizpulse/mode_controle/cycle_mode/activate/setup_mode_controle(datum/gizmodes/master, list/active_gizmodes, list/trigger_callbacks) + trigger_callbacks += CALLBACK(src, PROC_REF(cycle_mode), master) + +/datum/gizpulse/mode_controle/cycle_mode/activate/cycle_mode(datum/gizmodes/master, atom/movable/holder) + ..() + master.activate(holder) diff --git a/code/modules/research/gizmo/gizmodes/gizbad.dm b/code/modules/research/gizmo/gizmodes/gizbad.dm new file mode 100644 index 00000000000..07fb2b5c66a --- /dev/null +++ b/code/modules/research/gizmo/gizmodes/gizbad.dm @@ -0,0 +1,131 @@ +/// A bunch of bad effects that can maim or kill you (for science!!!) +/datum/gizmodes/bad + possible_active_modes = list( + /datum/gizpulse/explode = 1, + /datum/gizpulse/explode/fire = 1, + /datum/gizpulse/dispense/robot_spider = 1, + /datum/gizpulse/thrower = 1, + /datum/gizpulse/thrower/grenade = 1, + /datum/gizpulse/radiation_pulse = 1, + /datum/gizpulse/bone_breaker = 1, + ) + + guaranteed_active_gizmodes = list( + /datum/gizpulse/ominous, //it may warn you, it may immediately explode. Who knows! + ) + + min_modes = 1 + max_modes = 2 + + cooldown_time = 5 SECONDS + +/datum/gizpulse/explode + var/range_heavy = 0 + var/range_medium = 1 + var/range_light = 3 + var/range_flame = 0 + +/datum/gizpulse/explode/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + explosion(holder, range_heavy, range_medium, range_light, range_flame) + +/datum/gizpulse/explode/fire + range_flame = 5 + +/datum/gizpulse/dispense/robot_spider + possible_objects = list( + /mob/living/basic/spider/robot = 1, + ) + +/mob/living/basic/spider/robot + name = "robot spider" + desc = "Beep boop, the robot spider said." + icon_state = "robot" + mob_biotypes = MOB_ROBOTIC|MOB_BUG + + speed = 5 + maxHealth = 50 + health = 50 + obj_damage = 10 + melee_damage_lower = 10 + melee_damage_upper = 15 + + ai_controller = /datum/ai_controller/basic_controller/giant_spider + +/mob/living/basic/spider/robot/death(gibbed) + . = ..() + + explosion(src, 0, 0, 2) + if(prob(80)) + qdel(src) + +/mob/living/basic/spider/robot/emp_act(severity) + . = ..() + + death() //very sensitive spider robot antennae makes it die fast to emp + +/datum/gizpulse/thrower + /// Weighted list of items we can throw + var/list/throwables = list( + /obj/item/knife/kitchen = 1, + /obj/item/shard = 1, + + ) + /// Path of item to throw + var/throwing_path + +/datum/gizpulse/thrower/New() + . = ..() + throwing_path = pick_weight(throwables) + +/datum/gizpulse/thrower/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + var/obj/item/item = new throwing_path (get_turf(holder)) + + var/list/targets = list() + for(var/mob/living/victims in oview(5, holder)) + targets += victims + + if(!targets.len) + targets += get_edge_target_turf(holder, GLOB.alldirs) + item.throw_at(pick(targets), 20, 3) + modify(item) + +/// Do some extra modifications if need be +/datum/gizpulse/thrower/proc/modify(obj/item/item) + return + +/datum/gizpulse/ominous/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + holder.audible_message(span_hear("You hear an ominous hum.")) + +/datum/gizpulse/thrower/grenade + throwables = list( + /obj/item/grenade/iedcasing = 3, + /obj/item/grenade/chem_grenade/cleaner = 2, + /obj/item/grenade/smokebomb = 2, + /obj/item/grenade/syndieminibomb/concussion = 1, + /obj/item/grenade/frag = 1, + /obj/item/grenade/chem_grenade/teargas = 1, + /obj/item/grenade/chem_grenade/facid = 1, + /obj/item/grenade/chem_grenade/clf3 = 1, + ) + +/datum/gizpulse/thrower/grenade/modify(obj/item/item) + var/obj/item/grenade/regret = item + regret.arm_grenade() + +/datum/gizpulse/bone_breaker/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + var/list/victims = list() + for(var/mob/living/loser in orange(1, holder)) + victims += loser + + if(!victims.len) + return + + var/mob/living/victim = pick(victims) + holder.forceMove(get_turf(victim)) + playsound(victim, 'sound/effects/wounds/crack2.ogg', 70, TRUE) + + victim.apply_damage(60, BRUTE, wound_bonus = 100, sharpness = NONE) + victim.Stun(2 SECONDS) + victim.Knockdown(5 SECONDS) + + victim.emote("scream") diff --git a/code/modules/research/gizmo/gizmodes/gizcopier.dm b/code/modules/research/gizmo/gizmodes/gizcopier.dm new file mode 100644 index 00000000000..50fe333f105 --- /dev/null +++ b/code/modules/research/gizmo/gizmodes/gizcopier.dm @@ -0,0 +1,86 @@ +/// Scan and copies the nearest object. The copy copies no functionality, only visually +/datum/gizmodes/copier + guaranteed_active_gizmodes = list( + /datum/gizpulse/scan, + /datum/gizpulse/copy, + /datum/gizpulse/erase, + ) + + /// Weakref of what is marked to copy + var/datum/weakref/marked + /// List of copies currently in circulation + var/list/copies = list() + /// The max amount of copies that can exist at a time + var/max_copies = 50 + +/// Scan the nearest mob/object +/datum/gizpulse/scan/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + if(!istype(master, /datum/gizmodes/copier)) + return + + var/datum/gizmodes/copier/copier = master + + for(var/atom/movable/candidate in oview(1, holder)) + if(candidate.anchored || HAS_TRAIT(candidate, TRAIT_UNDERFLOOR)) // skips most undertile and hidden objects + continue + + copier.marked = WEAKREF(candidate) + playsound(src, 'sound/items/weapons/flash.ogg', 80) //give some feedback that *something* happened + return + +/// Make a copy of whatever you previously scanned +/datum/gizpulse/copy + /// Reference to the gizmodes' copies list so we can track deletions + var/list/copies + +/datum/gizpulse/copy/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + if(!istype(master, /datum/gizmodes/copier)) + return + + var/datum/gizmodes/copier/copier = master + copies = copier.copies + + var/atom/movable/object_to_copy = copier.marked?.resolve() + + if(!object_to_copy) + return + + // So you can pick up copied items but not copied structures and mobs + var/obj/item/copy = new /obj/item/gizmo_copy (get_turf(holder)) + if(!isitem(object_to_copy)) + copy.interaction_flags_item = NONE //so you cant pick it up anymore + + copy.appearance = object_to_copy + copy.density = object_to_copy.density + + copies.Add(copy) + + if(copies.len > copier.max_copies) + var/obj/item/gizmo_copy/copy_to_delete = copies[1] + qdel(copy_to_delete) //it gets removed from the list on del + + RegisterSignal(copy, COMSIG_QDELETING, PROC_REF(remove_from_list)) + +/// Remove a copy from a list if they're deleted +/datum/gizpulse/copy/proc/remove_from_list(datum/source) + SIGNAL_HANDLER + + copies.Remove(source) + +/// Wipe all current copies +/datum/gizpulse/erase/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + if(!istype(master, /datum/gizmodes/copier)) + return + + var/datum/gizmodes/copier/copier = master + + for(var/copy in copier.copies) + qdel(copy) + + copier.copies.Cut() + +/obj/item/gizmo_copy/emp_act(severity) + . = ..() + + do_sparks(2, FALSE, "gizmo copy") + qdel(src) diff --git a/code/modules/research/gizmo/gizmodes/gizlectric.dm b/code/modules/research/gizmo/gizmodes/gizlectric.dm new file mode 100644 index 00000000000..9d8d90a52df --- /dev/null +++ b/code/modules/research/gizmo/gizmodes/gizlectric.dm @@ -0,0 +1,166 @@ +/// Suck power and shoot it out again +/datum/gizmodes/electric + possible_active_modes = list( + /datum/gizpulse/electric/emp = 1, + /datum/gizpulse/electric/discharge = 1, + /datum/gizpulse/electric/charge = 1, + /datum/gizpulse/electric/revive = 1, + ) + + guaranteed_active_gizmodes = list( + GIZMO_PICK_ONE = list( + /datum/gizpulse/electric/draw = 1, + /datum/gizpulse/electric/passive_charge = 1, + ) + ) + + mode_pulses = list( + /datum/gizpulse/mode_controle/select_mode, + /datum/gizpulse/mode_controle/cycle_mode, + /datum/gizpulse/mode_controle/direct_activate, + ) + + min_modes = 3 + max_modes = 4 + + cooldown_time = 6 SECONDS + + /// The internal power cell + var/obj/item/stock_parts/power_store/battery/gizmo/power + +/datum/gizmodes/electric/activate(atom/movable/holder) + if(!power) + power = new(holder) + + return ..() + +/// Batter used in the gizmo device +/obj/item/stock_parts/power_store/battery/gizmo + charge = STANDARD_BATTERY_CHARGE * 0.1 //you gotta work for your fun + +/// Get the total charge +/datum/gizpulse/electric/proc/get_power(atom/movable/holder, datum/gizmodes/master) + if(istype(master, /datum/gizmodes/electric)) + var/datum/gizmodes/electric/electromaster = master + return electromaster.power.charge() + return 0 + +/// Use some charge +/datum/gizpulse/electric/proc/use_power(amount, atom/movable/holder, datum/gizmodes/master) + if(istype(master, /datum/gizmodes/electric)) + var/datum/gizmodes/electric/electromaster = master + return electromaster.power.use(amount) + return FALSE + +/// Do an EMP blast +/datum/gizpulse/electric/emp + /// Min power to do an EMP + var/min_power = STANDARD_CELL_CHARGE + +/// Do an EMP blast using the cell of our gizmode +/datum/gizpulse/electric/emp/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + var/charge = get_power(holder, master) + if(charge < min_power) + return + /// Max charge is 1MJ, standard cell charge is 10kJ. A full charge EMP is 1000 / 10 = 100. 100 / 15 is 6 tiles of light emp range + empulse(get_turf(holder), floor(charge / STANDARD_CELL_CHARGE / 40), floor(charge / STANDARD_CELL_CHARGE / 15), holder) + use_power(charge, holder, master) + +/// Shoot our current charge away as lightning +/datum/gizpulse/electric/discharge/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + var/charge = get_power(holder, master) + if(charge) + return + + tesla_zap(holder, power = charge, zap_flags = ZAP_GIZMO_FLAGS) + use_power(charge, holder, master) + +/// Look for an object with a cell, and CHARGE IT!!! +/datum/gizpulse/electric/charge/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + if(!istype(master, /datum/gizmodes/electric)) + return + + var/datum/gizmodes/electric/electromaster = master + + for(var/atom/movable/power_source in oview(4, holder)) + if(!power_source.get_cell()) + continue + + var/obj/item/stock_parts/power_store/cell = power_source.get_cell() + + var/charge = electromaster.power.charge() - cell.used_charge() + if(charge <= 0) + continue + + cell.give(charge) + electromaster.power.use(charge) + + holder.Beam(power_source, icon_state = "g_beam", time = 5) + playsound(power_source, 'sound/effects/magic/ethereal_exit.ogg', 40) + return + +/// Revive people in a radius like the revival surgery +/datum/gizpulse/electric/revive + /// The charge cost for a defibrillation pulse + var/defib_cost = STANDARD_CELL_CHARGE * 0.5 + +/datum/gizpulse/electric/revive/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + if(!istype(master, /datum/gizmodes/electric)) + return + + var/datum/gizmodes/electric/electromaster = master + + if(electromaster.power.charge() < defib_cost) + return + + electromaster.power.use(defib_cost) + + for(var/mob/living/dead in orange(holder, 3)) + if(dead.stat == DEAD) + if(dead.revive(excess_healing = 5)) + if(iscarbon(dead)) //still want it to work for simple/basicmobs too + var/mob/living/carbon/dead_but_carbon = dead + dead_but_carbon.set_heartattack(FALSE) + dead.adjust_oxy_loss(-200) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), dead, 'sound/machines/defib/defib_zap.ogg', 60), 3 SECONDS) + + new /obj/effect/temp_visual/circle_wave(get_turf(holder), COLOR_YELLOW) + playsound(holder, 'sound/machines/defib/defib_charge.ogg', 80) + +/// Look for the nearest power-containing object and suck the power out +/datum/gizpulse/electric/draw/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + if(!istype(master, /datum/gizmodes/electric)) + return + + var/datum/gizmodes/electric/electromaster = master + + for(var/atom/movable/power_source in oview(4, holder)) + if(!power_source.get_cell()) + continue + + var/obj/item/stock_parts/power_store/cell = power_source.get_cell() + + var/charge = cell.charge() - electromaster.power.used_charge() + if(charge <= 0) + continue + + cell.use(charge) + electromaster.power.give(charge) + + holder.Beam(power_source, icon_state = "r_beam", time = 5) + playsound(power_source, 'sound/effects/magic/ethereal_exit.ogg', 40) + return + +/// Give a bit of charge, FOR FREE +/datum/gizpulse/electric/passive_charge + /// How much we magically charge from nothing per pulse + var/recharge = STANDARD_BATTERY_CHARGE * 0.001 + +/datum/gizpulse/electric/passive_charge/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + if(!istype(master, /datum/gizmodes/electric)) + return + + var/datum/gizmodes/electric/electromaster = master + electromaster.power.give(recharge) + + playsound(holder, 'sound/effects/magic/charge.ogg', 50, TRUE) diff --git a/code/modules/research/gizmo/gizmodes/gizmisc.dm b/code/modules/research/gizmo/gizmodes/gizmisc.dm new file mode 100644 index 00000000000..ae3ad6ce9fa --- /dev/null +++ b/code/modules/research/gizmo/gizmodes/gizmisc.dm @@ -0,0 +1,51 @@ +/// Make the holder move by adding a movement element. Signal is for aestethic interactions mostly +/datum/gizmodes/mover + guaranteed_active_gizmodes = list(/datum/gizpulse/start_moving = 1, /datum/gizpulse/stop_moving = 1) + +/datum/gizpulse/start_moving/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + holder.AddElement(/datum/element/moving_randomly) + SEND_SIGNAL(holder, COMSIG_GIZMO_START_MOVING, src) + +/datum/gizpulse/stop_moving/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + holder.RemoveElement(/datum/element/moving_randomly) + SEND_SIGNAL(holder, COMSIG_GIZMO_STOP_MOVING, src) + +/// Start glowing +/datum/gizmodes/lights + guaranteed_active_gizmodes = list(/datum/gizpulse/lights_on, /datum/gizpulse/lights_off) + mode_pulses = list( + /datum/gizpulse/mode_controle/direct_activate, + /datum/gizpulse/mode_controle/cycle_mode/activate, + ) + +/datum/gizpulse/lights_on/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + SEND_SIGNAL(holder, COMSIG_GIZMO_ON_STATE) + + holder.set_light( + l_range = 3, + l_power = 2, + l_color = LIGHT_COLOR_INTENSE_RED, + l_on = TRUE, + ) + +/datum/gizpulse/lights_off/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + SEND_SIGNAL(holder, COMSIG_GIZMO_OFF_STATE) + holder.set_light_on(FALSE) + +/// Gives a voice hint or changes the voices language for use with a voice interface (i mean you give this to a wire interface or other but it then gives you +/// the info to use a voice interface) +/datum/gizmodes/voice + guaranteed_active_gizmodes = list(/datum/gizpulse/voice_hint, /datum/gizpulse/language_change) + mode_pulses = list( + /datum/gizpulse/mode_controle/direct_activate, + ) + +/// Spit out a hint for using the voice interface +/datum/gizpulse/voice_hint/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + var/datum/component/gizmo_voice/voice = holder.GetComponent(/datum/component/gizmo_voice) + + holder.say(voice.active_words.Join(" ")) + +/// Pick a different language +/datum/gizpulse/language_change/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + holder.grant_random_uncommon_language("gizmo") diff --git a/code/modules/research/gizmo/gizmodes/gizmood.dm b/code/modules/research/gizmo/gizmodes/gizmood.dm new file mode 100644 index 00000000000..ed9133f7e63 --- /dev/null +++ b/code/modules/research/gizmo/gizmodes/gizmood.dm @@ -0,0 +1,41 @@ +/// Send out mood pulses, good or bad +/datum/gizmodes/mood_pulser + guaranteed_active_gizmodes = list(/datum/gizpulse/mood_pulser/positive, /datum/gizpulse/mood_pulser/negative) + possible_active_modes = list( + /datum/gizpulse/radiation_pulse = 1, + ) + min_modes = 0 + max_modes = 1 + +/// Send out a mood pulse +/datum/gizpulse/mood_pulser + /// Mood event to give out + var/datum/mood_event/mood + /// Color of the ring effect (god i love the ring effect) + var/ring_color + /// Range of the mood pulse + var/range = 14 + +/datum/gizpulse/mood_pulser/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + mood_pulse(holder) + +/// Send a mood pulse to a range +/datum/gizpulse/mood_pulser/proc/mood_pulse(atom/movable/holder) + new /obj/effect/temp_visual/circle_wave(get_turf(holder), ring_color) + for(var/mob/living/carbon/human/human in urange(range, holder)) + human.add_mood_event("gizmo_mood_pulse", mood) + +/// Make a positive mood pulse +/datum/gizpulse/mood_pulser/positive + mood = /datum/mood_event/gizmo_positive + ring_color = COLOR_GREEN + +/// Make a negative mood pulse +/datum/gizpulse/mood_pulser/negative + mood = /datum/mood_event/gizmo_negative + ring_color = COLOR_RED + +/datum/gizpulse/radiation_pulse/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + new /obj/effect/temp_visual/circle_wave(get_turf(holder), COLOR_GREEN) + + radiation_pulse(holder, max_range = 5, threshold = RAD_LIGHT_INSULATION, chance = 30) diff --git a/code/modules/research/gizmo/gizmodes/gizmopper.dm b/code/modules/research/gizmo/gizmodes/gizmopper.dm new file mode 100644 index 00000000000..560fc3b8cfa --- /dev/null +++ b/code/modules/research/gizmo/gizmodes/gizmopper.dm @@ -0,0 +1,92 @@ +/// Gizmo mode that regenerates, cycles and expells reagents in different functions +/datum/gizmodes/mopper + possible_active_modes = list( + /datum/gizpulse/wet_tiles/fluid_circle/small = 1, + /datum/gizpulse/wet_tiles/fluid_circle/medium = 1, + /datum/gizpulse/wet_tiles/fluid_circle/large = 1, + /datum/gizpulse/fluid_smoke = 1, + /datum/gizpulse/swap_reagent = 1, + ) + + min_modes = 3 + max_modes = 5 + + /// Reagents that can be selected + var/list/reagents = list( + /datum/reagent/water, + /datum/reagent/toxin/acid, + /datum/reagent/consumable/salt, + /datum/reagent/uranium/radium, + ) + /// Reference to the reagent holder. Preferably access it from the holder instead, but some procs dont like that (process()) + var/datum/reagents/reagent_holder + /// Reagent that is being generated right now + var/active_reagent = /datum/reagent/water + /// Max volume of the reagent holder we hand out + var/max_volume = 50 + /// Amount of reagents we regenerate per second + var/regeneration_speed = 2 + /// How many reagents we grab from get_random_reagent_id + var/random_reagents_to_add = 1 + /// Flags to pass to the reagent holder + var/reagent_flags = AMOUNT_VISIBLE + +/datum/gizmodes/mopper/New() + . = ..() + + for(var/i in 1 to random_reagents_to_add) + reagents += get_random_reagent_id() + +/datum/gizmodes/mopper/activate(atom/movable/holder) + if(!holder.reagents) + holder.create_reagents(max_volume, reagent_flags) + holder.reagents.add_reagent(active_reagent, max_volume) + reagent_holder = holder.reagents + START_PROCESSING(SSdcs, src) + return ..() + +/datum/gizmodes/mopper/process(seconds_per_tick) + reagent_holder.add_reagent(active_reagent, regeneration_speed * seconds_per_tick) + +/// Wet the surounding tiles +/datum/gizpulse/wet_tiles/activate(atom/movable/holder, datum/gizmodes/mopper/master, datum/gizmo_interface/interface) + var/list/tiles = get_tiles(holder) + for(var/turf/open/tile in tiles) + tile.expose_reagents(holder.reagents.reagent_list, holder.reagents) + + holder.reagents.expose(tile, TOUCH, 1, master.max_volume / tiles.len) + holder.reagents.remove_reagent(master.active_reagent, master.max_volume / tiles.len) + +/// Get the tiles to wet +/datum/gizpulse/wet_tiles/proc/get_tiles(atom/movable/holder) + return + +/// Dump reagents in a circle +/datum/gizpulse/wet_tiles/fluid_circle + /// Size, in a circle, around the holder for wetting + var/size = 0 + +/datum/gizpulse/wet_tiles/fluid_circle/get_tiles(atom/movable/holder) + return RANGE_TURFS(size, holder) + +/// In a small circle +/datum/gizpulse/wet_tiles/fluid_circle/small + size = 1 + +/// In a medium circle +/datum/gizpulse/wet_tiles/fluid_circle/medium + size = 2 + +/// In a large circle +/datum/gizpulse/wet_tiles/fluid_circle/large + size = 3 + +/// Make a smoke cloud of our fluid +/datum/gizpulse/fluid_smoke/activate(atom/movable/holder, datum/gizmo_interface/interface) + do_chem_smoke(3, holder, get_turf(holder), carry = holder.reagents) + holder.reagents.clear_reagents() + +/// Select different reagents +/datum/gizpulse/swap_reagent/activate(atom/movable/holder, datum/gizmodes/mopper/master, datum/gizmo_interface/interface) + holder.reagents.clear_reagents() + master.active_reagent = pick(master.reagents - master.active_reagent) //maybe also add a cycle one instead of random diff --git a/code/modules/research/gizmo/gizmodes/gizporter.dm b/code/modules/research/gizmo/gizmodes/gizporter.dm new file mode 100644 index 00000000000..e28ca158057 --- /dev/null +++ b/code/modules/research/gizmo/gizmodes/gizporter.dm @@ -0,0 +1,45 @@ +/// Teleports itself and/or others +/datum/gizmodes/teleporter + possible_active_modes = list( + /datum/gizpulse/teleport/self = 1, + /datum/gizpulse/teleport/other = 1, + /datum/gizpulse/teleport/other/and_self = 1, + ) + + min_modes = 2 + max_modes = 3 + +/// Teleport... stuff... +/datum/gizpulse/teleport + /// Min distance to teleport + var/offset_min = 5 + /// Max distance to teleport + var/offset_max = 15 + +/// Teleport... stuff... +/datum/gizpulse/teleport/activate(atom/movable/holder, datum/gizmodes/master, datum/gizmo_interface/interface) + var/list/targets = get_teleport_targets(holder) + var/range = rand(offset_min, offset_max) + var/dir = pick(GLOB.alldirs) + + for(var/atom/movable/target as anything in targets) + var/turf/new_turf = get_ranged_target_turf(target, dir, range) + do_teleport(target, new_turf, asoundin = 'sound/effects/cartoon_sfx/cartoon_pop.ogg', channel = TELEPORT_CHANNEL_BLUESPACE) + +/datum/gizpulse/teleport/proc/get_teleport_targets(atom/movable/holder) + return list() + +/// Teleport yourself +/datum/gizpulse/teleport/self/get_teleport_targets(atom/movable/holder) + return list(holder) + +/// Teleport someone else +/datum/gizpulse/teleport/other/get_teleport_targets(atom/movable/holder) + . = list() + for(var/mob/living/living in view(2, holder)) + . += living + +/// Teleport yourself and someone else +/datum/gizpulse/teleport/other/and_self/get_teleport_targets(atom/movable/holder) + return ..() + holder + diff --git a/code/modules/research/gizmo/gizpuzzle.dm b/code/modules/research/gizmo/gizpuzzle.dm new file mode 100644 index 00000000000..18763db7a56 --- /dev/null +++ b/code/modules/research/gizmo/gizpuzzle.dm @@ -0,0 +1,95 @@ +/// Holds the puzzle sequences, receives the pulses, decides if theyre correct, and gives feedback and calls the right callbacks when it does +/datum/gizmo_puzzle + /// The wires we need to pulse for cracking the code + var/list/cryptic_pulse = list( + GIZMO_PULSE_1, + GIZMO_PULSE_2, + GIZMO_PULSE_3, + GIZMO_PULSE_4, + GIZMO_PULSE_5, + GIZMO_PULSE_6, + GIZMO_PULSE_7, + GIZMO_PULSE_8, + ) + + /// How long a code sequence can be + var/code_length = 3 + /// The codes that got generated, formatted as (1 = list(CRYPTIC_WIRE_5, CRYPTIC_WIRE_3, CRYPTIC_WIRE_7, 2 = list(...))) + var/list/code_sequences + /// The current sequence we're on. Will reset if it doesn't match anything + var/list/current_sequence = list() + /// List of callbacks that the solutions will call on succes + var/list/solution_callbacks + + /// For if you want something to happen on merely being pulsed. If null, simply ping, bleep and creak or whatever as feedback + var/datum/callback/pulsed_callback + + COOLDOWN_DECLARE(feedback_cooldown) + /// So the ping buzz feedback doesnt spam too much + var/feedback_cooldown_time = 0.2 SECONDS + +/datum/gizmo_puzzle/New(datum/callback/pulsed) + if(pulsed) + pulsed_callback = pulsed + else + pulsed_callback = CALLBACK(src, PROC_REF(default_on_pulsed)) + return ..() + +/// Make up a sequence +/datum/gizmo_puzzle/proc/generate_code_sequences(list/solution_callbacks) + src.solution_callbacks = solution_callbacks + code_sequences = list() + + for(var/i in 1 to solution_callbacks.len) + code_sequences += list(list()) + for(var/j in 1 to code_length) + code_sequences[i] += pick(cryptic_pulse) + +/// Whenever a puzzle attempt is made +/datum/gizmo_puzzle/proc/on_pulse(pulse_number, mob/living/user, atom/movable/holder, no_feedback = FALSE) + current_sequence += cryptic_pulse[pulse_number] + . = GIZMO_PUZZLE_CORRECT + + var/succeeded = FALSE + + for(var/i in 1 to code_sequences.len) + var/list/a = code_sequences[i] + for(var/j in 1 to current_sequence.len) + if(current_sequence[j] != a[j]) + break + if(current_sequence.len == j) + succeeded = TRUE + if(j == a.len) + var/datum/callback/callback = solution_callbacks[i] + callback.Invoke(holder) + current_sequence.Cut() + . = GIZMO_PUZZLE_SOLVED + break + + if(!succeeded) + current_sequence.Cut() + . = GIZMO_PUZZLE_WRONG + + pulsed_callback?.Invoke(holder, user, ., no_feedback) + +/// Just some feedback so people can start forcing sequences. No feedback if it's done automatically +/datum/gizmo_puzzle/proc/default_on_pulsed(atom/movable/holder, mob/living/user, solved_type, no_feedback = FALSE) + if(!COOLDOWN_FINISHED(src, feedback_cooldown) || !isliving(user) || no_feedback) + return + + COOLDOWN_START(src, feedback_cooldown, feedback_cooldown_time) + + switch(solved_type) + if(GIZMO_PUZZLE_WRONG) + holder.balloon_alert(user, "buzz") + playsound(holder, 'sound/machines/buzz/buzz-sigh.ogg', 30, FALSE) + if(GIZMO_PUZZLE_CORRECT) + holder.balloon_alert(user, "ping") + playsound(holder, 'sound/machines/ping.ogg', 30, FALSE) + if(GIZMO_PUZZLE_SOLVED) + holder.balloon_alert(user, "creak") + playsound(holder, 'sound/machines/creak.ogg', 30, FALSE) + +/// Sequences can be a bit shorter since you have to constantly type and scream them +/datum/gizmo_puzzle/voice + code_length = 2 diff --git a/icons/effects/random_spawners.dmi b/icons/effects/random_spawners.dmi index aa745b5d459..014a14ca4b1 100644 Binary files a/icons/effects/random_spawners.dmi and b/icons/effects/random_spawners.dmi differ diff --git a/icons/hud/screen_voidwalker.dmi b/icons/hud/screen_voidwalker.dmi index 7eb522053be..a4a5c5acd09 100644 Binary files a/icons/hud/screen_voidwalker.dmi and b/icons/hud/screen_voidwalker.dmi differ diff --git a/icons/mob/simple/arachnoid.dmi b/icons/mob/simple/arachnoid.dmi index 7e15fde6852..35e5212b817 100644 Binary files a/icons/mob/simple/arachnoid.dmi and b/icons/mob/simple/arachnoid.dmi differ diff --git a/icons/obj/science/gizmos.dmi b/icons/obj/science/gizmos.dmi new file mode 100644 index 00000000000..30792121a2c Binary files /dev/null and b/icons/obj/science/gizmos.dmi differ diff --git a/strings/gizmo_words.txt b/strings/gizmo_words.txt new file mode 100644 index 00000000000..e90a089ad23 --- /dev/null +++ b/strings/gizmo_words.txt @@ -0,0 +1,59 @@ +ba +be +bo +bi +ca +ce +co +ci +da +de +do +di +fa +fe +fo +fi +ga +ge +go +gi +ha +he +ho +hi +ja +je +jo +ji +ka +ke +ko +ki +la +le +lo +li +ma +me +mo +mi +na +ne +no +pa +pe +po +pi +ra +re +ro +ri +sa +se +so +si +wa +we +wo +wi diff --git a/tgstation.dme b/tgstation.dme index 802f7ba7817..9a999fb52ea 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -115,6 +115,7 @@ #include "code\__DEFINES\fov.dm" #include "code\__DEFINES\generators.dm" #include "code\__DEFINES\ghost.dm" +#include "code\__DEFINES\gizmo.dm" #include "code\__DEFINES\gradient.dm" #include "code\__DEFINES\gravity.dm" #include "code\__DEFINES\guardian_defines.dm" @@ -1648,6 +1649,7 @@ #include "code\datums\elements\move_force_on_death.dm" #include "code\datums\elements\movement_turf_changer.dm" #include "code\datums\elements\movetype_handler.dm" +#include "code\datums\elements\moving_randomly.dm" #include "code\datums\elements\muffles_speech.dm" #include "code\datums\elements\nav_computer_icon.dm" #include "code\datums\elements\nerfed_pulling.dm" @@ -6291,6 +6293,23 @@ #include "code\modules\research\experimentor\experimentor.dm" #include "code\modules\research\experimentor\experiments.dm" #include "code\modules\research\experimentor\wires.dm" +#include "code\modules\research\gizmo\gizmo_controller.dm" +#include "code\modules\research\gizmo\gizmo_interface.dm" +#include "code\modules\research\gizmo\gizmo_items.dm" +#include "code\modules\research\gizmo\gizmo_machines.dm" +#include "code\modules\research\gizmo\gizmo_spawner.dm" +#include "code\modules\research\gizmo\gizmo_voice.dm" +#include "code\modules\research\gizmo\gizmo_wires.dm" +#include "code\modules\research\gizmo\gizpuzzle.dm" +#include "code\modules\research\gizmo\gizmodes\giz_filler.dm" +#include "code\modules\research\gizmo\gizmodes\gizactives.dm" +#include "code\modules\research\gizmo\gizmodes\gizbad.dm" +#include "code\modules\research\gizmo\gizmodes\gizcopier.dm" +#include "code\modules\research\gizmo\gizmodes\gizlectric.dm" +#include "code\modules\research\gizmo\gizmodes\gizmisc.dm" +#include "code\modules\research\gizmo\gizmodes\gizmood.dm" +#include "code\modules\research\gizmo\gizmodes\gizmopper.dm" +#include "code\modules\research\gizmo\gizmodes\gizporter.dm" #include "code\modules\research\machinery\_production.dm" #include "code\modules\research\machinery\circuit_imprinter.dm" #include "code\modules\research\machinery\departmental_circuit_imprinter.dm"