Files
Bubberstation/code/modules/mod/adding_new_mod.md
Pickle-Coding c1f11f26ce Converts arbitrary energy units to the joule. Fixes conservation of energy issues relating to charging cells. (#81579)
## About The Pull Request
Removes all arbitrary energy and power units in the codebase. Everything
is replaced with the joule and watt, with 1 = 1 joule, or 1 watt if you
are going to multiply by time. This is a visible change, where all
arbitrary energy units you see in the game will get proper prefixed
units of energy.

With power cells being converted to the joule, charging one joule of a
power cell will require one joule of energy.

The grid will now store energy, instead of power. When an energy usage
is described as using the watt, a power to energy conversion based on
the relevant subsystem's timing (usually multiplying by seconds_per_tick
or applying power_to_energy()) is needed before adding or removing from
the grid. Power usages that are described as the watt is really anything
you would scale by time before applying the load. If it's described as a
joule, no time conversion is needed. Players will still read the grid as
power, having no visible change.

Machines that dynamically use power with the use_power() proc will
directly drain from the grid (and apc cell if there isn't enough)
instead of just tallying it up on the dynamic power usages for the area.
This should be more robust at conserving energy as the surplus is
updated on the go, preventing charging cells from nothing.

APCs no longer consume power for the dynamic power usage channels. APCs
will consume power for static power usages. Because static power usages
are added up without checking surplus, static power consumption will be
applied before any machine processes. This will give a more truthful
surplus for dynamic power consumers.

APCs will display how much power it is using for charging the cell. APC
cell charging applies power in its own channel, which gets added up to
the total. This will prevent invisible power usage you see when looking
at the power monitoring console.

After testing in MetaStation, I found roundstart power consumption to be
around 406kW after all APCs get fully charged. During the roundstart APC
charge rush, the power consumption can get as high as over 2MW (up to
25kW per roundstart APC charging) as long as there's that much
available.

Because of the absurd potential power consumption of charging APCs near
roundstart, I have changed how APCs decide to charge. APCs will now
charge only after all other machines have processed in the machines
processing subsystem. This will make sure APC charging won't disrupt
machines taking from the grid, and should stop APCs getting their power
drained due to others demanding too much power while charging. I have
removed the delays for APC charging too, so they start charging
immediately whenever there's excess power. It also stops them turning
red when a small amount of cell gets drained (airlocks opening and shit
during APC charge rush), as they immediately become fully charged
(unless too much energy got drained somehow) before changing icon.

Engineering SMES now start at 100% charge instead of 75%. I noticed
cells were draining earlier than usual after these changes, so I am
making them start maxed to try and combat that.

These changes will fix all conservation of energy issues relating to
charging powercells.
## Why It's Good For The Game
Closes #73438
Closes #75789
Closes #80634
Closes #82031

Makes it much easier to interface with the power system in the codebase.
It's more intuitive. Removes a bunch of conservation of energy issues,
making energy and power much more meaningful. It will help the
simulation remain immersive as players won't encounter energy
duplication so easily. Arbitrary energy units getting replaced with the
joule will also tell people more meaningful information when reading it.
APC charging will feel more snappy.
## Changelog
🆑
fix: Fixes conservation of energy issues relating to charging
powercells.
qol: APCs will display how much power they are using to charge their
cell. This is accounted for in the power monitoring console.
qol: All arbitrary power cell energy units you see are replaced with
prefixed joules.
balance: As a consequence of the conservation of energy issues getting
fixed, the power consumption for charging cells is now very significant.
balance: APCs only use surplus power from the grid after every machine
processes when charging, preventing APCs from causing others to
discharge while charging.
balance: Engineering SMES start at max charge to combat the increased
energy loss due to conservation of energy fixes.
/🆑

---------

Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
2024-03-23 16:58:56 +01:00

13 KiB

Introduction

This is a step by step guide for creating a MODsuit theme, skin and module.

Theme

This is pretty simple, we go here and add a new definition, let's go with a Psychologist theme as an example.
Their names should be like model names or use similar adjectives, like "magnate" or simply "engineering", so we'll go with "psychological".
After that, it's good to decide what company is manufacturing the suit, and a basic description of what it offers, we'll write that down in the desc.
So, let's our suit should be a low-power usage with lowered module capacity. We'd go with something like this.

/datum/mod_theme/psychological
	name = "psychological"
	desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."

For people that want to see additional stuff, we add an extended description with some more insight into what the suit does. We also set the default skin to usually the theme name, like so.

/datum/mod_theme/psychological
	name = "psychological"
	desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
	extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
		Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
		for operating at lower power levels, keeping people sane. As consequence, the capacity \
		of the suit has decreased, not being able to fit many modules at all."
	default_skin = "psychological"

Next we want to set the statistics, you can view them all in the theme file, so let's just grab our relevant ones, armor, charge and capacity and set them to what we establilished.
Currently crew MODsuits should be lightly armored in combat relevant stats.

/datum/mod_theme/psychological
	name = "psychological"
	desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
	extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
		Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
		for operating at lower power levels, keeping people sane. As consequence, the capacity \
		of the suit has decreased, not being able to fit many modules at all."
	default_skin = "psychological"
	armor_type = /datum/armor/modtheme_psychological
	complexity_max = DEFAULT_MAX_COMPLEXITY - 7
	charge_drain = DEFAULT_CHARGE_DRAIN * 0.5

Now we have a basic theme, it lacks a skin which will be covered in the next section, and an item, which we will add right now.
Let's go into here. It's as simple as adding a new suit type with the appropriate modules you want.

/obj/item/mod/control/pre_equipped/psychological
	theme = /datum/mod_theme/psychological
	initial_modules = list(
		/obj/item/mod/module/storage,
		/obj/item/mod/module/flashlight,
	)

This will create our psychological suit, equipped with a storage and flashlight modules by default. We might also want to make it craftable, in which case we go here and set this.

/obj/item/mod/construction/armor/psychological
	theme = /datum/mod_theme/psychological

After that we put it in the techweb or whatever other source we want. Now our suit is almost ready, it just needs a skin.

Skin

So, now that we have our theme, we want to add a skin to it (or another theme of our choosing). Let's start with a basis.

/datum/mod_theme/psychological
	name = "psychological"
	desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
	extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
		Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
		for operating at lower power levels, keeping people sane. As consequence, the capacity \
		of the suit has decreased, not being able to fit many modules at all."
	default_skin = "psychological"
	armor_type = /datum/armor/modtheme_psychological
	complexity_max = DEFAULT_MAX_COMPLEXITY - 7
	charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
	skins = list(
		"psychological" = list(
			HELMET_LAYER = null,
			HELMET_FLAGS = list(
			),
			CHESTPLATE_FLAGS = list(
			),
			GAUNTLETS_FLAGS = list(
			),
			BOOTS_FLAGS = list(
			),
		),
	)

We now have a psychological skin, this will apply the psychological icons to every part of the suit. Next we'll be looking at the flags. Boots, gauntlets and the chestplate are usually very standard, we set their thickmaterial and pressureproofness while hiding the jumpsuit on the chestplate. On the helmet however, we'll actually look at its' icon.
For example, if our helmet's icon covers the full head (like the research skin), we want to do something like this.

			HELMET_LAYER = null,
			HELMET_FLAGS = list(
				UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
				UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
				UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
			),

Otherwise, with an open helmet that becomes closed (like the engineering skin), we'd do this.

			HELMET_LAYER = NECK_LAYER,
			HELMET_FLAGS = list(
				UNSEALED_CLOTHING = SNUG_FIT,
				SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
				UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
				SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
				SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
			),

There are specific cases of helmets that semi-cover the head, like the cosmohonk, apocryphal and whatnot. You can look at these for more specific suits. So let's say our suit is an open helmet design, and also add an alternate skin with a closed helmet called psychotherapeutic. It'd look something like this.

/datum/mod_theme/psychological
	name = "psychological"
	desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
	extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
		Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
		for operating at lower power levels, keeping people sane. As consequence, the capacity \
		of the suit has decreased, not being able to fit many modules at all."
	default_skin = "psychological"
	armor_type = /datum/armor/modtheme_psychological
	complexity_max = DEFAULT_MAX_COMPLEXITY - 7
	charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
	skins = list(
		"psychological" = list(
			HELMET_LAYER = NECK_LAYER,
			HELMET_FLAGS = list(
				UNSEALED_CLOTHING = SNUG_FIT,
				SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
				UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
				SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
				SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
			),
			CHESTPLATE_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
				SEALED_INVISIBILITY = HIDEJUMPSUIT,
			),
			GAUNTLETS_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
			),
			BOOTS_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
			),
		"psychotherapeutic" = list(
			HELMET_LAYER = null,
			HELMET_FLAGS = list(
				UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
				UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
				UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
			),
			CHESTPLATE_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
				SEALED_INVISIBILITY = HIDEJUMPSUIT,
			),
			GAUNTLETS_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
			),
			BOOTS_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
			),
		),
	)

