[Solved B18] Make Modding Custom Melee Weapon Sounds More Intuitive

Started by O Negative, January 20, 2018, 01:38:50 PM

Previous topic - Next topic

O Negative

Hi,

This is a request to Tynan and the other developers of the game. The request I'd like to make is as follows: Make it possible for melee weapons to have custom sounds without the implementation of a new material. I'm making this request because I, and a few others, have run into a few issues with the method of using a custom material as a middle-man for a custom sound for our custom melee weapons.

Ideally, this would be an xml property listed under the "tools" of a weapon. For example:
<tools>
  <li>
<label>point</label>
        <soundCastHit>"soundDefName"</soundDefHit> ///This sound would play for the tool used, if the tool/pawn hits its target
        <soundCastMiss>"soundDefName"</soundDefMiss> ///This sound would play for the tool used, if the tool/pawn misses its target
<capacities>
  <li>Stab</li>
</capacities>
<power>10</power>
<cooldownTime>2.45</cooldownTime>
<commonality>0.1</commonality>
  </li>
</tools>

This would allow for the same melee weapon to sound different depending on the tool used, and give modders more creative freedom when it comes to adding more melee weapons to the game. I also think this has a lot of potential for core/vanilla game immersion.


In the past, I would use a C# method which was basically a copy/paste of the melee verb with some minor modifications. That is no longer possible, due to the new "tools" system for weapons. Sure, if I knew a bit more C#, I might be able to get around the tools system, but I don't think it should have to be that complicated.

I don't, by any means, think that everything should be moddable via xml. But, I do think this should be one of those things that is possible through xml modding, in the same way custom ranged weapon sounds are.

Chicken Plucker

#1
Hello, I second this so much.

B18 broke soundcast code for melee completely, I just want my chainsaw or an energy sword to make sound effects every time it's swung, why has Beta 18 made it such a pain to do that?

Anyway, I hope for the sake of future content for Rimworld, this gets fixed, if modding compatibility is not a priority then at least this issue gets noticed for that

BrokenValkyrie


O Negative

A17

Weapon Def
<ThingDef ParentName="SangheiliMeleeWeapon">
<defName>MeleeWeapon_EnergySword</defName>
<label>Energy Sword</label>
<description>Partially ionized blades of free moving electron based gas, held in a blade-like form by two small magnetic-field generators built into the handle of the weapon.</description>
<graphicData>
<texPath>Weapons/EnergySword</texPath>
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<soundInteract>InteractPistol</soundInteract>
<equippedAngleOffset>0</equippedAngleOffset>
<statBases>
  <MarketValue>3200</MarketValue>
  <Mass>1.5</Mass>
  <MeleeWeapon_DamageAmount>50</MeleeWeapon_DamageAmount>
  <MeleeWeapon_Cooldown>2.0</MeleeWeapon_Cooldown>
</statBases>
<verbs>
  <li>
<verbClass>HaloWeapons.Verb_EnergySwordAttack</verbClass> ///This is the reference to the new Custom Melee Verb Assembly
<hasStandardCommand>true</hasStandardCommand>
<!-- Change damage def to ignore armor -->
<meleeDamageDef>EnergyCut</meleeDamageDef>
  </li>
</verbs>
</ThingDef>


Custom Melee Verb Assembly (Sound Source)
*The important bits referencing custom soundDefName(s) is towards the bottom of this.
using RimWorld;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Verse;
using Verse.Sound;

