diff --git a/citadel.dme b/citadel.dme index 08ea92e3cc8d..bf5b01091b25 100644 --- a/citadel.dme +++ b/citadel.dme @@ -39,12 +39,12 @@ #include "code\__DEFINES\callbacks.dm" #include "code\__DEFINES\chat.dm" #include "code\__DEFINES\chemistry.dm" +#include "code\__DEFINES\coloration.dm" #include "code\__DEFINES\configuration.dm" #include "code\__DEFINES\construction.dm" #include "code\__DEFINES\damage_organs.dm" #include "code\__DEFINES\directional.dm" #include "code\__DEFINES\dna.dm" -#include "code\__DEFINES\event_args.dm" #include "code\__DEFINES\fonts.dm" #include "code\__DEFINES\gamemode.dm" #include "code\__DEFINES\holidays.dm" @@ -151,7 +151,10 @@ #include "code\__DEFINES\controllers\throwing.dm" #include "code\__DEFINES\controllers\ticker.dm" #include "code\__DEFINES\controllers\timer.dm" +#include "code\__DEFINES\datums\beam.dm" #include "code\__DEFINES\datums\design.dm" +#include "code\__DEFINES\datums\event_args.dm" +#include "code\__DEFINES\datums\item_interface.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\components\riding.dm" @@ -159,16 +162,17 @@ #include "code\__DEFINES\dcs\signals\signals_datum.dm" #include "code\__DEFINES\dcs\signals\signals_fish.dm" #include "code\__DEFINES\dcs\signals\signals_global.dm" +#include "code\__DEFINES\dcs\signals\signals_legacy_beams.dm" #include "code\__DEFINES\dcs\signals\signals_object.dm" #include "code\__DEFINES\dcs\signals\signals_turf.dm" #include "code\__DEFINES\dcs\signals\datums\signals_beam.dm" #include "code\__DEFINES\dcs\signals\datums\signals_perspective.dm" -#include "code\__DEFINES\dcs\signals\elements\conflict.dm" +#include "code\__DEFINES\dcs\signals\elements\signal_conflict_checking.dm" #include "code\__DEFINES\dcs\signals\items\signals_inducer.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\context_system.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_appearance.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_attack.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_buckling.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_context.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_defense.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_lighting.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_main.dm" @@ -177,9 +181,9 @@ #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_radiation.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_throwing.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_tool_system.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_visuals.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_x_act.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\tool_system.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_economy.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_inventory.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_mouse.dm" @@ -200,6 +204,7 @@ #include "code\__DEFINES\fishing\aquarium.dm" #include "code\__DEFINES\fishing\fish.dm" #include "code\__DEFINES\fishing\fishing.dm" +#include "code\__DEFINES\hardsuits\rig.dm" #include "code\__DEFINES\inventory\accessories.dm" #include "code\__DEFINES\inventory\bodytypes.dm" #include "code\__DEFINES\inventory\carry_weight.dm" @@ -295,7 +300,14 @@ #include "code\__DEFINES\research\integrated_circuits.dm" #include "code\__DEFINES\research\research.dm" #include "code\__DEFINES\research\xenoarcheaology.dm" -#include "code\__DEFINES\rigs\rig.dm" +#include "code\__DEFINES\rigsuits\activation.dm" +#include "code\__DEFINES\rigsuits\balancing.dm" +#include "code\__DEFINES\rigsuits\control.dm" +#include "code\__DEFINES\rigsuits\modules.dm" +#include "code\__DEFINES\rigsuits\piece.dm" +#include "code\__DEFINES\rigsuits\theme.dm" +#include "code\__DEFINES\rigsuits\ui.dm" +#include "code\__DEFINES\rigsuits\zones.dm" #include "code\__DEFINES\sound\alias.dm" #include "code\__DEFINES\sound\ambience.dm" #include "code\__DEFINES\sound\channels.dm" @@ -324,11 +336,11 @@ #include "code\__HELPERS\areas.dm" #include "code\__HELPERS\atom_movables.dm" #include "code\__HELPERS\chat.dm" +#include "code\__HELPERS\coloration.dm" #include "code\__HELPERS\datum.dm" #include "code\__HELPERS\debugging.dm" #include "code\__HELPERS\do_after.dm" #include "code\__HELPERS\events.dm" -#include "code\__HELPERS\filters.dm" #include "code\__HELPERS\game.dm" #include "code\__HELPERS\global_lists.dm" #include "code\__HELPERS\guid.dm" @@ -354,15 +366,17 @@ #include "code\__HELPERS\time.dm" #include "code\__HELPERS\turfs.dm" #include "code\__HELPERS\type_processing.dm" -#include "code\__HELPERS\typelists.dm" #include "code\__HELPERS\unsorted.dm" #include "code\__HELPERS\vector.dm" #include "code\__HELPERS\verbs.dm" #include "code\__HELPERS\animations\attack.dm" #include "code\__HELPERS\datastructs\armor.dm" #include "code\__HELPERS\datastructs\bodytypes.dm" +#include "code\__HELPERS\datastructs\filters.dm" +#include "code\__HELPERS\datastructs\generators.dm" #include "code\__HELPERS\datastructs\ingredients.dm" #include "code\__HELPERS\datastructs\priority_queue.dm" +#include "code\__HELPERS\datastructs\typelists.dm" #include "code\__HELPERS\files\client_io.dm" #include "code\__HELPERS\files\hashing.dm" #include "code\__HELPERS\files\paths.dm" @@ -413,11 +427,13 @@ #include "code\__HELPERS\sorts\InsertSort.dm" #include "code\__HELPERS\sorts\MergeSort.dm" #include "code\__HELPERS\sorts\TimSort.dm" +#include "code\__HELPERS\text\sanitize.dm" #include "code\__HELPERS\text\scramble.dm" #include "code\__HELPERS\type2type\color.dm" #include "code\__HELPERS\type2type\type2type.dm" #include "code\__HELPERS\unsorted\contents.dm" #include "code\__HELPERS\unsorted\locate.dm" +#include "code\__HELPERS\unsorted\profile.dm" #include "code\__HELPERS\unsorted\radiation.dm" #include "code\__HELPERS\vfx\appearance_cloning.dm" #include "code\__HELPERS\vfx\color_animations.dm" @@ -623,6 +639,7 @@ #include "code\datums\ai_law_sets.dm" #include "code\datums\ai_laws.dm" #include "code\datums\beam.dm" +#include "code\datums\beam_legacy.dm" #include "code\datums\callback.dm" #include "code\datums\category.dm" #include "code\datums\changelog.dm" @@ -635,13 +652,13 @@ #include "code\datums\ghost_query.dm" #include "code\datums\hierarchy.dm" #include "code\datums\is_abstract.dm" +#include "code\datums\item_interface.dm" #include "code\datums\material_container.dm" #include "code\datums\mind.dm" #include "code\datums\mixed.dm" #include "code\datums\mutable_appearance.dm" #include "code\datums\periodic_news.dm" #include "code\datums\position_point_vector.dm" -#include "code\datums\profile.dm" #include "code\datums\progressbar.dm" #include "code\datums\prototype.dm" #include "code\datums\radiation_wave.dm" @@ -731,7 +748,6 @@ #include "code\datums\components\atoms\radiation_listener.dm" #include "code\datums\components\atoms\radioactive.dm" #include "code\datums\components\crafting\crafting.dm" -#include "code\datums\components\crafting\guncrafting.dm" #include "code\datums\components\items\wielding.dm" #include "code\datums\components\movable\aquarium.dm" #include "code\datums\components\riding\riding_filter.dm" @@ -785,6 +801,8 @@ #include "code\datums\event_args\_event_args.dm" #include "code\datums\event_args\actor.dm" #include "code\datums\event_args\clickchain.dm" +#include "code\datums\event_args\melee_attack.dm" +#include "code\datums\event_args\unarmed_attack.dm" #include "code\datums\helper_datums\construction_datum.dm" #include "code\datums\helper_datums\events.dm" #include "code\datums\helper_datums\getrev.dm" @@ -809,6 +827,7 @@ #include "code\datums\math\graph.dm" #include "code\datums\math\vec2.dm" #include "code\datums\mocking\client.dm" +#include "code\datums\mocking\mocking.dm" #include "code\datums\observation\_debug.dm" #include "code\datums\observation\_defines.dm" #include "code\datums\observation\observation.dm" @@ -982,8 +1001,9 @@ #include "code\game\atoms\movable\pulling.dm" #include "code\game\atoms\movable\throwing.dm" #include "code\game\atoms\movable\vv.dm" +#include "code\game\atoms\movable\special\graphics_render.dm" #include "code\game\atoms\movable\special\overlay.dm" -#include "code\game\atoms\movable\special\render.dm" +#include "code\game\atoms\movable\special\particle_render.dm" #include "code\game\click\adjacency.dm" #include "code\game\click\adjacency_legacy.dm" #include "code\game\click\ai.dm" @@ -1532,6 +1552,7 @@ #include "code\game\objects\items\glassjar.dm" #include "code\game\objects\items\godfigures.dm" #include "code\game\objects\items\gunbox.dm" +#include "code\game\objects\items\guncrafting.dm" #include "code\game\objects\items\holosign_creator.dm" #include "code\game\objects\items\inducer.dm" #include "code\game\objects\items\inflatables.dm" @@ -1540,6 +1561,7 @@ #include "code\game\objects\items\paintkit.dm" #include "code\game\objects\items\pizza_voucher.dm" #include "code\game\objects\items\poi_items.dm" +#include "code\game\objects\items\polyfill_cartridge.dm" #include "code\game\objects\items\robobag.dm" #include "code\game\objects\items\shooting_range.dm" #include "code\game\objects\items\signs.dm" @@ -1696,6 +1718,9 @@ #include "code\game\objects\items\storage\single_use\_single_use.dm" #include "code\game\objects\items\storage\single_use\med_pouch.dm" #include "code\game\objects\items\storage\single_use\mre.dm" +#include "code\game\objects\items\stream_projector\holofabricator.dm" +#include "code\game\objects\items\stream_projector\medichine.dm" +#include "code\game\objects\items\stream_projector\stream_projector.dm" #include "code\game\objects\items\tools\_tool.dm" #include "code\game\objects\items\tools\crowbar.dm" #include "code\game\objects\items\tools\screwdriver.dm" @@ -2237,6 +2262,7 @@ #include "code\modules\asset_cache\assets\chemistry\patches.dm" #include "code\modules\asset_cache\assets\chemistry\pills.dm" #include "code\modules\asset_cache\assets\debug\fucky_wucky.dm" +#include "code\modules\asset_cache\assets\items\holofabricator.dm" #include "code\modules\asset_cache\transports\asset_transport.dm" #include "code\modules\asset_cache\transports\webroot_transport.dm" #include "code\modules\atmospherics\atmosphere\atmosphere.dm" @@ -3471,6 +3497,7 @@ #include "code\modules\mob\living\carbon\give.dm" #include "code\modules\mob\living\carbon\health.dm" #include "code\modules\mob\living\carbon\inventory.dm" +#include "code\modules\mob\living\carbon\life.dm" #include "code\modules\mob\living\carbon\organs.dm" #include "code\modules\mob\living\carbon\perspective.dm" #include "code\modules\mob\living\carbon\physiology.dm" @@ -3507,6 +3534,7 @@ #include "code\modules\mob\living\carbon\brain\say.dm" #include "code\modules\mob\living\carbon\human\appearance.dm" #include "code\modules\mob\living\carbon\human\blood.dm" +#include "code\modules\mob\living\carbon\human\damage_procs.dm" #include "code\modules\mob\living\carbon\human\death.dm" #include "code\modules\mob\living\carbon\human\defense.dm" #include "code\modules\mob\living\carbon\human\dummy.dm" @@ -3516,7 +3544,6 @@ #include "code\modules\mob\living\carbon\human\health.dm" #include "code\modules\mob\living\carbon\human\human.dm" #include "code\modules\mob\living\carbon\human\human_attackhand.dm" -#include "code\modules\mob\living\carbon\human\human_damage.dm" #include "code\modules\mob\living\carbon\human\human_defense.dm" #include "code\modules\mob\living\carbon\human\human_defines.dm" #include "code\modules\mob\living\carbon\human\human_helpers.dm" @@ -3666,6 +3693,7 @@ #include "code\modules\mob\living\silicon\robot\subtypes\syndicate.dm" #include "code\modules\mob\living\simple_mob\appearance.dm" #include "code\modules\mob\living\simple_mob\combat.dm" +#include "code\modules\mob\living\simple_mob\damage_procs.dm" #include "code\modules\mob\living\simple_mob\defense.dm" #include "code\modules\mob\living\simple_mob\hands.dm" #include "code\modules\mob\living\simple_mob\harvesting.dm" @@ -4466,6 +4494,48 @@ #include "code\modules\resleeving\machines.dm" #include "code\modules\resleeving\mirror.dm" #include "code\modules\resleeving\sleevecard.dm" +#include "code\modules\rigsuits\modules\dynamic.dm" +#include "code\modules\rigsuits\modules\rig_module.dm" +#include "code\modules\rigsuits\modules\toolset.dm" +#include "code\modules\rigsuits\modules\engineering\holofabricator.dm" +#include "code\modules\rigsuits\modules\engineering\toolset.dm" +#include "code\modules\rigsuits\modules\medical\medichine.dm" +#include "code\modules\rigsuits\modules\medical\toolset.dm" +#include "code\modules\rigsuits\rig\activation.dm" +#include "code\modules\rigsuits\rig\armor.dm" +#include "code\modules\rigsuits\rig\console.dm" +#include "code\modules\rigsuits\rig\construction.dm" +#include "code\modules\rigsuits\rig\control.dm" +#include "code\modules\rigsuits\rig\defense.dm" +#include "code\modules\rigsuits\rig\environmentals.dm" +#include "code\modules\rigsuits\rig\interaction.dm" +#include "code\modules\rigsuits\rig\modules.dm" +#include "code\modules\rigsuits\rig\pieces.dm" +#include "code\modules\rigsuits\rig\power.dm" +#include "code\modules\rigsuits\rig\rig.dm" +#include "code\modules\rigsuits\rig\rig_console.dm" +#include "code\modules\rigsuits\rig\rig_maint_panel.dm" +#include "code\modules\rigsuits\rig\rig_piece.dm" +#include "code\modules\rigsuits\rig\rig_zone.dm" +#include "code\modules\rigsuits\rig\themes.dm" +#include "code\modules\rigsuits\rig\ui.dm" +#include "code\modules\rigsuits\rig\zones.dm" +#include "code\modules\rigsuits\themes\rig_theme.dm" +#include "code\modules\rigsuits\themes\rig_theme_piece.dm" +#include "code\modules\rigsuits\themes\faction\mercenary.dm" +#include "code\modules\rigsuits\themes\faction\nanotrasen.dm" +#include "code\modules\rigsuits\themes\species\_species.dm" +#include "code\modules\rigsuits\themes\species\nepid.dm" +#include "code\modules\rigsuits\themes\species\protean.dm" +#include "code\modules\rigsuits\themes\station\_station.dm" +#include "code\modules\rigsuits\themes\station\cargo.dm" +#include "code\modules\rigsuits\themes\station\civilian.dm" +#include "code\modules\rigsuits\themes\station\command.dm" +#include "code\modules\rigsuits\themes\station\engineering.dm" +#include "code\modules\rigsuits\themes\station\exploration.dm" +#include "code\modules\rigsuits\themes\station\medical.dm" +#include "code\modules\rigsuits\themes\station\science.dm" +#include "code\modules\rigsuits\themes\station\security.dm" #include "code\modules\rogueminer_vr\asteroid.dm" #include "code\modules\rogueminer_vr\controller.dm" #include "code\modules\rogueminer_vr\debug.dm" diff --git a/code/__DEFINES/_bitfields.dm b/code/__DEFINES/_bitfields.dm index c212f80ddc7a..853add07cc91 100644 --- a/code/__DEFINES/_bitfields.dm +++ b/code/__DEFINES/_bitfields.dm @@ -13,3 +13,11 @@ // todo: get rid of this, rename BITFIELD_NAMED to this #define BITFIELD(thing) #thing = thing #define BITFIELD_NAMED(name, thing) name = thing + +/// KEY: must be unique, may be arbitrary; not a string, as it's used in typepath generation +/// CONSTRAINTS: list(/type = list(varname, ...), ...) +/// BITFIELDS: list of BITFIELD_NEW(). +#define DEFINE_BITFIELD_NEW(KEY, CONSTRAINTS, BITFIELDS) +/// NAME: must be a string +/// VALUE: the actual enum value, whatever it is +#define BITFIELD_NEW(NAME, VALUE) ##NAME = ##VALUE diff --git a/code/__DEFINES/_flags/atom_flags.dm b/code/__DEFINES/_flags/atom_flags.dm index 5beef6496248..cb595aa23b80 100644 --- a/code/__DEFINES/_flags/atom_flags.dm +++ b/code/__DEFINES/_flags/atom_flags.dm @@ -9,6 +9,7 @@ #define ATOM_OVERLAY_QUEUED (1<<3) /// Atom is absolute-abstract - should not be interactable or movable in any way shape or form /// This is for stuff like lighting. +/// If you detect this in Cross() while registering crossed objects, you should probably ignore this! #define ATOM_ABSTRACT (1<<4) /// Atom is not considered a game world object. /// This means semantic "wipe game world state" things like turf.empty(), saving, loading, etc, should ignore it, @@ -29,6 +30,8 @@ /// Does not leave user's fingerprints/fibers when used on things? #define NOPRINT (1<<12) // TODO: item flag +/// we were made by a holofabricator +#define ATOM_HOLOFABRICATED (1<<21) /// We are ticking in materials #define ATOM_MATERIALS_TICKING (1<<22) /// Use initial icon/icon state for HTML renders in things like VV @@ -48,6 +51,7 @@ DEFINE_BITFIELD(atom_flags, list( BITFIELD(OPENCONTAINER), BITFIELD(PHORONGUARD), BITFIELD(NOPRINT), + BITFIELD_NEW("Holofabricated", ATOM_HOLOFABRICATED), BITFIELD(ATOM_MATERIALS_TICKING), BITFIELD(ATOM_HTML_INITIAL_ICON), )) diff --git a/code/__DEFINES/_flags/item_flags.dm b/code/__DEFINES/_flags/item_flags.dm index 9feebe4d2d4f..81bfd53717d9 100644 --- a/code/__DEFINES/_flags/item_flags.dm +++ b/code/__DEFINES/_flags/item_flags.dm @@ -112,7 +112,11 @@ DEFINE_BITFIELD(clothing_flags, list( /// Hides the user's hair, facial and otherwise. #define BLOCKHAIR (1<<12) -DEFINE_BITFIELD(inv_hide_flags, list( +DEFINE_SHARED_BITFIELD(inv_hide_flags, list( + "inv_hide_flags", + "inv_hide_flags_active", + "inv_hide_flags_inactive", +), list( BITFIELD(HIDEGLOVES), BITFIELD(HIDESUITSTORAGE), BITFIELD(HIDEJUMPSUIT), diff --git a/code/__DEFINES/coloration.dm b/code/__DEFINES/coloration.dm new file mode 100644 index 000000000000..1c56409283f6 --- /dev/null +++ b/code/__DEFINES/coloration.dm @@ -0,0 +1,25 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +//* coloration_mode + +/// no recoloring +#define COLORATION_MODE_NONE (1<<0) +/// free normal multiply color +#define COLORATION_MODE_MULTIPLY (1<<1) +/// free matrix or normal multiply color +#define COLORATION_MODE_MATRIX (1<<2) +/// red-green matrix for parts 1, 2. +#define COLORATION_MODE_RG_MATRIX (1<<3) +/// red-blue matrix for parts 1, 2 +#define COLORATION_MODE_RB_MATRIX (1<<4) +/// green-blue matrix for parts 1, 2 +#define COLORATION_MODE_GB_MATRIX (1<<5) +/// red-green-blue matrix for parts 1, 2, 3 +#define COLORATION_MODE_RGB_MATRIX (1<<6) +/// overlays - dynamic amount +// todo: implement +#define COLORATION_MODE_OVERLAYS (1<<7) + +#define COLORATION_MODES_COMPLEX (COLORATION_MODE_RG_MATRIX | COLORATION_MODE_RB_MATRIX | COLORATION_MODE_GB_MATRIX \ + | COLORATION_MODE_RGB_MATRIX | COLORATION_MODE_OVERLAYS ) diff --git a/code/__DEFINES/datums/beam.dm b/code/__DEFINES/datums/beam.dm new file mode 100644 index 000000000000..0a57b01239b5 --- /dev/null +++ b/code/__DEFINES/datums/beam.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/// use single, transform matrix'd line +#define BEAM_VISUAL_STRETCH "stretch" +/// use segments +#define BEAM_VISUAL_SEGMENTS "segments" diff --git a/code/__DEFINES/event_args.dm b/code/__DEFINES/datums/event_args.dm similarity index 87% rename from code/__DEFINES/event_args.dm rename to code/__DEFINES/datums/event_args.dm index cfbbc03c37c3..2cd6cbe9cfa2 100644 --- a/code/__DEFINES/event_args.dm +++ b/code/__DEFINES/datums/event_args.dm @@ -1,3 +1,5 @@ +// todo: seriously evaluate how we use these + //? for /datum/event_args/actor #define WRAP_MOB_TO_ACTOR_EVENT_ARGS(VARNAME) VARNAME = ismob(VARNAME)? new /datum/event_args/actor(VARNAME) : VARNAME diff --git a/code/__DEFINES/datums/item_interface.dm b/code/__DEFINES/datums/item_interface.dm new file mode 100644 index 000000000000..8a2ebdc602e2 --- /dev/null +++ b/code/__DEFINES/datums/item_interface.dm @@ -0,0 +1,14 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +#define ITEM_AUTO_BINDS_SINGLE_INTERFACE_TO_VAR(TYPEPATH, VARNAME) \ +##TYPEPATH/interface_attached(datum/item_interface/interface) { \ + ASSERT(isnull(src.##VARNAME)); \ + src.##VARNAME = interface; \ + return ..(); \ +}; \ +##TYPEPATH/interface_detached(datum/item_interface/interface) { \ + ASSERT(src.##VARNAME == interface); \ + src.##VARNAME = null; \ + return ..(); \ +}; diff --git a/code/__DEFINES/dcs/signals/datums/signals_beam.dm b/code/__DEFINES/dcs/signals/datums/signals_beam.dm index edfbc7c43713..9371e5cdd292 100644 --- a/code/__DEFINES/dcs/signals/datums/signals_beam.dm +++ b/code/__DEFINES/dcs/signals/datums/signals_beam.dm @@ -1,3 +1,9 @@ -/// Called before beam is redrawn -#define COMSIG_BEAM_BEFORE_DRAW "beam_before_draw" - #define BEAM_CANCEL_DRAW (1 << 0) +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/// called on beam redraw +#define COMSIG_BEAM_REDRAW "beam_redraw" +/// called on something crossing the beam: (atom/what) +#define COMSIG_BEAM_CROSSED "beam_crossed" +/// called on something uncrossing from the beam: (atom/what) +#define COMSIG_BEAM_UNCROSSED "beam_uncrossed" diff --git a/code/__DEFINES/dcs/signals/datums/signals_perspective.dm b/code/__DEFINES/dcs/signals/datums/signals_perspective.dm index 45fe489bfae1..3ceaee90826f 100644 --- a/code/__DEFINES/dcs/signals/datums/signals_perspective.dm +++ b/code/__DEFINES/dcs/signals/datums/signals_perspective.dm @@ -1,4 +1,6 @@ -// +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + /// sent upon a mob being added: (mob) #define COMSIG_PERSPECTIVE_MOB_ADD "perspective_add_mob" /// sent upon a mob beiing removed: (mob, switching) diff --git a/code/__DEFINES/dcs/signals/elements/conflict.dm b/code/__DEFINES/dcs/signals/elements/signal_conflict_checking.dm similarity index 100% rename from code/__DEFINES/dcs/signals/elements/conflict.dm rename to code/__DEFINES/dcs/signals/elements/signal_conflict_checking.dm diff --git a/code/__DEFINES/dcs/signals/signals_atom/context_system.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_context.dm similarity index 100% rename from code/__DEFINES/dcs/signals/signals_atom/context_system.dm rename to code/__DEFINES/dcs/signals/signals_atom/signals_atom_context.dm diff --git a/code/__DEFINES/dcs/signals/signals_atom/tool_system.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_tool_system.dm similarity index 100% rename from code/__DEFINES/dcs/signals/signals_atom/tool_system.dm rename to code/__DEFINES/dcs/signals/signals_atom/signals_atom_tool_system.dm diff --git a/code/__DEFINES/dcs/signals/signals_changeling.dm b/code/__DEFINES/dcs/signals/signals_changeling.dm deleted file mode 100644 index 582e53e986df..000000000000 --- a/code/__DEFINES/dcs/signals/signals_changeling.dm +++ /dev/null @@ -1,8 +0,0 @@ -/** - *! ## Changeling Signals. Format: - * * When the signal is called: (signal arguments) - * * All signals send the source datum of the signal as the first argument - */ - -/// Called when a changeling uses its transform ability (source = carbon), from /datum/action/changeling/transform/sting_action(mob/living/carbon/human/user) -////#define COMSIG_CHANGELING_TRANSFORM "changeling_transform" diff --git a/code/__DEFINES/dcs/signals/signals_cytology.dm b/code/__DEFINES/dcs/signals/signals_cytology.dm deleted file mode 100644 index 05b632cc6d09..000000000000 --- a/code/__DEFINES/dcs/signals/signals_cytology.dm +++ /dev/null @@ -1,9 +0,0 @@ -/** - *! ## Cytology Signals. Format: - * * When the signal is called: (signal arguments) - * * All signals send the source datum of the signal as the first argument - */ - -/// Sent from /datum/biological_sample/proc/reset_sample -////#define COMSIG_SAMPLE_GROWTH_COMPLETED "sample_growth_completed" - ////#define SPARE_SAMPLE (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_fish.dm b/code/__DEFINES/dcs/signals/signals_fish.dm index 689470ccd550..9d26d8e37389 100644 --- a/code/__DEFINES/dcs/signals/signals_fish.dm +++ b/code/__DEFINES/dcs/signals/signals_fish.dm @@ -31,7 +31,7 @@ #define COMSIG_FISHING_ROD_REEL "fishing_rod_reel" #define FISHING_ROD_REEL_HANDLED (1<<0) -/// Sent by the fishing line /datum/beam when fishing line is snapped +/// Sent by the fishing line /datum/beam_legacy when fishing line is snapped #define COMSIG_FISHING_LINE_SNAPPED "fishing_line_interrupted" //? Fish diff --git a/code/__DEFINES/dcs/signals/signals_gib.dm b/code/__DEFINES/dcs/signals/signals_gib.dm deleted file mode 100644 index 8e36cc36ffbe..000000000000 --- a/code/__DEFINES/dcs/signals/signals_gib.dm +++ /dev/null @@ -1,10 +0,0 @@ -/** - *! ## Gibs Signals. Format: - * * When the signal is called: (signal arguments) - * * All signals send the source datum of the signal as the first argument - */ - -/// From base of /obj/effect/debris/cleanable/blood/gibs/streak(): (list/directions, list/diseases) -////#define COMSIG_GIBS_STREAK "gibs_streak" -/// Called on mobs when they step in blood. (blood_amount, blood_state, list/blood_DNA) -////#define COMSIG_STEP_ON_BLOOD "step_on_blood" diff --git a/code/__DEFINES/dcs/signals/signals_global_object.dm b/code/__DEFINES/dcs/signals/signals_global_object.dm deleted file mode 100644 index f14cc7e76fe4..000000000000 --- a/code/__DEFINES/dcs/signals/signals_global_object.dm +++ /dev/null @@ -1,17 +0,0 @@ -/** - *! ## Globally Accessible Object Signals. Format: - * * When the signal is called: (signal arguments) - * * All signals send the source datum of the signal as the first argument - */ - -/// From SSJob when DivideOccupations is called -////#define COMSIG_OCCUPATIONS_DIVIDED "occupations_divided" - -/// From SSsun when the sun changes position : (azimuth) -////#define COMSIG_SUN_MOVED "sun_moved" - -/// From SSsecurity_level when the security level changes : (new_level) -////#define COMSIG_SECURITY_LEVEL_CHANGED "security_level_changed" - -/// From SSshuttle when the supply shuttle starts spawning orders : () -////#define COMSIG_SUPPLY_SHUTTLE_BUY "supply_shuttle_buy" diff --git a/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm b/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm index fccb01f6f7c0..94355e98cebe 100644 --- a/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm +++ b/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm @@ -1,13 +1,20 @@ /// From base of obj/item/dropped: (mob/user, flags, atom/newLoc) #define COMSIG_ITEM_DROPPED "item_drop" - #define COMPONENT_ITEM_DROPPED_RELOCATE (1<<0) - #define COMPONENT_ITEM_DROPPED_SUPPRESS_SOUND (1<<1) /// From base of obj/item/pickup: (mob/user, flags, atom/oldLoc) #define COMSIG_ITEM_PICKUP "item_pickup" -/// From base of obj/item/equipped(): (/mob/equipper, slot, accessory) +/// From base of obj/item/equipped(): (/mob/equipper, slot, flags) #define COMSIG_ITEM_EQUIPPED "item_equip" -/// From base of obj/item/unequipped(): (/mob/unequipped, slot, accessory) +/// From base of obj/item/unequipped(): (/mob/unequipper, slot, flags) #define COMSIG_ITEM_UNEQUIPPED "item_unequip" + +//* Return values for all of the above 4 signals +// todo: implement on pickup +// todo: implement on unequipped +// todo: implement on equipped + #define COMPONENT_ITEM_INV_OP_RELOCATE (1<<0) + #define COMPONENT_ITEM_INV_OP_SUPPRESS_SOUND (1<<1) + + /// Called on [/obj/item] before unequip from base of [mob/proc/doUnEquip]: (force, atom/newloc, no_move, invdrop, silent) ////#define COMSIG_ITEM_PRE_UNEQUIP "item_pre_unequip" ///? Only the pre unequip can be cancelled. diff --git a/code/__DEFINES/dcs/signals/signals_legacy_beams.dm b/code/__DEFINES/dcs/signals/signals_legacy_beams.dm new file mode 100644 index 000000000000..edfbc7c43713 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_legacy_beams.dm @@ -0,0 +1,3 @@ +/// Called before beam is redrawn +#define COMSIG_BEAM_BEFORE_DRAW "beam_before_draw" + #define BEAM_CANCEL_DRAW (1 << 0) diff --git a/code/__DEFINES/dcs/signals/signals_light_eater.dm b/code/__DEFINES/dcs/signals/signals_light_eater.dm deleted file mode 100644 index 7a2fdeae5ff9..000000000000 --- a/code/__DEFINES/dcs/signals/signals_light_eater.dm +++ /dev/null @@ -1,15 +0,0 @@ -/** - *! ## Light Eater Signals. Format: - * * When the signal is called: (signal arguments) - * * All signals send the source datum of the signal as the first argument - */ - -//! /datum/element/light_eater -/// From base of [/datum/element/light_eater/proc/table_buffet]: (list/light_queue, datum/light_eater) -////#define COMSIG_LIGHT_EATER_QUEUE "light_eater_queue" -/// From base of [/datum/element/light_eater/proc/devour]: (datum/light_eater) -////#define COMSIG_LIGHT_EATER_ACT "light_eater_act" - ///? Prevents the default light eater behavior from running in case of immunity or custom behavior - ////#define COMPONENT_BLOCK_LIGHT_EATER (1<<0) -/// From base of [/datum/element/light_eater/proc/devour]: (atom/eaten_light) -////#define COMSIG_LIGHT_EATER_DEVOUR "light_eater_devour" diff --git a/code/__DEFINES/dcs/signals/signals_medical.dm b/code/__DEFINES/dcs/signals/signals_medical.dm deleted file mode 100644 index 5c3dd0d2d5f9..000000000000 --- a/code/__DEFINES/dcs/signals/signals_medical.dm +++ /dev/null @@ -1,11 +0,0 @@ -/** - *! ## Medical Signals. Format: - * * When the signal is called: (signal arguments) - * * All signals send the source datum of the signal as the first argument - */ - -/// From /datum/surgery/New(): (datum/surgery/surgery, surgery_location (body zone), obj/item/bodypart/targeted_limb) -////#define COMSIG_MOB_SURGERY_STARTED "mob_surgery_started" - -/// From /datum/surgery_step/success(): (datum/surgery_step/step, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results) -////#define COMSIG_MOB_SURGERY_STEP_SUCCESS "mob_surgery_step_success" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_defense.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_defense.dm index 18bfbe019bcb..fbe58fa32a65 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_defense.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_defense.dm @@ -1,4 +1,4 @@ //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2023 Citadel Station developers. *// -// todo: COMSIG_MOB_SHIELDCALL: prioritizes above atom shieldcall +// todo: this file left intentionally empty since shieldcalls were moved to datums diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_inventory.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_inventory.dm index 1aff02f288d3..2729326ec6a2 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_inventory.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_inventory.dm @@ -6,3 +6,11 @@ #define COMSIG_MOB_ITEM_PICKUP "mob_pickup_item" /// A mob has just dropped an item. Called on [/mob] from base of [/obj/item/dropped()]: (/obj/item/equipped_item, inv_op_flags, new_loc) #define COMSIG_MOB_ITEM_DROPPED "mob_dropped_item" + +//* Return values for all of the above 4 signals +// todo: implement on dropped +// todo: implement on pickup +// todo: implement on unequipped +// todo: implement on equipped + #define COMOPNENT_MOB_INV_OP_RELOCATE (1<<0) + #define COMOPNENT_MOB_INV_OP_SUPPRESS_SOUND (1<<1) diff --git a/code/__DEFINES/dcs/signals/signals_modsuit.dm b/code/__DEFINES/dcs/signals/signals_modsuit.dm deleted file mode 100644 index c0bc5050148b..000000000000 --- a/code/__DEFINES/dcs/signals/signals_modsuit.dm +++ /dev/null @@ -1,30 +0,0 @@ -/** - *! ## MODsuit Signals. Format: - * * When the signal is called: (signal arguments) - * * All signals send the source datum of the signal as the first argument - */ - -/// Called when a module is selected to be the active one from on_select(obj/item/mod/module/module) -////#define COMSIG_MOD_MODULE_SELECTED "mod_module_selected" -/// Called when a MOD activation is called from toggle_activate(mob/user) -////#define COMSIG_MOD_ACTIVATE "mod_activate" - ///? Cancels the suit's activation - ////#define MOD_CANCEL_ACTIVATE (1 << 0) -/// Called when a MOD is having modules removed from crowbar_act(mob/user, obj/crowbar) -////#define COMSIG_MOD_MODULE_REMOVAL "mod_module_removal" - ///? Cancels the removal of modules - ////#define MOD_CANCEL_REMOVAL (1 << 0) -/// Called when a module attempts to activate, however it does. At the end of checks so you can add some yourself, or work on trigger behavior (mob/user) -////#define COMSIG_MODULE_TRIGGERED "mod_module_triggered" - ///? Cancels activation, with no message. include feedback on your cancel. - ////#define MOD_ABORT_USE (1<<0) -/// Called when a module activates, after all checks have passed and cooldown started. -////#define COMSIG_MODULE_ACTIVATED "mod_module_activated" -/// Called when a module deactivates, after all checks have passed. -////#define COMSIG_MODULE_DEACTIVATED "mod_module_deactivated" -/// Called when a module is used, after all checks have passed and cooldown started. -////#define COMSIG_MODULE_USED "mod_module_used" -/// Called when the MODsuit wearer is set. -////#define COMSIG_MOD_WEARER_SET "mod_wearer_set" -/// Called when the MODsuit wearer is unset. -////#define COMSIG_MOD_WEARER_UNSET "mod_wearer_unset" diff --git a/code/__DEFINES/dcs/signals/signals_music.dm b/code/__DEFINES/dcs/signals/signals_music.dm deleted file mode 100644 index ddbfdb6de529..000000000000 --- a/code/__DEFINES/dcs/signals/signals_music.dm +++ /dev/null @@ -1,17 +0,0 @@ -/** - *! ## Music Signals. Format: - * * When the signal is called: (signal arguments) - * * All signals send the source datum of the signal as the first argument - */ - -//! /datum/song Signals -/// Sent to the instrument when a song starts playing -////#define COMSIG_INSTRUMENT_START "instrument_start" -/// Sent to the instrument when a song stops playing -////#define COMSIG_INSTRUMENT_END "instrument_end" -/// Sent to the instrument on /should_stop_playing(): (atom/player). Return values can be found in DEFINES/song.dm -////#define COMSIG_INSTRUMENT_SHOULD_STOP_PLAYING "instrument_should_stop_playing" -/// Sent to the instrument (and player if available) when a song repeats (datum/song) -////#define COMSIG_INSTRUMENT_REPEAT "instrument_repeat" -/// Sent to the instrument when tempo changes, skipped on new. (datum/song) -////#define COMSIG_INSTRUMENT_TEMPO_CHANGE "instrument_tempo_change" diff --git a/code/__DEFINES/dcs/signals/signals_operating_computer.dm b/code/__DEFINES/dcs/signals/signals_operating_computer.dm deleted file mode 100644 index 39d85d39b504..000000000000 --- a/code/__DEFINES/dcs/signals/signals_operating_computer.dm +++ /dev/null @@ -1,10 +0,0 @@ -/** - *! ## Operating Computer Signals. Format: - * * When the signal is called: (signal arguments) - * * All signals send the source datum of the signal as the first argument - */ - -//! /obj/machinery/computer/operating Signals -/// Fired when a dissection surgery completes. -/// (mob/living/target) -////#define COMSIG_OPERATING_COMPUTER_DISSECTION_COMPLETE "operating_computer_dissection_complete" diff --git a/code/__DEFINES/dcs/signals/signals_transform.dm b/code/__DEFINES/dcs/signals/signals_transform.dm deleted file mode 100644 index 70e9a50b7f72..000000000000 --- a/code/__DEFINES/dcs/signals/signals_transform.dm +++ /dev/null @@ -1,14 +0,0 @@ -/** - *! ## Transform Signals. Format: - * * When the signal is called: (signal arguments) - * * All signals send the source datum of the signal as the first argument - */ - -/// From /datum/component/transforming/proc/on_attack_self(obj/item/source, mob/user): (obj/item/source, mob/user, active) -////#define COMSIG_TRANSFORMING_PRE_TRANSFORM "transforming_pre_transform" - ///? Return COMPONENT_BLOCK_TRANSFORM to prevent the item from transforming. - ////#define COMPONENT_BLOCK_TRANSFORM (1<<0) -/// From /datum/component/transforming/proc/do_transform(obj/item/source, mob/user): (obj/item/source, mob/user, active) -////#define COMSIG_TRANSFORMING_ON_TRANSFORM "transforming_on_transform" - ///? Return COMPONENT_NO_DEFAULT_MESSAGE to prevent the transforming component from displaying the default transform message / sound. - ////#define COMPONENT_NO_DEFAULT_MESSAGE (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_xeno_control.dm b/code/__DEFINES/dcs/signals/signals_xeno_control.dm deleted file mode 100644 index 6eaeba74a800..000000000000 --- a/code/__DEFINES/dcs/signals/signals_xeno_control.dm +++ /dev/null @@ -1,19 +0,0 @@ -/** - *! ## Xenobiology Signals. Format: - * * When the signal is called: (signal arguments) - * * All signals send the source datum of the signal as the first argument - */ - -//! Xenobio Hotkeys -/// From slime CtrlClickOn(): (/mob) -////#define COMSIG_XENO_SLIME_CLICK_CTRL "xeno_slime_click_ctrl" -/// From slime AltClickOn(): (/mob) -////#define COMSIG_XENO_SLIME_CLICK_ALT "xeno_slime_click_alt" -/// From slime ShiftClickOn(): (/mob) -////#define COMSIG_XENO_SLIME_CLICK_SHIFT "xeno_slime_click_shift" -/// From turf ShiftClickOn(): (/mob) -////#define COMSIG_XENO_TURF_CLICK_SHIFT "xeno_turf_click_shift" -/// From turf AltClickOn(): (/mob) -////#define COMSIG_XENO_TURF_CLICK_CTRL "xeno_turf_click_alt" -/// From monkey CtrlClickOn(): (/mob) -////#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" diff --git a/code/__DEFINES/hardsuits/rig.dm b/code/__DEFINES/hardsuits/rig.dm new file mode 100644 index 000000000000..42133d7bad62 --- /dev/null +++ b/code/__DEFINES/hardsuits/rig.dm @@ -0,0 +1,9 @@ +// /obj/item/hardsuit/var/activation_state +/// suit not activating +#define HARDSUIT_ACTIVATION_OFF 0 +/// suit is cycling on +#define HARDSUIT_ACTIVATION_STARTUP 1 +/// suit is cycling off +#define HARDSUIT_ACTIVATION_SHUTDOWN 2 +/// suit is activated +#define HARDSUIT_ACTIVATION_ON 3 diff --git a/code/__DEFINES/inventory/bodytypes.dm b/code/__DEFINES/inventory/bodytypes.dm index 60b3c39cd06e..4e4704ec271f 100644 --- a/code/__DEFINES/inventory/bodytypes.dm +++ b/code/__DEFINES/inventory/bodytypes.dm @@ -60,6 +60,8 @@ #define BODYTYPE_XENOHYBRID "xenohybrid" /// digitigrade unathi #define BODYTYPE_UNATHI_DIGI "unathi-digi" +/// ipc - from aurora, currently unused other than in sprites +#define BODYTYPE_IPC "ipc" /// krisitik #define BODYTYPE_KRISITIK "krisitik" @@ -114,6 +116,7 @@ #define BODYTYPE_STRING_PHORONOID "phoronoid" #define BODYTYPE_STRING_WEREBEAST "werebeast" #define BODYTYPE_STRING_XENOHYBRID "xenohybrid" +#define BODYTYPE_STRING_IPC "ipc" #define BODYTYPE_STRING_KRISITIK "krisitik" //Currently Unused until I can do Suit Sprites /proc/bodytype_to_string(bodytype) @@ -157,6 +160,8 @@ return BODYTYPE_STRING_WEREBEAST if(BODYTYPE_XENOHYBRID) return BODYTYPE_STRING_XENOHYBRID + if(BODYTYPE_IPC) + return BODYTYPE_STRING_IPC if(BODYTYPE_KRISITIK) return BODYTYPE_STRING_VULPKANIN //Borrowing Vulp Sprites until I can sprite all the space suits. else diff --git a/code/__DEFINES/items_clothing.dm b/code/__DEFINES/items_clothing.dm index 28cac6d34723..ed9401f0283c 100644 --- a/code/__DEFINES/items_clothing.dm +++ b/code/__DEFINES/items_clothing.dm @@ -77,3 +77,45 @@ #define FIRE_MAX_STACKS 25 /// If the number of stacks goes above this firesuits won't protect you anymore. If not, you can walk around while on fire like a badass. #define FIRE_MAX_FIRESUIT_STACKS 20 + +// todo: move all this somewhere else +//* Cold / Heat Protection Defines *// +//* These are weird because there's far more heat protection *// +//* tiers than cold, as fire is far more varied than freezing. *// + +/// tier max +#define COLD_PROTECTION_VOIDSUIT (TCMB) + +/// tier 5 +#define COLD_PROTECTION_SHIELDED_CLOTHING (T0C - 200) +/// tier 4 +#define COLD_PROTECTION_HEAVY_WINTER_CLOTHING (T0C - 125) +/// tier 3 +#define COLD_PROTECTION_MEDIUM_WINTER_CLOTHING (T0C - 75) +/// tier 2 +#define COLD_PROTECTION_LIGHT_WINTER_CLOTHING (T0C - 35) +/// tier 1 +#define COLD_PROTECTION_MILDLY_THICK_CLOTHING (T0C - 10) + +#define COLD_PROTECTION_NONE (T20C) + +#define HEAT_PROTECTION_NONE (T20C) + +/// tier 1 +#define HEAT_PROTECTION_MILDLY_THICK_CLOTHING (T0C + 50) +/// tier 2 +#define HEAT_PROTECTION_VERY_THICK_CLOTHING (T0C + 100) +/// tier 3 +#define HEAT_PROTECTION_LIGHT_FIRESUIT (T0C + 500) +/// tier 4 +#define HEAT_PROTECTION_NORMAL_VOIDSUIT (T0C + 1000) +/// tier 4 +#define HEAT_PROTECTION_NORMAL_FIRESUIT (T0C + 1000) +/// tier 5 +#define HEAT_PROTECTION_HEAVY_FIRESUIT (T0C + 1500) +/// tier 6 +#define HEAT_PROTECTION_INDUSTRIAL_VOIDSUIT (T0C + 5000) +/// tier 7 +#define HEAT_PROTECTION_SHIELDED_VOIDSUIT (T0C + 10000) +/// tier 8 +#define HEAT_PROTECTION_ATMOS_VOIDSUIT (T0C + 20000) diff --git a/code/__DEFINES/mobs/biology.dm b/code/__DEFINES/mobs/biology.dm index 87625623887b..4b11c4459c21 100644 --- a/code/__DEFINES/mobs/biology.dm +++ b/code/__DEFINES/mobs/biology.dm @@ -15,8 +15,25 @@ #define BIOLOGY_TYPE_NANITES (1<<4) /// plant #define BIOLOGY_TYPE_PLANT (1<<5) +/// crystalline (adherent) +#define BIOLOGY_TYPE_CRYSTALLINE (1<<6) #define BIOLOGY_TYPES_FLESHY (BIOLOGY_TYPE_HUMAN | BIOLOGY_TYPE_CHIMERA | BIOLOGY_TYPE_PLANT | BIOLOGY_TYPE_SLIME) +#define BIOLOGY_TYPES_SYNTHETIC (BIOLOGY_TYPE_SYNTH | BIOLOGY_TYPE_NANITES) #define BIOLOGY_TYPES_ALL (ALL) -// todo: define bitfield (when do i rework define bitfield) +DEFINE_BITFIELD_NEW(biology_types, list( + /datum/medichine_effect/wound_healing = list( + "biology_types", + ), + /obj/item/organ = list( + "biology_type", + ), +), list( + BITFIELD_NEW("Human", BIOLOGY_TYPE_HUMAN), + BITFIELD_NEW("Synth", BIOLOGY_TYPE_SYNTH), + BITFIELD_NEW("Slime", BIOLOGY_TYPE_SLIME), + BITFIELD_NEW("Chimera", BIOLOGY_TYPE_CHIMERA), + BITFIELD_NEW("Nanites", BIOLOGY_TYPE_NANITES), + BITFIELD_NEW("Plant", BIOLOGY_TYPE_PLANT), +)) diff --git a/code/__DEFINES/power/balancing.dm b/code/__DEFINES/power/balancing.dm index d966ee58b549..6868ff51d0da 100644 --- a/code/__DEFINES/power/balancing.dm +++ b/code/__DEFINES/power/balancing.dm @@ -26,6 +26,8 @@ GLOBAL_VAR_INIT(cellefficiency, 1) /// cost of shield difussion in cell units #define CELL_COST_SHIELD_DIFFUSION 120 +/// cost per second in watts of base holofabricator operation per spot beamed +#define POWER_USAGE_HOLOFABRICATOR 6000 //* Machinery diff --git a/code/__DEFINES/rigs/rig.dm b/code/__DEFINES/rigs/rig.dm deleted file mode 100644 index 8cc94fa347b9..000000000000 --- a/code/__DEFINES/rigs/rig.dm +++ /dev/null @@ -1,9 +0,0 @@ -// /obj/item/hardsuit/var/activation_state -/// suit not activating -#define RIG_ACTIVATION_OFF 0 -/// suit is cycling on -#define RIG_ACTIVATION_STARTUP 1 -/// suit is cycling off -#define RIG_ACTIVATION_SHUTDOWN 2 -/// suit is activated -#define RIG_ACTIVATION_ON 3 diff --git a/code/__DEFINES/rigsuits/activation.dm b/code/__DEFINES/rigsuits/activation.dm new file mode 100644 index 000000000000..20eeb00b44be --- /dev/null +++ b/code/__DEFINES/rigsuits/activation.dm @@ -0,0 +1,11 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +//* /obj/item/rig activation_state + +#define RIG_ACTIVATION_OFFLINE (1<<0) +#define RIG_ACTIVATION_ACTIVATING (1<<1) +#define RIG_ACTIVATION_DEACTIVATING (1<<2) +#define RIG_ACTIVATION_ONLINE (1<<3) + +#define RIG_ACTIVATION_IS_CYCLING (RIG_ACTIVATION_ACTIVATING | RIG_ACTIVATION_DEACTIVATING) diff --git a/code/__DEFINES/rigsuits/balancing.dm b/code/__DEFINES/rigsuits/balancing.dm new file mode 100644 index 000000000000..1e4ad1aee0d1 --- /dev/null +++ b/code/__DEFINES/rigsuits/balancing.dm @@ -0,0 +1,18 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * balancing paradigm: + * + * * complexity balances complexity / variety of functions. + * * niche/powerful/adaptive gear = more complexity + * + * * size balances offense / defense / storage + * * 'pick two of three' + * + * * weight adds to rigsuit weight + * * rigsuits get slower and more inefficient at higher weights + */ + + + diff --git a/code/__DEFINES/rigsuits/control.dm b/code/__DEFINES/rigsuits/control.dm new file mode 100644 index 000000000000..27c830c19a3c --- /dev/null +++ b/code/__DEFINES/rigsuits/control.dm @@ -0,0 +1,32 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +//* rig_control_flags; do not edit these without editing TGUI side. +/// walk +#define RIG_CONTROL_MOVEMENT (1<<0) +/// hands, emoting, using items +#define RIG_CONTROL_HANDS (1<<1) +/// trigger hotbinds +#define RIG_CONTROL_USEBIND (1<<2) +/// assign hotbinds +#define RIG_CONTROL_BINDING (1<<3) +/// activation +#define RIG_CONTROL_ACTIVATION (1<<4) +/// piece deploy/seal/etc +#define RIG_CONTROL_PIECES (1<<5) +/// access + use modules +#define RIG_CONTROL_MODULES (1<<6) +/// view all modules on panel without access +#define RIG_CONTROL_VIEW_MODULES (1<<7) +/// view all pieces on panel without access +#define RIG_CONTROL_VIEW_PIECES (1<<8) +/// modify permissions +#define RIG_CONTROL_PERMISSIONS (1<<9) +/// use + lock/unlock + modify maintenance panel +#define RIG_CONTROL_MAINTENANCE (1<<10) +/// use internal console +#define RIG_CONTROL_CONSOLE (1<<11) + +#define RIG_CONTROL_FLAGS_ALL ALL +#define RIG_CONTROL_FLAGS_WEARER ALL +#define RIG_CONTROL_FLAGS_MAINT_PANEL ALL diff --git a/code/__DEFINES/rigsuits/modules.dm b/code/__DEFINES/rigsuits/modules.dm new file mode 100644 index 000000000000..d238b6dde230 --- /dev/null +++ b/code/__DEFINES/rigsuits/modules.dm @@ -0,0 +1,13 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +//* Module Conflict IDs + +// none yet! + +DEFINE_ENUM(rig_module_conflict, list( + /obj/item/rig_module = list( + "single_conflict_rig", + ), +), list( +)) diff --git a/code/__DEFINES/rigsuits/piece.dm b/code/__DEFINES/rigsuits/piece.dm new file mode 100644 index 000000000000..b1f4beb1949d --- /dev/null +++ b/code/__DEFINES/rigsuits/piece.dm @@ -0,0 +1,16 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +//* /datum/component/rig_piece rig_piece_flags + +#define RIG_PIECE_APPLY_ARMOR (1<<0) +#define RIG_PIECE_APPLY_ENVIRONMENTALS (1<<1) + +//* /datum/component/rig_piece sealed + +#define RIG_PIECE_UNSEALED (1<<0) +#define RIG_PIECE_SEALING (1<<1) +#define RIG_PIECE_UNSEALING (1<<2) +#define RIG_PIECE_SEALED (1<<3) + +#define RIG_PIECE_IS_CYCLING (RIG_PIECE_SEALING | RIG_PIECE_UNSEALING) diff --git a/code/__DEFINES/rigsuits/theme.dm b/code/__DEFINES/rigsuits/theme.dm new file mode 100644 index 000000000000..1e6ddefbeb33 --- /dev/null +++ b/code/__DEFINES/rigsuits/theme.dm @@ -0,0 +1,6 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/// Do **not** use on abstract themes. +#define AUTO_RIG_THEME(PATH) \ +/obj/item/rig##PATH { theme_preset = /datum/rig_theme##PATH }; diff --git a/code/__DEFINES/rigsuits/ui.dm b/code/__DEFINES/rigsuits/ui.dm new file mode 100644 index 000000000000..cbb5c6585ef4 --- /dev/null +++ b/code/__DEFINES/rigsuits/ui.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/// encodes a byond ref into a ui module ref +#define RIG_UI_ENCODE_PIECE_REF(REFERENCE) "P-[REFERENCE]" +/// encodes a byond ref into a ui module ref +#define RIG_UI_ENCODE_MODULE_REF(REFERENCE) "M-[REFERENCE]" diff --git a/code/__DEFINES/rigsuits/zones.dm b/code/__DEFINES/rigsuits/zones.dm new file mode 100644 index 000000000000..87cf5e418d0e --- /dev/null +++ b/code/__DEFINES/rigsuits/zones.dm @@ -0,0 +1,137 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +//* Enums + +#define RIG_ZONE_CHEST "chest" +#define RIG_ZONE_HEAD "head" +#define RIG_ZONE_LEFT_ARM "left_arm" +#define RIG_ZONE_RIGHT_ARM "right_arm" +#define RIG_ZONE_LEFT_LEG "left_leg" +#define RIG_ZONE_RIGHT_LEG "right_leg" + +#define RIG_ZONE_LEGS "legs" +#define RIG_ZONE_ARMS "arms" + +#define RIG_ZONE_LIMBS "limbs" + +#define RIG_ZONE_NONE "none" +#define RIG_ZONE_ALL "all" + +DEFINE_ENUM(rig_zones, list( + /obj/item/rig_module = list( + "zone", + ), +), list( + ENUM("Chest", RIG_ZONE_CHEST), + ENUM("Head", RIG_ZONE_HEAD), + ENUM("Both Arms", RIG_ZONE_ARMS), + ENUM("Left Arm", RIG_ZONE_LEFT_ARM), + ENUM("Right Arm", RIG_ZONE_RIGHT_ARM), + ENUM("Both Legs", RIG_ZONE_LEGS), + ENUM("Left Leg", RIG_ZONE_LEFT_LEG), + ENUM("Right Leg", RIG_ZONE_RIGHT_LEG), + ENUM("Zoneless", RIG_ZONE_NONE), + ENUM("All Limbs", RIG_ZONE_LIMBS), + ENUM("Full Body", RIG_ZONE_ALL), +)) + +#define ALL_RIG_ZONES list( \ + RIG_ZONE_CHEST, \ + RIG_ZONE_HEAD, \ + RIG_ZONE_LEFT_ARM, \ + RIG_ZONE_RIGHT_ARM, \ + RIG_ZONE_LEFT_LEG, \ + RIG_ZONE_RIGHT_LEG, \ +) + +#define ALL_RIG_ZONES_ASSOCIATED_TO_ZERO list( \ + RIG_ZONE_CHEST = 0, \ + RIG_ZONE_HEAD = 0, \ + RIG_ZONE_LEFT_ARM = 0, \ + RIG_ZONE_RIGHT_ARM = 0, \ + RIG_ZONE_LEFT_LEG = 0, \ + RIG_ZONE_RIGHT_LEG = 0, \ +) + +#define ALL_RIG_ZONES_ASSOCIATED_TO(X) list( \ + RIG_ZONE_CHEST = X, \ + RIG_ZONE_HEAD = X, \ + RIG_ZONE_LEFT_ARM = X, \ + RIG_ZONE_RIGHT_ARM = X, \ + RIG_ZONE_LEFT_LEG = X, \ + RIG_ZONE_RIGHT_LEG = X, \ +) + +//* Bits + +#define RIG_ZONE_BIT_CHEST (1<<0) +#define RIG_ZONE_BIT_HEAD (1<<1) +#define RIG_ZONE_BIT_LEFT_ARM (1<<2) +#define RIG_ZONE_BIT_RIGHT_ARM (1<<3) +#define RIG_ZONE_BIT_LEFT_LEG (1<<4) +#define RIG_ZONE_BIT_RIGHT_LEG (1<<5) + +#define RIG_ZONE_BIT_LEGS (RIG_ZONE_BIT_LEFT_LEG | RIG_ZONE_BIT_RIGHT_LEG) +#define RIG_ZONE_BIT_ARMS (RIG_ZONE_BIT_LEFT_ARM | RIG_ZONE_BIT_RIGHT_ARM) +#define RIG_ZONE_BIT_ALL (ALL) + +GLOBAL_REAL_LIST(rig_zones) = ALL_RIG_ZONES +GLOBAL_REAL_LIST(rig_zone_to_bit) = list( + RIG_ZONE_CHEST = RIG_ZONE_BIT_CHEST, + RIG_ZONE_HEAD = RIG_ZONE_BIT_HEAD, + RIG_ZONE_LEFT_ARM = RIG_ZONE_BIT_LEFT_ARM, + RIG_ZONE_RIGHT_ARM = RIG_ZONE_BIT_RIGHT_ARM, + RIG_ZONE_LEFT_LEG = RIG_ZONE_BIT_LEFT_LEG, + RIG_ZONE_RIGHT_LEG = RIG_ZONE_BIT_RIGHT_LEG, + RIG_ZONE_NONE = NONE, + RIG_ZONE_ALL = RIG_ZONE_BIT_ALL, + RIG_ZONE_LIMBS = RIG_ZONE_BIT_LEGS | RIG_ZONE_BIT_ARMS, + RIG_ZONE_LEGS = RIG_ZONE_BIT_LEGS, + RIG_ZONE_ARMS = RIG_ZONE_BIT_ARMS, +) +GLOBAL_REAL_LIST(rig_zone_bits) = list( + RIG_ZONE_BIT_CHEST, + RIG_ZONE_BIT_HEAD, + RIG_ZONE_BIT_LEFT_ARM, + RIG_ZONE_BIT_RIGHT_ARM, + RIG_ZONE_BIT_LEFT_LEG, + RIG_ZONE_BIT_RIGHT_LEG, +) + +DEFINE_BITFIELD_NEW(rig_zone_flags, list( + /datum/component/rig_piece = list( + "rig_zone_bits", + ), + /datum/rig_theme_piece = list( + "rig_zone_bits", + ), +), list( + BITFIELD_NEW("Chest", RIG_ZONE_BIT_CHEST), + BITFIELD_NEW("Head", RIG_ZONE_BIT_HEAD), + BITFIELD_NEW("Left Arm", RIG_ZONE_BIT_LEFT_ARM), + BITFIELD_NEW("Right Arm", RIG_ZONE_BIT_RIGHT_ARM), + BITFIELD_NEW("Left Leg", RIG_ZONE_BIT_LEFT_LEG), + BITFIELD_NEW("Right Leg", RIG_ZONE_BIT_RIGHT_LEG), +)) + +/proc/rig_zone_bit_to_zone_enum(bit) + switch(bit) + if(RIG_ZONE_BIT_CHEST) + return RIG_ZONE_CHEST + if(RIG_ZONE_BIT_LEFT_ARM) + return RIG_ZONE_LEFT_ARM + if(RIG_ZONE_BIT_RIGHT_ARM) + return RIG_ZONE_RIGHT_ARM + if(RIG_ZONE_BIT_LEFT_LEG) + return RIG_ZONE_LEFT_LEG + if(RIG_ZONE_BIT_RIGHT_LEG) + return RIG_ZONE_RIGHT_LEG + if(RIG_ZONE_BIT_HEAD) + return RIG_ZONE_HEAD + +//* Handedness - returned from procs as enum + +#define RIG_HANDEDNESS_LEFT "left" +#define RIG_HANDEDNESS_RIGHT "right" +#define RIG_HANDEDNESS_NONE "none" diff --git a/code/__DEFINES/tgui.dm b/code/__DEFINES/tgui.dm index a3eaf187df97..7549a391f2c9 100644 --- a/code/__DEFINES/tgui.dm +++ b/code/__DEFINES/tgui.dm @@ -75,3 +75,44 @@ #define TGUI_INPUT_DATATYPE_LIST_PICK "list_single" /// constraints: nothing #define TGUI_INPUT_DATATYPE_TOGGLE "bool" + +//* TGUI Themes - keep in sync. + +#define TGUI_THEME_ABDUCTOR "abductor" +#define TGUI_THEME_ABSTRACT "abstract" +#define TGUI_THEME_ADMIN "admin" +#define TGUI_THEME_CARDTABLE "cardtable" +#define TGUI_THEME_CITADEL "citadel" +#define TGUI_THEME_CLOCKCULT "clockcult" +#define TGUI_THEME_HACKERMAN "hackerman" +#define TGUI_THEME_MALFUNCTION "malfunction" +#define TGUI_THEME_NEUTRAL "neutral" +#define TGUI_THEME_NTOS "ntos" +#define TGUI_THEME_PAPER "paper" +#define TGUI_THEME_PDA_RETRO "pda-retro" +#define TGUI_THEME_RETRO "retro" +#define TGUI_THEME_SPOOKYCONSOLE "spookyconsole" +#define TGUI_THEME_SYNDICATE "syndicate" +#define TGUI_THEME_WIZARD "wizard" + +/** + * real global because lack of modification need + */ +GLOBAL_REAL_LIST(all_tgui_themes) = list( + TGUI_THEME_ABDUCTOR, + TGUI_THEME_ABSTRACT, + TGUI_THEME_ADMIN, + TGUI_THEME_CARDTABLE, + TGUI_THEME_CITADEL, + TGUI_THEME_CLOCKCULT, + TGUI_THEME_HACKERMAN, + TGUI_THEME_MALFUNCTION, + TGUI_THEME_NEUTRAL, + TGUI_THEME_NTOS, + TGUI_THEME_PAPER, + TGUI_THEME_PDA_RETRO, + TGUI_THEME_RETRO, + TGUI_THEME_SPOOKYCONSOLE, + TGUI_THEME_SYNDICATE, + TGUI_THEME_WIZARD, +) diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index f1d5c0a4fe9c..14e28e82826a 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -16,6 +16,8 @@ /// From a flashbulb #define FLASH_TRAIT "flash" +/// From a RIGsuit's controller +#define RIG_TRAIT "rig" //? Mob Sources @@ -51,7 +53,6 @@ #define MIME_TRAIT "mime" #define CLOTHING_TRAIT "clothing" -#define RIG_TRAIT "rig" #define MAGBOOT_TRAIT "magboot" #define TOGGLE_CLOTHING_TRAIT "toggle_clothing" #define AUGMENT_TRAIT "trait" diff --git a/code/__DEFINES/typeids.dm b/code/__DEFINES/typeids.dm index 155867151076..12c23fcd4248 100644 --- a/code/__DEFINES/typeids.dm +++ b/code/__DEFINES/typeids.dm @@ -1,9 +1,11 @@ //Byond type ids -#define TYPEID_NULL "0" +#define TYPEID_NULL "0x0" #define TYPEID_NORMAL_LIST "f" #define TYPEID_APPEARANCE "3a" +#define TYPEID_ANONYMOUS_TYPEPATH "29" //helper macros -#define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, length(ref)-6) ) ) +#define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? TYPEID_NULL : copytext(ref, 4, length(ref)-6) ) ) #define IS_NORMAL_LIST(V) (GET_TYPEID(!isnum(V) && ref(V)) == TYPEID_NORMAL_LIST) #define IS_APPEARANCE(V) (GET_TYPEID(!isnum(V) && ref(V)) == TYPEID_APPEARANCE) +#define IS_ANONYMOUS_TYPEPATH(V) (GET_TYPEID(!isnum(V) && ref(V)) == TYPEID_ANONYMOUS_TYPEPATH) diff --git a/code/__HELPERS/coloration.dm b/code/__HELPERS/coloration.dm new file mode 100644 index 000000000000..6cb31647728e --- /dev/null +++ b/code/__HELPERS/coloration.dm @@ -0,0 +1,22 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/proc/unpack_coloration_string(str) + RETURN_TYPE(/list) + var/list/fragments = splittext(str, "#") + fragments.Cut(1, 2) + return fragments + +/** + * Doesn't support matrices. Matrices get kicked to #ffffff. + */ +/proc/pack_coloration_string(list/colors) + var/cloned + for(var/i in 1 to length(colors)) + var/c = colors[i] + if(islist(c)) + if(!cloned) + cloned = TRUE + colors = colors.Copy() + colors[i] = "#ffffff" + return jointext(colors, "") diff --git a/code/__HELPERS/datastructs/bodytypes.dm b/code/__HELPERS/datastructs/bodytypes.dm index f1dd2c3a9262..fff00279e463 100644 --- a/code/__HELPERS/datastructs/bodytypes.dm +++ b/code/__HELPERS/datastructs/bodytypes.dm @@ -91,6 +91,8 @@ GLOBAL_LIST_EMPTY(struct_bodytypes) /proc/fetch_bodytypes_struct(encoded) + if(isnull(encoded)) + return encoded if(istype(encoded, /datum/bodytypes)) return encoded var/list/original diff --git a/code/__HELPERS/filters.dm b/code/__HELPERS/datastructs/filters.dm similarity index 100% rename from code/__HELPERS/filters.dm rename to code/__HELPERS/datastructs/filters.dm diff --git a/code/__HELPERS/datastructs/generators.dm b/code/__HELPERS/datastructs/generators.dm new file mode 100644 index 000000000000..b3738e330ca0 --- /dev/null +++ b/code/__HELPERS/datastructs/generators.dm @@ -0,0 +1,200 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +//! All of these will be tightly coupled with TGUI. Tread lightly and do not fuck around. !// + +GLOBAL_LIST_INIT(byond_generator_info, list( + "num" = list( + "defaults" = list( + "A" = 0, + "B" = 0, + ), + "datatype" = "number", + ), + "vector" = list( + "defaults" = list( + "A" = list(0, 0, 0), + "B" = list(0, 0, 0), + ), + "datatype" = "vec3", + ), + "box" = list( + "defaults" = list( + "A" = list(0, 0, 0), + "B" = list(0, 0, 0), + ), + "datatype" = "vec3", + ), + "color" = list( + "defaults" = list( + "A" = "#ffffff", + "B" = "#ffffff", + ), + "datatype" = "color", + ), + "circle" = list( + "defaults" = list( + "A" = 0, + "B" = 0, + ), + "datatype" = "number", + ), + "sphere" = list( + "defaults" = list( + "A" = 0, + "B" = 0, + ), + "datatype" = "number", + ), + "square" = list( + "defaults" = list( + "A" = 0, + "B" = 0, + ), + "datatype" = "number", + ), + "cube" = list( + "defaults" = list( + "A" = 0, + "B" = 0, + ), + "datatype" = "number", + ), +)) + +GLOBAL_LIST_INIT(byond_generator_rand_by_name, list( + "Uniform" = UNIFORM_RAND, + "Gaussian" = NORMAL_RAND, + "Linear" = LINEAR_RAND, + "Square" = SQUARE_RAND, +)) + +//* Data-List generators, able to be arglist'd *// + +/** + * @params + * * low - number + * * high - number + * * rand - X_RAND define for what kind of randomness to use + */ +/proc/num_generator_args(low, high, rand = UNIFORM_RAND) + return list( + "type" = "num", + "A" = low, + "B" = high, + "rand" = rand, + ) + +/** + * generates a random vector along a line + * + * @params + * * vec3_A - a vector of list(x, y, z) + * * vec3_B - a vector of list(x, y, z) + * * rand - X_RAND define for what kind of randomness to use + */ +/proc/vec3_line_generator_args(list/vec3_A, list/vec3_B, rand = UNIFORM_RAND) + return list( + "type" = "vector", + "A" = vec3_A, + "B" = vec3_B, + "rand" = rand, + ) + +/** + * generates a random vector inside a box with corners of A and B + * + * @params + * * vec3_A - a vector of list(x, y, z) + * * vec3_B - a vector of list(x, y, z) + * * rand - X_RAND define for what kind of randomness to use + */ +/proc/vec3_box_generator_args(list/vec3_A, list/vec3_B, rand = UNIFORM_RAND) + return list( + "type" = "box", + "A" = vec3_A, + "B" = vec3_B, + "rand" = rand, + ) + +/** + * generates a random vector inside a circle between radius A and B, centered at 0, 0 + * + * @params + * * low_radius - number + * * high_radius - number + * * rand - X_RAND define for what kind of randomness to use + */ +/proc/vec3_circle_generator_args(low_radius, high_radius, rand = UNIFORM_RAND) + return list( + "type" = "circle", + "A" = low_radius, + "B" = high_radius, + "rand" = rand, + ) + +/** + * generates a random vector inside a sphere between radius A and B, centered at 0, 0, 0 + * + * @params + * * low_radius - number + * * high_radius - number + * * rand - X_RAND define for what kind of randomness to use + */ +/proc/vec3_sphere_generator_args(low_radius, high_radius, rand = UNIFORM_RAND) + return list( + "type" = "sphere", + "A" = low_radius, + "B" = high_radius, + "rand" = rand, + ) + +/** + * generates a random vector inside a square between radius A and B, centered at 0, 0 + * + * @params + * * low_radius - number + * * high_radius - number + * * rand - X_RAND define for what kind of randomness to use + */ +/proc/vec3_square_generator_args(low_radius, high_radius, rand = UNIFORM_RAND) + return list( + "type" = "square", + "A" = low_radius, + "B" = high_radius, + "rand" = rand, + ) + +/** + * generates a random vector inside a cube between radius A and B, centered at 0, 0, 0 + * + * @params + * * low_radius - number + * * high_radius - number + * * rand - X_RAND define for what kind of randomness to use + */ +/proc/vec3_cube_generator_args(low_radius, high_radius, rand = UNIFORM_RAND) + return list( + "type" = "cube", + "A" = low_radius, + "B" = high_radius, + "rand" = rand, + ) + +/** + * generates a random color interpolated between two different colors + * + * A and B can either be color strings or matrices + * + * @params + * * A - a color + * * B - a color + * * rand - X_RAND define for what kind of randomness to use + */ +/proc/color_generator_args(A, B, rand = UNIFORM_RAND) + return list( + "type" = "color", + "A" = A, + "B" = B, + "rand" = rand, + ) diff --git a/code/__HELPERS/typelists.dm b/code/__HELPERS/datastructs/typelists.dm similarity index 100% rename from code/__HELPERS/typelists.dm rename to code/__HELPERS/datastructs/typelists.dm diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 7889e5416ba5..0e52de913fd7 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -213,6 +213,7 @@ ColorTone(rgb, tone) /** * reads RGB or RGBA values to list + * * @return list(r, g, b) or list(r, g, b, a), values 0 to 255. */ /proc/ReadRGB(rgb) @@ -498,18 +499,21 @@ ColorTone(rgb, tone) return hsv(hue, sat, val, alpha) -/* - Smooth blend between RGB colors - - amount=0 is the first color - amount=1 is the second color - amount=0.5 is directly between the two colors - - amount<0 or amount>1 are allowed +/** + * Smooth blend between RGB colors + * + * amount=0 is the first color + * amount=1 is the second color + * amount=0.5 is directly between the two colors + * + * amount<0 or amount>1 are allowed + * + * if rgb1/rgb2 are lists, they are treated as rgb lists directly. + * it is up to you to not pass in dumb shit. */ /proc/BlendRGB(rgb1, rgb2, amount) - var/list/RGB1 = ReadRGB(rgb1) - var/list/RGB2 = ReadRGB(rgb2) + var/list/RGB1 = islist(rgb1)? rgb1 : ReadRGB(rgb1) + var/list/RGB2 = islist(rgb2)? rgb2 : ReadRGB(rgb2) // add missing alpha if needed if(RGB1.len < RGB2.len) @@ -525,6 +529,38 @@ ColorTone(rgb, tone) return isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) +/** + * Smooth blend between RGB colors + * + * amount=0 is the first color + * amount=1 is the second color + * amount=0.5 is directly between the two colors + * + * amount<0 or amount>1 are allowed + * + * if rgb1/rgb2 are lists, they are treated as rgb lists directly. + * it is up to you to not pass in dumb shit. + * + * emits a rgb list + */ +/proc/BlendRGBList(rgb1, rgb2, amount) + var/list/RGB1 = islist(rgb1)? rgb1 : ReadRGB(rgb1) + var/list/RGB2 = islist(rgb2)? rgb2 : ReadRGB(rgb2) + + // add missing alpha if needed + if(RGB1.len < RGB2.len) + RGB1 += 255 + else if(RGB2.len < RGB1.len) + RGB2 += 255 + var/usealpha = RGB1.len > 3 + + var/r = round(RGB1[1] + (RGB2[1] - RGB1[1]) * amount, 1) + var/g = round(RGB1[2] + (RGB2[2] - RGB1[2]) * amount, 1) + var/b = round(RGB1[3] + (RGB2[3] - RGB1[3]) * amount, 1) + var/alpha = usealpha ? round(RGB1[4] + (RGB2[4] - RGB1[4]) * amount, 1) : null + + return isnull(alpha)? list(r, g, b) : list(r, g, b, alpha) + /proc/BlendRGBasHSV(rgb1, rgb2, amount) return HSVtoRGB(RGBtoHSV(rgb1), RGBtoHSV(rgb2), amount) diff --git a/code/__HELPERS/math/angle.dm b/code/__HELPERS/math/angle.dm index 3ba3ce9e7cf7..79e8f4847ff9 100644 --- a/code/__HELPERS/math/angle.dm +++ b/code/__HELPERS/math/angle.dm @@ -9,8 +9,8 @@ /proc/get_visual_angle(atom/start, atom/end) if(!start || !end) return 0 - var/dy =(32 * end.y + end.pixel_y) - (32 * start.y + start.pixel_y) - var/dx =(32 * end.x + end.pixel_x) - (32 * start.x + start.pixel_x) + var/dy =(WORLD_ICON_SIZE * end.y + end.pixel_y) - (WORLD_ICON_SIZE * start.y + start.pixel_y) + var/dx =(WORLD_ICON_SIZE * end.x + end.pixel_x) - (WORLD_ICON_SIZE * start.x + start.pixel_x) if(!dy) return (dx >= 0) ? 90 : 270 . = arctan(dx/dy) @@ -27,8 +27,8 @@ * this is also visual angle because ss13 uses weird CW of N instead of CCW of E angles (which the rest of the math world does). */ /proc/get_visual_angle_raw(start_x, start_y, start_pixel_x, start_pixel_y, end_x, end_y, end_pixel_x, end_pixel_y) - var/dy = (32 * end_y + end_pixel_y) - (32 * start_y + start_pixel_y) - var/dx = (32 * end_x + end_pixel_x) - (32 * start_x + start_pixel_x) + var/dy = (WORLD_ICON_SIZE * end_y + end_pixel_y) - (WORLD_ICON_SIZE * start_y + start_pixel_y) + var/dx = (WORLD_ICON_SIZE * end_x + end_pixel_x) - (WORLD_ICON_SIZE * start_x + start_pixel_x) if(!dy) return (dx >= 0) ? 90 : 270 . = arctan(dx/dy) diff --git a/code/__HELPERS/text/sanitize.dm b/code/__HELPERS/text/sanitize.dm new file mode 100644 index 000000000000..5b3033e3fdf2 --- /dev/null +++ b/code/__HELPERS/text/sanitize.dm @@ -0,0 +1,10 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * get rid of non alphanumeric chars in a string + */ +/proc/string_filter_to_alphanumeric(str) + // todo: rustg + var/static/regex/expression = regex(@"[^a-zA-Z0-9_ ]+", "g") + return replacetext_char(str, expression, "") diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 2e3e736f72bd..02e897d8927b 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -6,30 +6,6 @@ /// Checks if all high bits in req_mask are set in bitfield. #define BIT_TEST_ALL(bitfield, req_mask) ((~(bitfield) & (req_mask)) == 0) -/// Inverts the colour of an HTML string. -/// TODO: We can probably do this better these days. @Zandario -/proc/invertHTML(HTMLstring) - if (!(istext(HTMLstring))) - CRASH("Given non-text argument!") - else if(length(HTMLstring) != 7) - CRASH("Given non-HTML argument!") - var/textr = copytext(HTMLstring, 2, 4) - var/textg = copytext(HTMLstring, 4, 6) - var/textb = copytext(HTMLstring, 6, 8) - var/r = hex2num(textr) - var/g = hex2num(textg) - var/b = hex2num(textb) - textr = num2hex(255 - r) - textg = num2hex(255 - g) - textb = num2hex(255 - b) - if (length(textr) < 2) - textr = "0[textr]" - if (length(textg) < 2) - textr = "0[textg]" - if (length(textb) < 2) - textr = "0[textb]" - return "#[textr][textg][textb]" - /** * Returns location. Returns null if no location was found. * @@ -219,9 +195,6 @@ return TRUE return FALSE -/proc/sign(x) - return x!=0?x/abs(x):0 - /// Ultra-Fast Bresenham Line-Drawing Algorithm. /proc/getline(atom/M,atom/N) /// Starting x coordinate. @@ -239,9 +212,9 @@ /// Absolute value of y distance. var/dyabs = abs(dy) ///Sign of x distance (+ or -). - var/sdx = sign(dx) + var/sdx = SIGN(dx) ///Sign of y distance (+ or -). - var/sdy = sign(dy) + var/sdy = SIGN(dy) /// Counters for steps taken, setting to distance/2. var/x = (dxabs >> 1) /// Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast. @@ -491,13 +464,6 @@ // GLOB.mob_list.Add(M) return moblist -/// Forces a variable to be positive. -/proc/modulus(variable) - if(variable >= 0) - return variable - if(variable < 0) - return -variable - /// Returns the turf located at the map edge in the specified direction relative to A. /proc/get_edge_target_turf(atom/A, direction) //Used for mass driver diff --git a/code/datums/profile.dm b/code/__HELPERS/unsorted/profile.dm similarity index 100% rename from code/datums/profile.dm rename to code/__HELPERS/unsorted/profile.dm diff --git a/code/__HELPERS/vector.dm b/code/__HELPERS/vector.dm index 5470eb0e9628..e6ffaefaacf9 100644 --- a/code/__HELPERS/vector.dm +++ b/code/__HELPERS/vector.dm @@ -94,14 +94,14 @@ // calculate the offset per increment step if(abs(angle) in list(0, 45, 90, 135, 180)) // check if the angle is a cardinal if(abs(angle) in list(0, 45, 135, 180)) // if so we can skip the trigonometry and set these to absolutes as - offset_x = sign(dx) // they will always be a full step in one or more directions + offset_x = SIGN(dx) // they will always be a full step in one or more directions if(abs(angle) in list(45, 90, 135)) - offset_y = sign(dy) + offset_y = SIGN(dy) else if(abs(dy) > abs(dx)) offset_x = COT(abs(angle)) // otherwise set the offsets - offset_y = sign(dy) + offset_y = SIGN(dy) else - offset_x = sign(dx) + offset_x = SIGN(dx) offset_y = tan(angle) if(dx < 0) offset_y = -offset_y diff --git a/code/controllers/subsystem/early_init.dm b/code/controllers/subsystem/early_init.dm index 603af16e47fc..7f455a6ef2d7 100644 --- a/code/controllers/subsystem/early_init.dm +++ b/code/controllers/subsystem/early_init.dm @@ -1,3 +1,6 @@ +/** + * basically, for shit that's laggy so we don't want init'd with GLOB. + */ SUBSYSTEM_DEF(early_init) name = "Early Init" init_order = INIT_ORDER_EARLY_INIT diff --git a/code/controllers/subsystem/materials.dm b/code/controllers/subsystem/materials.dm index a4adda3d45d9..36f78528a3c4 100644 --- a/code/controllers/subsystem/materials.dm +++ b/code/controllers/subsystem/materials.dm @@ -137,6 +137,39 @@ SUBSYSTEM_DEF(materials) return CRASH("what?") +/** + * resolve a 'reskinned' material + * + * basically, a material but looking like another + * + * @params + * * id_or_path - real material to use + * * into_material - what material to make it look like + */ +/datum/controller/subsystem/materials/proc/resolve_reskinned_material(datum/material/id_or_path, datum/material/into_material) + // todo: optimize + var/datum/material/source_material = resolve_material(id_or_path) + var/datum/material/target_material = resolve_material(into_material) + var/reskinned_id = "[target_material.id]-reskinned-[source_material.id]" + if(isnull(material_lookup[reskinned_id])) + var/datum/material/created_reskin = source_material.clone() + var/static/list/direct_clone_vars = list( + NAMEOF(created_reskin, icon_colour), + NAMEOF(created_reskin, icon_base), + NAMEOF(created_reskin, icon_reinf), + NAMEOF(created_reskin, wall_stripe_icon), + NAMEOF(created_reskin, door_icon_base), + NAMEOF(created_reskin, table_icon_base), + NAMEOF(created_reskin, table_reinf_icon_base), + NAMEOF(created_reskin, icon_reinf_directionals), + NAMEOF(created_reskin, shard_icon), + NAMEOF(created_reskin, tgui_icon_key), + ) + for(var/varname in direct_clone_vars) + created_reskin.vars[varname] = into_material.vars[varname] + material_lookup[reskinned_id] = created_reskin + return material_lookup[reskinned_id] + /** * ensures a list is full of material ids for keys * diff --git a/code/datums/beam.dm b/code/datums/beam.dm index 01e0b280032c..7a948e6efccc 100644 --- a/code/datums/beam.dm +++ b/code/datums/beam.dm @@ -1,246 +1,513 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// -/** # Beam Datum and Effect - * **IF YOU ARE LAZY AND DO NOT WANT TO READ, GO TO THE BOTTOM OF THE FILE AND USE THAT PROC!** +/** + * creates a segmenting beam * - * This is the beam datum! It's a really neat effect for the game in drawing a line from one atom to another. - * It has two parts: - * The datum itself which manages redrawing the beam to constantly keep it pointing from the origin to the target. - * The effect which is what the beams are made out of. They're placed in a line from the origin to target, rotated towards the target and snipped off at the end. - * These effects are kept in a list and constantly created and destroyed (hence the proc names draw and reset, reset destroying all effects and draw creating more.) + * you must delete this beam yourself, unlike legacy beams * - * You can add more special effects to the beam itself by changing what the drawn beam effects do. For example you can make a vine that pricks people by making the beam_type - * include a crossed proc that damages the crosser. Examples in venus_human_trap.dm -*/ -/datum/beam - ///where the beam goes from - var/atom/origin = null - ///where the beam goes to - var/atom/target = null - ///list of beam objects. These have their visuals set by the visuals var which is created on starting - var/list/elements = list() - ///icon used by the beam. - var/icon - ///icon state of the main segments of the beam - var/icon_state = "" - ///The beam will qdel if it's longer than this many tiles. - var/max_distance = 0 - ///the objects placed in the elements list - var/beam_type = /obj/effect/ebeam - ///This is used as the visual_contents of beams, so you can apply one effect to this and the whole beam will look like that. never gets deleted on redrawing. - var/obj/effect/ebeam/visuals - ///The color of the beam we're drawing. - var/beam_color - ///If we use an emissive appearance - var/emissive = TRUE - /// If set will be used instead of origin's pixel_x in offset calculations - var/override_origin_pixel_x = null - /// If set will be used instead of origin's pixel_y in offset calculations - var/override_origin_pixel_y = null - /// If set will be used instead of targets's pixel_x in offset calculations - var/override_target_pixel_x = null - /// If set will be used instead of targets's pixel_y in offset calculations - var/override_target_pixel_y = null - -/datum/beam/New(origin, target, icon = 'icons/effects/beam.dmi', icon_state = "b_beam", time = INFINITY, max_distance = INFINITY, beam_type = /obj/effect/ebeam, beam_color = null, emissive = TRUE, override_origin_pixel_x = null, override_origin_pixel_y = null, override_target_pixel_x = null, override_target_pixel_y = null) - src.origin = origin - src.target = target - src.icon = icon - src.icon_state = icon_state - src.max_distance = max_distance - src.beam_type = beam_type - src.beam_color = beam_color - src.emissive = emissive - src.override_origin_pixel_x = override_origin_pixel_x - src.override_origin_pixel_y = override_origin_pixel_y - src.override_target_pixel_x = override_target_pixel_x - src.override_target_pixel_y = override_target_pixel_y - if(time < INFINITY) - QDEL_IN(src, time) + * @params + * * source - where to draw from + * * target - where to draw to + * * collider_type - set to a type to create colliders + * * icon - beam icon + * * icon_state - beam icon state + * * emissive_state - emissive overlay to use + */ +/proc/create_segmented_beam(atom/source, atom/target, collider_type, icon, icon_state, emissive_state) + var/datum/beam/created = new(source, target) + if(!isnull(collider_type)) + created.collider_type = collider_type + created.icon = icon + created.icon_state = icon_state + created.emissive_state = emissive_state + created.beam_visual_mode = BEAM_VISUAL_SEGMENTS + created.initialize() + return created + +/** + * creates a stretching beam + * + * you must delete this beam yourself, unlike legacy beams + * + * @params + * * source - where to draw from + * * target - where to draw to + * * collider_type - set to a type to create colliders + * * icon - beam icon + * * icon_state - beam icon state + * * emissive_state - emissive overlay to use + */ +/proc/create_stretched_beam(atom/source, atom/target, collider_type, icon, icon_state, emissive_state) + var/datum/beam/created = new(source, target) + if(!isnull(collider_type)) + created.collider_type = collider_type + created.icon = icon + created.icon_state = icon_state + created.emissive_state = emissive_state + created.beam_visual_mode = BEAM_VISUAL_STRETCH + created.initialize() + return created /** - * Proc called by the atom Beam() proc. Sets up signals, and draws the beam for the first time. + * creates a beam that... doesn't render (lmao) + * + * you must delete this beam yourself, unlike legacy beams + * + * @params + * * source - where to draw from + * * target - where to draw to + * * collider_type - set to a type to create colliders */ -/datum/beam/proc/Start() - visuals = new beam_type() - visuals.icon = icon - visuals.icon_state = icon_state - visuals.color = beam_color - visuals.layer = ABOVE_MOB_LAYER - visuals.vis_flags = VIS_INHERIT_PLANE - visuals.emissive = emissive - visuals.update_appearance() - Draw() - RegisterSignal(origin, COMSIG_MOVABLE_MOVED, PROC_REF(redrawing)) - RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(redrawing)) +/proc/create_particle_beam(atom/source, atom/target, collider_type) + var/datum/beam/created = new(source, target) + if(!isnull(collider_type)) + created.collider_type = collider_type + created.initialize() + return created /** - * Triggered by signals set up when the beam is set up. If it's still sane to create a beam, it removes the old beam, creates a new one. Otherwise it kills the beam. + * linear on-map line drawing datums + * + * can optionally handle colliders. * - * Arguments: - * mover: either the origin of the beam or the target of the beam that moved. - * oldloc: from where mover moved. - * direction: in what direction mover moved from. + * * beam_source, beam_target are both get_turf()'d. it is *your* job to handle whether or not this is valid + * * beam drawing is event-driven. it doesn't redraw unless necessary. */ -/datum/beam/proc/redrawing(atom/movable/mover, atom/oldloc, direction) - SIGNAL_HANDLER - if(origin && target && get_dist(origin,target) length(segmentation.segment_appearances)) + requires_update = TRUE + var/start_from_pixel = length(segmentation.segment_appearances) * WORLD_ICON_SIZE + for(var/i in 1 to steps_required - length(segmentation.segment_appearances)) + var/image/injecting = image(icon_state = icon_state) + injecting.pixel_y = i * WORLD_ICON_SIZE - (WORLD_ICON_SIZE * 0.5) + start_from_pixel + segmentation.segment_appearances += injecting + if(!isnull(emissive_segmentation)) + for(var/i in 1 to steps_required - length(emissive_segmentation.segment_appearances)) + var/image/emissive_injecting = image(icon_state = emissive_state) + emissive_injecting.pixel_y = i * WORLD_ICON_SIZE - (WORLD_ICON_SIZE * 0.5) + start_from_pixel + emissive_segmentation.segment_appearances += emissive_injecting + else if(steps_required < length(segmentation.segment_appearances)) + requires_update = TRUE + segmentation.segment_appearances.len = steps_required + emissive_segmentation?.segment_appearances.len = steps_required + if(requires_update) + segmentation.overlays = segmentation.segment_appearances + emissive_segmentation?.overlays = emissive_segmentation?.segment_appearances + // calculate matrix + var/matrix/transform_to_apply = matrix() + // transform as necessary to shrink just enough to cut off extra pixels + // todo: please use an alphamask filter on last element maybe, this looks like shit + transform_to_apply.Scale(1, distance / (steps_required * WORLD_ICON_SIZE)) + // rotate + transform_to_apply.Turn(angle) + // move renders to location + segmentation.forceMove(start) + segmentation.pixel_x = start_px + segmentation.pixel_y = start_py + segmentation.transform = transform_to_apply + if(BEAM_VISUAL_STRETCH) + // calculate matrix + var/matrix/transform_to_apply = matrix() + // shift up so it's not in the way + transform_to_apply.Translate(0, 16) + // scale up the now shifted one so we don't have to translate post-scale + transform_to_apply.Scale(1, distance / WORLD_ICON_SIZE) + // rotate + transform_to_apply.Turn(angle) + // handle normal render + line_renderer.forceMove(start) + line_renderer.pixel_x = start_px + line_renderer.pixel_y = start_py + line_renderer.transform = transform_to_apply + // deal with colliders + if(!isnull(collider_type)) + var/list/turf/colliding_turfs = getline(start, end) + if(length(colliding_turfs) < colliders) + // if more than needed, nullspace the rest but keep them on hand + for(var/i in length(colliding_turfs) + 1 to length(colliders)) + var/atom/movable/beam_collider/collider = colliders[i] + collider.move_onto(null) + else if(length(colliding_turfs) > colliders) + // if less than needed, make new ones but don't collide yet + for(var/i in 1 to length(colliding_turfs) - length(colliders)) + colliders += new /atom/movable/beam_collider(src) + // we should be greater-or-equal colliders than turfs now + // now-overallocated colliders are ignored and cached in nullspace + // this system also enforces deterministic source-to-target propagation as opposed + // to non-deterministic orderings. + for(var/i in 1 to min(length(colliders), length(colliding_turfs))) + var/atom/movable/beam_collider/collider = colliders[i] + collider.move_onto(colliding_turfs[i]) + // deal with particles + if(!isnull(particle_renderer)) + // calculate matrix + var/matrix/transform_to_apply = matrix() + // rotate + transform_to_apply.Turn(angle) + // move renders to location + particle_renderer.forceMove(start) + particle_renderer.pixel_x = start_px + particle_renderer.pixel_y = start_py + particle_renderer.transform = transform_to_apply + +/datum/beam/proc/uncrossed(atom/movable/entity) + SHOULD_CALL_PARENT(TRUE) + colliding -= entity + SEND_SIGNAL(src, COMSIG_BEAM_UNCROSSED, entity) + +/datum/beam/proc/crossed(atom/movable/entity) + SHOULD_CALL_PARENT(TRUE) + colliding += entity + SEND_SIGNAL(src, COMSIG_BEAM_CROSSED, entity) + /** - * Creates the beam effects and places them in a line from the origin to the target. Sets their rotation to make the beams face the target, too. + * adds a set of particles to this beam + * + * you have to control the particles yourself + * + * * the beam assumes that the particles will project in the **north** direction. + * * this just makes the beam automatically rotate the particles as necessary. + * * if you pass in 'TRUE' as emissive_particle_renderer, emissives will render_source from the main particle system. */ -/datum/beam/proc/Draw() - if(SEND_SIGNAL(src, COMSIG_BEAM_BEFORE_DRAW) & BEAM_CANCEL_DRAW) - return - var/origin_px = isnull(override_origin_pixel_x) ? origin.pixel_x : override_origin_pixel_x - var/origin_py = isnull(override_origin_pixel_y) ? origin.pixel_y : override_origin_pixel_y - var/target_px = isnull(override_target_pixel_x) ? target.pixel_x : override_target_pixel_x - var/target_py = isnull(override_target_pixel_y) ? target.pixel_y : override_target_pixel_y - var/Angle = get_visual_angle_raw(origin.x, origin.y, origin_px, origin_py, target.x , target.y, target_px, target_py) - ///var/Angle = round(get_visual_angle(origin,target)) - var/matrix/rot_matrix = matrix() - var/turf/origin_turf = get_turf(origin) - rot_matrix.Turn(Angle) - - //Translation vector for origin and target - var/DX = (32*target.x+target_px)-(32*origin.x+origin_px) - var/DY = (32*target.y+target_py)-(32*origin.y+origin_py) - var/N = 0 - var/length = round(sqrt((DX)**2+(DY)**2)) //hypotenuse of the triangle formed by target and origin's displacement - - for(N in 0 to length-1 step 32)//-1 as we want < not <=, but we want the speed of X in Y to Z and step X - if(QDELETED(src)) - break - var/obj/effect/ebeam/segment = new beam_type(origin_turf, src) - elements += segment - - //Assign our single visual ebeam to each ebeam's vis_contents - //ends are cropped by a transparent box icon of length-N pixel size laid over the visuals obj - if(N+32>length) //went past the target, we draw a box of space to cut away from the beam sprite so the icon actually ends at the center of the target sprite - var/icon/II = new(icon, icon_state)//this means we exclude the overshooting object from the visual contents which does mean those visuals don't show up for the final bit of the beam... - II.DrawBox(null,1,(length-N),32,32)//in the future if you want to improve this, remove the drawbox and instead use a 513 filter to cut away at the final object's icon - segment.icon = II - segment.color = beam_color +/datum/beam/proc/set_particles(particles/particle_reference, particles/emissive_particle_reference) + if(isnull(particle_renderer)) + particle_renderer = new + particle_renderer.particles = particle_reference + if(!isnull(emissive_particle_reference)) + if(isnull(emissive_particle_renderer)) + emissive_particle_renderer = new + if(emissive_particle_reference == TRUE) + particle_renderer.ensure_render_target() + emissive_particle_renderer.render_source = particle_renderer.render_target else - segment.vis_contents += visuals - segment.transform = rot_matrix - - //Calculate pixel offsets (If necessary) - var/Pixel_x - var/Pixel_y - if(DX == 0) - Pixel_x = 0 - else - Pixel_x = round(sin(Angle)+32*sin(Angle)*(N+16)/32) - if(DY == 0) - Pixel_y = 0 - else - Pixel_y = round(cos(Angle)+32*cos(Angle)*(N+16)/32) - - //Position the effect so the beam is one continous line - var/a - if(abs(Pixel_x)>32) - a = Pixel_x > 0 ? round(Pixel_x/32) : CEILING(Pixel_x/32, 1) - segment.x += a - Pixel_x %= 32 - if(abs(Pixel_y)>32) - a = Pixel_y > 0 ? round(Pixel_y/32) : CEILING(Pixel_y/32, 1) - segment.y += a - Pixel_y %= 32 - - segment.pixel_x = origin_px + Pixel_x - segment.pixel_y = origin_py + Pixel_y - CHECK_TICK - -/obj/effect/ebeam - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - anchored = TRUE - var/emissive = TRUE - var/datum/beam/owner + emissive_particle_renderer.particles = emissive_particle_reference + else if(!isnull(emissive_particle_renderer)) + QDEL_NULL(emissive_particle_renderer) + force_redraw() -/obj/effect/ebeam/Initialize(mapload, beam_owner) - owner = beam_owner - return ..() +//* Segmented Renderers *// -/obj/effect/ebeam/update_overlays() - . = ..() - if(!emissive) - return - var/mutable_appearance/emissive_overlay = emissive_appearance(icon, icon_state, src) - emissive_overlay.transform = transform - . += emissive_overlay +/atom/movable/graphics_render/beam_segments + appearance_flags = KEEP_TOGETHER + animate_movement = NONE + + /// segments in us + var/list/image/segment_appearances = list() + +/atom/movable/graphics_render/beam_segments/emissive + plane = EMISSIVE_PLANE -/obj/effect/ebeam/Destroy() - owner = null +/atom/movable/graphics_render/beam_segments/emissive/Initialize(mapload) + ASSERT(istype(loc, /atom/movable/graphics_render/beam_segments)) + loc:vis_contents += src return ..() -/obj/effect/ebeam/singularity_pull() - return +/atom/movable/graphics_render/beam_segments/emissive/Destroy() + ASSERT(istype(loc, /atom/movable/graphics_render/beam_segments)) + loc:vis_contents -= src + return ..() + +//* Colliders *// -/obj/effect/ebeam/singularity_act() - return +INITIALIZE_IMMEDIATE(/atom/movable/beam_collider) +/atom/movable/beam_collider + atom_flags = ATOM_ABSTRACT + animate_movement = NONE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + invisibility = INVISIBILITY_ABSTRACT -// 'Reactive' beam parts do something when touched or stood in. -/obj/effect/ebeam/reactive + /// parent beam + var/datum/beam/parent -/obj/effect/ebeam/reactive/Initialize(mapload) - START_PROCESSING(SSobj, src) - return ..() +/atom/movable/beam_collider/Initialize(mapload, datum/beam/parent) + SHOULD_CALL_PARENT(FALSE) + src.parent = parent + atom_flags |= ATOM_INITIALIZED + return INITIALIZE_HINT_NORMAL -/obj/effect/ebeam/reactive/Destroy() - STOP_PROCESSING(SSobj, src) +/atom/movable/beam_collider/Destroy() + if(!isnull(parent)) + // make sure we uncross everything! + move_onto(null) + parent = null return ..() -/obj/effect/ebeam/reactive/Crossed(atom/A) - if(A.is_incorporeal()) +/atom/movable/beam_collider/proc/move_onto(turf/where) + if(where == loc) + return + for(var/atom/movable/other as anything in loc) + if(other == src) + continue + if(other.atom_flags & ATOM_ABSTRACT) + continue + parent.uncrossed(other) + parent.uncrossed(loc) + abstract_move(where) + parent.crossed(loc) + for(var/atom/movable/other as anything in loc) + if(other == src) + continue + if(other.atom_flags & ATOM_ABSTRACT) + continue + parent.crossed(other) + +/atom/movable/beam_collider/Crossed(atom/movable/AM) + . = ..() + if(AM.atom_flags & (ATOM_ABSTRACT)) + return + parent.crossed(AM) + +/atom/movable/beam_collider/Uncrossed(atom/movable/AM) + . = ..() + if(AM.atom_flags & (ATOM_ABSTRACT)) return - ..() - on_contact(A) + parent.uncrossed(AM) -/obj/effect/ebeam/reactive/process(delta_time) - for(var/A in loc) - on_contact(A) +/atom/movable/beam_collider/Move() + return FALSE -// Override for things to do when someone touches the beam. -/obj/effect/ebeam/reactive/proc/on_contact(atom/movable/AM) - return +/atom/movable/beam_collider/doMove(atom/destination) + if(destination == null) + return ..() + return FALSE -// Shocks things that touch it. -/obj/effect/ebeam/reactive/electric - var/shock_amount = 25 // Be aware that high numbers may stun and result in dying due to not being able to get out of the beam. +//* Stretched Renderers *// -/obj/effect/ebeam/reactive/electric/on_contact(atom/movable/AM) - if(isliving(AM)) - var/mob/living/L = AM - L.inflict_shock_damage(shock_amount) +/atom/movable/graphics_render/beam_line + appearance_flags = KEEP_TOGETHER + animate_movement = NONE -/** - * This is what you use to start a beam. Example: origin.Beam(target, args). **Store the return of this proc if you don't set maxdist or time, you need it to delete the beam.** - * - * Unless you're making a custom beam effect (see the beam_type argument), you won't actually have to mess with any other procs. Make sure you store the return of this Proc, you'll need it - * to kill the beam. - * **Arguments:** - * BeamTarget: Where you're beaming from. Where do you get origin? You didn't read the docs, fuck you. - * icon_state: What the beam's icon_state is. The datum effect isn't the ebeam object, it doesn't hold any icon and isn't type dependent. - * icon: What the beam's icon file is. Don't change this, man. All beam icons should be in beam.dmi anyways. - * maxdistance: how far the beam will go before stopping itself. Used mainly for two things: preventing lag if the beam may go in that direction and setting a range to abilities that use beams. - * beam_type: The type of your custom beam. This is for adding other wacky stuff for your beam only. Most likely, you won't (and shouldn't) change it. - */ -/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=INFINITY,maxdistance=INFINITY,beam_type=/obj/effect/ebeam, beam_color = null, emissive = TRUE, override_origin_pixel_x = null, override_origin_pixel_y = null, override_target_pixel_x = null, override_target_pixel_y = null) - var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type, beam_color, emissive, override_origin_pixel_x, override_origin_pixel_y, override_target_pixel_x, override_target_pixel_y ) - INVOKE_ASYNC(newbeam, TYPE_PROC_REF(/datum/beam/, Start)) - return newbeam +/atom/movable/graphics_render/beam_line/emissive + plane = EMISSIVE_PLANE +/atom/movable/graphics_render/beam_line/emissive/Initialize(mapload) + ASSERT(istype(loc, /atom/movable/graphics_render/beam_line)) + loc:vis_contents += src + return ..() +/atom/movable/graphics_render/beam_line/emissive/Destroy() + ASSERT(istype(loc, /atom/movable/graphics_render/beam_line)) + loc:vis_contents -= src + return ..() + +//* Particle Renderers *// + +/atom/movable/particle_render/beam_line + appearance_flags = KEEP_TOGETHER + animate_movement = NONE + +/atom/movable/particle_render/beam_line/emissive + plane = EMISSIVE_PLANE + +/atom/movable/particle_render/beam_line/emissive/Initialize(mapload) + ASSERT(istype(loc, /atom/movable/particle_render/beam_line)) + loc:vis_contents += src + return ..() + +/atom/movable/particle_render/beam_line/emissive/Destroy() + ASSERT(istype(loc, /atom/movable/particle_render/beam_line)) + loc:vis_contents -= src + return ..() diff --git a/code/datums/beam_legacy.dm b/code/datums/beam_legacy.dm new file mode 100644 index 000000000000..e3feb2155d9c --- /dev/null +++ b/code/datums/beam_legacy.dm @@ -0,0 +1,246 @@ + +/** # Beam Datum and Effect + * **IF YOU ARE LAZY AND DO NOT WANT TO READ, GO TO THE BOTTOM OF THE FILE AND USE THAT PROC!** + * + * This is the beam datum! It's a really neat effect for the game in drawing a line from one atom to another. + * It has two parts: + * The datum itself which manages redrawing the beam to constantly keep it pointing from the origin to the target. + * The effect which is what the beams are made out of. They're placed in a line from the origin to target, rotated towards the target and snipped off at the end. + * These effects are kept in a list and constantly created and destroyed (hence the proc names draw and reset, reset destroying all effects and draw creating more.) + * + * You can add more special effects to the beam itself by changing what the drawn beam effects do. For example you can make a vine that pricks people by making the beam_type + * include a crossed proc that damages the crosser. Examples in venus_human_trap.dm +*/ +/datum/beam_legacy + ///where the beam goes from + var/atom/origin = null + ///where the beam goes to + var/atom/target = null + ///list of beam objects. These have their visuals set by the visuals var which is created on starting + var/list/elements = list() + ///icon used by the beam. + var/icon + ///icon state of the main segments of the beam + var/icon_state = "" + ///The beam will qdel if it's longer than this many tiles. + var/max_distance = 0 + ///the objects placed in the elements list + var/beam_type = /obj/effect/ebeam + ///This is used as the visual_contents of beams, so you can apply one effect to this and the whole beam will look like that. never gets deleted on redrawing. + var/obj/effect/ebeam/visuals + ///The color of the beam we're drawing. + var/beam_color + ///If we use an emissive appearance + var/emissive = TRUE + /// If set will be used instead of origin's pixel_x in offset calculations + var/override_origin_pixel_x = null + /// If set will be used instead of origin's pixel_y in offset calculations + var/override_origin_pixel_y = null + /// If set will be used instead of targets's pixel_x in offset calculations + var/override_target_pixel_x = null + /// If set will be used instead of targets's pixel_y in offset calculations + var/override_target_pixel_y = null + +/datum/beam_legacy/New(origin, target, icon = 'icons/effects/beam.dmi', icon_state = "b_beam", time = INFINITY, max_distance = INFINITY, beam_type = /obj/effect/ebeam, beam_color = null, emissive = TRUE, override_origin_pixel_x = null, override_origin_pixel_y = null, override_target_pixel_x = null, override_target_pixel_y = null) + src.origin = origin + src.target = target + src.icon = icon + src.icon_state = icon_state + src.max_distance = max_distance + src.beam_type = beam_type + src.beam_color = beam_color + src.emissive = emissive + src.override_origin_pixel_x = override_origin_pixel_x + src.override_origin_pixel_y = override_origin_pixel_y + src.override_target_pixel_x = override_target_pixel_x + src.override_target_pixel_y = override_target_pixel_y + if(time < INFINITY) + QDEL_IN(src, time) + +/** + * Proc called by the atom Beam() proc. Sets up signals, and draws the beam for the first time. + */ +/datum/beam_legacy/proc/Start() + visuals = new beam_type() + visuals.icon = icon + visuals.icon_state = icon_state + visuals.color = beam_color + visuals.layer = ABOVE_MOB_LAYER + visuals.vis_flags = VIS_INHERIT_PLANE + visuals.emissive = emissive + visuals.update_appearance() + Draw() + RegisterSignal(origin, COMSIG_MOVABLE_MOVED, PROC_REF(redrawing)) + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(redrawing)) + +/** + * Triggered by signals set up when the beam is set up. If it's still sane to create a beam, it removes the old beam, creates a new one. Otherwise it kills the beam. + * + * Arguments: + * mover: either the origin of the beam or the target of the beam that moved. + * oldloc: from where mover moved. + * direction: in what direction mover moved from. + */ +/datum/beam_legacy/proc/redrawing(atom/movable/mover, atom/oldloc, direction) + SIGNAL_HANDLER + if(origin && target && get_dist(origin,target)length) //went past the target, we draw a box of space to cut away from the beam sprite so the icon actually ends at the center of the target sprite + var/icon/II = new(icon, icon_state)//this means we exclude the overshooting object from the visual contents which does mean those visuals don't show up for the final bit of the beam... + II.DrawBox(null,1,(length-N),32,32)//in the future if you want to improve this, remove the drawbox and instead use a 513 filter to cut away at the final object's icon + segment.icon = II + segment.color = beam_color + else + segment.vis_contents += visuals + segment.transform = rot_matrix + + //Calculate pixel offsets (If necessary) + var/Pixel_x + var/Pixel_y + if(DX == 0) + Pixel_x = 0 + else + Pixel_x = round(sin(Angle)+32*sin(Angle)*(N+16)/32) + if(DY == 0) + Pixel_y = 0 + else + Pixel_y = round(cos(Angle)+32*cos(Angle)*(N+16)/32) + + //Position the effect so the beam is one continous line + var/a + if(abs(Pixel_x)>32) + a = Pixel_x > 0 ? round(Pixel_x/32) : CEILING(Pixel_x/32, 1) + segment.x += a + Pixel_x %= 32 + if(abs(Pixel_y)>32) + a = Pixel_y > 0 ? round(Pixel_y/32) : CEILING(Pixel_y/32, 1) + segment.y += a + Pixel_y %= 32 + + segment.pixel_x = origin_px + Pixel_x + segment.pixel_y = origin_py + Pixel_y + CHECK_TICK + +/obj/effect/ebeam + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + anchored = TRUE + var/emissive = TRUE + var/datum/beam_legacy/owner + +/obj/effect/ebeam/Initialize(mapload, beam_owner) + owner = beam_owner + return ..() + +/obj/effect/ebeam/update_overlays() + . = ..() + if(!emissive) + return + var/mutable_appearance/emissive_overlay = emissive_appearance(icon, icon_state, src) + emissive_overlay.transform = transform + . += emissive_overlay + +/obj/effect/ebeam/Destroy() + owner = null + return ..() + +/obj/effect/ebeam/singularity_pull() + return + +/obj/effect/ebeam/singularity_act() + return + +// 'Reactive' beam parts do something when touched or stood in. +/obj/effect/ebeam/reactive + +/obj/effect/ebeam/reactive/Initialize(mapload) + START_PROCESSING(SSobj, src) + return ..() + +/obj/effect/ebeam/reactive/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/ebeam/reactive/Crossed(atom/A) + if(A.is_incorporeal()) + return + ..() + on_contact(A) + +/obj/effect/ebeam/reactive/process(delta_time) + for(var/A in loc) + on_contact(A) + +// Override for things to do when someone touches the beam. +/obj/effect/ebeam/reactive/proc/on_contact(atom/movable/AM) + return + +// Shocks things that touch it. +/obj/effect/ebeam/reactive/electric + var/shock_amount = 25 // Be aware that high numbers may stun and result in dying due to not being able to get out of the beam. + +/obj/effect/ebeam/reactive/electric/on_contact(atom/movable/AM) + if(isliving(AM)) + var/mob/living/L = AM + L.inflict_shock_damage(shock_amount) + +/** + * This is what you use to start a beam. Example: origin.Beam(target, args). **Store the return of this proc if you don't set maxdist or time, you need it to delete the beam.** + * + * Unless you're making a custom beam effect (see the beam_type argument), you won't actually have to mess with any other procs. Make sure you store the return of this Proc, you'll need it + * to kill the beam. + * **Arguments:** + * BeamTarget: Where you're beaming from. Where do you get origin? You didn't read the docs, fuck you. + * icon_state: What the beam's icon_state is. The datum effect isn't the ebeam object, it doesn't hold any icon and isn't type dependent. + * icon: What the beam's icon file is. Don't change this, man. All beam icons should be in beam.dmi anyways. + * maxdistance: how far the beam will go before stopping itself. Used mainly for two things: preventing lag if the beam may go in that direction and setting a range to abilities that use beams. + * beam_type: The type of your custom beam. This is for adding other wacky stuff for your beam only. Most likely, you won't (and shouldn't) change it. + */ +/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=INFINITY,maxdistance=INFINITY,beam_type=/obj/effect/ebeam, beam_color = null, emissive = TRUE, override_origin_pixel_x = null, override_origin_pixel_y = null, override_target_pixel_x = null, override_target_pixel_y = null) + var/datum/beam_legacy/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type, beam_color, emissive, override_origin_pixel_x, override_origin_pixel_y, override_target_pixel_x, override_target_pixel_y ) + INVOKE_ASYNC(newbeam, TYPE_PROC_REF(/datum/beam_legacy/, Start)) + return newbeam + + diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index d46b995f7577..9abc432544bc 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -329,8 +329,8 @@ */ /datum/proc/GetComponent(datum/component/c_type) RETURN_TYPE(c_type) - . = datum_components?[initial(c_type.registered_type)] - return . && (length(.) ? .[1] : .) + . = datum_components?[initial(c_type.registered_type) || c_type] + . = length(.)? .[1] : . /** * Get all components of a given type that are attached to this datum @@ -435,9 +435,7 @@ * * ... additional arguments to be passed when creating the component if it does not exist */ /datum/proc/_LoadComponent(list/arguments) - . = GetComponent(arguments[1]) - if(!.) - return _AddComponent(arguments) + return GetComponent(arguments[1]) || _AddComponent(arguments) /** * qdels a component of given registered type, diff --git a/code/datums/datum.dm b/code/datums/datum.dm index ecbdbba3da60..449cc1be8bec 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -183,3 +183,9 @@ */ /datum/proc/deserialize(list/data) return TRUE + +/** + * clone this entity + */ +/datum/proc/clone() + return new type diff --git a/code/datums/design/design.dm b/code/datums/design/design.dm index b0d33e61afd0..e2061bd251ae 100644 --- a/code/datums/design/design.dm +++ b/code/datums/design/design.dm @@ -11,7 +11,7 @@ abstract_type = /datum/design //? Design Data - Core - /// Must be unique - id of design in CamelCase. + /// Must be unique - id of design should be 'written-like-this'. var/id /// design flags - see [code/__DEFINES/datums/design.dm] var/design_flags = NONE diff --git a/code/datums/event_args/_event_args.dm b/code/datums/event_args/_event_args.dm index d2569f99fff3..a105a2db6772 100644 --- a/code/datums/event_args/_event_args.dm +++ b/code/datums/event_args/_event_args.dm @@ -1,3 +1,6 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + /** * datums used to hold data for procs without having to pass too many args around */ diff --git a/code/datums/event_args/actor.dm b/code/datums/event_args/actor.dm index c5186ef2ed96..53ca2411b217 100644 --- a/code/datums/event_args/actor.dm +++ b/code/datums/event_args/actor.dm @@ -1,3 +1,6 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + /** * used to hold semantic data about an action being done by an actor vs initiator (controller) */ diff --git a/code/datums/event_args/clickchain.dm b/code/datums/event_args/clickchain.dm index 4f350e9cae3c..10c381c88dae 100644 --- a/code/datums/event_args/clickchain.dm +++ b/code/datums/event_args/clickchain.dm @@ -1,3 +1,6 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + /** * used to hold data about a click action */ diff --git a/code/datums/event_args/melee_attack.dm b/code/datums/event_args/melee_attack.dm new file mode 100644 index 000000000000..1873b0cde723 --- /dev/null +++ b/code/datums/event_args/melee_attack.dm @@ -0,0 +1,15 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * data about a melee attack with a weapon + */ +/datum/event_args/melee_attack + var/damage_multiplier = 1 + var/intent = INTENT_HARM + var/target_zone + var/obj/item/weapon + // future use, slash / stab / smash enum + // var/style + // future use, swing data + // var/datum/event_args/melee_swing/swing diff --git a/code/datums/event_args/unarmed_attack.dm b/code/datums/event_args/unarmed_attack.dm new file mode 100644 index 000000000000..abad6ee69279 --- /dev/null +++ b/code/datums/event_args/unarmed_attack.dm @@ -0,0 +1,14 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * data about an unarmed attack + */ +/datum/event_args/unarmed_attack + var/damage_multiplier = 1 + var/intent = INTENT_HARM + var/target_zone + // todo: get default style if not specified + var/datum/unarmed_attack/style + // future use, swing data + // var/datum/event_args/melee_swing/swing diff --git a/code/datums/item_interface.dm b/code/datums/item_interface.dm new file mode 100644 index 000000000000..7ab586cf2d2e --- /dev/null +++ b/code/datums/item_interface.dm @@ -0,0 +1,240 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * base, abstract type of item interfaces + * + * used to connect an item to a host + * + * * a standard implementation of a power handling bus is provided due to its difficulty. + */ +/datum/item_interface + abstract_type = /datum/item_interface + + //* Power Handling *// + /// allow default power bus to be invoked + var/default_power_bus_implementation = FALSE + /// current draw in W + var/default_power_bus_load_low = 0 + /// current draw in W + var/default_power_bus_load_high = 0 + /// usable power in W + var/default_power_bus_avail_low = 0 + /// usable power in W + var/default_power_bus_avail_high = 0 + /// registered power draws + var/list/default_power_bus_low + /// registered power draws + var/list/default_power_bus_high + + +/datum/item_interface/Destroy() + unmount_everything() + return ..() + +/datum/item_interface/proc/mount_item(obj/item/item) + SHOULD_CALL_PARENT(TRUE) + item.interface_attached(src) + return TRUE + +/datum/item_interface/proc/unmount_item(obj/item/item) + SHOULD_CALL_PARENT(TRUE) + item.interface_detached(src) + if(!isnull(default_power_bus_low?[item])) + default_power_bus_load_low -= default_power_bus_low[item] + default_power_bus_low -= item + if(!isnull(default_power_bus_high?[item])) + default_power_bus_load_high -= default_power_bus_high[item] + default_power_bus_high -= item + return TRUE + +/datum/item_interface/proc/is_mounted(obj/item/item) + return FALSE + +/datum/item_interface/proc/unmount_everything() + return TRUE + +//* Reagent Bus *// + +/datum/item_interface/proc/use_reagent_exact_gradual(obj/item/item, datum/reagent/reagentlike, amount, dt) + return FALSE + +/datum/item_interface/proc/use_reagent_exact_immediate(obj/item/item, datum/reagent/reagentlike, amount) + return FALSE + +//* Material Bus *// + +/datum/item_interface/proc/use_material_exact_gradual(obj/item/item, datum/material/materiallike, amount, dt) + return FALSE + +/datum/item_interface/proc/use_material_exact_immediate(obj/item/item, datum/material/materiallike, amount) + return FALSE + +//* Power Bus *// + +/datum/item_interface/proc/use_low_power(obj/item/item, joules) + return FALSE + +/datum/item_interface/proc/use_high_power(obj/item/item, joules) + return FALSE + +/** + * register a gradual power usage + * + * register as 0 watts to unregister + */ +/datum/item_interface/proc/set_low_power(obj/item/item, watts) + var/diff = watts - default_power_bus_low?[item] + if(!watts) + LAZYREMOVE(default_power_bus_low, item) + else + LAZYSET(default_power_bus_low, item, watts) + default_power_bus_load_low += diff + +/** + * check how much power is available from registered gradual power + */ +/datum/item_interface/proc/get_low_power(obj/item/item) + return default_power_bus_low?[item] * min(1, default_power_bus_avail_low / default_power_bus_load_low) + +/** + * check ratio of power load across any / all items that's met + * + * @return 0 to 1 inclusive + */ +/datum/item_interface/proc/check_low_power() + return min(1, default_power_bus_avail_low / default_power_bus_load_low) + +/** + * register a gradual power usage + * + * register as 0 watts to unregister + */ +/datum/item_interface/proc/set_high_power(obj/item/item, watts) + var/diff = watts - default_power_bus_high?[item] + if(!watts) + LAZYREMOVE(default_power_bus_high, item) + else + LAZYSET(default_power_bus_high, item, watts) + default_power_bus_load_high += diff + +/** + * check how much power is available from registered gradual power + */ +/datum/item_interface/proc/get_high_power(obj/item/item) + return default_power_bus_high?[item] * min(1, default_power_bus_avail_high / default_power_bus_load_high) + +/** + * check ratio of power load across any / all items that's met + * + * @return 0 to 1 inclusive + */ +/datum/item_interface/proc/check_high_power() + return min(1, default_power_bus_avail_high / default_power_bus_load_high) + +//* UI *// + +/** + * A hook used to determine if a user should be able to physically access an UI anchored / hosted on a specific item. + * + * Item-side must implement/use this. + * + * @return non-null to override if we can reach an item. + */ +/datum/item_interface/proc/ui_override_reachability(obj/item/item, mob/user) + #warn tgui needs a way to hook + return null + +//* Miscellaneous / Specific Items *// + +/** + * @return amount used + */ +/datum/item_interface/proc/use_medichines(datum/medichine_cell/requested, amount) + return 0 + +/** + * @return /datum/medichine_cell instance + */ +/datum/item_interface/proc/query_medichines() + return null + +//? Implementations ?// + +/** + * one-interface, one-item + * + * used when we need item-specific context + * + * * Only use this if shared can't work for you. + */ +/datum/item_interface/single + /// bound item + var/obj/item/bound_item + +/datum/item_interface/single/mount_item(obj/item/item) + if(bound_item == item) + return TRUE + if(!isnull(bound_item)) + unmount_item(bound_item) + bound_item = item + return ..() + +/datum/item_interface/single/unmount_item(obj/item/item) + if(bound_item == item) + bound_item = null + else + . = FALSE + CRASH("what?") + return ..() + +/datum/item_interface/single/is_mounted(obj/item/item) + return bound_item == item + +/datum/item_interface/single/unmount_everything() + unmount_item(bound_item) + return ..() + +/** + * one-interface, many-item + * + * used when we don't need item-specific context + * + * * You should probalby use this in most cases. + */ +/datum/item_interface/shared + /// bound items + var/list/obj/item/bound_items + +/datum/item_interface/shared/mount_item(obj/item/item) + if(item in bound_items) + return TRUE + LAZYADD(bound_items, item) + return ..() + +/datum/item_interface/shared/unmount_item(obj/item/item) + if(!(item in bound_items)) + . = FALSE + CRASH("what?") + LAZYREMOVE(bound_items, item) + return ..() + +/datum/item_interface/shared/is_mounted(obj/item/item) + return item in bound_items + +/datum/item_interface/shared/unmount_everything() + for(var/obj/item/item as anything in bound_items) + unmount_item(item) + return ..() + +/** + * called when we're attached to an interface + */ +/obj/item/proc/interface_attached(datum/item_interface/interface) + return + +/** + * called when we're detached from an interface + */ +/obj/item/proc/interface_detached(datum/item_interface/interface) + return diff --git a/code/datums/mocking/client.dm b/code/datums/mocking/client.dm index cb7d37ff967e..7c83f1f0e2c3 100644 --- a/code/datums/mocking/client.dm +++ b/code/datums/mocking/client.dm @@ -1,5 +1,5 @@ /// This should match the interface of /client wherever necessary. -/datum/client_interface +/datum/mocking/client /// Player preferences datum for the client var/datum/preferences/prefs diff --git a/code/datums/mocking/mocking.dm b/code/datums/mocking/mocking.dm new file mode 100644 index 000000000000..33034104fe13 --- /dev/null +++ b/code/datums/mocking/mocking.dm @@ -0,0 +1,4 @@ +/** + * fake datums that match another things' interface, used for .. purposes..? + */ +/datum/mocking diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm index ee4a71eee143..08250af92e46 100644 --- a/code/datums/progressbar.dm +++ b/code/datums/progressbar.dm @@ -17,7 +17,7 @@ EXCEPTION("Invalid target given") if (goal_number) goal = goal_number - bar = image('icons/effects/progessbar.dmi', target, "prog_bar_0") + bar = image('icons/effects/progressbar.dmi', target, "prog_bar_0") bar.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA bar.pixel_y = 32 bar.plane = HUD_PLANE diff --git a/code/game/atoms/movable/movable.dm b/code/game/atoms/movable/movable.dm index a489d59d2e6c..e6ba0af6259d 100644 --- a/code/game/atoms/movable/movable.dm +++ b/code/game/atoms/movable/movable.dm @@ -493,13 +493,12 @@ em_render?.layer = MANGLE_PLANE_AND_LAYER(plane, layer) /atom/movable/proc/add_emissive_blocker(full_copy = TRUE) + if(full_copy) + ensure_render_target() if(em_block) em_block.render_source = full_copy? render_target : null update_emissive_blocker() return - if(render_target && render_target != REF(src)) - CRASH("already had render target; refusing to overwrite.") - render_target = REF(src) em_block = new(src, full_copy? render_target : null) vis_contents += em_block update_emissive_blocker() @@ -518,13 +517,12 @@ em_block = null /atom/movable/proc/add_emissive_render(full_copy = TRUE) + if(full_copy) + ensure_render_target() if(em_render) em_render.render_source = full_copy? render_target : null update_emissive_render() return - if(render_target && render_target != REF(src)) - CRASH("already had render target; refusing to overwrite.") - render_target = REF(src) em_render = new(src, full_copy? render_target : null) vis_contents += em_render update_emissive_render() @@ -628,3 +626,13 @@ else if(C) color = C return + +//* Rendering *// + +/** + * for the love of god don't call this unnecessarily this fucks people's GPUs up if spammed + */ +/atom/movable/proc/ensure_render_target(make_us_invisible) + if(!isnull(render_target)) + return + render_target = "[make_us_invisible? "*":""][REF(src)]-[rand(1,1000)]-[world.time]" diff --git a/code/game/atoms/movable/movement.dm b/code/game/atoms/movable/movement.dm index f12fb2c6a239..9d35cca4ca64 100644 --- a/code/game/atoms/movable/movement.dm +++ b/code/game/atoms/movable/movement.dm @@ -394,7 +394,7 @@ * Do not do anything that will re-move the atom, or bad things happen. * Use spawn(0) to yield behavior until after the movement call stack is done if you want to do that. */ -/atom/movable/Crossed(atom/movable/AM, oldloc) +/atom/movable/Crossed(atom/movable/AM) SHOULD_CALL_PARENT(TRUE) . = ..() SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM) diff --git a/code/game/atoms/movable/special/render.dm b/code/game/atoms/movable/special/graphics_render.dm similarity index 51% rename from code/game/atoms/movable/special/render.dm rename to code/game/atoms/movable/special/graphics_render.dm index 7e5cc7d9a46a..1c37cc4dd303 100644 --- a/code/game/atoms/movable/special/render.dm +++ b/code/game/atoms/movable/special/graphics_render.dm @@ -1,5 +1,8 @@ /** * holds render effects / otherwise just acts like it isn't there other than its visual impacts */ -/atom/movable/render +/atom/movable/graphics_render + appearance_flags = KEEP_TOGETHER atom_flags = ATOM_ABSTRACT + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + vis_flags = NONE diff --git a/code/game/atoms/movable/special/overlay.dm b/code/game/atoms/movable/special/overlay.dm index 56889c02b356..9ec7bf0d6cc2 100644 --- a/code/game/atoms/movable/special/overlay.dm +++ b/code/game/atoms/movable/special/overlay.dm @@ -1,3 +1,4 @@ +// todo: these are legacy objects, should we get rid of them? /atom/movable/overlay atom_flags = ATOM_ABSTRACT vis_flags = VIS_INHERIT_ID diff --git a/code/game/atoms/movable/special/particle_render.dm b/code/game/atoms/movable/special/particle_render.dm new file mode 100644 index 000000000000..95ce07742c2b --- /dev/null +++ b/code/game/atoms/movable/special/particle_render.dm @@ -0,0 +1,8 @@ +/** + * holds render effects / otherwise just acts like it isn't there other than its visual impacts + */ +/atom/movable/particle_render + appearance_flags = KEEP_TOGETHER + atom_flags = ATOM_ABSTRACT + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + vis_flags = NONE diff --git a/code/game/click/items.dm b/code/game/click/items.dm index 368585390948..bccced747bea 100644 --- a/code/game/click/items.dm +++ b/code/game/click/items.dm @@ -42,6 +42,69 @@ // todo: signal for afterattack here return . | afterattack(target, user, clickchain_flags, params) +// todo: +//* tools +// /obj/item/proc/tool_attack_chain(atom/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click) +// /atom/proc/tool_interaction(obj/item/tool, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click) +// /atom/proc/tool_act(tool_function, ...) --> route to [function]_act +// /atom/proc/[function]_act(...) + +//* specific item interactions +// /obj/item/proc/item_attack_chain(atom/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click) +// /obj/item/proc/item_attack(atom/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click) +// /atom/proc/item_interaction(obj/item/item, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click) +// /atom/proc/item_act(obj/item/item, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click) + +//* melee attack +// /obj/item/proc/melee_attack_chain(atom/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click) +// /obj/item/proc/melee_attack(atom/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click, damage_multiplier = 1) +// /obj/item/proc/melee_(mob|obj)_hit((mob|obj)/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click, damage_multiplier = 1, target_zone, intent) +// /obj/item/proc/melee_(mob|obj)_miss((mob|obj)/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click, damage_multiplier = 1, target_zone, intent) +// /obj/item/proc/melee_(mob|obj)_finalize((mob|obj)/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click, damage_multiplier = 1, target_zone, intent, successful) +// /(obj|mob)/proc/melee_act(obj/item/weapon, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click, damage_multiplier = 1) + +//* misc +// /obj/item/proc/afterattack(atom/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click) <-- catch-all for 'did nothing so far, now what?' + +//* total +// proc chains as follows; +// +// anything returning CLICKCHAIN_DO_NOT_PROPAGATE will signal 'we did something, stop now' +// and terminate the chain +// +// mob/obj are intentionally separate from melee attacks, because many things +// like stunbatons would simply bludgeon an obj while having special effects on mobs! +// +// item/melee_interaction_chain: +// - item/tool_attack_chain +// - atom/tool_interaction +// - atom/tool_act +// - atom/[function]_act +// - item/item_attack_chain +// - item/item_attack +// - atom/item_interaction +// - atom/item_act +// - item/melee_attack_chain +// - item/melee_attack +// - item/melee_hit +// - atom/melee_act +// OR +// - item/melee_miss +// FINALLY +// - item/melee_finalize +// - item/afterattack +// +// ranged_interaction_chain: +// - item/afterattack + +//* iteration 2 melee +// /obj/item/proc/melee_attack_chain(atom/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click) +// /obj/item/proc/melee_attack(atom/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click, damage_multiplier, target_zone, intent) +// /obj/item/proc/melee_hit(atom/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click, damage_multiplier, target_zone, intent) +// /obj/item/proc/melee_miss(atom/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click, damage_multiplier, target_zone, intent) +// /obj/item/proc/melee_after(atom/entity, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click, damage_multiplier, target_zone, intent, successful) +// /atom/proc/melee_act(obj/item/weapon, datum/event_args/actor/actor, datum/event_args/actor/clickchain/from_click, damage_multiplier, target_zone, intent, successful) + /** * Called when trying to click something that the user can't Reachability() to. * diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index b574db389871..d63af25059cd 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -241,8 +241,8 @@ if(occupant.getToxLoss()) occupant.adjustToxLoss(max(-1, -20/occupant.getToxLoss())) var/heal_brute = occupant.getBruteLoss() ? min(1, 20/occupant.getBruteLoss()) : 0 - var/heal_fire = occupant.getFireLoss() ? min(1, 20/occupant.getFireLoss()) : 0 - occupant.heal_organ_damage(heal_brute,heal_fire) + var/heal_burn = occupant.getFireLoss() ? min(1, 20/occupant.getFireLoss()) : 0 + occupant.heal_organ_damage(heal_brute,heal_burn) var/has_cryo = occupant.reagents.get_reagent_amount("cryoxadone") >= 1 var/has_clonexa = occupant.reagents.get_reagent_amount("clonexadone") >= 1 var/has_cryo_medicine = has_cryo || has_clonexa diff --git a/code/game/machinery/doors/airlock/airlock.dm b/code/game/machinery/doors/airlock/airlock.dm index f587a639b8a7..b932122fb093 100644 --- a/code/game/machinery/doors/airlock/airlock.dm +++ b/code/game/machinery/doors/airlock/airlock.dm @@ -117,6 +117,77 @@ GLOBAL_REAL_VAR(airlock_typecache) = typecacheof(list( var/tinted var/id_tint +/obj/machinery/door/airlock/Initialize(mapload, obj/structure/door_assembly/assembly) + //if assembly is given, create the new door from the assembly + if (assembly && istype(assembly)) + assembly_type = assembly.type + + electronics = assembly.electronics + electronics.loc = src + + //update the door's access to match the electronics' + secured_wires = electronics.secure + req_one_access = electronics.conf_req_one_access?.Copy() + req_access = electronics.conf_req_access?.Copy() + + //get the name from the assembly + if(assembly.created_name) + name = assembly.created_name + else + name = "[istext(assembly.glass) ? "[assembly.glass] airlock" : assembly.base_name]" + + //get the dir from the assembly + setDir(assembly.dir) + + //wires + var/turf/T = get_turf(loc) + if(T && (T.z in (LEGACY_MAP_DATUM).admin_levels)) + secured_wires = 1 + if (secured_wires) + wires = new/datum/wires/airlock/secure(src) + else + wires = new/datum/wires/airlock(src) + + if(src.closeOtherId != null) + for (var/obj/machinery/door/airlock/A in GLOB.machines) + if(A.closeOtherId == src.closeOtherId && A != src) + src.closeOther = A + break + name = "\improper [name]" + if(autoset_dir) + for (var/cardinal in GLOB.cardinal) //No list copy please good sir + var/turf/step_turf = get_step(src, cardinal) + for(var/atom/thing as anything in step_turf) + if(thing.type in airlock_typecache) + switch(cardinal) + if(EAST) + setDir(SOUTH) + if(WEST) + setDir(SOUTH) + if(NORTH) + setDir(WEST) + if(SOUTH) + setDir(WEST) + break + if (step_turf.density == TRUE) + switch(cardinal) + if(EAST) + setDir(SOUTH) + if(WEST) + setDir(SOUTH) + if(NORTH) + setDir(WEST) + if(SOUTH) + setDir(WEST) + break + update_airlock_icon(AIRLOCK_CLOSED) + return ..() + +/obj/machinery/door/airlock/Destroy() + QDEL_NULL(wires) + QDEL_NULL(electronics) + return ..() + /obj/machinery/door/airlock/proc/set_airlock_overlays(state) var/icon/color_overlay var/icon/filling_overlay @@ -225,7 +296,7 @@ GLOBAL_REAL_VAR(airlock_typecache) = typecacheof(list( if(do_after(user,10 SECONDS,src)) src.locked = 0 src.welded = 0 - update_icon() + update_airlock_icon() open(1) if(prob(25)) src.shock(user, 100) @@ -254,7 +325,7 @@ GLOBAL_REAL_VAR(airlock_typecache) = typecacheof(list( playsound(src.loc, 'sound/machines/airlock_creaking.ogg', 100, 1) src.locked = 0 src.welded = 0 - update_icon() + update_airlock_icon() open(1) src.emag_act() else if(src.density) @@ -398,7 +469,7 @@ About the new airlock wires panel: if(electrified_until && isAllPowerLoss()) electrify(0) - update_icon() + update_airlock_icon() /obj/machinery/door/airlock/proc/loseBackupPower() backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) @@ -410,7 +481,7 @@ About the new airlock wires panel: if(electrified_until && isAllPowerLoss()) electrify(0) - update_icon() + update_airlock_icon() /obj/machinery/door/airlock/proc/regainMainPower() if(!mainPowerCablesCut()) @@ -419,14 +490,14 @@ About the new airlock wires panel: if(!backup_power_lost_until) backup_power_lost_until = -1 - update_icon() + update_airlock_icon() /obj/machinery/door/airlock/proc/regainBackupPower() if(!backupPowerCablesCut()) // Restore backup power only if main power is offline, otherwise permanently disable backup_power_lost_until = main_power_lost_until == 0 ? -1 : 0 - update_icon() + update_airlock_icon() /obj/machinery/door/airlock/proc/electrify(var/duration, var/feedback = 0) var/message = "" @@ -497,8 +568,7 @@ About the new airlock wires panel: else return 0 - -/obj/machinery/door/airlock/update_icon(var/doorstate) +/obj/machinery/door/airlock/proc/update_airlock_icon(var/doorstate) switch(doorstate) if(AIRLOCK_OPEN) icon_state = "open" @@ -507,8 +577,15 @@ About the new airlock wires panel: if(AIRLOCK_OPENING, AIRLOCK_CLOSING, AIRLOCK_EMAG, AIRLOCK_DENY) icon_state = "" + update_icon() + +/obj/machinery/door/airlock/update_icon(updates) + . = ..() set_airlock_overlays(state) +/obj/machinery/door/airlock/snowflake_please_refactor_me_later_update_icon_hook() + return // sigh. + /obj/machinery/door/airlock/custom_smooth() return //we only custom smooth because we don't need to do anything else. @@ -517,24 +594,24 @@ About the new airlock wires panel: if(DOOR_ANIMATION_OPEN) set_airlock_overlays(AIRLOCK_OPENING) flick("opening", src)//[stat ? "_stat":] - update_icon(AIRLOCK_OPEN) + update_airlock_icon(AIRLOCK_OPEN) if(DOOR_ANIMATION_CLOSE) set_airlock_overlays(AIRLOCK_CLOSING) flick("closing", src) - update_icon(AIRLOCK_CLOSED) + update_airlock_icon(AIRLOCK_CLOSED) if(DOOR_ANIMATION_DENY) set_airlock_overlays(AIRLOCK_DENY) if(density && arePowerSystemsOn()) flick("deny", src) if(speaker) playsound(loc, denied_sound, 50, 0) - update_icon(AIRLOCK_CLOSED) + update_airlock_icon(AIRLOCK_CLOSED) if(DOOR_ANIMATION_EMAG) set_airlock_overlays(AIRLOCK_EMAG) if(density && arePowerSystemsOn()) flick("deny", src) else - update_icon() + update_airlock_icon() /obj/machinery/door/airlock/attack_ai(mob/user as mob) ui_interact(user) @@ -674,14 +751,14 @@ About the new airlock wires panel: if("disrupt-main") if(!main_power_lost_until) loseMainPower() - update_icon() + update_airlock_icon() else to_chat(usr, SPAN_WARNING("Main power is already offline.")) . = TRUE if("disrupt-backup") if(!backup_power_lost_until) loseBackupPower() - update_icon() + update_airlock_icon() else to_chat(usr, SPAN_WARNING("Backup power is already offline.")) . = TRUE @@ -708,7 +785,7 @@ About the new airlock wires panel: to_chat(usr, "The bolt lights wire is cut - The door bolt lights are permanently disabled.") return lights = !lights - update_icon() + update_airlock_icon() . = TRUE if("safe-toggle") set_safeties(!safe, 1) @@ -723,7 +800,7 @@ About the new airlock wires panel: user_toggle_open(usr) . = TRUE - update_icon() + update_airlock_icon() return TRUE /obj/machinery/door/airlock/proc/user_allowed(mob/user) @@ -787,7 +864,7 @@ About the new airlock wires panel: else src.welded = null playsound(src.loc, C.tool_sound, 75, 1) - src.update_icon() + src.update_airlock_icon() return else return @@ -801,7 +878,7 @@ About the new airlock wires panel: else src.panel_open = 1 playsound(src, C.tool_sound, 50, 1) - src.update_icon() + src.update_airlock_icon() else if(C.is_wirecutter()) return src.attack_hand(user) else if(istype(C, /obj/item/multitool)) @@ -1007,7 +1084,7 @@ About the new airlock wires panel: playsound(src, bolt_down_sound, 30, 0, 3) for(var/mob/M in range(1,src)) M.show_message("You hear a click from the bottom of the door.", 2) - update_icon() + update_airlock_icon() return 1 /obj/machinery/door/airlock/proc/unlock(var/forced=0) @@ -1021,7 +1098,7 @@ About the new airlock wires panel: playsound(src, bolt_up_sound, 30, 0, 3) for(var/mob/M in range(1,src)) M.show_message("You hear a click from the bottom of the door.", 2) - update_icon() + update_airlock_icon() return 1 /obj/machinery/door/airlock/allowed(mob/M) @@ -1029,82 +1106,6 @@ About the new airlock wires panel: return 0 return ..(M) -/obj/machinery/door/airlock/can_pathfinding_enter(atom/movable/actor, dir, datum/pathfinding/search) - return ..() || (has_access(req_access, req_one_access, search.ss13_with_access) && !locked && !inoperable()) - -/obj/machinery/door/airlock/can_pathfinding_pass(atom/movable/actor, datum/pathfinding/search) - return ..() || (has_access(req_access, req_one_access, search.ss13_with_access) && !locked && !inoperable()) - -/obj/machinery/door/airlock/Initialize(mapload, obj/structure/door_assembly/assembly) - //if assembly is given, create the new door from the assembly - if (assembly && istype(assembly)) - assembly_type = assembly.type - - electronics = assembly.electronics - electronics.loc = src - - //update the door's access to match the electronics' - secured_wires = electronics.secure - req_one_access = electronics.conf_req_one_access?.Copy() - req_access = electronics.conf_req_access?.Copy() - - //get the name from the assembly - if(assembly.created_name) - name = assembly.created_name - else - name = "[istext(assembly.glass) ? "[assembly.glass] airlock" : assembly.base_name]" - - //get the dir from the assembly - setDir(assembly.dir) - - //wires - var/turf/T = get_turf(loc) - if(T && (T.z in (LEGACY_MAP_DATUM).admin_levels)) - secured_wires = 1 - if (secured_wires) - wires = new/datum/wires/airlock/secure(src) - else - wires = new/datum/wires/airlock(src) - - if(src.closeOtherId != null) - for (var/obj/machinery/door/airlock/A in GLOB.machines) - if(A.closeOtherId == src.closeOtherId && A != src) - src.closeOther = A - break - name = "\improper [name]" - if(autoset_dir) - for (var/cardinal in GLOB.cardinal) //No list copy please good sir - var/turf/step_turf = get_step(src, cardinal) - for(var/atom/thing as anything in step_turf) - if(thing.type in airlock_typecache) - switch(cardinal) - if(EAST) - setDir(SOUTH) - if(WEST) - setDir(SOUTH) - if(NORTH) - setDir(WEST) - if(SOUTH) - setDir(WEST) - break - if (step_turf.density == TRUE) - switch(cardinal) - if(EAST) - setDir(SOUTH) - if(WEST) - setDir(SOUTH) - if(NORTH) - setDir(WEST) - if(SOUTH) - setDir(WEST) - break - update_icon(AIRLOCK_CLOSED) - . = ..() - -/obj/machinery/door/airlock/Destroy() - qdel(wires) - wires = null - return ..() // Most doors will never be deconstructed over the course of a round, // so as an optimization defer the creation of electronics until @@ -1170,7 +1171,7 @@ About the new airlock wires panel: // If we lost power, disable electrification // Keeping door lights on, runs on internal battery or something. electrified_until = 0 - update_icon() + update_airlock_icon() /obj/machinery/door/airlock/proc/prison_open() if(arePowerSystemsOn()) @@ -1213,5 +1214,10 @@ About the new airlock wires panel: return TRUE return FALSE -/obj/machinery/door/airlock/glass_external/public - req_one_access = list() +//* Pathfinding *// + +/obj/machinery/door/airlock/can_pathfinding_enter(atom/movable/actor, dir, datum/pathfinding/search) + return ..() || (has_access(req_access, req_one_access, search.ss13_with_access) && !locked && !inoperable()) + +/obj/machinery/door/airlock/can_pathfinding_pass(atom/movable/actor, datum/pathfinding/search) + return ..() || (has_access(req_access, req_one_access, search.ss13_with_access) && !locked && !inoperable()) diff --git a/code/game/machinery/doors/airlock/airlock_subtypes.dm b/code/game/machinery/doors/airlock/airlock_subtypes.dm index 4c7756360015..b47633ccf132 100644 --- a/code/game/machinery/doors/airlock/airlock_subtypes.dm +++ b/code/game/machinery/doors/airlock/airlock_subtypes.dm @@ -135,6 +135,9 @@ door_color = COLOR_MAROON window_color = GLASS_COLOR +/obj/machinery/door/airlock/glass_external/public + req_one_access = list() + /obj/machinery/door/airlock/glass name = "Glass Airlock" open_sound_powered = 'sound/machines/door/hall1o.ogg' diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 645acadfb23c..3732a1592112 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -299,7 +299,12 @@ open() ..() -/obj/machinery/door/update_icon() +/obj/machinery/door/update_icon(updates) + snowflake_please_refactor_me_later_update_icon_hook() + return ..() + +// todo: FUCK. +/obj/machinery/door/proc/snowflake_please_refactor_me_later_update_icon_hook() if(density) icon_state = "door1" else diff --git a/code/game/mecha/equipment/tools/orescanner.dm b/code/game/mecha/equipment/tools/orescanner.dm index 71f91d224034..927b46127dc0 100644 --- a/code/game/mecha/equipment/tools/orescanner.dm +++ b/code/game/mecha/equipment/tools/orescanner.dm @@ -28,7 +28,7 @@ if(!enable_special) target = get_turf(chassis) - var/datum/beam/ScanBeam = chassis.Beam(target, "g_beam", 'icons/effects/beam.dmi', 2 SECONDS, 10, /obj/effect/ebeam, 2) + var/datum/beam_legacy/ScanBeam = chassis.Beam(target, "g_beam", 'icons/effects/beam.dmi', 2 SECONDS, 10, /obj/effect/ebeam, 2) if(do_after(chassis.occupant, 2 SECONDS)) my_scanner.ScanTurf(target, chassis.occupant, exact_scan) diff --git a/code/game/mecha/equipment/tools/syringe_gun.dm b/code/game/mecha/equipment/tools/syringe_gun.dm index f320235f3ac3..7f2a73976ba5 100644 --- a/code/game/mecha/equipment/tools/syringe_gun.dm +++ b/code/game/mecha/equipment/tools/syringe_gun.dm @@ -311,7 +311,7 @@ var/bone_heal = 0 // Percent chance it will heal a broken bone. this does not mean 'make it not instantly re-break'. var/mob/living/Target = null - var/datum/beam/MyBeam = null + var/datum/beam_legacy/MyBeam = null equip_type = EQUIP_HULL diff --git a/code/game/mecha/equipment/tools/weldinglaser.dm b/code/game/mecha/equipment/tools/weldinglaser.dm index bd30119657ee..471e62ea2505 100644 --- a/code/game/mecha/equipment/tools/weldinglaser.dm +++ b/code/game/mecha/equipment/tools/weldinglaser.dm @@ -15,7 +15,7 @@ /obj/item/mecha_parts/mecha_equipment/tool/powertool/welding/action(var/atom/target) ..() - var/datum/beam/weld_beam = null + var/datum/beam_legacy/weld_beam = null if(is_ranged()) var/atom/movable/beam_origin = chassis weld_beam = beam_origin.Beam(target, icon_state = "solar_beam", time = 0.3 SECONDS) diff --git a/code/game/objects/effects/chem/water.dm b/code/game/objects/effects/chem/water.dm index b5f941c096c8..2039dd91ef1d 100644 --- a/code/game/objects/effects/chem/water.dm +++ b/code/game/objects/effects/chem/water.dm @@ -36,7 +36,7 @@ reagents.touch(AM, reagents.total_volume) sleep(delay) -/obj/effect/water/Crossed(atom/movable/AM, oldloc) +/obj/effect/water/Crossed(atom/movable/AM) . = ..() if(!isobj(AM) && !ismob(AM)) return diff --git a/code/game/objects/effects/map_effects/beam_point.dm b/code/game/objects/effects/map_effects/beam_point.dm index d13396477552..38f570508e5f 100644 --- a/code/game/objects/effects/map_effects/beam_point.dm +++ b/code/game/objects/effects/map_effects/beam_point.dm @@ -74,14 +74,14 @@ GLOBAL_LIST_EMPTY(all_beam_points) // Used to make sure two points don't have more than one beam. /obj/effect/map_effect/beam_point/proc/has_active_beam(var/obj/effect/map_effect/beam_point/them) // First, check our beams. - for(var/datum/beam/B in my_beams) + for(var/datum/beam_legacy/B in my_beams) if(B.target == them) return TRUE if(B.origin == them) // This shouldn't be needed unless the beam gets built backwards but why not. return TRUE // Now check theirs, to see if they have a beam on us. - for(var/datum/beam/B in them.my_beams) + for(var/datum/beam_legacy/B in them.my_beams) if(B.target == src) return TRUE if(B.origin == src) // Same story as above. @@ -94,14 +94,14 @@ GLOBAL_LIST_EMPTY(all_beam_points) log_debug(SPAN_DEBUG("[src] ([src.type] \[[x],[y],[z]\])[ADMIN_JMP(src)] failed to build its beam due to not having a target.")) return FALSE - var/datum/beam/new_beam = Beam(beam_target, beam_icon_state, beam_icon, beam_time, beam_max_distance, beam_type, beam_sleep_time) + var/datum/beam_legacy/new_beam = Beam(beam_target, beam_icon_state, beam_icon, beam_time, beam_max_distance, beam_type, beam_sleep_time) my_beams += new_beam if(beam_creation_sound) playsound(src, beam_creation_sound, 70, 1) return TRUE -/obj/effect/map_effect/beam_point/proc/destroy_beam(var/datum/beam/B) +/obj/effect/map_effect/beam_point/proc/destroy_beam(var/datum/beam_legacy/B) if(!B) log_debug(SPAN_DEBUG("[src] ([src.type] \[[x],[y],[z]\])[ADMIN_JMP(src)] was asked to destroy a beam that does not exist.")) return FALSE @@ -118,7 +118,7 @@ GLOBAL_LIST_EMPTY(all_beam_points) return TRUE /obj/effect/map_effect/beam_point/proc/destroy_all_beams() - for(var/datum/beam/B in my_beams) + for(var/datum/beam_legacy/B in my_beams) destroy_beam(B) return TRUE diff --git a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm index 482e714b3984..a47d99f188fe 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm @@ -1,12 +1,12 @@ -/datum/beam_components_cache +/datum/beam_legacy_components_cache var/list/beam_components = list() -/datum/beam_components_cache/Destroy() +/datum/beam_legacy_components_cache/Destroy() for(var/component in beam_components) qdel(component) return ..() -/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, datum/beam_components_cache/beam_components, beam_type, color, qdel_in = 5, light_range = 2, light_color_override, light_intensity = 1, instance_key) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported! +/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, datum/beam_legacy_components_cache/beam_components, beam_type, color, qdel_in = 5, light_range = 2, light_color_override, light_intensity = 1, instance_key) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported! if(!istype(starting) || !istype(ending) || !ispath(beam_type)) return var/datum/point/midpoint = point_midpoint_points(starting, ending) diff --git a/code/datums/components/crafting/guncrafting.dm b/code/game/objects/items/guncrafting.dm similarity index 100% rename from code/datums/components/crafting/guncrafting.dm rename to code/game/objects/items/guncrafting.dm diff --git a/code/game/objects/items/inducer.dm b/code/game/objects/items/inducer.dm index 67b653310715..65d5393d0253 100644 --- a/code/game/objects/items/inducer.dm +++ b/code/game/objects/items/inducer.dm @@ -119,7 +119,7 @@ user.visible_message(SPAN_NOTICE("[user] starts recharging [A] with [src]."), SPAN_NOTICE("You start recharging [A] with [src].")) A.add_filter("inducer_outline", 1, outline_filter(1, "#22aaFF")) - var/datum/beam/charge_beam = user.Beam(A, icon_state = "rped_upgrade", time = 20 SECONDS) + var/datum/beam_legacy/charge_beam = user.Beam(A, icon_state = "rped_upgrade", time = 20 SECONDS) var/datum/effect_system/spark_spread/spark_system = new spark_system.set_up(5, 0, get_turf(A)) spark_system.attach(A) diff --git a/code/game/objects/items/polyfill_cartridge.dm b/code/game/objects/items/polyfill_cartridge.dm new file mode 100644 index 000000000000..9f1a68e9ee2c --- /dev/null +++ b/code/game/objects/items/polyfill_cartridge.dm @@ -0,0 +1,46 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * material cartridges for new RCDs + */ +/obj/item/polyfill_cartridge + name = "polyfill cartridge" + desc = "A cartridge filled with a predetermined mixture of construction polyfill. Used by holofabricators \ + and other fabrication machinery." + icon = 'icons/items/stream_projector/holofabricator.dmi' + icon_state = "cartridge" + w_class = WEIGHT_CLASS_NORMAL + + /// polyfill material type + var/polyfill_type = /datum/material/polyfill + /// maximum capacity + var/maximum_capacity = SHEET_MATERIAL_AMOUNT * 100 + /// current capacity + var/stored_capacity = 0 + +/obj/item/polyfill_cartridge/get_materials(respect_multiplier) + . = ..() + var/datum/material/resolved_polyfill = SSmaterials.resolve_material(polyfill_type) + /// only separate for weak alloys + if(!isnull(resolved_polyfill.weak_alloy)) + for(var/key in resolved_polyfill.weak_alloy) + .[key] += stored_capacity * resolved_polyfill.weak_alloy[key] + else + .[resolved_polyfill.id] += stored_capacity + +/obj/item/polyfill_cartridge/proc/use(amount) + . = min(amount, stored_capacity) + if(!.) + return + stored_capacity -= amount + +/obj/item/polyfill_cartridge/proc/give(amount) + . = min(amount, maximum_capacity - stored_capacity) + stored_capacity += . + +/** + * @return 0 to 1 inclusive of how full we are + */ +/obj/item/polyfill_cartridge/proc/get_ratio() + return clamp(stored_capacity / maximum_capacity, 0, 1) diff --git a/code/game/objects/items/stream_projector/holofabricator.dm b/code/game/objects/items/stream_projector/holofabricator.dm new file mode 100644 index 000000000000..5856dfd34853 --- /dev/null +++ b/code/game/objects/items/stream_projector/holofabricator.dm @@ -0,0 +1,556 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +//? initialized by SSearly_init due to icon ops +// maybe a serializable/persistable repository someday idfk +GLOBAL_LIST_EMPTY(holofabricator_templates) + +/proc/init_holofabricator_templates() + var/list/built = list() + GLOB.holofabricator_templates = built + for(var/datum/holofabricator_template/path as anything in subtypesof(/datum/holofabricator_template)) + if(initial(path.abstract_type) == path) + continue + var/datum/holofabricator_template/creating = new path + creating.initialize() + built += creating + tim_sort(built, /proc/cmp_holofabricator_templates) + +#define HOLOFABRICATOR_MODE_CONSTRUCT 1 +#define HOLOFABRICATOR_MODE_REPAIR 2 +#define HOLOFABRICATOR_MODE_DECONSTRUCT 3 + +//? Projector + +/** + * new RCDs + */ +ITEM_AUTO_BINDS_SINGLE_INTERFACE_TO_VAR(/obj/item/stream_projector/holofabricator, interface) +/obj/item/stream_projector/holofabricator + name = "holofabricator" + desc = "A precise triage tool used by many frontier engineers. Uses materials from a loaded cartridge \ + to rapidly fabricate a generated holotemplate." + icon = 'icons/items/stream_projector/holofabricator.dmi' + icon_state = "projector" + + // todo: proper cataloguing fluff desc system + description_fluff = "Despite having been around for hundreds of years, holofabricators are still a novel, alpha-stage concept \ + being iterated upon by many scientists across the galaxy. While not as reliable as traditional methods of construction, they nonetheless \ + make for a coveted item on many installations due to the ease of which they can perform emergency triage and the rapid prototyping of rooms. \ +
A holofabricator constructs a prefab by generating a hardlight template with its guide projector, then filling it \ + in with confined particular beams. Unfortunately, the resulting fabrication tends to be noticeably less weaker than \ + a conventional construction of the same design - it seems science has yet to nullify one of the core weaknesses of \ + 3d-printing technologies. \ +
Deconstruction is performed instead by abrasively blasting a target with a particulate beam. Only some materials and \ + designs are weak enough to be sliced apart this way; deconstructed matter can normally be recycled or disposed of as an installation \ + sees fit." + + #warn impl + + /// inserted matter cartridge + var/obj/item/polyfill_cartridge/inserted_cartridge + /// selected template + var/datum/holofabricator_template/selected_template + /// k-v part-to-material choices for current template + var/list/selected_materials + /// selected pattern for current template; null for default + var/selected_pattern + /// k-v options list selected for current template + var/list/selected_options + /// chosen access ids for current template, if applicable + var/list/selected_access + /// chosen direction + var/selected_direction = SOUTH + /// interface to draw from if provided + var/datum/item_interface/interface + /// we're in deconstruction mode + var/deconstruction_mode = FALSE + /// what cell type we start with + var/cell_type = /obj/item/cell/super + /// base work done per second + var/work_speed = 1 SECONDS + /// max distance + var/maximum_distance = 10 + /// no-penalty distance + var/no_penalty_distance = 1 + /// multiplier to distance for divisor when outisde of no_penalty_distance + var/distance_divisor_multiplier = 0.5 + /// watts used per second + var/power_draw = POWER_USAGE_HOLOFABRICATOR + /// things actually being worked on; used to allow for 'fast' ops when something + /// needs less work than SSprocessing's process interval. + /// if something isn't in this list, it won't be process()'d + var/list/atom/processing_targets + +#warn impl all + +/obj/item/stream_projector/holofabricator/Initialize(mapload) + . = ..() + init_cell_slot(cell_type) + obj_cell_slot.legacy_use_device_cells = FALSE + obj_cell_slot.remove_yank_offhand = TRUE + obj_cell_slot.remove_yank_context = TRUE + +/obj/item/stream_projector/holofabricator/examine(mob/user, dist) + . = ..() + if(!isnull(obj_cell_slot.cell)) + . += SPAN_NOTICE("Its cell has [round(obj_cell_slot.cell.percent())]% charge remaining.") + else if(!isnull(interface)) + . += SPAN_WARNING("Its cell is missing.") + + #warn matter + +/obj/item/stream_projector/holofabricator/examine_more(mob/user) + . = ..() + . += SPAN_RED("Things constructed with holofabricators do not have the same structural integrity as things built by conventional means.") + . += SPAN_RED("Transfer efficiency is lowered quadratically with a target's distance from the applied holofabricator.") + +/obj/item/stream_projector/holofabricator/update_icon(updates) + cut_overlays() + . = ..() + var/amount = inserted_cartridge?.get_ratio() * 10 + if(amount) + amount = CEILING(amount, 1) + add_overlay("projector-[amount]", TRUE) + +/obj/item/stream_projector/holofabricator/valid_target(atom/entity) + return isatom(entity) + +/obj/item/stream_projector/holofabricator/try_lock_target(atom/entity, datum/event_args/actor/actor, silent) + var/turf/where_we_are = get_turf(src) + var/turf/where_they_are = get_turf(entity) + if(get_dist(where_we_are, where_they_are) > maximum_distance) + if(!silent) + actor.chat_feedback( + SPAN_WARNING("That is out of range."), + target = src, + ) + return FALSE + return ..() + +/obj/item/stream_projector/holofabricator/setup_target_visuals(atom/entity) + var/datum/beam/beam = create_stretched_beam(src, entity, icon = 'icons/items/stream_projector/holofabricator.dmi', icon_state = "beam-double", collider_type = /atom/movable/beam_collider) + active_targets[entity] = beam + RegisterSignal(beam, COMSIG_BEAM_REDRAW, PROC_REF(on_beam_redraw)) + RegisterSignal(beam, COMSIG_BEAM_CROSSED, PROC_REF(on_beam_crossed)) + +/obj/item/stream_projector/holofabricator/proc/on_beam_redraw(datum/beam/source) + var/atom/movable/target = source.beam_target + if(get_dist(src, target) > maximum_distance) + drop_target(target) + var/turf/where_we_are = get_turf(src) + var/turf/where_they_are = get_turf(target) + if(where_we_are?.z != where_they_are?.z) + drop_target(target) + +/obj/item/stream_projector/holofabricator/proc/on_beam_crossed(datum/beam/source, atom/what) + if(what.pass_flags_self & ATOM_PASS_CLICK) + return + if(!what.density) + return + drop_target(source.beam_target) + +/obj/item/stream_projector/holofabricator/teardown_target_visuals(atom/entity) + var/datum/beam/beam = active_targets[entity] + QDEL_NULL(beam) + +/obj/item/stream_projector/holofabricator/attack_hand(mob/user, list/params) + if(user.is_holding_inactive(src)) + if(isnull(inserted_cartridge)) + user.action_feedback(SPAN_WARNING("[src] has no vial loaded."), src) + return CLICKCHAIN_DO_NOT_PROPAGATE + user.put_in_hands_or_drop(inserted_cartridge) + user.action_feedback(SPAN_NOTICE("You remove [inserted_cartridge] from [src]."), src) + var/obj/item/polyfill_cartridge/old_cartridge = inserted_cartridge + inserted_cartridge = null + playsound(src, 'sound/weapons/empty.ogg', 50, FALSE) + update_icon() + on_cartridge_swap(old_cartridge, null) + return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING + return ..() + +/obj/item/stream_projector/holofabricator/attackby(obj/item/I, mob/living/user, list/params, clickchain_flags, damage_multiplier) + if(istype(I, /obj/item/polyfill_cartridge)) + var/obj/item/polyfill_cartridge/cartridge = I + if(!user.transfer_item_to_loc(cartridge, src)) + user.action_feedback(SPAN_WARNING("[cartridge] is stuck to your hand!"), src) + return CLICKCHAIN_DO_NOT_PROPAGATE + var/obj/item/polyfill_cartridge/old_cartridge = inserted_cartridge + inserted_cartridge = cartridge + if(!isnull(old_cartridge)) + user.action_feedback(SPAN_NOTICE("You quickly swap [old_cartridge] with [cartridge]."), src) + user.put_in_hands_or_drop(old_cartridge) + else + user.action_feedback(SPAN_NOTICE("You insert [cartridge] into [src]."), src) + playsound(src, 'sound/weapons/autoguninsert.ogg', 50, FALSE) + update_icon() + on_cartridge_swap(old_cartridge, cartridge) + return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING + return ..() + +/obj/item/stream_projector/holofabricator/process(delta_time) + #warn impl + #warn a way to do 'fast' processed for things with less than delta-time work. + +/obj/item/stream_projector/holofabricator/transform_target_lock(atom/target) + if(isturf(target)) + #warn impl + else if(isobj(target)) + if(istype(target, /obj/structure/holofabricator_construction)) + pass() + else + pass() + #warn impl + else if(ismob(target)) + return null + else + CRASH("what?") + +/obj/item/stream_projector/holofabricator/proc/on_cartridge_swap(obj/item/polyfill_cartridge/old_cartridge, obj/item/polyfill_cartridge/new_cartridge) + return + +/obj/item/stream_projector/holofabricator/proc/toggle_deconstruction_mode(new_state) + drop_all_targets() + deconstruction_mode = new_state + +/obj/item/stream_projector/holofabricator/ui_static_data(mob/user, datum/tgui/ui) + . = ..() + .["materialContext"] = SSmaterials.tgui_materials_context() + #warn impl + +/obj/item/stream_projector/holofabricator/ui_data(mob/user, datum/tgui/ui) + . = ..() + #warn impl + +/obj/item/stream_projector/holofabricator/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Holofabricator") + ui.open() + +/obj/item/stream_projector/holofabricator/ui_act(action, list/params, datum/tgui/ui) + . = ..() + #warn impl + +/proc/cmp_holofabricator_templates(datum/holofabricator_template/A, datum/holofabricator_template/B) + if(A.priority != B.priority) + return B.priority - A.priority + return sorttext(A.name, B.name) + +//? Frame Object + +/** + * 'frame' of an object, that solidifies into the object itself at a certain point + */ +/obj/structure/holofabricator_construction + name = "hardlight template" + desc = "A partially assembled template for something, likely formed with a holofabricator." + + /// template + var/datum/holofabricator_template/template + /// selected pattern in template, if any + var/pattern + /// options + var/list/options + /// selected materials + var/list/materials + /// our polyfill material + var/datum/material/structure + /// work required + var/work_required + /// work remaining + var/work_remaining + /// locked holofabricators + var/list/obj/item/stream_projector/holofabricator/affecting = list() + +/obj/structure/holofabricator_construction/Initialize(mapload, datum/holofabricator_template/template, pattern, list/options, list/materials, list/req_access, list/req_one_access) + . = ..() + #warn impl + + src.req_access = req_access + src.req_one_access = req_one_access + +/obj/structure/holofabricator_construction/proc/work_on(obj/item/stream_projector/holofabricator/fabricator, amount) + work_remaining = max(0, work_remaining - amount) + if(!work_remaining) + template.finalize(src, loc, options, materials) + qdel(src) + #warn impl + +/obj/structure/holofabricator_construction/proc/attached(obj/item/stream_projector/holofabricator/fabricator) + affecting += fabricator + +/obj/structure/holofabricator_construction/proc/detached(obj/item/stream_projector/holofabricator/fabricator) + affecting -= fabricator + +#warn impl all + +//? Repair Field + +/** + * visual / effect handler for repairs + * created on entities when targeted by a holofabricator + */ +/datum/component/holofabricator_field + /// affecting fabricators + var/list/obj/item/stream_projector/holofabricator/affecting = list() + +/datum/component/holofabricator_field/Destroy() + #warn rid visuals + return ..() + +/datum/component/holofabricator_field/RegisterWithParent() + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + init_or_assert_visuals() + return ..() + +/datum/component/holofabricator_field/proc/init_or_assert_visuals() + #warn impl + +/datum/component/holofabricator_field/proc/attached(obj/item/stream_projector/holofabricator/fabricator) + affecting += fabricator + +/datum/component/holofabricator_field/proc/detached(obj/item/stream_projector/holofabricator/fabricator) + affecting -= fabricator + if(!length(affecting)) + qdel(src) + +#warn impl all + +//? Templates + +/** + * rcd templates + */ +/datum/holofabricator_template + /// our name + var/name = "unkw (?)" + /// templates are sorted by priority, ascending, then name + var/priority = 0 + /// template category + var/category = "Miscellaneous" + + /// typepath of object + var/build_path + #warn sigh how to dela with patterns like airlocks?? + /// patterns, please use pattern builder system + var/datum/holofabricator_template_patterns/patterns + /// material choices - list of keys + var/list/material_parts + /// options - please use option builder system. + var/datum/holofabricator_template_options/options + /// work required (baseline) + var/work_required = 3 SECONDS + /// has directions + var/has_directions = FALSE + /// has access settings + var/has_access = FALSE + /// spritesheet icon + var/spritesheet_icon + /// spritesheet prefix + var/spritesheet_prefix + +/** + * initializes and generates icons + */ +/datum/holofabricator_template/proc/initialize() + options = construct_options(new /datum/holofabricator_template_options) + patterns = construct_patterns(new /datum/holofabricator_template_patterns) + #warn impl + +/** + * constructs options datum + */ +/datum/holofabricator_template/proc/construct_options(datum/holofabricator_template_options/options) + return options +/** + * constructs patterns datum + */ +/datum/holofabricator_template/proc/construct_patterns(datum/holofabricator_template_patterns/patterns) + return patterns + +/datum/holofabricator_template/proc/tgui_template_data() + return list( + "name" = name, + "priority" = priority, + "category" = category, + "materialParts" = material_parts, + "patterns" = patterns.tgui_template_patterns(), + "options" = options.tgui_template_options(), + ) + +#warn a way to adapt to things, ergo 'there's already a low wall but we want to also make a low wall' + +/** + * create the work in progress construction object + */ +/datum/holofabricator_template/proc/create(atom/where, list/options, list/materials) + return new /obj/structure/holofabricator_construction(where, src, options, materials) + +/** + * finalize a construction object + * + * we will not delete the construction object! + * + * @params + * * in_progress - the thing being made + */ +/datum/holofabricator_template/proc/finalize(obj/structure/holofabricator_construction/in_progress) + var/obj/created = create(in_progress.loc, in_progress.pattern, in_progress.options, in_progress.materials, in_progress.req_access, in_progress.req_one_access) + return created + +/datum/holofabricator_template/proc/instantiate(atom/where, pattern, list/options, list/materials, list/req_access, list/req_one_access) + return new build_path(where) + +/datum/holofabricator_template_options + +/datum/holofabricator_template_options/proc/tgui_template_options() + return list( + + ) + +/datum/holofabricator_template_options/proc/with_color_option(key, name) + +#warn impl all + +/datum/holofabricator_template_patterns + +/datum/holofabricator_template_patterns/proc/tgui_template_patterns() + return list( + ) + +/** + * todo: someday when shit isn't slow, we should just auto-generate these things instead :/ + */ +/datum/holofabricator_template_patterns/proc/with_pattern(name, id, preview_state) + +#warn impl all + +//? Template Implementations ?// + +/datum/holofabricator_template/structure + category = "Structure" + spritesheet_icon = 'icons/items/stream_projector/holofabricator-structures.dmi' + +/datum/holofabricator_template/structure/wall + #warn impl + work_required = 3 SECONDS + #warn wall + +/datum/holofabricator_template/structure/wall/construct_patterns(datum/holofabricator_template_patterns/patterns) + . = ..() + #warn impl + +/datum/holofabricator_template/structure/floor + #warn impl + work_required = 0.25 SECONDS + #warn plating + +/datum/holofabricator_template/structure/floor/construct_patterns(datum/holofabricator_template_patterns/patterns) + . = ..() + #warn impl + +/datum/holofabricator_template/structure/airlock + #warn impl + work_required = 5 SECONDS + spritesheet_icon = 'icons/items/stream_projector/holofabricator-airlocks.dmi' + #warn airlock, airlock-glass, hatch, hatch-glass, external + #warn options for colors + +/datum/holofabricator_template/structure/airlock/construct_patterns(datum/holofabricator_template_patterns/patterns) + . = ..() + #warn impl + +/datum/holofabricator_template/structure/full_window + #warn impl + work_required = 2.5 SECONDS + #warn window, window-reinf + +/datum/holofabricator_template/structure/full_window/construct_patterns(datum/holofabricator_template_patterns/patterns) + . = ..() + #warn impl + +/datum/holofabricator_template/structure/low_wall + #warn impl + work_required = 1 SECONDS + #warn low-wall + +/datum/holofabricator_template/structure/low_wall/construct_patterns(datum/holofabricator_template_patterns/patterns) + . = ..() + #warn impl + +/datum/holofabricator_template/furniture + category = "Furniture" + spritesheet_icon = 'icons/items/stream_projector/holofabricator-furniture.dmi' + +/datum/holofabricator_template/furniture/table + #warn impl + work_required = 1 SECONDS + #warn table, table-reinf, table-frame + +/datum/holofabricator_template/furniture/table/construct_patterns(datum/holofabricator_template_patterns/patterns) + . = ..() + #warn impl + +/datum/holofabricator_template/furniture/office_chair + #warn impl + work_required = 1 SECONDS + #warn office-chair-white, office-chair-dark + +/datum/holofabricator_template/furniture/office_chair/construct_patterns(datum/holofabricator_template_patterns/patterns) + . = ..() + #warn impl + +/datum/holofabricator_template/furniture/stool + #warn impl + work_required = 1 SECONDS + #warn stool + +/datum/holofabricator_template/furniture/stool/construct_patterns(datum/holofabricator_template_patterns/patterns) + . = ..() + #warn impl + +/datum/holofabricator_template/furniture/bed + #warn impl + work_required = 1 SECONDS + #warn bed, bed-double + +/datum/holofabricator_template/furniture/bed/construct_patterns(datum/holofabricator_template_patterns/patterns) + . = ..() + #warn impl + +// todo: frame refactor + +// /datum/holofabricator_template/standing_frame +// category = "Frame (Floor)" + +// /datum/holofabricator_template/wall_frame +// category = "Frame (wall)" + +//? Materials + +/** + * A special polyfill material usable for most templates. + */ +/datum/material/polyfill + id = "holofab-polyfill" + name = "Structural Polyfill" + + relative_integrity = 1 + density = 8 + relative_conductivity = 1 + relative_permeability = 0 + relative_reactivity = 0.75 + hardness = MATERIAL_RESISTANCE_MODERATE + toughness = MATERIAL_RESISTANCE_HIGH + refraction = MATERIAL_RESISTANCE_LOW + absorption = MATERIAL_RESISTANCE_MODERATE + nullification = MATERIAL_RESISTANCE_NONE + +/datum/material/polyfill/generate_recipes() + return list() + +#undef HOLOFABRICATOR_MODE_CONSTRUCT +#undef HOLOFABRICATOR_MODE_DECONSTRUCT +#undef HOLOFABRICATOR_MODE_REPAIR diff --git a/code/game/objects/items/stream_projector/medichine.dm b/code/game/objects/items/stream_projector/medichine.dm new file mode 100644 index 000000000000..d92482a102ec --- /dev/null +++ b/code/game/objects/items/stream_projector/medichine.dm @@ -0,0 +1,829 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +// todo: peristable repository +GLOBAL_LIST_EMPTY(medichine_cell_datums) + +/proc/fetch_cached_medichine_cell_datum(datum/medichine_cell/typepath) + if(isnull(GLOB.medichine_cell_datums[typepath])) + GLOB.medichine_cell_datums[typepath] = new typepath + return GLOB.medichine_cell_datums[typepath] + +//? Projector + +/** + * medical beamgun + * + * todo: should we use reagents instead..? + */ +ITEM_AUTO_BINDS_SINGLE_INTERFACE_TO_VAR(/obj/item/stream_projector/medichine, interface) +/obj/item/stream_projector/medichine + name = "medichine stream projector" + desc = "A specialized, locked-down variant of a nanite stream projector. Deploys medichines from a cartridge onto a target's surface." + icon = 'icons/items/stream_projector/medichine.dmi' + icon_state = "projector" + base_icon_state = "projector" + + // todo: proper cataloguing fluff desc system + description_fluff = "An expensive prototype first developed jointly by Vey-Med and Nanotrasen, the medichine stream projector is essentially a \ + somewhat perfected cross between a holofabricator's confinement stream and a just-in-time nanoswarm compiler. Due to the relative little \ + need for a powerful, laminar stream of particles, this has a far higher efficient range than a standard holofabricator. Nanites must \ + be provided with prepared medichine cartridges." + + process_while_active = TRUE + + /// installed cartridge + var/obj/item/medichine_cell/inserted_cartridge + /// standard injection rate (amount per second) + var/injection_rate = 2 + /// standard suspension limit multiplier + var/suspension_multiplier = 1 + /// interface to draw from if provided + var/datum/item_interface/interface + /// all beams + var/list/datum/beam/beams_by_entity + /// maximum distance + var/maximum_distance = 10 + /// multiplier to distance when dividing by distance + var/distance_divisor_multiplier = 0.5 + /// distance allowed before starting to apply penalty + var/distance_penalty_start = 1 + +/obj/item/stream_projector/medichine/examine(mob/user, dist) + . = ..() + . += SPAN_RED("[src] loses some efficiency based on its distance to a target.") + if(isnull(inserted_cartridge)) + . += "[src] has nothing loaded." + else + . += "[src] has [inserted_cartridge] loaded. It has [CEILING(inserted_cartridge.volume, 0.01)]/[inserted_cartridge.max_volume] left." + +/obj/item/stream_projector/medichine/valid_target(atom/entity) + return isliving(entity) + +/obj/item/stream_projector/medichine/attack_hand(mob/user, list/params) + if(user.is_holding_inactive(src)) + if(isnull(inserted_cartridge)) + user.action_feedback(SPAN_WARNING("[src] has no vial loaded."), src) + return CLICKCHAIN_DO_NOT_PROPAGATE + user.put_in_hands_or_drop(inserted_cartridge) + user.action_feedback(SPAN_NOTICE("You remove [inserted_cartridge] from [src]."), src) + var/obj/item/medichine_cell/old_cell = inserted_cartridge + inserted_cartridge = null + playsound(src, 'sound/weapons/empty.ogg', 50, FALSE) + update_icon() + on_cell_swap(old_cell, null) + return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING + return ..() + +/obj/item/stream_projector/medichine/attackby(obj/item/I, mob/living/user, list/params, clickchain_flags, damage_multiplier) + if(istype(I, /obj/item/medichine_cell)) + var/obj/item/medichine_cell/cell = I + if(!user.transfer_item_to_loc(cell, src)) + user.action_feedback(SPAN_WARNING("[cell] is stuck to your hand!"), src) + return CLICKCHAIN_DO_NOT_PROPAGATE + var/obj/item/medichine_cell/old_cell = inserted_cartridge + inserted_cartridge = cell + if(!isnull(old_cell)) + user.action_feedback(SPAN_NOTICE("You quickly swap [old_cell] with [cell]."), src) + user.put_in_hands_or_drop(old_cell) + else + user.action_feedback(SPAN_NOTICE("You insert [cell] into [src]."), src) + playsound(src, 'sound/weapons/autoguninsert.ogg', 50, FALSE) + update_icon() + on_cell_swap(old_cell, cell) + return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING + return ..() + +/obj/item/stream_projector/medichine/try_lock_target(atom/entity, datum/event_args/actor/actor, silent) + var/turf/where_we_are = get_turf(src) + var/turf/where_they_are = get_turf(entity) + if(get_dist(where_we_are, where_they_are) > maximum_distance) + if(!silent) + actor.chat_feedback( + SPAN_WARNING("That is out of range."), + target = src, + ) + return FALSE + if(get_dist(where_we_are, where_they_are) <= 1 && !where_we_are.TurfAdjacency(where_they_are)) + if(!silent) + actor.chat_feedback( + SPAN_WARNING("Something is in the way!"), + target = src, + ) + return FALSE + var/list/turf/line_to_them = getline(where_we_are, get_turf(entity)) + if(isnull(line_to_them)) + return FALSE + var/atom/blocking + for(var/turf/potential_blocker in line_to_them) + if(potential_blocker.density && !(potential_blocker.pass_flags_self & ATOM_PASS_CLICK)) + blocking = potential_blocker + break + for(var/obj/object in potential_blocker) + if(object.density && !(object.pass_flags_self & ATOM_PASS_CLICK)) + blocking = object + break + if(!isnull(blocking)) + break + if(!isnull(blocking)) + if(!silent) + actor.chat_feedback( + SPAN_WARNING("Something is in the way!"), + target = src, + ) + return FALSE + return ..() + +/obj/item/stream_projector/medichine/setup_target_visuals(atom/entity) + . = ..() + var/datum/beam/creating_beam = create_segmented_beam(src, entity, icon = 'icons/items/stream_projector/medichine.dmi', icon_state = "beam", collider_type = /atom/movable/beam_collider) + creating_beam.shift_start_towards_target = 8 + creating_beam.shift_end_towards_source = 8 + LAZYSET(beams_by_entity, entity, creating_beam) + var/datum/medichine_cell/effective_cell = effective_cell_datum() + if(!isnull(effective_cell)) + creating_beam.segmentation.color = beam_color(effective_cell.color) + RegisterSignal(creating_beam, COMSIG_BEAM_REDRAW, PROC_REF(on_beam_redraw)) + RegisterSignal(creating_beam, COMSIG_BEAM_CROSSED, PROC_REF(on_beam_crossed)) + +/obj/item/stream_projector/medichine/proc/on_beam_redraw(datum/beam/source) + var/atom/movable/target = source.beam_target + if(get_dist(src, target) > maximum_distance) + drop_target(target) + +/obj/item/stream_projector/medichine/proc/on_beam_crossed(datum/beam/source, atom/what) + if(what.pass_flags_self & ATOM_PASS_CLICK) + return + if(!what.density) + return + drop_target(source.beam_target) + +/obj/item/stream_projector/medichine/teardown_target_visuals(atom/entity) + . = ..() + var/datum/beam/their_beam = beams_by_entity[entity] + beams_by_entity -= entity + qdel(their_beam) + +/obj/item/stream_projector/medichine/proc/on_cell_swap(obj/item/medichine_cell/old_cell, obj/item/medichine_cell/new_cell) + // drop all targets if no new cell + if(isnull(new_cell)) + drop_all_targets() + return + var/datum/medichine_cell/effective_cell = effective_cell_datum() + if(!isnull(effective_cell)) + for(var/atom/entity as anything in beams_by_entity) + var/datum/beam/entity_beam = beams_by_entity[entity] + entity_beam.segmentation.color = beam_color(effective_cell.color) + +/obj/item/stream_projector/medichine/proc/beam_color(color) + var/list/decoded = ReadRGB(color) + return list( + decoded[1] / 255, decoded[2] / 255, decoded[3] / 255, + 0, 0, 0, + 1, 1, 1, + ) + +/obj/item/stream_projector/medichine/proc/effective_cell_datum() + return isnull(interface)? inserted_cartridge?.cell_datum : interface.query_medichines() + +/obj/item/stream_projector/medichine/process(delta_time) + ..() + // if not injecting, drop + if(!length(active_targets)) + return + + // get effective cell + var/datum/medichine_cell/effective_package = effective_cell_datum() + // drop targets if not injecting + if(isnull(effective_package)) + drop_all_targets() + return + // todo: this is for multi-cell support; implement that. + var/list/datum/medichine_cell/injecting_packages = list(effective_package) + // for each cell + for(var/datum/medichine_cell/injecting_package as anything in injecting_packages) + // get effective suspension limit and injection rates + var/injecting_suspension = max(0, injecting_package.suspension_limit * suspension_multiplier) + var/injecting_rate = clamp(injection_rate * injecting_package.injection_multiplier, 0, injecting_suspension) + // inject to active targets + for(var/mob/entity in active_targets) + var/datum/component/medichine_field/field = entity.LoadComponent(/datum/component/medichine_field) + var/distance_multiplier = 1 / max(1, 1 + max(get_dist(src, entity) - distance_penalty_start, 0) * distance_divisor_multiplier) + var/requested = min(injecting_rate * distance_multiplier, max(0, injecting_suspension - field.active?[injecting_package])) + var/allowed = isnull(interface)? inserted_cartridge?.use(injecting_package, requested) : interface.use_medichines(injecting_package, requested) + field.inject_medichines(injecting_package, allowed) + +//? Field + +/** + * component used to form a mob's nanite cloud visuals + processing + */ +/datum/component/medichine_field + /// active effect packages associated to nanite volume + var/list/datum/medichine_cell/active + /// cached total volume + var/total_volume = 0 + /// what our color should be + var/current_color = "#ffffff" + /// our particles + var/atom/movable/particle_render/renderer + +/datum/component/medichine_field/Initialize() + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + return ..() + +/datum/component/medichine_field/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_MOB_ON_LIFE, PROC_REF(on_life)) + +/datum/component/medichine_field/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, COMSIG_MOB_ON_LIFE) + QDEL_NULL(renderer) + +/datum/component/medichine_field/proc/on_life(datum/source, seconds, times_fired) + tick(seconds) + +/** + * called by on_life or process depending on if we're on a mob or normal atom. + */ +/datum/component/medichine_field/proc/tick(seconds) + if(!isliving(parent)) // no support for atoms yet + return + var/mob/living/victim = parent + var/removed_something = FALSE + for(var/datum/medichine_cell/cell_package as anything in active) + var/cell_volume = active[cell_package] + var/reacting = min(cell_package.reaction_rate, cell_volume) + var/reacted_ratio = 0 + + // perform tick + for(var/datum/medichine_effect/cell_effect as anything in cell_package.effects) + var/used_ratio + used_ratio = cell_effect.tick_on_mob(src, victim, reacting) + if(isnull(used_ratio)) + continue + reacted_ratio = max(reacted_ratio, used_ratio) + + var/decaying = max(cell_package.decay_minimum_baseline, reacting * reacted_ratio) + active[cell_package] -= decaying + total_volume -= decaying + if(active[cell_package] < 0) + active -= cell_package + for(var/datum/medichine_effect/cell_effect as anything in cell_package.effects) + cell_effect.target_removed(victim) + removed_something = TRUE + if(removed_something) + recalculate_color() + +/datum/component/medichine_field/proc/recalculate_color() + if(!total_volume) + current_color = "#ffffff" + else + var/list/blended = list(0, 0, 0) + for(var/datum/medichine_cell/package as anything in active) + var/ratio = 1 / length(active) + blended[1] += package.color_rgb_list[1] * ratio + blended[2] += package.color_rgb_list[2] * ratio + blended[3] += package.color_rgb_list[3] * ratio + current_color = rgb(blended[1], blended[2], blended[3]) + renderer.color = current_color + +/datum/component/medichine_field/proc/inject_medichines(datum/medichine_cell/medichines, amount) + LAZYINITLIST(active) + ensure_visuals() + if(isnull(active[medichines])) + for(var/datum/medichine_effect/effect as anything in medichines.effects) + effect.target_added(parent) + recalculate_color() + active[medichines] += amount + total_volume += amount + +/datum/component/medichine_field/proc/ensure_visuals() + if(!isnull(renderer)) + return + renderer = new /atom/movable/particle_render/medichine_field(null) + if(ismovable(parent)) + var/atom/movable/entity = parent + entity.vis_contents += renderer + else + var/atom/entity = parent + renderer.loc = entity + var/particles/particle_instance = new /particles/medichine_field + renderer.particles = particle_instance + renderer.color = current_color + +//? VFX + +/atom/movable/particle_render/medichine_field + alpha = 200 + +/particles/medichine_field + width = 32 + height = 32 + count = 75 + spawning = 1.25 + fade = 3 + lifespan = 3 + velocity = list(0, 2.5, 0) + icon = 'icons/items/stream_projector/medichine.dmi' + icon_state = list( + "particle-plus" = 1, + ) + position = generator("box", list(-4, -16, 0), list(6, 0, 0)) + +//? Cell + +/** + * medical beamgun cell + */ +/obj/item/medichine_cell + name = "medichine cartridge (EMPTY)" + desc = "A cartridge meant to hold medicinal nanites." + icon = 'icons/items/stream_projector/medichine.dmi' + icon_state = "cell" + base_icon_state = "cell" + materials_base = list( + MAT_STEEL = 500, + MAT_PLASTIC = 500, + MAT_GLASS = 250, + ) + + /// path to cell datum + var/datum/medichine_cell/cell_datum = /datum/medichine_cell + /// units left + var/volume = 100 + /// maximum units left + var/max_volume = 100 + + /// materials needed per unit of nanites + var/list/materials_per_volume_unit = list() + +/obj/item/medichine_cell/Initialize(mapload) + . = ..() + cell_datum = fetch_cached_medichine_cell_datum(cell_datum) + update_icon() + +/obj/item/medichine_cell/examine(mob/user, dist) + . = ..() + . += SPAN_NOTICE("Its gauge reads [CEILING(volume, 0.01)]/[max_volume].") + +/obj/item/medichine_cell/update_icon(updates) + cut_overlays() + . = ..() + var/number = volume? round(volume / max_volume * 4) : 0 + if(number) + var/image/I = image(icon, "[base_icon_state]-[number]") + I.color = cell_datum.color + add_overlay(I, TRUE) + +/obj/item/medichine_cell/proc/fill(amount) + . = min(amount, volume) + if(!.) + return + volume = clamp(volume + amount, 0, max_volume) + +/obj/item/medichine_cell/proc/use(datum/medichine_cell/requested, amount) + if(requested != cell_datum) + return 0 + . = min(amount, volume) + if(!.) + return + volume = clamp(volume - amount, 0, max_volume) + update_icon() + +/obj/item/medichine_cell/detect_material_base_costs() + . = ..() + for(var/key in materials_per_volume_unit) + var/val = materials_per_volume_unit[key] + .[key] += round(val * volume) + +/obj/item/medichine_cell/get_materials(respect_multiplier) + . = ..() + var/multiplier = respect_multiplier? material_multiplier : 1 + for(var/key in materials_per_volume_unit) + var/val = materials_per_volume_unit[key] + .[key] += round(val * volume) * multiplier + +// todo: after med rework all of these need a look-over + +/obj/item/medichine_cell/seal_wounds + name = "medichine cartridge (SEAL)" + desc = "A cartridge of swirling dust. This will repair, disinfect, and seal open wounds." + cell_datum = /datum/medichine_cell/seal_wounds + materials_per_volume_unit = list( + MAT_GLASS = 2, + MAT_STEEL = 2, + ) + +/obj/item/medichine_cell/seal_wounds/violently + name = "medichine cartridge (DEBRIDE)" + desc = "A cartridge of angrily swirling dust. This will repair, disinfect, and seal open wounds. Rapidly, and painfully." + cell_datum = /datum/medichine_cell/seal_wounds/violently + materials_per_volume_unit = list( + MAT_GLASS = 2, + MAT_STEEL = 2, + MAT_SILVER = 2.5, + MAT_DIAMOND = 1, + ) + +// /obj/item/medichine_cell/fortify +// name = "medichine cartridge (FORTIFY)" +// desc = "A cartridge of robust swirling dust. This will toughen someone's skin with an artificial layer of cohesive nanites." +// cell_datum = /datum/medichine_cell/fortify + +/obj/item/medichine_cell/stabilize + name = "medichine cartridge (STABILIZE)" + desc = "A cartridge of cohesive swirling dust. This will stabilize someone's life functions and provide manual metabolism." + cell_datum = /datum/medichine_cell/stabilize + materials_per_volume_unit = list( + MAT_GLASS = 2, + MAT_STEEL = 2, + MAT_PLASTIC = 2, + MAT_SILVER = 2, + ) + +/obj/item/medichine_cell/deathmend + name = "medichine cartridge (DEATHMEND)" + desc = "A cartridge of necromantic swirling dust. This will repair the wounds, even of the dead." + cell_datum = /datum/medichine_cell/deathmend + materials_per_volume_unit = list( + MAT_GLASS = 2, + MAT_STEEL = 2, + MAT_PLASTIC = 1.5, + MAT_URANIUM = 1.5, + MAT_SILVER = 2, + MAT_GOLD = 1.5, + ) + +/obj/item/medichine_cell/synth_repair + name = "medichine cartridge (SYNTHFIX)" + desc = "A cartridge of metallic swirling dust. This will patch up damaged limbs on a synthetic." + cell_datum = /datum/medichine_cell/synth_repair + materials_per_volume_unit = list( + MAT_GLASS = 2, + MAT_STEEL = 2, + MAT_PLASTIC = 1.5, + MAT_DIAMOND = 2, + ) + +//? Effect Packages + +// /obj/item/medichine_cell/synth_tuning +// name = "medichine cartridge (SYNTHTUNE)" +// desc = "A cartridge of glowing, swirling dust. This will act in cohesion with a synthetic, as a performance amplifier." +// cell_datum = /datum/medichine_cell/synth_tuning + +/** + * medical beamgun effect package + */ +/datum/medichine_cell + /// list of effects; set to paths to init on new + var/list/datum/medichine_effect/effects + /// nanite consumption rate per second + var/reaction_rate = 1 + /// projector injection rate multiplier + var/injection_multiplier = 1 + /// amount of volume that can stick onto someone by default + var/suspension_limit = 10 + /// minimum decay rate so the field doesn't stick forever + var/decay_minimum_baseline = 0.1 + /// normal color + var/color = "#ffffff" + /// normal color as a list + var/list/color_rgb_list + +/datum/medichine_cell/New() + color_rgb_list = ReadRGB(color) + for(var/i in 1 to length(effects)) + var/datum/medichine_effect/effect = effects[i] + if(istype(effect)) + continue + effect = new effect + effects[i] = effect + +/datum/medichine_cell/seal_wounds + // each cell heals 400 + effects = list( + /datum/medichine_effect/wound_healing{ + biology_types = BIOLOGY_TYPES_FLESHY; + disinfect_wounds = TRUE; + seal_wounds = TRUE; + repair_strength_brute = 4; + repair_strength_burn = 4; + } + ) + color = "#aa0000" + +/datum/medichine_cell/seal_wounds/violently + reaction_rate = 2 + // agony needs to tick first + // each cell heals 400, but faster + effects = list( + /datum/medichine_effect/agony_from_open_wounds{ + hard_limit = 50; + }, + /datum/medichine_effect/wound_healing{ + biology_types = BIOLOGY_TYPES_FLESHY; + disinfect_wounds = TRUE; + seal_wounds = TRUE; + repair_strength_brute = 2; + repair_strength_burn = 2; + }, + ) + color = "#ff3300" + +// /datum/medichine_cell/fortify +// #warn impl + +/datum/medichine_cell/stabilize + effects = list( + /datum/medichine_effect/stabilize{ + ignore_consumption = FALSE; + }, + /datum/medichine_effect/forced_metabolism{ + ignore_consumption = FALSE; + only_dead = TRUE; + }, + /datum/medichine_effect/oxygenate{ + strength = 5; + only_while_above = 40; + }, + ) + // 30 seconds + suspension_limit = 10 + // constant draw, 5 minutes of stabilization per cell + reaction_rate = (1 / 3) + decay_minimum_baseline = (1 / 3) + color = "#9999ff" + +/datum/medichine_cell/deathmend + // each cell heals 600-1200 (!!) + effects = list( + /datum/medichine_effect/wound_healing{ + biology_types = BIOLOGY_TYPES_FLESHY; + disinfect_wounds = TRUE; + seal_wounds = TRUE; + repair_strength_brute = 6; + repair_strength_burn = 6; + } + ) + suspension_limit = 5 + // 18-36 hp/s + reaction_rate = 3 + color = "#883333" + +/datum/medichine_cell/synth_repair + // each cell heals 400 + effects = list( + /datum/medichine_effect/wound_healing{ + biology_types = BIOLOGY_TYPES_SYNTHETIC; + seal_wounds = TRUE; + disinfect_wounds = TRUE; + repair_strength_brute = 4; + repair_strength_burn = 4; + } + ) + color = "#888844" + // no pre-buffing allowed! + suspension_limit = 5 + decay_minimum_baseline = 0.5 + +// /datum/medichine_cell/synth_tuning +// #warn impl + +//? Effects + +/** + * medical beamgun effect + */ +/datum/medichine_effect + /// completely ignore us for calculations by assuming we are never using any nanite volume + /// set from the /datum/medichine_cell side to allow for 'side' effects that don't necessarily + /// use any nanite volume and are always just there. + var/ignore_consumption = FALSE + +/** + * as opposed to ticking on objs. + * + * @return 0 to 1 of ratio used; null for 'stop' + */ +/datum/medichine_effect/proc/tick_on_mob(datum/component/medichine_field/field, mob/living/entity, volume, seconds) + return 1 + +/** + * called on target add + */ +/datum/medichine_effect/proc/target_added(datum/component/medichine_field/field, atom/entity) + return + +/** + * called on target remove + */ +/datum/medichine_effect/proc/target_removed(datum/component/medichine_field/field, atom/entity) + return + +/datum/medichine_effect/wound_healing + /// allowed biologies + var/biology_types = NONE + // todo: strength, not instant + var/seal_wounds = FALSE + // todo: strength, not instant + var/disinfect_wounds = FALSE + /// per unit + var/repair_strength_brute = 0 + /// per unit + var/repair_strength_burn = 0 + /// works on the dead + var/while_dead = FALSE + /// only fixes open wounds, will not seal until it's closed + var/only_open = FALSE + +/datum/medichine_effect/wound_healing/tick_on_mob(datum/component/medichine_field/field, mob/living/entity, volume, seconds) + var/brute_healing_total = volume * repair_strength_brute + var/burn_healing_total = volume * repair_strength_burn + var/brute_healing_left = brute_healing_total + var/burn_healing_left = burn_healing_total + var/mob/living/carbon/humanlike = entity + // do simple healing for simplemobs + if(!istype(humanlike)) + brute_healing_left -= entity.heal_brute_loss(brute_healing_total) + burn_healing_left -= entity.heal_fire_loss(burn_healing_total) + return max(1 - (burn_healing_left / burn_healing_total), 1 - (brute_healing_left / brute_healing_total)) + var/brute_loss_instances = 0 + var/burn_loss_instances = 0 + var/list/datum/wound/wounds_healing = list() + for(var/obj/item/organ/external/ext as anything in humanlike.bad_external_organs) + // only heal 15 wounds at a time thank you! + if(length(wounds_healing) > 15) + break + for(var/datum/wound/wound as anything in ext.wounds) + // only heal 15 wounds at a time thank you! + if(length(wounds_healing) > 15) + break + if(!wound.damage) + continue + if(wound.internal) + continue + if(only_open && (wound.is_treated())) + continue + if(wound.damage_type == BURN) + burn_loss_instances++ + else + brute_loss_instances++ + wounds_healing += wound + var/burn_heal_per = burn_loss_instances && (burn_healing_left / burn_loss_instances) + var/brute_heal_per = brute_loss_instances && (brute_healing_left / brute_loss_instances) + burn_heal_per = CEILING(burn_heal_per, 1) + brute_heal_per = CEILING(brute_heal_per, 1) + var/burn_heal_overrun = 0 + var/brute_heal_overrun = 0 + for(var/datum/wound/wound as anything in wounds_healing) + var/effective_heal + if(wound.damage_type == BURN) + if(!burn_healing_left) + continue + effective_heal = min(burn_heal_per + burn_heal_overrun, burn_healing_left) + if(wound.damage > effective_heal) + wound.heal_damage(effective_heal) + burn_heal_overrun = 0 + burn_healing_left -= effective_heal + else + burn_heal_overrun = effective_heal - wound.damage + wound.heal_damage(wound.damage) + else + if(!brute_healing_left) + continue + effective_heal = min(brute_heal_per + brute_heal_overrun, brute_healing_left) + if(wound.damage > effective_heal) + wound.heal_damage(effective_heal) + brute_healing_left -= effective_heal + brute_heal_overrun = 0 + else + brute_heal_overrun = effective_heal - wound.damage + wound.heal_damage(wound.damage) + if(!wound.damage) + if(seal_wounds) + wound.bandage() + wound.salve() + if(disinfect_wounds) + wound.disinfect() + return max(1 - (burn_healing_left / burn_healing_total), 1 - (brute_healing_left / brute_healing_total)) + +/datum/medichine_effect/oxygenate + /// works on the dead + var/while_dead = FALSE + /// amount per volume + var/strength = 0 + /// don't heal above that much damage + var/only_while_above = INFINITY + +/datum/medichine_effect/oxygenate/tick_on_mob(datum/component/medichine_field/field, mob/living/entity, volume, seconds) + if(STAT_IS_DEAD(entity.stat) && !while_dead) + return null + var/amount_to_heal = min(strength * volume, entity.getOxyLoss() - only_while_above) + if(amount_to_heal <= 0) + return 0 + return entity.heal_oxy_loss(amount_to_heal) / amount_to_heal + +/datum/medichine_effect/toxfilter + /// works on the dead + var/while_dead = FALSE + /// amount per volume + var/strength = 0 + /// don't heal above that much damage + var/only_while_above = INFINITY + +/datum/medichine_effect/toxfilter/tick_on_mob(datum/component/medichine_field/field, mob/living/entity, volume, seconds) + if(STAT_IS_DEAD(entity.stat) && !while_dead) + return null + var/amount_to_heal = min(strength * volume, entity.getToxLoss(), only_while_above) + if(amount_to_heal <= 0) + return 0 + return entity.heal_tox_loss(amount_to_heal) / amount_to_heal + +/datum/medichine_effect/stabilize + +/datum/medichine_effect/stabilize/target_added(datum/component/medichine_field/field, atom/entity) + ADD_TRAIT(entity, TRAIT_MECHANICAL_VENTILATION, "medichine-[REF(src)]") + ADD_TRAIT(entity, TRAIT_MECHANICAL_CIRCULATION, "medichine-[REF(src)]") + +/datum/medichine_effect/stabilize/target_removed(datum/component/medichine_field/field, atom/entity) + REMOVE_TRAIT(entity, TRAIT_MECHANICAL_VENTILATION, "medichine-[REF(src)]") + REMOVE_TRAIT(entity, TRAIT_MECHANICAL_CIRCULATION, "medichine-[REF(src)]") + +/datum/medichine_effect/forced_metabolism + /// time multiplier; 2 = tick 2 seconds per second + /// this is the constant before volume is added. + var/scale_rate_constant = 0 + /// scale time multiplier to volume used; if non-0, each 1 volume adds this much rate. + var/scale_rate_to_volume = 1 + /// continue to force metabolism while dead? + var/while_dead = FALSE + /// only while dead? overrides [while_dead]. + var/only_dead = FALSE + +/datum/medichine_effect/forced_metabolism/tick_on_mob(datum/component/medichine_field/field, mob/living/entity, volume, seconds) + if(STAT_IS_DEAD(entity.stat)) + if(!while_dead || only_dead) + return null + else + if(only_dead) + return null + var/real_rate = scale_rate_constant * seconds + volume * scale_rate_to_volume + entity.forced_metabolism(real_rate) + return 1 + +/datum/medichine_effect/agony_from_open_wounds + // it makes no sense to use this for consumption rate most of the time. + ignore_consumption = TRUE + /// hard limit to agony + var/hard_limit = 0 + /// grace threshold above hard limit we can deal to so we don't oscillate too hard + var/hard_overrun_allowed = 5 + /// agony multiplier + var/global_multiplier = 1 + /// agony per open damage + var/per_damage = 0.5 + /// increase rate per missing (minimum 1) + var/gain_rate_scaling = 0.15 + /// limit by bodypart based on bodypart ratio + /// if 0.5, it means 'agony can only be half of the bodypart's ratio or less' + /// you therefore usually want it way above say, 10, because 0.4 ratio with 100 ratio_scaling would be 40. + var/ratio_scaling = 100 + +/datum/medichine_effect/agony_from_open_wounds/tick_on_mob(datum/component/medichine_field/field, mob/living/entity, volume, seconds) + var/mob/living/carbon/humanlike = entity + if(!istype(humanlike)) + return null + if(humanlike.halloss >= hard_limit) + return 0 + var/can_deal = (hard_limit + hard_overrun_allowed) - humanlike.halloss + var/will_deal = 0 + for(var/obj/item/organ/external/ext as anything in humanlike.bad_external_organs) + var/size_ratio = organ_rel_size[ext.organ_tag] / GLOB.organ_combined_size + if(!size_ratio) + continue + var/organ_limit = ratio_scaling * size_ratio + var/total_damage = 0 + for(var/datum/wound/wound as anything in ext.wounds) + if(wound.internal) + continue + if(wound.is_treated()) + continue + total_damage += wound.damage + will_deal += min(organ_limit, per_damage * total_damage) + var/missing = humanlike.halloss - will_deal + var/dealing = min(can_deal, missing * gain_rate_scaling + 1) + humanlike.adjustHalLoss(dealing) + // todo: actually calculate consumption rate based on something..? + return 1 + +// /datum/medichine_effect/stoneskin + +// /datum/medichine_effect/stoneskin/tick_on_mob(mob/living/entity, volume) + +// /datum/medichine_effect/dextrous_motion + +// /datum/medichine_effect/dextrous_motion/tick_on_mob(mob/living/entity, volume) diff --git a/code/game/objects/items/stream_projector/stream_projector.dm b/code/game/objects/items/stream_projector/stream_projector.dm new file mode 100644 index 000000000000..e0c15aef78be --- /dev/null +++ b/code/game/objects/items/stream_projector/stream_projector.dm @@ -0,0 +1,171 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * base supertype of stream projection devices + * + * e.g. mediguns + * + * processing uses SSprocessing. + */ +/obj/item/stream_projector + /// locked targets; associated value must be truthy but is reserved by visual construction. + var/list/atom/active_targets + /// drop all targets on attack self + var/drop_all_targets_on_attack_self = TRUE + /// process while active + var/process_while_active = FALSE + /// process always + var/process_always = FALSE + +/obj/item/stream_projector/Initialize(mapload) + . = ..() + if(process_always) + START_PROCESSING(SSprocessing, src) + +/obj/item/stream_projector/Destroy() + drop_all_targets() + if(datum_flags & DF_ISPROCESSING) + STOP_PROCESSING(SSprocessing, src) + return ..() + +/obj/item/stream_projector/on_attack_self(datum/event_args/actor/e_args) + if(drop_all_targets_on_attack_self && length(active_targets)) + drop_all_targets() + e_args.chat_feedback( + SPAN_NOTICE("You disengage [src] from all locked targets."), + target = src, + ) + return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING + return ..() + +/obj/item/stream_projector/attack_mob(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) + // flatly don't emit the attack if not in harm + if(intent == INTENT_HARM) + return ..() + return NONE + +/obj/item/stream_projector/afterattack(atom/target, mob/user, clickchain_flags, list/params) + if(!valid_target(target)) + return ..() + . = CLICKCHAIN_DO_NOT_PROPAGATE + if(active_targets?[target]) + try_drop_target(target, new /datum/event_args/actor(user)) + else + try_lock_target(target, new /datum/event_args/actor(user)) + +/** + * used to potentially redirect target before lock-on completes; useful for things like holofabricators + */ +/obj/item/stream_projector/proc/transform_target_lock(atom/target) + return target + +/obj/item/stream_projector/process(delta_time) + return // don't process_kill by default + +/** + * checks if a target is valid to be locked at all + */ +/obj/item/stream_projector/proc/valid_target(atom/entity) + return istype(entity) + +/** + * checks if we currently can lock onto a target + */ +/obj/item/stream_projector/proc/can_target(atom/entity, datum/event_args/actor/actor, silent) + return TRUE + +/** + * immediately lock to target + */ +/obj/item/stream_projector/proc/lock_target(atom/entity) + SHOULD_NOT_OVERRIDE(TRUE) + entity = transform_target_lock(entity) + if(isnull(entity)) + return FALSE + if(active_targets?[entity]) + return FALSE + LAZYSET(active_targets, entity, TRUE) + setup_target_visuals(entity) + on_target_add(entity) + if(process_while_active && !process_always) + // START_PROCESSING checks for DF_ISPROCESSING already + START_PROCESSING(SSprocessing, src) + if(ismovable(entity)) + RegisterSignal(entity, COMSIG_MOVABLE_MOVED, PROC_REF(on_target_moved)) + return TRUE + +/** + * setup visuals to target + */ +/obj/item/stream_projector/proc/setup_target_visuals(atom/entity) + return + +/** + * teardown visuals to target + */ +/obj/item/stream_projector/proc/teardown_target_visuals(atom/entity) + return + +/** + * do target removal logic here + */ +/obj/item/stream_projector/proc/on_target_add(atom/entity) + return + +/** + * do target removal logic here + */ +/obj/item/stream_projector/proc/on_target_remove(atom/entity) + return + +/** + * this proc should include all instant checks + */ +/obj/item/stream_projector/proc/try_lock_target(atom/entity, datum/event_args/actor/actor, silent) + // check if we're already locking that target + if(active_targets?[entity]) + return FALSE + + // check if it's remotely the right type + if(!valid_target(entity)) + return FALSE + + // check if we can target + if(!can_target(entity, actor, silent)) + return FALSE + + if(!perform_try_lock_target(entity, actor, silent)) + return FALSE + + return lock_target(entity) + +/** + * this proc can block and should include blocking checks + */ +/obj/item/stream_projector/proc/perform_try_lock_target(atom/entity, datum/event_args/actor/actor, silent) + return TRUE + +/obj/item/stream_projector/proc/try_drop_target(atom/entity, datum/event_args/actor/actor, silent) + return drop_target(entity) + +/obj/item/stream_projector/proc/drop_target(atom/entity) + SHOULD_NOT_OVERRIDE(TRUE) + if(!active_targets?[entity]) + return FALSE + teardown_target_visuals(entity) + on_target_remove(entity) + LAZYREMOVE(active_targets, entity) + if(!length(active_targets) && process_while_active && !process_always) + STOP_PROCESSING(SSprocessing, src) + return TRUE + +/obj/item/stream_projector/proc/on_target_moved(atom/movable/source) + // notice how we specifically don't check for being on a turf + // if you want that, you have to check on subtypes (or add a var to base type later) + if(get_z(source) != get_z(src)) + drop_target(source) + +/obj/item/stream_projector/proc/drop_all_targets() + for(var/atom/entity as anything in active_targets) + drop_target(entity) diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/crowbar.dm index 9c6aa40a2934..8a380093352e 100644 --- a/code/game/objects/items/tools/crowbar.dm +++ b/code/game/objects/items/tools/crowbar.dm @@ -94,12 +94,6 @@ damage_force = 10 tool_speed = 0.5 -/obj/item/tool/crowbar/RIGset - name = "integrated prybar" - desc = "If you're seeing this, someone did a dum-dum." - tool_sound = 'sound/items/jaws_pry.ogg' - tool_speed = 0.7 - /obj/item/tool/crowbar/power name = "jaws of life" desc = "A set of jaws of life, compressed through the magic of science. It's fitted with a prying head." diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm index d74fd0bd23af..a33fa64aa0d8 100644 --- a/code/game/objects/items/tools/screwdriver.dm +++ b/code/game/objects/items/tools/screwdriver.dm @@ -140,12 +140,6 @@ tool_sound = 'sound/items/drill_use.ogg' tool_speed = 0.5 -/obj/item/tool/screwdriver/RIGset - name = "integrated screwdriver" - desc = "If you're seeing this, someone did a dum-dum." - tool_sound = 'sound/items/drill_use.ogg' - tool_speed = 0.7 - /obj/item/tool/screwdriver/power name = "hand drill" desc = "A simple powered hand drill. It's fitted with a screw bit." diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index 377eb9947ca0..17fac6cef425 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -25,6 +25,8 @@ //Welding tool specific stuff var/welding = 0 //Whether or not the welding tool is off(0), on(1) or currently welding(2) + /// damage force when on (+ will be burning damage instead of brute) + var/damage_on = 15 var/status = 1 //Whether the welder is secured or unsecured (able to attach rods to it to make a flamethrower) var/max_fuel = 20 //The max amount of fuel the welder can hold @@ -250,7 +252,7 @@ else if(T) T.visible_message("\The [src] turns on.") playsound(loc, acti_sound, 50, 1) - src.damage_force = 15 + src.damage_force = damage_on src.damtype = "fire" src.set_weight_class(WEIGHT_CLASS_BULKY) src.attack_sound = 'sound/items/welder.ogg' @@ -272,7 +274,7 @@ else if(T) T.visible_message("\The [src] turns off.") playsound(loc, deac_sound, 50, 1) - src.damage_force = 3 + src.damage_force = initial(damage_force) src.damtype = "brute" src.set_weight_class(initial(src.w_class)) src.welding = 0 @@ -698,11 +700,6 @@ tool_speed = 0.5 -/obj/item/weldingtool/electric/mounted/RIGset - name = "arc welder" - tool_speed = 0.7 // Let's see if this works with RIGs - desc = "If you're seeing this, someone did a dum-dum." - /obj/item/weldingtool/electric/mounted/exosuit var/obj/item/mecha_parts/mecha_equipment/equip_mount = null flame_intensity = 1 diff --git a/code/game/objects/items/tools/wirecutters.dm b/code/game/objects/items/tools/wirecutters.dm index 227d6982cea9..b31bfbbbc349 100644 --- a/code/game/objects/items/tools/wirecutters.dm +++ b/code/game/objects/items/tools/wirecutters.dm @@ -107,12 +107,6 @@ tool_sound = 'sound/items/jaws_cut.ogg' tool_speed = 0.5 -/obj/item/tool/wirecutters/RIGset - name = "integrated wirecutters" - desc = "If you're seeing this, someone did a dum-dum." - tool_sound = 'sound/items/jaws_cut.ogg' - tool_speed = 0.7 - /obj/item/tool/wirecutters/hybrid name = "strange wirecutters" desc = "This cuts wires. With Science!" diff --git a/code/game/objects/items/tools/wrench.dm b/code/game/objects/items/tools/wrench.dm index b2beb981e201..317a8a92fde9 100644 --- a/code/game/objects/items/tools/wrench.dm +++ b/code/game/objects/items/tools/wrench.dm @@ -79,12 +79,6 @@ tool_speed = 0.5 random_color = FALSE -/obj/item/tool/wrench/RIGset - name = "integrated wrench" - desc = "If you're seeing this, someone did a dum-dum." - tool_sound = 'sound/items/drill_use.ogg' - tool_speed = 0.7 - /obj/item/tool/wrench/hybrid // Slower and bulkier than normal power tools, but it has the power of reach. name = "strange wrench" desc = "A wrench with many common uses. Can be usually found in your hand." diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index 541c78e76f45..af4d1581c3aa 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -207,7 +207,7 @@ var/true_delay = rcd_results[RCD_VALUE_DELAY] * tool_speed - var/datum/beam/rcd_beam = null + var/datum/beam_legacy/rcd_beam = null if(ranged) var/atom/movable/beam_origin = user // This is needed because mecha pilots are inside an object and the beam won't be made if it tries to attach to them.. if(!isturf(beam_origin.loc)) diff --git a/code/game/objects/items/weapons/barrier_tape.dm b/code/game/objects/items/weapons/barrier_tape.dm index 8eb40321c06d..e6259f2aa44d 100644 --- a/code/game/objects/items/weapons/barrier_tape.dm +++ b/code/game/objects/items/weapons/barrier_tape.dm @@ -320,7 +320,7 @@ var/list/tape_roll_applications = list() update_icon() name = "crumpled [name]" -/obj/item/barrier_tape_segment/Crossed(atom/movable/AM, oldloc) +/obj/item/barrier_tape_segment/Crossed(atom/movable/AM) . = ..() if(!lifted && isliving(AM)) var/mob/living/M = AM //so that ghosts don't get spammed diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 1b61869d85d2..8ece65538a85 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -31,6 +31,16 @@ /// nominal climb delay before modifiers var/climb_delay = 3.5 SECONDS + //* Coloration + /// coloration mode + var/coloration_mode = COLORATION_MODE_NONE + /// coloration: + /// in MULTIPLY / MATRIX, this is unused as we just use color var + /// in RG, RB, GB, RGB, OVERLAYS, GAGS modes, this is a concat'd string of #aabbcc#112233... for the n colors in this. + var/coloration + /// coloration amount - only used in COMPLEX modes that aren't MULTIPLY/MATRIX, null otherwise. + var/coloration_amount + //? Depth /// logical depth in pixels. people can freely run from high to low objects without being blocked. /// @@ -514,6 +524,90 @@ H.update_health() */ +//* Coloration *// + +/obj/proc/amount_coloration() + switch(coloration_mode) + if(COLORATION_MODE_NONE) + return 0 + if(COLORATION_MODE_MATRIX, COLORATION_MODE_MULTIPLY) + return 1 + if(COLORATION_MODE_RB_MATRIX) + return 2 + if(COLORATION_MODE_RG_MATRIX) + return 2 + if(COLORATION_MODE_GB_MATRIX) + return 2 + if(COLORATION_MODE_RGB_MATRIX) + return 3 + return coloration_amount + +/obj/proc/set_coloration_matrix(list/color_matrix) + if(coloration_mode != COLORATION_MODE_MATRIX) + return + color = color_matrix + // well, we can't pack a matrix :/ + coloration = null + +/obj/proc/set_coloration_parts(list/colors) + switch(coloration_mode) + if(COLORATION_MODE_MATRIX, COLORATION_MODE_MULTIPLY) + ASSERT(length(colors) == 1) + color = colors[1] + if(COLORATION_MODE_RB_MATRIX) + ASSERT(length(colors) == 2) + // todo: implement + pass() + if(COLORATION_MODE_GB_MATRIX) + ASSERT(length(colors) == 2) + // todo: implement + pass() + if(COLORATION_MODE_RB_MATRIX) + ASSERT(length(colors) == 2) + // todo: implement + pass() + if(COLORATION_MODE_RGB_MATRIX) + ASSERT(length(colors) == 3) + // todo: implement + pass() + if(COLORATION_MODE_OVERLAYS) + ASSERT(length(colors) == coloration_amount) + // todo: implement; we'll probably have to hook both update_overlays as well as + // todo: something in [code/modules/mob/inventory/rendering.dm]. + pass() + if(COLORATION_MODE_NONE) + // why are we here? + ASSERT(!length(colors)) + color = null + coloration = pack_coloration_string(colors) + + +/obj/proc/set_coloration_packed(packed_colors) + var/list/unpacked = unpack_coloration_string(packed_colors) + return set_coloration_parts(unpacked) + +/obj/proc/get_coloration_parts() + if(!(coloration_mode & COLORATION_MODES_COMPLEX)) + switch(coloration_mode) + if(COLORATION_MODE_MULTIPLY) + // matrices are unsupported + return list(islist(color)? "#ffffff" : color) + if(COLORATION_MODE_MATRIX) + // unsupported + return list("#ffffff") + return list() + return unpack_coloration_string(coloration) + +/obj/proc/get_coloration_packed() + switch(coloration_mode) + if(COLORATION_MODE_MULTIPLY) + // matrices are unsupported + return islist(color)? "#ffffff" : color + if(COLORATION_MODE_MATRIX) + // unsupported + return "#ffffff" + return coloration + //* Context *// /obj/context_query(datum/event_args/actor/e_args) diff --git a/code/game/objects/structures/props/swarm.dm b/code/game/objects/structures/props/swarm.dm index 5eff6bf6f4b6..ddba8865426f 100644 --- a/code/game/objects/structures/props/swarm.dm +++ b/code/game/objects/structures/props/swarm.dm @@ -27,7 +27,7 @@ active_beams = list() /obj/structure/cult/pylon/swarm/Destroy() - for(var/datum/beam/B in active_beams) + for(var/datum/beam_legacy/B in active_beams) QDEL_NULL(B) active_beams = null ..() @@ -89,13 +89,13 @@ for(var/mob/living/silicon/robot/drone/swarm/S in view(3, src)) var/has_beam = FALSE - for(var/datum/beam/B in active_beams) + for(var/datum/beam_legacy/B in active_beams) if(B.target == S) has_beam = TRUE break if(!has_beam) - active_beams |= Beam(S,icon='icons/effects/beam.dmi',icon_state="holo_beam",time=3 SECONDS,maxdistance=3,beam_type = /obj/effect/ebeam) + active_beams |= Beam(S,icon='icons/effects/beam.dmi',icon_state="holo1_scaling",time=3 SECONDS,maxdistance=3,beam_type = /obj/effect/ebeam) if(S.cell) S.cell.give(rand(5, 30)) diff --git a/code/game/objects/systems/_system.dm b/code/game/objects/systems/_system.dm index bdedfe22a3b3..e6f3466145a6 100644 --- a/code/game/objects/systems/_system.dm +++ b/code/game/objects/systems/_system.dm @@ -6,8 +6,6 @@ * * components are just terrible API, inefficient, and obnoxious sometimes * /obj systems are the replacement for stuff like storage, cell slots, etc - * - * they are singletons on /obj level. */ /datum/object_system abstract_type = /datum/object_system diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm index 00099001805e..94089a1a4213 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm @@ -261,6 +261,12 @@ . += T return pick(.) +/proc/_filter(...) + return filter(arglist(args)) + +/proc/_generator(...) + return generator(arglist(args)) + /proc/_url_encode(str) return url_encode(str) diff --git a/code/modules/asset_cache/assets/items/holofabricator.dm b/code/modules/asset_cache/assets/items/holofabricator.dm new file mode 100644 index 000000000000..e00fba4d348a --- /dev/null +++ b/code/modules/asset_cache/assets/items/holofabricator.dm @@ -0,0 +1,5 @@ +/datum/asset/spritesheet/holofabricator + name = "holofabricator" + +/datum/asset/spritesheet/holofabricator/create_spritesheets() + #warn InsertAll stuff lmao diff --git a/code/modules/atmospherics/machinery/air_alarm.dm b/code/modules/atmospherics/machinery/air_alarm.dm index 96a13304af28..93feb3f80038 100644 --- a/code/modules/atmospherics/machinery/air_alarm.dm +++ b/code/modules/atmospherics/machinery/air_alarm.dm @@ -764,6 +764,9 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/air_alarm, 26) spawn(rand(0,15)) update_icon() +#undef MAX_TEMPERATURE +#undef MIN_TEMPERATURE + #undef LOAD_TLV_VALUES #undef TEST_TLV_VALUES #undef DECLARE_TLV_VALUES diff --git a/code/modules/automata/sonar.dm b/code/modules/automata/sonar.dm index 58b80ec5f52e..b5302528dc06 100644 --- a/code/modules/automata/sonar.dm +++ b/code/modules/automata/sonar.dm @@ -7,7 +7,7 @@ /// renderer anchor var/turf/rendering_anchor /// this tick's renderer - var/atom/movable/rendering + var/atom/movable/graphics_render/rendering /// cached rendering anchor x var/anchoring_x /// cached rendering anchor y @@ -68,7 +68,7 @@ overlay.layer = MANGLE_PLANE_AND_LAYER(AM.plane, AM.layer) rendering_overlays += overlay -/datum/automata/wave/sonar/proc/flick_renderer(atom/movable/renderer) +/datum/automata/wave/sonar/proc/flick_renderer(atom/movable/graphics_render/renderer) if(isnull(renderer)) return renderer.overlays += rendering_overlays diff --git a/code/modules/cargo/supplypacks/medical.dm b/code/modules/cargo/supplypacks/medical.dm index faea4af16db4..4d8195b71cfc 100644 --- a/code/modules/cargo/supplypacks/medical.dm +++ b/code/modules/cargo/supplypacks/medical.dm @@ -424,24 +424,22 @@ access = ACCESS_MEDICAL_EQUIPMENT /datum/supply_pack/med/medigun - name = "Cell-Loaded Medigun crate" + name = "Medichine Stream Projector" contains = list( - /obj/item/gun/ballistic/cell_loaded/medical = 1, - /obj/item/ammo_magazine/cell_mag/medical = 1 - ) - cost = 250 + /obj/item/stream_projector/medichine = 1, + ) + cost = 50 container_type = /obj/structure/closet/crate/secure/corporate/veymed - container_name = "Cell-Loaded Medigun crate" - access = ACCESS_MEDICAL_CMO + access = ACCESS_MEDICAL_EQUIPMENT -/datum/supply_pack/med/medigun_cells - name = "Cell-Loaded Medigun Cell Pack crate" +/datum/supply_pack/med/medigun + name = "Medichine Stream Cells (Assorted)" contains = list( - /obj/item/ammo_casing/microbattery/medical/brute = 3, - /obj/item/ammo_casing/microbattery/medical/burn = 3, - /obj/item/ammo_casing/microbattery/medical/stabilize = 3 - ) - cost = 100 + /obj/item/medichine_cell/seal_wounds = 4, + /obj/item/medichine_cell/seal_wounds/violently = 2, + /obj/item/medichine_cell/stabilize = 4, + /obj/item/medichine_cell/synth_repair = 4, + ) + cost = 50 container_type = /obj/structure/closet/crate/secure/corporate/veymed - container_name = "Cell-Loaded Medigun Cell Pack crate" - access = ACCESS_MEDICAL_CMO + access = ACCESS_MEDICAL_EQUIPMENT diff --git a/code/modules/catalogue/cataloguer.dm b/code/modules/catalogue/cataloguer.dm index fa12bc6f348d..a4590e89ff55 100644 --- a/code/modules/catalogue/cataloguer.dm +++ b/code/modules/catalogue/cataloguer.dm @@ -102,7 +102,7 @@ GLOBAL_LIST_EMPTY(all_cataloguers) // Start the special effects. busy = TRUE update_icon() - var/datum/beam/scan_beam = user.Beam(target, icon_state = "rped_upgrade", time = scan_delay) + var/datum/beam_legacy/scan_beam = user.Beam(target, icon_state = "rped_upgrade", time = scan_delay) var/filter = filter(type = "outline", size = 1, color = "#FFFFFF") target.filters += filter var/list/box_segments = list() diff --git a/code/modules/client/viewport.dm b/code/modules/client/viewport.dm index dad6f2b9bed0..c5f43a0f9c0d 100644 --- a/code/modules/client/viewport.dm +++ b/code/modules/client/viewport.dm @@ -44,6 +44,21 @@ temporary_viewsize_width = null request_viewport_update() +/** + * checks if something is roughly within distance of our view range + * + * this is not accurate, does not take into account opacity, non-square views. + * + * todo: take into account pixel shifting (pixel_x/y) of client viewport + */ +/client/proc/roughly_within_render_distance(atom/target) + if(isnull(eye)) + return FALSE + target = get_turf(target) + if(isnull(target)) + return FALSE + return get_dist(target, eye) < CEILING(max(current_viewport_height, current_viewport_width) / 2, 1) + // these two variables control max dynamic resize for viewport GLOBAL_VAR_INIT(max_client_view_x, 19) GLOBAL_VAR_INIT(max_client_view_y, 15) diff --git a/code/modules/fishing/equipment/rod.dm b/code/modules/fishing/equipment/rod.dm index 017c8a110f98..6a68fec3b4d0 100644 --- a/code/modules/fishing/equipment/rod.dm +++ b/code/modules/fishing/equipment/rod.dm @@ -30,7 +30,7 @@ var/adminbus_hooking = FALSE /// Fishing line visual for the hooked item - var/datum/beam/hooked_item_fishing_line + var/datum/beam_legacy/hooked_item_fishing_line /// Are we currently casting var/casting = FALSE @@ -112,12 +112,12 @@ if(!istype(user)) return var/beam_color = line?.line_color || default_line_color - var/datum/beam/fishing_line/fishing_line_beam = new(user, target, icon_state = "fishing_line", beam_color = beam_color, override_target_pixel_y = target_py) + var/datum/beam_legacy/fishing_line/fishing_line_beam = new(user, target, icon_state = "gray_pixel_line_scaling", beam_color = beam_color, override_target_pixel_y = target_py) fishing_line_beam.lefthand = user.get_held_index(src) % 2 == 1 RegisterSignal(fishing_line_beam, COMSIG_BEAM_BEFORE_DRAW, PROC_REF(check_los)) RegisterSignal(fishing_line_beam, COMSIG_PARENT_QDELETING, PROC_REF(clear_line)) fishing_lines += fishing_line_beam - INVOKE_ASYNC(fishing_line_beam, TYPE_PROC_REF(/datum/beam/, Start)) + INVOKE_ASYNC(fishing_line_beam, TYPE_PROC_REF(/datum/beam_legacy/, Start)) update_worn_icon() return fishing_line_beam @@ -130,7 +130,7 @@ . = ..() if(currently_hooked_item) clear_hooked_item() - for(var/datum/beam/fishing_line in fishing_lines) + for(var/datum/beam_legacy/fishing_line in fishing_lines) SEND_SIGNAL(fishing_line, COMSIG_FISHING_LINE_SNAPPED) QDEL_LIST(fishing_lines) @@ -176,7 +176,7 @@ currently_hooked_item = null // Checks fishing line for interruptions and range -/obj/item/fishing_rod/proc/check_los(datum/beam/source) +/obj/item/fishing_rod/proc/check_los(datum/beam_legacy/source) SIGNAL_HANDLER . = NONE @@ -448,26 +448,26 @@ #undef ROD_SLOT_LINE #undef ROD_SLOT_HOOK -/datum/beam/fishing_line +/datum/beam_legacy/fishing_line // Is the fishing rod held in left side hand var/lefthand = FALSE -/datum/beam/fishing_line/Start() +/datum/beam_legacy/fishing_line/Start() update_offsets(origin.dir) . = ..() RegisterSignal(origin, COMSIG_ATOM_DIR_CHANGE, PROC_REF(handle_dir_change)) -/datum/beam/fishing_line/Destroy() +/datum/beam_legacy/fishing_line/Destroy() SEND_SIGNAL(src, COMSIG_FISHING_LINE_SNAPPED) UnregisterSignal(origin, COMSIG_ATOM_DIR_CHANGE) . = ..() -/datum/beam/fishing_line/proc/handle_dir_change(atom/movable/source, olddir, newdir) +/datum/beam_legacy/fishing_line/proc/handle_dir_change(atom/movable/source, olddir, newdir) SIGNAL_HANDLER update_offsets(newdir) - INVOKE_ASYNC(src, TYPE_PROC_REF(/datum/beam/, redrawing)) + INVOKE_ASYNC(src, TYPE_PROC_REF(/datum/beam_legacy/, redrawing)) -/datum/beam/fishing_line/proc/update_offsets(user_dir) +/datum/beam_legacy/fishing_line/proc/update_offsets(user_dir) switch(user_dir) if(SOUTH) override_origin_pixel_x = lefthand ? lefthand_s_px : righthand_s_px @@ -483,7 +483,7 @@ override_origin_pixel_y = lefthand ? lefthand_n_py : righthand_n_py // Make these inline with final sprites -/datum/beam/fishing_line +/datum/beam_legacy/fishing_line var/righthand_s_px = 13 var/righthand_s_py = 16 diff --git a/code/modules/fishing/minigame/fishing_minigame.dm b/code/modules/fishing/minigame/fishing_minigame.dm index 78a914fd5211..9d657c102d91 100644 --- a/code/modules/fishing/minigame/fishing_minigame.dm +++ b/code/modules/fishing/minigame/fishing_minigame.dm @@ -37,7 +37,7 @@ var/max_distance = 5 /// Fishing line visual - var/datum/beam/fishing_line + var/datum/beam_legacy/fishing_line /datum/fishing_challenge/New(atom/spot, reward_path, obj/item/fishing_rod/rod, mob/user) src.user = user diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 6dcd35967de5..311299e17e7c 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -248,7 +248,7 @@ proc/check_panel(mob/M) src.health -= P.damage_force -/obj/effect/fake_attacker/Crossed(var/mob/M, somenumber) +/obj/effect/fake_attacker/Crossed(var/mob/M) . = ..() if(M == my_target) step_away(src,my_target,2) diff --git a/code/modules/hardsuits/_rig.dm b/code/modules/hardsuits/_rig.dm index f2101ac3f0df..f193cfaa82f5 100644 --- a/code/modules/hardsuits/_rig.dm +++ b/code/modules/hardsuits/_rig.dm @@ -39,7 +39,7 @@ // Activation /// activation state - var/activation_state = RIG_ACTIVATION_OFF + var/activation_state = HARDSUIT_ACTIVATION_OFF /// last online, set in process() var/last_online = FALSE @@ -323,7 +323,7 @@ /obj/item/hardsuit/proc/reset() - set_activation_state(RIG_ACTIVATION_OFF) + set_activation_state(HARDSUIT_ACTIVATION_OFF) REMOVE_TRAIT(src, TRAIT_ITEM_NODROP, RIG_TRAIT) //Reset the trap and upgrade it. Won't affect standard rigs. trapSprung = 0 @@ -407,7 +407,7 @@ M.client.screen += booting_R ADD_TRAIT(src, TRAIT_ITEM_NODROP, RIG_TRAIT) - set_activation_state(is_sealing? RIG_ACTIVATION_STARTUP : RIG_ACTIVATION_SHUTDOWN) + set_activation_state(is_sealing? HARDSUIT_ACTIVATION_STARTUP : HARDSUIT_ACTIVATION_SHUTDOWN) if(is_sealing && !suit_is_deployed()) M.visible_message("[M]'s suit flashes an error light.","Your suit flashes an error light. It can't function properly without being fully deployed.") @@ -493,10 +493,10 @@ // Success! if(is_sealing) - set_activation_state(RIG_ACTIVATION_ON) + set_activation_state(HARDSUIT_ACTIVATION_ON) ADD_TRAIT(src, TRAIT_ITEM_NODROP, RIG_TRAIT) else - set_activation_state(RIG_ACTIVATION_OFF) + set_activation_state(HARDSUIT_ACTIVATION_OFF) REMOVE_TRAIT(src, TRAIT_ITEM_NODROP, RIG_TRAIT) if(M.hud_used) diff --git a/code/modules/hardsuits/activation.dm b/code/modules/hardsuits/activation.dm index 48638f39c2ca..ec58ee83c370 100644 --- a/code/modules/hardsuits/activation.dm +++ b/code/modules/hardsuits/activation.dm @@ -6,16 +6,16 @@ activation_state = new_state /obj/item/hardsuit/proc/is_activated() - return activation_state == RIG_ACTIVATION_ON + return activation_state == HARDSUIT_ACTIVATION_ON /obj/item/hardsuit/proc/is_activating() - return activation_state == RIG_ACTIVATION_STARTUP + return activation_state == HARDSUIT_ACTIVATION_STARTUP /obj/item/hardsuit/proc/is_deactivating() - return activation_state == RIG_ACTIVATION_SHUTDOWN + return activation_state == HARDSUIT_ACTIVATION_SHUTDOWN /obj/item/hardsuit/proc/is_cycling() - return activation_state == RIG_ACTIVATION_STARTUP || activation_state == RIG_ACTIVATION_SHUTDOWN + return activation_state == HARDSUIT_ACTIVATION_STARTUP || activation_state == HARDSUIT_ACTIVATION_SHUTDOWN /** * online - whether or not the hardsuit is semantically online. a completely depowered suit can be activated but not online. diff --git a/code/modules/hardsuits/modules/utility.dm b/code/modules/hardsuits/modules/utility.dm index 621dd28c09f5..ccd5dfe3d93d 100644 --- a/code/modules/hardsuits/modules/utility.dm +++ b/code/modules/hardsuits/modules/utility.dm @@ -689,11 +689,11 @@ /obj/item/hardsuit_module/device/toolset/Initialize(mapload) . = ..() - intcrowbar = new /obj/item/tool/crowbar/RIGset(src) - intwrench = new /obj/item/tool/wrench/RIGset(src) - intcutter = new /obj/item/tool/wirecutters/RIGset(src) - intdriver = new /obj/item/tool/screwdriver/RIGset(src) - //intwelder = new /obj/item/weldingtool/electric/mounted/RIGset(src) + intcrowbar = new /obj/item/tool/crowbar/rig_basic(src) + intwrench = new /obj/item/tool/wrench/rig_basic(src) + intcutter = new /obj/item/tool/wirecutters/rig_basic(src) + intdriver = new /obj/item/tool/screwdriver/rig_basic(src) + //intwelder = new /obj/item/weldingtool/electric/mounted/rig_basic(src) device = intcrowbar /obj/item/hardsuit_module/device/toolset/engage(atom/target) @@ -724,4 +724,4 @@ interface_name = "Integrated arc-welder" interface_desc = "A hardsuit-mounted electrical welder. Smells of ozone." engage_string = "Engage/Disengage" - device_type = /obj/item/weldingtool/electric/mounted/RIGset + device_type = /obj/item/weldingtool/electric/mounted/rig_basic diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm index 227313e13da3..1d4d583e7d6f 100644 --- a/code/modules/hydroponics/seed_datums.dm +++ b/code/modules/hydroponics/seed_datums.dm @@ -1272,7 +1272,9 @@ seed_name = "grass" display_name = "grass" kitchen_tag = "grass" - mutants = "carpet" + mutants = list( + "carpet", + ) chems = list("nutriment" = list(1,20)) /datum/seed/grass/New() diff --git a/code/modules/instruments/instruments/item.dm b/code/modules/instruments/instruments/item.dm index a4fb544ec222..d4fbca788cd1 100644 --- a/code/modules/instruments/instruments/item.dm +++ b/code/modules/instruments/instruments/item.dm @@ -78,9 +78,9 @@ */ /obj/item/instrument/interact(mob/user) - nano_ui_interact(user) + ui_interact(user) -/obj/item/instrument/nano_ui_interact(mob/living/user) +/obj/item/instrument/ui_interact(mob/living/user) if(!isliving(user) || user.stat || user.restrained()) return diff --git a/code/modules/instruments/instruments/stationary.dm b/code/modules/instruments/instruments/stationary.dm index 390f10f10f11..e1351fc18cd5 100644 --- a/code/modules/instruments/instruments/stationary.dm +++ b/code/modules/instruments/instruments/stationary.dm @@ -29,11 +29,11 @@ return if(!anchored) return - nano_ui_interact(user) + ui_interact(user) -/obj/structure/musician/nano_ui_interact(mob/user) - . = ..() - song.nano_ui_interact(user) +/obj/structure/musician/ui_interact(mob/user) + song.ui_interact(user) + return TRUE /obj/structure/musician/attackby(obj/item/I, mob/living/user) if(I.is_wrench()) diff --git a/code/modules/instruments/songs/editor.dm b/code/modules/instruments/songs/editor.dm index 18b5682cc2dd..bc3479748de6 100644 --- a/code/modules/instruments/songs/editor.dm +++ b/code/modules/instruments/songs/editor.dm @@ -27,7 +27,7 @@ . += "Sustain indefinitely last held note: [full_sustain_held_note? "Enabled" : "Disabled"].
" . += "" -/datum/song/nano_ui_interact(mob/user) +/datum/song/ui_interact(mob/user) var/list/dat = list() dat += instrument_status_ui() diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm index 5b41b5cf0def..cd753d8adb64 100644 --- a/code/modules/integrated_electronics/subtypes/output.dm +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -411,7 +411,7 @@ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH var/obj/effect/overlay/holographic/hologram = null // Reference to the hologram effect, and also used to see if component is active. var/icon/holo_base = null // Uncolored holographic icon. -// var/datum/beam/holo_beam = null // A visual effect, to make it easy to know where a hologram is coming from. +// var/datum/beam_legacy/holo_beam = null // A visual effect, to make it easy to know where a hologram is coming from. // It is commented out due to picking up the assembly killing the beam. /obj/item/integrated_circuit/output/holographic_projector/Destroy() diff --git a/code/modules/logging/logging.dm b/code/modules/logging/logging.dm index dd3ca429729d..75e9a0464305 100644 --- a/code/modules/logging/logging.dm +++ b/code/modules/logging/logging.dm @@ -26,7 +26,7 @@ * todo: log initiator */ /proc/log_construction(datum/event_args/actor/e_args, atom/target, message) - log_game("CONSTRUCTION: [key_name(e_args.performer)] [COORD(e_args.performer)] -> [target] [COORD(target)]: [message]") + log_game("CONSTRUCTION: [key_name(e_args.performer)] [COORD(e_args.performer)] -> [target] [COORD(target)] ([ref(target)]): [message]") /** * log click - context menu diff --git a/code/modules/materials/definitions/special/clown_planet.dm b/code/modules/materials/definitions/special/clown_planet.dm index a0a81c5b60b7..28c2f7e1d334 100644 --- a/code/modules/materials/definitions/special/clown_planet.dm +++ b/code/modules/materials/definitions/special/clown_planet.dm @@ -5,7 +5,6 @@ icon_colour = "#fff127" explosion_resistance = 25 opacity = 0.8 - negation = 5 // boing. stack_origin_tech = list(TECH_MATERIAL = 5, TECH_ILLEGAL = 3) tgui_icon_key = "bananium" diff --git a/code/modules/materials/definitions/special/morphium.dm b/code/modules/materials/definitions/special/morphium.dm index b1a41f364c87..abb463ad5bcf 100644 --- a/code/modules/materials/definitions/special/morphium.dm +++ b/code/modules/materials/definitions/special/morphium.dm @@ -7,7 +7,6 @@ door_icon_base = "metal" icon_colour = "#37115A" shard_type = SHARD_SHARD - negation = 25 explosion_resistance = 85 stack_origin_tech = list(TECH_MATERIAL = 8, TECH_MAGNET = 8, TECH_PHORON = 6, TECH_BLUESPACE = 6, TECH_ARCANE = 3) diff --git a/code/modules/materials/definitions/special/valhollide.dm b/code/modules/materials/definitions/special/valhollide.dm index 5e0c69eb5673..dbe2c8d0dcbc 100644 --- a/code/modules/materials/definitions/special/valhollide.dm +++ b/code/modules/materials/definitions/special/valhollide.dm @@ -6,8 +6,6 @@ door_icon_base = "stone" icon_reinf = 'icons/turf/walls/reinforced_mesh.dmi' icon_colour = "##FFF3B2" - negation = 2 - spatial_instability = 30 stack_origin_tech = list(TECH_MATERIAL = 7, TECH_PHORON = 5, TECH_BLUESPACE = 5) sheet_singular_name = "gem" sheet_plural_name = "gems" diff --git a/code/modules/materials/definitions/special/verdantium.dm b/code/modules/materials/definitions/special/verdantium.dm index 25d227b3883b..3625966e47fa 100644 --- a/code/modules/materials/definitions/special/verdantium.dm +++ b/code/modules/materials/definitions/special/verdantium.dm @@ -7,7 +7,6 @@ door_icon_base = "metal" icon_colour = "#4FE95A" shard_type = SHARD_SHARD - negation = 15 stack_origin_tech = list(TECH_MATERIAL = 6, TECH_POWER = 5, TECH_BIO = 4) sheet_singular_name = "sheet" sheet_plural_name = "sheets" diff --git a/code/modules/materials/material.dm b/code/modules/materials/material.dm index 2c0819a97216..082a5d07eb9b 100644 --- a/code/modules/materials/material.dm +++ b/code/modules/materials/material.dm @@ -55,15 +55,24 @@ /// Will stacks made from this material pass their colors onto objects? var/pass_stack_colors = FALSE - //* Armor + //* Alloying *// + /// if set, we are considered a 'weak' allow; get_materials (when supported) will consider this material to be composite + /// and in many cases this material can even be broken down to materials during fabrication + var/list/weak_alloy + /// if set, we are considered a 'strong' allow; this doesn't separate properly, and while materials science + /// can use this to detect our alloy composition, we won't automatically break down during fabrication to + /// the component materials + var/list/strong_alloy + + //* Armor *// /// caching of armor. text2num(significance)_[mob_armor? 1 : 0] = armor datum instance var/tmp/list/armor_cache = list() - //* Attacks + //* Attacks *// /// melee stats cache. text2num(mode)_text2num(significance) = list(stats) var/tmp/list/melee_cache = list() - //* Attributes + //* Attributes *// /// relative HP multiplier for something made out of this var/relative_integrity = 1 @@ -128,13 +137,13 @@ /// * impacts acid armor var/relative_permeability = 1 - //* Flags + //* Flags *// /// material flags var/material_flags = NONE /// material constraint flags - what we are considered var/material_constraints = NONE - //* Traits + //* Traits *// /// Material traits - set to list of paths to instance on New / register; associate them to their initial data. var/list/material_traits /// Material trait sensitivity hooks - total @@ -151,11 +160,8 @@ var/opacity = 1 /// Only used by walls currently. var/explosion_resistance = 5 - /// Objects that respect this will randomly absorb impacts with this var as the percent chance. - var/negation = 0 - /// Objects that have trouble staying in the same physical space by sheer laws of nature have this. Percent for respecting items to cause teleportation. - var/spatial_instability = 0 /// If set, object matter var will be a list containing these values. + // todo: replace with weak_alloy and strong_alloy var/list/composite_material //! Placeholder vars for the time being, todo properly integrate windows/light tiles/rods. @@ -240,6 +246,7 @@ shard_icon = shard_type init_traits() + init_alloys() return TRUE @@ -266,6 +273,10 @@ trait.deserialize(data_list["trait"]) material_traits[trait] = data_list["data"] +/datum/material/clone() + var/datum/material/cloned = new type + return cloned + /// This is a placeholder for proper integration of windows/windoors into the system. /datum/material/proc/build_windows(mob/living/user, obj/item/stack/used_stack) return FALSE @@ -300,7 +311,27 @@ /datum/material/proc/wall_touch_special(turf/simulated/wall/W, mob/living/L) return -//* traits & trait hooks +//* Alloys *// + +/datum/material/proc/init_alloys() + for(var/i in 1 to length(weak_alloy)) + var/key = weak_alloy[i] + var/value = weak_alloy[key] + if(ispath(key)) + var/datum/material/casted = key + key = initial(casted.id) + weak_alloy[i] = key + weak_alloy[key] = value + for(var/i in 1 to length(strong_alloy)) + var/key = strong_alloy[i] + var/value = strong_alloy[key] + if(ispath(key)) + var/datum/material/casted = key + key = initial(casted.id) + strong_alloy[i] = key + strong_alloy[key] = value + +//* traits & trait hooks *// /datum/material/proc/init_traits() for(var/i in 1 to length(material_traits)) diff --git a/code/modules/materials/traits/glow.dm b/code/modules/materials/traits/glow.dm index 0ffbe378fcab..4316a509bbbb 100644 --- a/code/modules/materials/traits/glow.dm +++ b/code/modules/materials/traits/glow.dm @@ -16,7 +16,7 @@ /datum/material_trait/glow/on_add(atom/host, existing_data, our_data) . = ..() - var/atom/movable/render/material_glow/renderer + var/atom/movable/graphics_render/material_glow/renderer if(isnull(existing_data)) renderer = new host.contents += renderer @@ -26,11 +26,11 @@ /datum/material_trait/glow/on_remove(atom/host, existing_data, our_data, destroying) . = ..() - qdel(locate(/atom/movable/render/material_glow) in host.contents) + qdel(locate(/atom/movable/graphics_render/material_glow) in host.contents) -/atom/movable/render/material_glow +/atom/movable/graphics_render/material_glow -/atom/movable/render/material_glow/doMove(atom/destination) +/atom/movable/graphics_render/material_glow/doMove(atom/destination) if(!QDESTROYING(src)) CRASH("something tried to move us") return ..() diff --git a/code/modules/mob/characteristics/holder.dm b/code/modules/mob/characteristics/holder.dm index 9ce0b73f00a6..8f341760a1d6 100644 --- a/code/modules/mob/characteristics/holder.dm +++ b/code/modules/mob/characteristics/holder.dm @@ -131,10 +131,7 @@ add_talent(id) return TRUE -/** - * clones - */ -/datum/characteristics_holder/proc/clone() +/datum/characteristics_holder/clone() RETURN_TYPE(/datum/characteristics_holder) var/datum/characteristics_holder/cloning = new cloning.skills = skills.Copy() diff --git a/code/modules/mob/health.dm b/code/modules/mob/health.dm index f6f263f0f296..881c137d2aa2 100644 --- a/code/modules/mob/health.dm +++ b/code/modules/mob/health.dm @@ -1,3 +1,5 @@ +//* Critical *// + /** * are we in critical? * @@ -6,12 +8,16 @@ /mob/proc/is_in_critical() return FALSE +//* Health *// + /** * update health */ /mob/proc/update_health() return +//* Stat *// + /** * update stat, return new stat * @@ -52,6 +58,8 @@ update_hud_med_status() return TRUE +//* Revival & Rejuvenation *// + /** * brings a mob back to life * diff --git a/code/modules/mob/inventory/items.dm b/code/modules/mob/inventory/items.dm index c05f784e2137..b749edd2b990 100644 --- a/code/modules/mob/inventory/items.dm +++ b/code/modules/mob/inventory/items.dm @@ -33,8 +33,6 @@ */ /obj/item/proc/equipped(mob/user, slot, flags) SHOULD_CALL_PARENT(TRUE) - SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot, flags) - SEND_SIGNAL(user, COMSIG_MOB_ITEM_EQUIPPED, user, slot, flags) worn_slot = slot if(!(flags & INV_OP_IS_ACCESSORY)) // todo: shouldn't be in here @@ -57,6 +55,9 @@ encumbrance_registered = get_encumbrance() L.adjust_current_carry_encumbrance(encumbrance_registered) + SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot, flags) + SEND_SIGNAL(user, COMSIG_MOB_ITEM_EQUIPPED, user, slot, flags) + /** * called when an item is unequipped from inventory or moved around in inventory * @@ -67,8 +68,6 @@ */ /obj/item/proc/unequipped(mob/user, slot, flags) SHOULD_CALL_PARENT(TRUE) - SEND_SIGNAL(src, COMSIG_ITEM_UNEQUIPPED, user, slot, flags) - SEND_SIGNAL(user, COMSIG_MOB_ITEM_UNEQUIPPED, user, slot, flags) worn_slot = null if(!(flags & INV_OP_IS_ACCESSORY)) // todo: shouldn't be in here @@ -88,6 +87,9 @@ L.adjust_current_carry_encumbrance(-encumbrance_registered) encumbrance_registered = null + SEND_SIGNAL(src, COMSIG_ITEM_UNEQUIPPED, user, slot, flags) + SEND_SIGNAL(user, COMSIG_MOB_ITEM_UNEQUIPPED, user, slot, flags) + /** * called when a mob drops an item * @@ -114,7 +116,7 @@ . = SEND_SIGNAL(src, COMSIG_ITEM_DROPPED, user, flags, newLoc) SEND_SIGNAL(user, COMSIG_MOB_ITEM_DROPPED, src, flags, newLoc) - if(!(flags & INV_OP_SUPPRESS_SOUND) && isturf(newLoc) && !(. & COMPONENT_ITEM_DROPPED_SUPPRESS_SOUND)) + if(!(flags & INV_OP_SUPPRESS_SOUND) && isturf(newLoc) && !(. & COMPONENT_ITEM_INV_OP_SUPPRESS_SOUND)) playsound(src, drop_sound, 30, ignore_walls = FALSE) // user?.update_equipment_speed_mods() if(zoom) @@ -138,7 +140,7 @@ continue user.unregister_shieldcall(shieldcall) - return ((. & COMPONENT_ITEM_DROPPED_RELOCATE)? ITEM_RELOCATED_BY_DROPPED : NONE) + return ((. & COMPONENT_ITEM_INV_OP_RELOCATE)? ITEM_RELOCATED_BY_DROPPED : NONE) /** * called when a mob picks up an item diff --git a/code/modules/mob/life.dm b/code/modules/mob/life.dm index 4b910cb7f72e..54bab44080f3 100644 --- a/code/modules/mob/life.dm +++ b/code/modules/mob/life.dm @@ -53,3 +53,13 @@ * handle modifiers - physical/biological life haltedd is passed in */ /mob/proc/handle_modifiers(component_signal) + // todo: nuke this probably i think status effects is fine idk??? + + +//* Metabolism *// + +/** + * Force x seconds of metabolism. + */ +/mob/proc/forced_metabolism(seconds) + return diff --git a/code/modules/mob/living/carbon/cpr.dm b/code/modules/mob/living/carbon/cpr.dm index 3aedb9610009..fdf1c9d63d14 100644 --- a/code/modules/mob/living/carbon/cpr.dm +++ b/code/modules/mob/living/carbon/cpr.dm @@ -84,7 +84,6 @@ ingested?.metabolize(strength, TRUE) touching?.metabolize(strength, TRUE) - /mob/living/carbon/proc/cpr_act(atom/actor) var/clipping = HAS_TRAIT(src, TRAIT_CPR_COOLDOWN) diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index 0f6d2d9f2c61..3fef015bcaa2 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -1,6 +1,24 @@ //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2023 Citadel Station developers. *// +//* Damage *// + +/mob/living/carbon/heal_brute_loss(amount) + return heal_overall_damage(amount, 0) + +/mob/living/carbon/heal_fire_loss(amount) + return heal_overall_damage(0, amount) + +/mob/living/carbon/heal_tox_loss(amount) + . = toxloss + toxloss -= amount + return . - toxloss + +/mob/living/carbon/heal_oxy_loss(amount) + . = oxyloss + oxyloss -= amount + return . - oxyloss + //* Raw Damage *// /mob/living/carbon/take_random_targeted_damage(brute, burn, damage_mode, weapon_descriptor, defer_updates) diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/damage_procs.dm similarity index 98% rename from code/modules/mob/living/carbon/human/human_damage.dm rename to code/modules/mob/living/carbon/human/damage_procs.dm index 7a74efb5c0c0..65fb2e61fe55 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/damage_procs.dm @@ -298,9 +298,11 @@ //Heal MANY external organs, in random order //'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs +// todo: this is awful, should properly spread it out along with take_overall_damage. /mob/living/carbon/human/heal_overall_damage(var/brute, var/burn, var/include_robo) var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) + . = 0 var/update = 0 while(parts.len && (brute>0 || burn>0) ) var/obj/item/organ/external/picked = pick(parts) @@ -312,6 +314,8 @@ brute -= (brute_was-picked.brute_dam) burn -= (burn_was-picked.burn_dam) + // todo: this is dumb + . += (brute_was-picked.brute_dam) + (burn_was-picked.burn_dam) parts -= picked update_health() diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm new file mode 100644 index 000000000000..687b5be71b34 --- /dev/null +++ b/code/modules/mob/living/carbon/life.dm @@ -0,0 +1,9 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/mob/living/carbon/forced_metabolism(seconds) + . = ..() + + bloodstr?.metabolize(seconds, TRUE) + ingested?.metabolize(seconds, TRUE) + touching?.metabolize(seconds, TRUE) diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 058777a602f1..817ff8797bd2 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -107,6 +107,61 @@ // todo: refactor above +//* Afflictions *// + +/** + * inflicts radiation. + * will not heal it. + * + * @params + * - amt - how much + * - check_armor - do'th we care about armor? + * - def_zone - zone to check if we do + */ +/mob/living/proc/afflict_radiation(amt, run_armor, def_zone) + if(amt <= 0) + return + if(run_armor) + amt *= 1 - ((legacy_mob_armor(def_zone, ARMOR_RAD)) / 100) + radiation += max(0, RAD_MOB_ADDITIONAL(amt, radiation)) + +/** + * heals radiation. + * + * @params + * - amt - how much + */ +/mob/living/proc/cure_radiation(amt) + if(amt <= 0) + return + radiation = max(0, radiation - amt) + +//* Damage *// + +/** + * @return amount healed + */ +/mob/living/proc/heal_brute_loss(amount) + return 0 + +/** + * @return amount healed + */ +/mob/living/proc/heal_fire_loss(amount) + return 0 + +/** + * @return amount healed + */ +/mob/living/proc/heal_tox_loss(amount) + return 0 + +/** + * @return amount healed + */ +/mob/living/proc/heal_oxy_loss(amount) + return 0 + //* Raw Damage *// /** @@ -154,31 +209,209 @@ // todo: don't update health immediately . = adjustBruteLoss(brute) + adjustFireLoss(burn) -//? Afflictions -/** - * inflicts radiation. - * will not heal it. - * - * @params - * - amt - how much - * - check_armor - do'th we care about armor? - * - def_zone - zone to check if we do - */ -/mob/living/proc/afflict_radiation(amt, run_armor, def_zone) - if(amt <= 0) +//* Unsorted *// + +// Applies direct "cold" damage while checking protection against the cold. +/mob/living/proc/inflict_cold_damage(amount) + amount *= 1 - get_cold_protection(50) // Within spacesuit protection. + if(amount > 0) + adjustFireLoss(amount) + +// Ditto, but for "heat". +/mob/living/proc/inflict_heat_damage(amount) + amount *= 1 - get_heat_protection(10000) // Within firesuit protection. + if(amount > 0) + adjustFireLoss(amount) + +// and one for electricity because why not +/mob/living/proc/inflict_shock_damage(amount) + electrocute_act(amount, null, 1 - get_shock_protection(), pick(BP_HEAD, BP_TORSO, BP_GROIN)) + +// also one for water (most things resist it entirely, except for slimes) +/mob/living/proc/inflict_water_damage(amount) + amount *= 1 - get_water_protection() + if(amount > 0) + adjustToxLoss(amount) + +// one for abstracted away ""poison"" (mostly because simplemobs shouldn't handle reagents) +/mob/living/proc/inflict_poison_damage(amount) + if(isSynthetic()) return - if(run_armor) - amt *= 1 - ((legacy_mob_armor(def_zone, ARMOR_RAD)) / 100) - radiation += max(0, RAD_MOB_ADDITIONAL(amt, radiation)) + amount *= 1 - get_poison_protection() + if(amount > 0) + adjustToxLoss(amount) + +// heal ONE external organ, organ gets randomly selected from damaged ones. +/mob/living/proc/heal_organ_damage(var/brute, var/burn) + adjustBruteLoss(-brute) + adjustFireLoss(-burn) + src.update_health() /** - * heals radiation. - * - * @params - * - amt - how much + * @return amount healed */ -/mob/living/proc/cure_radiation(amt) - if(amt <= 0) - return - radiation = max(0, radiation - amt) +/mob/living/proc/heal_overall_damage(var/brute, var/burn) + adjustBruteLoss(-brute) + adjustFireLoss(-burn) + . = brute + burn + src.update_health() + +/mob/living/proc/getBruteLoss() + return bruteloss + +/mob/living/proc/getShockBruteLoss() //Only checks for things that'll actually hurt (not robolimbs) + return bruteloss + +/mob/living/proc/getActualBruteLoss() // Mostly for humans with robolimbs. + return getBruteLoss() + +//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs +/mob/living/proc/adjustBruteLoss(var/amount,var/include_robo) + if(status_flags & STATUS_GODMODE) return 0 //godmode + + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_brute_damage_percent)) + amount *= M.incoming_brute_damage_percent + else if(amount < 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + + bruteloss = min(max(bruteloss + amount, 0),(getMaxHealth()*2)) + update_health() + +/mob/living/proc/getOxyLoss() + return oxyloss + +/mob/living/proc/adjustOxyLoss(var/amount) + if(status_flags & STATUS_GODMODE) return 0 //godmode + + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_oxy_damage_percent)) + amount *= M.incoming_oxy_damage_percent + else if(amount < 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + + oxyloss = min(max(oxyloss + amount, 0),(getMaxHealth()*2)) + update_health() + +/mob/living/proc/setOxyLoss(var/amount) + if(status_flags & STATUS_GODMODE) return 0 //godmode + oxyloss = amount + +/mob/living/proc/getToxLoss() + return toxloss + +/mob/living/proc/adjustToxLoss(var/amount) + if(status_flags & STATUS_GODMODE) return 0 //godmode + + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_tox_damage_percent)) + amount *= M.incoming_tox_damage_percent + else if(amount < 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + + toxloss = min(max(toxloss + amount, 0),(getMaxHealth()*2)) + update_health() + +/mob/living/proc/setToxLoss(var/amount) + if(status_flags & STATUS_GODMODE) return 0 //godmode + toxloss = amount + +/mob/living/proc/getFireLoss() + return fireloss + +/mob/living/proc/getShockFireLoss() //Only checks for things that'll actually hurt (not robolimbs) + return fireloss + +/mob/living/proc/getActualFireLoss() // Mostly for humans with robolimbs. + return getFireLoss() + +//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs +/mob/living/proc/adjustFireLoss(var/amount,var/include_robo) + if(status_flags & STATUS_GODMODE) return 0 //godmode + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_fire_damage_percent)) + amount *= M.incoming_fire_damage_percent + else if(amount < 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + + fireloss = min(max(fireloss + amount, 0),(getMaxHealth()*2)) + update_health() + +/mob/living/proc/getCloneLoss() + return cloneloss + +/mob/living/proc/adjustCloneLoss(var/amount) + if(status_flags & STATUS_GODMODE) return 0 //godmode + + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_clone_damage_percent)) + amount *= M.incoming_clone_damage_percent + else if(amount < 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + + cloneloss = min(max(cloneloss + amount, 0),(getMaxHealth()*2)) + update_health() + +/mob/living/proc/setCloneLoss(var/amount) + if(status_flags & STATUS_GODMODE) return 0 //godmode + cloneloss = amount + +/mob/living/proc/getBrainLoss() + return brainloss + +/mob/living/proc/adjustBrainLoss(var/amount) + if(status_flags & STATUS_GODMODE) return 0 //godmode + brainloss = min(max(brainloss + amount, 0),(getMaxHealth()*2)) + +/mob/living/proc/setBrainLoss(var/amount) + if(status_flags & STATUS_GODMODE) return 0 //godmode + brainloss = amount + +/mob/living/proc/getHalLoss() + return halloss + +/mob/living/proc/adjustHalLoss(var/amount) + if(status_flags & STATUS_GODMODE) return 0 //godmode + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_hal_damage_percent)) + amount *= M.incoming_hal_damage_percent + if(!isnull(M.disable_duration_percent)) + amount *= M.incoming_hal_damage_percent + else if(amount < 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + halloss = min(max(halloss + amount, 0),(getMaxHealth()*2)) + update_health() +/mob/living/proc/setHalLoss(var/amount) + if(status_flags & STATUS_GODMODE) return 0 //godmode + halloss = amount diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 5c9b43483fa1..26d41034afe7 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -140,171 +140,6 @@ default behaviour is: // to_chat(world, "[src] ~ [src.bodytemperature] ~ [temperature]") return temperature - -// ++++ROCKDTBEN++++ MOB PROCS -- Ask me before touching. -// Stop! ... Hammertime! ~Carn -// I touched them without asking... I'm soooo edgy ~Erro (added nodamage checks) - -/mob/living/proc/getBruteLoss() - return bruteloss - -/mob/living/proc/getShockBruteLoss() //Only checks for things that'll actually hurt (not robolimbs) - return bruteloss - -/mob/living/proc/getActualBruteLoss() // Mostly for humans with robolimbs. - return getBruteLoss() - -//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs -/mob/living/proc/adjustBruteLoss(var/amount,var/include_robo) - if(status_flags & STATUS_GODMODE) return 0 //godmode - - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_brute_damage_percent)) - amount *= M.incoming_brute_damage_percent - else if(amount < 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - - bruteloss = min(max(bruteloss + amount, 0),(getMaxHealth()*2)) - update_health() - -/mob/living/proc/getOxyLoss() - return oxyloss - -/mob/living/proc/adjustOxyLoss(var/amount) - if(status_flags & STATUS_GODMODE) return 0 //godmode - - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_oxy_damage_percent)) - amount *= M.incoming_oxy_damage_percent - else if(amount < 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - - oxyloss = min(max(oxyloss + amount, 0),(getMaxHealth()*2)) - update_health() - -/mob/living/proc/setOxyLoss(var/amount) - if(status_flags & STATUS_GODMODE) return 0 //godmode - oxyloss = amount - -/mob/living/proc/getToxLoss() - return toxloss - -/mob/living/proc/adjustToxLoss(var/amount) - if(status_flags & STATUS_GODMODE) return 0 //godmode - - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_tox_damage_percent)) - amount *= M.incoming_tox_damage_percent - else if(amount < 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - - toxloss = min(max(toxloss + amount, 0),(getMaxHealth()*2)) - update_health() - -/mob/living/proc/setToxLoss(var/amount) - if(status_flags & STATUS_GODMODE) return 0 //godmode - toxloss = amount - -/mob/living/proc/getFireLoss() - return fireloss - -/mob/living/proc/getShockFireLoss() //Only checks for things that'll actually hurt (not robolimbs) - return fireloss - -/mob/living/proc/getActualFireLoss() // Mostly for humans with robolimbs. - return getFireLoss() - -//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs -/mob/living/proc/adjustFireLoss(var/amount,var/include_robo) - if(status_flags & STATUS_GODMODE) return 0 //godmode - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_fire_damage_percent)) - amount *= M.incoming_fire_damage_percent - else if(amount < 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - - fireloss = min(max(fireloss + amount, 0),(getMaxHealth()*2)) - update_health() - -/mob/living/proc/getCloneLoss() - return cloneloss - -/mob/living/proc/adjustCloneLoss(var/amount) - if(status_flags & STATUS_GODMODE) return 0 //godmode - - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_clone_damage_percent)) - amount *= M.incoming_clone_damage_percent - else if(amount < 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - - cloneloss = min(max(cloneloss + amount, 0),(getMaxHealth()*2)) - update_health() - -/mob/living/proc/setCloneLoss(var/amount) - if(status_flags & STATUS_GODMODE) return 0 //godmode - cloneloss = amount - -/mob/living/proc/getBrainLoss() - return brainloss - -/mob/living/proc/adjustBrainLoss(var/amount) - if(status_flags & STATUS_GODMODE) return 0 //godmode - brainloss = min(max(brainloss + amount, 0),(getMaxHealth()*2)) - -/mob/living/proc/setBrainLoss(var/amount) - if(status_flags & STATUS_GODMODE) return 0 //godmode - brainloss = amount - -/mob/living/proc/getHalLoss() - return halloss - -/mob/living/proc/adjustHalLoss(var/amount) - if(status_flags & STATUS_GODMODE) return 0 //godmode - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_hal_damage_percent)) - amount *= M.incoming_hal_damage_percent - if(!isnull(M.disable_duration_percent)) - amount *= M.incoming_hal_damage_percent - else if(amount < 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - halloss = min(max(halloss + amount, 0),(getMaxHealth()*2)) - update_health() - -/mob/living/proc/setHalLoss(var/amount) - if(status_flags & STATUS_GODMODE) return 0 //godmode - halloss = amount - // Use this to get a mob's max health whenever possible. Reading maxHealth directly will give inaccurate results if any modifiers exist. /mob/living/proc/getMaxHealth() var/result = maxHealth @@ -348,38 +183,6 @@ default behaviour is: amount = round(amount * M.disable_duration_percent) ..(amount) -// ++++ROCKDTBEN++++ MOB PROCS //END - -// Applies direct "cold" damage while checking protection against the cold. -/mob/living/proc/inflict_cold_damage(amount) - amount *= 1 - get_cold_protection(50) // Within spacesuit protection. - if(amount > 0) - adjustFireLoss(amount) - -// Ditto, but for "heat". -/mob/living/proc/inflict_heat_damage(amount) - amount *= 1 - get_heat_protection(10000) // Within firesuit protection. - if(amount > 0) - adjustFireLoss(amount) - -// and one for electricity because why not -/mob/living/proc/inflict_shock_damage(amount) - electrocute_act(amount, null, 1 - get_shock_protection(), pick(BP_HEAD, BP_TORSO, BP_GROIN)) - -// also one for water (most things resist it entirely, except for slimes) -/mob/living/proc/inflict_water_damage(amount) - amount *= 1 - get_water_protection() - if(amount > 0) - adjustToxLoss(amount) - -// one for abstracted away ""poison"" (mostly because simplemobs shouldn't handle reagents) -/mob/living/proc/inflict_poison_damage(amount) - if(isSynthetic()) - return - amount *= 1 - get_poison_protection() - if(amount > 0) - adjustToxLoss(amount) - /mob/proc/get_contents() . = list() var/list/obj/processing = get_equipped_items(TRUE, TRUE) @@ -393,7 +196,7 @@ default behaviour is: . += iterating var/list/returned = iterating.return_inventory() . += returned - + /mob/living/proc/can_inject(var/mob/user, var/error_msg, var/target_zone, var/ignore_thickness = FALSE) return 1 @@ -405,19 +208,6 @@ default behaviour is: var/obj/item/organ/external/def_zone = ran_zone(t) return def_zone - -// heal ONE external organ, organ gets randomly selected from damaged ones. -/mob/living/proc/heal_organ_damage(var/brute, var/burn) - adjustBruteLoss(-brute) - adjustFireLoss(-burn) - src.update_health() - -// heal MANY external organs, in random order -/mob/living/proc/heal_overall_damage(var/brute, var/burn) - adjustBruteLoss(-brute) - adjustFireLoss(-burn) - src.update_health() - /mob/living/proc/restore_all_organs() return diff --git a/code/modules/mob/living/organs.dm b/code/modules/mob/living/organs.dm index 24a8df7690ce..7097519c2b2a 100644 --- a/code/modules/mob/living/organs.dm +++ b/code/modules/mob/living/organs.dm @@ -1,3 +1,4 @@ +// todo: move all organs to /carbon WHY are they on living?? /mob/living var/list/internal_organs = list() var/list/organs = list() @@ -6,6 +7,7 @@ /// So internal organs have less ickiness too var/list/internal_organs_by_name = list() /// Organs we check until they are good. + // todo: whta the fuck does this mean exactly? re-evaluate and document, please! var/list/bad_external_organs = list() /mob/living/proc/get_bodypart_name(zone) diff --git a/code/modules/mob/living/simple_mob/damage_procs.dm b/code/modules/mob/living/simple_mob/damage_procs.dm new file mode 100644 index 000000000000..05666df1d3cd --- /dev/null +++ b/code/modules/mob/living/simple_mob/damage_procs.dm @@ -0,0 +1,24 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +//* Damage *// + +/mob/living/simple_mob/heal_brute_loss(amount) + . = bruteloss + bruteloss -= amount + return . - bruteloss + +/mob/living/simple_mob/heal_fire_loss(amount) + . = fireloss + fireloss -= amount + return . - fireloss + +/mob/living/simple_mob/heal_tox_loss(amount) + . = toxloss + toxloss -= amount + return . - toxloss + +/mob/living/simple_mob/heal_oxy_loss(amount) + . = oxyloss + oxyloss -= amount + return . - oxyloss diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index b0512526e191..446b87178400 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -367,7 +367,7 @@ //? Unit Tests /// A mock client, provided by tests and friends - var/datum/client_interface/mock_client + var/datum/mocking/client/mock_client //? Throwing /// whether or not we're prepared to throw stuff. diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index fd9b6f16800c..176d0c26ce8d 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -102,6 +102,14 @@ return return TRUE +/** + * Is this an admin attempting to use admin interaction? + * + * todo: move this to a new 'interaction' framework for admin datums. + */ +/proc/is_admin_interactive(mob/user, datum/target) + return IsAdminGhost(user) // same checks for now + /// Is the passed in mob a ghost with admin powers, doesn't check for AI interact like isAdminGhost() used to /proc/isAdminObserver(mob/user) if(!user) //Are they a mob? Auto interface updates call this with a null src @@ -552,6 +560,7 @@ var/list/global/base_miss_chance = list( * Used to weight organs when an organ is hit randomly (i.e. not a directed, aimed attack). * Also used to weight the protection value that armour provides for covering that body part when calculating protection from full-body effects. */ +// todo: uhh should we do something about this..? var/list/global/organ_rel_size = list( BP_HEAD = 25, BP_TORSO = 70, @@ -565,6 +574,8 @@ var/list/global/organ_rel_size = list( BP_L_FOOT = 10, BP_R_FOOT = 10, ) +//* Keep this up to date with organ_rel_size. This is all of them added together. +GLOBAL_VAR_INIT(organ_combined_size, 25 + 70 + 30 + 25 + 25 + 25 + 25 + 10 + 10 + 10 + 10) /mob/proc/flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = FALSE, visual = FALSE, type = /atom/movable/screen/fullscreen/tiled/flash) return diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm index ef886b871ed7..69999c57cc24 100644 --- a/code/modules/modular_computers/file_system/computer_file.dm +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -25,16 +25,12 @@ var/global/file_uid = 0 holder = null return ..() -// Returns independent copy of this file. -/datum/computer_file/proc/clone(var/rename = 0) +/datum/computer_file/clone() var/datum/computer_file/temp = new type temp.unsendable = unsendable temp.undeletable = undeletable temp.size = size - if(rename) - temp.filename = filename + "(Copy)" - else - temp.filename = filename + temp.filename = filename temp.filetype = filetype return temp diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm index 4d5782179774..b8a97428ef5e 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -47,6 +47,7 @@ if(!F || !istype(F)) return var/datum/computer_file/C = F.clone(1) + C.filename = "[C.filename] (Copy)" HDD.store_file(C) return TRUE if("PRG_edit") diff --git a/code/modules/organs/external/species/adherent.dm b/code/modules/organs/external/species/adherent.dm index 21a16bccc14a..652ca5636f06 100644 --- a/code/modules/organs/external/species/adherent.dm +++ b/code/modules/organs/external/species/adherent.dm @@ -11,6 +11,7 @@ // arterial_bleed_severity = 0 encased = "ceramic hull" robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE // limb_flags = ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_HEALS_OVERKILL /obj/item/organ/external/groin/crystal @@ -23,6 +24,7 @@ gendered_icon = 0 encased = "ceramic hull" robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE // limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_CAN_BREAK /obj/item/organ/external/head/crystal @@ -38,6 +40,7 @@ eye_icon_location = 'icons/mob/species/adherent/eyes.dmi' eye_icon = "eyes" robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE // can_intake_reagents = FALSE //! DO NOT UNCOMMENT THIS LINE UNTIL ADHERENTS CAN PASS IsSynthetic() // limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_HEALS_OVERKILL | ORGAN_FLAG_CAN_BREAK @@ -51,6 +54,7 @@ max_damage = 20 min_broken_damage = 10 robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE // limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_GRASP | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_CAN_BREAK /obj/item/organ/external/arm/right/crystal @@ -63,6 +67,7 @@ max_damage = 20 min_broken_damage = 10 robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE // limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_GRASP | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_CAN_BREAK /obj/item/organ/external/hand/crystal @@ -75,6 +80,7 @@ max_damage = 20 min_broken_damage = 10 robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE // limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_GRASP | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_CAN_BREAK /obj/item/organ/external/hand/right/crystal @@ -87,6 +93,7 @@ max_damage = 20 min_broken_damage = 10 robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE // limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_GRASP | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_CAN_BREAK @@ -102,16 +109,20 @@ max_damage = 20 min_broken_damage = 10 robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE // limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_GRASP | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_CAN_BREAK /obj/item/organ/external/leg/right/tendril name = "second tendril" robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE /obj/item/organ/external/foot/tendril name = "third tendril" robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE /obj/item/organ/external/foot/right/tendril name = "fourth tendril" robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE diff --git a/code/modules/organs/external/species/alraune.dm b/code/modules/organs/external/species/alraune.dm index 735f0cd40e56..43ba7f492aed 100644 --- a/code/modules/organs/external/species/alraune.dm +++ b/code/modules/organs/external/species/alraune.dm @@ -1,34 +1,45 @@ /obj/item/organ/external/chest/unbreakable/plant spread_dam = TRUE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/external/groin/unbreakable/plant spread_dam = TRUE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/external/arm/unbreakable/plant spread_dam = TRUE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/external/arm/right/unbreakable/plant spread_dam = TRUE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/external/leg/unbreakable/plant spread_dam = TRUE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/external/leg/right/unbreakable/plant spread_dam = TRUE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/external/foot/unbreakable/plant spread_dam = TRUE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/external/foot/right/unbreakable/plant spread_dam = TRUE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/external/hand/unbreakable/plant spread_dam = TRUE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/external/hand/right/unbreakable/plant spread_dam = TRUE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/external/head/unbreakable/plant //They don't need this anymore. spread_dam = TRUE cannot_gib = FALSE vital = FALSE + biology_type = BIOLOGY_TYPE_PLANT diff --git a/code/modules/organs/external/species/diona.dm b/code/modules/organs/external/species/diona.dm index a7030767b8e8..3c1f1263065d 100644 --- a/code/modules/organs/external/species/diona.dm +++ b/code/modules/organs/external/species/diona.dm @@ -20,6 +20,7 @@ amputation_point = "branch" joint = "structural ligament" dislocated = -1 + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/external/diona/chest name = "core trunk" diff --git a/code/modules/organs/external/species/slime.dm b/code/modules/organs/external/species/slime.dm index a4ad1b4c5f0f..e0121eccba64 100644 --- a/code/modules/organs/external/species/slime.dm +++ b/code/modules/organs/external/species/slime.dm @@ -4,60 +4,70 @@ encased = 0 spread_dam = 1 transparent = 1 + biology_type = BIOLOGY_TYPE_SLIME /obj/item/organ/external/groin/unbreakable/slime nonsolid = 1 max_damage = 30 spread_dam = 1 transparent = 1 + biology_type = BIOLOGY_TYPE_SLIME /obj/item/organ/external/arm/unbreakable/slime nonsolid = 1 max_damage = 20 spread_dam = 1 transparent = 1 + biology_type = BIOLOGY_TYPE_SLIME /obj/item/organ/external/arm/right/unbreakable/slime nonsolid = 1 max_damage = 20 spread_dam = 1 transparent = 1 + biology_type = BIOLOGY_TYPE_SLIME /obj/item/organ/external/leg/unbreakable/slime nonsolid = 1 max_damage = 20 spread_dam = 1 transparent = 1 + biology_type = BIOLOGY_TYPE_SLIME /obj/item/organ/external/leg/right/unbreakable/slime nonsolid = 1 max_damage = 20 spread_dam = 1 transparent = 1 + biology_type = BIOLOGY_TYPE_SLIME /obj/item/organ/external/foot/unbreakable/slime nonsolid = 1 max_damage = 20 spread_dam = 1 transparent = 1 + biology_type = BIOLOGY_TYPE_SLIME /obj/item/organ/external/foot/right/unbreakable/slime nonsolid = 1 max_damage = 20 spread_dam = 1 transparent = 1 + biology_type = BIOLOGY_TYPE_SLIME /obj/item/organ/external/hand/unbreakable/slime nonsolid = 1 max_damage = 20 spread_dam = 1 transparent = 1 + biology_type = BIOLOGY_TYPE_SLIME /obj/item/organ/external/hand/right/unbreakable/slime nonsolid = 1 max_damage = 20 spread_dam = 1 transparent = 1 + biology_type = BIOLOGY_TYPE_SLIME /obj/item/organ/external/head/unbreakable/slime //They don't need this anymore. nonsolid = 1 @@ -68,3 +78,4 @@ spread_dam = 1 transparent = 1 hair_opacity = 160 + biology_type = BIOLOGY_TYPE_SLIME diff --git a/code/modules/organs/internal/robotic/_robotic.dm b/code/modules/organs/internal/robotic/_robotic.dm index 6404d8e753f8..6c4f6effd65f 100644 --- a/code/modules/organs/internal/robotic/_robotic.dm +++ b/code/modules/organs/internal/robotic/_robotic.dm @@ -11,3 +11,4 @@ robotic = ORGAN_ROBOT // I'd like to see you try to butcher a robot. butcherable = FALSE + biology_type = BIOLOGY_TYPE_SYNTH diff --git a/code/modules/organs/internal/species/adherent.dm b/code/modules/organs/internal/species/adherent.dm index 6b1f515b9cd0..f5381191cb1e 100644 --- a/code/modules/organs/internal/species/adherent.dm +++ b/code/modules/organs/internal/species/adherent.dm @@ -5,17 +5,19 @@ icon_state = "brain" organ_tag = O_BRAIN robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE -/obj/item/organ/internal/powered +/obj/item/organ/internal/adherent icon = 'icons/mob/species/adherent/organs.dmi' robotic = ORGAN_CRYSTAL + biology_type = BIOLOGY_TYPE_CRYSTALLINE var/maintenance_cost = 0.5 var/base_action_state var/active = FALSE var/use_descriptor -/obj/item/organ/internal/powered/process(delta_time) +/obj/item/organ/internal/adherent/process(delta_time) . = ..() if(!owner) @@ -29,13 +31,13 @@ to_chat(owner, SPAN_DANGER("Your [name] [gender == PLURAL ? "are" : "is"] out of power!")) refresh_action_button() -/obj/item/organ/internal/powered/refresh_action_button() +/obj/item/organ/internal/adherent/refresh_action_button() . = ..() if(.) action.button_icon_state = "[base_action_state]-[active ? "on" : "off"]" if(action.button) action.button.UpdateIcon() -/obj/item/organ/internal/powered/attack_self(mob/user) +/obj/item/organ/internal/adherent/attack_self(mob/user) . = ..() if(.) return @@ -51,7 +53,7 @@ refresh_action_button() -/obj/item/organ/internal/powered/jets +/obj/item/organ/internal/adherent/jets name = "maneuvering jets" desc = "Gas jets from a Adherent chassis." action_button_name = "Toggle Maneuvering Pack" @@ -64,14 +66,14 @@ base_action_state = "adherent-pack" maintenance_cost = 0.2 -/obj/item/organ/internal/powered/jets/Initialize(mapload) +/obj/item/organ/internal/adherent/jets/Initialize(mapload) . = ..() - //add_obj_verb(src, /obj/item/organ/internal/powered/jets/proc/activatej) + //add_obj_verb(src, /obj/item/organ/internal/adherent/jets/proc/activatej) -/obj/item/organ/internal/powered/jets/ui_action_click() +/obj/item/organ/internal/adherent/jets/ui_action_click() activatej() -/obj/item/organ/internal/powered/jets/proc/activatej() +/obj/item/organ/internal/adherent/jets/proc/activatej() /*set name = "Toggle Maneuvering Pack" set desc = "Toggles your manuevering jets" set category = "Abilities"*/ @@ -88,13 +90,13 @@ C.update_floating() to_chat(C, SPAN_NOTICE("You have [C.flying?"started":"stopped"] flying.")) -/obj/item/organ/internal/powered/jets/process(delta_time) +/obj/item/organ/internal/adherent/jets/process(delta_time) var/mob/living/carbon/human/C = src.owner if(!active) return C.nutrition = C.nutrition - maintenance_cost -/obj/item/organ/internal/powered/float +/obj/item/organ/internal/adherent/float name = "levitation plate" desc = "A broad, flat disc of exotic matter. Slick to the touch." action_button_name = "Toggle Antigravity" @@ -105,12 +107,12 @@ use_descriptor = "hover" base_action_state = "adherent-float" -/obj/item/organ/internal/powered/float/Initialize(mapload) +/obj/item/organ/internal/adherent/float/Initialize(mapload) . = ..() - //add_obj_verb(src, /obj/item/organ/internal/powered/float/proc/flying_toggle) - add_obj_verb(src, /obj/item/organ/internal/powered/float/proc/hover) + //add_obj_verb(src, /obj/item/organ/internal/adherent/float/proc/flying_toggle) + add_obj_verb(src, /obj/item/organ/internal/adherent/float/proc/hover) -/obj/item/organ/internal/powered/float/ui_action_click() +/obj/item/organ/internal/adherent/float/ui_action_click() hover() /obj/item/organ/internal/eyes/adherent name = "receptor prism" @@ -135,7 +137,7 @@ status = ORGAN_CRYSTAL -/obj/item/organ/internal/powered/cooling_fins +/obj/item/organ/internal/adherent/cooling_fins name = "cooling fins" gender = PLURAL desc = "A lacy filligree of heat-radiating fins." @@ -152,14 +154,14 @@ var/max_cooling = 10 var/target_temp = T20C -/obj/item/organ/internal/powered/cooling_fins/Initialize(mapload) +/obj/item/organ/internal/adherent/cooling_fins/Initialize(mapload) . = ..() - add_obj_verb(src, /obj/item/organ/internal/powered/cooling_fins/proc/activatecf) + add_obj_verb(src, /obj/item/organ/internal/adherent/cooling_fins/proc/activatecf) -/obj/item/organ/internal/powered/cooling_fins/ui_action_click() +/obj/item/organ/internal/adherent/cooling_fins/ui_action_click() activatecf() -/obj/item/organ/internal/powered/cooling_fins/proc/activatecf() +/obj/item/organ/internal/adherent/cooling_fins/proc/activatecf() var/mob/living/carbon/human/C = src.owner set name = "Toggle Cooling Fins" set desc = "Turns on your onboard cooling fin array." @@ -168,7 +170,7 @@ cooling = !cooling to_chat(C, "You toggle your cooling fans [cooling ? "on" : "off"] ") -/obj/item/organ/internal/powered/cooling_fins/process(delta_time) +/obj/item/organ/internal/adherent/cooling_fins/process(delta_time) var/mob/living/carbon/human/C = src.owner if(cooling) var/temp_diff = min(C.bodytemperature - target_temp, max_cooling) @@ -179,7 +181,7 @@ maintenance_cost = 0 -/obj/item/organ/internal/powered/float/proc/flying_toggle() +/obj/item/organ/internal/adherent/float/proc/flying_toggle() /*set name = "Toggle Flight" set desc = "While flying over open spaces, you will use up some energy. If you run out energy, you will fall. Additionally, you can't fly if you are too heavy." set category = "Abilities" @@ -196,7 +198,7 @@ C.update_floating() to_chat(C, "You have [C.flying?"started":"stopped"] flying.")*/ -/obj/item/organ/internal/powered/float/proc/hover() +/obj/item/organ/internal/adherent/float/proc/hover() set name = "Hover" set desc = "Allows you to stop gliding and hover. This will take a fair amount of energy to perform." set category = "Abilities" diff --git a/code/modules/organs/internal/species/alraune.dm b/code/modules/organs/internal/species/alraune.dm index 3341b4f4caa5..d9f995b7c3e2 100644 --- a/code/modules/organs/internal/species/alraune.dm +++ b/code/modules/organs/internal/species/alraune.dm @@ -4,24 +4,28 @@ name = "neuro-stroma" desc = "A knot of fibrous plant matter." parent_organ = BP_TORSO // brains in their core + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/eyes/alraune icon = 'icons/mob/clothing/species/alraune/organs.dmi' icon_state = "photoreceptors" name = "photoreceptors" desc = "Bulbous and fleshy plant matter." + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/kidneys/alraune icon = 'icons/mob/clothing/species/alraune/organs.dmi' icon_state = "rhyzofilter" name = "rhyzofilter" desc = "A tangle of root nodules." + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/liver/alraune icon = 'icons/mob/clothing/species/alraune/organs.dmi' icon_state = "phytoextractor" name = "enzoretector" desc = "A bulbous gourd-like structure." + biology_type = BIOLOGY_TYPE_PLANT //Begin fruit gland and its code. /obj/item/organ/internal/fruitgland //Amazing name, I know. @@ -30,6 +34,7 @@ name = "fruit gland" desc = "A bulbous gourd-like structure." organ_tag = O_FRUIT + biology_type = BIOLOGY_TYPE_PLANT var/generated_reagents = list("sugar" = 2) //This actually allows them. This could be anything, but sugar seems most fitting. var/usable_volume = 250 //Five fruit. var/transfer_amount = 50 diff --git a/code/modules/organs/internal/species/diona.dm b/code/modules/organs/internal/species/diona.dm index b43caa1d74a1..c3bb87a7bf75 100644 --- a/code/modules/organs/internal/species/diona.dm +++ b/code/modules/organs/internal/species/diona.dm @@ -4,6 +4,7 @@ icon = 'icons/obj/objects.dmi' icon_state = "nymph" organ_tag = "special" // Turns into a nymph instantly, no transplanting possible. + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/diona/removed(var/mob/living/user, var/skip_nymph) if(robotic >= ORGAN_ROBOT) @@ -22,31 +23,37 @@ name = "neural strata" parent_organ = BP_TORSO organ_tag = O_STRATA + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/diona/bladder name = "gas bladder" parent_organ = BP_HEAD organ_tag = O_GBLADDER + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/diona/polyp name = "polyp segment" parent_organ = BP_GROIN organ_tag = O_POLYP + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/diona/ligament name = "anchoring ligament" parent_organ = BP_GROIN organ_tag = O_ANCHOR + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/diona/node name = "receptor node" parent_organ = BP_HEAD organ_tag = O_RESPONSE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/diona/nutrients name = O_NUTRIENT parent_organ = BP_TORSO organ_tag = O_NUTRIENT + biology_type = BIOLOGY_TYPE_PLANT // These are different to the standard diona organs as they have a purpose in other // species (absorbing radiation and light respectively) @@ -55,6 +62,7 @@ organ_tag = O_NUTRIENT icon = 'icons/mob/alien.dmi' icon_state = "claw" + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/diona/nutrients/removed(mob/user) return ..(user, 1) @@ -65,6 +73,7 @@ organ_tag = O_RESPONSE icon = 'icons/mob/alien.dmi' icon_state = "claw" + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/diona/node/removed() return @@ -74,6 +83,7 @@ name = "cephalon mass" parent_organ = BP_TORSO vital = TRUE + biology_type = BIOLOGY_TYPE_PLANT /obj/item/organ/internal/brain/cephalon/Initialize(mapload) . = ..() diff --git a/code/modules/organs/internal/species/slime.dm b/code/modules/organs/internal/species/slime.dm index 4220898c7ff9..aa6de3bfba04 100644 --- a/code/modules/organs/internal/species/slime.dm +++ b/code/modules/organs/internal/species/slime.dm @@ -8,6 +8,7 @@ icon_state = "sac_slime" dead_icon = null standard_pulse_level = PULSE_NONE + biology_type = BIOLOGY_TYPE_SLIME /obj/item/organ/internal/heart/grey/colormatch/slime/process(delta_time) ..() @@ -26,6 +27,7 @@ organ_tag = O_REGBRUTE icon_state = "sac_slime" + biology_type = BIOLOGY_TYPE_SLIME var/strain = 0 // The amount of stress this organ is under. Capped at min_broken_damage, usually half its max damage. diff --git a/code/modules/organs/internal/species/xenochimera.dm b/code/modules/organs/internal/species/xenochimera.dm index 0f8330fd12c4..4a2b499a81dc 100644 --- a/code/modules/organs/internal/species/xenochimera.dm +++ b/code/modules/organs/internal/species/xenochimera.dm @@ -2,39 +2,48 @@ name = "circulatory tumor" icon_state = "xenoch_heart_on" dead_icon = "xenoch_heart_off" + biology_type = BIOLOGY_TYPE_CHIMERA /obj/item/organ/internal/lungs/xenochimera name = "respiratory flora" icon_state = "xenoch_lungs" parent_organ = BP_HEAD + biology_type = BIOLOGY_TYPE_CHIMERA /obj/item/organ/internal/voicebox/xenochimera name = "vocalization trellis" icon_state = "xenoch_larynx" + biology_type = BIOLOGY_TYPE_CHIMERA /obj/item/organ/internal/liver/xenochimera name = "purification honeycomb" icon_state = "xenoch_liver" parent_organ = BP_TORSO + biology_type = BIOLOGY_TYPE_CHIMERA /obj/item/organ/internal/kidneys/xenochimera name = "filtration fungi" icon_state = "xenoch_kidneys" + biology_type = BIOLOGY_TYPE_CHIMERA /obj/item/organ/internal/brain/xenochimera name = "neural colony" icon_state = "xenoch_brain" parent_organ = BP_TORSO + biology_type = BIOLOGY_TYPE_CHIMERA /obj/item/organ/internal/eyes/xenochimera name = "photosensitive nodules" icon_state = "xenoch_eyes" + biology_type = BIOLOGY_TYPE_CHIMERA /obj/item/organ/internal/stomach/xenochimera name = "digestive sac" icon_state = "xenoch_stomach" dead_icon = null + biology_type = BIOLOGY_TYPE_CHIMERA /obj/item/organ/internal/intestine/xenochimera name = "harvesting fronds" icon_state = "xenoch_intestine" + biology_type = BIOLOGY_TYPE_CHIMERA diff --git a/code/modules/organs/internal/subtypes/augment.dm b/code/modules/organs/internal/subtypes/augment.dm index 851229b75b91..7acc182c864d 100644 --- a/code/modules/organs/internal/subtypes/augment.dm +++ b/code/modules/organs/internal/subtypes/augment.dm @@ -83,7 +83,7 @@ var/obj/item/I = source I.visible_message(SPAN_NOTICE("[I] snaps back into [src]!")) I.forceMove(src) - . = COMPONENT_ITEM_DROPPED_RELOCATE | COMPONENT_ITEM_DROPPED_SUPPRESS_SOUND + . = COMPONENT_ITEM_INV_OP_RELOCATE | COMPONENT_ITEM_INV_OP_SUPPRESS_SOUND /obj/item/organ/internal/augment/proc/check_item_yank(obj/item/I) if(I.loc != src && I.loc != owner) diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 07dcf906ae5e..66710be11e7f 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -5,6 +5,10 @@ drop_sound = 'sound/items/drop/flesh.ogg' pickup_sound = 'sound/items/pickup/flesh.ogg' + //* Biology / Identity *// + /// what biology we are + var/biology_type = BIOLOGY_TYPE_HUMAN + //! ## STRINGS VARS /// Unique identifier. var/organ_tag = "organ" @@ -318,6 +322,7 @@ /// Being used to make robutt hearts, etc /obj/item/organ/proc/robotize() robotic = ORGAN_ROBOT + biology_type = BIOLOGY_TYPE_SYNTH src.status &= ~ORGAN_BROKEN src.status &= ~ORGAN_BLEEDING src.status &= ~ORGAN_CUT_AWAY diff --git a/code/modules/organs/subtypes/machine.dm b/code/modules/organs/subtypes/machine.dm index 70dc9c4f6aec..a0bb0f3e23ef 100644 --- a/code/modules/organs/subtypes/machine.dm +++ b/code/modules/organs/subtypes/machine.dm @@ -5,6 +5,8 @@ organ_tag = O_CELL parent_organ = BP_TORSO vital = 1 + biology_type = BIOLOGY_TYPE_SYNTH + robotic = ORGAN_ROBOT /obj/item/organ/internal/cell/Initialize(mapload) . = ..() @@ -37,6 +39,7 @@ var/obj/item/mmi/stored_mmi robotic = ORGAN_ASSISTED butcherable = FALSE + biology_type = BIOLOGY_TYPE_SYNTH /obj/item/organ/internal/mmi_holder/Destroy() if(stored_mmi && (stored_mmi.loc == src)) diff --git a/code/modules/organs/subtypes/nano.dm b/code/modules/organs/subtypes/nano.dm index 02cdd22195e2..9fd5a22785e4 100644 --- a/code/modules/organs/subtypes/nano.dm +++ b/code/modules/organs/subtypes/nano.dm @@ -6,6 +6,7 @@ min_broken_damage = 1000 vital = TRUE emp_mod = 7 + biology_type = BIOLOGY_TYPE_NANITES /obj/item/organ/external/groin/unbreakable/nano robotic = ORGAN_NANOFORM @@ -14,6 +15,7 @@ min_broken_damage = 1000 //Multiple vital = FALSE emp_mod = 4 + biology_type = BIOLOGY_TYPE_NANITES /obj/item/organ/external/head/unbreakable/nano robotic = ORGAN_NANOFORM @@ -22,6 +24,7 @@ min_broken_damage = 1000 //Inheritance vital = FALSE emp_mod = 4 + biology_type = BIOLOGY_TYPE_NANITES /obj/item/organ/external/arm/unbreakable/nano robotic = ORGAN_NANOFORM @@ -30,6 +33,7 @@ min_broken_damage = 1000 //Please vital = FALSE emp_mod = 4 + biology_type = BIOLOGY_TYPE_NANITES /obj/item/organ/external/arm/right/unbreakable/nano robotic = ORGAN_NANOFORM @@ -38,6 +42,7 @@ min_broken_damage = 1000 vital = FALSE emp_mod = 4 + biology_type = BIOLOGY_TYPE_NANITES /obj/item/organ/external/leg/unbreakable/nano robotic = ORGAN_NANOFORM @@ -46,6 +51,7 @@ min_broken_damage = 1000 vital = FALSE emp_mod = 4 + biology_type = BIOLOGY_TYPE_NANITES /obj/item/organ/external/leg/right/unbreakable/nano robotic = ORGAN_NANOFORM @@ -54,6 +60,7 @@ min_broken_damage = 1000 vital = FALSE emp_mod = 4 + biology_type = BIOLOGY_TYPE_NANITES /obj/item/organ/external/hand/unbreakable/nano robotic = ORGAN_NANOFORM @@ -62,6 +69,7 @@ min_broken_damage = 1000 vital = FALSE emp_mod = 4 + biology_type = BIOLOGY_TYPE_NANITES /obj/item/organ/external/hand/right/unbreakable/nano robotic = ORGAN_NANOFORM @@ -70,6 +78,7 @@ min_broken_damage = 1000 vital = FALSE emp_mod = 4 + biology_type = BIOLOGY_TYPE_NANITES /obj/item/organ/external/foot/unbreakable/nano robotic = ORGAN_NANOFORM @@ -78,6 +87,7 @@ min_broken_damage = 1000 vital = FALSE emp_mod = 4 + biology_type = BIOLOGY_TYPE_NANITES /obj/item/organ/external/foot/right/unbreakable/nano robotic = ORGAN_NANOFORM @@ -86,10 +96,12 @@ min_broken_damage = 1000 vital = FALSE emp_mod = 4 + biology_type = BIOLOGY_TYPE_NANITES // // // Internal Organs /obj/item/organ/internal/nano robotic = ORGAN_ROBOT + biology_type = BIOLOGY_TYPE_SYNTH /obj/item/organ/internal/nano/orchestrator name = "orchestrator module" diff --git a/code/modules/power/antimatter/engine.dm b/code/modules/power/antimatter/engine.dm index f09a34bbdbd8..312d4fda0b68 100644 --- a/code/modules/power/antimatter/engine.dm +++ b/code/modules/power/antimatter/engine.dm @@ -97,7 +97,7 @@ H_fuel = 0 antiH_fuel = 0 else - var/residual_matter = modulus(H_fuel - antiH_fuel) + var/residual_matter = abs(H_fuel - antiH_fuel) var/mass = antiH_fuel + H_fuel - residual_matter energy = convert2energy(mass) if( H_fuel > antiH_fuel ) @@ -148,7 +148,7 @@ else //else if they're not equal determine which isn't equal //and set it equal to either H or antiH so we don't lose anything - var/residual_matter = modulus(H_fuel - antiH_fuel) + var/residual_matter = abs(H_fuel - antiH_fuel) mass = antiH_fuel + H_fuel - residual_matter energy = convert2energy(mass) diff --git a/code/modules/power/antimatter/fuel.dm b/code/modules/power/antimatter/fuel.dm index 837c8344e528..042f4d0933fd 100644 --- a/code/modules/power/antimatter/fuel.dm +++ b/code/modules/power/antimatter/fuel.dm @@ -66,7 +66,6 @@ qdel(src) return - /obj/item/fuel/examine(mob/user, dist) if(get_dist(src, user) <= 1) . += "A magnetic storage ring, it contains [fuel]kg of [content ? content : "nothing"]." diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 6aa868fd1640..37d219c36bde 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -48,7 +48,7 @@ var/tracer_type var/muzzle_type var/impact_type - var/datum/beam_components_cache/beam_components + var/datum/beam_legacy_components_cache/beam_components var/miss_sounds var/ricochet_sounds diff --git a/code/modules/reagents/chemistry/metabolism.dm b/code/modules/reagents/chemistry/metabolism.dm index 4de7a6acc4b7..123069172991 100644 --- a/code/modules/reagents/chemistry/metabolism.dm +++ b/code/modules/reagents/chemistry/metabolism.dm @@ -11,6 +11,7 @@ if(istype(parent_mob)) parent = parent_mob +// todo: this should be by seconds. /datum/reagents/metabolism/proc/metabolize(speed_mult = 1, force_allow_dead) var/metabolism_type = 0 //non-human mobs diff --git a/code/modules/research/designs/medical.dm b/code/modules/research/designs/medical.dm index 31ba401b0a4c..fb479059d549 100644 --- a/code/modules/research/designs/medical.dm +++ b/code/modules/research/designs/medical.dm @@ -121,158 +121,24 @@ materials_base = list(MAT_STEEL = 500, MAT_GLASS = 1500, MAT_SILVER = 2000, MAT_GOLD = 1500, MAT_URANIUM = 1000) build_path = /obj/item/hypospray/advanced -/datum/design/science/medical/cell_based - abstract_type = /datum/design/science/medical/cell_based +/datum/design/science/medical/medichine_cell + abstract_type = /datum/design/science/medical/medichine_cell -// ML-3M medigun and cells -/datum/design/science/medical/cell_based/generate_name(template) - return "Cell-based medical prototype ([..()])" +/datum/design/science/medical/medichine_cell/generate_name(template) + return "Medichine Cell ([..()])" -/datum/design/science/medical/cell_based/cell_medigun_mag_advanced - design_name = "advanced medical cell magazine" - id = "cell_medigun_mag_advanced" - req_tech = list(TECH_MATERIAL = 7, TECH_MAGNET = 6, TECH_POWER = 4, TECH_BIO = 7) - materials_base = list(MAT_STEEL = 5000, MAT_PLASTIC = 10000, MAT_GLASS = 5000, MAT_SILVER = 1500, MAT_GOLD = 1500, MAT_DIAMOND = 5000) - build_path = /obj/item/ammo_magazine/cell_mag/medical/advanced +/datum/design/science/medical/medichine_cell/seal_wounds + id = "medichine-cell-woundseal" + build_path = /obj/item/medichine_cell/seal_wounds -/datum/design/science/medigun_cell - abstract_type = /datum/design/science/medigun_cell +/datum/design/science/medical/medichine_cell/debride + id = "medichine-cell-debride" + build_path = /obj/item/medichine_cell/seal_wounds/violently -/datum/design/science/medigun_cell/generate_name(template) - return "Nanite cell prototype ([..()])" +/datum/design/science/medical/medichine_cell/deathmend + id = "medichine-cell-deathmend" + build_path = /obj/item/medichine_cell/deathmend -//Tier 1 - -/datum/design/science/medigun_cell/toxin - design_name = "TOXIN" - id = "medigun_cell_toxin" - req_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_BIO = 4) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250) - build_path = /obj/item/ammo_casing/microbattery/medical/toxin - -/datum/design/science/medigun_cell/omni - design_name = "OMNI" - id = "medigun_cell_omni" - req_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_BIO = 4) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250) - build_path = /obj/item/ammo_casing/microbattery/medical/omni - -/datum/design/science/medigun_cell/antirad - design_name = "ANTIRAD" - id = "medigun_cell_antirad" - req_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_BIO = 4) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250) - build_path = /obj/item/ammo_casing/microbattery/medical/antirad - -//Tier 2 - -/datum/design/science/medigun_cell/brute2 - design_name = "BRUTE-II" - id = "medigun_cell_brute2" - req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_POWER = 2, TECH_BIO = 5) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_GOLD = 900) - build_path = /obj/item/ammo_casing/microbattery/medical/brute2 - -/datum/design/science/medigun_cell/burn2 - design_name = "BURN-II" - id = "medigun_cell_burn2" - req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_POWER = 2, TECH_BIO = 5) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_GOLD = 900) - build_path = /obj/item/ammo_casing/microbattery/medical/burn2 - -/datum/design/science/medigun_cell/stabilize2 - design_name = "STABILIZE-II" - id = "medigun_cell_stabilize2" - req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_POWER = 2, TECH_BIO = 5) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_SILVER = 900) - build_path = /obj/item/ammo_casing/microbattery/medical/stabilize2 - -/datum/design/science/medigun_cell/omni2 - design_name = "OMNI-II" - id = "medigun_cell_omni2" - req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_POWER = 2, TECH_BIO = 5) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_URANIUM = 900) - build_path = /obj/item/ammo_casing/microbattery/medical/omni2 - -//Tier 3 - -/datum/design/science/medigun_cell/toxin2 - design_name = "TOXIN-II" - id = "medigun_cell_toxin2" - req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 3, TECH_POWER = 3, TECH_BIO = 6) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_URANIUM = 900, MAT_SILVER = 900, MAT_DIAMOND = 500) - build_path = /obj/item/ammo_casing/microbattery/medical/toxin2 - -/datum/design/science/medigun_cell/haste - design_name = "HASTE" - id = "medigun_cell_haste" - req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 3, TECH_POWER = 3, TECH_BIO = 6) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_GOLD = 900, MAT_SILVER = 900, MAT_DIAMOND = 900) - build_path = /obj/item/ammo_casing/microbattery/medical/haste - -/datum/design/science/medigun_cell/resist - design_name = "RESIST" - id = "medigun_cell_resist" - req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 3, TECH_POWER = 3, TECH_BIO = 6) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_GOLD = 900, MAT_URANIUM = 900, MAT_DIAMOND = 900) - build_path = /obj/item/ammo_casing/microbattery/medical/resist - -/datum/design/science/medigun_cell/corpse_mend - design_name = "CORPSE MEND" - id = "medigun_cell_corpse_mend" - req_tech = list(TECH_MATERIAL = 6, TECH_MAGNET = 3, TECH_POWER = 3, TECH_BIO = 6) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_PHORON = 3000, MAT_DIAMOND = 3000) - build_path = /obj/item/ammo_casing/microbattery/medical/corpse_mend - -//Tier 4 - -/datum/design/science/medigun_cell/brute3 - design_name = "BRUTE-III" - id = "medigun_cell_brute3" - req_tech = list(TECH_MATERIAL = 7, TECH_MAGNET = 6, TECH_POWER = 5, TECH_BIO = 7, TECH_PRECURSOR = 2) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_DIAMOND = 500, MAT_VERDANTIUM = 900) - build_path = /obj/item/ammo_casing/microbattery/medical/brute3 - -/datum/design/science/medigun_cell/burn3 - design_name = "BURN-III" - id = "medigun_cell_burn3" - req_tech = list(TECH_MATERIAL = 7, TECH_MAGNET = 6, TECH_POWER = 5, TECH_BIO = 7, TECH_PRECURSOR = 2) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_DIAMOND = 500, MAT_VERDANTIUM = 900) - build_path = /obj/item/ammo_casing/microbattery/medical/burn3 - -/datum/design/science/medigun_cell/toxin3 - design_name = "TOXIN-III" - id = "medigun_cell_toxin3" - req_tech = list(TECH_MATERIAL = 7, TECH_MAGNET = 6, TECH_POWER = 5, TECH_BIO = 7, TECH_ARCANE = 2) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_DIAMOND = 500, MAT_VERDANTIUM = 900) - build_path = /obj/item/ammo_casing/microbattery/medical/toxin3 - -/datum/design/science/medigun_cell/omni3 - design_name = "OMNI-III" - id = "medigun_cell_omni3" - req_tech = list(TECH_MATERIAL = 7, TECH_MAGNET = 6, TECH_POWER = 5, TECH_BIO = 7, TECH_ARCANE = 2) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_DIAMOND = 500, MAT_VERDANTIUM = 900) - build_path = /obj/item/ammo_casing/microbattery/medical/omni3 - -//Tierless - -/datum/design/science/medigun_cell/shrink - design_name = "SHRINK" - id = "medigun_cell_shrink" - req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_BLUESPACE = 3, TECH_BIO = 5, TECH_ILLEGAL = 5) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_URANIUM = 1800) - build_path = /obj/item/ammo_casing/microbattery/medical/shrink - -/datum/design/science/medigun_cell/grow - design_name = "GROW" - id = "medigun_cell_grow" - req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_BLUESPACE = 3, TECH_BIO = 5, TECH_ILLEGAL = 5) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_URANIUM = 1800) - build_path = /obj/item/ammo_casing/microbattery/medical/grow - -/datum/design/science/medigun_cell/normalsize - design_name = "NORMALSIZE" - id = "medigun_cell_normalsize" - req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3, TECH_BLUESPACE = 3, TECH_BIO = 5, TECH_ILLEGAL = 5) - materials_base = list(MAT_STEEL = 4500, MAT_GLASS = 4500, MAT_PLASTIC = 2250, MAT_URANIUM = 1800) - build_path = /obj/item/ammo_casing/microbattery/medical/normalsize +/datum/design/science/medical/medichine_cell/synthfix + id = "medichine-cell-synthfix" + build_path = /obj/item/medichine_cell/synth_repair diff --git a/code/modules/rigsuits/modules/dynamic.dm b/code/modules/rigsuits/modules/dynamic.dm new file mode 100644 index 000000000000..6b3c15d7a79c --- /dev/null +++ b/code/modules/rigsuits/modules/dynamic.dm @@ -0,0 +1,132 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * 'Dynamic' modules + * + * These allow for a custom schema-list definition for the UI, so that the implementor doesn't + * have to write their own TGUI templates. + */ +/obj/item/rig_module/dynamic + tgui_interface = "dynamic" + #warn impl + + /// by type + var/static/list/cached_schema_by_type = list() + /// our schema, set on new + var/datum/rig_dynamic_schema/schema + +/obj/item/rig_module/dynamic/Initialize(mapload) + if(isnull(schema)) + schema = get_schema() + return ..() + +/obj/item/rig_module/dynamic/proc/get_schema() + if(isnull(cached_schema_by_type[type])) + var/datum/rig_dynamic_schema/schema = new + construct_schema(schema) + schema.compile() + cached_schema_by_type = schema + return cached_schema_by_type[type] + +/obj/item/rig_module/dynamic/proc/construct_schema(datum/rig_dynamic_schema/schema) + return TRUE + +/obj/item/rig_module/dynamic/proc/config_adjust() + #warn impl + +/obj/item/rig_module/dynamic/proc/action_trigger() + #warn impl + +/obj/item/rig_module/dynamic/tgui_module_static() + . = ..() + .["schema"] = schema.tgui_schema_data() + +#warn impl all + +/** + * schema for ui + */ +/datum/rig_dynamic_schema + /// storage list + var/list/fragments = list() + /// constraint / data list for key + var/list/config = list() + /// buttons left in section + var/section_remaining = 0 + +/datum/rig_dynamic_schema/proc/tgui_schema_data() + return list( + "fragments" = fragments, + "config" = config, + ) + +/datum/rig_dynamic_schema/proc/compile() + . = FALSE + ASSERT(!section_remaining) + return TRUE + +/datum/rig_dynamic_schema/proc/validate_config_input(key, value) + var/list/data = config[key] + if(isnull(data)) + return null + switch(data["type"]) + if("toggle") + return !!value + if("number") + return round(clamp(value, data["min"], data["max"]), data["round"]) + if("string") + . = copytext_char(value, 1, data["maxLength"] + 1) + if(data["alphanumeric"]) + . = string_filter_to_alphanumeric(.) + +/datum/rig_dynamic_schema/proc/with_config_toggle(name = "Option", key = "?") + config[++config.len] = list( + "key" = key, + "name" = name, + "type" = "toggle", + ) + +/datum/rig_dynamic_schema/proc/with_config_number(name = "Option", key = "?", min = -INFINITY, max = INFINITY, round_to = null) + config[++config.len] = list( + "key" = key, + "name" = name, + "type" = "number", + "min" = min, + "max" = max, + "round" = round_to, + ) + +/datum/rig_dynamic_schema/proc/with_config_string(name = "Option", key = "?", max_length = 32, alphanumeric_only = FALSE) + config[++config.len] = list( + "key" = key, + "name" = name, + "type" = "string", + "maxLength" = max_length, + "alphanumeric" = alphanumeric_only, + ) + +/datum/rig_dynamic_schema/proc/with_action(name = "Action", key = "?", icon = null, confirm = FALSE, bindable = FALSE, confirm_text = null, confirm_icon = null) + var/list/action = list( + "name" = name, + "key" = key, + "icon" = icon, + "confirm" = confirm, + "bindable" = bindable, + "confirmText" = confirm_text, + "confirmIcon" = confirm_icon, + ) + if(section_remaining) + var/list/actions = fragments[fragments.len]["actions"] + actions.Add(action) + --section_remaining + else + fragments[++fragments.len] = action + +/datum/rig_dynamic_schema/proc/create_section(number_of_actions = 3) + ASSERT(!section_remaining) + section_remaining = number_of_actions + fragments[++fragments.len] = list( + "type" = "section", + "actions" = list(), + ) diff --git a/code/modules/rigsuits/modules/engineering/holofabricator.dm b/code/modules/rigsuits/modules/engineering/holofabricator.dm new file mode 100644 index 000000000000..d69c17e54a7a --- /dev/null +++ b/code/modules/rigsuits/modules/engineering/holofabricator.dm @@ -0,0 +1,4 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +#warn impl diff --git a/code/modules/rigsuits/modules/engineering/toolset.dm b/code/modules/rigsuits/modules/engineering/toolset.dm new file mode 100644 index 000000000000..55d7abfe2db5 --- /dev/null +++ b/code/modules/rigsuits/modules/engineering/toolset.dm @@ -0,0 +1,80 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/obj/item/rig_module/toolset/engineering + abstract_type = /obj/item/rig_module/toolset/engineering + +//? Simple + +/obj/item/rig_module/toolset/engineering/simple + items = list( + /obj/item/tool/screwdriver/rig_basic, + /obj/item/tool/wirecutters/rig_basic, + /obj/item/tool/wrench/rig_basic, + /obj/item/tool/crowbar/rig_basic, + ) + #warn impl + +/obj/item/rig_module/toolset/engineering/simple/with_multitool + items = list( + /obj/item/tool/screwdriver/rig_basic, + /obj/item/tool/wirecutters/rig_basic, + /obj/item/tool/wrench/rig_basic, + /obj/item/tool/crowbar/rig_basic, + /obj/item/multitool, + ) + +/obj/item/rig_module/toolset/engineering/simple/with_welder + items = list( + /obj/item/tool/screwdriver/rig_basic, + /obj/item/tool/wirecutters/rig_basic, + /obj/item/tool/wrench/rig_basic, + /obj/item/tool/crowbar/rig_basic, + /obj/item/weldingtool/electric, + ) + +/obj/item/rig_module/toolset/engineering/simple/full + items = list( + /obj/item/tool/screwdriver/rig_basic, + /obj/item/tool/wirecutters/rig_basic, + /obj/item/tool/wrench/rig_basic, + /obj/item/tool/crowbar/rig_basic, + /obj/item/weldingtool/electric, + /obj/item/multitool, + ) + +/obj/item/tool/wrench/rig_basic + name = "integrated wrench" + tool_sound = 'sound/items/drill_use.ogg' + tool_speed = 1.0 + +/obj/item/tool/wirecutters/rig_basic + name = "integrated wirecutters" + tool_sound = 'sound/items/jaws_cut.ogg' + tool_speed = 1.0 + +/obj/item/weldingtool/electric/mounted/rig_basic + name = "arc welder" + tool_speed = 1.0 + +/obj/item/tool/crowbar/rig_basic + name = "integrated prybar" + tool_sound = 'sound/items/jaws_pry.ogg' + tool_speed = 1.0 + +/obj/item/tool/screwdriver/rig_basic + name = "integrated screwdriver" + tool_sound = 'sound/items/drill_use.ogg' + tool_speed = 1.0 + +//? Power (tier 2) + +// todo: powertools + +/obj/item/rig_module/toolset/engineering/power + +//? Hardlight (tier 3) + +// todo: hardlight + +/obj/item/rig_module/toolset/engineering/hardlight diff --git a/code/modules/rigsuits/modules/medical/medichine.dm b/code/modules/rigsuits/modules/medical/medichine.dm new file mode 100644 index 000000000000..d69c17e54a7a --- /dev/null +++ b/code/modules/rigsuits/modules/medical/medichine.dm @@ -0,0 +1,4 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +#warn impl diff --git a/code/modules/rigsuits/modules/medical/toolset.dm b/code/modules/rigsuits/modules/medical/toolset.dm new file mode 100644 index 000000000000..0a1b9a292623 --- /dev/null +++ b/code/modules/rigsuits/modules/medical/toolset.dm @@ -0,0 +1,22 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/obj/item/rig_module/toolset/surgical + abstract_type = /obj/item/rig_module/toolset/surgical + +//? Basic (tier 1) + +/obj/item/rig_module/toolset/surgical/basic + #warn impl + +//? Advanced (tier 2) + +// todo: advanced + +/obj/item/rig_module/toolset/surgical/advanced + +//? Hardlight (tier 3) + +// todo: hardlight + +/obj/item/rig_module/toolset/surgical/hardlight diff --git a/code/modules/rigsuits/modules/rig_module.dm b/code/modules/rigsuits/modules/rig_module.dm new file mode 100644 index 000000000000..41c2467123c8 --- /dev/null +++ b/code/modules/rigsuits/modules/rig_module.dm @@ -0,0 +1,266 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * modules are modular items that can be inserted into specific complexity on the rig. + */ +/obj/item/rig_module + #warn impl all + + //* Core + /// lookup id + var/lookup_id + /// lookup prefix - set this please + var/lookup_prefix = "unkw" + + //* Balancing + /// complexity this takes up + /// + /// complexity is the metric used to balance complexity / variety of functions + /// more niche & powerful/varied gear takes up more complexity + var/module_complexity = 0 + /// volume this takes up + /// + /// volume is the metric used to balance offense/defense/storage + /// you generally can have two of the three, not all of the above + var/module_volume = 0 + /// weight to add to rigsuit + /// + /// stuff like heavy armor tends to be heavier. + var/module_weight = 0 + + //* Conflicts + /// single-conflict enum; if set, only one of this kind of module can exist on a rig + /// this will not conflict with any of the other conflict types! + var/global_conflict_enum + /// cannot put more than one of this type in the rig; + var/global_conflict_type + /// cannot put more than one of ourselves into the rig + var/global_conflict_self + /// zone-conflict enum; if set, only one our exact type can exist in any of our zones + var/zone_conflict_enum + /// cannot put more than one of this type in any of our zones + var/zone_conflict_type + /// cannot put more htan one of our own exact type into any of our zones + var/zone_conflict_self + + //* Defense + /// brute damage + var/brute_damage = 0 + /// burn damage + var/burn_damage = 0 + /// total integrity + var/max_health = 100 + + //* Registration + /// the rig we're on + var/obj/item/rig/host + /// registered low power draw in watts + var/registered_low_power = 0 + /// registered high power draw in watts + var/registered_high_power = 0 + + //* UI + //! todo: this is fucking evil + /// cached b64 string of our UI icon + var/cached_tgui_icon_b64 + /// is our UI update queued? + var/ui_update_queued = FALSE + /// TGUI route; this is handled by routes.tsx in the Rigsuit folder on TGUI! + var/tgui_interface = "inert" + + //* Zone + /// our zone define, e.g. RIG_ZONE_X + /// all = takes complexity from all zones + /// none = takes complexity from any zone + /// this is a define instead of a bit because of speed reasons. + var/zone = RIG_ZONE_NONE + /// allow automatically swapping handedness if we are only on one arm zone + /// via a certain tool + /// legs also count as handedness. + var/swap_handedness_tool = TOOL_SCREWDRIVER + +/obj/item/rig_module/Destroy() + if(host) + host.remove_module(src) + return ..() + +/obj/item/rig_module/dynamic_tool_query(obj/item/I, datum/event_args/actor/clickchain/e_args, list/hint_images) + . = list() + if(swap_handedness_tool) + .[swap_handedness_tool] = list("swap arm" = dyntool_image_neutral(swap_handedness_tool)) + return merge_double_lazy_assoc_list(., ..()) + +/obj/item/rig_module/tool_act(obj/item/I, datum/event_args/actor/clickchain/e_args, function, flags, hint) + if(function == swap_handedness_tool) + if(auto_swap_handedness()) + e_args.visible_feedback( + target = src, + range = MESSAGE_RANGE_CONFIGURATION, + visible = SPAN_NOTICE("[e_args.performer] tinkers with [src], mirroring its mechanism's orientations."), + audible = SPAN_NOTICE("You hear mechanisms being re-fastened."), + visible_self = SPAN_NOTICE("You swap the orientation on [src]."), + ) + log_construction(e_args, src, "swapped handedness") + else + e_args.chat_feedback( + SPAN_WARNING("failed to swap handedness on [src] despite being marked as handed and swappable; contact a coder."), + target = src, + ) + return TRUE + return ..() + +//* Attachment *// + +/** + * has final say over rig + * most regular checks should be rig-side + * + * @params + * * rig - the suit + * * rig_opinion - what does the suit say about this? + * * actor - (optional) person attaching + * * silent - (optional) should we inform the person attaching? + */ +/obj/item/rig_module/proc/can_attach(obj/item/rig/rig, rig_opinion, datum/event_args/actor/actor, silent) + return TRUE + +/** + * first check - 'does it make sense for this to be attached to the rig?' + * + * if not, specify why if not silent and actor is provided + * + * @params + * * rig - the suit + * * actor - (optional) person attaching + * * silent - (optional) should we inform the person attaching? + */ +/obj/item/rig_module/proc/valid_attach(obj/item/rig/rig, datum/event_args/actor/actor, silent) + return TRUE + +/** + * has final say over rig + * most regular checks should be rig-side + * + * @params + * * rig - the suit + * * rig_opinion - what does the suit say about this? + * * actor - (optional) person attaching + * * silent - (optional) should we inform the person attaching? + */ +/obj/item/rig_module/proc/can_detach(obj/item/rig/rig, rig_opinion, datum/event_args/actor/actor, silent) + return TRUE + +//* Console *// + +/** + * @return list(command = desc, ...) + */ +/obj/item/rig_module/proc/console_query(effective_control_flags, username) + return list() + +/** + * @return list(output, admin log text) + */ +/obj/item/rig_module/proc/console_process(effective_control_flags, username, command, list/arguments) + return list("unknown command", "") + +//* Power *// + +/obj/item/rig_module/proc/set_high_power_draw(watts) + #warn impl + +/obj/item/rig_module/proc/set_low_power_draw(watts) + #warn impl + +/obj/item/rig_module/proc/use_high_burst_power(joules) + return isnull(host)? 0 : host.draw_high_power(joules) + +/obj/item/rig_module/proc/use_low_burst_power(joules) + return isnull(host)? 0 : host.draw_low_power(joules) + +//* Setters *// + +/obj/item/rig_module/proc/set_module_weight(weight) + var/old = module_weight + module_weight = weight + host?.on_module_weight_change(src, old, weight) + +/obj/item/rig_module/proc/set_module_volume(volume) + var/old = module_volume + module_volume = volume + host?.on_module_volume_change(src, old, volume) + +/obj/item/rig_module/proc/set_module_complexity(complexity) + var/old = module_complexity + module_complexity = complexity + host?.on_module_complexity_change(src, old, complexity) + +/obj/item/rig_module/proc/set_module_zone(new_zone) + // yeah, nah, we're not handling this cleanly + ASSERT(!host) + zone = new_zone + +//* UI *// + +/obj/item/rig_module/proc/tgui_module_static() + return list( + "$tgui" = tgui_interface, + "$src" = ref(src), + ) + #warn impl + +/obj/item/rig_module/proc/tgui_module_data() + #warn impl + +/obj/item/rig_module/proc/tgui_push_data(list/data) + #warn impl + +/obj/item/rig_module/proc/tgui_queue_data() + #warn impl + +//* Zones *// + +/** + * detect handedness; + * this only makes sense if we are hand-based + * we do not check for hand zones existing here + * we do not check if our arms and legs are for some reason on different sides. + */ +/obj/item/rig_module/proc/unsafe_is_left_handed() + return rig_zone_to_bit[zone] & (RIG_ZONE_BIT_LEFT_ARM | RIG_ZONE_BIT_LEFT_LEG) + +/** + * detect handedness; + * this only makes sense if we are hand-based + * we do not check if our arms and legs are for some reason on different sides. + * + * @return RIG_HANDEDNESS_X enum + */ +/obj/item/rig_module/proc/safe_handedness() + var/our_bits = rig_zone_to_bit[zone] + if(our_bits & (RIG_ZONE_BIT_ARMS | RIG_ZONE_BIT_LEGS)) + return our_bits & (RIG_ZONE_BIT_LEFT_ARM | RIG_ZONE_BIT_LEFT_LEG) ? RIG_HANDEDNESS_LEFT : RIG_HANDEDNESS_RIGHT + return RIG_HANDEDNESS_NONE + +/** + * tries to swap handedness + * + * legs also count as handedness so uh + */ +/obj/item/rig_module/proc/auto_swap_handedness() + switch(zone) + if(RIG_ZONE_LEFT_ARM) + set_module_zone(RIG_ZONE_RIGHT_ARM) + return TRUE + if(RIG_ZONE_RIGHT_ARM) + set_module_zone(RIG_ZONE_LEFT_ARM) + return TRUE + if(RIG_ZONE_LEFT_LEG) + set_module_zone(RIG_ZONE_RIGHT_LEG) + return TRUE + if(RIG_ZONE_RIGHT_LEG) + set_module_zone(RIG_ZONE_LEFT_LEG) + return TRUE + else + return FALSE diff --git a/code/modules/rigsuits/modules/toolset.dm b/code/modules/rigsuits/modules/toolset.dm new file mode 100644 index 000000000000..0e0b3692abbd --- /dev/null +++ b/code/modules/rigsuits/modules/toolset.dm @@ -0,0 +1,16 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * Root subtype of toolset modules + * + * Toolset modules allow for deploying a set of items in hand. + * Said items work with item mounts/energy draw/etc. + */ +/obj/item/rig_module/toolset + abstract_type = /obj/item/rig_module/toolset + + /// list of items; set to a list of typepaths to init. + var/list/obj/item/items = list() + +#warn impl all diff --git a/code/modules/rigsuits/rig/activation.dm b/code/modules/rigsuits/rig/activation.dm new file mode 100644 index 000000000000..853eb4be9553 --- /dev/null +++ b/code/modules/rigsuits/rig/activation.dm @@ -0,0 +1,216 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/obj/item/rig/proc/set_activation_state(new_state) + activation_state = new_state + if(activation_state & (RIG_ACTIVATION_ACTIVATING | RIG_ACTIVATION_OFFLINE)) + set_weight(offline_weight) + set_encumbrance(offline_encumbrance) + else + set_weight(online_weight) + set_encumbrance(online_encumbrance) + +/obj/item/rig/proc/fully_activated() + return activation_state == RIG_ACTIVATION_ONLINE + +/obj/item/rig/proc/partially_activated() + return activation_state & RIG_ACTIVATION_IS_CYCLING + +/** + * checks if we're in the right inventory slot to activate. + */ +/obj/item/rig/proc/is_in_right_slot() + var/datum/inventory_slot_meta/wslot = wearer_required_slot_id + return worn_slot == (ispath(wslot)? initial(wslot.id) : wslot) + +/** + * blocking proc + * + * uses existing activation operation if one is in progress + * interrupts existing deactivation operation if one is in progress + * + * @return TRUE / FALSE success / failure + */ +/obj/item/rig/proc/activation_sequence(instant, deploy, auto_seal = TRUE, instant_seal, subtle, silent, force) + if(!is_in_right_slot()) + return FALSE + + ASSERT(!isnull(wearer)) + + if(activation_state == RIG_ACTIVATION_ONLINE) + return TRUE + interrupt_if_deactivating() + if(activation_mutex) + if(instant) + interrupt_if_activating() + else + return block_on_activation(activation_operation) + + if(deploy) + deploy_suit_async(FALSE, FALSE, force) + + activation_mutex = TRUE + activation_state = RIG_ACTIVATION_ACTIVATING + push_ui_data(data = list("activation" = RIG_ACTIVATION_ACTIVATING)) + maint_panel?.push_ui_data(data = list("activation" = RIG_ACTIVATION_ACTIVATING)) + + if(!instant) + wearer.visible_message( + SPAN_NOTICE("[src] hums to life, adjusting its seals as it starts to attach to [wearer].") + ) + + var/delay = instant? 0 : boot_delay + var/start_time = world.time + var/operation_id = activation_operation + + while(world.time < start_time + delay) + if(activation_operation != operation_id) + break + stoplag(1) + + if(activation_operation == operation_id) + activate(deploy, auto_seal, instant_seal, silent, subtle, force, instant) + ++activation_operation + activation_mutex = FALSE + +/** + * blocking proc + * + * uses existing activation operation if one is in progress + * interrupts existing deactivation operation if one is in progress + * + * @return TRUE / FALSE success / failure + */ +/obj/item/rig/proc/deactivation_sequence(instant, undeploy, silent, subtle, force) + if(activation_state == RIG_ACTIVATION_OFFLINE) + return TRUE + if(interrupt_if_activating()) + return TRUE + if(activation_mutex) + if(instant) + interrupt_if_deactivating() + else + return block_on_deactivation(activation_operation) + + activation_mutex = TRUE + activation_state = RIG_ACTIVATION_DEACTIVATING + push_ui_data(data = list("activation" = RIG_ACTIVATION_DEACTIVATING)) + maint_panel?.push_ui_data(data = list("activation" = RIG_ACTIVATION_DEACTIVATING)) + + if(!instant) + // wearer is not necessarily there for deactivation + wearer?.visible_message( + SPAN_NOTICE("[src] hums to life, adjusting its seals as it starts to detach from [wearer].") + ) + + var/delay = instant? 0 : boot_delay + var/start_time = world.time + var/operation_id = activation_operation + + while(world.time < start_time + delay) + if(activation_operation != operation_id) + break + stoplag(1) + + if(activation_operation == operation_id) + deactivate(silent, subtle, force, instant) + ++activation_operation + activation_mutex = FALSE + +/obj/item/rig/proc/activate(deploy, auto_seal, instant_seal, silent, subtle, force, was_instant) + if(!is_in_right_slot()) + return FALSE + + ASSERT(!isnull(wearer)) + + interrupt_if_deactivating() + + . = TRUE + + set_weight(online_weight) + set_encumbrance(online_encumbrance) + + ADD_TRAIT(src, TRAIT_ITEM_NODROP, RIG_TRAIT) + + if(activation_state != RIG_ACTIVATION_ONLINE) + activation_state = RIG_ACTIVATION_ONLINE + push_ui_data(data = list("activation" = RIG_ACTIVATION_ONLINE)) + maint_panel?.push_ui_data(data = list("activation" = RIG_ACTIVATION_ONLINE)) + + if(was_instant) + wearer.visible_message( + SPAN_NOTICE("[src] latches itself around [wearer], its seals and mechanisms locking snugly around their body.") + ) + else + wearer.visible_message( + SPAN_NOTICE("[src] finishes adjusting its seals around [wearer], snugly latching itself around their body") + ) + + if(deploy) + deploy_suit_async(auto_seal, instant_seal, force) + else if(auto_seal) + for(var/id in piece_lookup) + var/datum/component/rig_piece/piece = piece_lookup[id] + if(!piece.is_deployed()) + continue + INVOKE_ASYNC(src, PROC_REF(seal_piece_sync), piece) + +/obj/item/rig/proc/deactivate(undeploy, silent, subtle, force, was_instant) + set_weight(offline_weight) + set_encumbrance(offline_encumbrance) + + interrupt_if_activating() + + // todo: for now, undeploy/unseal is just instant, instead of being done before the main shutdown sequence + if(undeploy) + undeploy_suit_async(TRUE, force) + else + for(var/id in piece_lookup) + var/datum/component/rig_piece/piece = piece_lookup[id] + if(!piece.is_deployed()) + continue + unseal_piece_sync(piece, TRUE) + + REMOVE_TRAIT(src, TRAIT_ITEM_NODROP, RIG_TRAIT) + + if(activation_state != RIG_ACTIVATION_OFFLINE) + activation_state = RIG_ACTIVATION_OFFLINE + push_ui_data(data = list("activation" = RIG_ACTIVATION_OFFLINE)) + maint_panel?.push_ui_data(data = list("activation" = RIG_ACTIVATION_OFFLINE)) + + // wearer is not always there for deactivation + wearer?.visible_message( + SPAN_NOTICE("[src] completely detaches from [wearer], its lights and panels going dim.") + ) + +/** + * @return TRUE if op was interrupted + */ +/obj/item/rig/proc/interrupt_if_activating() + if(activation_state != RIG_ACTIVATION_ACTIVATING) + return FALSE + ++activation_operation + activation_mutex = FALSE + activation_state = RIG_ACTIVATION_OFFLINE + return TRUE + +/** + * @return TRUE if op was interrupted + */ +/obj/item/rig/proc/interrupt_if_deactivating() + if(activation_state != RIG_ACTIVATION_DEACTIVATING) + return TRUE + ++activation_operation + activation_mutex = FALSE + activation_state = RIG_ACTIVATION_ONLINE + return FALSE + +/obj/item/rig/proc/block_on_activation(operation_id) + // todo: behavior unverified; operation id is not checked. + UNTIL(!activation_mutex) + return activation_state == RIG_ACTIVATION_ONLINE + +/obj/item/rig/proc/block_on_deactivation(operation_id) + // todo: behavior unverified; operation id is not checked. + UNTIL(!activation_mutex) + return activation_state == RIG_ACTIVATION_OFFLINE diff --git a/code/modules/rigsuits/rig/armor.dm b/code/modules/rigsuits/rig/armor.dm new file mode 100644 index 000000000000..206f84cc9c87 --- /dev/null +++ b/code/modules/rigsuits/rig/armor.dm @@ -0,0 +1,21 @@ +//* hardcoded armor defines, to be replaced with components later *// + +/datum/armor/rigsuit + melee = 0.3 + melee_tier = MELEE_TIER_MEDIUM + melee_soak = 2 + melee_deflect = 2 + bullet = 0.2 + bullet_tier = BULLET_TIER_MEDIUM + bullet_soak = 2 + bullet_deflect = 2 + laser = 0.3 + laser_tier = LASER_TIER_MEDIUM + laser_soak = 2 + laser_deflect = 2 + energy = 0.1 + bomb = 0.3 + bio = 1.0 + rad = 0.5 + fire = 0.5 + acid = 1.0 diff --git a/code/modules/rigsuits/rig/console.dm b/code/modules/rigsuits/rig/console.dm new file mode 100644 index 000000000000..7aaec3ebc9d4 --- /dev/null +++ b/code/modules/rigsuits/rig/console.dm @@ -0,0 +1,21 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/obj/item/rig/proc/console_query(effective_control_flags, username) + return list( + "activate" = "Engage the hardsuit's systems and bind it to its wearer.", + "deactivate" = "Disengage the hardsuit's systems and unbinds it from its wearer", + ) + +/** + * @return list(text, log text override) + */ +/obj/item/rig/proc/console_process(effective_control_flags, username, command, list/arguments) + switch(command) + if("activate") + activation_sequence() + return list("activating...", "") + if("deactivate") + deactivation_sequence() + return list("deactivating...", "") + return list("unknown command", "") diff --git a/code/modules/rigsuits/rig/construction.dm b/code/modules/rigsuits/rig/construction.dm new file mode 100644 index 000000000000..ae2ec8a742a7 --- /dev/null +++ b/code/modules/rigsuits/rig/construction.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +// this is just a file saying 'we should add buildable rigs someday but i don't have a good idea on balancing' +// so it isn't done yet + +// L + ratio + rip bozo + lol + lmao diff --git a/code/modules/rigsuits/rig/control.dm b/code/modules/rigsuits/rig/control.dm new file mode 100644 index 000000000000..f0405d2192d6 --- /dev/null +++ b/code/modules/rigsuits/rig/control.dm @@ -0,0 +1,19 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/obj/item/rig/proc/effective_control_flags(mob/M) + if(is_admin_interactive(M)) + return RIG_CONTROL_FLAGS_ALL + // todo: rig control system + return M == wearer? RIG_CONTROL_FLAGS_WEARER : NONE + +/obj/item/rig/proc/check_control_flags(mob/M, control_flags) + return (effective_control_flags(M) & control_flags) == control_flags + +/** + * todo: this is kinda terrible and just a lazy patch rn. + */ +/obj/item/rig/proc/check_control_flags_or_reject(mob/M, control_flags) + . = check_control_flags(M, control_flags) + if(!.) + M.action_feedback(SPAN_WARNING("Insufficient access to perform command."), src) diff --git a/code/modules/rigsuits/rig/defense.dm b/code/modules/rigsuits/rig/defense.dm new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/modules/rigsuits/rig/environmentals.dm b/code/modules/rigsuits/rig/environmentals.dm new file mode 100644 index 000000000000..e09597820166 --- /dev/null +++ b/code/modules/rigsuits/rig/environmentals.dm @@ -0,0 +1,2 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// diff --git a/code/modules/rigsuits/rig/interaction.dm b/code/modules/rigsuits/rig/interaction.dm new file mode 100644 index 000000000000..37dafd5c7053 --- /dev/null +++ b/code/modules/rigsuits/rig/interaction.dm @@ -0,0 +1,45 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/obj/item/rig/attackby(obj/item/I, mob/living/user, list/params, clickchain_flags, damage_multiplier) + if(istype(I, /obj/item/rig_module)) + #warn impl + if(istype(I, /obj/item/cell)) + #warn impl + if(istype(I, /obj/item/tank)) + #warn impl + return ..() + +/obj/item/rig/tool_act(obj/item/I, datum/event_args/actor/clickchain/e_args, function, flags, hint) + . = ..() + if(.) + return + var/datum/rig_maint_panel/panel = request_maint() + panel.ui_interact(e_args.initiator) + +/obj/item/rig/context_query(datum/event_args/actor/e_args) + var/list/tuples = ..() + if(e_args.performer == wearer) + tuples[++tuples.len] = atom_context_tuple("access controls", image(src), 1, NONE) + tuples[++tuples.len] = atom_context_tuple("maint panel", image(src), 1, MOBILITY_CAN_USE) + +/obj/item/rig/context_act(datum/event_args/actor/e_args, key) + . = ..() + if(.) + return + switch(key) + if("access controls") + if(e_args.initiator != wearer) + return TRUE + ui_interact(e_args.initiator) + return TRUE + if("maint panel") + if(!e_args.performer.Reachability(src)) + e_args.chat_feedback( + SPAN_WARNING("You can't reach that right now."), + target = src, + ) + return TRUE + var/datum/rig_maint_panel/panel = request_maint() + panel.ui_interact(e_args.initiator) + return TRUE diff --git a/code/modules/rigsuits/rig/modules.dm b/code/modules/rigsuits/rig/modules.dm new file mode 100644 index 000000000000..67ebc9fc0210 --- /dev/null +++ b/code/modules/rigsuits/rig/modules.dm @@ -0,0 +1,34 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + + +#warn fuck + +/obj/item/rig/proc/add_module(obj/item/rig_module/module, datum/event_args/actor/actor) + +/obj/item/rig/proc/remove_module(obj/item/rig_module/module, datum/event_args/actor/actor) + +/obj/item/rig/proc/can_add_module(obj/item/rig_module/module, datum/event_args/actor/actor, silent) + +/obj/item/rig/proc/can_remove_module(obj/item/rig_module/module, datum/event_args/actor/actor, silent) + +/obj/item/rig/proc/try_add_module(obj/item/rig_module/module, datum/event_args/actor/actor, silent) + if(!can_add_module(module, actor, silent)) + return FALSE + return add_module(module, actor) + +/obj/item/rig/proc/try_remove_module(obj/item/rig_module/module, datum/event_args/actor/actor, silent) + if(!can_remove_module(module, actor, silent)) + return FALSE + return remove_module(module, actor) + +/obj/item/rig/proc/on_module_weight_change(obj/item/rig_module/module, old_value, new_value) + module_weight_tally += (new_value - old_value) + update_module_weight() + +/obj/item/rig/proc/on_module_complexity_change(obj/item/rig_module/module, old_value, new_value) + +/obj/item/rig/proc/on_module_volume_change(obj/item/rig_module/module, old_value, new_value) + +/obj/item/rig/proc/update_module_weight() + #warn impl diff --git a/code/modules/rigsuits/rig/pieces.dm b/code/modules/rigsuits/rig/pieces.dm new file mode 100644 index 000000000000..77403bed4dba --- /dev/null +++ b/code/modules/rigsuits/rig/pieces.dm @@ -0,0 +1,150 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/// Regarding sealing / unsealing +/// +/// RIG pieces are nominally sealed if the RIG is activated, +/// unsealed otherwise. +/// +/// They must unseal to retract. +/// They should seal after deployment. + +/** + * does not block + * + * @return TRUE/FALSE success/failure + */ +/obj/item/rig/proc/deploy_piece_async(datum/component/rig_piece/piece, auto_seal = TRUE, instant_seal, force, subtle, silent) + if(!piece.deploy(wearer, force? INV_OP_FORCE | INV_OP_CAN_DISPLACE : NONE, subtle, silent)) + return FALSE + piece.currently_retracting = FALSE + if(auto_seal) + INVOKE_ASYNC(src, PROC_REF(seal_piece_sync), piece, instant_seal, subtle, silent) + return TRUE + +/** + * blocks on unsealing + * + * @return TRUE/FALSE success/failure + */ +/obj/item/rig/proc/undeploy_piece_sync(datum/component/rig_piece/piece, instant_unseal, force, subtle, silent) + piece.currently_retracting = TRUE + if(!unseal_piece_sync(piece, instant_unseal, subtle, silent)) + return FALSE + if(!piece.currently_retracting) + return FALSE + piece.currently_retracting = FALSE + if(!piece.retract(force? INV_OP_FORCE : NONE, subtle, silent)) + return FALSE + return TRUE + +/** + * blocks on sealing + * + * if existing sealing operation is occurring, uses that instead of starting a new one. + * if existing unsealing operation is occurring, interrupts that. + * + * @return TRUE/FALSE success/failure + */ +/obj/item/rig/proc/seal_piece_sync(datum/component/rig_piece/piece, instant, subtle, silent) + if(activation_state != RIG_ACTIVATION_ONLINE) + return FALSE + return piece.seal_sync(instant, subtle, silent) + +/** + * blocks on unsealing + * + * if existing unsealing operation is occurring, uses that instead of starting a new one. + * if existing sealing operation is occurring, interrupts that. + * + * @return TRUE/FALSE success/failure + */ +/obj/item/rig/proc/unseal_piece_sync(datum/component/rig_piece/piece, instant, subtle, silent) + return piece.unseal_sync(instant, subtle, silent) + +/** + * deploys all pieces; non-blocking + */ +/obj/item/rig/proc/deploy_suit_async(auto_seal = TRUE, instant_seal, force, subtle, silent) + for(var/id in piece_lookup) + var/datum/component/rig_piece/piece = piece_lookup[id] + INVOKE_ASYNC(src, PROC_REF(deploy_piece_async), piece, auto_seal, instant_seal, force, subtle, silent) + return TRUE + +/** + * undeploys all pieces; non-blocking + */ +/obj/item/rig/proc/undeploy_suit_async(instant_unseal, force, subtle, silent) + for(var/id in piece_lookup) + var/datum/component/rig_piece/piece = piece_lookup[id] + if(!piece.is_deployed()) + continue + INVOKE_ASYNC(src, PROC_REF(undeploy_piece_sync), piece, instant_unseal, force, subtle, silent) + return TRUE + +/** + * undeploys all pieces; blocking + * + * can instantly fails if one piece fails, does not wait for the others. + * this behavior is not ensured 100% of the time. + * + * @return TRUE / FALSE on success / failure + */ +/obj/item/rig/proc/undeploy_suit_sync(instant_unseal, force, subtle, silent) + var/list/collected = list() + . = TRUE + for(var/id in piece_lookup) + var/datum/component/rig_piece/piece = piece_lookup[id] + if(!piece.is_deployed()) + continue + INVOKE_ASYNC(src, PROC_REF(undeploy_piece_sync), piece, instant_unseal, force, subtle, silent) + //* warning: shitcode ahead + //* this relies on the fact byond is single-threaded + //* because we know for sure that sync immediately sleeps, wihch means that + //* we can grab the operation id immediately + if(!piece.seal_mutex && (piece.sealed != RIG_PIECE_UNSEALED)) + // failed instantly + . = FALSE + else + collected[piece] = piece.seal_operation + if(!.) + return + for(var/datum/component/rig_piece/piece as anything in collected) + . = . && piece.block_on_unsealing(collected[piece]) + +/obj/item/rig/proc/add_piece(datum/component/rig_piece/piece) + LAZYINITLIST(piece_lookup) + if(piece_lookup[piece.lookup_prefix]) + var/count = 1 + do + piece.lookup_id = "[piece.lookup_prefix][++count]" + while(piece_lookup[piece.lookup_id]) + else + piece.lookup_id = piece.lookup_prefix + piece_lookup[piece.lookup_id] = piece + +/obj/item/rig/proc/remove_piece(datum/component/rig_piece/piece) + piece_lookup -= piece.lookup_id + // obliterates the component too + QDEL_NULL(piece.parent) + +/obj/item/rig/proc/legacy_sync_piece(datum/component/rig_piece/piece, sealed) + var/obj/item/physical = piece.parent + if(piece.rig_piece_flags & RIG_PIECE_APPLY_ARMOR) + physical.set_armor(suit_armor) + else + physical.reset_armor() + if((piece.rig_piece_flags & RIG_PIECE_APPLY_ENVIRONMENTALS) && sealed) + physical.max_pressure_protection = max_pressure_protect + physical.min_pressure_protection = min_pressure_protect + physical.max_heat_protection_temperature = max_temperature_protect + physical.min_cold_protection_temperature = min_temperature_protect + else + physical.max_pressure_protection = initial(physical.max_pressure_protection) + physical.min_pressure_protection = initial(physical.min_pressure_protection) + physical.max_heat_protection_temperature = initial(physical.max_heat_protection_temperature) + physical.min_cold_protection_temperature = initial(physical.min_cold_protection_temperature) + if(!piece.always_fully_insulated) + physical.siemens_coefficient = siemens_coefficient + else + physical.siemens_coefficient = 0 diff --git a/code/modules/rigsuits/rig/power.dm b/code/modules/rigsuits/rig/power.dm new file mode 100644 index 000000000000..c5671edb6a3a --- /dev/null +++ b/code/modules/rigsuits/rig/power.dm @@ -0,0 +1,14 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * use a burst of power from rig core capacitors + */ +/obj/item/rig/proc/draw_high_power(joules) + +/** + * use some power from rig cell + */ +/obj/item/rig/proc/draw_low_power(joules) + +#warn fuck diff --git a/code/modules/rigsuits/rig/rig.dm b/code/modules/rigsuits/rig/rig.dm new file mode 100644 index 000000000000..fbbf42c9dd98 --- /dev/null +++ b/code/modules/rigsuits/rig/rig.dm @@ -0,0 +1,297 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * # Rigsuits + * + * Modular suit system. + * + * ## Pieces + * + * * Pieces automatically un/seal based on activation state when un/deploying. + */ +/obj/item/rig + name = "rig control module" + desc = "A control module for some kind of suit." + w_class = WEIGHT_CLASS_HUGE + slot_flags = SLOT_BACK + + + #warn we gotta refactor actions for this lmao, esp for remote control (?!!) + action_button_name = "Debug UI" + + //* Activation + /// activation state + var/activation_state = RIG_ACTIVATION_OFFLINE + /// currently in an activation operation. + /// do not fuck with this var. + var/activation_mutex = FALSE + /// activation operation identifier - this lets us abort by just changing this while the loop is running + var/activation_operation = 0 + + //* Appearance (Self) + var/state_worn_sealed + var/state_worn_unsealed + var/state_sealed + var/state_unsealed + + //* Console + /// Our console + var/datum/rig_console/console + + //* Control + #warn todo + + //* Defense + + //* Environmentals + #warn todo + + //* Legacy - to be made into dynamic data once components/modules are done. + var/datum/armor/suit_armor + var/min_pressure_protect + var/max_pressure_protect + var/min_temperature_protect + var/max_temperature_protect + + //* Internal + /// lookup id + var/next_lookup_id = 0 + + //* Maintenance + /// panel datum to host our maint tgui + /// lazy-init'd + var/datum/rig_maint_panel/maint_panel + /// is the panel open? + var/maint_panel_open = FALSE + /// is the panel locked? + var/maint_panel_locked = FALSE + /// is the panel broken? + var/maint_panel_broken = FALSE + /// points of health left on the panel + var/maint_panel_integrity = 100 + /// points of health normally on the panel + var/maint_panel_integrity_max = 100 + /// armor on panel - lazy-grabbed + var/datum/armor/maint_panel_armor + /// armor type on panel + var/maint_panel_armor_type = /datum/armor/object/light + + //* Modules + /// list of /obj/item/rig_module's by its lookup_id + var/list/obj/item/rig_module/module_lookup + /// total weight of all modules + var/module_weight_tally = 0 + /// registered high power load from modules + var/module_power_high = 0 + /// registered low power load from modules + var/module_power_low = 0 + /// conflict enums and types + var/list/module_conflict_lookup + + //* Pieces + /// list of /datum/component/rig_piece's by its lookup_id + var/list/datum/component/rig_piece/piece_lookup + + //* Power + #warn todo. + + //* Stats - Base + /// startup/shutdown time + var/boot_delay + /// seal/unseal time + // todo: maybe by-piece ones too..? + var/seal_delay + /// offline weight + var/offline_weight + /// offline encumbrance + // todo: maybe by-piece ones too..? + var/offline_encumbrance + /// online weight + var/online_weight + /// online encumbrance + // todo: maybe by-piece ones too..? + var/online_encumbrance + + //* Theme + /// default theme + var/theme_preset = /datum/rig_theme/station/civilian/standard + /// Is our theme initialized? + var/theme_initialized = FALSE + /// theme name - this is the fluff/player facing name + var/theme_name = "Unknown" + + //* UI + /// update timerid + var/ui_queued_timer + /// core systems awaiting update + var/ui_queued_general = FALSE + /// piece + component + module list awaiting update + var/ui_queued_reflists = FALSE + /// such a big change has happened that everything is queued + var/ui_queued_everything = FALSE + /// pieces awaiting update + var/list/datum/component/rig_piece/ui_queued_pieces + /// modules awaiting update + var/list/obj/item/rig_module/ui_queued_modules + //! todo: this is fucking evil + /// cached b64 string of our UI icon + var/cached_tgui_icon_b64 + /// ui theme, set by the theme datum; if null, use default + #warn hook + var/ui_theme + + //* Wearer + /// Our wearer + var/mob/wearer + /// What slot we must be in - typepath or ID. + var/wearer_required_slot_id = /datum/inventory_slot_meta/inventory/back + + //* Zones + var/datum/rig_zone/z_head = new /datum/rig_zone/head + var/datum/rig_zone/z_chest = new /datum/rig_zone/chest + var/datum/rig_zone/z_left_arm = new /datum/rig_zone/left_arm + var/datum/rig_zone/z_right_arm = new /datum/rig_zone/right_arm + var/datum/rig_zone/z_left_leg = new /datum/rig_zone/left_leg + var/datum/rig_zone/z_right_leg = new /datum/rig_zone/right_leg + +#warn impl all + +//* Main + +/obj/item/rig/Initialize(mapload, datum/rig_theme/theme_like) + . = ..() + // todo: this is shitcode and just bypasses the init sleep check, if shit breaks idfk lmao + INVOKE_ASYNC(src, PROC_REF(init_theme), theme_like || theme_preset) + +/obj/item/rig/Destroy() + hard_reset() + wipe_everything() + QDEL_NULL(console) + QDEL_NULL(maint_panel) + QDEL_NULL(z_head) + QDEL_NULL(z_chest) + QDEL_NULL(z_left_arm) + QDEL_NULL(z_right_arm) + QDEL_NULL(z_left_leg) + QDEL_NULL(z_right_leg) + return ..() + +/obj/item/rig/examine(mob/user, dist) + . = ..() + . += SPAN_NOTICE("Alt-Click while holding this or nearby to access the back panel.") + +/obj/item/rig/context_query(datum/event_args/actor/e_args) + . = ..() + var/image/back_image = image(src) + .["panel"] = ATOM_CONTEXT_TUPLE("back panel", back_image, 1, MOBILITY_CAN_USE) + +/obj/item/rig/context_act(datum/event_args/actor/e_args, key) + switch(key) + if("panel") + var/datum/rig_maint_panel/panel = request_maint() + // todo: performer vs initiator + panel.ui_interact(e_args.performer) + return TRUE + return ..() + +/obj/item/rig/unequipped() + . = ..() + // todo: should we optimize this? + hard_reset() + wearer = null + +/obj/item/rig/dropped(mob/user, flags, atom/newLoc) + . = ..() + push_ui_data(data = list("wornCorrectly" = is_in_right_slot())) + +/obj/item/rig/equipped(mob/user, slot, flags) + . = ..() + var/right_slot = is_in_right_slot() + push_ui_data(data = list("wornCorrectly" = right_slot)) + if(right_slot) + wearer = user + +/obj/item/rig/proc/hard_reset() + deactivate(TRUE, TRUE, TRUE, TRUE) + +/obj/item/rig/proc/wipe_everything() + hard_reset() + for(var/id in piece_lookup) + var/datum/component/rig_piece/piece = piece_lookup[id] + remove_piece(piece) + #warn annihilate modules + z_head.reset_state_after_wipe() + z_chest.reset_state_after_wipe() + z_left_arm.reset_state_after_wipe() + z_right_arm.reset_state_after_wipe() + z_left_leg.reset_state_after_wipe() + z_right_leg.reset_state_after_wipe() + ui_queue_everything() + +/obj/item/rig/get_encumbrance() + return fully_activated()? online_encumbrance : offline_encumbrance + +/obj/item/rig/get_weight() + return fully_activated()? online_weight : offline_weight + +/obj/item/rig/update_icon_state() + if(partially_activated()) + icon_state = state_sealed + worn_state = state_worn_sealed + else + icon_state = state_unsealed + worn_state = state_worn_unsealed + return ..() + +#warn debug only code below + +/obj/item/rig/ui_action_click(datum/action/action, mob/user) + . = ..() + ui_interact(user) + +//* Console *// + +/obj/item/rig/proc/request_console() + RETURN_TYPE(/datum/rig_console) + if(isnull(console)) + console = new(src) + return console + +//* Inventory *// + +/obj/item/rig/strip_menu_options(mob/user) + . = ..() + .["maint"] = "Access Panel" + +/obj/item/rig/strip_menu_act(mob/user, action) + . = ..() + if(.) + return + if(action == "maint") + var/datum/rig_maint_panel/panel = request_maint() + panel.ui_interact(user) + return TRUE + +//* Maintenance *// + +/obj/item/rig/proc/request_maint() + RETURN_TYPE(/datum/rig_maint_panel) + if(isnull(maint_panel)) + maint_panel = new(src) + return maint_panel + +/obj/item/rig/proc/is_maint_panel_locked() + // todo: better access locking? maybe. for now, it's always unlocked if not being worn. + return maint_panel_locked && (activation_state == RIG_ACTIVATION_ONLINE) + +/obj/item/rig/proc/assert_maint_panel_armor() + #warn impl + +/obj/item/rig/proc/repair_maint_panel(datum/event_args/actor/actor, obj/item/tool) + +/obj/item/rig/proc/attack_maint_panel(datum/event_args/actor/actor, obj/item/tool, damage_multiplier = 1) + +/obj/item/rig/proc/cut_maint_panel(datum/event_args/actor/actor, obj/item/tool) + +#warn handling for armor etc etc diff --git a/code/modules/rigsuits/rig/rig_console.dm b/code/modules/rigsuits/rig/rig_console.dm new file mode 100644 index 000000000000..731e4c3541ed --- /dev/null +++ b/code/modules/rigsuits/rig/rig_console.dm @@ -0,0 +1,160 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +#define COMMAND_LOG_LIMIT 20 + +/** + * rigsuit command line + */ +/datum/rig_console + /// owner + var/obj/item/rig/host + /// list of log strings + var/list/command_logs = list() + +/datum/rig_console/New(obj/item/rig/rig) + src.host = rig + +/datum/rig_console/Destroy() + src.host = null + return ..() + +/datum/rig_console/proc/input_command(mob/user, string, log_command, effective_control_flags, username) + if(isnull(effective_control_flags)) + effective_control_flags = host.effective_control_flags(user) + // todo: identity system? + if(isnull(username)) + username = user.name + string = trim(string) + if(!length(string)) + return + if(log_command) + if(length(command_logs) > COMMAND_LOG_LIMIT) + command_logs.Cut(1, length(command_logs) - COMMAND_LOG_LIMIT + 1) + command_logs += "[string]" + var/list/split = split_command(string) + if(!length(split)) + return + var/list/result = process_command(user, split, effective_control_flags, username) + var/console_out = result[1] + var/log_out = result[2] + command_logs += "> [console_out]" + // todo: better logging lmao + log_game("RIG-CONSOLE: [key_name(user)] @ [REF(host)] ([key_name(host.wearer)]): [string] --> [log_out || console_out]") + // todo: should this really be here? + host.maint_panel?.push_ui_data(list("console" = tgui_console_data())) + return result + +/datum/rig_console/proc/split_command(mob/user, raw) + . = list() + var/pos = 1 + var/in_space = FALSE + for(var/i in 1 to length_char(raw)) + if(raw[i] != " ") + if(in_space) + in_space = FALSE + pos = i + continue + if(!in_space) + . += copytext_char(raw, 1, i) + in_space = TRUE + +/datum/rig_console/proc/tgui_console_data() + return list( + "lines" = command_logs, + ) + +/** + * @return list(text, log text override) + */ +/datum/rig_console/proc/process_command(mob/user, list/fragments, effective_control_flags, username) + RETURN_TYPE(/list) + switch(fragments[1]) + if("help") + var/list/built = list( + "-- Valid commands --", + "system \[command\]- Send a command to the hardsuit's OS; omit command for help.", + "piece \[id\] \[command\] - Send a command to one of the hardsuit's pieces; omit command for help, omit id for list.", + "module \[id\] \[command\] - Send a command to one of the hardsuit's modules; omit command for help, omit id for list.", + "deploy \[id\] \['seal'?\] - Send a command to a piece to deploy.", + "retract \[id\] - Send a command to a piece to retract.", + "seal \[id\] - Send a command to a piece to seal.", + "unseal \[id\] - Send a command to a piece to unseal.", + ) + return list(jointext(built, "
"), "") + if("system") + if(length(fragments) >= 2) + return host.console_process(effective_control_flags, username, fragments[2], fragments.Copy(3)) + else + var/list/query = host.console_query(effective_control_flags, username) + var/list/built = list() + for(var/command in query) + built += "[command] - [query[command]]" + return list(jointext(built, "
"), "") + if("module") + switch(length(fragments)) + if(1) + var/list/built = list("-- Module List --") + for(var/id in host.module_lookup) + built += id + return list(jointext(built, "
"), "") + if(2) + var/id = fragments[2] + if(isnull(host.module_lookup[id])) + return list("invalid module", "") + var/obj/item/rig_module/module = host.module_lookup[id] + var/list/query = module.console_query(effective_control_flags, username) + var/list/built = list("-- [id] commands --") + for(var/command in query) + built += "[command] - [query[command]]" + return list(jointext(built, "
"), "") + else + var/id = fragments[2] + if(isnull(host.module_lookup[id])) + return list("invalid module", "") + var/obj/item/rig_module/module = host.module_lookup[id] + return module.console_process(effective_control_flags, username, fragments[3], fragments.Copy(4)) + if("piece") + switch(length(fragments)) + if(1) + var/list/built = list("-- Piece List --") + for(var/id in host.piece_lookup) + built += id + return list(jointext(built, "
"), "") + if(2) + var/id = fragments[2] + if(isnull(host.piece_lookup[id])) + return list("invalid piece", "") + var/datum/component/rig_piece/piece = host.piece_lookup[id] + var/list/query = piece.console_query(effective_control_flags, username) + var/list/built = list("-- [id] commands --") + for(var/command in query) + built += "[command] - [query[command]]" + return list(jointext(built, "
"), "") + else + var/id = fragments[2] + if(isnull(host.piece_lookup[id])) + return list("invalid piece", "") + var/datum/component/rig_piece/piece = host.piece_lookup[id] + return piece.console_process(effective_control_flags, username, fragments[3], fragments.Copy(4)) + + if("deploy", "retract", "seal", "unseal") + switch(length(fragments)) + if(1) + var/list/built = list("-- Piece List --") + for(var/id in host.piece_lookup) + built += id + return list(jointext(built, "
"), "") + else + var/id = fragments[2] + if(isnull(host.piece_lookup[id])) + return list("invalid piece", "") + var/datum/component/rig_piece/piece = host.piece_lookup[id] + return piece.console_process(effective_control_flags, username, fragments[1], fragments.Copy(3)) + + else + return list("unknown command - type 'help' for help.", "") + +#warn impl all + +#undef COMMAND_LOG_LIMIT diff --git a/code/modules/rigsuits/rig/rig_maint_panel.dm b/code/modules/rigsuits/rig/rig_maint_panel.dm new file mode 100644 index 000000000000..1563422b98d3 --- /dev/null +++ b/code/modules/rigsuits/rig/rig_maint_panel.dm @@ -0,0 +1,99 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * separate maintenance handler from main rig interface + */ +/datum/rig_maint_panel + /// owner + var/obj/item/rig/host + +/datum/rig_maint_panel/New(obj/item/rig/rig) + src.host = rig + +/datum/rig_maint_panel/Destroy() + src.host = null + return ..() + +/datum/rig_maint_panel/ui_host() + return host + +/datum/rig_maint_panel/ui_static_data(mob/user, datum/tgui/ui) + . = ..() + .["activation"] = host.activation_state + var/list/assembled_ids = list() + for(var/id in host.piece_lookup) + assembled_ids += id + .["pieceIDs"] = assembled_ids + .["console"] = host.request_console().tgui_console_data() + .["theme"] = host.theme_name + // todo: b64 is shit + .["sprite64"] = host.cached_tgui_icon_b64 || (host.cached_tgui_icon_b64 = icon2base64(icon(host.icon, host.icon_state, SOUTH, 1, FALSE))) + #warn piece IDs updates + +/datum/rig_maint_panel/ui_data(mob/user, datum/tgui/ui) + . = ..() + // todo: update this more smartly with push data instead + .["panelLock"] = host.maint_panel_locked + .["panelOpen"] = host.maint_panel_open + .["panelBroken"] = host.maint_panel_broken + .["panelIntegrityRatio"] = host.maint_panel_integrity / host.maint_panel_integrity_max + +/datum/rig_maint_panel/ui_static_modules(mob/user, datum/tgui/ui) + . = ..() + for(var/id in host.piece_lookup) + var/datum/component/rig_piece/piece = host.piece_lookup[id] + .[piece.lookup_id] = list( + "id" = piece.lookup_id, + "sealed" = piece.sealed, + "deployed" = piece.is_deployed(), + ) + +/datum/rig_maint_panel/ui_act(action, list/params, datum/tgui/ui) + . = ..() + if(.) + return + switch(action) + if("smashPanel") + // todo: clickcd rewrite + if(usr.next_click > world.time) + return FALSE + host.attack_maint_panel(new /datum/event_args/actor(usr), usr.get_active_held_item()) + return TRUE + if("fixPanel") + host.repair_maint_panel(new /datum/event_args/actor(usr), usr.get_active_held_item()) + return FALSE + if("openPanel") + #warn impl + if("cutPanel") + host.cut_maint_panel(new /datum/event_args/actor(usr), usr.get_active_held_item()) + return FALSE + if("consoleInput") + var/raw = params["command"] + var/datum/rig_console/console = host.request_console() + console.input_command(usr, raw, effective_control_flags = RIG_CONTROL_FLAGS_MAINT_PANEL, username = "root") + return FALSE + if("forceUnseal") + var/piece = params["piece"] + #warn impl + if("forceSeal") + var/piece = params["piece"] + #warn impl + if("forceDeploy") + var/piece = params["piece"] + #warn impl + if("forceRetract") + var/piece = params["piece"] + #warn impl + +/datum/rig_maint_panel/ui_route(action, list/params, datum/tgui/ui, id) + #warn agh + return ..() + +/datum/rig_maint_panel/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "RigsuitMaintenance") + ui.open() + +#warn impl all diff --git a/code/modules/rigsuits/rig/rig_piece.dm b/code/modules/rigsuits/rig/rig_piece.dm new file mode 100644 index 000000000000..d5a117e8e3f2 --- /dev/null +++ b/code/modules/rigsuits/rig/rig_piece.dm @@ -0,0 +1,360 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/component/rig_piece + //* Appearance + /// display name - what is used for UIs + var/display_name + var/state_sealed + var/state_unsealed + var/state_worn_sealed + var/state_worn_unsealed + + //* Deployment + /// currently undeploying - used as a mutex-ish so deploy/undeploy can interrupt each other. + var/currently_retracting = FALSE + + //* Flags + /// piece intrinsic flags + var/rig_piece_flags = NONE + /// which zones we count as + var/rig_zone_bits = NONE + /// inventory hide flags when sealed + var/inv_hide_flags_sealed + /// inventory hide flags when unsealed + var/inv_hide_flags_unsealed + + //* RIG / Piece + /// Our controller + var/obj/item/rig/controller + /// Our deployment slot + var/inventory_slot + /// our controller-unique ID; this is set per controller + var/lookup_id + /// lookup prefix + var/lookup_prefix = "unkw" + + //* Sealing + /// are we sealed? + /// if we're currently cycling, seal_mutex should always be TRUE. the opposite is not always the case. + var/sealed = RIG_PIECE_UNSEALED + /// in the process of a seal operation - used internally as a mutex. don't fuck with this var. + /// this is set to TRUE at the start of a cycle and set to FALSe after + /// do not use this to interrupt, do not fuck with this var. + var/seal_mutex = FALSE + /// the cycle of the sealing operation we're in, so we can override existing ones by changing this number + /// when a new operation starts, the current number is used; interrupting or finishing adds 1. + var/seal_operation = 0 + + //* Stats + //! todo: legacy + /// insulated gloves support + var/always_fully_insulated = FALSE + + //* UI + //! todo: this is fucking evil + /// cached b64 string of our UI icon + var/cached_tgui_icon_b64 + /// is our UI update queued? + var/ui_update_queued = FALSE + +/datum/component/rig_piece/Initialize(obj/item/rig/controller) + . = ..() + if(. & COMPONENT_INCOMPATIBLE) + return + if(!isitem(parent)) + return . | COMPONENT_INCOMPATIBLE + src.controller = controller + #warn impl + +/datum/component/rig_piece/Destroy() + controller = null + return ..() + +/datum/component/rig_piece/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_ITEM_UNEQUIPPED, PROC_REF(signal_unequipped)) + +/datum/component/rig_piece/UnregisterFromParent() + // todo: optimize this + interrupt_if_sealing() + interrupt_if_unsealing() + . = ..() + UnregisterSignal(parent, COMSIG_ITEM_UNEQUIPPED) + +/datum/component/rig_piece/proc/signal_unequipped(datum/source, mob/unequipper, slot, flags) + SIGNAL_HANDLER + if(flags & INV_OP_SHOULD_NOT_INTERCEPT) + return + retract(INV_OP_FORCE) + return COMPONENT_ITEM_INV_OP_RELOCATE | COMPONENT_ITEM_INV_OP_SUPPRESS_SOUND + +/datum/component/rig_piece/proc/tgui_piece_data() + var/obj/item/physical = parent + #warn needs to have better caching for the b64, this is way too slow for production + return list( + "$tgui" = "RigsuitPiece", + "$src" = RIG_UI_ENCODE_MODULE_REF(ref(src)), + "name" = display_name, + "id" = lookup_id, + "sealed" = sealed, + "deployed" = is_deployed(), + "flags" = rig_piece_flags, + "sprite64" = isnull(cached_tgui_icon_b64)? (cached_tgui_icon_b64 = icon2base64(icon(physical.icon, state_sealed, SOUTH, 1, FALSE))) : cached_tgui_icon_b64 + ) + +/datum/component/rig_piece/proc/push_piece_data(list/data) + controller?.push_ui_modules(updates = list(RIG_UI_ENCODE_PIECE_REF(src) = data)) + +/datum/component/rig_piece/proc/update_piece_data() + if(ui_update_queued) + return + ui_update_queued = TRUE + controller?.ui_queue_piece(src) + +/datum/component/rig_piece/proc/seal_sync(instant, silent, subtle) + if(sealed == RIG_PIECE_SEALED) + return TRUE + interrupt_if_unsealing() + // we're still sealing / not interrupted + if(seal_mutex) + // if instant, interrupt anyways + if(instant) + interrupt_if_sealing() + else + return block_on_sealing(seal_operation) + seal_mutex = TRUE + sealed = RIG_PIECE_SEALING + push_piece_data(list("sealed" = RIG_PIECE_SEALING)) + #warn maint panel + + var/delay = instant? 0 : controller.seal_delay + var/start_time = world.time + var/operation_id = seal_operation + + while(world.time < start_time + delay) + if(seal_operation != operation_id) + break + stoplag(1) + + if(seal_operation == operation_id) + seal(silent, subtle) + ++seal_operation + seal_mutex = FALSE + + return sealed == RIG_PIECE_SEALED + +/datum/component/rig_piece/proc/unseal_sync(instant, silent, subtle) + if(sealed == RIG_PIECE_UNSEALED) + return TRUE + interrupt_if_sealing() + // we're still sealing / not interrupted + if(seal_mutex) + // if instant, interrupt anyways + if(instant) + interrupt_if_unsealing() + else + return block_on_unsealing(seal_operation) + seal_mutex = TRUE + sealed = RIG_PIECE_UNSEALING + push_piece_data(list("sealed" = RIG_PIECE_UNSEALING)) + #warn maint panel + + var/delay = instant? 0 : controller.seal_delay + var/start_time = world.time + var/operation_id = seal_operation + + while(world.time < start_time + delay) + if(seal_operation != operation_id) + break + stoplag(1) + + if(seal_operation == operation_id) + unseal(silent, subtle) + ++seal_operation + seal_mutex = FALSE + + return sealed == RIG_PIECE_UNSEALED + +/** + * @params + * * silent - suppress sound + * * subtle - suppress message + */ +/datum/component/rig_piece/proc/seal(silent, subtle) + // todo: sound + // todo: feedback visual? + var/obj/item/physical = parent + physical.worn_state = state_worn_sealed + physical.icon_state = state_sealed + physical.update_worn_icon() + controller.legacy_sync_piece(src, TRUE) + sealed = RIG_PIECE_SEALED + #warn maint panel + update_piece_data() + + if(!is_deployed()) + stack_trace("not properly deployed?") + + ADD_TRAIT(src, TRAIT_ITEM_NODROP, RIG_TRAIT) + + if(!subtle) + physical.visible_message( + SPAN_NOTICE("[physical] snugly latches around [controller.wearer]."), + range = MESSAGE_RANGE_INVENTORY_SOFT, + ) + + return TRUE + +/** + * @params + * * actor - (optional) actor data for this action + * * silent - suppress sound + * * subtle - suppress message + */ +/datum/component/rig_piece/proc/unseal(silent, subtle) + // todo: sound + // todo: feedback visual? + var/obj/item/physical = parent + physical.worn_state = state_worn_unsealed + physical.icon_state = state_unsealed + physical.update_worn_icon() + controller.legacy_sync_piece(src, FALSE) + sealed = RIG_PIECE_UNSEALED + #warn maint panel + update_piece_data() + + REMOVE_TRAIT(src, TRAIT_ITEM_NODROP, RIG_TRAIT) + + if(!subtle) + physical.visible_message( + SPAN_NOTICE("[physical] unlatches from [controller.wearer]."), + range = MESSAGE_RANGE_INVENTORY_SOFT, + ) + + return TRUE + +/datum/component/rig_piece/proc/deploy(mob/onto, inv_op_flags, subtle, silent) + if(isnull(onto)) + return FALSE + var/obj/item/I = parent + if(I.loc == onto) + return TRUE + else if(I.loc != controller) + retract(INV_OP_FORCE, TRUE, TRUE) + if(isnull(inventory_slot)) + return FALSE + . = onto.equip_to_slot_if_possible(I, inventory_slot, inv_op_flags, onto) + if(!.) + return + // todo: some kind of visual feedback to people around them? + #warn impl - audio only + #warn maint panel + update_piece_data() + +/datum/component/rig_piece/proc/retract(inv_op_flags, subtle, silent) + var/obj/item/I = parent + if(I.loc == controller) + return TRUE + + switch(sealed) + if(RIG_PIECE_SEALED) + unseal() + if(RIG_PIECE_SEALING) + interrupt_if_sealing() + if(RIG_PIECE_UNSEALING) + interrupt_if_unsealing() + unseal() + + var/mob/wearing = I.worn_mob() + if(isnull(wearing)) + I.forceMove(controller) + else + . = wearing.transfer_item_to_loc(I, controller, inv_op_flags | INV_OP_SHOULD_NOT_INTERCEPT, wearing) + if(!.) + return + // todo: some kind of visual feedback to people around them? + #warn impl - audio only + #warn maint panel + update_piece_data() + +/datum/component/rig_piece/proc/is_deployed() + var/obj/item/I = parent + if(ispath(inventory_slot)) + var/datum/inventory_slot_meta/slot_meta = inventory_slot + inventory_slot = initial(slot_meta.id) + return I.worn_slot == inventory_slot + +/** + * interrupt if sealing + */ +/datum/component/rig_piece/proc/interrupt_if_sealing() + if(sealed != RIG_PIECE_SEALING) + return + sealed = RIG_PIECE_UNSEALED + ++seal_operation + seal_mutex = FALSE + +/** + * interrupt if unsealing + */ +/datum/component/rig_piece/proc/interrupt_if_unsealing() + if(sealed != RIG_PIECE_UNSEALING) + return + sealed = RIG_PIECE_SEALED + ++seal_operation + seal_mutex = FALSE + +/datum/component/rig_piece/proc/block_on_sealing(operation_id) + // todo: behavior unverified; operation id is not checked. + UNTIL(!(seal_mutex && (sealed == RIG_PIECE_SEALING))) + return sealed == RIG_PIECE_SEALED + +/datum/component/rig_piece/proc/block_on_unsealing(operation_id) + // todo: behavior unverified; operation id is not checked. + UNTIL(!(seal_mutex && (sealed == RIG_PIECE_UNSEALING))) + return sealed == RIG_PIECE_UNSEALED + +//* Console *// + +/** + * @return list(command = desc, ...) + */ +/datum/component/rig_piece/proc/console_query(effective_control_flags, username) + return list( + "deploy \['seal'?\]" = "Deploy to user. Use 'deploy seal' to seal after deployment.", + "retract" = "Retract into controller.", + "seal" = "Seal around user", + "unseal" = "Unseal from user.", + ) + +/** + * @return list(output, admin log text) + */ +/datum/component/rig_piece/proc/console_process(effective_control_flags, username, command, list/arguments) + switch(command) + if("deploy") + #warn impl + if("retract") + #warn impl + if("seal") + #warn impl + if("unseal") + #warn impl + return list("unknown command", "") + +//* Base Piece Defs *// + +/obj/item/clothing/head/rig + +/obj/item/clothing/suit/rig + +/obj/item/clothing/gloves/rig + +/obj/item/clothing/gloves/rig/equip_worn_over_check(mob/M, slot, mob/user, obj/item/I, flags) + return !istype(I, /obj/item/clothing/gloves/rig) + +/obj/item/clothing/shoes/rig + +/obj/item/clothing/shoes/rig/equip_worn_over_check(mob/M, slot, mob/user, obj/item/I, flags) + return !istype(I, /obj/item/clothing/shoes/rig) diff --git a/code/modules/rigsuits/rig/rig_zone.dm b/code/modules/rigsuits/rig/rig_zone.dm new file mode 100644 index 000000000000..804cdfb00d49 --- /dev/null +++ b/code/modules/rigsuits/rig/rig_zone.dm @@ -0,0 +1,65 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * to avoid having 10 lists, we instead have 6 datums :/ + */ +/datum/rig_zone + //* STATIC VALUES, DO NOT CHANGE AT RUNTIME *// + var/zone_enum + var/zone_bit + + //* State Store *// + /// modules registered on us + var/list/obj/item/rig_module/modules + /// our brute damage + var/brute_damage = 0 + /// our burn damage + var/burn_damage = 0 + /// our max integrity + var/max_integrity = 100 + /// used complexity + var/complexity_used = 0 + /// complexity available + var/complexity_capacity = 0 + /// used volume + var/volume_used = 0 + /// volume available + var/volume_capacity = 0 + +/datum/rig_zone/Destroy() + if(length(modules)) + stack_trace("deleted with active modules") + modules = null + return ..() + +/datum/rig_zone/proc/reset_state_after_wipe() + ASSERT(!length(modules)) + brute_damage = 0 + burn_damage = 0 + complexity_used = 0 + volume_used = 0 + +/datum/rig_zone/head + zone_enum = RIG_ZONE_HEAD + zone_bit = RIG_ZONE_BIT_HEAD + +/datum/rig_zone/chest + zone_enum = RIG_ZONE_CHEST + zone_bit = RIG_ZONE_BIT_CHEST + +/datum/rig_zone/left_arm + zone_enum = RIG_ZONE_LEFT_ARM + zone_bit = RIG_ZONE_BIT_LEFT_ARM + +/datum/rig_zone/right_arm + zone_enum = RIG_ZONE_RIGHT_ARM + zone_bit = RIG_ZONE_BIT_RIGHT_ARM + +/datum/rig_zone/left_leg + zone_enum = RIG_ZONE_LEFT_LEG + zone_bit = RIG_ZONE_BIT_LEFT_LEG + +/datum/rig_zone/right_leg + zone_enum = RIG_ZONE_RIGHT_LEG + zone_bit = RIG_ZONE_BIT_RIGHT_LEG diff --git a/code/modules/rigsuits/rig/themes.dm b/code/modules/rigsuits/rig/themes.dm new file mode 100644 index 000000000000..3041c325968a --- /dev/null +++ b/code/modules/rigsuits/rig/themes.dm @@ -0,0 +1,28 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/obj/item/rig/proc/init_theme(datum/rig_theme/override) + if(isnull(override)) + override = theme_preset + if(isnull(override)) + override = initial(theme_preset) + if(ispath(override)) + override = fetch_rig_theme(override) + ASSERT(istype(override)) + wipe_everything() + theme_initialized = TRUE + var/datum/rig_theme/initializing = override + initializing.imprint_control_appearance(src) + initializing.imprint_control_behavior(src) + initializing.imprint_control_legacy(src) + for(var/datum/rig_theme_piece/piece_data as anything in initializing.pieces) + var/datum/component/rig_piece/piece = piece_data.instantiate(initializing, src) + add_piece(piece) + legacy_sync_piece(piece) + // todo: modules + ui_queue_everything() + +/obj/item/rig/proc/ensure_theme() + if(theme_initialized) + return + init_theme() diff --git a/code/modules/rigsuits/rig/ui.dm b/code/modules/rigsuits/rig/ui.dm new file mode 100644 index 000000000000..6784f3d602c4 --- /dev/null +++ b/code/modules/rigsuits/rig/ui.dm @@ -0,0 +1,308 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +//* WELCOME TO THE SEVENTH CIRCLE OF WEBDEV HELL +/// For efficiency, rigs will internally track and cache what needs to update. +/// We heavily abuse the TGUI modules system to selectively update data +/// with the module system's 2-deep reducer. + +/obj/item/rig/proc/ui_queue() + ui_queued_general = TRUE + if(isnull(ui_queued_timer)) + ui_queued_timer = addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/item/rig, ui_flush), 0, TIMER_STOPPABLE)) + +/obj/item/rig/proc/ui_queue_piece(datum/component/rig_piece/piece) + LAZYDISTINCTADD(ui_queued_pieces, piece) + if(isnull(ui_queued_timer)) + ui_queued_timer = addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/item/rig, ui_flush), 0, TIMER_STOPPABLE)) + +/obj/item/rig/proc/ui_queue_module(obj/item/rig_module/module) + LAZYDISTINCTADD(ui_queued_modules, module) + if(isnull(ui_queued_timer)) + ui_queued_timer = addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/item/rig, ui_flush), 0, TIMER_STOPPABLE)) + +/obj/item/rig/proc/ui_queue_reflists() + ui_queued_reflists = TRUE + if(isnull(ui_queued_timer)) + ui_queued_timer = addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/item/rig, ui_flush), 0, TIMER_STOPPABLE)) + +/obj/item/rig/proc/ui_queue_everything() + ui_queued_everything = TRUE + if(isnull(ui_queued_timer)) + ui_queued_timer = addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/item/rig, ui_flush), 0, TIMER_STOPPABLE)) + +/obj/item/rig/proc/ui_flush() + if(!isnull(ui_queued_timer)) + deltimer(ui_queued_timer) + if(ui_queued_everything) + ui_queued_everything = FALSE + ui_queued_general = FALSE + ui_queued_reflists = FALSE + ui_queued_pieces = null + ui_queued_modules = null + + var/list/assembled_fragments = list() + var/list/piece_refs = list() + var/list/module_refs = list() + + for(var/id in piece_lookup) + var/datum/component/rig_piece/piece = piece_lookup[id] + var/reference = RIG_UI_ENCODE_PIECE_REF(ref(piece)) + assembled_fragments[reference] = piece.tgui_piece_data() + piece_refs += reference + piece.ui_update_queued = FALSE + + for(var/id in module_lookup) + var/obj/item/rig_module/module = module_lookup[id] + var/reference = RIG_UI_ENCODE_MODULE_REF(ref(module)) + // todo: for when modules are made + #warn module data + how to handle data vs static? + assembled_fragments[reference] = list() + module_refs += reference + module.ui_update_queued = FALSE + + // control flags skipped as it's per-person, and is updated later. + push_ui_data(data = list( + "pieceRefs" = piece_refs, + "moduleRefs" = module_refs, + "theme" = theme_name, + )) + push_ui_modules(updates = assembled_fragments) + return + + if(ui_queued_general) + ui_queued_general = FALSE + + if(ui_queued_reflists) + ui_queued_reflists = FALSE + var/list/piece_refs = list() + var/list/module_refs = list() + for(var/id in piece_lookup) + var/datum/component/rig_piece/piece = piece_lookup[id] + var/reference = RIG_UI_ENCODE_PIECE_REF(ref(piece)) + piece_refs += reference + for(var/id in module_lookup) + var/obj/item/rig_module/module = module_lookup[id] + var/reference = RIG_UI_ENCODE_MODULE_REF(ref(module)) + module_refs += reference + push_ui_data(data = list( + "pieceRefs" = piece_refs, + "moduleRefs" = module_refs, + )) + + if(length(ui_queued_modules)) + var/list/assembled_fragments = list() + + for(var/obj/item/rig_module/module as anything in ui_queued_modules) + var/reference = RIG_UI_ENCODE_MODULE_REF(ref(module)) + // todo: for when modules are made + assembled_fragments[reference] = list() + module.ui_update_queued = FALSE + + push_ui_modules(updates = assembled_fragments) + ui_queued_modules = null + + if(length(ui_queued_pieces)) + var/list/assembled_fragments = list() + + for(var/datum/component/rig_piece/piece as anything in ui_queued_pieces) + var/reference = RIG_UI_ENCODE_PIECE_REF(ref(piece)) + assembled_fragments[reference] = piece.tgui_piece_data() + piece.ui_update_queued = FALSE + + push_ui_modules(updates = assembled_fragments) + ui_queued_pieces = null + +#warn impl all + +/obj/item/rig/ui_static_data(mob/user, datum/tgui/ui) + #warn icecream update will modify this, check the root definition! + . = ..() + .["theme"] = theme_name + .["windowTheme"] = ui_theme || detect_ui_theme() + .["activation"] = activation_state + .["sprite64"] = isnull(cached_tgui_icon_b64)? (cached_tgui_icon_b64 = icon2base64(icon(icon, state_sealed, SOUTH, 1, FALSE))) : cached_tgui_icon_b64 + var/list/piece_refs = list() + var/list/module_refs = list() + for(var/id in piece_lookup) + var/datum/component/rig_piece/piece = piece_lookup[id] + var/reference = RIG_UI_ENCODE_PIECE_REF(ref(piece)) + piece_refs += reference + for(var/id in module_lookup) + var/obj/item/rig_module/module = module_lookup[id] + var/reference = RIG_UI_ENCODE_MODULE_REF(ref(module)) + // todo: for when modules are made + module_refs += reference + .["pieceRefs"] = piece_refs + .["moduleRefs"] = module_refs + .["wornCorrectly"] = is_in_right_slot() + +/obj/item/rig/ui_data(mob/user, datum/tgui/ui) + . = ..() + // todo: maybe cache this + .["controlFlags"] = effective_control_flags(user) + +/obj/item/rig/ui_static_modules(mob/user, datum/tgui/ui) + . = ..() + for(var/obj/item/rig_module/module as anything in null) + // todo: modules + .[RIG_UI_ENCODE_MODULE_REF(ref(module))] = list() + for(var/id in piece_lookup) + var/datum/component/rig_piece/piece = piece_lookup[id] + .[RIG_UI_ENCODE_PIECE_REF(ref(piece))] = piece.tgui_piece_data() + +/obj/item/rig/ui_route(action, list/params, datum/tgui/ui, id) + . = ..() + if(.) + return + // P / M switch + // todo: need fast lookup for module control + switch(id[1]) + if("P") + var/lookup_id = params["id"] + #warn piece control (?) + return TRUE + if("M") + var/lookup_id = params["id"] + // todo: route to modules + return TRUE + #warn impl :/ + +/obj/item/rig/ui_act(action, list/params, datum/tgui/ui) + . = ..() + if(.) + return + var/mob/user = usr + switch(action) + if("activation") + var/desired = text2num(params["on"]) + // todo: better reject + if(!check_control_flags_or_reject(user, RIG_CONTROL_ACTIVATION)) + return TRUE + if(isnull(desired)) + return TRUE + if(desired) + if(activation_state == RIG_ACTIVATION_ACTIVATING || activation_state == RIG_ACTIVATION_ONLINE) + return TRUE + INVOKE_ASYNC(src, PROC_REF(activation_sequence)) + else + if(activation_state == RIG_ACTIVATION_DEACTIVATING || activation_state == RIG_ACTIVATION_OFFLINE) + return TRUE + INVOKE_ASYNC(src, PROC_REF(deactivation_sequence)) + return TRUE + if("seal") + if(activation_state != RIG_ACTIVATION_ONLINE) + return TRUE + var/piece = params["piece"] + var/desired = text2num(params["on"]) + // todo: better reject + if(!check_control_flags_or_reject(user, RIG_CONTROL_PIECES)) + return TRUE + if(isnull(desired)) + return TRUE + if(isnull(piece)) + return TRUE + var/datum/component/rig_piece/piece_instance = piece_lookup[piece] + if(isnull(piece_instance)) + return TRUE + if(desired) + if(piece_instance.sealed == RIG_PIECE_SEALING || piece_instance.sealed == RIG_PIECE_SEALED) + return TRUE + INVOKE_ASYNC(src, PROC_REF(seal_piece_sync), piece_instance) + else + if(piece_instance.sealed == RIG_PIECE_UNSEALING || piece_instance.sealed == RIG_PIECE_UNSEALED) + return TRUE + INVOKE_ASYNC(src, PROC_REF(unseal_piece_sync), piece_instance) + return TRUE + if("deploy") + var/piece = params["piece"] + var/desired = text2num(params["on"]) + // todo: better reject + if(!check_control_flags_or_reject(user, RIG_CONTROL_PIECES)) + return TRUE + if(isnull(desired)) + return TRUE + if(isnull(piece)) + if(desired) + deploy_suit_async(TRUE) + else + undeploy_suit_async() + return TRUE + else + var/datum/component/rig_piece/piece_instance = piece_lookup[piece] + if(isnull(piece)) + return TRUE + if(desired) + if(piece_instance.is_deployed()) + return TRUE + deploy_piece_async(piece_instance, TRUE) + else + if(!piece_instance.is_deployed()) + return TRUE + INVOKE_ASYNC(src, PROC_REF(undeploy_piece_sync), piece_instance) + return TRUE + #warn impl all + +/obj/item/rig/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(isnull(ui)) + ui = new(user, src, "RigsuitController") + ui.open() + +/obj/item/rig/ui_state() + // give always state because we manually override all of this in ui_status + #warn shitcode lol + return GLOB.always_state + +/obj/item/rig/ui_status(mob/user, datum/ui_state/state) + #warn shitcode lol + // "guys, I swear I have a reason to reimplement all of this" + // rigs use a very complicated status system so things like AI control can be done + // it'd be a better idea to use state datums but i don't care + + // if they're admin abusing, give full control + if(is_admin_interactive(user, src)) + return UI_INTERACTIVE + + // check if they should be able to view + var/can_observe = isobserver(user) && user.client?.roughly_within_render_distance(src) + + #warn impl - default state behaviors + #warn this is just a testing shim + return UI_INTERACTIVE + +/* + var/src_object = ui_host(user) + . = UI_CLOSE + if(!state) + return + + if(isobserver(user)) + // If they turn on ghost AI control, admins can always interact. + if(IsAdminGhost(user)) + . = max(., UI_INTERACTIVE) + + // Regular ghosts can always at least view if in range. + if(user.client) + // todo: in view range for zooming + if(get_dist(src_object, user) < max(CEILING(user.client.current_viewport_width / 2, 1), CEILING(user.client.current_viewport_height / 2, 1))) + . = max(., UI_UPDATE) + + // Check if the state allows interaction + var/result = state.can_use_topic(src_object, user) + . = max(., result) +*/ + +/** + * attempts to detect theme from wearer, if ui theme is not being forced + */ +/obj/item/rig/proc/detect_ui_theme() + var/mob/living/carbon/human/casted = wearer + if(!istype(casted)) + return + if(!isnull(casted.nif)) + // 1: attempt to detect from nif + var/detected = casted.nif.save_data["ui_theme"] + if(detected in global.all_tgui_themes) + return detected + // failed diff --git a/code/modules/rigsuits/rig/zones.dm b/code/modules/rigsuits/rig/zones.dm new file mode 100644 index 000000000000..aa4d25516d75 --- /dev/null +++ b/code/modules/rigsuits/rig/zones.dm @@ -0,0 +1,35 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/obj/item/rig/proc/zone_datums_for_bits(bits) + . = list() + if(bits & RIG_ZONE_HEAD) + . += z_head + if(bits & RIG_ZONE_CHEST) + . += z_chest + if(bits & RIG_ZONE_LEFT_ARM) + . += z_left_arm + if(bits & RIG_ZONE_RIGHT_ARM) + . += z_right_arm + if(bits & RIG_ZONE_LEFT_LEG) + . += z_left_leg + if(bits & RIG_ZONE_RIGHT_LEG) + . += z_right_leg + +/obj/item/rig/proc/zone_datums_for_enum(enum) + . = zone_datums_for_bits(global.rig_zone_to_bit[enum]) + +/obj/item/rig/proc/zone_datum_for_enum(enum) + switch(enum) + if(RIG_ZONE_HEAD) + return z_head + if(RIG_ZONE_CHEST) + return z_chest + if(RIG_ZONE_LEFT_ARM) + return z_left_arm + if(RIG_ZONE_RIGHT_ARM) + return z_right_arm + if(RIG_ZONE_LEFT_LEG) + return z_left_leg + if(RIG_ZONE_RIGHT_LEG) + return z_right_leg diff --git a/code/modules/rigsuits/themes/faction/mercenary.dm b/code/modules/rigsuits/themes/faction/mercenary.dm new file mode 100644 index 000000000000..6faef0e65ef9 --- /dev/null +++ b/code/modules/rigsuits/themes/faction/mercenary.dm @@ -0,0 +1,128 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * generic bad-or-maybe-bad guy suits + */ +/datum/rig_theme/mercenary + abstract_type = /datum/rig_theme/mercenary + // bye-bye security + siemens_coefficient = 0.5 + +AUTO_RIG_THEME(/mercenary/gorlex) +/datum/rig_theme/mercenary/gorlex + abstract_type = /datum/rig_theme/mercenary/gorlex + base_icon = 'icons/modules/rigsuits/suits/factions/military_gorlex.dmi' + +AUTO_RIG_THEME(/mercenary/gorlex_raider) +/datum/rig_theme/mercenary/gorlex_raider + name = "nukeops rig" + base_state = "syndicate" + desc = "TBD" + fluff_desc = "TBD" + display_name = "operator" + visible_name = "Operator" + #warn impl + +AUTO_RIG_THEME(/mercenary/gorlex_infiltrator) +/datum/rig_theme/mercenary/gorlex_infiltrator + name = "contractor rig" + base_state = "infiltrator" + desc = "TBD" + fluff_desc = "TBD" + display_name = "streamlined" + visible_name = "Streamlined" + #warn impl + +AUTO_RIG_THEME(/mercenary/gorlex_assault) +/datum/rig_theme/mercenary/gorlex_assault + name = "elite nukeops rig" + base_state = "elite" + desc = "TBD" + fluff_desc = "TBD" + display_name = "battle" + visible_name = "Battle" + #warn impl + +AUTO_RIG_THEME(/mercenary/marine) +/datum/rig_theme/mercenary/marine + base_icon = 'icons/modules/rigsuits/suits/factions/military_marine.dmi' + name = "marine rig" + base_state = "marine" + desc = "TBD" + fluff_desc = "TBD" + display_name = "marine" + visible_name = "Marine" + control_sealed_append = "" + #warn impl + +/datum/rig_theme/mercenary/pmc + abstract_type = /datum/rig_theme/mercenary/pmc + base_icon = 'icons/modules/rigsuits/suits/factions/military_pmc.dmi' + control_sealed_append = "" + pieces = list( + /datum/rig_theme_piece/helmet{ + worn_bodytypes = BODYTYPES(BODYTYPE_DEFAULT, BODYTYPE_SKRELL); + }, + /datum/rig_theme_piece/chestplate, + /datum/rig_theme_piece/gloves{ + piece_base_state = "pmc"; + }, + /datum/rig_theme_piece/boots{ + piece_base_state = "pmc"; + }, + ) + +AUTO_RIG_THEME(/mercenary/pmc/commander) +/datum/rig_theme/mercenary/pmc/commander + name = "pmc commander rig" + base_state = "commander" + desc = "TBD" + fluff_desc = "TBD" + display_name = "leader" + visible_name = "Leader" + #warn impl + +AUTO_RIG_THEME(/mercenary/pmc/medic) +/datum/rig_theme/mercenary/pmc/medic + name = "pmc medic rig" + base_state = "medic" + desc = "TBD" + fluff_desc = "TBD" + display_name = "corpsman" + visible_name = "Corpsman" + #warn impl + + +AUTO_RIG_THEME(/mercenary/pmc/engineer) +/datum/rig_theme/mercenary/pmc/engineer + name = "pmc engineer rig" + base_state = "engineer" + desc = "TBD" + fluff_desc = "TBD" + display_name = "sapper" + visible_name = "Sapper" + #warn impl + + +AUTO_RIG_THEME(/mercenary/pmc/security) +/datum/rig_theme/mercenary/pmc/security + name = "pmc security rig" + base_state = "security" + desc = "TBD" + fluff_desc = "TBD" + display_name = "officer" + visible_name = "Officer" + #warn impl + +AUTO_RIG_THEME(/mercenary/sleek) +/datum/rig_theme/mercenary/sleek + base_icon = 'icons/modules/rigsuits/suits/factions/military_sleek.dmi' + name = "sleek combat rig" + base_state = "sleek" + desc = "TBD" + fluff_desc = "TBD" + display_name = "combat" + visible_name = "Combat" + control_sealed_append = "" + #warn impl diff --git a/code/modules/rigsuits/themes/faction/nanotrasen.dm b/code/modules/rigsuits/themes/faction/nanotrasen.dm new file mode 100644 index 000000000000..b83f99dec3f9 --- /dev/null +++ b/code/modules/rigsuits/themes/faction/nanotrasen.dm @@ -0,0 +1,182 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/rig_theme/nanotrasen + abstract_type = /datum/rig_theme/nanotrasen + // bye-bye security + siemens_coefficient = 0.5 + +AUTO_RIG_THEME(/nanotrasen/asset_protection) +/datum/rig_theme/nanotrasen/asset_protection + name = "deathsquad rig" + base_state = "deathsquad" + base_icon = 'icons/modules/rigsuits/suits/factions/nanotrasen_military.dmi' + display_name = "asset protection" + visible_name = "Asset Protection" + control_sealed_append = "" + desc = "A heavy voidsuit used by corporate marines." + fluff_desc = "The polar opposite to the Gorlex Marauder's combat armor, this is nonetheless still a great \ + example of Trans-Stellar Corporations having all too much power out on the frontiers. The Nanotrasen \ + Asset Protection hardsuit is rarely something seen on exonet marketing streams - and rarely something one \ + sees in person while still living to tell the tale." + armor = /datum/armor/rigsuit/nanotrasen/asset_protection + // bye-bye security + siemens_coefficient = 0.35 + #warn encumbrance + +/datum/armor/rigsuit/nanotrasen/asset_protection + melee = 0.55 + melee_tier = MELEE_TIER_HEAVY + melee_soak = 0 + melee_deflect = 5 + bullet = 0.55 + bullet_tier = BULLET_TIER_HIGH + bullet_soak = 0 + bullet_deflect = 5 + laser = 0.55 + laser_tier = LASER_TIER_HIGH + laser_soak = 0 + laser_deflect = 5 + energy = 0.45 + bomb = 0.55 + bio = 1.0 + rad = 0.7 + fire = 0.75 + acid = 1.0 + +AUTO_RIG_THEME(/nanotrasen/officer) +/datum/rig_theme/nanotrasen/officer + name = "centcom rig" + base_state = "responsory" + base_icon = 'icons/modules/rigsuits/suits/factions/nanotrasen.dmi' + display_name = "officer" + visible_name = "Officer" + control_sealed_append = "" + desc = "A relatively drab, but rugged looking hardsuit." + fluff_desc = "Based off of a semi-visor design, this looks vaguely like an even more boring modification of \ + an inner system civilian hardsuit. Clearly, though, the armor on this thing has been enhanced; in-fact, \ + every facet of this suit seems to have been redone with heavier internals. The quite-literal silver \ + linings does point towards the holder of this suit having some rank, or perhaps wishes for such." + armor = /datum/armor/rigsuit/nanotrasen/officer + #warn encumbrance + +/datum/armor/rigsuit/nanotrasen/officer + melee = 0.45 + melee_tier = MELEE_TIER_MEDIUM + melee_soak = 0 + melee_deflect = 5 + bullet = 0.45 + bullet_tier = BULLET_TIER_MEDIUM + bullet_soak = 0 + bullet_deflect = 5 + laser = 0.45 + laser_tier = LASER_TIER_MEDIUM + laser_soak = 0 + laser_deflect = 5 + energy = 0.35 + bomb = 0.45 + bio = 1.0 + rad = 0.55 + fire = 0.75 + acid = 1.0 + +AUTO_RIG_THEME(/nanotrasen/inquisition) +/datum/rig_theme/nanotrasen/inquisition + name = "inquisition rig" + base_state = "inquisitory" + base_icon = 'icons/modules/rigsuits/suits/factions/nanotrasen.dmi' + display_name = "pmd" + visible_name = "PMD" + control_sealed_append = "" + desc = "A strange, modified officer hardsuit rigged with what looks like silver paneling." + fluff_desc = "This looks like a heavily modified officer's hardsuit. Perhaps whoever did it has a \ + serious superstition of vampires and werewolves. There's silver paneling everywhere on it, including a \ + rather gaudy cap on the top. How ridiculous." + armor = /datum/armor/rigsuit/nanotrasen/inquisition + #warn encumbrance + +/datum/armor/rigsuit/nanotrasen/inquisition + melee = 0.35 + melee_tier = MELEE_TIER_HEAVY + melee_soak = 0 + melee_deflect = 5 + bullet = 0.35 + bullet_tier = BULLET_TIER_HIGH + bullet_soak = 0 + bullet_deflect = 5 + laser = 0.35 + laser_tier = LASER_TIER_HIGH + laser_soak = 0 + laser_deflect = 5 + energy = 0.35 + bomb = 0.55 + bio = 1.0 + rad = 0.7 + fire = 0.75 + acid = 1.0 + +/datum/rig_theme/nanotrasen/response + abstract_type = /datum/rig_theme/nanotrasen/response + control_sealed_append = "" + armor = /datum/armor/rigsuit/nanotrasen/response + desc = "The insignia'd hardsuits of Nanotrasen's emergency responders." + fluff_desc = "Made with a joint, heavily publicized effort with Hephaestus Industries, these heavily \ + reinforced hardsuits are used by Nanotrasen's Emergency Responders. Reception to the sight of these \ + tends to be mixed, given that something usually will have went down for responders to be sent." + #warn encumbrance + +/datum/armor/rigsuit/nanotrasen/response + melee = 0.35 + melee_tier = MELEE_TIER_HEAVY + melee_soak = 0 + melee_deflect = 5 + bullet = 0.35 + bullet_tier = BULLET_TIER_HIGH + bullet_soak = 0 + bullet_deflect = 5 + laser = 0.35 + laser_tier = LASER_TIER_HIGH + laser_soak = 0 + laser_deflect = 5 + energy = 0.35 + bomb = 0.55 + bio = 1.0 + rad = 0.7 + fire = 0.75 + acid = 1.0 + +AUTO_RIG_THEME(/nanotrasen/response/commander) +/datum/rig_theme/nanotrasen/response/commander + name = "ert commander rig" + base_state = "commander" + base_icon = 'icons/modules/rigsuits/suits/factions/nanotrasen_response.dmi' + display_name = "command" + visible_name = "Command" + control_sealed_append = "" + +AUTO_RIG_THEME(/nanotrasen/response/medic) +/datum/rig_theme/nanotrasen/response/medic + name = "ert medic rig" + base_state = "medic" + base_icon = 'icons/modules/rigsuits/suits/factions/nanotrasen_response.dmi' + display_name = "rescue" + visible_name = "Rescue" + control_sealed_append = "" + +AUTO_RIG_THEME(/nanotrasen/response/engineer) +/datum/rig_theme/nanotrasen/response/engineer + name = "ert engineer rig" + base_state = "engineer" + base_icon = 'icons/modules/rigsuits/suits/factions/nanotrasen_response.dmi' + display_name = "engineering" + visible_name = "Engineering" + control_sealed_append = "" + +AUTO_RIG_THEME(/nanotrasen/response/security) +/datum/rig_theme/nanotrasen/response/security + name = "ert security rig" + base_state = "security" + base_icon = 'icons/modules/rigsuits/suits/factions/nanotrasen_response.dmi' + display_name = "officer" + visible_name = "Officer" + control_sealed_append = "" diff --git a/code/modules/rigsuits/themes/rig_theme.dm b/code/modules/rigsuits/themes/rig_theme.dm new file mode 100644 index 000000000000..f08233f0d819 --- /dev/null +++ b/code/modules/rigsuits/themes/rig_theme.dm @@ -0,0 +1,156 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +GLOBAL_LIST_EMPTY(rig_theme_cache) + +/proc/fetch_rig_theme(datum/rig_theme/path) + if(!isnull(GLOB.rig_theme_cache[path])) + return GLOB.rig_theme_cache[path] + var/datum/rig_theme/instance = new path + GLOB.rig_theme_cache[path] = instance + return instance + +/datum/rig_theme + /// in code theme name + var/name = "unknown rig" + /// visible description + var/desc = "A powered exochassis of unknown design." + /// visible fluff description + var/fluff_desc + /// display name - overrides name on internal UIs. + var/display_name + /// visible name - overrides display name, which overrides name. renders as [visible_name][piece.visible_name] + var/visible_name + /// what this is called; usually just 'RIG' + var/suit_name = "RIG" + /// pieces - paths. init'd on, well, new/init. + var/list/pieces = list( + /datum/rig_theme_piece/helmet, + /datum/rig_theme_piece/chestplate, + /datum/rig_theme_piece/gloves, + /datum/rig_theme_piece/boots, + ) + /// base icon + var/base_icon + /// base icon state + /// combined as "[base_state][piece_state_append][sealed_state_append]" to get final state + /// the x_base_when_un/sealed vars can modify this. + var/base_state + /// control module state + var/control_state_append = "-control" + /// control module sealed append + var/control_sealed_append = "-sealed" + /// control module base state; defaults to base_state + var/control_base_state + /// control module worn icon uses base state; defaults to control_base_state, then base_state + var/control_base_state_worn + /// default coloration colors + var/list/coloration_colors + /// default coloration matrix + var/list/coloration_matrix + /// default coloration mode + var/coloration_mode = COLORATION_MODE_NONE + /// default coloration amount + var/coloration_amount = 0 + /// bodytypes implemented + var/datum/bodytypes/worn_bodytypes = BODYTYPES(BODYTYPE_DEFAULT) + /// fallback bodytypes + var/datum/bodytypes/worn_bodytypes_fallback = BODYTYPES_ALL + /// render flags + var/worn_render_flags = WORN_RENDER_SLOT_ONE_FOR_ALL + /// TGUI theme; unset to default + var/ui_theme + + //* base stats + /// startup / shutdown time + var/boot_delay = 5 SECONDS + /// piece seal/unseal time + var/seal_delay = 3 SECONDS + /// base weight + #warn weight + var/offline_weight = 0 + /// base encumbrance + #warn encumbrance + var/offline_encumbrance = 0 + /// base online weight + #warn weight + var/online_weight = 0 + /// base online encumbrance + #warn encumbrance + var/online_encumbrance = 0 + + //* vars to be replaced by components/modules at some point + var/datum/armor/armor = /datum/armor/rigsuit + var/min_pressure_protect = 0 * ONE_ATMOSPHERE + var/max_pressure_protect = 10 * ONE_ATMOSPHERE + var/min_temperature_protect = COLD_PROTECTION_VOIDSUIT + var/max_temperature_protect = HEAT_PROTECTION_NORMAL_VOIDSUIT + var/insulated_gloves = TRUE + // todo: this should be dropped down way more later so tasers hit the suit instead of the user + // this is pretty much a maintainer mandate too so discuss before trying to go against this + // if you're reading it + // hardsuits, like armor, absolutely should be one of those things that forces a lethal + // engagement if you don't want an easy 3 click win via stuns + // that said, ions/electrical stuns absolutely should fuck up internal systems and cause lockups + // so it's still very useful to pack a taser to a rig fight + var/siemens_coefficient = 0.8 + + #warn values lmao + +/datum/rig_theme/New() + CONSTRUCT_BODYTYPES(worn_bodytypes) + CONSTRUCT_BODYTYPES(worn_bodytypes_fallback) + var/list/old_pieces = pieces + pieces = list() + for(var/path in old_pieces) + add_piece(path) + +/datum/rig_theme/proc/imprint_control_appearance(obj/item/rig/control_module) + control_module.icon = base_icon + // todo: optimize + // state + control_module.state_sealed = "[control_base_state || base_state][control_state_append][control_sealed_append]" + control_module.state_unsealed = "[control_base_state || base_state][control_state_append]" + control_module.state_worn_sealed = "[control_base_state_worn || control_base_state || base_state][control_state_append][control_sealed_append]" + control_module.state_worn_unsealed = "[control_base_state_worn || control_base_state || base_state][control_state_append]" + // coloration + control_module.coloration_amount = coloration_amount + control_module.coloration_mode = coloration_mode + if(control_module.coloration_mode == COLORATION_MODE_MATRIX) + control_module.set_coloration_matrix(coloration_matrix) + else + control_module.set_coloration_parts(coloration_colors) + // rendering + control_module.worn_render_flags = worn_render_flags + #warn impl + // update + control_module.update_icon() + control_module.update_encumbrance() + control_module.update_weight() + +/datum/rig_theme/proc/imprint_control_behavior(obj/item/rig/control_module) + control_module.siemens_coefficient = siemens_coefficient + control_module.offline_encumbrance = offline_encumbrance + control_module.offline_weight = offline_weight + control_module.online_encumbrance = online_encumbrance + control_module.online_weight = online_weight + control_module.boot_delay = boot_delay + control_module.seal_delay = seal_delay + control_module.theme_name = display_name || name + control_module.ui_theme = ui_theme + #warn impl + +/datum/rig_theme/proc/imprint_control_legacy(obj/item/rig/control_module) + #warn impl - vars like armor/insulated/etc + +/datum/rig_theme/proc/add_piece(datum/rig_theme_piece/piece_path) + var/datum/rig_theme_piece/piece + if(ispath(piece_path) || IS_ANONYMOUS_TYPEPATH(piece_path)) + piece = new piece_path + else if(istype(piece_path)) + piece = piece_path + pieces += piece + +/datum/rig_theme/proc/apply_piece(datum/component/rig_piece/piece) + #warn impl + diff --git a/code/modules/rigsuits/themes/rig_theme_piece.dm b/code/modules/rigsuits/themes/rig_theme_piece.dm new file mode 100644 index 000000000000..1a3bcc1b7fb5 --- /dev/null +++ b/code/modules/rigsuits/themes/rig_theme_piece.dm @@ -0,0 +1,160 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * RIG piece definition datums + * Should only ever be belonging to one /datum/rig_theme at a time + */ +/datum/rig_theme_piece + abstract_type = /datum/rig_theme_piece + /// path + var/path + /// display name - overrides name on UIs + var/display_name + /// visible name - appended directly to host theme's visible name + var/visible_name + /// lookup prefix + var/lookup_prefix + /// piece component flags + var/rig_piece_flags = NONE + /// multiplier for armor to apply - does not affect tier. + /// > 1 values don't scale the same as < 1 for balancing reasons, + /// and will intsead use 2 = 50% *more armor* as opposed to 2x raw armor + /// e.g. 1 = 0.5, 2 = 0.75, 3 = 0.875, etc. + var/armor_multiplier = 1 + /// this piece's state append - this is separate from base_state so overriding is easier + var/piece_state_append + /// sealed state append + var/sealed_state_append = "-sealed" + /// base state - defaults to rig theme + var/piece_base_state + /// base state used when worn - defaults to piece_base_state, then rig theme + var/piece_base_state_worn + /// worn rendering flags + var/worn_render_flags = WORN_RENDER_SLOT_ONE_FOR_ALL + /// bodytypes implemented + var/datum/bodytypes/worn_bodytypes + /// fallback bodytypes + var/datum/bodytypes/worn_bodytypes_fallback + /// slot this goes in - SLOT_ID_HANDS for an inhand item. specific-hand binding not supported yet. + var/equip_slot + /// inv hide flags while unsealed + var/inv_hide_flags_inactive = NONE + /// inv hide flags while sealed + var/inv_hide_flags_active = NONE + /// default coloration colors - defaults to rig theme + var/list/coloration_colors + /// default coloration matrix - defaults to rig theme + var/list/coloration_matrix + /// default coloration mode - defaults to rig theme + var/coloration_mode + /// default coloration amount - defaults to rig theme + var/coloration_amount + /// seal delay add to base seal delay + var/piece_seal_delay_adjust = 0 + /// body cover flags + var/body_cover_flags = NONE + /// rig zone flags + var/rig_zone_bits = NONE + +/datum/rig_theme_piece/New() + CONSTRUCT_BODYTYPES(worn_bodytypes) + CONSTRUCT_BODYTYPES(worn_bodytypes_fallback) + +/** + * returns rig_piece component + */ +/datum/rig_theme_piece/proc/instantiate(datum/rig_theme/theme, obj/item/rig/controller) + ASSERT(ispath(path, /obj/item)) + var/obj/item/created_item = new path(controller) + var/datum/component/rig_piece/created_piece = created_item.AddComponent(/datum/component/rig_piece, controller) + imprint_appearance(theme, created_piece) + imprint_behavior(theme, created_piece) + // trigger an update by unsealing + created_piece.unseal() + return created_piece + +/datum/rig_theme_piece/proc/imprint_appearance(datum/rig_theme/theme, datum/component/rig_piece/piece_component) + var/obj/item/physical = piece_component.parent + // inv appearance / hide / cover flags + piece_component.inv_hide_flags_sealed = inv_hide_flags_active + piece_component.inv_hide_flags_unsealed = inv_hide_flags_inactive + physical.body_cover_flags = body_cover_flags + physical.heat_protection_cover = body_cover_flags + physical.cold_protection_cover = body_cover_flags + // rendering + physical.worn_render_flags = worn_render_flags || theme.worn_render_flags + // bodytypes + physical.worn_bodytypes = worn_bodytypes || theme.worn_bodytypes + physical.worn_bodytypes_fallback = worn_bodytypes_fallback || theme.worn_bodytypes_fallback + // icon + physical.icon = theme.base_icon + // state + piece_component.state_sealed = "[piece_base_state || theme.base_state][piece_state_append][sealed_state_append]" + piece_component.state_unsealed = "[piece_base_state || theme.base_state][piece_state_append]" + piece_component.state_worn_sealed = "[piece_base_state_worn || piece_base_state || theme.base_state][piece_state_append][sealed_state_append]" + piece_component.state_worn_unsealed = "[piece_base_state_worn || piece_base_state || theme.base_state][piece_state_append]" + // coloration + physical.coloration_amount = isnull(coloration_amount)? theme.coloration_amount : coloration_amount + physical.coloration_mode = isnull(coloration_mode)? theme.coloration_mode : coloration_mode + if(physical.coloration_mode == COLORATION_MODE_MATRIX) + physical.set_coloration_matrix(isnull(coloration_matrix)? theme.coloration_matrix : coloration_matrix) + else + physical.set_coloration_parts(isnull(coloration_colors)? theme.coloration_colors : coloration_colors) + +/datum/rig_theme_piece/proc/imprint_behavior(datum/rig_theme/theme, datum/component/rig_piece/piece_component) + var/obj/item/physical = piece_component.parent + piece_component.inv_hide_flags_sealed = inv_hide_flags_active + piece_component.inv_hide_flags_unsealed = inv_hide_flags_inactive + piece_component.rig_piece_flags = rig_piece_flags + piece_component.inventory_slot = equip_slot + piece_component.lookup_prefix = lookup_prefix + piece_component.rig_zone_bits = rig_zone_bits + +/datum/rig_theme_piece/helmet + display_name = "helmet" + visible_name = "Helmet" + lookup_prefix = "helmet" + path = /obj/item/clothing/head/rig + rig_piece_flags = RIG_PIECE_APPLY_ARMOR | RIG_PIECE_APPLY_ENVIRONMENTALS + piece_state_append = "-helmet" + inv_hide_flags_active = HIDEFACE | HIDEEARS | HIDEEARS | HIDEEYES | HIDEMASK | BLOCKHEADHAIR + equip_slot = /datum/inventory_slot_meta/inventory/head + body_cover_flags = HEAD + +/datum/rig_theme_piece/chestplate + display_name = "chestplate" + visible_name = "Chestplate" + lookup_prefix = "torso" + path = /obj/item/clothing/suit/rig + rig_piece_flags = RIG_PIECE_APPLY_ARMOR | RIG_PIECE_APPLY_ENVIRONMENTALS + piece_state_append = "-chestplate" + inv_hide_flags_active = HIDETAIL | HIDEJUMPSUIT | HIDETIE + equip_slot = /datum/inventory_slot_meta/inventory/suit + body_cover_flags = UPPER_TORSO | LOWER_TORSO | ARMS | LEGS + +/datum/rig_theme_piece/gloves + display_name = "gauntlets" + visible_name = "Gauntlets" + lookup_prefix = "arms" + path = /obj/item/clothing/gloves/rig + rig_piece_flags = RIG_PIECE_APPLY_ARMOR | RIG_PIECE_APPLY_ENVIRONMENTALS + piece_state_append = "-gloves" + equip_slot = /datum/inventory_slot_meta/inventory/gloves + body_cover_flags = HANDS + +/datum/rig_theme_piece/gloves/imprint_behavior(datum/rig_theme/theme, datum/component/rig_piece/piece_component) + . = ..() + // todo: legacy + if(theme.insulated_gloves) + piece_component.always_fully_insulated = TRUE + +/datum/rig_theme_piece/boots + display_name = "boots" + visible_name = "Boots" + lookup_prefix = "legs" + path = /obj/item/clothing/shoes/rig + rig_piece_flags = RIG_PIECE_APPLY_ARMOR | RIG_PIECE_APPLY_ENVIRONMENTALS + piece_state_append = "-boots" + equip_slot = /datum/inventory_slot_meta/inventory/shoes + body_cover_flags = FEET diff --git a/code/modules/rigsuits/themes/species/_species.dm b/code/modules/rigsuits/themes/species/_species.dm new file mode 100644 index 000000000000..edad1e07c226 --- /dev/null +++ b/code/modules/rigsuits/themes/species/_species.dm @@ -0,0 +1,5 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/rig_theme/species + abstract_type = /datum/rig_theme/species diff --git a/code/modules/rigsuits/themes/species/nepid.dm b/code/modules/rigsuits/themes/species/nepid.dm new file mode 100644 index 000000000000..73c8634d92d7 --- /dev/null +++ b/code/modules/rigsuits/themes/species/nepid.dm @@ -0,0 +1,42 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/rig_theme/species/nepid + abstract_type = /datum/rig_theme/species/nepid + base_icon = 'icons/modules/rigsuits/suits/species/nepid.dmi' + armor = /datum/armor/rigsuit/species/nepid + +/datum/armor/rigsuit/species/nepid + melee = 0.3 + melee_tier = MELEE_TIER_MEDIUM + melee_soak = 0 + melee_deflect = 5 + bullet = 0.2 + bullet_tier = BULLET_TIER_MEDIUM + bullet_soak = 0 + bullet_deflect = 5 + laser = 0.3 + laser_tier = LASER_TIER_MEDIUM + laser_soak = 3 + laser_deflect = 0 + energy = 0.225 + bomb = 0.3 + bio = 1.0 + rad = 0.75 + fire = 1.0 + acid = 1.0 + +AUTO_RIG_THEME(/species/nepid/generic) +/datum/rig_theme/species/nepid/generic + name = "Dnin-Nepid EVA harness" + base_state = "generic" + desc = "A sleek, alien-looking hardsuit worn by nomadic spacefarers." + fluff_desc = "The standard EVA harness worn by Dnin-Nepids everywhere. Mass produced and stocked aboard practically \ + every Nepid arkship, this piece of equipment has become integral to their ascension to the stars. Fitted with a modest \ + yet usable amount of hardware mounting points, it is the most common piece of equipment used by Nepid \ + explorers." + display_name = "EVA" + visible_name = "flight" + insulated_gloves = TRUE + siemens_coefficient = 0.5 + #warn encumbrance diff --git a/code/modules/rigsuits/themes/species/protean.dm b/code/modules/rigsuits/themes/species/protean.dm new file mode 100644 index 000000000000..920da3229f30 --- /dev/null +++ b/code/modules/rigsuits/themes/species/protean.dm @@ -0,0 +1,37 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +AUTO_RIG_THEME(/species/protean) +/datum/rig_theme/species/protean + name = "protean rig" + base_icon = 'icons/modules/rigsuits/suits/species/protean.dmi' + base_state = "nanosuit" + desc = "A sleek, form-fitting hardsuit made out of some kind of alloy." + fluff_desc = "Unlike other hardsuits, this one is missing the usual seams, bolts, and reinforcements. \ + Its motions are unnaturally fluid - the internals are likely not made out of the usual mechanisms." + display_name = "Nanocluster" + visible_name = "Sleek" + insulated_gloves = TRUE + siemens_coefficient = 0.75 + armor = /datum/armor/rigsuit/species/protean + #warn encumbrance + +/datum/armor/rigsuit/species/protean + melee = 0.3 + melee_tier = MELEE_TIER_MEDIUM + melee_soak = 0 + melee_deflect = 5 + bullet = 0.2 + bullet_tier = BULLET_TIER_MEDIUM + bullet_soak = 0 + bullet_deflect = 5 + laser = 0.3 + laser_tier = LASER_TIER_MEDIUM + laser_soak = 3 + laser_deflect = 0 + energy = 0.225 + bomb = 0.3 + bio = 1.0 + rad = 0.75 + fire = 1.0 + acid = 1.0 diff --git a/code/modules/rigsuits/themes/station/_station.dm b/code/modules/rigsuits/themes/station/_station.dm new file mode 100644 index 000000000000..86408ef54bc1 --- /dev/null +++ b/code/modules/rigsuits/themes/station/_station.dm @@ -0,0 +1,8 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/** + * While called 'station', these RIG themes are meant to be general non-faction ones. + */ +/datum/rig_theme/station + abstract_type = /datum/rig_theme/station diff --git a/code/modules/rigsuits/themes/station/cargo.dm b/code/modules/rigsuits/themes/station/cargo.dm new file mode 100644 index 000000000000..8e67f258737f --- /dev/null +++ b/code/modules/rigsuits/themes/station/cargo.dm @@ -0,0 +1,93 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/rig_theme/station/cargo + abstract_type = /datum/rig_theme/station/cargo + base_icon = 'icons/modules/rigsuits/suits/cargo.dmi' + armor = /datum/armor/rigsuit/station/cargo + +/datum/armor/rigsuit/station/cargo + melee = 0.5 + melee_tier = MELEE_TIER_MEDIUM + melee_soak = 0 + melee_deflect = 5 + bullet = 0.35 + bullet_tier = BULLET_TIER_MEDIUM + bullet_soak = 0 + bullet_deflect = 5 + laser = 0.25 + laser_tier = LASER_TIER_MEDIUM + laser_soak = 0 + laser_deflect = 0 + energy = 0.05 + bomb = 0.45 + bio = 1.0 + rad = 0.85 + fire = 0.75 + acid = 1.0 + +AUTO_RIG_THEME(/station/cargo/asteroid) +/datum/rig_theme/station/cargo/asteroid + name = "salvage rig" + base_state = "salvage" + desc = "A hardened salvage suit used by spacers and asteroid miners." + fluff_desc = "A third-generation Hephaestus Industries salvage hardsuit with all the bells and whistles. \ + Often seen on the suit racks of corporations and more well-off frontiersmen, this suit is as hardy as it is \ + expensive. The S-3 line of hardsuits boast significant improvements in mobility and versatility, \ + often being used in space and terrestrial environments alike." + display_name = "salvage" + visible_name = "Salvage" + armor = /datum/armor/rigsuit/station/cargo/asteroid + // sealed + max_temperature_protect = HEAT_PROTECTION_INDUSTRIAL_VOIDSUIT + #warn encumbrance + +/datum/armor/rigsuit/station/cargo/asteroid + +AUTO_RIG_THEME(/station/cargo/mining) +/datum/rig_theme/station/cargo/mining + name = "mining rig" + base_state = "mining" + desc = "A prototype, ash-like suit used by many a corporate spelunker." + fluff_desc = "The Nanotrasen PK-H line of terrestrial mining suits. Based off of prior designs given by \ + Hephaestus Industries, this suit incorporates some of the same technologies used in the proto-kinetic line of \ + mining prototypes. While lacking in penetrative armor, it offers far more mobility than most industrial RIGs." + display_name = "mining" + visible_name = "Mining" + armor = /datum/armor/rigsuit/station/cargo/mining + // non-ish-sealed + min_pressure_protect = ONE_ATMOSPHERE + max_pressure_protect = ONE_ATMOSPHERE + min_temperature_protect = COLD_PROTECTION_HEAVY_WINTER_CLOTHING + max_temperature_protect = HEAT_PROTECTION_LIGHT_FIRESUIT + #warn encumbrance + +/datum/armor/rigsuit/station/cargo/mining + bullet = 0.2 + bullet_tier = BULLET_TIER_LOW + laser = 0.3 + laser_tier = LASER_TIER_LOW + rad = 0.35 + +AUTO_RIG_THEME(/station/cargo/loader) +/datum/rig_theme/station/cargo/loader + name = "loader rig" + base_state = "loader" + desc = "An efficient, sleek suit used by logistics workers." + fluff_desc = "The Hephaestus Industries L-9 series of assisted loader suits, used to provide underpaid \ + frontier workers just a bit more protection in their long toils aboard their freighters - as well as a lot more \ + comfort." + display_name = "loader" + visible_name = "Loader" + armor = /datum/armor/rigsuit/station/cargo/loader + // non-sealed + min_pressure_protect = ONE_ATMOSPHERE + max_pressure_protect = ONE_ATMOSPHERE + min_temperature_protect = COLD_PROTECTION_MEDIUM_WINTER_CLOTHING + max_temperature_protect = HEAT_PROTECTION_VERY_THICK_CLOTHING + #warn encumbrance + +/datum/armor/rigsuit/station/cargo/loader + bullet = 0.25 + laser = 0.15 + rad = 0.1 diff --git a/code/modules/rigsuits/themes/station/civilian.dm b/code/modules/rigsuits/themes/station/civilian.dm new file mode 100644 index 000000000000..c9df24979f79 --- /dev/null +++ b/code/modules/rigsuits/themes/station/civilian.dm @@ -0,0 +1,40 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/rig_theme/station/civilian + abstract_type = /datum/rig_theme/station/civilian + base_icon = 'icons/modules/rigsuits/suits/civilian.dmi' + +AUTO_RIG_THEME(/station/civilian/standard) +/datum/rig_theme/station/civilian/standard + name = "standard rig" + base_state = "standard" + desc = "A standard-issue civilian hardsuit." + fluff_desc = "Originally made by inner-system manufacturers in the Orion Confederacy, this design is \ + widely produced by almost every TSC in existence. Cheap and reliable, this lineage of suit is up there \ + on the list of innovations responsible for space travel being as safe as it is today." + display_name = "standard" + visible_name = "Standard" + armor = /datum/armor/rigsuit/station/civilian/standard + max_temperature_protect = HEAT_PROTECTION_LIGHT_FIRESUIT + #warn encumbrance + +/datum/armor/rigsuit/station/civilian/standard + melee = 0.15 + melee_tier = MELEE_TIER_MEDIUM + melee_soak = 0 + melee_deflect = 5 + bullet = 0.15 + bullet_tier = BULLET_TIER_LOW + bullet_soak = 0 + bullet_deflect = 0 + laser = 0.25 + laser_tier = LASER_TIER_LOW + laser_soak = 2 + laser_deflect = 2 + energy = 0.25 + bomb = 0.25 + bio = 1.0 + rad = 0.25 + fire = 0.5 + acid = 1.0 diff --git a/code/modules/rigsuits/themes/station/command.dm b/code/modules/rigsuits/themes/station/command.dm new file mode 100644 index 000000000000..21daba702f78 --- /dev/null +++ b/code/modules/rigsuits/themes/station/command.dm @@ -0,0 +1,40 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/rig_theme/station/command + abstract_type = /datum/rig_theme/station/command + base_icon = 'icons/modules/rigsuits/suits/command.dmi' + +AUTO_RIG_THEME(/station/command/captain) +/datum/rig_theme/station/command/captain + name = "director rig" + base_state = "magnate" + desc = "A reinforced hardsuit used by the captain of the installation." + fluff_desc = "A modification of the successful S-3 lineage from Hephaestus Industries, this is a \ + special issue hardsuit offered to the leaders of Nanotrasen's deep-space installations. It is unnaturally \ + protective for such a (relatively) mobile suit, being fitted with high quality alloys and compliant \ + interlocks." + display_name = "director" + visible_name = "Director" + armor = /datum/armor/rigsuit/station/command/captain + #warn encumbrance + +/datum/armor/rigsuit/station/command/captain + melee = 0.4 + melee_tier = MELEE_TIER_HEAVY + melee_soak = 0 + melee_deflect = 5 + bullet = 0.35 + bullet_tier = BULLET_TIER_HIGH + bullet_soak = 0 + bullet_deflect = 5 + laser = 0.35 + laser_tier = LASER_TIER_HIGH + laser_soak = 5 + laser_deflect = 0 + energy = 0.35 + bomb = 0.45 + bio = 1.0 + rad = 0.35 + fire = 0.75 + acid = 1.0 diff --git a/code/modules/rigsuits/themes/station/engineering.dm b/code/modules/rigsuits/themes/station/engineering.dm new file mode 100644 index 000000000000..60a4a73a94fd --- /dev/null +++ b/code/modules/rigsuits/themes/station/engineering.dm @@ -0,0 +1,87 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/rig_theme/station/engineering + abstract_type = /datum/rig_theme/station/engineering + base_icon = 'icons/modules/rigsuits/suits/engineering.dmi' + armor = /datum/armor/rigsuit/station/engineering + siemens_coefficient = 0.7 + +/datum/armor/rigsuit/station/engineering + melee = 0.25 + melee_tier = MELEE_TIER_MEDIUM + melee_soak = 0 + melee_deflect = 5 + bullet = 0.15 + bullet_tier = BULLET_TIER_LOW + bullet_soak = 0 + bullet_deflect = 0 + laser = 0.3 + laser_tier = LASER_TIER_MEDIUM + laser_soak = 5 + laser_deflect = 0 + energy = 0.25 + bomb = 0.4 + bio = 1.0 + rad = 0.65 + fire = 0.75 + acid = 1.0 + +AUTO_RIG_THEME(/station/engineering/standard) +/datum/rig_theme/station/engineering/standard + name = "engineering rig" + base_state = "engineering" + desc = "A sleek suit used by corporate engineers." + fluff_desc = "Originally an Aether Atmospherics & Recycling design, this sleek suit was 'appropriated' by \ + the Nanotrasen triad early into the corporate space race. While royalties are still paid to a marginal degree, \ + the designs for this suit has long been disseminated to many different benefactors - and elements of its \ + design can be seen in the internals of many similar voidsuits to this day." + display_name = "engineering" + visible_name = "Engineering" + max_pressure_protect = ONE_ATMOSPHERE * 15 + max_temperature_protect = HEAT_PROTECTION_INDUSTRIAL_VOIDSUIT + armor = /datum/armor/rigsuit/station/engineering/standard + #warn encumbrance + +/datum/armor/rigsuit/station/engineering/standard + rad = 0.8 + +AUTO_RIG_THEME(/station/engineering/atmospherics) +/datum/rig_theme/station/engineering/atmospherics + name = "atmospherics rig" + base_state = "atmospheric" + desc = "A modified engineering suit sacrificing some degree of plating for enhanced atmospheric shielding" + fluff_desc = "A modified version of the popular technician suits from AAR, this is used by industrial \ + life-support maintainers for those especially heated problems." + display_name = "atmospherics" + visible_name = "Atmospherics" + max_pressure_protect = ONE_ATMOSPHERE * 20 + max_temperature_protect = HEAT_PROTECTION_ATMOS_VOIDSUIT + armor = /datum/armor/rigsuit/station/engineering/atmospherics + #warn encumbrance + +/datum/armor/rigsuit/station/engineering/atmospherics + fire = 1.0 + rad = 0.45 + +AUTO_RIG_THEME(/station/engineering/advanced) +/datum/rig_theme/station/engineering/advanced + name = "advanced rig" + base_state = "advanced" + desc = "A sleek, shiny, and obnoxiously expensive hardsuit used by high-end corporate engineers." + fluff_desc = "The popular AAR technician suit modified by Hephaestus Industries, and given an once-over \ + by Nanotrasen for a more welcoming paintjob. This suit packs enhanced shielding, plating, and everything \ + in-between - though it tends to be more power-hungry than similar suits thanks to the similarly 'enhanced' weight." + display_name = "advanced" + visible_name = "Advanced" + max_pressure_protect = ONE_ATMOSPHERE * 20 + max_temperature_protect = HEAT_PROTECTION_ATMOS_VOIDSUIT + armor = /datum/armor/rigsuit/station/engineering/advanced + #warn encumbrance + +/datum/armor/rigsuit/station/engineering/advanced + fire = 1.0 + rad = 0.85 + melee = 0.3 + bullet_tier = BULLET_TIER_MEDIUM + laser_deflect = 5 diff --git a/code/modules/rigsuits/themes/station/exploration.dm b/code/modules/rigsuits/themes/station/exploration.dm new file mode 100644 index 000000000000..59c855581ddf --- /dev/null +++ b/code/modules/rigsuits/themes/station/exploration.dm @@ -0,0 +1,57 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/rig_theme/station/exploration + abstract_type = /datum/rig_theme/station/exploration + base_icon = 'icons/modules/rigsuits/suits/exploration.dmi' + pieces = list( + /datum/rig_theme_piece/helmet, + /datum/rig_theme_piece/chestplate, + /datum/rig_theme_piece/gloves, + /datum/rig_theme_piece/boots{ + piece_base_state = ""; + }, + ) + control_base_state_worn = "" + control_sealed_append = "" + +AUTO_RIG_THEME(/station/exploration/standard) +/datum/rig_theme/station/exploration/standard + name = "excursion rig" + base_state = "explo" + desc = "TBD" + fluff_desc = "TBD" + display_name = "excursion" + visible_name = "excursion" + #warn impl + +AUTO_RIG_THEME(/station/exploration/pathfinder) +/datum/rig_theme/station/exploration/pathfinder + name = "pathfinder rig" + base_state = "pf" + desc = "TBD" + fluff_desc = "TBD" + display_name = "pathfinder" + visible_name = "pathfinder" + control_base_state_worn = null + #warn impl + +AUTO_RIG_THEME(/station/exploration/medic) +/datum/rig_theme/station/exploration/medic + name = "field medic rig" + base_state = "medic" + desc = "TBD" + fluff_desc = "TBD" + display_name = "medic" + visible_name = "medic" + #warn impl + +AUTO_RIG_THEME(/station/exploration/pilot) +/datum/rig_theme/station/exploration/pilot + name = "pilot rig" + base_state = "pilot" + desc = "TBD" + fluff_desc = "TBD" + display_name = "pilot" + visible_name = "pilot" + #warn impl diff --git a/code/modules/rigsuits/themes/station/medical.dm b/code/modules/rigsuits/themes/station/medical.dm new file mode 100644 index 000000000000..c5841a04a12b --- /dev/null +++ b/code/modules/rigsuits/themes/station/medical.dm @@ -0,0 +1,77 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/rig_theme/station/medical + abstract_type = /datum/rig_theme/station/medical + base_icon = 'icons/modules/rigsuits/suits/medical.dmi' + armor = /datum/armor/rigsuit/station/medical + +/datum/armor/rigsuit/station/medical + melee = 0.3 + melee_tier = MELEE_TIER_MEDIUM + melee_soak = 3 + melee_deflect = 0 + bullet = 0.25 + bullet_tier = BULLET_TIER_LOW + bullet_soak = 0 + bullet_deflect = 0 + laser = 0.3 + laser_tier = LASER_TIER_LOW + laser_soak = 0 + laser_deflect = 0 + energy = 0.2 + bomb = 0.35 + bio = 1.0 + rad = 0.45 + fire = 0.575 + acid = 1.0 + +AUTO_RIG_THEME(/station/medical/standard) +/datum/rig_theme/station/medical/standard + name = "medical rig" + base_state = "medical" + desc = "A common search and rescue hardsuit used by frontier medics." + fluff_desc = "One of the more common lineages of Vey-Med's rescue hardsuits, this design is a widely exported \ + gem used by many a doctor - albeit at a mildly hefty price. Optimized for speed and maneuverability, it does \ + lack some of the heavier shielding found on industrial spacesiuts." + display_name = "medical" + visible_name = "Medical" + pieces = list( + /datum/rig_theme_piece/helmet{ + worn_bodytypes = BODYTYPES(BODYTYPE_DEFAULT, BODYTYPE_TAJARAN, BODYTYPE_SKRELL, BODYTYPE_UNATHI, BODYTYPE_UNATHI_DIGI, BODYTYPE_IPC); + }, + /datum/rig_theme_piece/chestplate{ + worn_bodytypes = BODYTYPES(BODYTYPE_DEFAULT, BODYTYPE_TAJARAN, BODYTYPE_UNATHI, BODYTYPE_UNATHI_DIGI, BODYTYPE_IPC); + }, + /datum/rig_theme_piece/gloves{ + worn_bodytypes = BODYTYPES(BODYTYPE_DEFAULT, BODYTYPE_TAJARAN, BODYTYPE_IPC); + }, + /datum/rig_theme_piece/boots{ + worn_bodytypes = BODYTYPES(BODYTYPE_DEFAULT, BODYTYPE_TAJARAN, BODYTYPE_IPC); + }, + ) + #warn encumbrance + +AUTO_RIG_THEME(/station/medical/advanced) +/datum/rig_theme/station/medical/advanced + name = "chief medical rig" + base_state = "corpsman" + desc = "An expensive, upgraded rescue hardsuit often used by frontier medical officers." + fluff_desc = "An upgraded, and likely overpriced hardsuit straight from Vey-Med. Contains enhanced \ + plating and shielding - as much as can be packed without compromising its superior mobility. \ + Often found in the hands of corporate medical services, as well as more wealthy frontiersmen whom can \ + actually afford it." + display_name = "rescue" + visible_name = "Rescue" + armor = /datum/armor/rigsuit/station/medical/advanced + siemens_coefficient = 0.7 + #warn encumbrance + +/datum/armor/rigsuit/station/medical/advanced + melee = 0.35 + melee_tier = MELEE_TIER_MEDIUM + bullet = 0.2 + bullet_tier = BULLET_TIER_MEDIUM + laser = 0.3 + laser_tier = LASER_TIER_MEDIUM + bomb = 0.4 diff --git a/code/modules/rigsuits/themes/station/science.dm b/code/modules/rigsuits/themes/station/science.dm new file mode 100644 index 000000000000..f78935fd5f69 --- /dev/null +++ b/code/modules/rigsuits/themes/station/science.dm @@ -0,0 +1,26 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/rig_theme/station/science + abstract_type = /datum/rig_theme/station/science + base_icon = 'icons/modules/rigsuits/suits/science.dmi' + +AUTO_RIG_THEME(/station/science/standard) +/datum/rig_theme/station/science/standard + name = "prototype rig" + base_state = "prototype" + desc = "TBD" + fluff_desc = "TBD" + display_name = "prototype" + visible_name = "Prototype" + #warn impl + +AUTO_RIG_THEME(/station/science/anomaly) +/datum/rig_theme/station/science/anomaly + name = "anomaly rig" + base_state = "apocryphal" + desc = "TBD" + fluff_desc = "TBD" + display_name = "AMI" + visible_name = "AMI" + #warn impl diff --git a/code/modules/rigsuits/themes/station/security.dm b/code/modules/rigsuits/themes/station/security.dm new file mode 100644 index 000000000000..78ae73256210 --- /dev/null +++ b/code/modules/rigsuits/themes/station/security.dm @@ -0,0 +1,28 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2023 Citadel Station developers. *// + +/datum/rig_theme/station/security + abstract_type = /datum/rig_theme/station/security + base_icon = 'icons/modules/rigsuits/suits/security.dmi' + // combat rigs + siemens_coefficient = 0.65 + +AUTO_RIG_THEME(/station/security/standard) +/datum/rig_theme/station/security/standard + name = "security rig" + base_state = "security" + desc = "TBD" + fluff_desc = "TBD" + display_name = "security" + visible_name = "Security" + #warn impl + +AUTO_RIG_THEME(/station/security/) +/datum/rig_theme/station/security/safeguard + name = "safeguard rig" + base_state = "safeguard" + desc = "TBD" + fluff_desc = "TBD" + display_name = "safeguard" + visible_name = "Safeguard" + #warn impl diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index f49084ac8169..58561ba8b724 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -872,12 +872,11 @@ GLOBAL_LIST_INIT(species_oxygen_tank_by_gas, list( /datum/species/proc/handle_falling(mob/living/carbon/human/H, atom/hit_atom, damage_min, damage_max, silent, planetary) return FALSE -/** - * clones us into a new datum - */ -/datum/species/proc/clone() - var/datum/species/created = new type +// todo: serialize, deserialize, and fix this shit lmao +/datum/species/clone() + var/datum/species/created = ..() created.copy_from(src) + return created /** * completely clones us from another species, updating the provided human in the process diff --git a/code/modules/species/station/adherent.dm b/code/modules/species/station/adherent.dm index e7f226da246e..c9d6c96c4349 100644 --- a/code/modules/species/station/adherent.dm +++ b/code/modules/species/station/adherent.dm @@ -110,10 +110,10 @@ has_organ = list( O_BRAIN = /obj/item/organ/internal/brain/adherent, O_CELL = /obj/item/organ/internal/cell/adherent, - O_COOLING_FINS = /obj/item/organ/internal/powered/cooling_fins, + O_COOLING_FINS = /obj/item/organ/internal/adherent/cooling_fins, O_EYES = /obj/item/organ/internal/eyes/adherent, - O_FLOAT = /obj/item/organ/internal/powered/float, - O_JETS = /obj/item/organ/internal/powered/jets, + O_FLOAT = /obj/item/organ/internal/adherent/float, + O_JETS = /obj/item/organ/internal/adherent/jets, ) move_trail = /obj/effect/debris/cleanable/blood/tracks/snake @@ -152,7 +152,7 @@ /datum/species/adherent/can_overcome_gravity(mob/living/carbon/human/H) . = FALSE if(H && H.stat == CONSCIOUS) - for(var/obj/item/organ/internal/powered/float/float in H.internal_organs) + for(var/obj/item/organ/internal/adherent/float/float in H.internal_organs) if(float.active) . = TRUE break @@ -171,7 +171,7 @@ /datum/species/adherent/handle_fall_special(mob/living/carbon/human/H, turf/landing) var/float_is_usable = FALSE if(H && H.stat == CONSCIOUS) - for(var/obj/item/organ/internal/powered/float/float in H.internal_organs) + for(var/obj/item/organ/internal/adherent/float/float in H.internal_organs) float_is_usable = TRUE break if(float_is_usable) diff --git a/code/modules/tgui/datum.dm b/code/modules/tgui/datum.dm index fa0530b5093c..09b81cfac690 100644 --- a/code/modules/tgui/datum.dm +++ b/code/modules/tgui/datum.dm @@ -84,6 +84,24 @@ /datum/proc/ui_static_data(mob/user, datum/tgui/ui) return list() +/** + * public + * + * Static module data to be sent to the UI. + * + * * We only have this, and not a module update, because you should be using module registration. + * * This is for stuff like RIGsuits, which do special implementations and need the first data send to have modules. + * + * + * @params + * * user - (optional) the mob using the UI + * * ui - (optional) the host tgui + * + * return key-value list: key is module ID, value is a list of data. + */ +/datum/proc/ui_static_modules(mob/user, datum/tgui/ui) + return list() + /** * public * diff --git a/code/modules/tgui/status_composers.dm b/code/modules/tgui/status_composers.dm index a061b6c6208f..239983a9a22d 100644 --- a/code/modules/tgui/status_composers.dm +++ b/code/modules/tgui/status_composers.dm @@ -33,7 +33,7 @@ return UI_INTERACTIVE // Regular ghosts can always at least view if in range. - var/datum/client_interface/client = GET_CLIENT(user) + var/datum/mocking/client/client = GET_CLIENT(user) if(client) if(get_dist(source, user) < max(client.current_viewport_width, client.current_viewport_height)) return UI_UPDATE diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index 8e806bb64c9b..b16b3c6194ec 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -288,18 +288,18 @@ "observer" = isobserver(user), ), ) - var/list/modules = list() + var/list/modules = src_object.ui_static_modules(user, src) // static first if(with_static_data) - json_data["static"] = src_object.ui_static_data(user, src, state) + json_data["static"] = src_object.ui_static_data(user, src) for(var/datum/module as anything in modules_registered) var/id = modules_registered[module] - modules[id] = module.ui_static_data(user, src, TRUE) + modules[id] = module.ui_static_data(user, src) if(with_data) json_data["data"] = src_object.ui_data(user, src, state) for(var/datum/module as anything in (with_static_data? modules_registered : modules_processed)) var/id = modules_registered[module] - modules[id] = modules[id] | module.ui_data(user, src, TRUE) + modules[id] = modules[id] | module.ui_data(user, src) if(modules) json_data["modules"] = modules if(src_object.tgui_shared_states) diff --git a/icons/README.md b/icons/README.md index f03be528e5ba..51cc9098677d 100644 --- a/icons/README.md +++ b/icons/README.md @@ -34,11 +34,9 @@ Yes, this currently includes all turfs, mobs, objs, and misc things. Sorry. We'l - /clothing - clothing - /machinery - machinery - /structures - structures + - /overmaps - **All** overmap sprites, whether or not it's screen rendering, goes in here - /objects - miscellaneous objects that are not items, machinery, or structures - - /overmaps - **All** overmap sprites, whether or not it's screen rendering, goes in here - - /runtime - follow similar structure inside this, treat it as a sub-copy. icons in this are copied over for use during runtime - - **Warning!** - everything not in this folder cannot be accessed by "filename" and must be compiled in with 'filename'. - - /screen - all screen objects, like hud icons, buttons, inventory interface, parallax, etc + - /screen - all screen objects, like hud icons, buttons, inventory interface, parallax, etc - /actions - all action button sprites go in here - /atom_hud - the /image huds that atoms can have has their icons in here - /fullscreen - fullscreen effects go in here (see code/_rendering/ fullscreen) diff --git a/icons/effects/ULIcons.dmi b/icons/effects/ULIcons.dmi deleted file mode 100644 index 3104e6ea6688..000000000000 Binary files a/icons/effects/ULIcons.dmi and /dev/null differ diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi index af2e21f8cb31..915fc9442aa1 100644 Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ diff --git a/icons/effects/particles/bonfire.dmi b/icons/effects/particles/bonfire.dmi index e8e2e36346da..925e8f38dd5c 100644 Binary files a/icons/effects/particles/bonfire.dmi and b/icons/effects/particles/bonfire.dmi differ diff --git a/icons/effects/progessbar.dmi b/icons/effects/progessbar.dmi deleted file mode 100644 index 99284060aa60..000000000000 Binary files a/icons/effects/progessbar.dmi and /dev/null differ diff --git a/icons/items/stream_projector/holofabricator-airlocks.dmi b/icons/items/stream_projector/holofabricator-airlocks.dmi new file mode 100644 index 000000000000..2e22cf0dd7e4 Binary files /dev/null and b/icons/items/stream_projector/holofabricator-airlocks.dmi differ diff --git a/icons/items/stream_projector/holofabricator-furniture.dmi b/icons/items/stream_projector/holofabricator-furniture.dmi new file mode 100644 index 000000000000..78a937cc13ea Binary files /dev/null and b/icons/items/stream_projector/holofabricator-furniture.dmi differ diff --git a/icons/items/stream_projector/holofabricator-structures.dmi b/icons/items/stream_projector/holofabricator-structures.dmi new file mode 100644 index 000000000000..64fd2a248858 Binary files /dev/null and b/icons/items/stream_projector/holofabricator-structures.dmi differ diff --git a/icons/items/stream_projector/holofabricator.dmi b/icons/items/stream_projector/holofabricator.dmi new file mode 100644 index 000000000000..7528447a8329 Binary files /dev/null and b/icons/items/stream_projector/holofabricator.dmi differ diff --git a/icons/items/stream_projector/medichine.dmi b/icons/items/stream_projector/medichine.dmi new file mode 100644 index 000000000000..e225e1182206 Binary files /dev/null and b/icons/items/stream_projector/medichine.dmi differ diff --git a/icons/mob/clothing/modsuit/mod_clothing.dmi b/icons/mob/clothing/modsuit/mod_clothing.dmi deleted file mode 100644 index 5f51f6182762..000000000000 Binary files a/icons/mob/clothing/modsuit/mod_clothing.dmi and /dev/null differ diff --git a/icons/mob/clothing/modsuit/mod_modules.dmi b/icons/mob/clothing/modsuit/mod_modules.dmi deleted file mode 100644 index aaf9541d5b78..000000000000 Binary files a/icons/mob/clothing/modsuit/mod_modules.dmi and /dev/null differ diff --git a/icons/modules/rigsuits/baylikes/clothing1.dmi b/icons/modules/rigsuits/baylikes/clothing1.dmi new file mode 100644 index 000000000000..fbbfbd1db264 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing1.dmi differ diff --git a/icons/modules/rigsuits/baylikes/clothing10.dmi b/icons/modules/rigsuits/baylikes/clothing10.dmi new file mode 100644 index 000000000000..fb7277c6bf37 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing10.dmi differ diff --git a/icons/modules/rigsuits/baylikes/clothing11.dmi b/icons/modules/rigsuits/baylikes/clothing11.dmi new file mode 100644 index 000000000000..66a18d08c550 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing11.dmi differ diff --git a/icons/modules/rigsuits/baylikes/clothing12.dmi b/icons/modules/rigsuits/baylikes/clothing12.dmi new file mode 100644 index 000000000000..f214c1e6133f Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing12.dmi differ diff --git a/icons/modules/rigsuits/baylikes/clothing2.dmi b/icons/modules/rigsuits/baylikes/clothing2.dmi new file mode 100644 index 000000000000..c82024e7141c Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing2.dmi differ diff --git a/icons/modules/rigsuits/baylikes/clothing3.dmi b/icons/modules/rigsuits/baylikes/clothing3.dmi new file mode 100644 index 000000000000..612be17223da Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing3.dmi differ diff --git a/icons/modules/rigsuits/baylikes/clothing4.dmi b/icons/modules/rigsuits/baylikes/clothing4.dmi new file mode 100644 index 000000000000..019688f5c133 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing4.dmi differ diff --git a/icons/modules/rigsuits/baylikes/clothing5.dmi b/icons/modules/rigsuits/baylikes/clothing5.dmi new file mode 100644 index 000000000000..441da3dd82bc Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing5.dmi differ diff --git a/icons/modules/rigsuits/baylikes/clothing6.dmi b/icons/modules/rigsuits/baylikes/clothing6.dmi new file mode 100644 index 000000000000..155e3ec831ee Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing6.dmi differ diff --git a/icons/modules/rigsuits/baylikes/clothing7.dmi b/icons/modules/rigsuits/baylikes/clothing7.dmi new file mode 100644 index 000000000000..8d36fa2cef15 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing7.dmi differ diff --git a/icons/modules/rigsuits/baylikes/clothing8.dmi b/icons/modules/rigsuits/baylikes/clothing8.dmi new file mode 100644 index 000000000000..396788174abd Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing8.dmi differ diff --git a/icons/modules/rigsuits/baylikes/clothing9.dmi b/icons/modules/rigsuits/baylikes/clothing9.dmi new file mode 100644 index 000000000000..93f194ebba95 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/clothing9.dmi differ diff --git a/icons/modules/rigsuits/baylikes/modules.dmi b/icons/modules/rigsuits/baylikes/modules.dmi new file mode 100644 index 000000000000..3c541dc8ecc5 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/modules.dmi differ diff --git a/icons/modules/rigsuits/baylikes/modules_onmob.dmi b/icons/modules/rigsuits/baylikes/modules_onmob.dmi new file mode 100644 index 000000000000..e9bd202acc8e Binary files /dev/null and b/icons/modules/rigsuits/baylikes/modules_onmob.dmi differ diff --git a/icons/modules/rigsuits/baylikes/polaris_itemback.dmi b/icons/modules/rigsuits/baylikes/polaris_itemback.dmi new file mode 100644 index 000000000000..3b8f6b7dda51 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/polaris_itemback.dmi differ diff --git a/icons/modules/rigsuits/baylikes/polaris_itemchest.dmi b/icons/modules/rigsuits/baylikes/polaris_itemchest.dmi new file mode 100644 index 000000000000..6e85184ebf03 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/polaris_itemchest.dmi differ diff --git a/icons/modules/rigsuits/baylikes/polaris_itemgloves.dmi b/icons/modules/rigsuits/baylikes/polaris_itemgloves.dmi new file mode 100644 index 000000000000..7b934cc6f6d0 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/polaris_itemgloves.dmi differ diff --git a/icons/modules/rigsuits/baylikes/polaris_itemhead.dmi b/icons/modules/rigsuits/baylikes/polaris_itemhead.dmi new file mode 100644 index 000000000000..2de98c328f96 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/polaris_itemhead.dmi differ diff --git a/icons/modules/rigsuits/baylikes/polaris_itemshoes.dmi b/icons/modules/rigsuits/baylikes/polaris_itemshoes.dmi new file mode 100644 index 000000000000..e6a44884b42c Binary files /dev/null and b/icons/modules/rigsuits/baylikes/polaris_itemshoes.dmi differ diff --git a/icons/modules/rigsuits/baylikes/polaris_mobback.dmi b/icons/modules/rigsuits/baylikes/polaris_mobback.dmi new file mode 100644 index 000000000000..6ff53858bb83 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/polaris_mobback.dmi differ diff --git a/icons/modules/rigsuits/baylikes/polaris_mobchest.dmi b/icons/modules/rigsuits/baylikes/polaris_mobchest.dmi new file mode 100644 index 000000000000..96e0a2a9ba1e Binary files /dev/null and b/icons/modules/rigsuits/baylikes/polaris_mobchest.dmi differ diff --git a/icons/modules/rigsuits/baylikes/polaris_mobgloves.dmi b/icons/modules/rigsuits/baylikes/polaris_mobgloves.dmi new file mode 100644 index 000000000000..8c9c908b54ee Binary files /dev/null and b/icons/modules/rigsuits/baylikes/polaris_mobgloves.dmi differ diff --git a/icons/modules/rigsuits/baylikes/polaris_mobhead.dmi b/icons/modules/rigsuits/baylikes/polaris_mobhead.dmi new file mode 100644 index 000000000000..3a8885b892f9 Binary files /dev/null and b/icons/modules/rigsuits/baylikes/polaris_mobhead.dmi differ diff --git a/icons/modules/rigsuits/baylikes/polaris_mobshoes.dmi b/icons/modules/rigsuits/baylikes/polaris_mobshoes.dmi new file mode 100644 index 000000000000..bf56ab05696c Binary files /dev/null and b/icons/modules/rigsuits/baylikes/polaris_mobshoes.dmi differ diff --git a/icons/modules/rigsuits/suits/cargo.dmi b/icons/modules/rigsuits/suits/cargo.dmi new file mode 100644 index 000000000000..bd8b9c87fc59 Binary files /dev/null and b/icons/modules/rigsuits/suits/cargo.dmi differ diff --git a/icons/modules/rigsuits/suits/civilian.dmi b/icons/modules/rigsuits/suits/civilian.dmi new file mode 100644 index 000000000000..a366a79760f7 Binary files /dev/null and b/icons/modules/rigsuits/suits/civilian.dmi differ diff --git a/icons/modules/rigsuits/suits/command.dmi b/icons/modules/rigsuits/suits/command.dmi new file mode 100644 index 000000000000..dc8a9b1a92f6 Binary files /dev/null and b/icons/modules/rigsuits/suits/command.dmi differ diff --git a/icons/modules/rigsuits/suits/engineering.dmi b/icons/modules/rigsuits/suits/engineering.dmi new file mode 100644 index 000000000000..5e26d0c87aaa Binary files /dev/null and b/icons/modules/rigsuits/suits/engineering.dmi differ diff --git a/icons/modules/rigsuits/suits/exploration.dmi b/icons/modules/rigsuits/suits/exploration.dmi new file mode 100644 index 000000000000..b489e140999f Binary files /dev/null and b/icons/modules/rigsuits/suits/exploration.dmi differ diff --git a/icons/modules/rigsuits/suits/factions/military_gorlex.dmi b/icons/modules/rigsuits/suits/factions/military_gorlex.dmi new file mode 100644 index 000000000000..7c4bd8bc1367 Binary files /dev/null and b/icons/modules/rigsuits/suits/factions/military_gorlex.dmi differ diff --git a/icons/modules/rigsuits/suits/factions/military_marine.dmi b/icons/modules/rigsuits/suits/factions/military_marine.dmi new file mode 100644 index 000000000000..e960428ce3af Binary files /dev/null and b/icons/modules/rigsuits/suits/factions/military_marine.dmi differ diff --git a/icons/modules/rigsuits/suits/factions/military_pmc.dmi b/icons/modules/rigsuits/suits/factions/military_pmc.dmi new file mode 100644 index 000000000000..2096bece2238 Binary files /dev/null and b/icons/modules/rigsuits/suits/factions/military_pmc.dmi differ diff --git a/icons/modules/rigsuits/suits/factions/military_sleek.dmi b/icons/modules/rigsuits/suits/factions/military_sleek.dmi new file mode 100644 index 000000000000..29d77bfdd5cd Binary files /dev/null and b/icons/modules/rigsuits/suits/factions/military_sleek.dmi differ diff --git a/icons/modules/rigsuits/suits/factions/nanotrasen.dmi b/icons/modules/rigsuits/suits/factions/nanotrasen.dmi new file mode 100644 index 000000000000..10570d58e3e8 Binary files /dev/null and b/icons/modules/rigsuits/suits/factions/nanotrasen.dmi differ diff --git a/icons/modules/rigsuits/suits/factions/nanotrasen_military.dmi b/icons/modules/rigsuits/suits/factions/nanotrasen_military.dmi new file mode 100644 index 000000000000..024dfc320f75 Binary files /dev/null and b/icons/modules/rigsuits/suits/factions/nanotrasen_military.dmi differ diff --git a/icons/modules/rigsuits/suits/factions/nanotrasen_response.dmi b/icons/modules/rigsuits/suits/factions/nanotrasen_response.dmi new file mode 100644 index 000000000000..515ae8b7398b Binary files /dev/null and b/icons/modules/rigsuits/suits/factions/nanotrasen_response.dmi differ diff --git a/icons/modules/rigsuits/suits/medical.dmi b/icons/modules/rigsuits/suits/medical.dmi new file mode 100644 index 000000000000..a957b23350e4 Binary files /dev/null and b/icons/modules/rigsuits/suits/medical.dmi differ diff --git a/icons/modules/rigsuits/suits/science.dmi b/icons/modules/rigsuits/suits/science.dmi new file mode 100644 index 000000000000..d319046f9540 Binary files /dev/null and b/icons/modules/rigsuits/suits/science.dmi differ diff --git a/icons/modules/rigsuits/suits/security.dmi b/icons/modules/rigsuits/suits/security.dmi new file mode 100644 index 000000000000..b606277268f3 Binary files /dev/null and b/icons/modules/rigsuits/suits/security.dmi differ diff --git a/icons/modules/rigsuits/suits/species/nepid.dmi b/icons/modules/rigsuits/suits/species/nepid.dmi new file mode 100644 index 000000000000..78f004ac0205 Binary files /dev/null and b/icons/modules/rigsuits/suits/species/nepid.dmi differ diff --git a/icons/modules/rigsuits/suits/species/protean.dmi b/icons/modules/rigsuits/suits/species/protean.dmi new file mode 100644 index 000000000000..4185e12b8cbb Binary files /dev/null and b/icons/modules/rigsuits/suits/species/protean.dmi differ diff --git a/icons/modules/rigsuits/tg/clothing.dmi b/icons/modules/rigsuits/tg/clothing.dmi new file mode 100644 index 000000000000..13791df4f8e5 Binary files /dev/null and b/icons/modules/rigsuits/tg/clothing.dmi differ diff --git a/icons/modules/rigsuits/tg/clothing_onmob.dmi b/icons/modules/rigsuits/tg/clothing_onmob.dmi new file mode 100644 index 000000000000..bbd44cf2ef7d Binary files /dev/null and b/icons/modules/rigsuits/tg/clothing_onmob.dmi differ diff --git a/icons/modules/rigsuits/tg/construction.dmi b/icons/modules/rigsuits/tg/construction.dmi new file mode 100644 index 000000000000..50e0848c5b32 Binary files /dev/null and b/icons/modules/rigsuits/tg/construction.dmi differ diff --git a/icons/modules/rigsuits/tg/modules.dmi b/icons/modules/rigsuits/tg/modules.dmi new file mode 100644 index 000000000000..25268e5ad1c0 Binary files /dev/null and b/icons/modules/rigsuits/tg/modules.dmi differ diff --git a/icons/modules/rigsuits/tg/modules_onmob.dmi b/icons/modules/rigsuits/tg/modules_onmob.dmi new file mode 100644 index 000000000000..3c41dac86b2d Binary files /dev/null and b/icons/modules/rigsuits/tg/modules_onmob.dmi differ diff --git a/icons/obj/clothing/modsuit/mod_clothing.dmi b/icons/obj/clothing/modsuit/mod_clothing.dmi deleted file mode 100644 index aebff91268f4..000000000000 Binary files a/icons/obj/clothing/modsuit/mod_clothing.dmi and /dev/null differ diff --git a/icons/obj/clothing/modsuit/mod_construction.dmi b/icons/obj/clothing/modsuit/mod_construction.dmi deleted file mode 100644 index a6be94284af8..000000000000 Binary files a/icons/obj/clothing/modsuit/mod_construction.dmi and /dev/null differ diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi deleted file mode 100644 index e41fc164a2a5..000000000000 Binary files a/icons/obj/clothing/modsuit/mod_modules.dmi and /dev/null differ diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml index 31d1b345d478..5242eb9d80d8 100644 --- a/tgui/.eslintrc.yml +++ b/tgui/.eslintrc.yml @@ -716,7 +716,7 @@ rules: ## Validate JSX has key prop when in array or iterator react/jsx-key: error ## Validate JSX maximum depth - react/jsx-max-depth: [error, { max: 10 }] ## Generous + react/jsx-max-depth: [error, { max: 15 }] ## Even more generous ~silicons ## Limit maximum of props on a single line in JSX (fixable) # react/jsx-max-props-per-line: error ## Prevent usage of .bind() and arrow functions in JSX props diff --git a/tgui/packages/tgfont/config.cjs b/tgui/packages/tgfont/config.cjs index 4f6b58f1061e..d4ac72dd87f3 100644 --- a/tgui/packages/tgfont/config.cjs +++ b/tgui/packages/tgfont/config.cjs @@ -8,7 +8,11 @@ module.exports = { name: 'tgfont', inputDir: './icons', outputDir: './dist', + normalize: true, fontTypes: ['woff2', 'eot'], assetTypes: ['css'], prefix: 'tg', + formatOptions: { + preserveAspectRatio: true, + }, }; diff --git a/tgui/packages/tgfont/icons/s1-boots.svg b/tgui/packages/tgfont/icons/s1-boots.svg new file mode 100644 index 000000000000..087b48b29059 --- /dev/null +++ b/tgui/packages/tgfont/icons/s1-boots.svg @@ -0,0 +1,41 @@ + + + + + + + + diff --git a/tgui/packages/tgfont/icons/s1-chestplate.svg b/tgui/packages/tgfont/icons/s1-chestplate.svg new file mode 100644 index 000000000000..33ca1bac7d0c --- /dev/null +++ b/tgui/packages/tgfont/icons/s1-chestplate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tgui/packages/tgfont/icons/s1-cube.svg b/tgui/packages/tgfont/icons/s1-cube.svg new file mode 100644 index 000000000000..7b8e7825c361 --- /dev/null +++ b/tgui/packages/tgfont/icons/s1-cube.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/tgui/packages/tgfont/icons/s1-glove-left.svg b/tgui/packages/tgfont/icons/s1-glove-left.svg new file mode 100644 index 000000000000..4a74d5ee57c0 --- /dev/null +++ b/tgui/packages/tgfont/icons/s1-glove-left.svg @@ -0,0 +1,37 @@ + + + + + + diff --git a/tgui/packages/tgfont/icons/s1-glove-right.svg b/tgui/packages/tgfont/icons/s1-glove-right.svg new file mode 100644 index 000000000000..18dcc5d620d0 --- /dev/null +++ b/tgui/packages/tgfont/icons/s1-glove-right.svg @@ -0,0 +1,37 @@ + + + + + + diff --git a/tgui/packages/tgfont/icons/s1-gloves.svg b/tgui/packages/tgfont/icons/s1-gloves.svg new file mode 100644 index 000000000000..34cdcd1013c5 --- /dev/null +++ b/tgui/packages/tgfont/icons/s1-gloves.svg @@ -0,0 +1,37 @@ + + + + + + diff --git a/tgui/packages/tgfont/icons/s1-space-helmet.svg b/tgui/packages/tgfont/icons/s1-space-helmet.svg new file mode 100644 index 000000000000..e63b5aa41423 --- /dev/null +++ b/tgui/packages/tgfont/icons/s1-space-helmet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tgui/packages/tgfont/icons/s1-stack.svg b/tgui/packages/tgfont/icons/s1-stack.svg new file mode 100644 index 000000000000..d0ecd0136f7f --- /dev/null +++ b/tgui/packages/tgfont/icons/s1-stack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tgui/packages/tgfont/icons/t-cable.svg b/tgui/packages/tgfont/icons/t-cable.svg new file mode 100644 index 000000000000..90eb74af185f --- /dev/null +++ b/tgui/packages/tgfont/icons/t-cable.svg @@ -0,0 +1,41 @@ + + + + diff --git a/tgui/packages/tgfont/icons/t-crowbar.svg b/tgui/packages/tgfont/icons/t-crowbar.svg new file mode 100644 index 000000000000..5ea9f04f2901 --- /dev/null +++ b/tgui/packages/tgfont/icons/t-crowbar.svg @@ -0,0 +1,42 @@ + + + + diff --git a/tgui/packages/tgfont/icons/t-multitool.svg b/tgui/packages/tgfont/icons/t-multitool.svg new file mode 100644 index 000000000000..05ee4ef0b9a6 --- /dev/null +++ b/tgui/packages/tgfont/icons/t-multitool.svg @@ -0,0 +1,66 @@ + + + + diff --git a/tgui/packages/tgfont/icons/t-screwdriver.svg b/tgui/packages/tgfont/icons/t-screwdriver.svg new file mode 100644 index 000000000000..ee82c96963e1 --- /dev/null +++ b/tgui/packages/tgfont/icons/t-screwdriver.svg @@ -0,0 +1,42 @@ + + + + diff --git a/tgui/packages/tgfont/icons/t-welder.svg b/tgui/packages/tgfont/icons/t-welder.svg new file mode 100644 index 000000000000..dffa7f2e6d7f --- /dev/null +++ b/tgui/packages/tgfont/icons/t-welder.svg @@ -0,0 +1,42 @@ + + + + diff --git a/tgui/packages/tgfont/icons/t-wirecutters.svg b/tgui/packages/tgfont/icons/t-wirecutters.svg new file mode 100644 index 000000000000..6f6f60e951d7 --- /dev/null +++ b/tgui/packages/tgfont/icons/t-wirecutters.svg @@ -0,0 +1,51 @@ + + + + diff --git a/tgui/packages/tgfont/icons/t-wrench.svg b/tgui/packages/tgfont/icons/t-wrench.svg new file mode 100644 index 000000000000..349076e4ee3d --- /dev/null +++ b/tgui/packages/tgfont/icons/t-wrench.svg @@ -0,0 +1,69 @@ + + + + diff --git a/tgui/packages/tgfont/package.json b/tgui/packages/tgfont/package.json index 9459aa129696..37ae13328c82 100644 --- a/tgui/packages/tgfont/package.json +++ b/tgui/packages/tgfont/package.json @@ -6,6 +6,6 @@ "tgfont:build": "node mkdist.cjs && fantasticon --config config.cjs" }, "dependencies": { - "fantasticon": "^1.2.2" + "fantasticon": "1.2.3" } } diff --git a/tgui/packages/tgfont/static/tgfont.css b/tgui/packages/tgfont/static/tgfont.css index ad732b8fc068..583a7543af07 100644 --- a/tgui/packages/tgfont/static/tgfont.css +++ b/tgui/packages/tgfont/static/tgfont.css @@ -1,7 +1,7 @@ @font-face { font-family: "tgfont"; - src: url("./tgfont.woff2?45c3c7acc69dd413375d77898d24e41e") format("woff2"), -url("./tgfont.eot?45c3c7acc69dd413375d77898d24e41e#iefix") format("embedded-opentype"); + src: url("./tgfont.woff2?0aae4a9273063ed9348b1c17407151fd") format("woff2"), +url("./tgfont.eot?0aae4a9273063ed9348b1c17407151fd#iefix") format("embedded-opentype"); } i[class^="tg-"]:before, i[class*=" tg-"]:before { @@ -39,12 +39,57 @@ i[class^="tg-"]:before, i[class*=" tg-"]:before { .tg-prosthetic-leg:before { content: "\f108"; } -.tg-sound-minus:before { +.tg-s1-boots:before { content: "\f109"; } -.tg-sound-plus:before { +.tg-s1-chestplate:before { content: "\f10a"; } -.tg-syndicate-logo:before { +.tg-s1-cube:before { content: "\f10b"; } +.tg-s1-glove-left:before { + content: "\f10c"; +} +.tg-s1-glove-right:before { + content: "\f10d"; +} +.tg-s1-gloves:before { + content: "\f10e"; +} +.tg-s1-space-helmet:before { + content: "\f10f"; +} +.tg-s1-stack:before { + content: "\f110"; +} +.tg-sound-minus:before { + content: "\f111"; +} +.tg-sound-plus:before { + content: "\f112"; +} +.tg-syndicate-logo:before { + content: "\f113"; +} +.tg-t-cable:before { + content: "\f114"; +} +.tg-t-crowbar:before { + content: "\f115"; +} +.tg-t-multitool:before { + content: "\f116"; +} +.tg-t-screwdriver:before { + content: "\f117"; +} +.tg-t-welder:before { + content: "\f118"; +} +.tg-t-wirecutters:before { + content: "\f119"; +} +.tg-t-wrench:before { + content: "\f11a"; +} diff --git a/tgui/packages/tgfont/static/tgfont.eot b/tgui/packages/tgfont/static/tgfont.eot index 09e774836e30..6a5eb28aabaa 100644 Binary files a/tgui/packages/tgfont/static/tgfont.eot and b/tgui/packages/tgfont/static/tgfont.eot differ diff --git a/tgui/packages/tgfont/static/tgfont.woff2 b/tgui/packages/tgfont/static/tgfont.woff2 index 0590bf6c44fb..270eedb0be41 100644 Binary files a/tgui/packages/tgfont/static/tgfont.woff2 and b/tgui/packages/tgfont/static/tgfont.woff2 differ diff --git a/tgui/packages/tgui/backend.ts b/tgui/packages/tgui/backend.ts index 83a76d0f89bf..cbaa9932efbd 100644 --- a/tgui/packages/tgui/backend.ts +++ b/tgui/packages/tgui/backend.ts @@ -453,7 +453,7 @@ export type ModuleBackend = { data: TData; act: actFunctionType; backend: Backend<{}>; - // / module id if is currently embedded module, null otherwise + // module id if is currently embedded module, null otherwise moduleID: string | null; } @@ -511,3 +511,11 @@ export const constructModuleAct = (id: string, ref: string): actFunctionType => Byond.sendMessage('mod/' + action, sent); }; }; + +/** + * Extracts module data from context + */ +export const getModuleData = (context, id: string): TData => { + let backend = useBackend(context); + return backend.modules[id]; +}; diff --git a/tgui/packages/tgui/components/Box.tsx b/tgui/packages/tgui/components/Box.tsx index f5503e5482f3..419217a91183 100644 --- a/tgui/packages/tgui/components/Box.tsx +++ b/tgui/packages/tgui/components/Box.tsx @@ -4,7 +4,7 @@ * @license MIT */ -import { BooleanLike, classes, pureComponentHooks } from 'common/react'; +import { BooleanLike, classes, pureComponentHooks } from '../../common/react'; import { createVNode } from 'inferno'; import { ChildFlags, VNodeFlags } from 'inferno-vnode-flags'; import { CSS_COLORS } from '../constants'; @@ -17,7 +17,7 @@ export type BoxProps = ComponentProps & { position?: string | BooleanLike; overflow?: string | BooleanLike; overflowX?: string | BooleanLike; - overflowY?: string | BooleanLike; + overflowY?: string| BooleanLike; top?: string | BooleanLike; bottom?: string | BooleanLike; left?: string | BooleanLike; diff --git a/tgui/packages/tgui/components/Button.tsx b/tgui/packages/tgui/components/Button.tsx index 6b1789c508a9..7ee66486c329 100644 --- a/tgui/packages/tgui/components/Button.tsx +++ b/tgui/packages/tgui/components/Button.tsx @@ -1,4 +1,7 @@ /** + * todo: combine flashing and selected into ButtonStatus or something, god. + * todo: combine circular/elipssis/etc into a style, god. this is awful. + * * @file * @copyright 2020 Aleksej Komarov * @license MIT @@ -11,13 +14,14 @@ import { Component, createRef } from 'inferno'; import { createLogger } from '../logging'; import { Box, BoxProps, computeBoxClassName, computeBoxProps } from './Box'; import { Icon } from './Icon'; +import { markClickEventNoSwitchTab } from './Tabs'; import { Tooltip } from './Tooltip'; const logger = createLogger('Button'); export type ButtonProps = BoxProps & { readonly fluid?: BooleanLike; - readonly icon?: string | BooleanLike; + readonly icon?: string; readonly iconRotation?: number; readonly iconSpin?: BooleanLike; readonly iconColor?: any; @@ -33,6 +37,7 @@ export type ButtonProps = BoxProps & { readonly content?: any; readonly onClick?: any; readonly verticalAlignContent?: 'top' | 'middle' | 'bottom'; + readonly flashing?: BooleanLike; } export const Button = (props: ButtonProps) => { @@ -69,6 +74,7 @@ export const Button = (props: ButtonProps) => { + `https://infernojs.org/docs/guides/event-handling`); } rest.onClick = e => { + markClickEventNoSwitchTab(e); if (!disabled && onClick) { onClick(e); } @@ -77,13 +83,13 @@ export const Button = (props: ButtonProps) => { if (Byond.IS_LTE_IE8) { rest.unselectable = true; } + let buttonColor = props.color || 'default'; let buttonContent = (
{ verticalAlignContent && "Button--flex", (verticalAlignContent && fluid) && "Button--flex--fluid", verticalAlignContent && 'Button--verticalAlignContent--' + verticalAlignContent, - (color && typeof color === 'string') - ? 'Button--color--' + color - : 'Button--color--default', + (props.flashing? `Button--flash--${buttonColor}` : (`Button--color--${buttonColor}`)), + selected && !props.flashing && (buttonColor === 'default' || buttonColor === 'transparent') + && 'Button--selected', className, computeBoxClassName(rest), ])} @@ -159,15 +165,25 @@ Button.defaultHooks = pureComponentHooks; interface ButtonCheckboxProps extends ButtonProps { readonly checked?: BooleanLike; + readonly checkedIcon?: string; + readonly uncheckedIcon?: string; } export const ButtonCheckbox = (props: ButtonCheckboxProps) => { - const { checked, ...rest } = props; + const { + checked, + icon, + color, + selected = checked, + checkedIcon = 'check-square-o', + uncheckedIcon = 'square-o', + ...rest + } = props; return ( - - - - - ); -}; - -const displayText = param => { - switch (param) { - case 1: - return "Use"; - case 2: - return "Toggle"; - case 3: - return "Select"; - } -}; - -const ParametersSection = (props, context) => { - const { act, data } = useBackend(context); - const { - active, - malfunctioning, - locked, - open, - selected_module, - complexity, - complexity_max, - wearer_name, - wearer_job, - AI, - } = data; - const status = malfunctioning - ? 'Malfunctioning' : active - ? 'Active' : 'Inactive'; - return ( -
- - act('activate')} /> - } > - {status} - - act('lock')} /> - } > - {locked ? 'Locked' : 'Unlocked'} - - - {open ? 'Open' : 'Closed'} - - - {selected_module || "None"} - - - {complexity} ({complexity_max}) - - - {wearer_name}, {wearer_job} - - - {AI || 'None'} - - -
- ); -}; - -const HardwareSection = (props, context) => { - const { act, data } = useBackend(context); - const { - active, - control, - helmet, - chestplate, - gauntlets, - boots, - core, - charge, - } = data; - return ( -
- - - - {control} - - - {helmet || "None"} - - - {chestplate || "None"} - - - {gauntlets || "None"} - - - {boots || "None"} - - - - - {core && ( - - - {core} - - - - - - ) || ( - No Core Detected - )} - -
- ); -}; - -const InfoSection = (props, context) => { - const { act, data } = useBackend(context); - const { - active, - modules, - } = data; - const info_modules = modules.filter(module => !!module.id); - - return ( -
- - {info_modules.length !== 0 && info_modules.map(module => { - const Module = ID2MODULE[module.id]; - return ( - - {!active && } - - - ); - }) || ( - No Info Modules Detected - )} - -
- ); -}; - -const ModuleSection = (props, context) => { - const { act, data } = useBackend(context); - const { - complexity_max, - modules, - } = data; - const [configureState, setConfigureState] - = useLocalState(context, "module_configuration", null); - return ( -
- - {modules.length !== 0 && modules.map(module => { - return ( - - -
- {configureState === module.ref && ( - setConfigureState(null)} />)} - - - -
- - {module.description} - -
-
-
- ); - }) || ( - - No Modules Detected - - )} -
-
- ); -}; - -export const MODsuit = (props, context) => { - const { act, data } = useBackend(context); - const { - ui_theme, - interface_break, - } = data; - return ( - - - {!!interface_break && ( - - ) || ( - - - - - - - - - - - - - - - )} - - - ); -}; diff --git a/tgui/packages/tgui/interfaces/RIGSuit.js b/tgui/packages/tgui/interfaces/RIGSuit.js deleted file mode 100644 index 167fc1fe1c9a..000000000000 --- a/tgui/packages/tgui/interfaces/RIGSuit.js +++ /dev/null @@ -1,321 +0,0 @@ -import { Fragment } from 'inferno'; -import { useBackend } from "../backend"; -import { Box, Button, Flex, LabeledList, ProgressBar, Section } from "../components"; -import { Window } from "../layouts"; -import { capitalize, toTitleCase } from 'common/string'; - -export const RIGSuit = (props, context) => { - const { act, data } = useBackend(context); - - const { - interfacelock, - malf, - aicontrol, - ai, - } = data; - - let override = null; - - if (interfacelock || malf) { - // Interface is offline, or a malf AI took over, either way, the user is - // no longer permitted to view this interface. - override = --HARDSUIT INTERFACE OFFLINE--; - } else if (!ai && aicontrol) { - // Non-AI trying to control the hardsuit while it's AI control overridden - override = -- HARDSUIT CONTROL OVERRIDDEN BY AI --; - } - - return ( - - - {override || ( - - - - - - )} - - - ); -}; - -const RIGSuitStatus = (props, context) => { - const { act, data } = useBackend(context); - - const { - // Power Bar - chargestatus, - charge, - maxcharge, - // AI Control Toggle - aioverride, - // Suit Status - sealing, - sealed, - cooling, - // Cover Locks - emagged, - securitycheck, - coverlock, - } = data; - - const SealButton = ( -