Thus we finished our codeside. Now we go to the icon files for the suits and simply add our new skin's icons.
Now our suit is finished. But let's say we want to give it an unique module.

Module

So, for our psychological suit, let's say we want a module that heals the brain damage of everyone in range.
As it's a medical module, we'll put it here. Let's start with the object definition.

/obj/item/mod/module/neuron_healer
	name = "MOD neuron healer module"
	desc = "A module made experimentally by DeForest Medical Corporation. On demand it releases waves \
		that heal neuron damage of everyone nearby, getting their brains to a better state."
	icon_state = "neuron_healer"

As we want this effect to be on demand, we probably want this to be an usable module. There are four types of modules:

  • Passive: These have a passive effect.
  • Togglable: You can turn these on and off.
  • Usable: You can use these for a one time effect.
  • Active: You can only have one selected at a time. It gives you a special click effect.

As we have an usable module, we want to set a cooldown time. All modules are also incompatible with themselves, have a specific power cost and complexity varying on how powerful they are, so let's update our definition, and also add a new variable for how much brain damage we'll heal.

/obj/item/mod/module/neuron_healer
	name = "MOD neuron healer module"
	desc = "A module made experimentally by DeForest Medical Corporation. On demand it releases waves \
		that heal neuron damage of everyone nearby, getting their brains to a better state."
	icon_state = "neuron_healer"
	module_type = MODULE_USABLE
	complexity = 3
	use_energy_cost = DEFAULT_CHARGE_DRAIN
	incompatible_modules = list(/obj/item/mod/module/neuron_healer)
	cooldown_time = 15 SECONDS
	var/brain_damage_healed = 25