namespace HaloWeapons
{
public class Verb_EnergySwordAttack : Verb_MeleeAttack
{
private const int TargetCooldown = 50;

private const float DefaultHitChance = 0.6f;

protected override bool TryCastShot()
{
Pawn casterPawn = base.get_CasterPawn();
bool fullBodyBusy = casterPawn.stances.get_FullBodyBusy();
bool result;
if (fullBodyBusy)
{
result = false;
}
else
{
Thing thing = this.currentTarget.get_Thing();
bool flag = !base.CanHitTarget(thing);
if (flag)
{
Log.Warning(string.Concat(new object[]
{
casterPawn,
" meleed ",
thing,
" from out of melee position."
}));
}
casterPawn.get_Drawer().rotator.Face(thing.get_DrawPos());
bool flag2 = !this.IsTargetImmobile(this.currentTarget) && casterPawn.skills != null;
if (flag2)
{
casterPawn.skills.Learn(SkillDefOf.Melee, 250f, false);
}
bool flag3 = Rand.get_Value() < this.GetHitChance(thing);
bool flag4;
SoundDef soundDef;
if (flag3)
{
flag4 = true;
this.ApplyMeleeDamageToTarget(this.currentTarget);
bool flag5 = thing.def.category == 4;
if (flag5)
{
soundDef = this.SoundHitBuilding();
}
else
{
soundDef = this.SoundHitPawn();
}
}
else
{
flag4 = false;
soundDef = this.SoundMiss();
}
SoundStarter.PlayOneShot(soundDef, new TargetInfo(thing.get_Position(), casterPawn.get_Map(), false));
casterPawn.get_Drawer().Notify_MeleeAttackOn(thing);
Pawn pawn = thing as Pawn;
bool flag6 = pawn != null && !pawn.get_Dead();
if (flag6)
{
pawn.stances.StaggerFor(95);
bool flag7 = casterPawn.get_MentalStateDef() != MentalStateDefOf.SocialFighting || pawn.get_MentalStateDef() != MentalStateDefOf.SocialFighting;
if (flag7)
{
pawn.mindState.meleeThreat = casterPawn;
pawn.mindState.lastMeleeThreatHarmTick = Find.get_TickManager().get_TicksGame();
}
}
casterPawn.get_Drawer().rotator.FaceCell(thing.get_Position());
bool flag8 = casterPawn.caller != null;
if (flag8)
{
casterPawn.caller.Notify_DidMeleeAttack();
}
result = flag4;
}
return result;
}

[DebuggerHidden]
private IEnumerable<DamageInfo> DamageInfosToApply(LocalTargetInfo target)
{
Verb_EnergySwordAttack.<DamageInfosToApply>d__3 expr_07 = new Verb_EnergySwordAttack.<DamageInfosToApply>d__3(-2);
expr_07.<>4__this = this;
expr_07.<>3__target = target;
return expr_07;
}

private float GetHitChance(LocalTargetInfo target)
{
bool surpriseAttack = this.surpriseAttack;
float result;
if (surpriseAttack)
{
result = 1f;
}
else
{
bool flag = this.IsTargetImmobile(target);
if (flag)
{
result = 1f;
}
else
{
bool flag2 = base.get_CasterPawn().skills != null;
if (flag2)
{
result = StatExtension.GetStatValue(base.get_CasterPawn(), StatDefOf.MeleeHitChance, true);
}
else
{
result = 0.6f;
}
}
}
return result;
}

private bool IsTargetImmobile(LocalTargetInfo target)
{
Thing thing = target.get_Thing();
Pawn pawn = thing as Pawn;
return thing.def.category != 1 || pawn.get_Downed() || PawnUtility.GetPosture(pawn) > 0;
}

private void ApplyMeleeDamageToTarget(LocalTargetInfo target)
{
foreach (DamageInfo current in this.DamageInfosToApply(target))
{
bool thingDestroyed = target.get_ThingDestroyed();
if (thingDestroyed)
{
break;
}
target.get_Thing().TakeDamage(current);
}
}

private SoundDef SoundHitPawn()
{
bool flag = this.ownerEquipment != null && this.ownerEquipment.get_Stuff() != null;
SoundDef result;
if (flag)
{
bool flag2 = this.verbProps.meleeDamageDef.armorCategory == DamageArmorCategoryDefOf.Sharp;
if (flag2)
{
bool flag3 = !SoundDefHelper.NullOrUndefined(this.ownerEquipment.get_Stuff().stuffProps.soundMeleeHitSharp);
if (flag3)
{
result = this.ownerEquipment.get_Stuff().stuffProps.soundMeleeHitSharp;
return result;
}
}
else
{
bool flag4 = !SoundDefHelper.NullOrUndefined(this.ownerEquipment.get_Stuff().stuffProps.soundMeleeHitBlunt);
if (flag4)
{
result = this.ownerEquipment.get_Stuff().stuffProps.soundMeleeHitBlunt;
return result;
}
}
}
bool flag5 = base.get_CasterPawn() != null && !SoundDefHelper.NullOrUndefined(base.get_CasterPawn().def.race.soundMeleeHitPawn);
if (flag5)
{
result = SoundDef.Named("SwordHitPawn");
}
else
{
result = SoundDef.Named("SwordHitPawn");
}
return result;
}

private SoundDef SoundHitBuilding()
{
bool flag = this.ownerEquipment != null && this.ownerEquipment.get_Stuff() != null;
SoundDef result;
if (flag)
{
bool flag2 = this.verbProps.meleeDamageDef.armorCategory == DamageArmorCategoryDefOf.Sharp;
if (flag2)
{
bool flag3 = !SoundDefHelper.NullOrUndefined(this.ownerEquipment.get_Stuff().stuffProps.soundMeleeHitSharp);
if (flag3)
{
result = this.ownerEquipment.get_Stuff().stuffProps.soundMeleeHitSharp;
return result;
}
}
else
{
bool flag4 = !SoundDefHelper.NullOrUndefined(this.ownerEquipment.get_Stuff().stuffProps.soundMeleeHitBlunt);
if (flag4)
{
result = this.ownerEquipment.get_Stuff().stuffProps.soundMeleeHitBlunt;
return result;
}
}
}
bool flag5 = base.get_CasterPawn() != null && !SoundDefHelper.NullOrUndefined(base.get_CasterPawn().def.race.soundMeleeHitBuilding);
if (flag5)
{
result = SoundDef.Named("SwordHitBuilding");
}
else
{
result = SoundDef.Named("SwordHitBuilding");
}
return result;
}

private SoundDef SoundMiss()
{
bool flag = base.get_CasterPawn() != null && !SoundDefHelper.NullOrUndefined(base.get_CasterPawn().def.race.soundMeleeMiss);
SoundDef result;
if (flag)
{
result = SoundDef.Named("SwordMiss");
}
else
{
result = SoundDef.Named("SwordMiss");
}
return result;
}
}
}





