diff --git a/code/techno.cpp b/code/techno.cpp index 151fa1552..f1b453510 100644 --- a/code/techno.cpp +++ b/code/techno.cpp @@ -5469,7 +5469,8 @@ void TechnoClass::Do_Uncloak(bool silent) CloakingDevice.Set_Rate(TClass->CloakingSpeed); CloakingDevice.Set_Step(-1); if (!silent) { - Sound_Effect(Rule->CloakSound, PositionCoord); + VocType uncloak_sound = TClass->UncloakSound != VOC_NONE ? TClass->UncloakSound : Rule->CloakSound; + Sound_Effect(uncloak_sound, PositionCoord); } } } @@ -5498,7 +5499,8 @@ void TechnoClass::Do_Cloak(bool silent) CloakingDevice.Set_Rate(TClass->CloakingSpeed); CloakingDevice.Set_Step(1); if (!silent) { - Sound_Effect(Rule->CloakSound, PositionCoord); + VocType cloak_sound = TClass->CloakSound != VOC_NONE ? TClass->CloakSound : Rule->CloakSound; + Sound_Effect(cloak_sound, PositionCoord); } } } diff --git a/code/techtype.cpp b/code/techtype.cpp index 7cc93f898..c4d924334 100644 --- a/code/techtype.cpp +++ b/code/techtype.cpp @@ -201,7 +201,9 @@ TechnoTypeClass::TechnoTypeClass(char const * ininame, SpeedType speed) : ZFudgeColumn(5), ZFudgeTunnel(10), ZFudgeBridge(0), - Description("") + Description(""), + CloakSound(VOC_NONE), + UncloakSound(VOC_NONE) { IsSentient = true; @@ -676,6 +678,8 @@ bool TechnoTypeClass::Read_INI(CCINIClass const & ini) TargetDistanceCoefficient = ini.Get_Float(Name(), "TargetDistanceCoefficient", TargetDistanceCoefficient == 0 ? Rule->TargetDistanceCoefficientDefault : TargetDistanceCoefficient); SpecialThreatValue = ini.Get_Float(Name(), "SpecialThreatValue", SpecialThreatValue); Description = ini.Get_String(Name(), "Description", Description.c_str()); + CloakSound = ini.Get_VocType(Name(), "CloakSound", CloakSound); + UncloakSound = ini.Get_VocType(Name(), "UncloakSound", UncloakSound); IsLeader = false; if (Weapons[0].Weapon != NULL && Weapons[0].Weapon->Attack > 0) { @@ -1084,6 +1088,8 @@ void TechnoTypeClass::Serialize(SaveStreamClass & stream) stream.Serialize(ZFudgeTunnel); stream.Serialize(ZFudgeBridge); stream.Serialize(Description); + stream.Serialize(CloakSound); + stream.Serialize(UncloakSound); } @@ -1186,6 +1192,8 @@ void TechnoTypeClass::Compute_CRC(class CRCEngine & crc) const crc(IsVisibleLoad); crc(IsLightningRod); crc(Description.c_str()); + crc(CloakSound); + crc(UncloakSound); } diff --git a/code/techtype.h b/code/techtype.h index 61a7f627b..bc71101db 100644 --- a/code/techtype.h +++ b/code/techtype.h @@ -750,6 +750,13 @@ class TechnoTypeClass : public ObjectTypeClass */ std::string Description; + /* + * These are the sounds this type plays as it starts to cloak and to uncloak. + * VOC_NONE plays the rules-wide CloakSound in either case. + */ + VocType CloakSound; + VocType UncloakSound; + //-------------------------------------------------------------------- TechnoTypeClass(char const * ininame, SpeedType speed); virtual ~TechnoTypeClass() override; diff --git a/manual/changes/type-cloak-and-uncloak-sounds.md b/manual/changes/type-cloak-and-uncloak-sounds.md new file mode 100644 index 000000000..08543242a --- /dev/null +++ b/manual/changes/type-cloak-and-uncloak-sounds.md @@ -0,0 +1,29 @@ +--- +title: Let a type set its own cloak and uncloak sounds +category: feature +release: 0.3.0 +targets: +- type: key + id: CloakSound + scope: aircrafttype + effect: added +- type: key + id: UncloakSound + effect: added +- type: system + id: cloaking + effect: changed +- type: format + id: save-games + effect: changed +credit: +- JoyfulShush +- CCHyper +- tomsons26 +--- + +`CloakSound=` in a vehicle, infantryman, aircraft or structure type's `rules.ini` section sets the sound that type plays at its position as it starts to hide. It replaces the `[AudioVisual]` `CloakSound` for that type when the key is set. + +`UncloakSound=` in the same section sets the sound the type plays as it starts to reappear. A type that sets only `CloakSound=` still plays the `[AudioVisual]` `CloakSound` when it reappears, since no rules-wide uncloak sound exists. + +CCHyper and tomsons26 are credited for the original Vinifera implementation. diff --git a/manual/content/keys/cloaksound--aircrafttype.md b/manual/content/keys/cloaksound--aircrafttype.md new file mode 100644 index 000000000..54c70d25e --- /dev/null +++ b/manual/content/keys/cloaksound--aircrafttype.md @@ -0,0 +1,20 @@ +--- +key: CloakSound +scope: aircrafttype +label: Type cloak sound +see_also: [UncloakSound] +when_omitted: + kind: inherited + note: The object plays the rules-wide CloakSound instead. +--- + +```ini title="rules.ini" +[STNK] +CloakSound=CLOAKHIDE ; a sound ID registered in SOUND.INI +``` + +An object of this type plays this sound at its position as it starts to hide, in place of the [rules-wide `CloakSound`](/keys/cloaksound/#scope-global-rules). A [cloaking field](/systems/cloaking/#cloaking-fields) that hides an object plays this sound too. + +The setting covers hiding only. The same object plays its own [`UncloakSound`](/keys/uncloaksound/), or the rules-wide `CloakSound`, as it starts to reappear. + +A name that matches no [registered sound](/formats/sound-ini/) is ignored. diff --git a/manual/content/keys/cloaksound--global-rules.md b/manual/content/keys/cloaksound--global-rules.md new file mode 100644 index 000000000..5d3e9a74e --- /dev/null +++ b/manual/content/keys/cloaksound--global-rules.md @@ -0,0 +1,19 @@ +--- +key: CloakSound +scope: global-rules +label: Rules-wide cloak sound +when_omitted: + kind: value + value: none +--- + +```ini title="rules.ini" +[AudioVisual] +CloakSound=CLOAK5 ; the shipped sound, defined in SOUND.INI +``` + +The rules-wide sound plays at an object's position as it starts to hide and as it starts to reappear. It plays for objects of every house. That includes objects a [cloaking field](/systems/cloaking/#cloaking-fields) hides: vehicles, infantrymen and aircraft on a covered cell, and structures whose center cell the field covers. + +A name that matches no [registered sound](/formats/sound-ini/) is ignored; with no sound already set, nothing plays. + +A type can replace it with sounds of its own: [`CloakSound`](/keys/cloaksound/#scope-aircrafttype) plays as that type hides, and [`UncloakSound`](/keys/uncloaksound/) plays as it reappears. diff --git a/manual/content/keys/cloaksound.md b/manual/content/keys/cloaksound.md index c020886fd..c1e193543 100644 --- a/manual/content/keys/cloaksound.md +++ b/manual/content/keys/cloaksound.md @@ -1,17 +1,7 @@ --- key: CloakSound -summary: The sound played at an object's position as it starts to hide or to reappear. +summary: The rules-wide or per-type sound played at an object's position as it starts to hide or to reappear. see_also: ["system:cloaking"] -when_omitted: - kind: value - value: none --- -```ini title="rules.ini" -[AudioVisual] -CloakSound=CLOAK5 ; the shipped sound, defined in SOUND.INI -``` - -`CloakSound` plays at an object's position each time the object starts to hide or to reappear. It plays for objects of every house. That includes each vehicle and infantryman a growing [cloaking field](/systems/cloaking/#cloaking-fields) passes over, and each structure that fades out under one. - One kind of reappearance is silent. A critically damaged vehicle, infantryman or aircraft, with health at or below [`ConditionRed`](/keys/conditionred/), can abandon its cloak partway through hiding: it has a ten percent chance each frame while its fade is in the darkened band. When it abandons the cloak this way, it fades back into view without the sound. diff --git a/manual/content/keys/uncloaksound.md b/manual/content/keys/uncloaksound.md new file mode 100644 index 000000000..81c07cecb --- /dev/null +++ b/manual/content/keys/uncloaksound.md @@ -0,0 +1,19 @@ +--- +key: UncloakSound +summary: The sound an object of this type plays as it starts to reappear. +see_also: [CloakSound] +when_omitted: + kind: inherited + note: The object plays the rules-wide CloakSound instead. +--- + +```ini title="rules.ini" +[STNK] +UncloakSound=MYREVEAL ; a sound ID registered in SOUND.INI +``` + +An object of this type plays this sound at its position as it starts to reappear, in place of the [rules-wide `CloakSound`](/keys/cloaksound/#scope-global-rules). No rules-wide uncloak sound exists, so a type that doesn't specify this sound plays the rules-wide `CloakSound` as it reappears. + +The setting covers reappearing only. The object still plays its own [`CloakSound`](/keys/cloaksound/#scope-aircrafttype), or the rules-wide `CloakSound`, as it starts to hide. + +A name that matches no [registered sound](/formats/sound-ini/) is ignored. A type that still has no sound of its own then plays the rules-wide `CloakSound`. diff --git a/manual/content/systems/cloaking.md b/manual/content/systems/cloaking.md index fff809bc8..e8732ba0f 100644 --- a/manual/content/systems/cloaking.md +++ b/manual/content/systems/cloaking.md @@ -19,6 +19,7 @@ keys: - RadialColor - SensorArray - Sensors + - UncloakSound related: - type: system id: emp-pulse @@ -70,7 +71,7 @@ The two fades differ in length: - **Fading out** ends early. The object becomes hidden as soon as it reaches the shadowy band, at the first step at or above half of `CloakingStages`. It never shows the ripple on the way out. - **Fading back in** starts one step below the whole, so it opens with the ripple, and runs all the way down to fully visible. -With the stock `CloakingStages=9` and the stealth tank's `CloakingSpeed=5` from the example above, the tank disappears after about 25 frames and takes about 40 to reappear. [`CloakSound`](/keys/cloaksound/) plays at the object's position as each fade begins. +With the stock `CloakingStages=9` and the stealth tank's `CloakingSpeed=5` from the example above, the tank disappears after about 25 frames and takes about 40 to reappear. A [`CloakSound`](/keys/cloaksound/) plays at the object's position as the fade out begins, and as the fade back in begins unless the object's type sets an [`UncloakSound`](/keys/uncloaksound/). A structure ignores both settings. It steps through fifteen fixed levels of translucency, one level per frame in either direction, and is hidden at the fifteenth. @@ -119,7 +120,7 @@ Each event below ends the cloak and starts the full fade back into view: - **A jumpjet nearby:** a moving jumpjet uncloaks everything near it, whoever owns it. [Jumpjet overflight](#jumpjet-overflight) gives the area. - **Losing cover:** an object hidden only because a field covered its cell, as soon as that cover is lifted. - **Being immobilized:** an [EM pulse](/systems/emp-pulse/) and anything else that stuns. An object that holds the `CLOAK` ability, or stands in its house's cloaking field, keeps its cloak. -- **Critical damage while fading out:** while the fade out is in its darkened band and health is at or below `ConditionRed`, a 10% chance on each frame abandons the cloak. The chance is taken on every frame of that band, so a slow fade is abandoned more often. With the stock nine stages the band lasts about twice `CloakingSpeed` frames, so at the stealth tank's `CloakingSpeed=5` about two critically damaged cloaks in three are abandoned. This is the only uncloak that plays no `CloakSound`. +- **Critical damage while fading out:** while the fade out is in its darkened band and health is at or below `ConditionRed`, a 10% chance on each frame abandons the cloak. The chance is taken on every frame of that band, so a slow fade is abandoned more often. With the stock nine stages the band lasts about twice `CloakingSpeed` frames, so at the stealth tank's `CloakingSpeed=5` about two critically damaged cloaks in three are abandoned. This is the only uncloak that plays no sound. - **A detector beside a structure:** a hidden structure has a `Sensors=yes` object within one cell of its footprint, and that object's owner does not consider the structure allied. Sensor coverage is not on the list. Marking a cell as sensed never uncloaks anything standing on it. diff --git a/manual/data/adjudications.yaml b/manual/data/adjudications.yaml index d0bc50343..3058203a2 100644 --- a/manual/data/adjudications.yaml +++ b/manual/data/adjudications.yaml @@ -69,6 +69,10 @@ Bright: "WeaponTypeClass::IsBright": {verdict: distinct} "WarheadTypeClass::IsBright": {verdict: distinct} +CloakSound: + "RulesClass::CloakSound": {verdict: distinct} + "TechnoTypeClass::CloakSound": {verdict: distinct} + Color: "BulletTypeClass::Color": {verdict: distinct} "HouseTypeClass::Scheme": {verdict: distinct} diff --git a/manual/data/ini-keys.yaml b/manual/data/ini-keys.yaml index 9e64aa358..c4c806725 100644 --- a/manual/data/ini-keys.yaml +++ b/manual/data/ini-keys.yaml @@ -4983,6 +4983,20 @@ CloakRadiusInCells: CloakSound: key: CloakSound scopes: + - applies_to: *id003 + file: rules.ini + section: + kind: identifier + source: object-type + value_type: sound + status: reviewed + _provenance: + default_candidate: none + declared_in: TechnoTypeClass + member: CloakSound + source: code/techtype.cpp + guard: null + level: TechnoTypeClass - applies_to: - global rules file: rules.ini @@ -4990,7 +5004,7 @@ CloakSound: kind: literal name: AudioVisual value_type: sound - status: generated + status: reviewed _provenance: default_candidate: none declared_in: RulesClass @@ -24883,6 +24897,23 @@ URepairRate: source: code/rules.cpp guard: null level: RulesClass +UncloakSound: + key: UncloakSound + scopes: + - applies_to: *id003 + file: rules.ini + section: + kind: identifier + source: object-type + value_type: sound + status: generated + _provenance: + default_candidate: none + declared_in: TechnoTypeClass + member: UncloakSound + source: code/techtype.cpp + guard: null + level: TechnoTypeClass UndeploySound: key: UndeploySound scopes: