Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,41 @@ public sealed interface GameConfig permits GameConfigImpl, InternalGameConfig {
*/
Key DEFAULT_DAMAGE_SOUND = Key.key("entity.player.hurt");

/**
* The static the slender hears while the survivors take his pages away.
* <p>
* A resource pack sound rather than a vanilla one: three 2.2 second takes of tape hiss the
* client picks between, high-passed at 520 Hz so the effect's own pitch drop to 0.7 leaves
* it hissing rather than humming. Nothing in vanilla comes close - rain is the nearest, and
* it reads as weather.
* </p>
* <p>
* A server running without the Cygnus pack therefore hears nothing here. That is the right
* way round: the static is a horror cue, and half of one played through the wrong sample is
* worse than none.
* </p>
*/
Key DEFAULT_SLENDER_STATIC_SOUND = Key.key("cygnus", "vhs_static");

/** The {@link #slenderStaticQuietInterval()} a configuration gets when it says nothing. */
int DEFAULT_SLENDER_STATIC_QUIET_INTERVAL = 12;

/** The {@link #slenderStaticFranticInterval()} a configuration gets when it says nothing. */
int DEFAULT_SLENDER_STATIC_FRANTIC_INTERVAL = 3;

/**
* The longest {@link #slenderStaticQuietInterval()} a configuration may ask for. Past this a
* round could end before the slender has heard the static twice, which makes it noise rather
* than a clock.
*/
int MAX_SLENDER_STATIC_INTERVAL = 120;

/** The {@link #slenderStaticMinVolume()} a configuration gets when it says nothing. */
float DEFAULT_SLENDER_STATIC_MIN_VOLUME = 0.15F;

/** The {@link #slenderStaticMaxVolume()} a configuration gets when it says nothing. */
float DEFAULT_SLENDER_STATIC_MAX_VOLUME = 0.8F;

/**
* The largest {@link #glitchRange()} a configuration may ask for. Beyond this the slender would
* tear a survivor's view apart from across the map, which is the behaviour this range exists to
Expand Down Expand Up @@ -298,6 +333,62 @@ static Builder builder() {
*/
Key damageSound();

/**
* Returns whether the slender hears static as the survivors collect his pages.
*
* @return {@code true} while the static is on
* @since 2.14.0
*/
boolean slenderStaticEnabled();

/**
* Returns the sound the static is built from.
* <p>
* The key is not resolved against the sound registry here: a resource pack sound is a perfectly
* good answer and would not be found in it. It is sent as named.
* </p>
*
* @return the sound key, never {@code null}
* @since 2.14.0
*/
Key slenderStaticSound();

/**
* Returns how many seconds lie between two bursts while no page has been found.
*
* @return the interval in seconds, at most {@link #MAX_SLENDER_STATIC_INTERVAL}
* @since 2.14.0
*/
int slenderStaticQuietInterval();

/**
* Returns how many seconds lie between two bursts once every page is gone.
* <p>
* The gap shrinks from {@link #slenderStaticQuietInterval()} towards this value as the pages
* disappear, which is what tells the slender how late in the round he is.
* </p>
*
* @return the interval in seconds, below {@link #slenderStaticQuietInterval()}
* @since 2.14.0
*/
int slenderStaticFranticInterval();

/**
* Returns how loud the static is while no page has been found.
*
* @return the volume, between 0 and {@link #slenderStaticMaxVolume()}
* @since 2.14.0
*/
float slenderStaticMinVolume();

/**
* Returns how loud the static is once every page is gone.
*
* @return the volume, at most 1
* @since 2.14.0
*/
float slenderStaticMaxVolume();

/**
* Returns how close the slender has to be before the sight of him tears a survivor's view.
* <p>
Expand Down Expand Up @@ -504,6 +595,65 @@ sealed interface Builder permits GameConfigBuilder {
*/
Builder damageSound(Key damageSound);

/**
* Sets whether the slender hears static as the survivors collect his pages.
*
* @param slenderStaticEnabled {@code true} to keep the static on
* @return the builder instance
* @since 2.14.0
*/
Builder slenderStaticEnabled(boolean slenderStaticEnabled);

/**
* Sets the sound the static is built from.
*
* @param slenderStaticSound the sound key
* @return the builder instance
* @since 2.14.0
*/
Builder slenderStaticSound(Key slenderStaticSound);

/**
* Sets how many seconds lie between two bursts while no page has been found.
*
* @param slenderStaticQuietInterval the interval in seconds
* @return the builder instance
* @throws IllegalArgumentException if the interval is below 1 or above
* {@link GameConfig#MAX_SLENDER_STATIC_INTERVAL}
* @since 2.14.0
*/
Builder slenderStaticQuietInterval(int slenderStaticQuietInterval);

/**
* Sets how many seconds lie between two bursts once every page is gone.
*
* @param slenderStaticFranticInterval the interval in seconds
* @return the builder instance
* @throws IllegalArgumentException if the interval is below 1
* @since 2.14.0
*/
Builder slenderStaticFranticInterval(int slenderStaticFranticInterval);

/**
* Sets how loud the static is while no page has been found.
*
* @param slenderStaticMinVolume the volume
* @return the builder instance
* @throws IllegalArgumentException if the volume is below 0 or above 1
* @since 2.14.0
*/
Builder slenderStaticMinVolume(float slenderStaticMinVolume);

/**
* Sets how loud the static is once every page is gone.
*
* @param slenderStaticMaxVolume the volume
* @return the builder instance
* @throws IllegalArgumentException if the volume is below 0 or above 1
* @since 2.14.0
*/
Builder slenderStaticMaxVolume(float slenderStaticMaxVolume);

/**
* Sets how close the slender has to be before the sight of him tears a survivor's view.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ public final class GameConfigBuilder implements GameConfig.Builder {
private int glitchRange = GameConfig.DEFAULT_GLITCH_RANGE;
private int glitchCloseRange = GameConfig.DEFAULT_GLITCH_CLOSE_RANGE;
private int glitchViewAngle = GameConfig.DEFAULT_GLITCH_VIEW_ANGLE;
private boolean slenderStaticEnabled;
private Key slenderStaticSound = GameConfig.DEFAULT_SLENDER_STATIC_SOUND;
// Pre-set for the same reason as the glitch distances above: build() checks the two intervals
// and the two volumes against each other, and a builder that was never told about the static
// would trip those checks on a pair of zeroes.
private int slenderStaticQuietInterval = GameConfig.DEFAULT_SLENDER_STATIC_QUIET_INTERVAL;
private int slenderStaticFranticInterval = GameConfig.DEFAULT_SLENDER_STATIC_FRANTIC_INTERVAL;
private float slenderStaticMinVolume = GameConfig.DEFAULT_SLENDER_STATIC_MIN_VOLUME;
private float slenderStaticMaxVolume = GameConfig.DEFAULT_SLENDER_STATIC_MAX_VOLUME;

@Override
public GameConfig.Builder minPlayers(int minPlayers) {
Expand Down Expand Up @@ -197,6 +206,67 @@ public GameConfig.Builder glitchViewAngle(int glitchViewAngle) {
return this;
}

@Override
public GameConfig.Builder slenderStaticEnabled(boolean slenderStaticEnabled) {
this.slenderStaticEnabled = slenderStaticEnabled;
return this;
}

@Override
public GameConfig.Builder slenderStaticSound(Key slenderStaticSound) {
this.slenderStaticSound = slenderStaticSound;
return this;
}

@Override
public GameConfig.Builder slenderStaticQuietInterval(int slenderStaticQuietInterval) {
if (slenderStaticQuietInterval < 1
|| slenderStaticQuietInterval > GameConfig.MAX_SLENDER_STATIC_INTERVAL) {
throw new IllegalArgumentException(
"Slender static quiet interval must be between 1 and "
+ GameConfig.MAX_SLENDER_STATIC_INTERVAL + " seconds");
}
this.slenderStaticQuietInterval = slenderStaticQuietInterval;
return this;
}

@Override
public GameConfig.Builder slenderStaticFranticInterval(int slenderStaticFranticInterval) {
if (slenderStaticFranticInterval < 1) {
throw new IllegalArgumentException("Slender static frantic interval must be at least 1 second");
}
this.slenderStaticFranticInterval = slenderStaticFranticInterval;
return this;
}

@Override
public GameConfig.Builder slenderStaticMinVolume(float slenderStaticMinVolume) {
this.slenderStaticMinVolume = checkVolume(slenderStaticMinVolume, "minimum");
return this;
}

@Override
public GameConfig.Builder slenderStaticMaxVolume(float slenderStaticMaxVolume) {
this.slenderStaticMaxVolume = checkVolume(slenderStaticMaxVolume, "maximum");
return this;
}

/**
* Checks a static volume against the range a sound can carry.
*
* @param volume the volume to check
* @param name how the volume is named in the message of a failure
* @return the volume
* @throws IllegalArgumentException if the volume is outside 0 to 1
*/
private static float checkVolume(float volume, String name) {
if (volume < 0.0F || volume > 1.0F) {
throw new IllegalArgumentException(
"Slender static " + name + " volume must be between 0 and 1");
}
return volume;
}

/**
* {@inheritDoc}
* <p>
Expand All @@ -215,6 +285,16 @@ public GameConfig build() {
"Glitch close range (" + glitchCloseRange + ") must be below the glitch range ("
+ glitchRange + ")");
}
if (slenderStaticFranticInterval >= slenderStaticQuietInterval) {
throw new IllegalArgumentException(
"Slender static frantic interval (" + slenderStaticFranticInterval
+ ") must be below the quiet interval (" + slenderStaticQuietInterval + ")");
}
if (slenderStaticMinVolume > slenderStaticMaxVolume) {
throw new IllegalArgumentException(
"Slender static minimum volume (" + slenderStaticMinVolume
+ ") must not be above the maximum volume (" + slenderStaticMaxVolume + ")");
}
return new GameConfigImpl(
minPlayers,
maxPlayers,
Expand All @@ -235,7 +315,13 @@ public GameConfig build() {
damageSound,
glitchRange,
glitchCloseRange,
glitchViewAngle
glitchViewAngle,
slenderStaticEnabled,
slenderStaticSound,
slenderStaticQuietInterval,
slenderStaticFranticInterval,
slenderStaticMinVolume,
slenderStaticMaxVolume
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
* @param glitchCloseRange the distance in blocks at which the tearing is at its worst
* @param glitchViewAngle how far off the centre of their view he may stand and still count
* as seen, in degrees
* @param slenderStaticEnabled whether the slender hears static as his pages are collected
* @param slenderStaticSound the sound the static is built from
* @param slenderStaticQuietInterval the seconds between two bursts while no page has been found
* @param slenderStaticFranticInterval the seconds between two bursts once every page is gone
* @param slenderStaticMinVolume how loud the static is while no page has been found
* @param slenderStaticMaxVolume how loud the static is once every page is gone
* @author theEvilReaper
* @version 1.4.0
* @since 1.0.0
Expand All @@ -57,7 +63,13 @@ public record GameConfigImpl(
Key damageSound,
int glitchRange,
int glitchCloseRange,
int glitchViewAngle
int glitchViewAngle,
boolean slenderStaticEnabled,
Key slenderStaticSound,
int slenderStaticQuietInterval,
int slenderStaticFranticInterval,
float slenderStaticMinVolume,
float slenderStaticMaxVolume
) implements GameConfig {

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
* <li>glitchRange</li>
* <li>glitchCloseRange</li>
* <li>glitchViewAngle</li>
* <li>slenderStaticEnabled</li>
* <li>slenderStaticSound</li>
* <li>slenderStaticQuietInterval</li>
* <li>slenderStaticFranticInterval</li>
* <li>slenderStaticMinVolume</li>
* <li>slenderStaticMaxVolume</li>
* </ul>
* <p>
* If a property can not be found in the file, the default value will be used.
Expand All @@ -59,6 +65,7 @@ public final class GameConfigReader {
private static final Pattern SHA1_PATTERN = Pattern.compile("[0-9a-fA-F]{40}");
private static final String PAGE_PROXIMITY_SOUND_KEY = "pageProximitySound";
private static final String DAMAGE_SOUND_KEY = "damageSound";
private static final String SLENDER_STATIC_SOUND_KEY = "slenderStaticSound";

private final Path path;

Expand Down Expand Up @@ -120,7 +127,13 @@ public GameConfig getConfig() {
.damageSound(getSound(properties, DAMAGE_SOUND_KEY, internal.damageSound()))
.glitchRange(getInt(properties, "glitchRange", internal.glitchRange()))
.glitchCloseRange(getInt(properties, "glitchCloseRange", internal.glitchCloseRange()))
.glitchViewAngle(getInt(properties, "glitchViewAngle", internal.glitchViewAngle()));
.glitchViewAngle(getInt(properties, "glitchViewAngle", internal.glitchViewAngle()))
.slenderStaticEnabled(getBoolean(properties, "slenderStaticEnabled", internal.slenderStaticEnabled()))
.slenderStaticSound(getSound(properties, SLENDER_STATIC_SOUND_KEY, internal.slenderStaticSound()))
.slenderStaticQuietInterval(getInt(properties, "slenderStaticQuietInterval", internal.slenderStaticQuietInterval()))
.slenderStaticFranticInterval(getInt(properties, "slenderStaticFranticInterval", internal.slenderStaticFranticInterval()))
.slenderStaticMinVolume(getFloat(properties, "slenderStaticMinVolume", internal.slenderStaticMinVolume()))
.slenderStaticMaxVolume(getFloat(properties, "slenderStaticMaxVolume", internal.slenderStaticMaxVolume()));

return configBuilder.build();
}
Expand Down
Loading
Loading