B18

Weapon Def
<!-- USE STUFF TO MAKE SOUNDS YOU WANT -->
<ThingDef ParentName="BaseMeleeWeapon_Sharp">
<defName>Halo_EnergySword</defName>
<label>Energy Sword</label>
<description>Partially ionized blades of free moving electron based gas, held in a blade-like form by two small magnetic-field generators built into the handle of the weapon.</description>
<techLevel>Spacer</techLevel>
<graphicData>
<texPath>Weapons/EnergySword</texPath>
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<equippedAngleOffset>-65</equippedAngleOffset>
<weaponTags>
  <li>SangheiliMelee</li>
  <li>NeolithicMeleeAdvanced</li>
</weaponTags>
<statBases>
<Mass>1.5</Mass>
<Weapon_Bulk>0.5</Weapon_Bulk>
<WorkToMake>1000000</WorkToMake>
</statBases>
<stuffCategories>
<li>EnergySwordStuffCategory</li> ///This is now what has to be used to make a custom sound for a melee weapon
</stuffCategories>
<costStuffCount>10</costStuffCount>
<smeltable>false</smeltable>
<tools>
  <li>
<label>point</label>
<capacities>
  <li>Stab</li>
</capacities>
<power>100</power>
<cooldownTime>2.45</cooldownTime>
<commonality>0.1</commonality>
  </li>
  <li>
<label>edge</label>
<capacities>
  <li>Cut</li>
</capacities>
<power>100</power>
<cooldownTime>1.95</cooldownTime>
  </li>
</tools>
</ThingDef>


Stuff Def (Sound Source)
  <ThingDef ParentName="ResourceBase">
    <defName>EnergySwordStuff</defName>
    <label>Covenant</label>
    <description>You shouldn't be seeing this resource. It only exists to make energy sword sounds possible.</description>
    <graphicData>
      <texPath>Things/Item/Resource/Plasteel</texPath>
      <graphicClass>Graphic_Single</graphicClass>
    </graphicData>
    <soundInteract>Metal_Drop</soundInteract>
    <soundDrop>Metal_Drop</soundDrop>
    <useHitPoints>false</useHitPoints>
    <statBases>
      <MarketValue>0</MarketValue>
      <Mass>0.5</Mass>
      <SharpDamageMultiplier>1.0</SharpDamageMultiplier>
    </statBases>
<thingCategories>
      <li>Chunks</li>
    </thingCategories>
<stackLimit>1000</stackLimit>
    <smallVolume>true</smallVolume>
    <deepCommonality>0</deepCommonality>
    <stuffProps>
      <categories>
        <li>EnergySwordStuffCategory</li>
      </categories>
      <commonality>0</commonality>
      <constructEffect>ConstructMetal</constructEffect>
      <color>(225,225,225)</color>
  <allowColorGenerators>false</allowColorGenerators>
      <soundImpactStuff>SwordHitBuilding</soundImpactStuff> ---------\
      <soundMeleeHitSharp>SwordHitBuilding</soundMeleeHitSharp> ------|///These are now the only place you can you put custom sounds for weapons
      <soundMeleeHitBlunt>SwordHitBuilding</soundMeleeHitBlunt> -----/
      <statFactors>
        <MaxHitPoints>1</MaxHitPoints>
        <Beauty>1</Beauty>
        <Flammability>1</Flammability>
        <WorkToMake>1</WorkToMake>
<WorkToBuild>1</WorkToBuild>
        <MeleeWeapon_CooldownMultiplier>1</MeleeWeapon_CooldownMultiplier>
      </statFactors>
      <smeltable>true</smeltable>
    </stuffProps>
  </ThingDef>

BrokenValkyrie

Are you aware that verbs(A17 melee implementation) has been moved to<ManeuverDef> ? This includes sound casting.

I explained it in the use of custom melee damage here, https://ludeon.com/forums/index.php?topic=37745.0.

Sorry I don't think I can be much help from here on.

O Negative

Quote from: BrokenValkyrie on January 20, 2018, 11:09:55 PM
Are you aware that verbs(A17 melee implementation) has been moved to<ManeuverDef> ? This includes sound casting.

I explained it in the use of custom melee damage here, https://ludeon.com/forums/index.php?topic=37745.0.

Sorry I don't think I can be much help from here on.
Shoot! I was completely oblivious to that fact! Thanks for letting me know.

You've been more than enough help :)