Now, we want to override the on_use proc for our new effect. We want to make sure the use checks passed from parent. You can read about most procs and variables by reading this

/obj/item/mod/module/neuron_healer/on_use()
	. = ..()
	if(!.)
		return

After this, we want to put our special code, a basic effect of healing all mobs nearby for their brain damage and creating a beam to them.

/obj/item/mod/module/neuron_healer/on_use()
	. = ..()
	if(!.)
		return
	for(var/mob/living/carbon/carbon_mob in range(5, src))
		if(carbon_mob == mod.wearer)
			continue
		carbon_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -brain_damage_healed)
		mod.wearer.Beam(carbon_mob, icon_state = "plasmabeam", time = 1.5 SECONDS)
	playsound(src, 'sound/effects/magic.ogg', 100, TRUE)
	drain_power(use_energy_cost)

We now have a basic module, we can add it to the techwebs to make it printable ingame, and we can add an inbuilt, advanced version of it for our psychological suit. We'll give it more healing power, no complexity and make it unremovable.

/obj/item/mod/module/neuron_healer/advanced
	name = "MOD advanced neuron healer module"
	complexity = 0
	brain_damage_healed = 50

Now we want to add it to the psychological theme, which is very simple, finishing with this:

/datum/mod_theme/psychological
	name = "psychological"
	desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
	extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
		Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
		for operating at lower power levels, keeping people sane. As consequence, the capacity \
		of the suit has decreased, not being able to fit many modules at all."
	default_skin = "psychological"
	armor_type = /datum/armor/modtheme_psychological
	complexity_max = DEFAULT_MAX_COMPLEXITY - 7
	charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
	inbuilt_modules = list(/obj/item/mod/module/neuron_healer/advanced)
	skins = list(
		"psychological" = list(
			HELMET_LAYER = NECK_LAYER,
			HELMET_FLAGS = list(
				UNSEALED_CLOTHING = SNUG_FIT,
				SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
				UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
				SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
				SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
			),
			CHESTPLATE_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
				SEALED_INVISIBILITY = HIDEJUMPSUIT,
			),
			GAUNTLETS_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
			),
			BOOTS_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
			),
		"psychotherapeutic" = list(
			HELMET_LAYER = null,
			HELMET_FLAGS = list(
				UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
				UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
				UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
			),
			CHESTPLATE_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
				SEALED_INVISIBILITY = HIDEJUMPSUIT,
			),
			GAUNTLETS_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
			),
			BOOTS_FLAGS = list(
				UNSEALED_CLOTHING = THICKMATERIAL,
				SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
			),
		),
	)

Ending

This finishes this hopefully easy to follow along tutorial. You should now know how to make a basic theme, a skin for it, and a module.